Difference between revisions of "Object to Data v1.4"

From Second Life Wiki
Jump to navigation Jump to search
m (<lsl> tag to <source>)
 
(35 intermediate revisions by 7 users not shown)
Line 1: Line 1:
(http://www.gnu.org/copyleft/fdl.html) in the spirit of which this script is GPL'd. Copyright (C) 2007 [[Xaviar Czervik]]
{{LSL Header}}


(This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details.
<div style="float:right;">__TOC__</div>


<div id="box">
== Introduction ==
<div style="padding: 0.5em;">


I took the list serialize list code from someone, and it was posted on a wiki somewhere... (If this was you, please add your name so I can credit you).
(http://www.gnu.org/copyleft/fdl.html) in the spirit of which this script is GPL'd. Copyright (C) 2011 [[User:Xaviar Czervik|Xaviar Czervik]]


I'm not one for writing documentation, so I apologize in advance for the confusion of the following.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details.


This is one of the projects that I've spent a significant amount of time on, and that I've uploaded to the wiki.
It's purpose is simple: it allows anyone to transfer objects through text. This may seem trivial, but it has several advantages to it.
Below are a few ways in which this script can be used:
# The Internet. There is no way (yet) to go on to the Internet and request an object be sent to your SL character. This solves this issue. One could post the data on to the Internet, and allow people to copy the text down to SL, run this script, and obtain an object from it.
# Management Scripts. There is no way to have an object create a totally new object. And objects can only call llGiveInventory on other objects when in the same sim. This solves the issue by allowing objects to use email to send data, which can then be turned into an object.


Here is a map of the objects:
<pre>
ObjectMakerFromNotecard
|
|---Setup (Notecard)
|---Main (Script)
|---Replicated (Object)
|---|
|---|---Main (Script)


</div></div>


ObjectDataSaver
|
|---Main (Script)
|---ListenToReplicate(Object)
|---|
|---|---Main (Script)
</pre>


<div id="box">


The following code goes in an object called Replicated. It is needed for the ObjectMakerFromNotecard.
== Revision History ==
<div style="padding: 0.5em;">


<pre>
Version 1.4:
string SERIALIZER_DELIMITER = "$!#";
# Doesn't require rerezing.
integer num = 0;
# Simpler to turn object to data.
# Backwards compatible with 1.3.
# Bug fix from [[User:KimAnn Galaxy|KimAnn Galaxy]].


list unserializeList(string serialized_data) {
Version 1.3:
    // TODO: add some checking in-case we encounter a poorly formed serialization
# 100% success rate.
    //      consider using the same mem-packing list pushing technique used above
# Added PRIM_GLOW
    //      (want to run performace tests first)
# Added functionality for multiple notecards (for really big objects).
    list result = [];
# Improved efficiency; removed the delink/relink sections.
    list t;
    list l = llParseStringKeepNulls(serialized_data, [SERIALIZER_DELIMITER], []);


    string item;
Version 1.2:
    integer i = (l != []);//This is a hack, it gets list lenght.
# Around 95% success rate.
    integer type = 0;
# Changed to almost all llGetPrimitiveParams calls.
    do
# Added more than just ten different descriptors of an object.
    {
# Removed freakish menu-controlled system.
        if((type = (integer)(item = llList2String(l, (i=~-i)))))
# Script need only be dropped in root object, not every prim.
        {//Little error checking (also takes care of null strings).
 
            integer p = llSubStringIndex(item, ",");
Version 1.1:
            item = llDeleteSubString(item, 0, p);
# Made process simpler: removed replicate from listen.
            // How about those switch statements, Lindens???
# Changed to work (slightly) better.
            if (TYPE_INTEGER == type)
 
                t = [(integer)item];
Version 1.0:
            else if (TYPE_FLOAT == type)
# First release.
                t = [(float)item];
 
            else if (TYPE_STRING == type)
 
                t = [item];
</div></div>
            else if (TYPE_KEY == type)
 
                t = [(key)item];
<div id="box">
            else
== Instructions ==
            {
<div style="padding: 0.5em;">
                if (TYPE_ROTATION ^ type)// if (TYPE_VECTOR == type)
 
                    t = [(vector)("<" + item + ">")];
Thanks to [[User:DMC Zsigmond|DMC Zsigmond]] for the original documentation shown below, modified for Version 1.4 by Xaviar.
                else// if (TYPE_ROTATION == type)
 
                    t = [(rotation)("<" + item + ">")];
-------------------------------------------------------------
            }
 
            //when dealing with very long lists it might be advantagous to use the commented out line instead.
Check List (Parts Required):
            //result = [result = t] + result;
 
            result = t + result;
# 1 x Script, named: "Object to Data"
# 1 x Script, named: "Holo Script"
# 1 x Object, named: "HoloBox"
# 1 x Notecard, named: "Data_Default"
 
Plus,
# 1 x Any Object you wish to use these scripts with to turn into data, or re-rez it back again.
 
Part 1/3 - Creating Scripts and Assembling Objects
-------------------------------------------------------------
# Open your "Inventory", and create a new folder, called: "Object to Data v1.4".
# Under the new "Object to Data v1.4" folder, right-click and create a new script, named "Data To Object". Then copy and paste the "Data To Object" LSL code on this wiki page into the "Data To Object" script you just created under your "Object to Data v1.4" folder. Click on "Save" to compile the "Data to Object" script,  and then after that "Data to Object" script has successfully compiled, close the script.
# Repeat Step 2, for the "Holo Script" and "Object to Data" scripts.
# Create a new Object on the ground, and then right-click on it, and select "Edit".
# Under the "General" tab, name the Object you just created as, "HoloBox". This object must be inserted into your "Data to Object" object in future to supply the prim that will be converted back into the original Object you once sort to have turned into text/notecard data.
# Go the the "Content" tab of the "HoloBox" prim, to view its contents.
# Drag'n'drop the "Holo Script" script under the "Object to Data v1.4" folder in your Inventory, to the "Contents" folder of the "HoloBox" object you have selected.
# Right-click the "HoloBox" object, and select "Take", to take the object back into your inventory.
# Create a new Object on the ground, right-click it, and select "Edit".
# Under the "General" tab, name the Object you just created as, "Data to Object". (not necessary)
# Go the the "Content" tab of the "Data to Object" prim, to view its contents.
# Drag'n'drop the "Data To Object" script under the "Object to Data v1.4" folder in your Inventory, to the "Contents" folder of the "Data to Object" object you have selected.
# Drag'n'drop the "HoloBox" object you have either in your "Objects" folder in your Inventory or under the "Object to Data v1.4" folder in your Inventory, to the "Contents" folder of the "Data to Object" object you have selected.
 
You should now have two things:
# An object named "Data to Object" which contains (1) a script named "Data to Object" (found below) and (2) an object named HoloBox. Holobox should contain a script named "Holo Script" (found below).
# A script named "Object to Data".
 
 
Part 2/3 - Convert an Object to Data
-------------------------------------------------------------
 
# Select an Object you wish to turn into data (NB. perhaps start with a single object which is relatively simple).
# Rez the Object you wish to turn into data on the ground.
# Right-click the Object you wish to turn into data, and select edit. Then click on the "Content" tab to view its contents.
# Now drag'n'drop the "Object to Data" script you created in your Inventory, from your "Object to Data v1.4" folder into the "Contents" folder of the Object you wish to turn into data.
# Wait a few moments (or possibly a minute or two) until your Local Chat Window outputs the Object's data.
# Copy the text data from your Local Chat Window (including the time code) from the very beginning to the very end.
# Return to your Inventory folder named, "Object to Data v1.4", and then right-click and create a new note (i.e. notecard) called, "Data_Default".
# Paste the Object code data copied from your Local Chat Window to the "Data_Default" notecard, and then "Save" it. This notecard now holds the data information for your Object.
# If the information does not fit in to one notecard, the first object stays named "Data_Default", however name the second notecard "Data_Default 1", the third "Data_Default 2", etc.
 
The entire object has now been turned into data. It can now be transferred through many means not usually possible, including a text file on the Internet.
 
 
Part 3/3 Instructions to convert Data to Object
--------------------------------------------------------
 
# Rez the "Data to Object" object from the "Object to Data v1.4" folder. (Move the object some meters above the ground, since it will silently fail and hang if later if it attempts to rez objects slightly below ground level!)
# Right-click the Object you wish to turn into data, and select edit. Go to the "Contents" tab of the "Data to Object" object.
# Drag'n'drop the "Data_Default" notecard(s) to the object.
# Now touch the object, "Data to Object" to begin rebuilding the original Object that you had turned into data.
# View your Local Chat window for confirm of the build "Start", and notification when the build is "Done".
 
The original Object you sort to have turned into data will now rez as a new object above the "Data to Object" prim.
 
This object will be empty, ready for use as intended.
 
 
 
</div></div>
 
 
<div id="box">
 
== Data To Object (Script) ==
<div style="padding: 0.5em;">
 
<source lang="lsl2">
string name = "Data_Default";
 
integer numberOfNotes;
key    qid;
integer line = 0;
integer lineTotal = 0;
integer prims;
list    totalLines;
integer totalLinesTotal;
 
string hexDigits = "0123456789ABCDEF";
 
integer rand;
 
integer whichNote;
 
vector start;
 
get() {
    if (whichNote == 0) {
        qid = llGetNotecardLine(name, line);
    } else {
        qid = llGetNotecardLine(name + " " + (string)whichNote, line);
    }
       
}
 
rezObj(vector pos, string num) {
    while (llVecDist(llGetPos(), start-pos) > .001) llSetPos(start-pos);
    llRezObject("HoloBox", llGetPos(), <0,0,0>, <0,0,0,0>, hex2int(num) + rand);
}


        }
integer hex2int(string hex) {
     }while(i);
    integer p1 = llSubStringIndex(hexDigits, llGetSubString(hex, 0, 0));
     return result;
    integer p2 = llSubStringIndex(hexDigits, llGetSubString(hex, 1, 1));
     integer data = p2 + (p1 << 4);
     return data;
}
}


default {
default {
     on_rez(integer i) {
     touch_start(integer i) {
         llListen(i, "", "", "");
         llSetText("Starting Up... (1/4)", <1,1,1>, 1);
        numberOfNotes = llGetInventoryNumber(INVENTORY_NOTECARD);
       
        qid = llGetNumberOfNotecardLines(name);
     }
     }
     listen(integer i, string n, key id, string m) {
     dataserver(key queryId, string data) {
         if (llGetOwnerKey(id) == llGetOwner()) {
         if (queryId == qid) {
             list l = unserializeList(m);
             totalLines += data;
             num++;
            totalLinesTotal += (integer)data;
             if (num == 1) {
             ++whichNote;
                 list l2 = [PRIM_TYPE] + l;
             if (whichNote < numberOfNotes) {
                 llSetPrimitiveParams(l2);
                 llOwnerSay(name + " " + (string)whichNote);
                 qid = llGetNumberOfNotecardLines(name + " " + (string)whichNote);
            } else {
                state rez;
             }
             }
             if (num == 2) {
        }
                 llSetColor((vector)m, ALL_SIDES);
    }
}
 
state rez {
    state_entry() {
        start = llGetPos();
        rand = (integer)llFrand(0x6FFFFFFF) + 0x10000000;
        llOwnerSay("Start");
        line = 0;
        qid = llGetNotecardLine(name, line);
        whichNote = 0;
    }
    dataserver(key queryId, string data) {
        if (queryId == qid)  {
             if (data != EOF) {
                data = llList2String(llParseString2List(data, ["-----: "], []), 1);
                if (llGetSubString(data, 0, 3) == "#NEW") {
                    string num = llGetSubString(data, 5, 6);
                    vector pos = (vector)llGetSubString(data, 8, -1);
                    rezObj(pos, num);
                }
                llSetText("Rezing Prims: " + (string)((integer)(100*lineTotal/totalLinesTotal)) + "% (2/4)", <1,1,1>, 1);
                line += 1;
                lineTotal += 1;
                get();
            } else {
                 whichNote++;
                if (whichNote < numberOfNotes) {
                    line = 0;
                    get();
                } else {
                    state run;
                }
             }
             }
             if (num == 3) {
        }
                 llSetRot((rotation)m);
    }
}
 
state run {
    state_entry() {
        line = 0;
        lineTotal = 0;
        qid = llGetNotecardLine(name, line);
        whichNote = 0;
    }
    dataserver(key queryId, string data) {
        if (queryId == qid)  {
             if (data != EOF) {
                data = llList2String(llParseString2List(data, ["-----: "], []), 1);
                if (llGetSubString(data, 0, 3) != "#NEW") {
                    list parts = llParseString2List(data, [":::"], []);
                    integer prim = hex2int(llList2String(parts, 0));
                    llRegionSay(rand + prim, llList2String(parts, 1));
                }
                 llSetText("Sending Data: " + (string)((integer)(100*lineTotal/totalLinesTotal)) + "% (3/4)", <1,1,1>, 1);
                line += 1;
                lineTotal += 1;
                get();
            } else {
                whichNote++;
                if (whichNote < numberOfNotes) {
                    line = 0;
                    get();
                } else {
                    llSetText("Cleaning Up... (4/4)", <1,1,1>, 1);
                    integer i = 0;
                    while (i < 256) {
                        llRegionSay(rand + i, "Finish");
                        ++i;
                    }
                    llSetTimerEvent(3);
                }
             }
             }
            if (num == 4) {
                llSetScale((vector)m);
            }
            if (num == 5) {
                llSetTexture(m, ALL_SIDES);
            }
            if (num == 6) {
                vector t = (vector)m;
                llScaleTexture(t.x, t.y, ALL_SIDES);
            }
            if (num == 7) {
                vector t = (vector)m;
                llOffsetTexture(t.x, t.y, ALL_SIDES);
            }
            if (num == 8) {
                llRotateTexture((integer)m, ALL_SIDES);
            }
            if (num == 9) {
                list l2 = [PRIM_BUMP_SHINY, ALL_SIDES] + l;
                llSetPrimitiveParams(l2);
            }
            if (num == 10) {
                while (llVecDist(llGetPos(), (vector)m) > .1) llSetPos((vector)m);
                llRemoveInventory(llGetScriptName());
            }
           
           
           
         }
         }
    }
    timer() {
        llSetText("Finished.", <1,1,1>, 1);
        llOwnerSay("Done");
        llSetTimerEvent(0);
        llResetScript();
     }
     }
}
}
</pre>
</source>
 
</div></div>




The following object is called ObjectMakerFromNotecard. It has the following script and a note called Setup. (The data for setup will be given later).
<div id="box">
== Object To Data (Script) ==
<div style="padding: 0.5em;">
<source lang="lsl2">
string hexc="0123456789ABCDEF";//faster
 
string Float2Hex(float input)
{// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html
    if((integer)input != input)//LL screwed up hex integers support in rotation & vector string typecasting
    {//this also keeps zero from hanging the zero stripper.
        float unsigned = llFabs(input);//logs don't work on negatives.
        integer exponent = llFloor(llLog(unsigned) / 0.69314718055994530941723212145818);//floor(log2(b)) + rounding error
        integer mantissa = (integer)((unsigned / (float)("0x1p"+(string)(exponent -= (exponent == 128)))) * 0x1000000);//shift up into integer range
        integer index = (integer)(llLog(mantissa & -mantissa) / 0.69314718055994530941723212145818);//index of first 'on' bit
        string str = "p" + (string)((exponent += index) - 24);
        mantissa = mantissa >> index;
        do
            str = llGetSubString(hexc,15&mantissa,15&mantissa) + str;
        while(mantissa = mantissa >> 4);
        if(input < 0)
            return "-0x" + str;
        return "0x" + str;
    }//integers pack well so anything that qualifies as an integer we dump as such, supports netative zero
    return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
}


<pre>
key gSetupQueryId;
integer gSetupNotecardLine;
string  gSetupNotecardName = "Setup";


string safeVector(vector v) {
    return "<"+safeFloat(v.x)+","+safeFloat(v.y)+","+safeFloat(v.z)+">";
}


readSettingsNotecard() {
string safeRotation(rotation v) {
  gSetupNotecardLine = 0;
    return "<"+safeFloat(v.x)+","+safeFloat(v.y)+","+safeFloat(v.z)+","+safeFloat(v.s)+">";
  gSetupQueryId = llGetNotecardLine(gSetupNotecardName,gSetupNotecardLine);  
}
}


string safeFloat(float f) {
    return Float2Hex(f);
}


default {
string list2string(list l) {
     state_entry() {
     string ret;
        readSettingsNotecard();
    integer len = llGetListLength(l);
     }
    integer i;
     dataserver(key queryId, string data) {
      
         if(queryId == gSetupQueryId) {
     while (i < len) {
            if(data != EOF) {
         integer type = llGetListEntryType(l, i);
                integer f = (integer)llFrand(10000) - 10000;
        if (type == 1) {
                llRezObject("Replicated", llGetPos() + <0,0,2>, <0,0,0>, <0,0,0,0>, f);
            ret += "#I"+(string)llList2Integer(l, i);
                list lis = llParseString2List(data, ["-=!!=-"], []);
        } else if (type == 2) {
                integer i = 0;
            ret += "#F"+safeFloat(llList2Float(l, i));
                while (i < llGetListLength(lis)) {
        } else if (type == 3) {
                    llSay(f, llList2String(lis, i));
            ret += "#S"+llList2String(l, i);
                    i++;
        } else if (type == 4) {
                }
            ret += "#K"+(string)llList2Key(l, i);
                gSetupQueryId = llGetNotecardLine(gSetupNotecardName,++gSetupNotecardLine);  
        } else if (type == 5) {
            } else {
            ret += "#V"+safeVector(llList2Vector(l, i));      
                state running;  
        } else if (type == 6) {
            }
            ret += "#R"+safeRotation(llList2Rot(l, i));
         }
         }
    }       
        ret += "-=-";
    changed(integer change) {
         i++;
         if (change&CHANGED_INVENTORY)
            llResetScript();
     }
     }
    return ret;
}
}


state running {
integer LINK_NUM = 0;
     changed(integer change) {
 
         if (change&CHANGED_INVENTORY)
string getTexture() {
             llResetScript();
     string ret;
   
    integer sides = llGetLinkNumberOfSides(LINK_NUM);
   
    integer same = 1;
    list texture;
    list repeat;
    list offset;
    list rot;
   
    integer i = 0;
    while (i < sides) {
        list side  = llGetLinkPrimitiveParams(LINK_NUM, [PRIM_TEXTURE, i]);
        texture += llList2String(side, 0);
        repeat  += llList2Vector(side, 1);
        offset  += llList2Vector(side, 2);
        rot    += llList2Float(side, 3);
       
         if (!(llList2String(texture, i) == llList2String(texture, i-1) && llList2Vector(repeat, i) == llList2Vector(repeat, i-1) &&
             llList2Vector(offset, i) == llList2Vector(offset, i-1) && llList2Float(rot, i) == llList2Float(rot, i-1))) {
                same = 0;
        }
        i++;
     }
     }
    if (same) {
        ret += list2string([PRIM_TEXTURE, ALL_SIDES, llList2String(texture, 0), llList2Vector(repeat, 0), llList2Vector(offset, 0), llList2Float(rot, 0)]);
    } else {
        integer j = 0;
        while (j < llGetListLength(texture)) {
            ret += list2string([PRIM_TEXTURE, j, llList2String(texture, j), llList2Vector(repeat, j), llList2Vector(offset, j), llList2Float(rot, j)]);
            j++;
        }
    }       
   
    return ret;
}
}
</pre>


string getColor() {
    string ret;


The following object is called ListenToReplicate. Place it in ObjectDataSaver.
    integer sides = llGetLinkNumberOfSides(LINK_NUM);
   
    integer same = 1;
    list color;
    list alpha;
   
    integer i = 0;
    while (i < sides) {
        list side  = llGetLinkPrimitiveParams(LINK_NUM, [PRIM_COLOR, i]);
        alpha += llList2Float(side, 1);
        color  += llList2Vector(side, 0);
       
        if (!(llList2String(color, i) == llList2String(color, i-1) && llList2Vector(alpha, i) == llList2Vector(alpha, i-1))) {
                same = 0;
        }
        i++;
    }


<pre>
    if (same) {
integer num = 0;
        ret += list2string([PRIM_COLOR, ALL_SIDES, llList2Vector(color, 0), llList2Float(alpha, 0)]);
list total = [];
    } else {
        integer j = 0;
        while (j < llGetListLength(color)) {
            ret += list2string([PRIM_COLOR, j, llList2Vector(color, j), llList2Float(alpha, j)]);
            j++;
        }
    }       
   
    return ret;
}
 
string getShiny() {
    string ret;


default {
    integer sides = llGetLinkNumberOfSides(LINK_NUM);
     on_rez(integer i) {
   
         llListen(i, "", "", "");
    integer same = 1;
    list shiny;
    list bump;
   
    integer i = 0;
     while (i < sides) {
         list side  = llGetLinkPrimitiveParams(LINK_NUM, [PRIM_BUMP_SHINY, i]);
        shiny += llList2Integer(side, 0);
        bump += llList2Integer(side, 1);
       
        if (!(llList2Integer(shiny, i) == llList2Integer(shiny, i-1) && llList2Integer(bump, i) == llList2Integer(bump, i-1))) {
                same = 0;
        }
        i++;
     }
     }
     listen(integer i, string n, key id, string m) {
 
         if (llGetOwnerKey(id) == llGetOwner()) {
     if (same) {
            num++;
         ret += list2string([PRIM_BUMP_SHINY, ALL_SIDES, llList2Integer(shiny, 0), llList2Integer(bump, 0)]);
            total += m;
    } else {
            if (num == 10) {
        integer j = 0;
                llShout(-10, llDumpList2String(total, "-=!!=-"));
        while (j < llGetListLength(shiny)) {
                llDie();
            ret += list2string([PRIM_BUMP_SHINY, j, llList2Integer(shiny, j), llList2Integer(bump, j)]);
             }
             j++;
           
           
         }
         }
     }
     }      
   
    return ret;
}
}
</pre>


string getBright() {
    string ret;


The following code is for the ObjectDataSaver. It will also have and object called ListenToReplicate.
    integer sides = llGetLinkNumberOfSides(LINK_NUM);
   
    integer same = 1;
    list fullbright;
   
    integer i = 0;
    while (i < sides) {
        list side  = llGetLinkPrimitiveParams(LINK_NUM, [PRIM_FULLBRIGHT, i]);
        fullbright += llList2Integer(side, 0);
       
        if (!(llList2Integer(fullbright, i) == llList2Integer(fullbright, i-1))) {
                same = 0;
        }
        i++;
    }


<pre>
    if (same) {
list data;
        ret += list2string([PRIM_FULLBRIGHT, ALL_SIDES, llList2Integer(fullbright, 0)]);
    } else {
        integer j = 0;
        while (j < llGetListLength(fullbright)) {
            ret += list2string([PRIM_FULLBRIGHT, j, llList2Integer(fullbright, j)]);
            j++;
        }
    }       
   
    return ret;
}
string getGlow() {
    string ret;


default {
    integer sides = llGetLinkNumberOfSides(LINK_NUM);
     state_entry() {
   
         llListen(-5, "", "", "");
    integer same = 1;
         llListen(-10, "", "", "");
    list glow;
         llListen(1, "", "", "");
   
    integer i = 0;
     while (i < sides) {
         list side  = llGetLinkPrimitiveParams(LINK_NUM, [PRIM_GLOW, i]);
         glow += llList2Integer(side, 0);
          
        if (!(llList2Integer(glow, i) == llList2Integer(glow, i-1))) {
                same = 0;
        }
        i++;
     }
     }
     listen(integer i, string n, key id, string m) {
 
         if (i == -5)
     if (same) {
            llRezObject("ListenToReplicate", llGetPos() + <0,0,2>, <0,0,0>, <0,0,0,0>, (integer)m);
         ret += list2string([PRIM_GLOW, ALL_SIDES, llList2Integer(glow, 0)]);
        if (i == -10)
    } else {
            data += m;
         integer j = 0;
         if (i == 1) {
        while (j < llGetListLength(glow)) {
            integer i = 0;
            ret += list2string([PRIM_GLOW, j, llList2Integer(glow, j)]);
            while (i < llGetListLength(data)) {
            j++;
                llOwnerSay("\n" + llList2String(data, i));
                i++;
            }
         }
         }
    }       
   
    return ret;
}
vector getPos() {
    vector pos = llGetPos();
    pos -= llList2Vector(llGetLinkPrimitiveParams(LINK_NUM, [PRIM_POSITION]), 0);
   
    if (LINK_NUM == 1) {
        pos = <0,0,0>;
     }
     }
    return pos;
}
}
</pre>


string getType() {
    list type = [PRIM_TYPE] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_TYPE]);
    type += [PRIM_PHYSICS] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_PHYSICS]);
    type += [PRIM_MATERIAL] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_MATERIAL]);
    type += [PRIM_TEMP_ON_REZ] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_TEMP_ON_REZ]);
    type += [PRIM_PHANTOM] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_PHANTOM]);
    type += [PRIM_ROTATION] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_ROTATION]);
    type += [PRIM_SIZE] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_SIZE]);
    type += [PRIM_FLEXIBLE] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_FLEXIBLE]);
    type += [PRIM_POINT_LIGHT] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_POINT_LIGHT]);
    return list2string(type);
}


