View difference between Paste ID: VyxjeVmV and x6BRxH8c
SHOW: | | - or go back to the newest paste.
1
2
--# Notes
3
--# Notes
4
--[[
5
Version 1.2 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")
20+
b=Backup("MyProject Ver 100") -- NB no full stops or other "illegal" filename chars!
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 then --print reminder if backup exists and reminder not disabled
92
        if remind~=false then 
93
            print("Last backup was",string.format("%.0f",os.difftime(os.time(),t)/60),"minutes ago")
94
        end
95
    else
96
        self:Store(title,testproject)  --do backup
97
        saveProjectData(title,os.time()) --store date/time of backup
98
    end
99
end
100
101
function Backup001:Store(title,testproject)
102
    local txt=self:EncodeTabs(testproject)
103
    --setup image
104
    local n=string.len(txt)
105
    local s=math.floor((n/3)^.5)+1
106
    local img=image(s,s)  
107
    local row,col=0,1
108
    local e={}
109
    for i=0,n-1,3 do
110
        for j=1,3 do
111
            e[j]=string.byte(string.sub(txt,i+j,i+j)) or 0
112
        end
113
        row = row + 1
114
        if row>s then row=1 col = col + 1 end
115
        img:set(col,row,e[1],e[2],e[3],255)
116
    end
117
    local docName="Dropbox:"..title
118
    saveImage(docName,nil)
119
    saveImage(docName,img)
120
    print(title.." - backup made")
121
    --verify image by restoring and comparing result with original
122
    img2=readImage(docName)
123
    local r=Restore(img2,true)
124
    if txt==r.RecoveredText then 
125
        print("Backup verified") 
126
    else 
127
        print("ERROR - backup faulty")
128
    end
129
end
130
131
-- Private: Encode all tabs, including header.
132
-- Returns encoded string.
133
function Backup001:EncodeTabs(testproject)
134
    local SOH, STX, ETX = "\001", "\002", "\003"
135
    local encoded = self.version
136
    local tabs
137
    if testproject==nil then tabs = listProjectTabs() else tabs = listProjectTabs(testproject) end
138
    for i,t in pairs(tabs) do
139
        local tName=t  if testproject~=nil then tName=testproject..":"..t end
140
        encoded = encoded..SOH..t..STX..readProjectTab(tName)..ETX
141
    end
142
    encoded = encoded.."\000"
143
    return encoded
144
end
145
--# Restore
146
Restore = class()
147
148
function Restore:init(img,test)
149
    local r,g,b,a=img:get(1,1)
150
    if string.char(r)..string.char(g)..string.char(b)=="001" then
151
        local b=Restore001(img,test)
152
        self.RecoveredText=b.RecoveredText
153
    else
154
        print("ERROR - Unknown version "..r)
155
    end
156
end
157
158
159
--# Restore001
160
Restore001 = class()
161
162
--test=true if verifying a backup, we need to return the decoded string and not save any tabs
163
--if test is false or missing, this is a real restore, save the tabs
164
function Restore001:init(img,test)
165
    self.version="001"
166
    local txt=self:ReadImage(img)
167
    if txt=="ERROR" then print("ERROR: I couldn't read the backup") return end
168
    if test then self.RecoveredText=txt  return  end --if testing, return the string
169
    print(self:DecodeAndSave(txt))
170
end
171
172
function Restore001:ReadImage(img)
173
    if type(img)=="string" then img=readImage(img) end
174
    rows,cols=img.height,img.width
175
    t={}
176
    local n=0
177
    local r,g,b
178
    for col=1,cols do
179
        for row=1,rows do
180
            r,g,b=img:get(col,row)
181
            table.insert(t,string.char(r)) table.insert(t,string.char(g)) table.insert(t,string.char(b))
182
            n = n + 3
183
        end
184
    end
185
    --truncate at end of text
186
    txt=table.concat(t)
187
    local u=string.find(txt,"\000",nil,true)
188
    if u>0 then txt=string.sub(txt,1,u) end
189
    return txt
190
end
191
192
-- Private: Decode one tab of an encoded string.
193
--
194
-- encoded - encoded string.
195
-- start   - start index inside |encoded|.
196
--
197
-- Returns tab name, tab contents, next index on success.
198
-- Returns nil on failure.
199
function Restore001:DecodeTab(encoded, start)
200
    local SOH, STX, ETX = "\001", "\002", "\003"
201
    local SOHidx = string.find(encoded, SOH, start)
202
    local STXidx = string.find(encoded, STX, start)
203
    local ETXidx = string.find(encoded, ETX, start)
204
    -- assert some basic format properties
205
    if not SOHidx or not STXidx or not ETXidx then
206
        return nil
207
    end
208
    if SOHidx ~= start or ETXidx < STXidx then
209
        return nil
210
    end
211
    -- decode
212
    local tabname  = string.sub(encoded, SOHidx + 1, STXidx - 1)
213
    local contents = string.sub(encoded, STXidx + 1, ETXidx - 1)
214
    local nextidx  = ETXidx + 1
215
    return tabname, contents, nextidx
216
end
217
218
-- Private: Decode all tabs of an encoded string and write them
219
-- into the project.
220
--
221
-- encoded - encoded string, including header.
222
-- Returns success or error message on failure.
223
--   Tabs may have been saved until that point.
224
function Restore001:DecodeAndSave(encoded)
225
    local tabidx = string.len(self.version)+1
226
    local result = "All done!"
227
    repeat
228
        -- check for end of data
229
        if string.sub(encoded, tabidx, tabidx) == "\000"  then
230
            tabidx = nil
231
        else
232
            local tabname, contents
233
            tabname, contents, tabidx = self:DecodeTab(encoded, tabidx)
234
            if tabname ~= nil then
235
                saveProjectTab(tabname, contents)
236
            else
237
                result = "ERROR - Decoding error, recovery incomplete"
238
            end
239
        end
240
    until tabidx == nil
241
    return result
242
end