// :CATEGORY:XS Pet // :NAME:XS Pet Xundra Snowpaw Original Quail // :AUTHOR:Xundra Snowpaw // :CREATED:2013-09-06 // :EDITED:2013-09-18 15:39:11 // :ID:989 // :NUM:1484 // :REV:1 // :WORLD:Second Life, Opensim // :DESCRIPTION: // Original Pet Quail // :CODE: integer API_CHANNEL = -999198; float VERSION = 0.22; string SECRET_PASSWORD = "top secret"; integer SECRET_NUMBER = 99999; key YOUR_UUID = "00000000-0000-0000-0000-000000000000"; integer ANIMAL_CHANNEL = -999192; integer BOX_CHANNEL = -999195; // DON'T CHANGE THE FOLLOWING! (Unless you know what you are doing!) integer XTEA_DELTA = 0x9E3779B9; // (sqrt(5) - 1) * 2^31 integer xtea_num_rounds = 6; list xtea_key = [0, 0, 0, 0]; integer hex2int(string hex) { if(llGetSubString(hex,0,1) == "0x") return (integer)hex; if(llGetSubString(hex,0,0) == "x") return (integer)("0"+hex); return(integer)("0x"+hex); } // Convers any string to a 32 char MD5 string and then to a list of // 4 * 32 bit integers = 128 bit Key. MD5 ensures always a specific // 128 bit key is generated for any string passed. list xtea_key_from_string( string str ) { str = llMD5String(str,0); // Use Nonce = 0 return [ hex2int(llGetSubString( str, 0, 7)), hex2int(llGetSubString( str, 8, 15)), hex2int(llGetSubString( str, 16, 23)), hex2int(llGetSubString( str, 24, 31))]; } // Encipher two integers and return the result as a 12-byte string // containing two base64-encoded integers. string xtea_encipher( integer v0, integer v1 ) { integer num_rounds = xtea_num_rounds; integer sum = 0; do { // LSL does not have unsigned integers, so when shifting right we // have to mask out sign-extension bits. v0 += (((v1 << 4) ^ ((v1 >> 5) & 0x07FFFFFF)) + v1) ^ (sum + llList2Integer(xtea_key, sum & 3)); sum += XTEA_DELTA; v1 += (((v0 << 4) ^ ((v0 >> 5) & 0x07FFFFFF)) + v0) ^ (sum + llList2Integer(xtea_key, (sum >> 11) & 3)); } while( num_rounds = ~-num_rounds ); //return only first 6 chars to remove "=="'s and compact encrypted text. return llGetSubString(llIntegerToBase64(v0),0,5) + llGetSubString(llIntegerToBase64(v1),0,5); } // Decipher two base64-encoded integers and return the FIRST 30 BITS of // each as one 10-byte base64-encoded string. string xtea_decipher( integer v0, integer v1 ) { integer num_rounds = xtea_num_rounds; integer sum = XTEA_DELTA*xtea_num_rounds; do { // LSL does not have unsigned integers, so when shifting right we // have to mask out sign-extension bits. v1 -= (((v0 << 4) ^ ((v0 >> 5) & 0x07FFFFFF)) + v0) ^ (sum + llList2Integer(xtea_key, (sum>>11) & 3)); sum -= XTEA_DELTA; v0 -= (((v1 << 4) ^ ((v1 >> 5) & 0x07FFFFFF)) + v1) ^ (sum + llList2Integer(xtea_key, sum & 3)); } while ( num_rounds = ~-num_rounds ); return llGetSubString(llIntegerToBase64(v0), 0, 4) + llGetSubString(llIntegerToBase64(v1), 0, 4); } // Encrypt a full string using XTEA. string xtea_encrypt_string( string str ) { // encode string str = llStringToBase64(str); // remove trailing =s so we can do our own 0 padding integer i = llSubStringIndex( str, "=" ); if ( i != -1 ) str = llDeleteSubString( str, i, -1 ); // we don't want to process padding, so get length before adding it integer len = llStringLength(str); // zero pad str += "AAAAAAAAAA="; string result; i = 0; do { // encipher 30 (5*6) bits at a time. result += xtea_encipher(llBase64ToInteger(llGetSubString(str, i, i + 4) + "A="), llBase64ToInteger(llGetSubString(str, i+5, i + 9) + "A=")); i+=10; } while ( i < len ); return result; } // Decrypt a full string using XTEA string xtea_decrypt_string( string str ) { integer len = llStringLength(str); integer i=0; string result; //llOwnerSay(str); do { integer v0; integer v1; v0 = llBase64ToInteger(llGetSubString(str, i, i + 5) + "=="); i+= 6; v1 = llBase64ToInteger(llGetSubString(str, i, i + 5) + "=="); i+= 6; result += xtea_decipher(v0, v1); } while ( i < len ); // Replace multiple trailing zeroes with a single one i = llStringLength(result) - 1; while ( llGetSubString(result, i - 1, i) == "AA" ){ result = llDeleteSubString(result, i, i); i--; } i = llStringLength(result) - 1; // while (llGetSubString(result, i, i + 1) == "A" ) { // i--; // } result = llGetSubString(result, 0, i+1); i = llStringLength(result); integer mod = i%4; //Depending on encoded length diffrent appends are needed if(mod == 1) result += "A=="; else if(mod == 2 ) result += "=="; else if(mod == 3) result += "="; return llBase64ToString(result); } string base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; vector colour1; vector colour2; integer gen; integer shine; float glow; key uuid; integer listener; integer owner_touch; integer sex; integer age; integer hunger; string myname; integer glow_gene; string special; say_details() { string mysex; string myshine; if (sex == 1) { mysex = "Male"; } else if (sex == 2) { mysex = "Female"; } else { mysex = (string)sex; } if (shine == 0) { myshine = "None"; } else if (shine == 1) { myshine = "Low"; } else if (shine == 2) { myshine = "Medium"; } else if (shine == 3) { myshine = "High"; } llWhisper(0, "Head/Body Colour: " + (string)colour1); llWhisper(0, "Tail/Wing Colour: " + (string)colour2); llWhisper(0, "Shine: " + myshine); llWhisper(0, "Glow: " + (string)((integer)(glow * 100)) + "%"); llWhisper(0, "Special: " + special); llWhisper(0, "Sex: " + mysex); llWhisper(0, "Age: " + (string)age + " days"); llWhisper(0, "Generation: " + (string)gen); llSay(API_CHANNEL, "XSQuail^" + (string)llGetKey() + "^" + (string)colour1 + "^" + (string)colour2 + "^" + (string)shine + "^" + (string)glow + "^" + special + "^" + (string)gen + "^" + (string)sex + "^" + (string)age + "^XSQuailEnd"); } set_colours(vector c1, vector c2, integer shine, float glow) { llSetPrimitiveParams([PRIM_COLOR, 0, c1, 1.0, PRIM_COLOR, 1, c1, 1.0, PRIM_COLOR, 3, c1, 1.0, PRIM_COLOR, 5, c1, 1.0, PRIM_COLOR, 2, c2, 1.0, PRIM_COLOR, 4, c2, 1.0, PRIM_BUMP_SHINY, ALL_SIDES, shine, PRIM_BUMP_NONE, PRIM_GLOW, ALL_SIDES, glow]); } default { state_entry() { xtea_key = xtea_key_from_string(SECRET_PASSWORD); uuid = NULL_KEY; listener = llListen(BOX_CHANNEL, "", "", ""); } touch_start(integer num) { llWhisper(0, "This Cryo-Crate is v" + (string)VERSION); } listen(integer channel, string name, key id, string msg) { if (llGetOwnerKey(id) != llGetOwner()) { return; } list data = llParseString2List(xtea_decrypt_string(msg), ["^"], []); if (uuid == NULL_KEY) { if (llList2String(data, 0) == "XSPET" && llList2String(data, 2) == "CRATE_PING") { uuid = id; llSay(ANIMAL_CHANNEL, xtea_encrypt_string("XSPET^CRATE_PONG^" + (string)id + "^" + (string)VERSION)); llSetTimerEvent(15.0); } } else if (uuid == id) { if (llList2String(data, 0) == "XSPET" && llList2Key(data, 1) == llGetKey()) { llSetTimerEvent(0.0); colour1 = (vector)llList2String(data, 2); colour2 = (vector)llList2String(data, 3); shine = llList2Integer(data, 4); glow = llList2Float(data, 5); gen = llList2Integer(data, 6); sex = llList2Integer(data, 7); age = llList2Integer(data, 8); hunger = llList2Integer(data, 9); myname = llList2String(data, 10); glow_gene = llList2Integer(data, 11); special = llList2String(data, 12); llListenRemove(listener); llSay(ANIMAL_CHANNEL, xtea_encrypt_string("XSPET^CRATE_DIE^" + (string)id + "^" + (string)VERSION)); state full; } else if (llList2String(data, 0) == "XSPET" && llList2Key(data, 1) != llGetKey()) { uuid = NULL_KEY; } } } timer() { llSetTimerEvent(0.0); uuid = NULL_KEY; } } state full { state_entry() { llSetObjectName("XS Cryo-Crate: " + myname); set_colours(colour1, colour2, shine, glow); llListen(API_CHANNEL, "", "", ""); } touch_start(integer num) { owner_touch = 0; say_details(); if (llDetectedKey(0) == llGetOwner()) { owner_touch = 1; integer chan = (integer)llFrand(10000.0) + 1000; listener = llListen(chan, "", llGetOwner(), ""); llDialog(llGetOwner(), "Would you like to unpackage me?", ["Yes", "No"], chan); } llSetTimerEvent(20.0); } timer() { //llOwnerSay("Menu timeout."); if (owner_touch == 1) { llListenRemove(listener); } llSetTimerEvent(0.0); } listen(integer channel, string name, key id, string msg) { if (channel == API_CHANNEL) { list data = llParseString2List(msg, ["^"], []); if (llList2String(data, 0) == "XSQuail" && llList2Key(data, 1) == llGetKey()) { llSay(API_CHANNEL, "XSQuail^" + (string)llGetKey() + "^" + (string)colour1 + "^" + (string)colour2 + "^" + (string)shine + "^" + (string)glow + "^" + special + "^" + (string)gen + "^" + (string)sex + "^" + (string)age + "^XSQuailEnd"); } } else { llListenRemove(listener); if (msg == "Yes") { state unbox; } } } } state unbox { state_entry() { llRezObject("Quail", llGetPos() + <0.0, 0.0, 0.25>, ZERO_VECTOR, ZERO_ROTATION, SECRET_NUMBER); } object_rez(key id) { listener = llListen(BOX_CHANNEL, "", id, ""); } listen(integer channel, string name, key id, string msg) { if (channel == BOX_CHANNEL) { list data = llParseString2List(xtea_decrypt_string(msg), ["^"], []); if (llList2String(data, 0) == "XSPET" && llList2String(data, 1) == "READY") { llGiveInventory(id, "XS Egg"); llGiveInventory(id, "Quail"); llSay(ANIMAL_CHANNEL, xtea_encrypt_string("XSPET^CONFIG^" + (string)id + "^" + (string)colour1 + "^" + (string)colour2 + "^" + (string)sex + "^" + (string)shine + "^" + (string)glow + "^" + (string)gen + "^" +(string)age + "^" + (string)hunger + "^" + myname + "^" + (string)glow_gene + "^" + special)); llDie(); } } } }