After you created the tree, drag the ObjectDataSaver down. Then place the following script in every prim of the object you want to save. Lastly, type any message on ch 1 and it will tell you the data. Then copy and paste it into a note called Setup and place it in the ObjectMakerFromNotecard.
(WARNING: EACH PRIM IS DELETED ONCE THE SCRIPT RUNS!!)


<pre>
string getPrimitiveParams() {
string SERIALIZER_DELIMITER = "$!#";
    string ret;
    ret += getType();
    ret += getTexture();
    ret += getShiny();
    ret += getColor();
    ret += getBright();
    ret += getGlow();
    return ret;
}
 
string getMeta() {
    return llDumpList2String([llGetLinkName(LINK_NUM), llList2String(llGetLinkPrimitiveParams(LINK_NUM, [PRIM_DESC]), 0)], "-=-");
}
 
string int2hex(integer num) {
    integer p1 = num & 0xF;
    integer p2 = (num >> 4) & 0xF;
    string data = llGetSubString(hexc, p2, p2) + llGetSubString(hexc, p1, p1);
    return data;
}


string hexc="0123456789ABCDEF";//faster


string Float2Hex(float a)
default {
{// Copyright Strife Onizuka, 2006, LGPL, http://www.gnu.org/copyleft/lesser.html
     state_entry() {
     if(a)
         string name = llGetObjectName();
    {
         llSetObjectName("-----");
         float b = llFabs(a);
        LINK_NUM = 1;
         integer c = llFloor(llLog(b) / 0.69314718055994530941723212145818);//floor(log2(b))
        while (LINK_NUM <= llGetNumberOfPrims()) {
         if(c > 127) c = 127;//catch fatal rounding error in exponent.
            llOwnerSay("#NEW " + int2hex(LINK_NUM) + " " + safeVector(getPos()));
        integer d = (integer)((b / llPow(2.0,c)) * 0x1000000);//shift up into integer range
            LINK_NUM++;
        string f = "p";
        }
        while(!(d & 0xf))
        LINK_NUM = 1;
        {//strip extra zeros off before converting or they break "p"
        llSetObjectName(name);
            d = d >> 4;
         while (LINK_NUM <= llGetNumberOfPrims()) {
             c+=4;
            string meta = getMeta();
            string data = getPrimitiveParams();
            llSetObjectName("-----");
            llOwnerSay(int2hex(llGetLinkNumber()) + ":::0"+meta);
            integer i = 0;
            integer num = 1;
            while (i < llStringLength(data)) {
                llOwnerSay(int2hex(LINK_NUM) + ":::" + (string)num + "" + (string)llGetSubString(data, i, i+199));
                i += 200;
                llSleep(.1);
                num++;
             }
            LINK_NUM++;
         }
         }
        do
            f = llGetSubString(hexc, 0xf & d, 0xf & d) + f;
        while(d = d >> 4);
        if(a < 0)
            return "-0x" + f + (string)(c - 24);
        return "0x" + f + (string)(c - 24);
     }
     }
    return "0";//zero would hang the zero stripper.
}
}
</source>


