Last active
April 2, 2025 15:38
-
-
Save FelixWolf/d6b78b39a06419693a93046046913e15 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// WARNING: THIS MAY CONTAIN BUGS | |
// I hereby waive all rights to this code to Linden Lab / Second Life | |
// Should be covered by contributor agreement anyway, I think? | |
// Everyone else can use this as LGPL. | |
#include "library.h" | |
#include "linden_common.h" | |
#include "lscript_library.h" | |
void llList2TypedString(LLScriptLibData *retval, LLScriptLibData *args, const LLUUID &id) | |
{ | |
retval->mType = LST_STRING; | |
std::string serializedData; | |
int listSize = args[0].getListLength(); | |
LLScriptLibData *current = &args[0]; | |
for(int i = 0; i < listSize; i++) | |
{ | |
current = current->mListp; | |
// Type identifiers cannot contain a number or be any of ABCDEF | |
// Current type identifiers: | |
// i = integer | |
// r = real / float | |
// k = key | |
// K = arbitrary string as key | |
// s = string | |
// v = vector | |
// q = quaternion | |
if(current->mType == LST_INTEGER) | |
{ | |
if (current->mInteger == 0) | |
serializedData += "i"; | |
else | |
serializedData += "i" + std::to_string(current->mInteger); | |
} | |
else if(current->mType == LST_FLOATINGPOINT) | |
{ | |
std::string source = std::to_string(current->mFP); | |
std::size_t start = source.find_first_not_of("0"); | |
if (start == std::string::npos) | |
serializedData += "r"; | |
else | |
{ | |
std::size_t end = source.find_last_not_of("0."); | |
serializedData += "r" + source.substr(start, end - start + 1); | |
} | |
} | |
else if(current->mType == LST_KEY) | |
{ | |
if (sizeof(current->mKey) == 0) | |
{ | |
serializedData += "k"; | |
} | |
else if (LLUUID::validate(current->mKey)) | |
{ | |
LLUUID key(current->mKey); | |
if (!key.isNull()) | |
serializedData += "k" + key.asString(); | |
} | |
else | |
{ | |
LLWString srcstr = utf8str_to_wstring(current->mKey); | |
size_t size = srcstr.size(); | |
if (size == 0) | |
serializedData += "K"; | |
else | |
serializedData += "K" + std::to_string(size) + ":" + current->mKey; | |
} | |
} | |
else if(current->mType == LST_STRING) | |
{ | |
LLWString srcstr = utf8str_to_wstring(current->mString); | |
size_t size = srcstr.size(); | |
if (size == 0) | |
serializedData += "s"; | |
else | |
serializedData += "s" + std::to_string(size) + ":" + current->mString; | |
} | |
else if(current->mType == LST_VECTOR) | |
{ | |
serializedData += "v"; | |
if (!current->mVec.isExactlyZero()) | |
{ | |
// In theory, can just do 0, 1, 2, but if VX, VY, or VZ changes, | |
// we will run into issues. Compiler should optimize it. | |
const U32 axises[3] = {VX, VY, VZ}; | |
for(int i = 0; i < 3; i++) | |
{ | |
if (current->mVec.mV[axises[i]] != 0) | |
{ | |
std::string source = std::to_string(current->mVec.mV[axises[i]]); | |
std::size_t start = source.find_first_not_of("0"); | |
if (start != std::string::npos) | |
{ | |
std::size_t end = source.find_last_not_of("0."); | |
serializedData += source.substr(start, end - start + 1); | |
} | |
} | |
if(i != 2) | |
serializedData += ","; | |
} | |
} | |
} | |
else if(current->mType == LST_QUATERNION) | |
{ | |
serializedData += "q"; | |
// Quaternions have no isExactlyZero(). :( | |
if (current->mQuat.mQ[VX] != 0 || current->mQuat.mQ[VY] != 0 || | |
current->mQuat.mQ[VZ] != 0 || current->mQuat.mQ[VW] != 0 | |
) | |
{ | |
// In theory, can just do 0, 1, 2, but if VX, VY, VZ, or VW changes, | |
// we will run into issues. Compiler should optimize it. | |
const U32 axises[4] = {VX, VY, VZ, VW}; | |
for (int i = 0; i < 4; i++) | |
{ | |
if (current->mQuat.mQ[axises[i]] != 0) | |
{ | |
std::string source = std::to_string(current->mQuat.mQ[axises[i]]); | |
std::size_t start = source.find_first_not_of("0"); | |
if (start != std::string::npos) | |
{ | |
std::size_t end = source.find_last_not_of("0."); | |
serializedData += source.substr(start, end - start + 1); | |
} | |
} | |
if(i != 3) | |
serializedData += ","; | |
} | |
} | |
} | |
else | |
{ | |
serializedData += "?"; | |
} | |
} | |
retval->mString = new char[serializedData.length() + 1]; | |
std::strcpy(retval->mString, serializedData.c_str()); | |
} | |
void llTypedString2List(LLScriptLibData *retval, LLScriptLibData *args, const LLUUID &id) | |
{ | |
retval->mType = LST_LIST; | |
LLScriptLibData *current = retval; | |
LLWString payload = utf8str_to_wstring(args[0].mString); | |
for(int i = 0; i < payload.size();) | |
{ | |
llwchar entryType = payload[i]; | |
i++; | |
switch(entryType) | |
{ | |
case 'i': | |
{ | |
current->mListp = new LLScriptLibData; | |
current = current->mListp; | |
current->mType = LST_INTEGER; | |
int start = i; | |
for (; i < payload.size(); i++) | |
{ | |
if (i == start && payload[i] == '-') | |
continue; | |
else if ((i-1 == start && payload[i-1] == '0' && payload[i] == 'x' )) | |
continue; | |
else if ((i-2 == start && payload[i-2] == '-' && payload[i-1] == '0' && payload[i] == 'x' )) | |
continue; | |
else if (payload[i] >= '0' && payload[i] <= '9') | |
continue; | |
break; | |
} | |
// std::stoi does conversion from hex to integer automagically for us! | |
try | |
{ | |
current->mInteger = std::stoi(wstring_to_utf8str(payload.substr(start, i - start + 1))); | |
} | |
catch (const std::invalid_argument& e) | |
{ | |
current->mInteger = 0; | |
} | |
} | |
break; | |
case 'r': | |
{ | |
current->mListp = new LLScriptLibData; | |
current = current->mListp; | |
current->mType = LST_FLOATINGPOINT; | |
int start = i; | |
bool foundDot = false; | |
for(; i < payload.size(); i++) | |
{ | |
if (i == start && payload[i] == '-') | |
continue; | |
else if (payload[i] == '.' && foundDot == false) | |
{ | |
foundDot = true; | |
continue; | |
} | |
else if (payload[i] >= '0' && payload[i] <= '9') | |
continue; | |
break; | |
} | |
try | |
{ | |
current->mFP = std::stof(wstring_to_utf8str(payload.substr(start, i - start + 1))); | |
} | |
catch (const std::invalid_argument& e) | |
{ | |
current->mFP = 0; | |
} | |
} | |
break; | |
case 'k': | |
{ | |
current->mListp = new LLScriptLibData; | |
current = current->mListp; | |
current->mType = LST_KEY; | |
// Only copy it if it is a valid key, otherwise assume it is null key | |
// then continue to the next element | |
if(i + UUID_STR_SIZE < payload.size()) | |
{ | |
std::string utf8str = wstring_to_utf8str(payload.substr(i, UUID_STR_SIZE - 1)); | |
bool validKey = LLUUID::validate(utf8str); | |
if (validKey) | |
{ | |
const char* srckey = utf8str.c_str(); | |
size_t length = std::strlen(srckey); | |
current->mKey = new char[length + 1]; | |
std::strcpy(current->mKey, srckey); | |
i += utf8str.size(); | |
} | |
else | |
{ | |
current->mKey = new char[UUID_STR_SIZE]; | |
LLUUID::null.toString(current->mKey); | |
} | |
} | |
} | |
break; | |
case 's': | |
case 'K': // Arbitrary keys | |
{ | |
current->mListp = new LLScriptLibData; | |
current = current->mListp; | |
if (entryType == 's') | |
current->mType = LST_STRING; | |
else if (entryType == 'K') | |
current->mType = LST_KEY; | |
int start = i; | |
for (; i < payload.size(); i++) | |
{ | |
if (payload[i] >= '0' && payload[i] <= '9') | |
continue; | |
else if (payload[i] == ':') | |
{ | |
i++; | |
} | |
break; | |
} | |
if(start == i) | |
break; | |
int size = 0; | |
try | |
{ | |
size = std::stoi(wstring_to_utf8str(payload.substr(start, i - start))); | |
} | |
catch (const std::invalid_argument& e) | |
{ | |
size = 0; | |
} | |
std::string utf8str = wstring_to_utf8str(payload.substr(i, size)); | |
const char* value = utf8str.c_str(); | |
size_t length = std::strlen(value); | |
if (entryType == 's') | |
{ | |
current->mString = new char[length + 1]; | |
std::strcpy(current->mString, value); | |
} | |
else if (entryType == 'K') | |
{ | |
current->mKey = new char[length + 1]; | |
std::strcpy(current->mKey, value); | |
} | |
i += size; | |
} | |
break; | |
case 'v': | |
case 'q': | |
{ | |
int size = 3; | |
if(payload[i-1] == 'q') | |
size = 4; | |
const U32 axises[4] = {VX, VY, VZ, VW}; | |
current->mListp = new LLScriptLibData; | |
current = current->mListp; | |
if (size == 3) | |
current->mType = LST_VECTOR; | |
else if (size == 4) | |
current->mType = LST_QUATERNION; | |
for (int axis = 0; axis < size; axis++) | |
{ | |
if (i >= payload.size()) | |
break; | |
if (!(payload[i] >= '0' || payload[i] <= '9' || payload[i] == '-' || payload[i] == ',' || payload[i] == '.')) | |
{ | |
i--; | |
break; | |
} | |
// Skip over seperators | |
if (payload[i] == ',') | |
{ | |
i++; | |
axis--; | |
continue; | |
} | |
int start = i; | |
bool foundDot = false; | |
for (; i < payload.size(); i++) | |
{ | |
if (i == start && payload[i] == '-') | |
continue; | |
else if (payload[i] == '.' && foundDot == false) | |
{ | |
foundDot = true; | |
continue; | |
} | |
else if (payload[i] >= '0' && payload[i] <= '9') | |
continue; | |
else if (i != start) | |
i--; | |
else if (payload[i] != ',') | |
i--; | |
break; | |
} | |
try | |
{ | |
if (size == 3) | |
current->mVec.mV[axises[axis]] = std::stof(wstring_to_utf8str(payload.substr(start, i - start + 1))); | |
else if (size == 4) | |
current->mQuat.mQ[axises[axis]] = std::stof(wstring_to_utf8str(payload.substr(start, i - start + 1))); | |
} | |
catch (const std::invalid_argument& e) | |
{ | |
if (size == 3) | |
current->mVec.mV[axises[axis]] = 0; | |
else if (size == 4) | |
current->mQuat.mQ[axises[axis]] = 0; | |
} | |
if (i != size - 1) | |
i++; | |
} | |
} | |
break; | |
// White spaces are ignored, allows for pretty printing | |
case ' ': | |
case '\n': | |
case '\t': | |
case '\r': | |
break; | |
default: | |
printf("Invalid type %c\n", payload[i]); | |
// Not sure what the behavior should be here | |
// Probably throw a LSL error | |
// Various possibilities here: | |
// 1. Skip invalids | |
// 2. Return what we already parsed | |
// 3. Return none | |
break; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment