I am studying a way for serializing objects in sl. NOTE: This is still a preliminary draft: I'm heavily working on it to have it properly designed/programmed. Any suggestions are welcome :)
Current version has still some major problems, including:
* Too slow: each prim needs at least 10 seconds to be rezzed (probably due to BTLT serialization :(
* Position is not maintained (some major bug to solve)
Probably will simplify using standard listdump without BTLT.
Using llGetPrimitiveParams and putting information in a convenient format that can be reread by a rezzer. This is essential for moving objects from SL to opensim whenever they will implement llRezObject and llSetPrimitiveParams :) [Possibly we will lose the textures, so protocol should be robust enough to be able to produce a subset of the parameters to be really tight and simple to use].
OBJS protocol consists of a serialization of object properties convenient enough to be produced on a chat output so people can easily cut/paste from the history and put result into a notecard. This means that protocol tolerates the presence of extra beginning characters such as [HH:MM] Object: at the head.
I want to be as "standard" as possible and reusing work from other people here in this wiki, as well as being decently compatible with standard lsl Functions.
So here is the protocol:
// Packing lines longer than 255 bytes (limit in reading notecard) in multiple lines:
// You look for +OBJS or @OBJS so to be sure to have data of this protocol. + means the line will continue, @ means the line is finishing
..(prefix).......+OBJS......(data)...................+
..(prefix).......@OBJS.....(data)...................@
// (data) when repacked will contain a BTLT strong typed list of arguments composed by the following entries:
[-1, name // first pair is the name
[-2, desc // second pair is the description
...rest of the list // is in llSetPrimitiveParams format so to be easily fed to it to easily reconstruct the object
// MUST START with the position of the object to be rezzed for easily understanding where to rez it in reference to others
//We use BTLT to preserve the float vectors so to avoid drifting. Also it is quite tight
//Also we will try to use optimizations to be able not to specify all single faces when they refer to the same properties [NOTDONE]
Protocol consists in 3 pieces:
* "OBJS Serializer" to be put in a prim chatting main parameters, then people can cut and paste them and put in a notecard,
* "OBJS Rezzer" will read the notecard (pack it), rez a prim and linkMessage to it the serialization. A subset of the properties are handled (name, description, position, size, type, textures, colors).
* "OBJS Prim" will interpret the (packed)
The idea is that positions will be rebuilt on the top of the rezzer object (plus a displacement of 2 meters) and all the other objects will be rebuilt with reference to the first object.
Here an example (Note that the protocol will tolerate the prefixes from the chat window, like HH:MM and object name. Moreover it will support the splitting of lines longer than 240 chars using a continuation character as suffix. (Warning, while the Serializer should be OK, I'm still heavily working on the rezzer and the prim part)
[3:06] Ishtar Temple: OBJS|?!@#$|/////w!Ishtar Temple|/////g!BlueVertigo Heron|AAAABg#2QwUJ4QQlurQgQxrve|AAAABw#SQSPwQQFHrg|AAAAC$G2uvQmcwOvEelQvzUEywvzUEyg|AAAAEQ|@5748decc-f629-461c-9a36-a35a221fe21f#2PkzMzQPZmZmg#EPwAB?|AAAAEg|#SP4P4P4?P4|AAAA+
[3:06] Ishtar Temple: OBJSFg||AAAAAQ|AAAAEQ|AAAAAQ@3f530516-8709-d211-b8e9-4c609f88625d#2PkzMzQPZmZmg#EPwAB?|AAAAEg|AAAAAQ#SP4P4P4?P4|AAAAFg|AAAAAQ|AAAAAQ|AAAAEQ|AAAAAg@3f530516-8709-d211-b8e9-4c609f88625d#2PkzMzQPZmZmg#EPwAB?|AAAAEg|AAAAAg#SP4P4+
[3:06] Ishtar Temple: OBJSP4?|AAAAFg|AAAAAg|AAAAAQ|AAAAEQ|AAAAAw@3f530516-8709-d211-b8e9-4c609f88625d#2PkzMzQPZmZmg#EPwAB?|AAAAEg|AAAAAw#SP4P4P4?P4|AAAAFg|AAAAAw|AAAAAQ|AAAAEQ|AAAAB@5748decc-f629-461c-9a36-a35a221fe21f#2PkzMzQPZmZmg#EPwAB?|AAAAEg+
[3:06] Ishtar Temple: OBJS|AAAAB#SP4P4P4?|AAAAFg|AAAAB|AAAAAQ|AAAAEQ|AAAABQ@5748decc-f629-461c-9a36-a35a221fe21f#2PkzMzQPZmZmg#EPwAB?|AAAAEg|AAAABQ#SP4P4P4?P4|AAAAFg|AAAABQ|AAAAAQ|AAAACQ||#QP4?#A#SP4P4#A@
Script producing the serialization sequence (Note: I gave up using the standard serialization function from Strife Onizuka becase it was too slow and not working with textures because of a bug in sl :(
Instead I developed a personal version which emits ONLY one llGetPrimitiveParams, and works on the output to make it compatible with a llSetPrimitiveParams.
//
// December 2007
// OBJS serializer
// This script will simply say its properties on public chat allowing to pick them and use in a
// V1.0 Follows specs found on
//BTLT is part of the Combined Library.
//BTLT Version 0.1 beta
//===================================================//
// Combined Library //
// "Nov 5 2007", "04:00:34" //
// Copyright (C) 2004-2007, Strife Onizuka (cc-by) //
// http://creativecommons.org/licenses/by/3.0/ //
//===================================================//
//{
rotation stiufr(string in) //Mono Unsafe, LSO Safe, Double Unsafe
{ //--XXXXYYYYZZZZSSSS
integer raw = llBase64ToInteger(llGetSubString("AAA" + in + "AAA", 0, 5)) >> 2;
integer offset = 1;
integer len = raw & 7;
return <stiuf(llDeleteSubString(in, len + 2, 1)),
stiuf(llDeleteSubString(in, (len = (7 & (raw >> 3))) - ~(offset += len), offset)),
stiuf(llDeleteSubString(in, (len = (7 & (raw >> 6))) - ~(offset += len), offset)),
stiuf(llDeleteSubString(in, 0, offset + len)) >;
}
vector stiufv(string in) //Mono Unsafe, LSO Safe, Double Unsafe
{ //-XXXXYYYYZZZZ
integer raw = llBase64ToInteger(llGetSubString("AAAA" + in + "AA", 0, 5)) >> 2;
integer offset = 0;
integer len = raw & 7;
return <stiuf(llDeleteSubString(in, -~len, 0)),
stiuf(llDeleteSubString(in, (len = (7 & (raw >> 3))) - ~(offset += len), offset)),
stiuf(llDeleteSubString(in, 0, offset + len)) >;
}
string vfuist(vector in) //Mono Unsafe, LSO Safe, Double Unsafe
{
string buf = fuist(in.y);
integer len = llStringLength(buf);
return llGetSubString(llIntegerToBase64(((len << 3) + llStringLength(buf)) << 2), 4, 4) + (buf =
fuist(in.x)) + buf + fuist(in.z);
} //-XXXXYYYYZZZZ
string rfuist(rotation in) //Mono Unsafe, LSO Safe, Double Unsafe
{
string z = fuist(in.z);
string buf = fuist(in.y);
integer len = (llStringLength(z) << 3) | llStringLength(buf);
return llGetSubString(llIntegerToBase64(((len << 3) | llStringLength(buf)) << 2), 3, 4) + (buf =
fuist(in.x)) + buf + z +
fuist(in.s);
} //--XXXXYYYYZZZZSSSS
string ist(integer b) //Mono Safe, LSO Safe, Double Unsafe
{
string src = llIntegerToBase64(b);
integer c = 6;
do;
while ("A" == llGetSubString(src, c = ~-c, c));
return llDeleteSubString(src, -~c, 0x8000);
}
string fuist(float input) //Mono Unsafe, LSO Safe, Double Unsafe
{ //float union to base64ed integer
if (input) { //is it non zero?
integer sign = (input < 0) << 31; //the sign, but later this variable is reused to store the shift
integer exp;
if ((input = llFabs(input)) < 2.3509887016445750159374730744445e-38) //Denormalized range check & last stirde of normalized range
sign = sign | (integer) (input / 1.4012984643248170709237295832899e-45); //the math overlaps; saves cpu time.
else
sign = (0x7FFFFF & (integer) (input * (0x1000000 >> sign))) | (((exp + 126 + (sign = ((integer) input - (3 <= (input /= (float) ("0x1p" + (string) (exp -= ((exp = llFloor(llLog(input) / 0.69314718055994530941723212145818)) == 128)))))))) << 23) | sign); //extremes will error towards extremes. this yuch corrects it.
return ist(sign);
} //for grins, detect the sign on zero. it's not pretty but it works. the previous requires alot of unwinding to understand it.
if ((string) input == (string) (0.0))
return "";
return "g";
}
float stiuf(string input)
{ //base64ed integer union to float
integer buf = llBase64ToInteger(llGetSubString(input + "AAAAAA", 0, 5));
return ((float) ("0x1p" + (string) ((buf | !buf) - 150))) * ((!!(buf = (0xff & (buf >> 23))) << 23) | ((buf & 0x7fffff))) *
(1 | (buf >> 31));
} //will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.
list BTLTParse(string input)
{
list partial;
if (llStringLength(input) > 6) {
string seperators = llGetSubString(input, (0), 6);
integer pos =
([] !=
(partial =
llList2List(input +
llParseStringKeepNulls(llDeleteSubString(input, (0), 5),[],
[input =
llGetSubString(seperators, (0), (0)), llGetSubString(seperators, 1, 1),
llGetSubString(seperators, 2, 2), llGetSubString(seperators, 3, 3),
llGetSubString(seperators, 4, 4), llGetSubString(seperators, 5, 5)]),
(llSubStringIndex(seperators, llGetSubString(seperators, 6, 6)) < 6) << 1, -1)));
integer type = (0);
integer sub_pos = (0);
do {
list current = (list) (input = llList2String(partial, sub_pos = -~pos)); //TYPE_STRING || TYPE_INVALID (though we don't care about invalid)
if (!(type = llSubStringIndex(seperators, llList2String(partial, pos)))) //TYPE_INTEGER
current = (list) (llBase64ToInteger(llGetSubString(input + "AAAAAA", 0, 5)));
else if (type == 1) //TYPE_FLOAT
current = (list) (stiuf(input));
else if (type == 3) //TYPE_KEY
current = (list) ((key) (input));
else if (type == 4) //TYPE_VECTOR
current = (list) (stiufv(input));
else if (type == 5) //TYPE_ROTATION
current = (list) (stiufr(input));
partial = llListReplaceList(partial, current, pos, sub_pos);
} while ((pos = -~sub_pos) & 0x80000000);
}
return partial;
}
string BTLTDump(list input, string seperators)
{ //This function is dangerous
seperators += "|/?!@#$%^&*()_=:;~`'<>{}[],.\n\" qQxXzZ\\";
string cumulator = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + (string) (input);
integer counter = (0);
do
if (~llSubStringIndex(cumulator, llGetSubString(seperators, counter, counter)))
seperators = llDeleteSubString(seperators, counter, counter);
else
counter = -~counter;
while (counter < 6);
seperators = llGetSubString(seperators, (0), 5);
string buffer = cumulator = "";
if ((counter = (input !=[]))) {
do {
integer type = ~-llGetListEntryType(input, counter = ~-counter);
if (type == 4) //TYPE_VECTOR - 1
buffer = vfuist(llList2Vector(input, counter));
else
if (type == 5) //TYPE_ROTATION - 1
buffer = rfuist(llList2Rot(input, counter));
else
if (type == 1) //TYPE_FLOAT - 1
buffer = fuist(llList2Float(input, counter));
else
if (!type) //TYPE_INTEGER - 1
{
buffer = ist(llList2Integer(input, counter));
} else
buffer = llList2String(input, counter);
cumulator = (cumulator = llGetSubString(seperators, type, type)) + buffer + cumulator;
} while (counter);
}
return seperators + cumulator;
}
//} Combined Library
list GetAllParams()
{
list lAsk=[ PRIM_POSITION, PRIM_SIZE, PRIM_ROTATION ];
// NEXT LIST will contain
// number of pos returned by the call
// number of parameters (right after) to be pushed
list lIns=[ 1,1,PRIM_POSITION,1,1,PRIM_SIZE,1,1,PRIM_ROTATION];
integer side=llGetNumberOfSides();
integer i;
for(i=0;i<side;i++)
{
lAsk+= [PRIM_TEXTURE, i, PRIM_COLOR, i, PRIM_TEXGEN, i];
lIns+= [4,2,PRIM_TEXTURE,i,2,2,PRIM_COLOR,i,1,2,PRIM_TEXGEN,i];
}
lAsk+= [ PRIM_TYPE ];
lIns+= [ -1,1,PRIM_TYPE]; // we cannot know the length of this :(
lAsk=llGetPrimitiveParams(lAsk);
list lOut=[];
integer iAsk=0; integer iIns=0; integer iRetValues=0; integer iInsValues;
list lRetValues; list lInsValues;
while(iRetValues!=-1)
{
iRetValues=llList2Integer(lIns,iIns++);
iInsValues=llList2Integer(lIns,iIns++);
if(iRetValues>=0)
{
lInsValues=llList2List(lIns ,iIns,iIns+iInsValues-1);
iIns+=iInsValues;
lRetValues=llList2List(lAsk ,iAsk,iAsk+iRetValues-1);
iAsk+=iRetValues;
}
else
{
lInsValues=llList2List(lIns ,iIns, iIns+iInsValues-1);
lRetValues=llList2List(lAsk ,iAsk,-1);
}
lOut+=lInsValues + lRetValues;
}
lAsk=[]; lIns=[];
return lOut;
}
//Contributed by Salahzar Stenvaag
Show(string x)
{
integer WRAP=220; // to have short lines
integer len=llStringLength(x)+1+llStringLength(llGetObjectName());
while(len>WRAP)
{
//llSay(0,"finallen: "+(string)finallen);
llSay(0,"OBJS"+llGetSubString(x,0,WRAP-1)+"+");
x=llGetSubString(x,WRAP,-1);
len-=WRAP;
}
llSay(0,"OBJS"+x+"@");
}
default
{
state_entry()
{
llSay(0, "OBJS Serializer V1.0");
}
touch_start(integer total_number)
{
list lst=[-1,llGetObjectName(),-2,llGetObjectDesc() ] + GetAllParams();
Show(BTLTDump(lst,""));
//Show(llList2CSV(GetAllParams()));
}
}
OBJS Prim: script interpreting the BTLT sequence from linked rezzer
//
// December 2007
// OBJS Prim
// This script will simply interpret OBJS command received on LINK channel (BTLT syntax)
// Will implement extra commands -1: Name, -2: description
//
debug(string type,string str)
{
//llOwnerSay("::"+str);
}
//BTLT is part of the Combined Library.
//BTLT Version 0.1 beta
//===================================================//
// Combined Library //
// "Nov 5 2007", "04:00:34" //
// Copyright (C) 2004-2007, Strife Onizuka (cc-by) //
// http://creativecommons.org/licenses/by/3.0/ //
//===================================================//
//{
rotation stiufr(string in) //Mono Unsafe, LSO Safe, Double Unsafe
{ //--XXXXYYYYZZZZSSSS
integer raw = llBase64ToInteger(llGetSubString("AAA" + in + "AAA", 0, 5)) >> 2;
integer offset = 1;
integer len = raw & 7;
return <stiuf(llDeleteSubString(in, len + 2, 1)),
stiuf(llDeleteSubString(in, (len = (7 & (raw >> 3))) - ~(offset += len), offset)),
stiuf(llDeleteSubString(in, (len = (7 & (raw >> 6))) - ~(offset += len), offset)),
stiuf(llDeleteSubString(in, 0, offset + len)) >;
}
vector stiufv(string in) //Mono Unsafe, LSO Safe, Double Unsafe
{ //-XXXXYYYYZZZZ
integer raw = llBase64ToInteger(llGetSubString("AAAA" + in + "AA", 0, 5)) >> 2;
integer offset = 0;
integer len = raw & 7;
return <stiuf(llDeleteSubString(in, -~len, 0)),
stiuf(llDeleteSubString(in, (len = (7 & (raw >> 3))) - ~(offset += len), offset)),
stiuf(llDeleteSubString(in, 0, offset + len)) >;
}
string vfuist(vector in) //Mono Unsafe, LSO Safe, Double Unsafe
{
string buf = fuist(in.y);
integer len = llStringLength(buf);
return llGetSubString(llIntegerToBase64(((len << 3) + llStringLength(buf)) << 2), 4, 4) + (buf =
fuist(in.x)) + buf + fuist(in.z);
} //-XXXXYYYYZZZZ
string rfuist(rotation in) //Mono Unsafe, LSO Safe, Double Unsafe
{
string z = fuist(in.z);
string buf = fuist(in.y);
integer len = (llStringLength(z) << 3) | llStringLength(buf);
return llGetSubString(llIntegerToBase64(((len << 3) | llStringLength(buf)) << 2), 3, 4) + (buf =
fuist(in.x)) + buf + z +
fuist(in.s);
} //--XXXXYYYYZZZZSSSS
string ist(integer b) //Mono Safe, LSO Safe, Double Unsafe
{
string src = llIntegerToBase64(b);
integer c = 6;
do;
while ("A" == llGetSubString(src, c = ~-c, c));
return llDeleteSubString(src, -~c, 0x8000);
}
string fuist(float input) //Mono Unsafe, LSO Safe, Double Unsafe
{ //float union to base64ed integer
if (input) { //is it non zero?
integer sign = (input < 0) << 31; //the sign, but later this variable is reused to store the shift
integer exp;
if ((input = llFabs(input)) < 2.3509887016445750159374730744445e-38) //Denormalized range check & last stirde of normalized range
sign = sign | (integer) (input / 1.4012984643248170709237295832899e-45); //the math overlaps; saves cpu time.
else
sign = (0x7FFFFF & (integer) (input * (0x1000000 >> sign))) | (((exp + 126 + (sign = ((integer) input - (3 <= (input /= (float) ("0x1p" + (string) (exp -= ((exp = llFloor(llLog(input) / 0.69314718055994530941723212145818)) == 128)))))))) << 23) | sign); //extremes will error towards extremes. this yuch corrects it.
return ist(sign);
} //for grins, detect the sign on zero. it's not pretty but it works. the previous requires alot of unwinding to understand it.
if ((string) input == (string) (0.0))
return "";
return "g";
}
float stiuf(string input)
{ //base64ed integer union to float
integer buf = llBase64ToInteger(llGetSubString(input + "AAAAAA", 0, 5));
return ((float) ("0x1p" + (string) ((buf | !buf) - 150))) * ((!!(buf = (0xff & (buf >> 23))) << 23) | ((buf & 0x7fffff))) *
(1 | (buf >> 31));
} //will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.
list BTLTParse(string input)
{
list partial;
if (llStringLength(input) > 6) {
string seperators = llGetSubString(input, (0), 6);
integer pos =
([] !=
(partial =
llList2List(input +
llParseStringKeepNulls(llDeleteSubString(input, (0), 5),[],
[input =
llGetSubString(seperators, (0), (0)), llGetSubString(seperators, 1, 1),
llGetSubString(seperators, 2, 2), llGetSubString(seperators, 3, 3),
llGetSubString(seperators, 4, 4), llGetSubString(seperators, 5, 5)]),
(llSubStringIndex(seperators, llGetSubString(seperators, 6, 6)) < 6) << 1, -1)));
integer type = (0);
integer sub_pos = (0);
do {
list current = (list) (input = llList2String(partial, sub_pos = -~pos)); //TYPE_STRING || TYPE_INVALID (though we don't care about invalid)
if (!(type = llSubStringIndex(seperators, llList2String(partial, pos)))) //TYPE_INTEGER
current = (list) (llBase64ToInteger(llGetSubString(input + "AAAAAA", 0, 5)));
else if (type == 1) //TYPE_FLOAT
current = (list) (stiuf(input));
else if (type == 3) //TYPE_KEY
current = (list) ((key) (input));
else if (type == 4) //TYPE_VECTOR
current = (list) (stiufv(input));
else if (type == 5) //TYPE_ROTATION
current = (list) (stiufr(input));
partial = llListReplaceList(partial, current, pos, sub_pos);
} while ((pos = -~sub_pos) & 0x80000000);
}
return partial;
}
string BTLTDump(list input, string seperators)
{ //This function is dangerous
seperators += "|/?!@#$%^&*()_=:;~`'<>{}[],.\n\" qQxXzZ\\";
string cumulator = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + (string) (input);
integer counter = (0);
do
if (~llSubStringIndex(cumulator, llGetSubString(seperators, counter, counter)))
seperators = llDeleteSubString(seperators, counter, counter);
else
counter = -~counter;
while (counter < 6);
seperators = llGetSubString(seperators, (0), 5);
string buffer = cumulator = "";
if ((counter = (input !=[]))) {
do {
integer type = ~-llGetListEntryType(input, counter = ~-counter);
if (type == 4) //TYPE_VECTOR - 1
buffer = vfuist(llList2Vector(input, counter));
else
if (type == 5) //TYPE_ROTATION - 1
buffer = rfuist(llList2Rot(input, counter));
else
if (type == 1) //TYPE_FLOAT - 1
buffer = fuist(llList2Float(input, counter));
else
if (!type) //TYPE_INTEGER - 1
{
buffer = ist(llList2Integer(input, counter));
} else
buffer = llList2String(input, counter);
cumulator = (cumulator = llGetSubString(seperators, type, type)) + buffer + cumulator;
} while (counter);
}
return seperators + cumulator;
}
//} Combined Library
default
{
state_entry()
{
llListen(0,"","",NULL_KEY);
}
link_message(integer sendernum, integer num, string message, key id)
{
debug("MSG","Message: "+message);
llResetTime();
list lOBJS=BTLTParse(message);
//interpret(message);
debug("MSG","Converted: "+llList2CSV(lOBJS));
// interpret and strip -1 and -2 (first positions only!!!)
if(llList2Integer(lOBJS,0)==-1)
{
llSetObjectName(llList2String(lOBJS,1));
lOBJS=llDeleteSubList(lOBJS, 0, 1);
}
if(llList2Integer(lOBJS,0)==-2)
{
llSetObjectDesc(llList2String(lOBJS,1));
lOBJS=llDeleteSubList(lOBJS, 0, 1);
}
// what's next is simply the setPrimitiveParams
llSetPrimitiveParams(lOBJS);
llOwnerSay("Done. (Used: "+(string)llGetAndResetTime()+" secs");
}
}
Script rezzing the object and communicating to it the proper commands (also repacks the split lines from the notecard)
//
// December 2007
// OBJS rezzer
// This script will rez an object reading the BTLT serialization build by the serializer
string sNOTECARD="SCENE";
integer iDEBUG=0;
vector vSTARTINGPOINT;
string sCONTINUATION="";
integer iREZOBJ=TRUE;
list lOBJS;
string sOBJS;
integer iFIRST=1;
vector vFIRSTOBJECT;
string sPRIMNAME="OBJS Prim";
vector vPOS;
//BTLT is part of the Combined Library.
//BTLT Version 0.1 beta
//===================================================//
// Combined Library //
// "Nov 5 2007", "04:00:34" //
// Copyright (C) 2004-2007, Strife Onizuka (cc-by) //
// http://creativecommons.org/licenses/by/3.0/ //
//===================================================//
//{
rotation stiufr(string in) //Mono Unsafe, LSO Safe, Double Unsafe
{ //--XXXXYYYYZZZZSSSS
integer raw = llBase64ToInteger(llGetSubString("AAA" + in + "AAA", 0, 5)) >> 2;
integer offset = 1;
integer len = raw & 7;
return <stiuf(llDeleteSubString(in, len + 2, 1)),
stiuf(llDeleteSubString(in, (len = (7 & (raw >> 3))) - ~(offset += len), offset)),
stiuf(llDeleteSubString(in, (len = (7 & (raw >> 6))) - ~(offset += len), offset)),
stiuf(llDeleteSubString(in, 0, offset + len)) >;
}
vector stiufv(string in) //Mono Unsafe, LSO Safe, Double Unsafe
{ //-XXXXYYYYZZZZ
integer raw = llBase64ToInteger(llGetSubString("AAAA" + in + "AA", 0, 5)) >> 2;
integer offset = 0;
integer len = raw & 7;
return <stiuf(llDeleteSubString(in, -~len, 0)),
stiuf(llDeleteSubString(in, (len = (7 & (raw >> 3))) - ~(offset += len), offset)),
stiuf(llDeleteSubString(in, 0, offset + len)) >;
}
string vfuist(vector in) //Mono Unsafe, LSO Safe, Double Unsafe
{
string buf = fuist(in.y);
integer len = llStringLength(buf);
return llGetSubString(llIntegerToBase64(((len << 3) + llStringLength(buf)) << 2), 4, 4) + (buf =
fuist(in.x)) + buf + fuist(in.z);
} //-XXXXYYYYZZZZ
string rfuist(rotation in) //Mono Unsafe, LSO Safe, Double Unsafe
{
string z = fuist(in.z);
string buf = fuist(in.y);
integer len = (llStringLength(z) << 3) | llStringLength(buf);
return llGetSubString(llIntegerToBase64(((len << 3) | llStringLength(buf)) << 2), 3, 4) + (buf =
fuist(in.x)) + buf + z +
fuist(in.s);
} //--XXXXYYYYZZZZSSSS
string ist(integer b) //Mono Safe, LSO Safe, Double Unsafe
{
string src = llIntegerToBase64(b);
integer c = 6;
do;
while ("A" == llGetSubString(src, c = ~-c, c));
return llDeleteSubString(src, -~c, 0x8000);
}
string fuist(float input) //Mono Unsafe, LSO Safe, Double Unsafe
{ //float union to base64ed integer
if (input) { //is it non zero?
integer sign = (input < 0) << 31; //the sign, but later this variable is reused to store the shift
integer exp;
if ((input = llFabs(input)) < 2.3509887016445750159374730744445e-38) //Denormalized range check & last stirde of normalized range
sign = sign | (integer) (input / 1.4012984643248170709237295832899e-45); //the math overlaps; saves cpu time.
else
sign = (0x7FFFFF & (integer) (input * (0x1000000 >> sign))) | (((exp + 126 + (sign = ((integer) input - (3 <= (input /= (float) ("0x1p" + (string) (exp -= ((exp = llFloor(llLog(input) / 0.69314718055994530941723212145818)) == 128)))))))) << 23) | sign); //extremes will error towards extremes. this yuch corrects it.
return ist(sign);
} //for grins, detect the sign on zero. it's not pretty but it works. the previous requires alot of unwinding to understand it.
if ((string) input == (string) (0.0))
return "";
return "g";
}
float stiuf(string input)
{ //base64ed integer union to float
integer buf = llBase64ToInteger(llGetSubString(input + "AAAAAA", 0, 5));
return ((float) ("0x1p" + (string) ((buf | !buf) - 150))) * ((!!(buf = (0xff & (buf >> 23))) << 23) | ((buf & 0x7fffff))) *
(1 | (buf >> 31));
} //will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.
list BTLTParse(string input)
{
list partial;
if (llStringLength(input) > 6) {
string seperators = llGetSubString(input, (0), 6);
integer pos =
([] !=
(partial =
llList2List(input +
llParseStringKeepNulls(llDeleteSubString(input, (0), 5),[],
[input =
llGetSubString(seperators, (0), (0)), llGetSubString(seperators, 1, 1),
llGetSubString(seperators, 2, 2), llGetSubString(seperators, 3, 3),
llGetSubString(seperators, 4, 4), llGetSubString(seperators, 5, 5)]),
(llSubStringIndex(seperators, llGetSubString(seperators, 6, 6)) < 6) << 1, -1)));
integer type = (0);
integer sub_pos = (0);
do {
list current = (list) (input = llList2String(partial, sub_pos = -~pos)); //TYPE_STRING || TYPE_INVALID (though we don't care about invalid)
if (!(type = llSubStringIndex(seperators, llList2String(partial, pos)))) //TYPE_INTEGER
current = (list) (llBase64ToInteger(llGetSubString(input + "AAAAAA", 0, 5)));
else if (type == 1) //TYPE_FLOAT
current = (list) (stiuf(input));
else if (type == 3) //TYPE_KEY
current = (list) ((key) (input));
else if (type == 4) //TYPE_VECTOR
current = (list) (stiufv(input));
else if (type == 5) //TYPE_ROTATION
current = (list) (stiufr(input));
partial = llListReplaceList(partial, current, pos, sub_pos);
} while ((pos = -~sub_pos) & 0x80000000);
}
return partial;
}
string BTLTDump(list input, string seperators)
{ //This function is dangerous
seperators += "|/?!@#$%^&*()_=:;~`'<>{}[],.\n\" qQxXzZ\\";
string cumulator = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + (string) (input);
integer counter = (0);
do
if (~llSubStringIndex(cumulator, llGetSubString(seperators, counter, counter)))
seperators = llDeleteSubString(seperators, counter, counter);
else
counter = -~counter;
while (counter < 6);
seperators = llGetSubString(seperators, (0), 5);
string buffer = cumulator = "";
if ((counter = (input !=[]))) {
do {
integer type = ~-llGetListEntryType(input, counter = ~-counter);
if (type == 4) //TYPE_VECTOR - 1
buffer = vfuist(llList2Vector(input, counter));
else
if (type == 5) //TYPE_ROTATION - 1
buffer = rfuist(llList2Rot(input, counter));
else
if (type == 1) //TYPE_FLOAT - 1
buffer = fuist(llList2Float(input, counter));
else
if (!type) //TYPE_INTEGER - 1
{
buffer = ist(llList2Integer(input, counter));
} else
buffer = llList2String(input, counter);
cumulator = (cumulator = llGetSubString(seperators, type, type)) + buffer + cumulator;
} while (counter);
}
return seperators + cumulator;
}
debug(string module,string str)
{
if(iDEBUG==TRUE) llOwnerSay("DEBUG("+llGetScriptName()+"): ["+module+"] "+str);
}
integer iNOTECARDLINE=0;
readnext()
{
iNOTECARDLINE++;
llGetNotecardLine(sNOTECARD, iNOTECARDLINE);
}
readscene()
{
// read notecard
iNOTECARDLINE=0;
llGetNotecardLine(sNOTECARD,iNOTECARDLINE);
//debug("llGetNotecardLine returned: "+(string)k);
}
// Rezzing an object (called when encountered "F" part of notecard reading
// will continue on object_rez event
rezz()
{
debug("REZZ","Trying to rez the object");
// vPOS decides where to rez the object
// if object will be more than 10m far then must rez it at starting point
// also will try to keep notice of coordinates of FIRSTOBJECT to have all
// subsequent object be rezzed relative to it
vPOS=vSTARTINGPOINT; // start from starting point
vector vOriginalPos=(vector)llList2String(lOBJS,5); // 5th element is the position!!!
if(iFIRST==TRUE)
{
iFIRST=FALSE;
vFIRSTOBJECT=vOriginalPos;
}
else
{
vPOS=vOriginalPos-vFIRSTOBJECT+vSTARTINGPOINT;
}
vector vRezPos=vPOS;
if(llVecDist(vPOS,llGetPos()) > 10) vRezPos=vSTARTINGPOINT;
debug("REZZ"," Final pos at "+(string)vPOS+" initial rez at "+(string)vRezPos);
// update list and string representation
//lOBJS=llListReplaceList(lOBJS,[ vPOS ],5,5);
//sOBJS=BTLTDump(lOBJS,"");
if(iREZOBJ==TRUE) llRezObject(sPRIMNAME,vRezPos,<0,0,0>,ZERO_ROTATION,0);
else
{
// for debug purposes
// talk only to this prim with current serialization
llMessageLinked(2,0,sOBJS,"");
// makes elaboration progress on next line
readnext();
}
// calmly wait until the object get rezzed (object_rez)
return;
}
integer already=0;
default
{
state_entry()
{
string scriptname=llGetScriptName();
list pieces=llParseString2List(scriptname,[" ","-","."],[]);
string lastpiece=llList2String(pieces,-1);
if(llSubStringIndex(llToUpper(scriptname),"DEBUG")>=0) iDEBUG=TRUE;
//debug("INIT"," freeMemory: "+(string)llGetFreeMemory());
vSTARTINGPOINT=llGetPos();
vSTARTINGPOINT.z+=2; // starts 2 m above me
if(iREZOBJ==TRUE) llRequestPermissions(llGetOwner(),PERMISSION_CHANGE_LINKS);
}
// Wait until the object is rezzed
object_rez(key id)
{
// create link with this object so to be able to talk with it very easily without
// llSay and listening. Create link is slow
debug("OBJECT_REZ","Linking and setting parameters");
llCreateLink(id,TRUE);
// talk only to this prim with current serialization
llMessageLinked(2,0,sOBJS,"");
// clean out memory
sOBJS=""; lOBJS=[];
// makes elaboration progress on next line
readnext();
}
touch_start(integer total_number)
{
// now reading scene
if(iREZOBJ && already)
{
llSay(0,"Cannot rerez. Unlink delete, reset if you really want to");
return;
}
already=1;
sCONTINUATION="";
readscene();
//llRezObject("Camaleont Prim",llGetPos()+<0,0,2>,ZERO_VECTOR,ZERO_ROTATION,1000);
//llSay(0,"Created prim just over me");
}
// notecard reader
dataserver(key id, string sMessage)
{
//debug("dataserver received data: "+data);
string sMethod="DATASERVER";
if (sMessage != EOF)
{
debug(sMethod,"Reading scene line #"+(string)iNOTECARDLINE);
if(llGetSubString(sMessage,0,0)!="#") // ignore comments
{
// try to decode one string. Eliminate starting :
integer iFound=llSubStringIndex(sMessage,"OBJS");
if(iFound<0)
{
llSay(0,"Invalid OBJS line at line#"+(string)iNOTECARDLINE);
return;
}
debug(sMethod,"read message "+(string)llStringLength(sMessage)+":"+sMessage);
//
// handle continuation character
string last=llGetSubString(sMessage,-1,-1);
if(last=="+")
{
debug(sMethod,"Found continuation, caching it..");
sCONTINUATION+=llGetSubString(sMessage,iFound+4,-2);
readnext();
return;
}
if(last!="@")
{
llSay(0,"Invalid OBJS line (missing @) at line#"+(string)iNOTECARDLINE);
return;
}
// we are at last continuation or no continuation at all
sOBJS=sCONTINUATION+llGetSubString(sMessage,iFound+4,-2);
sMessage="";
debug(sMethod,"Found final line: "+sOBJS);
sCONTINUATION="";
lOBJS=BTLTParse(sOBJS);
rezz();
return; // when rezzing, notecard reading will be deferred
}
// go to next line
readnext();
//debug("tried to read line "+(string)notecardline+" key "+(string)k);
return;
}
else
{
llSay(0,"Finished rezzing the scene unlinking me (the root)");
if(iREZOBJ==TRUE) llBreakLink(LINK_ROOT);
// remove the joker from the linkset
//llSleep(5); // wait some seconds before unlinking root
}
}
}