string serializeList(list l) {
</div></div>
    integer i = (l != []);//This is a hack, it gets list lenght.
    if(i)
    {
        string serialized_data = "";
        integer type = 0;
        string result;
        {@loop;
            // this custom loop is about as fast as a while loop.
            // we build the string backwords for memory reasons.
            // My kingdom for select statements....


            if (TYPE_FLOAT == (type = llGetListEntryType(l, (i=~-i))))
<div id="box">
== Holo Box (Script) ==
<div style="padding: 0.5em;">
<source lang="lsl2">
string meta;
list data = ["", "", "", "", "", "", "", "", "", "", "", "", "", ""];


                // floats get extra love
                result = Float2Hex(llList2Float(l, i));
            else if (TYPE_VECTOR == type) {
                vector v = llList2Vector(l, i);
                result = Float2Hex(v.x) + "," + Float2Hex(v.y) + "," + Float2Hex(v.z);
            } else  if (TYPE_ROTATION == type) {
                rotation r = llList2Rot(l, i);
                result = Float2Hex(r.x) + "," + Float2Hex(r.y) + "," + Float2Hex(r.z) + "," + Float2Hex(r.s);
            } else //if ((TYPE_INTEGER == type) || (TYPE_STRING ==  type) || (TYPE_KEY == type))
                result = llList2String(l, i);// integers, strings and keys required no voodoo


            if(i)
setLink() {
            {
    list m = llParseString2List(meta, ["-=-"], []);
                //This came to me after reverse engeneering LSL bytecode, the realization that LSL memory management sucks.
    llSetObjectName(llList2String(m, 0));
                serialized_data = SERIALIZER_DELIMITER + (string)type + (serialized_data = result = ",") + result + serialized_data;
    llSetObjectDesc(llList2String(m, 1));
                jump loop;
   
             }
    list l = llParseString2List((string)data, ["-=-"], []);
    list real;
    integer i = 0;
    while (i < llGetListLength(l)) {
        string this = llList2String(l, i);
        string thisPart = llGetSubString(this, 0, 1);
        if (thisPart == "#S") {
            real += (string)llGetSubString(this, 2, -1);
        } else if (thisPart == "#K") {
            real += (key)llGetSubString(this, 2, -1);
        } else if (thisPart == "#I") {
            real += (integer)llGetSubString(this, 2, -1);
        } else if (thisPart == "#F") {
            real += (float)llGetSubString(this, 2, -1);
        } else if (thisPart == "#V") {
            real += (vector)llGetSubString(this, 2, -1);
        } else if (thisPart == "#R") {
             real += (rotation)llGetSubString(this, 2, -1);
         }
         }
         return (string)type + (serialized_data = result = ",") + result + serialized_data;
         i++;
     }
     }
     return "";
     llSetPrimitiveParams(real);
}
}


Say(integer i, string m) {
    llSleep(.15);
    llShout(i, m);
}


default {
default {
     state_entry() {
     on_rez(integer i) {
         integer f = (integer)llFrand(10000) + 1691507124;
         llListen(i, "", "", "");
        llShout(-5, (string)f);
    }
         llSleep(1);
    listen(integer i, string n, key id, string m) {
        list total;
         if (m == "Finish") {
        string tot;
            setLink();
        total = llGetPrimitiveParams([PRIM_TYPE]);
            llSleep(.5);
        tot = serializeList(total);
            llRemoveInventory(llGetScriptName());
        Say(f, tot);
         }
        Say(f, (string)llGetColor(ALL_SIDES));
         integer num = (integer)llGetSubString(m, 0, 0);
         Say(f, (string)llGetRot());
         if (num == 0) {
         Say(f, (string)llGetScale());
            meta = llGetSubString(m, 1, -1);
         Say(f, (string)llGetTexture(ALL_SIDES));
         } else {
        Say(f, (string)llGetTextureScale(ALL_SIDES));
            num--;
         Say(f, (string)llGetTextureOffset(ALL_SIDES));
            data = llListReplaceList(data, [llGetSubString(m, 1, -1)], num, num);
        Say(f, (string)llGetTextureRot(ALL_SIDES));
         }
        total = llGetPrimitiveParams([PRIM_BUMP_SHINY, ALL_SIDES]);
        total = [llList2Integer(total, 0), llList2Integer(total, 1)];
        tot = serializeList(total);
        Say(f, tot);
        Say(f, (string)(llGetPos()));
        llSleep(1);
         llDie();
     }
     }
}
}
</pre>
</source>
</div></div>


