Script to sort & match .MENUITEMS posename and ".POSITIONS" posenames

== Match .MENUITEMS posename and ".POSITIONS" posenames ==
When sorting and reorganizing my MLP or Xpose menus I often paste the pos vectors from the .POSITIONS notecard beneath the matching POSE line of the .MENUITEMS card, plus the props line for MLP.
Handy to have the information together: 
 
- if I want to change the pose name I can easily do it when they are together.
- If I want to compare two (or three!) POSITIONS cards or a POSITIONS card and a fresh posdump. 
- To make sure all POSE in .MENUITEMS have matching pos vector in .POSITIONS 
- I like to sort my animation collection in inventory, one pose pair per folder with a note of the settings, and this way I can archive the POSE line from the menu, with the .POSITIONS vectors for a chair, a rug and a bed for that animation pair all together in my reference note for easy re-use when making new pieces. I just add a //comment telling what item each line is for at the end of the position vectors. 
Very tedious to do the merge and sort by hand and a little rough on the ol' carpal tunnel so I made this script to automate the process, and was asked on MLPV2 group to post it here. You can pick it up ready-made in Sorting Hat shape from my store freebies- look at Yvana Zadark profile picks. 
 
It will filter, and skip duplicate pos vector lines for its chat output. 
The resulting output is your .MENUITEMS note reproduced in owner chat, with the pos vectors from the .POSITIONS note (and the MLPV2 props!) added below the POSE lines. 
Looks like this: 
 
