document.write('
Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1.  
  2. --# Notes
  3. --# Notes
  4. --[[
  5. Version 1.1 March 2013
  6. by Ignatz and Codeslinger
  7.  
  8. This utility backs up and restores your projects for you, by storing them as images in your Dropbox folder.
  9. Its features are as follows:
  10. 1. backs up up behind the scenes every time you change the version number
  11. 2. tells you how long it is since the last backup
  12. 3. restores your backup into a new project, tabs and all
  13.  
  14. INSTALLING IT
  15. Copy this project to your Codea, named as Backup
  16.  
  17. BACKING UP
  18.  
  19. Include a line like this in your function setup()
  20. b=Backup("MyProject Ver 1.00")
  21.  
  22. Also create a dependency to the Backup project
  23. (click the + at upper right of screen, find Backup on the project list, and press it)
  24.  
  25. The utility will only back up when the string in brackets changes. It will create a new image with that name
  26. 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.
  27.  
  28. 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.
  29.  
  30. RESTORING
  31.  
  32. Create a new project to hold your restored project, and put this in function setup()
  33. img=readImage("Dropbox:AAA")
  34. r=Restore(img)
  35.  
  36. Now change the image name to the Dropbox file you want to restore
  37.  
  38. BEFORE YOU RUN, create a dependency to the Backup project
  39. (click the + at upper right of screen, find Backup on the project list, and press it)
  40.  
  41. Now run, and when it's done, go back to the code and it should all be there.
  42.  
  43. --]]
  44.  
  45. --# Main
  46.  
  47. -- NEVER restore in this function, even for testing, because it may overwrite code
  48. function setup()
  49. --b=Backup("test302a",false,"Jeopardy")
  50. end
  51.  
  52. --[[
  53.  
  54. Backup file format - version 1
  55.  
  56. 3 bytes version number (currently = 001)
  57.  
  58. then for each tab...
  59. char 1
  60. tab name
  61. char 2
  62. tab text
  63. char 3
  64.  
  65. final char is 0
  66.  
  67. --]]
  68.  
  69.  
  70. --# Backup
  71. Backup = class()
  72.  
  73. function Backup:init(title,remind,testproject)
  74. local version="001"
  75. if version=="001" then
  76. local b=Backup001(title,remind,testproject)
  77. end
  78. end
  79.  
  80.  
  81.  
  82. --# Backup001
  83. Backup001 = class()
  84.  
  85. --title is name of backup, backup only occurs when this changes
  86. --remind parameter if set to false turns off the reminder of how long it has been since last backup
  87. --testproject allows us to backup a named project instead of the open project, for use in debugging
  88. function Backup001:init(title,remind,testproject)
  89. self.version="001"
  90. local t=readProjectData(title)
  91. if t~=nil and remind~=false then --print reminder if backup exists and reminder not disabled
  92. print("Last backup was",string.format("%.0f",os.difftime(os.time(),t)/60),"minutes ago")
  93. else
  94. self:Store(title,testproject) --do backup
  95. saveProjectData(title,os.time()) --store date/time of backup
  96. end
  97. end
  98.  
  99. function Backup001:Store(title,testproject)
  100. local txt=self:EncodeTabs(testproject)
  101. --setup image
  102. local n=string.len(txt)
  103. local s=math.floor((n/3)^.5)+1
  104. local img=image(s,s)
  105. local row,col=0,1
  106. local e={}
  107. for i=0,n-1,3 do
  108. for j=1,3 do
  109. e[j]=string.byte(string.sub(txt,i+j,i+j)) or 0
  110. end
  111. row = row + 1
  112. if row>s then row=1 col = col + 1 end
  113. img:set(col,row,e[1],e[2],e[3],255)
  114. end
  115. local docName="Dropbox:"..title
  116. saveImage(docName,nil)
  117. saveImage(docName,img)
  118. print(title.." - backup made")
  119. --verify image by restoring and comparing result with original
  120. local r=Restore(img,true)
  121. if txt==r.RecoveredText then
  122. print("Backup verified")
  123. else
  124. print("ERROR - backup faulty")
  125. end
  126. end
  127.  
  128. -- Private: Encode all tabs, including header.
  129. -- Returns encoded string.
  130. function Backup001:EncodeTabs(testproject)
  131. local SOH, STX, ETX = "\\001", "\\002", "\\003"
  132. local encoded = self.version
  133. local tabs
  134. if testproject==nil then tabs = listProjectTabs() else tabs = listProjectTabs(testproject) end
  135. for i,t in pairs(tabs) do
  136. local tName=t if testproject~=nil then tName=testproject..":"..t end
  137. encoded = encoded..SOH..t..STX..readProjectTab(tName)..ETX
  138. end
  139. encoded = encoded.."\\000"
  140. return encoded
  141. end
  142. --# Restore
  143. Restore = class()
  144.  
  145. function Restore:init(img,test)
  146. local r,g,b,a=img:get(1,1)
  147. if string.char(r)..string.char(g)..string.char(b)=="001" then
  148. local b=Restore001(img,test)
  149. self.RecoveredText=b.RecoveredText
  150. else
  151. print("ERROR - Unknown version "..r)
  152. end
  153. end
  154.  
  155.  
  156. --# Restore001
  157. Restore001 = class()
  158.  
  159. --test=true if verifying a backup, we need to return the decoded string and not save any tabs
  160. --if test is false or missing, this is a real restore, save the tabs
  161. function Restore001:init(img,test)
  162. self.version="001"
  163. local txt=self:ReadImage(img)
  164. if txt=="ERROR" then print("ERROR: I couldn't read the backup") return end
  165. if test then self.RecoveredText=txt return end --if testing, return the string
  166. print(self:DecodeAndSave(txt))
  167. end
  168.  
  169. function Restore001:ReadImage(img)
  170. if type(img)=="string" then img=readImage(img) end
  171. rows,cols=img.height,img.width
  172. t={}
  173. local n=0
  174. local r,g,b
  175. for col=1,cols do
  176. for row=1,rows do
  177. r,g,b=img:get(col,row)
  178. table.insert(t,string.char(r)) table.insert(t,string.char(g)) table.insert(t,string.char(b))
  179. n = n + 3
  180. end
  181. end
  182. --truncate at end of text
  183. txt=table.concat(t)
  184. local u=string.find(txt,"\\000",nil,true)
  185. if u>0 then txt=string.sub(txt,1,u) end
  186. return txt
  187. end
  188.  
  189. -- Private: Decode one tab of an encoded string.
  190. --
  191. -- encoded - encoded string.
  192. -- start - start index inside |encoded|.
  193. --
  194. -- Returns tab name, tab contents, next index on success.
  195. -- Returns nil on failure.
  196. function Restore001:DecodeTab(encoded, start)
  197. local SOH, STX, ETX = "\\001", "\\002", "\\003"
  198. local SOHidx = string.find(encoded, SOH, start)
  199. local STXidx = string.find(encoded, STX, start)
  200. local ETXidx = string.find(encoded, ETX, start)
  201. -- assert some basic format properties
  202. if not SOHidx or not STXidx or not ETXidx then
  203. return nil
  204. end
  205. if SOHidx ~= start or ETXidx < STXidx then
  206. return nil
  207. end
  208. -- decode
  209. local tabname = string.sub(encoded, SOHidx + 1, STXidx - 1)
  210. local contents = string.sub(encoded, STXidx + 1, ETXidx - 1)
  211. local nextidx = ETXidx + 1
  212. return tabname, contents, nextidx
  213. end
  214.  
  215. -- Private: Decode all tabs of an encoded string and write them
  216. -- into the project.
  217. --
  218. -- encoded - encoded string, including header.
  219. -- Returns success or error message on failure.
  220. -- Tabs may have been saved until that point.
  221. function Restore001:DecodeAndSave(encoded)
  222. local tabidx = string.len(self.version)+1
  223. local result = "All done!"
  224. repeat
  225. -- check for end of data
  226. if string.sub(encoded, tabidx, tabidx) == "\\000" then
  227. tabidx = nil
  228. else
  229. local tabname, contents
  230. tabname, contents, tabidx = self:DecodeTab(encoded, tabidx)
  231. if tabname ~= nil then
  232. saveProjectTab(tabname, contents)
  233. else
  234. result = "ERROR - Decoding error, recovery incomplete"
  235. end
  236. end
  237. until tabidx == nil
  238. return result
  239. end
');