{{LSLC|Library}}
{{LSLC|Library}}

Latest revision as of 08:14, 25 January 2015

Introduction

(http://www.gnu.org/copyleft/fdl.html) in the spirit of which this script is GPL'd. Copyright (C) 2011 Xaviar Czervik

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details.

This is one of the projects that I've spent a significant amount of time on, and that I've uploaded to the wiki. It's purpose is simple: it allows anyone to transfer objects through text. This may seem trivial, but it has several advantages to it. Below are a few ways in which this script can be used:

  1. The Internet. There is no way (yet) to go on to the Internet and request an object be sent to your SL character. This solves this issue. One could post the data on to the Internet, and allow people to copy the text down to SL, run this script, and obtain an object from it.
  2. Management Scripts. There is no way to have an object create a totally new object. And objects can only call llGiveInventory on other objects when in the same sim. This solves the issue by allowing objects to use email to send data, which can then be turned into an object.



Revision History

Version 1.4:

  1. Doesn't require rerezing.
  2. Simpler to turn object to data.
  3. Backwards compatible with 1.3.
  4. Bug fix from KimAnn Galaxy.

Version 1.3:

  1. 100% success rate.
  2. Added PRIM_GLOW
  3. Added functionality for multiple notecards (for really big objects).
  4. Improved efficiency; removed the delink/relink sections.

Version 1.2:

  1. Around 95% success rate.
  2. Changed to almost all llGetPrimitiveParams calls.
  3. Added more than just ten different descriptors of an object.
  4. Removed freakish menu-controlled system.
  5. Script need only be dropped in root object, not every prim.

Version 1.1:

  1. Made process simpler: removed replicate from listen.
  2. Changed to work (slightly) better.

Version 1.0:

  1. First release.


Instructions

Thanks to DMC Zsigmond for the original documentation shown below, modified for Version 1.4 by Xaviar.


Check List (Parts Required):

  1. 1 x Script, named: "Object to Data"
  2. 1 x Script, named: "Holo Script"
  3. 1 x Object, named: "HoloBox"
  4. 1 x Notecard, named: "Data_Default"

Plus,

  1. 1 x Any Object you wish to use these scripts with to turn into data, or re-rez it back again.

Part 1/3 - Creating Scripts and Assembling Objects


  1. Open your "Inventory", and create a new folder, called: "Object to Data v1.4".
  2. Under the new "Object to Data v1.4" folder, right-click and create a new script, named "Data To Object". Then copy and paste the "Data To Object" LSL code on this wiki page into the "Data To Object" script you just created under your "Object to Data v1.4" folder. Click on "Save" to compile the "Data to Object" script, and then after that "Data to Object" script has successfully compiled, close the script.
  3. Repeat Step 2, for the "Holo Script" and "Object to Data" scripts.
  4. Create a new Object on the ground, and then right-click on it, and select "Edit".
  5. Under the "General" tab, name the Object you just created as, "HoloBox". This object must be inserted into your "Data to Object" object in future to supply the prim that will be converted back into the original Object you once sort to have turned into text/notecard data.
  6. Go the the "Content" tab of the "HoloBox" prim, to view its contents.
  7. Drag'n'drop the "Holo Script" script under the "Object to Data v1.4" folder in your Inventory, to the "Contents" folder of the "HoloBox" object you have selected.
  8. Right-click the "HoloBox" object, and select "Take", to take the object back into your inventory.
  9. Create a new Object on the ground, right-click it, and select "Edit".
  10. Under the "General" tab, name the Object you just created as, "Data to Object". (not necessary)
  11. Go the the "Content" tab of the "Data to Object" prim, to view its contents.
  12. Drag'n'drop the "Data To Object" script under the "Object to Data v1.4" folder in your Inventory, to the "Contents" folder of the "Data to Object" object you have selected.
  13. Drag'n'drop the "HoloBox" object you have either in your "Objects" folder in your Inventory or under the "Object to Data v1.4" folder in your Inventory, to the "Contents" folder of the "Data to Object" object you have selected.

You should now have two things:

  1. An object named "Data to Object" which contains (1) a script named "Data to Object" (found below) and (2) an object named HoloBox. Holobox should contain a script named "Holo Script" (found below).
  2. A script named "Object to Data".


Part 2/3 - Convert an Object to Data


  1. Select an Object you wish to turn into data (NB. perhaps start with a single object which is relatively simple).
  2. Rez the Object you wish to turn into data on the ground.
  3. Right-click the Object you wish to turn into data, and select edit. Then click on the "Content" tab to view its contents.
  4. Now drag'n'drop the "Object to Data" script you created in your Inventory, from your "Object to Data v1.4" folder into the "Contents" folder of the Object you wish to turn into data.
  5. Wait a few moments (or possibly a minute or two) until your Local Chat Window outputs the Object's data.
  6. Copy the text data from your Local Chat Window (including the time code) from the very beginning to the very end.
  7. Return to your Inventory folder named, "Object to Data v1.4", and then right-click and create a new note (i.e. notecard) called, "Data_Default".
  8. Paste the Object code data copied from your Local Chat Window to the "Data_Default" notecard, and then "Save" it. This notecard now holds the data information for your Object.
  9. If the information does not fit in to one notecard, the first object stays named "Data_Default", however name the second notecard "Data_Default 1", the third "Data_Default 2", etc.

The entire object has now been turned into data. It can now be transferred through many means not usually possible, including a text file on the Internet.


Part 3/3 Instructions to convert Data to Object


  1. Rez the "Data to Object" object from the "Object to Data v1.4" folder. (Move the object some meters above the ground, since it will silently fail and hang if later if it attempts to rez objects slightly below ground level!)
  2. Right-click the Object you wish to turn into data, and select edit. Go to the "Contents" tab of the "Data to Object" object.
  3. Drag'n'drop the "Data_Default" notecard(s) to the object.
  4. Now touch the object, "Data to Object" to begin rebuilding the original Object that you had turned into data.
  5. View your Local Chat window for confirm of the build "Start", and notification when the build is "Done".

The original Object you sort to have turned into data will now rez as a new object above the "Data to Object" prim.

This object will be empty, ready for use as intended.



Data To Object (Script)

string name = "Data_Default";

integer numberOfNotes;
key     qid;
integer line = 0;
integer lineTotal = 0;
integer prims;
list    totalLines;
integer totalLinesTotal;

string hexDigits = "0123456789ABCDEF";

integer rand;

integer whichNote;

vector start;

get() {
    if (whichNote == 0) {
        qid = llGetNotecardLine(name, line);
    } else {
        qid = llGetNotecardLine(name + " " + (string)whichNote, line);
    }
        
}

rezObj(vector pos, string num) {
    while (llVecDist(llGetPos(), start-pos) > .001) llSetPos(start-pos);
    llRezObject("HoloBox", llGetPos(), <0,0,0>, <0,0,0,0>, hex2int(num) + rand);
}

integer hex2int(string hex) {
    integer p1 = llSubStringIndex(hexDigits, llGetSubString(hex, 0, 0));
    integer p2 = llSubStringIndex(hexDigits, llGetSubString(hex, 1, 1));
    integer data = p2 + (p1 << 4);
    return data;
}

default {
    touch_start(integer i) {
        llSetText("Starting Up... (1/4)", <1,1,1>, 1);
        numberOfNotes = llGetInventoryNumber(INVENTORY_NOTECARD);
        
        qid = llGetNumberOfNotecardLines(name);
    }
    dataserver(key queryId, string data) {
        if (queryId == qid)  {
            totalLines += data;
            totalLinesTotal += (integer)data;
            ++whichNote;
            if (whichNote < numberOfNotes) {
                llOwnerSay(name + " " + (string)whichNote);
                qid = llGetNumberOfNotecardLines(name + " " + (string)whichNote);
            } else {
                state rez;
            }
        }
    }
}

state rez {
    state_entry() {
        start = llGetPos();
        rand = (integer)llFrand(0x6FFFFFFF) + 0x10000000;
        llOwnerSay("Start");
        line = 0;
        qid = llGetNotecardLine(name, line); 
        whichNote = 0;
    }
    dataserver(key queryId, string data) {
        if (queryId == qid)  {
            if (data != EOF) {
                data = llList2String(llParseString2List(data, ["-----: "], []), 1);
                if (llGetSubString(data, 0, 3) == "#NEW") {
                    string num = llGetSubString(data, 5, 6);
                    vector pos = (vector)llGetSubString(data, 8, -1);
                    rezObj(pos, num);
                }
                llSetText("Rezing Prims: " + (string)((integer)(100*lineTotal/totalLinesTotal)) + "% (2/4)", <1,1,1>, 1);
                line += 1;
                lineTotal += 1;
                get();
            } else {
                whichNote++;
                if (whichNote < numberOfNotes) {
                    line = 0;
                    get();
                } else {
                    state run;
                }
            }
        }
    }
}

state run {
    state_entry() {
        line = 0;
        lineTotal = 0;
        qid = llGetNotecardLine(name, line); 
        whichNote = 0;
    }
    dataserver(key queryId, string data) {
        if (queryId == qid)  {
            if (data != EOF) {
                data = llList2String(llParseString2List(data, ["-----: "], []), 1);
                if (llGetSubString(data, 0, 3) != "#NEW") {
                    list parts = llParseString2List(data, [":::"], []);
                    integer prim = hex2int(llList2String(parts, 0));
                    llRegionSay(rand + prim, llList2String(parts, 1));
                }
                llSetText("Sending Data: " + (string)((integer)(100*lineTotal/totalLinesTotal)) + "% (3/4)", <1,1,1>, 1);
                line += 1;
                lineTotal += 1;
                get();
            } else {
                whichNote++;
                if (whichNote < numberOfNotes) {
                    line = 0;
                    get();
                } else {
                    llSetText("Cleaning Up... (4/4)", <1,1,1>, 1);
                    integer i = 0;
                    while (i < 256) {
                        llRegionSay(rand + i, "Finish");
                        ++i;
                    }
                    llSetTimerEvent(3);
                }
            }
        }
    }
    timer() {
        llSetText("Finished.", <1,1,1>, 1);
        llOwnerSay("Done");
        llSetTimerEvent(0);
        llResetScript();
    }
}


Object To Data (Script)

string hexc="0123456789ABCDEF";//faster

string Float2Hex(float input)
{// Copyright Strife Onizuka, 2006-2007, LGPL, http://www.gnu.org/copyleft/lesser.html
    if((integer)input != input)//LL screwed up hex integers support in rotation & vector string typecasting
    {//this also keeps zero from hanging the zero stripper.
        float unsigned = llFabs(input);//logs don't work on negatives.
        integer exponent = llFloor(llLog(unsigned) / 0.69314718055994530941723212145818);//floor(log2(b)) + rounding error
        integer mantissa = (integer)((unsigned / (float)("0x1p"+(string)(exponent -= (exponent == 128)))) * 0x1000000);//shift up into integer range
        integer index = (integer)(llLog(mantissa & -mantissa) / 0.69314718055994530941723212145818);//index of first 'on' bit
        string str = "p" + (string)((exponent += index) - 24);
        mantissa = mantissa >> index;
        do
            str = llGetSubString(hexc,15&mantissa,15&mantissa) + str;
        while(mantissa = mantissa >> 4);
        if(input < 0)
            return "-0x" + str;
        return "0x" + str;
    }//integers pack well so anything that qualifies as an integer we dump as such, supports netative zero
    return llDeleteSubString((string)input,-7,-1);//trim off the float portion, return an integer
}


string safeVector(vector v) {
    return "<"+safeFloat(v.x)+","+safeFloat(v.y)+","+safeFloat(v.z)+">";
}

string safeRotation(rotation v) {
    return "<"+safeFloat(v.x)+","+safeFloat(v.y)+","+safeFloat(v.z)+","+safeFloat(v.s)+">";
}

string safeFloat(float f) {
    return Float2Hex(f);
}

string list2string(list l) {
    string ret;
    integer len = llGetListLength(l);
    integer i;
    
    while (i < len) {
        integer type = llGetListEntryType(l, i);
        if (type == 1) {
            ret += "#I"+(string)llList2Integer(l, i);
        } else if (type == 2) {
            ret += "#F"+safeFloat(llList2Float(l, i));
        } else if (type == 3) {
            ret += "#S"+llList2String(l, i);
        } else if (type == 4) {
            ret += "#K"+(string)llList2Key(l, i);
        } else if (type == 5) {
            ret += "#V"+safeVector(llList2Vector(l, i));        
        } else if (type == 6) {
            ret += "#R"+safeRotation(llList2Rot(l, i));
        }
        ret += "-=-";
        i++;
    }
    return ret;
}

integer LINK_NUM = 0;

string getTexture() {
    string ret;
    
    integer sides = llGetLinkNumberOfSides(LINK_NUM);
    
    integer same = 1;
    list texture;
    list repeat;
    list offset;
    list rot;
    
    integer i = 0;
    while (i < sides) {
        list side  = llGetLinkPrimitiveParams(LINK_NUM, [PRIM_TEXTURE, i]);
        texture += llList2String(side, 0);
        repeat  += llList2Vector(side, 1);
        offset  += llList2Vector(side, 2);
        rot     += llList2Float(side, 3);
        
        if (!(llList2String(texture, i) == llList2String(texture, i-1) && llList2Vector(repeat, i) == llList2Vector(repeat, i-1) &&
            llList2Vector(offset, i) == llList2Vector(offset, i-1) && llList2Float(rot, i) == llList2Float(rot, i-1))) {
                same = 0;
        }
        i++;
    }

    if (same) {
        ret += list2string([PRIM_TEXTURE, ALL_SIDES, llList2String(texture, 0), llList2Vector(repeat, 0), llList2Vector(offset, 0), llList2Float(rot, 0)]);
    } else {
        integer j = 0;
        while (j < llGetListLength(texture)) {
            ret += list2string([PRIM_TEXTURE, j, llList2String(texture, j), llList2Vector(repeat, j), llList2Vector(offset, j), llList2Float(rot, j)]);
            j++;
        }
    }        
    
    return ret;
}

string getColor() {
    string ret;

    integer sides = llGetLinkNumberOfSides(LINK_NUM);
    
    integer same = 1;
    list color;
    list alpha;
    
    integer i = 0;
    while (i < sides) {
        list side  = llGetLinkPrimitiveParams(LINK_NUM, [PRIM_COLOR, i]);
        alpha += llList2Float(side, 1);
        color  += llList2Vector(side, 0);
        
        if (!(llList2String(color, i) == llList2String(color, i-1) && llList2Vector(alpha, i) == llList2Vector(alpha, i-1))) {
                same = 0;
        }
        i++;
    }

    if (same) {
        ret += list2string([PRIM_COLOR, ALL_SIDES, llList2Vector(color, 0), llList2Float(alpha, 0)]);
    } else {
        integer j = 0;
        while (j < llGetListLength(color)) {
            ret += list2string([PRIM_COLOR, j, llList2Vector(color, j), llList2Float(alpha, j)]);
            j++;
        }
    }        
    
    return ret;
}

string getShiny() {
    string ret;

    integer sides = llGetLinkNumberOfSides(LINK_NUM);
    
    integer same = 1;
    list shiny;
    list bump;
    
    integer i = 0;
    while (i < sides) {
        list side  = llGetLinkPrimitiveParams(LINK_NUM, [PRIM_BUMP_SHINY, i]);
        shiny += llList2Integer(side, 0);
        bump += llList2Integer(side, 1);
        
        if (!(llList2Integer(shiny, i) == llList2Integer(shiny, i-1) && llList2Integer(bump, i) == llList2Integer(bump, i-1))) {
                same = 0;
        }
        i++;
    }

    if (same) {
        ret += list2string([PRIM_BUMP_SHINY, ALL_SIDES, llList2Integer(shiny, 0), llList2Integer(bump, 0)]);
    } else {
        integer j = 0;
        while (j < llGetListLength(shiny)) {
            ret += list2string([PRIM_BUMP_SHINY, j, llList2Integer(shiny, j), llList2Integer(bump, j)]);
            j++;
        }
    }        
    
    return ret;
}

string getBright() {
    string ret;

    integer sides = llGetLinkNumberOfSides(LINK_NUM);
    
    integer same = 1;
    list fullbright;
    
    integer i = 0;
    while (i < sides) {
        list side  = llGetLinkPrimitiveParams(LINK_NUM, [PRIM_FULLBRIGHT, i]);
        fullbright += llList2Integer(side, 0);
        
        if (!(llList2Integer(fullbright, i) == llList2Integer(fullbright, i-1))) {
                same = 0;
        }
        i++;
    }

    if (same) {
        ret += list2string([PRIM_FULLBRIGHT, ALL_SIDES, llList2Integer(fullbright, 0)]);
    } else {
        integer j = 0;
        while (j < llGetListLength(fullbright)) {
            ret += list2string([PRIM_FULLBRIGHT, j, llList2Integer(fullbright, j)]);
            j++;
        }
    }        
    
    return ret;
}
string getGlow() {
    string ret;

    integer sides = llGetLinkNumberOfSides(LINK_NUM);
    
    integer same = 1;
    list glow;
    
    integer i = 0;
    while (i < sides) {
        list side  = llGetLinkPrimitiveParams(LINK_NUM, [PRIM_GLOW, i]);
        glow += llList2Integer(side, 0);
        
        if (!(llList2Integer(glow, i) == llList2Integer(glow, i-1))) {
                same = 0;
        }
        i++;
    }

    if (same) {
        ret += list2string([PRIM_GLOW, ALL_SIDES, llList2Integer(glow, 0)]);
    } else {
        integer j = 0;
        while (j < llGetListLength(glow)) {
            ret += list2string([PRIM_GLOW, j, llList2Integer(glow, j)]);
            j++;
        }
    }        
    
    return ret;
}

vector getPos() {
    vector pos = llGetPos();
    pos -= llList2Vector(llGetLinkPrimitiveParams(LINK_NUM, [PRIM_POSITION]), 0);
    
    if (LINK_NUM == 1) {
        pos = <0,0,0>;
    }
    return pos;
}

string getType() {
    list type = [PRIM_TYPE] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_TYPE]);
    type += [PRIM_PHYSICS] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_PHYSICS]);
    type += [PRIM_MATERIAL] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_MATERIAL]);
    type += [PRIM_TEMP_ON_REZ] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_TEMP_ON_REZ]);
    type += [PRIM_PHANTOM] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_PHANTOM]);
    type += [PRIM_ROTATION] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_ROTATION]);
    type += [PRIM_SIZE] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_SIZE]);
    type += [PRIM_FLEXIBLE] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_FLEXIBLE]);
    type += [PRIM_POINT_LIGHT] + llGetLinkPrimitiveParams(LINK_NUM, [PRIM_POINT_LIGHT]);
    return list2string(type);
}