MENU MenuA | ALL | PINK | BLUE 
POSE MyPose | poserF | poserM 
{MyPose} <-0.004,-0.031,0.8> <-2,0,-89> | <-0.004,-0.054,0.535> <-2,0,-89> //chair 
{MyPose} <-0.006,-0.031,0.8> <-2,0,-90> | <-0.006,-0.054,0.535> <-2,0,-90> 
|MyPose|Doodad|<0.,-3.83,-0.02>/<0.,0.,0.> <<< MLP props only!
It can handle as many position and props lines as you can cram into a notecard. 
Basic instructions are repeated at the top of the script for your reference 
Script: 
<lsl>
 // match .MENUITEMS posename and ".POSITIONS" posenames
 // by Yvana Zadark 2015
 // with thanks to Omei Qunhua for tips on optimization
 // this script reads an MLP or XPOSE menucard, and finds the pos info
 // for the matching POSE from the ".POSITIONS" note, writes
 // a new menucard with the position info below the POSE info
 // like this:
 // MENU MenuA | ALL | PINK | BLUE
 // POSE MyPose | poserF | poserM
 // {MyPose} <-0.004,-0.031,0.8> <-2,0,-89> <-0.004,-0.054,0.535> <-2,0,-89>
 // {MyPose} <-0.006,-0.031,0.8> <-2,0,-90> <-0.006,-0.054,0.535> <-2,0,-90>
 // |MyPose|Doodad|<0.,-3.83,-0.02>/<0.,0.,0.> <<< MLP only!
 //
 // it does not handle XPOSE props, it will handle MLP props, paste them into the .POSITIONS card.
 //
 // makes it easier to re-organize the .MENUITEMS notecards
 // or archive the data for your pose pairs
 // or quick comparisons of an old .POSITIONS card and a fresh posdump/propsdump:
 // - Just paste the new posdump & propsdump onto the bottom of the old .POSITIONS card
 // - can handle up to 5 pos and props vectors with the same pose name in the .POSITIONS card
 //
 // ----------------
 // Instructions
 // put the script in a prim
 // paste all your .MENUITEMS notecards into one named .MENUITEMS
 // paste all your .POSITIONS notecards into one named .POSITIONS
 // put both new notes in the prim with the script and touch it.
 // **IMPORTANT**
 // Remove the timestamps from .POSITIONS
 // Remove any extra spaces from the start of lines in both notes.
 // Turn off timestamps in chat for cleaner output
 //========================================
 integer iDebug = 0;
 integer iMenuline;
 integer iPosline;
 integer ipozidx;
 integer ipozidx2;
 integer ipozidx3;
 integer ipozidx4;
 integer ipozidx5;
 integer ipozidx6;
 integer ipozidx7;
 key MenulineID;
 key PoslineID;
 key PoslineID2;
 key PoslineID3;
 key PoslineID4;
 key PoslineID5;
 list lstNone = ["MENU ","MENU:","POSE "];
 list lstPoseslines;
 list lstPosnames;
 list lstPosnames2;
 list lstPosnames3;
 list lstPosnames4;
 string sPOSEline;
 string sPOSEline2;
 string sPOSEline3;
 string sPOSEline4;
 string sPOSEline5;
 string sPosename;
 string sPosname;
 default
 {
 touch_start(integer total_number)
 {
 llSetObjectName(""); //cleaner output
 llOwnerSay("/" + "/" + "Starting to make pose name list from .POSITIONS note... wait.");//notify starting
 PoslineID = llGetNotecardLine(".POSITIONS", iPosline);
 }
 dataserver(key id, string data)
 {
 //processing .POSITIONS to make a list of the pose names
 if(id == PoslineID)
 {
 if (data == EOF) //end of pos card, start sorting menu note
 {
 integer iLstlen = llGetListLength(lstPosnames);
 llOwnerSay("/" + "/" + ".POSITIONS note has " + (string)iPosline + "lines.\nPosename list has " + (string)iLstlen + " items.\ Memory remaining = " + (string)llGetFreeMemory() + "\n Reading .MENUITEMS note.");
 MenulineID = llGetNotecardLine(".MENUITEMS", iMenuline); //read first menu note line
 }
 if (data != EOF) 
 {
 string marker = llGetSubString(data,0,0); //does it start with "{" or "|" ?
 if(marker == "{") //yes its pos vectors, get the pose name and add to list
 {
 string sSubstr3 = llGetSubString(data,1,-1);//strip off "{"
 integer iPsub1Idx = llSubStringIndex(sSubstr3,"}"); //find closing "}"
 string sSubstr4 = llGetSubString(sSubstr3,0,iPsub1Idx -1); //extract posename
 string sSubstr5 = llStringTrim(sSubstr4, STRING_TRIM);//kill spaces
 sPosname = llStringTrim(sSubstr5, STRING_TRIM);//kill spaces
 lstPosnames = lstPosnames + sPosname; //add to list
 }
 else if(marker == "|")//its an MLP props line, get the pose name and add to list
 {
 string sSubstr3 = llGetSubString(data,1,-1);//strip off "|"
 integer iPsub1Idx = llSubStringIndex(sSubstr3,"|"); //find closing "|"
 string sSubstr4 = llGetSubString(sSubstr3,0,iPsub1Idx -1); //extract posename
 string sSubstr5 = llStringTrim(sSubstr4, STRING_TRIM);//kill spaces
 sPosname = llStringTrim(sSubstr5, STRING_TRIM);//kill spaces
 lstPosnames = lstPosnames + sPosname; //add to list
 }
 else
 {
 lstPosnames = lstPosnames + "$"; //add a blank to posenames list to keep indexes straight
 }
 PoslineID = llGetNotecardLine(".POSITIONS", ++iPosline); //get next line
 }
 }
 //processing MENUITEMS note lines & finding matching pos
 if(id == MenulineID)
 {
 if (data == EOF)//finished menu note, report and shut down
 {
 llOwnerSay("/" + "/" + "Finished sort. Memory remaining = " + (string)llGetFreeMemory());
 llSetObjectName(llGetObjectDesc()); //set back to original name
 llResetScript();
 }
 sPOSEline = data; //save data renamed
 string mark = llGetSubString(sPOSEline,0,4); //first 5 characters of the line
 if(llListFindList(lstNone,[mark]) == - 1)// != "MENU ") && (mark != "MENU:") && (mark != "POSE "))
 {
 llOwnerSay(sPOSEline);
 MenulineID = llGetNotecardLine(".MENUITEMS", ++iMenuline);//onto the next line
 }
 else if((mark == "MENU ") || (mark == "MENU:"))
 {
 llOwnerSay("\n" + sPOSEline); //add a line break above MENU
 MenulineID = llGetNotecardLine(".MENUITEMS", ++iMenuline);//onto the next line
 }
 else if(mark == "POSE ") //is a pose line, process
 {
 llOwnerSay(sPOSEline);
 lstPoseslines = [sPOSEline];
 string sSubstr1 = llGetSubString(sPOSEline,5,-1);//strip off "POSE "
 integer iMsub1Idx = llSubStringIndex(sSubstr1,"|"); //find end of pose name
 string sSubstr2 = llGetSubString(sSubstr1,0,iMsub1Idx -1); //extract pose name from line
 string sSubstr3 = llStringTrim(sSubstr2, STRING_TRIM);//kill spaces
 sPosename = llStringTrim(sSubstr3, STRING_TRIM);//kill more spaces
 ipozidx = llListFindList(lstPosnames,[sPosename]); //find pose name in list from positions note
 if(ipozidx != -1) //found it
 {
 PoslineID2 = llGetNotecardLine(".POSITIONS", ipozidx);
 }
 else
 {
 MenulineID = llGetNotecardLine(".MENUITEMS", ++iMenuline);//onto the next line
 }
 }
 }
 //other pos vectors with the same pose name?
 else if(id == PoslineID2)
 {
 sPOSEline2 = data; //save data renamed
 if(sPOSEline2 != sPOSEline)
 {
 lstPoseslines = lstPoseslines + sPOSEline2;
 llOwnerSay(data); //say matching position info
 }
 lstPosnames2 = llList2List(lstPosnames,ipozidx +1,-1);
 ipozidx2 = llListFindList(lstPosnames2,[sPosename]); //search for more
 if(ipozidx2 != -1) //found another
 {
 ipozidx3 = (ipozidx + ipozidx2 + 1); //add to get the index for main list
 PoslineID3 = llGetNotecardLine(".POSITIONS", ipozidx3);
 }
 else
 {
 MenulineID = llGetNotecardLine(".MENUITEMS", ++iMenuline);//onto the next line
 }
 }
 else if(id == PoslineID3)
 {
 sPOSEline3 = data; //save data renamed
 if(llListFindList(lstPoseslines,[sPOSEline3]) == -1) //test for duplicates
 {
 lstPoseslines = lstPoseslines + sPOSEline3;
 llOwnerSay(data); //say matching position info
 }
 lstPosnames3 = llList2List(lstPosnames,ipozidx3 +1,-1);
 ipozidx4 = llListFindList(lstPosnames3,[sPosename]);
 if(ipozidx4 != -1) //found another
 {
 ipozidx5 = (ipozidx3 + ipozidx4 + 1); //add to get the index for main list
 PoslineID4 = llGetNotecardLine(".POSITIONS", ipozidx5);
 }
 else
 {
 MenulineID = llGetNotecardLine(".MENUITEMS", ++iMenuline);//onto the next line
 }
 }
 else if(id == PoslineID4)
 {
 sPOSEline4 = data; //save data renamed
 if(llListFindList(lstPoseslines,[sPOSEline4]) == -1) //test for duplicates
 {
 lstPoseslines = lstPoseslines + sPOSEline4;
 llOwnerSay(data); //say matching position info
 }
 lstPosnames4 = llList2List(lstPosnames,ipozidx5+1,-1);
 ipozidx6 = llListFindList(lstPosnames4,[sPosename]);
 if(ipozidx6 != -1) //found another
 {
 ipozidx7 = (ipozidx5 + ipozidx6 + 1); //add to get the index for main list
 PoslineID5 = llGetNotecardLine(".POSITIONS", ipozidx7);
 }
 else
 {
 MenulineID = llGetNotecardLine(".MENUITEMS", ++iMenuline);//onto the next line
 }
 }
 else if(id == PoslineID5)
 {
 sPOSEline5 = data; //save data renamed
 if(llListFindList(lstPoseslines,[sPOSEline5]) == -1) //test for duplicates
 {
 lstPoseslines = lstPoseslines + sPOSEline5;
 llOwnerSay(data); //say matching position info
 }
 MenulineID = llGetNotecardLine(".MENUITEMS", ++iMenuline);//onto the next line
 }
 }
 }
