330 lines
7.8 KiB
Plaintext
330 lines
7.8 KiB
Plaintext
/*
|
|
HS_DollyCam - Preset Transfer Utility
|
|
|
|
Drop this script into a camera object only when importing/exporting presets.
|
|
It reads and writes the existing Linkset Data preset keys: P1, P2, ...
|
|
|
|
Commands on channel 88:
|
|
/88 xfer help
|
|
/88 xfer status
|
|
/88 xfer export [from] [count]
|
|
/88 xfer import <notecard>
|
|
/88 xfer importreplace <notecard>
|
|
/88 xfer poof
|
|
|
|
Export format:
|
|
HS_DOLLYCAM_PRESETS_V1
|
|
P1=px|py|pz|fx|fy|fz|rx|ry|rz|rs|fovRad
|
|
HS_DOLLYCAM_PRESETS_END
|
|
*/
|
|
|
|
integer CH = 88;
|
|
integer MAX_PRESET_SCAN = 999;
|
|
|
|
string HEADER = "HS_DOLLYCAM_PRESETS_V1";
|
|
string FOOTER = "HS_DOLLYCAM_PRESETS_END";
|
|
|
|
key gOwner;
|
|
integer gListen;
|
|
|
|
string gImportCard;
|
|
key gImportQuery;
|
|
integer gImportLine;
|
|
integer gImporting;
|
|
integer gReplaceBeforeImport;
|
|
integer gImported;
|
|
integer gSkipped;
|
|
|
|
string preKey(integer idx)
|
|
{
|
|
return "P" + (string)idx;
|
|
}
|
|
|
|
say(string msg)
|
|
{
|
|
llOwnerSay(msg);
|
|
}
|
|
|
|
string firstToken(string msg)
|
|
{
|
|
msg = llStringTrim(msg, STRING_TRIM);
|
|
integer sp = llSubStringIndex(msg, " ");
|
|
if (sp < 0) return msg;
|
|
return llGetSubString(msg, 0, sp - 1);
|
|
}
|
|
|
|
string restAfterFirstToken(string msg)
|
|
{
|
|
msg = llStringTrim(msg, STRING_TRIM);
|
|
integer sp = llSubStringIndex(msg, " ");
|
|
if (sp < 0) return "";
|
|
return llStringTrim(llGetSubString(msg, sp + 1, -1), STRING_TRIM);
|
|
}
|
|
|
|
integer isCommentOrMarker(string line)
|
|
{
|
|
if (line == "") return TRUE;
|
|
if (line == HEADER) return TRUE;
|
|
if (line == FOOTER) return TRUE;
|
|
if (llGetSubString(line, 0, 0) == "#") return TRUE;
|
|
if (llGetSubString(line, 0, 1) == "//") return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
integer hasPresetMinimumFields(string data)
|
|
{
|
|
integer pipes = 0;
|
|
integer pos = -1;
|
|
integer len = llStringLength(data);
|
|
|
|
while (pipes < 5) {
|
|
integer next = llSubStringIndex(llGetSubString(data, pos + 1, -1), "|");
|
|
if (next < 0) return FALSE;
|
|
pos += next + 1;
|
|
++pipes;
|
|
if (pos >= len) return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
integer validPresetIndexString(string s)
|
|
{
|
|
integer len = llStringLength(s);
|
|
if (len < 2) return FALSE;
|
|
if (llToUpper(llGetSubString(s, 0, 0)) != "P") return FALSE;
|
|
|
|
integer idx = (integer)llGetSubString(s, 1, -1);
|
|
if (idx < 1 || idx > MAX_PRESET_SCAN) return FALSE;
|
|
|
|
if ("P" + (string)idx != llToUpper(s)) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
integer importLine(string line)
|
|
{
|
|
line = llStringTrim(line, STRING_TRIM);
|
|
if (isCommentOrMarker(line)) return FALSE;
|
|
|
|
integer eq = llSubStringIndex(line, "=");
|
|
if (eq < 2) return FALSE;
|
|
|
|
string keyName = llStringTrim(llGetSubString(line, 0, eq - 1), STRING_TRIM);
|
|
string data = llStringTrim(llGetSubString(line, eq + 1, -1), STRING_TRIM);
|
|
|
|
if (!validPresetIndexString(keyName)) return FALSE;
|
|
if (!hasPresetMinimumFields(data)) return FALSE;
|
|
|
|
integer idx = (integer)llGetSubString(keyName, 1, -1);
|
|
llLinksetDataWrite(preKey(idx), data);
|
|
return TRUE;
|
|
}
|
|
|
|
clearPresets()
|
|
{
|
|
integer i;
|
|
for (i = 1; i <= MAX_PRESET_SCAN; ++i) {
|
|
llLinksetDataDelete(preKey(i));
|
|
}
|
|
}
|
|
|
|
printHelp()
|
|
{
|
|
say(
|
|
"HS DollyCam Preset Transfer\n"
|
|
+ "/88 xfer status\n"
|
|
+ "/88 xfer export [from] [count]\n"
|
|
+ "/88 xfer import <notecard>\n"
|
|
+ "/88 xfer importreplace <notecard>\n"
|
|
+ "/88 xfer poof\n"
|
|
+ "Import notecard lines use: P1=px|py|pz|fx|fy|fz|rx|ry|rz|rs|fovRad"
|
|
);
|
|
}
|
|
|
|
printStatus()
|
|
{
|
|
integer count = 0;
|
|
integer first = 0;
|
|
integer last = 0;
|
|
integer i;
|
|
|
|
for (i = 1; i <= MAX_PRESET_SCAN; ++i) {
|
|
if (llLinksetDataRead(preKey(i)) != "") {
|
|
++count;
|
|
if (first == 0) first = i;
|
|
last = i;
|
|
}
|
|
}
|
|
|
|
if (count == 0) {
|
|
say("Preset Transfer: no saved presets found.");
|
|
return;
|
|
}
|
|
say("Preset Transfer: " + (string)count + " presets found, first P" + (string)first + ", last P" + (string)last + ".");
|
|
}
|
|
|
|
exportPresets(string args)
|
|
{
|
|
integer from = 1;
|
|
integer count = 50;
|
|
|
|
string tok = firstToken(args);
|
|
if (tok != "") {
|
|
from = (integer)tok;
|
|
string rest = restAfterFirstToken(args);
|
|
tok = firstToken(rest);
|
|
if (tok != "") count = (integer)tok;
|
|
}
|
|
|
|
if (from < 1) from = 1;
|
|
if (from > MAX_PRESET_SCAN) from = MAX_PRESET_SCAN;
|
|
if (count < 1) count = 50;
|
|
if (count > 100) count = 100;
|
|
|
|
integer stop = from + count - 1;
|
|
if (stop > MAX_PRESET_SCAN) stop = MAX_PRESET_SCAN;
|
|
|
|
integer shown = 0;
|
|
integer i;
|
|
|
|
say(HEADER);
|
|
for (i = from; i <= stop; ++i) {
|
|
string data = llLinksetDataRead(preKey(i));
|
|
if (data != "") {
|
|
say(preKey(i) + "=" + data);
|
|
++shown;
|
|
}
|
|
}
|
|
say(FOOTER);
|
|
|
|
if (shown == 0) {
|
|
say("Preset Transfer: no presets found in requested range.");
|
|
return;
|
|
}
|
|
say("Preset Transfer: exported " + (string)shown + " presets from P" + (string)from + " to P" + (string)stop + ".");
|
|
}
|
|
|
|
startImport(string card, integer replace)
|
|
{
|
|
card = llStringTrim(card, STRING_TRIM);
|
|
if (card == "") {
|
|
say("Preset Transfer: missing notecard name.");
|
|
return;
|
|
}
|
|
|
|
if (llGetInventoryType(card) != INVENTORY_NOTECARD) {
|
|
say("Preset Transfer: notecard not found: " + card);
|
|
return;
|
|
}
|
|
|
|
if (gImporting) {
|
|
say("Preset Transfer: import already running.");
|
|
return;
|
|
}
|
|
|
|
gImportCard = card;
|
|
gImportLine = 0;
|
|
gImported = 0;
|
|
gSkipped = 0;
|
|
gReplaceBeforeImport = replace;
|
|
gImporting = TRUE;
|
|
|
|
if (gReplaceBeforeImport) {
|
|
clearPresets();
|
|
say("Preset Transfer: cleared existing P1-P" + (string)MAX_PRESET_SCAN + " presets.");
|
|
}
|
|
|
|
say("Preset Transfer: importing from notecard '" + gImportCard + "' ...");
|
|
gImportQuery = llGetNotecardLine(gImportCard, gImportLine);
|
|
}
|
|
|
|
handleCommand(string msg)
|
|
{
|
|
string cmd = llToLower(firstToken(msg));
|
|
if (cmd != "xfer" && cmd != "transfer") return;
|
|
|
|
string rest = restAfterFirstToken(msg);
|
|
string sub = llToLower(firstToken(rest));
|
|
string args = restAfterFirstToken(rest);
|
|
|
|
if (sub == "" || sub == "help") {
|
|
printHelp();
|
|
return;
|
|
}
|
|
|
|
if (sub == "status") {
|
|
printStatus();
|
|
return;
|
|
}
|
|
|
|
if (sub == "export") {
|
|
exportPresets(args);
|
|
return;
|
|
}
|
|
|
|
if (sub == "import") {
|
|
startImport(args, FALSE);
|
|
return;
|
|
}
|
|
|
|
if (sub == "importreplace") {
|
|
startImport(args, TRUE);
|
|
return;
|
|
}
|
|
|
|
if (sub == "poof" || sub == "done" || sub == "remove") {
|
|
say("Preset Transfer: removing script.");
|
|
llRemoveInventory(llGetScriptName());
|
|
return;
|
|
}
|
|
|
|
say("Preset Transfer: unknown command. Use /88 xfer help");
|
|
}
|
|
|
|
default
|
|
{
|
|
state_entry()
|
|
{
|
|
gOwner = llGetOwner();
|
|
gListen = llListen(CH, "", "", "");
|
|
say("HS DollyCam Preset Transfer ready. Use /88 xfer help");
|
|
}
|
|
|
|
on_rez(integer start_param)
|
|
{
|
|
gOwner = llGetOwner();
|
|
}
|
|
|
|
changed(integer change)
|
|
{
|
|
if (change & CHANGED_OWNER) {
|
|
llResetScript();
|
|
}
|
|
}
|
|
|
|
listen(integer channel, string name, key id, string msg)
|
|
{
|
|
if (llGetOwnerKey(id) != gOwner) return;
|
|
handleCommand(llStringTrim(msg, STRING_TRIM));
|
|
}
|
|
|
|
dataserver(key query_id, string data)
|
|
{
|
|
if (!gImporting || query_id != gImportQuery) return;
|
|
|
|
if (data == EOF) {
|
|
say("Preset Transfer: import complete. imported=" + (string)gImported + " skipped=" + (string)gSkipped + ".");
|
|
gImporting = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (importLine(data)) ++gImported;
|
|
else {
|
|
string trimmed = llStringTrim(data, STRING_TRIM);
|
|
if (!isCommentOrMarker(trimmed)) ++gSkipped;
|
|
}
|
|
|
|
++gImportLine;
|
|
gImportQuery = llGetNotecardLine(gImportCard, gImportLine);
|
|
}
|
|
}
|