string getPrimitiveParams() {
    string ret;
    ret += getType();
    ret += getTexture();
    ret += getShiny();
    ret += getColor();
    ret += getBright();
    ret += getGlow();
    return ret;
}

string getMeta() {
    return llDumpList2String([llGetLinkName(LINK_NUM), llList2String(llGetLinkPrimitiveParams(LINK_NUM, [PRIM_DESC]), 0)], "-=-");
}

string int2hex(integer num) {
    integer p1 = num & 0xF;
    integer p2 = (num >> 4) & 0xF;
    string data = llGetSubString(hexc, p2, p2) + llGetSubString(hexc, p1, p1);
    return data;
}


default {
    state_entry() {
        string name = llGetObjectName();
        llSetObjectName("-----");
        LINK_NUM = 1;
        while (LINK_NUM <= llGetNumberOfPrims()) {
            llOwnerSay("#NEW " + int2hex(LINK_NUM) + " " + safeVector(getPos()));
            LINK_NUM++;
        }
        LINK_NUM = 1;
        llSetObjectName(name);
        while (LINK_NUM <= llGetNumberOfPrims()) {
            string meta = getMeta();
            string data = getPrimitiveParams();
            llSetObjectName("-----");
            llOwnerSay(int2hex(llGetLinkNumber()) + ":::0"+meta);
            integer i = 0;
            integer num = 1;
            while (i < llStringLength(data)) {
                llOwnerSay(int2hex(LINK_NUM) + ":::" + (string)num + "" + (string)llGetSubString(data, i, i+199));
                i += 200;
                llSleep(.1);
                num++;
            }
            LINK_NUM++;
        }
    }
}