</lsl>
== Instructions ==
Rez a prim, make a new script and paste the script above into it, save and close. 
- You may have to delete the spaces in front of the globals and default, I had to add them to make it look ok in wiki.
Label the prim and put the same name in the prim description field. 
(It renames itself blank while working, for clean output, then copies its name back from the desc field when it is done.)
Paste your .MENUITEMS notecards into a single note named .MENUITEMS
Paste your .POSITIONS notecards into a single note named .POSITIONS
For MLP, paste the contents of your .PROPS note into the note named .POSITIONS
-It can only handle those two note cards, with those exact names.
-Save and put them into the prim with the script.
Turn off timestamps in chat, via viewer preferences for cleaner chat output.
Touch the prim with the script and notecards, and wait for it to do its thing, it will show output to owner-only chat. 
It will let you know when it is finished, and how much memory it has left. 
 
== IMPORTANT*** ==
 
Clean up your note cards. 
!!! ALL Timestamps must be removed from the .POSITIONS notes !!! 
No extra spaces at the start of lines in either note because it works by counting characters. 
It will ignore any .POSITIONS note lines that do not start with a "{" 
It will sort props lines for MLP but not for XPOSE, put the props in the .POSITIONS note.
It will ignore any MLPV2 .PROPS note lines that do not start with a "|" 
I tested it with a .POSITIONS notecard that had more than 800 lines and it still had memory to spare, it is quite robust as only the pose names are saved to the script memory. It will handle as many pose lines as you can fit into a notecard. 
-It does not sort XPOSE props info, as there is too much difference between MLP and XPOSE in how they handle props and making the script longer to include XPOSE props would make it less robust in how many pos lines it can sort. 
--------------
== Function - how it works ==
.
First it will read the .POSITIONS note and write only the pose names to an internal list for indexing, with a marker entry for any line without an opening "{" or "|" if its MLP props. 
Then it starts on the .MENUITEMS notecard and it will copy every line to chat.
When it finds a POSE line it separates out the pose name and looks for a matching entry in its pose name list. 
If it finds one it will use the list index to find the whole line in the .POSITIONS note and write it to chat below its POSE menu line. It will examine the list 5x to see if there are any more pos lines with the same pose name and write those too. 
It can handle up to five pos vector lines or MLP props lines with the same posename. 
== Tips! ==
 
I use notetab light for editing and cleaning up my note cards. It is a plain text editor with lots of nice features, extended search and replace, sort alphabetically with one click, see two pages side by side and drag and drop between them. It can also be set as a clip board, and any text you copy in sl is automatically added to the clips page.
It is free! 
 
To remove time stamps from .POSITIONS I search and replace the closing "] " of the time stamp, with "^P" which makes a line break, then use the "sort alphabetically" button so all the time stamps move to the top for easy deletion.
If I want to keep the lines in the original order then I just use search and replace the time stamp with nothing several times to get them all. Posdump goes quick so I usually only have to repeat a few times unless seconds are turned on. 
To set the menu back to being useable after editing, I select each menu's POSEs and its pos lines and click to sort alphabetically, all the position vector lines for each menu will come together at the top under the MENU line, to be copied back into the .POSITIONS note and deleted from the .MENUITEMS. 
This is my first wiki entry. :) Have never claimed to be a brilliant scripter - improve it if you can, please be kind if you do. -YZ
 
Category:MLPV2