HSDollyCam/HS_CamPresetTransfer.lsl
2026-05-20 06:03:40 +02:00

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);
}
}