Holo Box (Script)

string meta;
list data = ["", "", "", "", "", "", "", "", "", "", "", "", "", ""];


setLink() {
    list m = llParseString2List(meta, ["-=-"], []);
    llSetObjectName(llList2String(m, 0));
    llSetObjectDesc(llList2String(m, 1));
    
    list l = llParseString2List((string)data, ["-=-"], []);
    list real;
    integer i = 0;
    while (i < llGetListLength(l)) {
        string this = llList2String(l, i);
        string thisPart = llGetSubString(this, 0, 1);
        if (thisPart == "#S") {
            real += (string)llGetSubString(this, 2, -1);
        } else if (thisPart == "#K") {
            real += (key)llGetSubString(this, 2, -1);
        } else if (thisPart == "#I") {
            real += (integer)llGetSubString(this, 2, -1);
        } else if (thisPart == "#F") {
            real += (float)llGetSubString(this, 2, -1);
        } else if (thisPart == "#V") {
            real += (vector)llGetSubString(this, 2, -1);
        } else if (thisPart == "#R") {
            real += (rotation)llGetSubString(this, 2, -1);
        }
        i++;
    }
    llSetPrimitiveParams(real);
}


default {
    on_rez(integer i) {
        llListen(i, "", "", "");
    }
    listen(integer i, string n, key id, string m) {
        if (m == "Finish") {
            setLink();
            llSleep(.5);
            llRemoveInventory(llGetScriptName());
        }
        integer num = (integer)llGetSubString(m, 0, 0);
        if (num == 0) {
            meta = llGetSubString(m, 1, -1);
        } else {
            num--;
            data = llListReplaceList(data, [llGetSubString(m, 1, -1)], num, num);
        }
    }
}