// George script for TVPaint // // File name: import_csv.grg // Description: imports csv and its pictures back into TVPaint // Created by: László Fazekas (fazek) // Version: 1.6/31-Aug-2016 // License: Public domain, no warranty // Straight Alpha version: // the RGB values in the PNG source are NOT // premultiplied with the transparency // (this is the standard) tv_AlphaLoadMode NoPreMultiply tv_LayerCurrentID parse result keepid tv_LayerGetImage parse result keeppos tv_ReadUserString fileRequest 0 parse result path tv_ReqFile "Select CSV file to import|"path"||*.csv" parse result file if (cmp(file,"Cancel") == 0) llen= len(file) //trick: search for a different directory separator character dsep= "/" lpos= llen quit= 0 do if (lpos < 1) quit= 1 else c= char(file,lpos) if (cmp(c,"/") == 1) quit= 1 else if (cmp(c,"\") == 1) dsep= c quit= 1 else lpos= lpos - 1 end end end until (quit == 1) base= "" if (lpos >= 1) base= cut(file,1,lpos) path= '"'base'"' tv_WriteUserString fileRequest 0 path end //convert filename.csv into filename.frames/ for the picture folder path path= cut(file,1,llen-3)"frames"dsep if (cmp(char(file,1),'"') != 1) file='"'file'"' end row= 0 state= 0 quit= 0 laymax= 0 newscene= 0 //arguments for scene creation scname= "Unnamed" width= 0 height= 0 fcnt= 1 frate= 24.0 aspect= 1.0 field= "Progressive" frame= 0 scname_id= -1 width_id= -1 height_id= -1 fcnt_id= -1 frate_id= -1 aspect_id= -1 field_id= -1 crlf="??" //surely not matching //simplified CSV format: //the separator is only ',', the decimal point is '.' //rfc4180 parentheses escaping supported //but not cr/lf between parentheses //CSV 1.1: #folder tag: no folder search do tv_readtextfile row file parse result eof line c= char(eof,1) if ((cmp(c,"0")+cmp(c,"1")+cmp(c,"2")+cmp(c,"3")+cmp(c,"4")+cmp(c,"5")+cmp(c,"6")+cmp(c,"7")+cmp(c,"8")+cmp(c,"9")) > 0) llen= len(line) - 2 if (state == 0) state= 1 //not possible to give cr/lf as a string literal, so get //them from the end of the very first row... //CSV permits missing cr/lf at the end of the final row if (llen > 0) //avoid protocol changes if (cmp(char(line,llen + 2),'"') != 1) crlf= cut(line,llen + 1,llen + 2) end end llen= 0 //skip the first row end if (llen > 0) if (cmp(cut(line,llen + 1,llen + 2),crlf) == 1) line= cut(line,1,llen) else llen= llen + 2 end lay= 1 lpos= 1 name= SeekStr() //simplified frame recognition, frames 0..9999 only if (state == 5) if (IsFrame(name) != 0) //frame rows, frame step is always 1 tv_LockDisplay "Loading CSV frame "frame"/"fcnt SeekFrame() end else if (state == 1) id= 1 lpos= 1 while (lpos <= llen) name= SeekStr() if (cmp(name,"Project Name") == 1) scname_id= id else if (cmp(name,"Width") == 1) width_id= id else if (cmp(name,"Height") == 1) height_id= id else if (cmp(name,"Frame Count") == 1) fcnt_id= id else if (cmp(name,"Frame Rate") == 1) frate_id= id else if (cmp(name,"Pixel Aspect Ratio") == 1) pix_id= id else if (cmp(name,"Field Mode") == 1) field_id= id end end end end end end end id= id + 1 end state= 2 else if (state == 2) id= 1 lpos= 1 while (lpos <= llen) name= SeekStr() if (id == scname_id) scname= name else if (id == width_id) width= name else if (id == height_id) height= name else if (id == fcnt_id) fcnt= name else if (id == frate_id) frate= name else if (id == aspect_id) pix= name else if (id == field_id) field= name end end end end end end end id= id + 1 end state= 3 if ((width * height) != 0) tv_request "Do you want to create a new project?|Yes|No" parse result newscene tv_LockDisplay "Loading CSV..." if (newscene == 1) if (cmp(field, "Lower First") == 1) field= "LOWER" else if (cmp(field, "Upper First") == 1) field= "UPPER" else //Progressive field= "NONE" end end scname='"'scname'"' tv_ProjectCurrentID parse result keepid tv_ProjectNew scname width height aspect frate field 0 tv_ProjectCurrentID parse result id if (cmp(keepid,id) == 1) newscene= 0 //no new scene else keeppos= 0 //jump to the first frame after finished end tv_LayerCurrentID parse result keepid end end else if (state == 3) //seek for layer header if (llen > 7) if (cmp(name,"#Layers") == 1) while (lpos <= llen) name= SeekStr() name= '"'name'"' tv_LayerCreate name parse result id layid[lay]= id laypic[lay]= "NULL" laylen[lay]= 1 layvis[lay]= 1 layfolder[lay]= path lay= lay + 1 tv_LayerAnim end state= 4 laymax= lay end end else //state == 4 if (len(name) >= 2) if (IsFrame(name) != 0) //the first frame row state= 5 SeekFrame() else //optional layer parameters if (cmp(name,"#Blending") == 1) while (lpos <= llen) if (lay >= laymax) lpos= llen + 1 else name= SeekStr() id= layid[lay] tv_LayerSet id tv_LayerBlendingMode name lay= lay + 1 end end else if (cmp(name,"#Density") == 1) while (lpos <= llen) if (lay >= laymax) lpos= llen + 1 else num= SeekStr() id= layid[lay] tv_LayerSet id if (num < 0) num= 0 else if (num > 1) num= 1 end end num= num * 100 tv_LayerDensity num lay= lay + 1 end end else if (cmp(name,"#Visible") == 1) while (lpos <= llen) if (lay >= laymax) lpos= llen + 1 else layvis[lay]= SeekStr() lay= lay + 1 end end else if (cmp(name,"#Folder") == 1) while (lpos <= llen) if (lay >= laymax) lpos= llen + 1 else name= SeekStr() if (len(name) > 1) layfolder[lay]= RealPath(name) end lay= lay + 1 end end end end end end end end end end end end end row= row + 1 else if (cmp(eof,"EOF") == 0) print "Read error : "result end quit= 1 end until (quit == 1) //flush all nonempty layers lay= 1 while (lay < laymax) name= laypic[lay] lpos= laylen[lay] id= layid[lay] if (cmp(name,"NULL") == 1) //final empty if (lpos < frame) tv_LayerStretch id end -1 cut end else //final picture file for the layer name= concat(layfolder[lay],name) name= '"'name'"' id= layid[lay] tv_LayerSet id if (lpos > 1) lpos= lpos - 1 tv_LayerStretch id end lpos last lpos= lpos + 1 end lpos= frame - lpos tv_LayerImage lpos tv_LoadImage name stretch end //visibility must be the final step if (layvis[lay] == 0) tv_LayerDisplay id Off end lay= lay + 1 end if (newscene == 1) //erase the automatically created layer if (laymax > 0) tv_LayerKill keepid keepid= layid[1] end end tv_LayerSet keepid tv_LayerImage keeppos tv_UnLockDisplay end //end of main //functions function IsFrame(name) local c if (len(name) < 2) return 0 end if (cmp(char(name,1),"#") != 1) return 0 end c= char(name,2) return cmp(c,"0")+cmp(c,"1")+cmp(c,"2")+cmp(c,"3")+cmp(c,"4")+cmp(c,"5")+cmp(c,"6")+cmp(c,"7")+cmp(c,"8")+cmp(c,"9") end function TrimStr(name) local i c //drop the trailing whitespace characters //(no escape sequence for the tab character) i= len(name) while (i > 0) c= char(name,i) if ((cmp(c,' ')+cmp(c,' ')) == 0) return cut(name,1,i) end i= i - 1 end return "" end function SeekStr() local c min max str step quit quit= 0 //skip leading whitespace characters //(no escape sequence for the tab character) do if (lpos > llen) quit= 1 else c= char(line,lpos) if ((cmp(c,' ')+cmp(c,' ')) == 1) lpos= lpos + 1 else quit= 1 end end until (quit == 1) step= 0 quit= 0 if (lpos <= llen) c= char(line,lpos) if (cmp(c,'"') == 1) //leading parentheses lpos= lpos + 1 min= lpos do if (lpos > llen) //missing trailing parentheses return "NULL" else c= char(line,lpos) lpos= lpos + 1 if (cmp(c,'"') == 1) if (lpos > llen) quit= 1 else c= char(line,lpos) if (cmp(c,'"') == 1) //escaped double parentheses lpos = lpos - 2 if (step == 0) if (lpos >= min) str= cut(line,min,lpos) else str= "" end step= 1 else if (lpos >= min) str= concat(str,cut(line,min,lpos)) end end lpos = lpos + 3 min = lpos else //trailing parentheses quit= 1 end end end end until (quit == 1) max= lpos - 2 quit= 0 //seek until the next comma do if (lpos > llen) quit= 1 else c= char(line,lpos) lpos= lpos + 1 if (cmp(c,",") == 1) quit= 1 end end until (quit == 1) if (step == 0) if (max < min) return "NULL" end str= cut(line,min,max) else if (max >= min) str= concat(str,cut(line,min,max)) end end else //without parentheses min= lpos do if (lpos > llen) max= llen quit= 1 else c= char(line,lpos) lpos= lpos + 1 if (cmp(c,",") == 1) max= lpos - 2 quit= 1 end end until (quit == 1) if (max < min) return "NULL" end str= TrimStr(cut(line,min,max)) end end return str end function SeekFrame() local plen prev pic while (lpos <= llen) if (lay >= laymax) lpos= llen else pic= SeekStr() if (cmp(laypic[lay],pic) == 1) laylen[lay]= laylen[lay] + 1 else prev= laypic[lay] plen= laylen[lay] id= layid[lay] tv_LayerSet id if (cmp(prev,"NULL") == 1) //empty frame if (plen < frame) tv_LayerStretch id end plen last plen= frame - plen tv_LayerImage plen tv_Clear 0 else if (plen > 1) plen= plen - 1 tv_LayerShift id plen end end else //picture file prev= concat(layfolder[lay],prev) prev= '"'prev'"' tv_LayerStretch id end plen last plen= frame - plen tv_LayerImage plen tv_LoadImage prev stretch end laypic[lay]= pic laylen[lay]= 1 end lay= lay + 1 end end frame= frame + 1 end function RealPath(name) local c min max folder quit min= len(name) c= char(name,min) if ((cmp(c,"/")+cmp(c,"\")) != 0) min= min - 1 name= cut(name,1,min) end folder= "" max= min quit= 0 do if (min < 1) quit= 1 else c= char(name,min) if ((cmp(c,"/")+cmp(c,"\")) != 0) folder= concat(dsep,folder) folder= concat(cut(name,min + 1,max),folder) if (max > 7) if (cmp(cut(name,max - 6,max),".layers") == 1) quit= 2 end end max= min - 1 end end min= min - 1 until (quit > 0) if (quit == 2) folder= concat(base,folder) else folder= concat(name,dsep) end return folder end //end of file