document.write('
Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1.  
  2. --# Notes
  3. --# Notes
  4. --[[
  5. This utility backs up and restores your projects for you, by storing them as images in your Dropbox folder.
  6. Its features are as follows:
  7. 1. backs up up behind the scenes every time you change the version number
  8. 2. tells you how long it is since the last backup
  9. 3. restores your backup into a new project, tabs and all
  10.  
  11. INSTALLING IT
  12. Copy this project to your Codea, named as Backup
  13.  
  14. BACKING UP
  15.  
  16. Include a line like this in your function setup()
  17. b=Backup("MyProject Ver 1.00")
  18.  
  19. Also create a dependency to the Backup project
  20. (click the + at upper right of screen, find Backup on the project list, and press it)
  21.  
  22. The utility will only back up when the string in brackets changes. It will create a new image with that name
  23. in your Dropbox folder (you may need to sync to see it). The reason for not backing up every time you run, is that when you are developing, you will try a lot of stuff, and you may get into a mess. You will then want to go back to your last stable version. So the idea is that whenever you are at a checkpoint, make a new version name.
  24.  
  25. NB each time you run, the utility will tell you how long it is since you last backed up. You can turn this off by adding a second parameter, false, when calling Backup.
  26.  
  27. RESTORING
  28.  
  29. Create a new project to hold your restored project, and put this in function setup()
  30. img=readImage("Dropbox:AAA")
  31. r=Restore(img)
  32.  
  33. Now change the image name to the Dropbox file you want to restore
  34.  
  35. BEFORE YOU RUN, create a dependency to the Backup project
  36. (click the + at upper right of screen, find Backup on the project list, and press it)
  37.  
  38. Now run, and when it's done, go back to the code and it should all be there.
  39.  
  40. Ignatz version 1.10 March 2013
  41.  
  42. --]]
  43.  
  44. --# Main
  45.  
  46. function setup()
  47. Tests()
  48. end
  49.  
  50. --[[
  51.  
  52. Backup file format - version 1
  53.  
  54. 3 bytes version number (currently = 1)
  55. 7 bytes size of file
  56.  
  57. then for each tab...
  58. 3 bytes char #!#
  59. tab name
  60. 3 bytes char #!#
  61. tab text
  62.  
  63. there is no need for a closing marker because we know the file size
  64.  
  65. NOTES
  66. The version number and size of file are stored digit by digit, so 125 takes 3 chars. It is not easily
  67. feasible to use ASCII encoding to make this more compact (ie 125 translates to one char of ASCII 125),
  68. because Codea only accepts about 80 valid characters. And we can afford a handful of extra bytes.
  69.  
  70. The tab delimiter similarly has to come from the 80 odd valid characters, and to make it unique, needs to be a character combination never found in normal program text.
  71.  
  72.  
  73.  
  74.  
  75. --]]
  76.  
  77.  
  78. --# Test
  79. function Tests()
  80. success=true
  81. success=Test_Headers() --tests that the file headers work
  82. if success then success=Test_Split("rfv") end --tests that a pile of concatenated tab text can be split accurately
  83. if success then success=Test_Backup("rfv") end --tests the hol system using the project name provided
  84. end
  85.  
  86. function Test_Headers()
  87. print("Testing file headers")
  88. result=".... passed"
  89. for i=1,100 do --do some tests
  90. --random string with specific prefix/suffix where problems are most likely to occur
  91. txt1="12345"..RandomText(20,1500).."67890"
  92. txt2=Backup:AddHeader(1,txt1)
  93. txt3=txt2..RandomText(0,100) --add chars to represent unused part of last col of image
  94. version,txt4=Restore:StripHeader(txt3)
  95. if version~="001" then result="Incorrect version" break end
  96. --if n~=string.len(txt1) then result="Incorrect length" break end
  97. if txt4~=txt1 then result="Incorrect text" break end
  98. end
  99. print(result)
  100. if result~=".... passed" then
  101. print("==== Original text ====")
  102. print(txt1)
  103. print("Size=",string.len(txt1))
  104. print("==== Text with header ====")
  105. print(txt2)
  106. print("==== Text with image padding ====")
  107. print(txt3)
  108. print("==== Decoded header details ====")
  109. print("Version",version)
  110. print("==== Decoded text ====")
  111. print(txt4)
  112. return false
  113. else return true
  114. end
  115. end
  116.  
  117. function RandomText(n1,n2,c)
  118. --create a random string of printable chars
  119. --if you provide c, it will be used instead (sometimes it's easier to look at a repeated char than randoms)
  120. Codes="!#&~*+,)/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ]_`abcdefghijklmnopqrstuvwxyz"
  121. local t=""
  122. m=math.random(n1,n2)
  123. if c==nil then
  124. for i=1,m do
  125. x=math.random(1,string.len(Codes))
  126. t=t..string.sub(Codes,x,x)
  127. end
  128. else
  129. t=string.rep(c,m)
  130. end
  131. return t
  132. end
  133.  
  134. function Test_Split(p)
  135. print("Testing code splitter")
  136. delim="#!#"
  137. tabs={}
  138. t={}
  139. tabs=listProjectTabs(p)
  140. n=#tabs
  141. txt=""
  142. for i=1,n do
  143. t[i]=readProjectTab(p..":"..tabs[i])
  144. txt=txt..delim..tabs[i]..delim..t[i]
  145. end
  146. result=".... passed"
  147. if n==0 then result="No tabs found" end
  148. for i=1,n do
  149. tabName,tabText,txt2=Restore:SplitCode(txt,delim,2)
  150. if tabName~=tabs[i] then result="Tab name incorrect" j=i break end
  151. if tabText~=t[i] or tabText=="" or tabText==nil then result="Text incorrect" j=i break end
  152. txt=txt2
  153. end
  154. print(result)
  155. if result~=".... passed" then
  156. print("Error in tab",j)
  157. print("==== Text passed ====")
  158. print(txt)
  159. print("==== Remaining text ====")
  160. print(txt2)
  161. print("==== Tab name ====")
  162. print("Correct name",tabs[j])
  163. print("Recovered name",tabName)
  164. print("==== Tab text ====")
  165. print("==Correct text==")
  166. print(t[j])
  167. print("==Recovered text==")
  168. print(tabText)
  169. return false
  170. else return true
  171. end
  172. end
  173.  
  174. function Test_Backup(p)
  175. print("Testing complete system")
  176. b=Backup("Test "..tostring(math.random(1,1000)),true,p)
  177. end
  178.  
  179.  
  180.  
  181.  
  182. --# Backup
  183. Backup = class()
  184.  
  185. --title is name of backup, backup only occurs when this changes
  186. --remind parameter if set to false turns off the reminder of how long it has been since last backup
  187. function Backup:init(title,remind,testproject)
  188. local t=readProjectData(title)
  189. if t~=nil and remind~=false then
  190. print("Last backup was",string.format("%.0f",os.difftime(os.time(),t)/60),"minutes ago")
  191. else
  192. local version="001"
  193. local delim="#!#" --tab and tabname delimiter
  194. self:Store(title,version,delim,testproject)
  195. saveProjectData(title,os.time())
  196. end
  197. end
  198.  
  199. function Backup:Store(title,version,delim,testproject)
  200. local origTxt=""
  201. local tabs
  202. if testproject==nil then tabs=listProjectTabs() else tabs=listProjectTabs(testproject) end
  203. for i,t in pairs(tabs) do
  204. local tt=t
  205. if testproject~=nil then tt=testproject..":"..t end
  206. origTxt=origTxt..delim..t..delim..readProjectTab(tt)
  207. end
  208. local txt=Backup:AddHeader(version,origTxt) --add file header
  209. --setup image
  210. local n=string.len(txt)
  211. local s=math.floor((n/3)^.5)+1
  212. local img=image(s,s)
  213. local row,col=0,1
  214. local e={}
  215. local m=0
  216. for i=0,n-1,3 do
  217. for j=1,3 do
  218. e[j]=string.byte(string.sub(txt,i+j,i+j)) or 0
  219. end
  220. row = row + 1
  221. if row>s then row=1 col = col + 1 end
  222. img:set(col,row,e[1],e[2],e[3],255)
  223. end
  224. local docName="Dropbox:"..title
  225. saveImage(docName,nil)
  226. saveImage(docName,img)
  227. print(title.." - backup made")
  228. --verify image by restoring and comparing result with original
  229. local r=Restore(img,1)
  230. if origTxt==r.txt then
  231. print("Backup verified")
  232. else
  233. print("ERROR - backup faulty")
  234. print(r.txt)
  235. end
  236. end
  237.  
  238. --parameters are version number and text
  239. function Backup:AddHeader(v,t)
  240. local n=tostring(string.len(t) + 10) -- 3 version bytes + 7 length bytes
  241. return Backup:Pad(tostring(v),3)..Backup:Pad(n,7)..t
  242. end
  243.  
  244. function Backup:Pad(str,n)
  245. local m=string.len(str)
  246. if m>=n then
  247. return str
  248. else
  249. return string.rep("0",n-m)..str
  250. end
  251. end
  252. --# Restore
  253. Restore = class()
  254.  
  255. function Restore:init(img,test)
  256. local txt=self:ReadImage(img)
  257. if txt=="ERROR" then
  258. print("ERROR: I couldn't read the backup")
  259. else
  260. local version,t=Restore:StripHeader(txt)
  261. if test==1 then self.txt=t return end
  262. if version=="001" then
  263. local delim="#!#" --tab and tabname delimiter
  264. Restore:SplitCode(t,delim,test)
  265. end
  266. end
  267. end
  268.  
  269. function Restore:ReadImage(img)
  270. if type(img)=="string" then img=readImage(img) end
  271. rows,cols=img.height,img.width
  272. t={}
  273. local n=0
  274. local r,g,b
  275. for col=1,cols do
  276. for row=1,rows do
  277. r,g,b=img:get(col,row)
  278. table.insert(t,string.char(r)) table.insert(t,string.char(g)) table.insert(t,string.char(b))
  279. n = n + 3
  280. end
  281. end
  282. return table.concat(t)
  283. end
  284.  
  285. function Restore:SplitCode(str,delim,test)
  286. local n=string.len(delim)
  287. local i,j=string.find(str,delim,nil,true)
  288. while i~=nil and j~=nil do
  289. local k=string.find(str,delim,j+1,true)
  290. local tabName=string.sub(str,j+1,k-1)
  291. i,j=string.find(str,delim,k+n,true)
  292. if i~=nil then
  293. tabText=string.sub(str,k+n,i-1)
  294. else
  295. tabText=string.sub(str,k+n)
  296. i=string.len(str)+1
  297. end
  298. if test==2 then
  299. return tabName,tabText,string.sub(str,i)
  300. elseif test==nil then
  301. print("Restoring",tabName)
  302. saveProjectTab(tabName,tabText)
  303. end
  304. end
  305. if test==nil then print("All done!") end
  306. end
  307.  
  308. function Restore:StripHeader(txt)
  309. local version=string.sub(txt,1,3)
  310. local n=tonumber(string.sub(txt,4,10))
  311. local t=string.sub(txt,11,n)
  312. return version,t
  313. end
');