'***************************************************************************** 
'*                                                                           * 
'*   __    _           ___  __      __    _     __               __          * 
'*  |  \  / \  |    | |    |  \    |  \  / \   /  \  |  / |   | |  \         * 
'*  |__/ |   | |    | |__  |__/    |__/ |   | |      |_/  |   | |__/         * 
'*  |    |   | | /\ | |    | \     |  \ |---| |      | \  |   | |            * 
'*  |     \_/  |/  \| |___ |  \    |__/ |   |  \__/  |  \  \_/  |     V1.0   * 
'*                                                                           * 
'*                                                                           * 
'*                       written by    CHRISTIAN KARGL                       * 
'*                                                                           * 
'*                             Copyright   ©2002                             * 
'*                                                                           * 
'*              Implementation V0.9ß: (AMOS Backup) 16.Sep 1999              * 
'*                             V1.00:  6.Okt 2001 - 16.Feb 2002              * 
'*                             V1.01: 20.Feb 2002 - 23.Feb 2002              * 
'*                                                                           * 
'*        Compiled:  23.Feb 2002 (Options: WB, NoScrn, IncLib, IncErr)       * 
'*                                                                           * 
'***************************************************************************** 
'
'
'  Linked List for Directory Tree
' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
'  NALLOC (4,+ 0) .... bytes allocated by this item
'  NXTPTR (4,+ 4) .... address to next item (else NULL)
'  PARPTR (4,+ 8) .... address to parent dir item (or NULL for root) 
'  FILPTR (4,+12) .... address to sorted file list (or NULL for none)
'  FLBYTE (4,+16) .... number of bytes for all files 
'  SLBYTE (4,+20) .... number of bytes selected (init with 0)
'  NFILES (2,+24) .... number of files for this directory item 
'  DIRLVL (1,+26) .... directory level = number of sub-dirs from root
'  DRSEL$ (1,+27) .... directory selection (same as file selection)
'  FLSEL$ (N,+28) .... one byte per file: "@" when selected, else "."
'  TNAME$ (T,+N+28) .. select info, tree structure and directory name
'
'
'  Linked List for Files 
' ~~~~~~~~~~~~~~~~~~~~~~~
'  NALLOC (4) ... bytes allocated by this item 
'  NXTPTR (4) ... address to next item (else NULL) 
'  LENGTH (4) ... file length
'  FNAME$ (?) ... file name
'
'
'  Linked List for Backup History
' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
'  NALLOC (4) ... bytes allocated by this item 
'  NXTPTR (4) ... address to next item (else NULL) 
'  MTEXT$ (?) ... message text to display
'
'
'  Interface programs and variables
' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
' --+------
' 1 | main program 
'   |  0 ... (STR) program version, 3 characters 
'   |  1 ... (STR) compilation date, 11 characters 
'   +----
'   | Label0 file selection and setup for backup 
'   |   0 ... (STR) root dir 
'   |   1 ... (STR) backup disc labels 
'   |   2 ... (NUM) top position of dir list 
'   |   3 ... (NUM) total number of dirs 
'   |   4 ... (NUM) top position of file list
'   |   5 ... (NUM) total number of files
'   |   9 ... (NUM) temp var for all cycle buttons 
'   +----
'   | Label1 reading source directory (creating dir tree)
'   +----
'   | Label2 backup/restore progress panel (with history list) 
'   |   0 ... (NUM) top position of history list 
'   |   1 ... (NUM) total number of history lines
'   |   2 ... (NUM) number for first message (title, progress) 
'   +----
'   | Label3 final backup report (buttons to saving history) 
'   +----
'   | Label4 restore options (with overwrite cycle button) 
'   |   0 ... (STR) custom destination to restore files
'   +----
'   | UI BB zonenr,xpos,ypos,textreference (BorderButton)
'   | UI CB zonenr,xpos,ypos,basemsgnum,totalmsgnum (CycleButton)
' --+------
' 2 | alert with buttons 
'   |  0 ... (STR) alert text
'   |  1 ... (NUM) selected button: 1=OKAY, 2=CANCEL 
' --+------
' 3 | information box
'   |  0 ... (STR) information text
'
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'
Set Buffer 12
'
Screen Close 0
'Break Off : Request Off : Amos Lock : Close Workbench : Wait 100
'
Global VERS$,COMP$,PDIR$,C0$,LF$,SQUEST$,SQFLSH$,YTOP,SECS
Global TREEROOTADRS,MXTREE,FILELISTROOT,MXFILE,NUMDIRS,NUMFILES
Global NUMBYTES,SELBYTES,SQUEFF,REDIRECT,DISKFREE,DISKUSED,MXDISKBYTE
Global NUMDISKS,SPLITNUM,HISTROOTADRS,HISTADRS,NUMHIST,BYTEWRIT,BYTEREAD
Global BKNAME$,SOURCE$,DESTPATH$,DESTFILE$,BKHDRINF$,BKHDRDAT$,NOSIMULATE
'
' init: set version and compilation date, load program data
'
PDIR$=Dir$ : VERS$="1.01" : COMP$="23-Feb-2002" : FL$="PowerBackup.Dat"
'
If Not Exist(FL$) : Edit : End If : Load FL$ : Resource Bank 16 : Hide 
YTOP=52 : SECS=50 : If Ntsc : YTOP=26 : SECS=60 : End If : OKAY=False
Resource Screen Open 1,640,74,0 : Screen Display 1,128,YTOP+64,640,74
Cls 0 : SQUEST$=Chr$(60)+Chr$(54)+Chr$(50)+Chr$(48)+Chr$(46) : C0$=Chr$(0)
BKHDRINF$=Chr.l$($2BAD4815) : BKHDRDAT$=Chr.l$($E0F63C97) : LF$=Chr$(10)
SQFLSH$="(00F,1)(70F,1)(F0F,1)(F07,1)(F00,1)(F70,1)(FF0,1)(7F0,1)(0F0,1)(0F7,1)(0FF,1)(07F,1)"
'
' check for OS 2.0+, RAM disk, hard disk, powerpacker.library
'
If Deek(Leek(4)+Equ("LIB_VERSION"))<37
   INFO["Sorry, you need OS2.0+ for this program!"]
Else 
   If Not Exist("RAM:")
      INFO["Can't find RAM-Disk! Please check your system!"]
   Else 
      If Not Exist("Dh0:")
         INFO["No Hard-disk found! Please reboot your system!"]
      Else : OKAY=True
         If Not Exist("LIBS:powerpacker.library")
            INFO["Need 'powerpacker.library' for compression!"]
         End If 
      End If 
   End If 
End If 
If OKAY
   '
   ' system okay, check my data
   '
   '+-                                                    -+
   '|  SNIPPED -> Make your own security check if you want |
   '+-                                                    -+
   If OKAY
      '
      ' okay; check for 30 floppy buffers; init main screen
      '
      Erase 20 : FLOPPYADDBUFFERS[0] : NBUFFERS=Param
      If Param<30 : FLOPPYADDBUFFERS[30-NBUFFERS] : End If 
      For NR=0 To 31 : Colour NR,0 : Next : Screen Display 1,,YTOP+158,,
      Dialog Open 1,1 : Vdialog$(1,0)=VERS$ : Vdialog$(1,1)=COMP$
      NR=Dialog Run(1) : Wait Vbl : Unpack 11 : Show : Change Mouse 4
      Fade 3 To -1 : Wait 45 : Limit Mouse
      '
      ' main loop (simple, eh?)
      '
      Do 
         SL=Dialog(1) : Exit If SL=4
         If SL
            Dialog Freeze 1 : Limit Mouse 128,YTOP To 447,YTOP+231
            On SL Proc MN_ABOUT,MN_BACKUP,MN_RESTORE
            Dialog Unfreeze 1 : Limit Mouse : Wait Vbl : NR=Free
         End If 
      Loop : Fade 3 : Wait 45 : Dialog Close : Screen Close 1
      If NBUFFERS<30 : FLOPPYADDBUFFERS[NBUFFERS-30] : End If 
   Else 
      INFO["Program data corrupted!"]
   End If 
End If 
Dir$=PDIR$ : Erase All : Edit 
'Dir$=PDIR$ : Erase All : System 
'
' Main Procedures
'
Procedure MN_ABOUT
   '
   ' init scrolled graphics on hidden screen
   '
   PAGE=4 : Resource Screen Open 2,256,384,0 : Screen Hide 2 : Cls 6
   AD=Start(10) : Unpack 11 : Cls 6,8,4 To 12,70 : Cls 6,238,4 To 240,70
   Get Block 8,8,5,240,64 : Cls 6 : Put Block 8,0,0 : Ink 2,6 : YP=64
   For PG=1 To PAGE
      For LI=0 To 5 : CL=Peek(AD) : LL=Peek(AD+1) : TT$=Peek$(AD+2,LL)
         Add AD,LL+2 : XX=116-4*LL : YY=YP+9 : Add YP,10 : Gr Writing 0
         Ink 0 : Text XX-1,YY,TT$ : Text XX+1,YY,TT$ : Text XX,YY-1,TT$
         Text XX,YY+1,TT$ : Ink CL : Text XX,YY,TT$ : Gr Writing 1
      Next : Add YP,4
   Next : Put Block 8,0,YP : Inc PAGE : Del Block 8 : Screen 1
   Get Block 8,248,4,384,66 : Change Mouse 5 : Ink 0,3 : YP=0
   Set Pattern 30 : Bar 248,4 To 631,69 : Set Pattern 0 : PG=0
   '
   ' scroll info text; wait for key or mouse button each page 
   '
   Do : Inc PG
      For LI=0 To 63 : Inc YP
         Screen Copy 2,4,YP,230,YP+64 To 1,12,5 : Wait Vbl 
      Next : Exit If PG=PAGE
      Clear Key : Repeat : Until Mouse Click or Inkey$>""
   Loop : Change Mouse 4 : Put Block 8 : Del Block 8 : Wait Vbl 
   Screen Close 2
   '
End Proc
Procedure MN_BACKUP
   '
   ' init file selection; appear screen 
   '
   SOURCE$="" : BKNAME$="Backup"
   REDIRECT=False : INSTALL=False : TREEROOTADRS=0 : FILELISTROOT=0
   NUMDIRS=0 : MXTREE=0 : MXFILE=0 : NUMFILES=0 : NUMBYTES=0 : SELBYTES=0
   SQUEFF=0 : Resource Screen Open 2,640,232,0 : Cls 0 : Pen 2
   Paper 6 : Screen Display 2,128,YTOP+248,640,232 : Reserve Zone 2
   Set Zone 1,32,48 To 319,175 : Set Zone 2,368,48 To 607,159 : REDRAW=8
   Dialog Open 2,1,16,4096 : Vdialog$(2,0)=SOURCE$ : Vdialog$(2,1)=BKNAME$
   Vdialog(2,3)=0 : Vdialog(2,5)=0 : SL=Dialog Run(2,0) : CONTINUE=1
   For YP=240 To 0 Step -8 : Screen Display 2,,YTOP+YP,, : Wait Vbl : Next 
   '
   ' main backup selection loop 
   '
   Repeat 
      '
      ' check for click on list areas; else check dialog 
      '
      If Mouse Click
         MZ=Mouse Zone : PS=(Y Screen(Y Mouse)-48)/8
         If MZ=1
            If DRTOP+PS<MXTREE : MK=Mouse Key
               If MK=1 : SL=13 : Else If MK=2 : SL=14 : End If 
            End If 
         Else If MZ=2
            If FLTOP+PS<MXFILE : SL=15 : End If 
         End If 
      Else 
         SL=Dialog(2)
      End If 
      '
      ' check for selection
      '
      If SL
         Dialog Freeze 2 : Window 0 : Wait Vbl 
         If SL=1
            '
            ' change source dir: if exist read new source directory
            '
            TT$=Path$(Rdialog$(2,1)+"/")
            If Exist(TT$) : MT=MXTREE
               ND=NUMDIRS : NF=NUMFILES : NB=NUMBYTES : SB=SELBYTES
               Get Block 8,0,0,640,232 : Ink 0,3 : Set Pattern 30
               Bar 0,0 To 639,231 : Set Pattern 0 : Dialog Open 3,1
               SL=Dialog Run(3,1) : MAKE_DIRTREE[TT$] : Dialog Close 3
               '
               ' delete old tree and update vars, else restore old values 
               '
               If Param>0 : SOURCE$=TT$ : DRTOP=0 : FLTOP=0 : REDRAW=7
                  AD=TREEROOTADRS : TREEROOTADRS=Param : DELETE_DIRTREE[AD]
                  FILELISTROOT=TREEROOTADRS : MXFILE=Deek(FILELISTROOT+24)
               Else : MXTREE=MT
                  NUMDIRS=ND : NUMFILES=NF : NUMBYTES=NB : SELBYTES=SB
                  If Param : INFO["Sorry, I'm low on memory!"]
                  Else : INFO["Directory tree setup aborted."] : End If 
               End If : Put Block 8 : Del Block 8 : Gosub DISPLAY : Wait Vbl 
            End If : Dialog Update 2,1,SOURCE$
         Else If SL=2
            '
            ' remove path character; ask if user wants to redirect backup
            '
            TT$=Rdialog$(2,2) : PS=Instr(TT$,">>")+2
            If PS=2
               BKNAME$=TT$-" "-"/"-":" : REDIRECT=False
            Else If PS>3
               If Exist(Mid$(TT$,PS))
                  ALERT["Redirect backup to '"+Mid$(TT$,PS)+"' ?"]
                  If Param=1 : BKNAME$=TT$ : REDIRECT=True : End If 
               End If 
            End If : Dialog Update 2,2,BKNAME$
         Else If SL<6
            DRTOP=Vdialog(2,2) : REDRAW=1 : Gosub DISPLAY
         Else If SL<9
            FLTOP=Vdialog(2,4) : REDRAW=2 : Gosub DISPLAY
         Else If SL=9
            INSTALL=Rdialog(2,9) : REDRAW=0 : Gosub DISPLAY
         Else If SL=10
            SQUEFF=Rdialog(2,10)/2 : REDRAW=8 : Gosub DISPLAY
         Else If SL<13
            '
            ' check to leave selection loop
            '
            CONTINUE=SL-12
            If SL=11 and SELBYTES=0
               INFO["No data selected to backup!"] : CONTINUE=1
            Else If REDIRECT
               TT$=Disc Info$(Mid$(BKNAME$,Instr(BKNAME$,">>")+2))
               MX=Val(Mid$(TT$,Instr(TT$,":")+1)) : NR=SELBYTES
               If SQUEFF : NR=(NR/100)*Asc(Mid$(SQUEST$,SQUEFF)) : End If 
               '
               ' check if enough disc space when redirecting
               '
               If MX<NR
                  INFO["Not enough disc space to redirect!"] : CONTINUE=1
               Else If MX<104*(NR/100)
                  ALERT["Free disc space may be insufficent. Continue?"]
                  If Param>1 : CONTINUE=1 : End If 
               End If 
            End If 
         Else If SL=13
            '
            ' left click on tree area -> select dir
            '
            MOVE_TO_ENTRY[TREEROOTADRS,DRTOP+PS]
            If Param : ADRS=Param : REDRAW=3
               SNEW=110-Peek(ADRS+27) : MX=Peek(ADRS+26)
               Repeat 
                  NR=Deek(ADRS+24)+1 : AD=ADRS+27
                  While NR : Poke AD,SNEW : Inc AD : Dec NR : Wend 
                  If SNEW=64 : NR=Leek(ADRS+16) Else NR=0 : End If 
                  Add SELBYTES,NR-Leek(ADRS+20) : Loke ADRS+20,NR
                  ADRS=Leek(ADRS+4) : Exit If ADRS=0
               Until Peek(ADRS+26)<=MX : Gosub DISPLAY
            End If 
         Else If SL=14
            '
            ' right click on tree area -> display files
            '
            MOVE_TO_ENTRY[TREEROOTADRS,DRTOP+PS]
            If Param : FILELISTROOT=Param : FLTOP=0
               MXFILE=Deek(Param+24) : REDRAW=7 : Gosub DISPLAY
            End If 
         Else If SL=15
            '
            ' click on file area -> select file
            '
            MOVE_TO_ENTRY[Leek(FILELISTROOT+12),FLTOP+PS]
            If Param
               BYTE=Leek(Param+8) : ADRS=FILELISTROOT : REDRAW=3
               AD=ADRS+FLTOP+PS+28 : SNEW=110-Peek(AD) : Poke AD,SNEW
               If SNEW=46 : BYTE=-BYTE : End If : Add SELBYTES,BYTE
               NR=Leek(ADRS+20)+BYTE : If NR : SL=64 Else SL=46 : End If 
               Loke ADRS+20,NR : Poke ADRS+27,SL : Gosub DISPLAY
            End If 
         End If : Dialog Unfreeze 2 : Wait Vbl 
      End If 
   Until CONTINUE<1 : Dialog Close 2
   '
   ' if not aborted, check for redirection
   '
   If CONTINUE : DESTPATH$="DF0:"
      If REDIRECT : PS=Instr(BKNAME$,">>") : TT$=Mid$(BKNAME$,PS+2)
         BKNAME$=Left$(BKNAME$,PS-1) : NR=Asc(Right$(TT$,1)) : Ink 0,3
         If NR<>47 and NR<>58 : TT$=TT$+"/" : End If : DESTPATH$=TT$
         Set Pattern 30 : Bar 0,0 To 639,231 : Set Pattern 0 : BYTE=488
         NR=1760 : DISKUSED=0 : Dialog Open 3,1 : SL=Dialog Run(3,6)
         Do 
            SL=Dialog(3) : Exit If SL>3
            If SL
               If Rdialog(3,1) : NR=3520 Else NR=1760 : End If 
               If Rdialog(3,2) : BYTE=512 Else BYTE=488 : End If 
               MX=BYTE*(NR-4) : Window 0 : Print At(61,13);Lsstr$(MX/1024,4)
               SB=Min(1024*Rdialog(3,3),MX/2) : Dialog Update 3,3,SB/1024
            End If 
         Loop : CONTINUE=(SL=4)
         MXDISKBYTE=BYTE*((1730*NR)/1760-6) : Dialog Close 3 : Wait Vbl 
      End If 
      If CONTINUE
         '
         ' init history messages
         '
         Timer=0 : HISTROOTADRS=0 : HISTADRS=0 : NUMHIST=0 : DRTOP=0
         GTDATETIME : HISTORYENTRY["Starting backup: "+Param$] : NF=0
         Dec SQUEFF : HISTORYENTRY["Writing to: '"+DESTPATH$+"'"+LF$]
         '
         ' calculate number of selected files 
         '
         NUMDISKS=1 : BYTEWRIT=0 : BYTEREAD=0 : MX=0 : ADRS=TREEROOTADRS
         While ADRS : AD=ADRS+28 : NR=Deek(ADRS+24) : ADRS=Leek(ADRS+4)
            While NR : Add MX,Peek(AD)/64 : Inc AD : Dec NR : Wend 
         Wend : TT$=Chr.l$(MX xor $A45B918D)+Chr.l$(MX xor $C2370E6F)
         '
         ' init backup dialog, ask for first disc and write init header 
         '
         Fade 2 : Wait 30 : Cls 0 : Dialog Open 2,1 : Vdialog(2,0)=0
         Vdialog(2,1)=NUMHIST : Vdialog(2,2)=28 : RN=Dialog Run(2,2)
         DISPLAY_HISTORY[0] : Fade 3 To 1 : Wait 45 : REQUEST_BACKDISC[1]
         DISKUSED=SB+16 : Print #1,"PPBACK10"+TT$; : ADRS=TREEROOTADRS
         '
         ' add required data and program to start from first disc 
         '
         If INSTALL : Add DISKUSED,169824
            SOURCE$=Extpath$(PDIR$) : TT$="powerpacker.library"
            Mkdir "Libs" : Trap File Copy "LIBS:"+TT$ To "Libs/"+TT$
            TT$=Command Name$ : File Copy SOURCE$+TT$ To TT$
            TT$=TT$+".info" : File Copy SOURCE$+TT$ To TT$
            TT$="PowerBackup.Dat" : File Copy SOURCE$+TT$ To TT$
            HISTORYENTRY["Copying program and data to first disc."+LF$]
         End If 
         '
         ' read through directory tree and backup selected files
         '
         While ADRS : FLADRS=Leek(ADRS+12) : Loke ADRS+12,0
            If FLADRS>0 and Peek(ADRS+27)=64 : CREATE_DIRTREE_PATH[ADRS]
               TT$=Param$ : FADR=FLADRS : NR=Deek(ADRS+24) : AD=ADRS+28
               While NR
                  If Peek(AD)=64 : PS=Max(NUMHIST-8,0)
                     BACKUP_FILE[TT$+Peek$(FADR+12,Leek(FADR),C0$)]
                     '
                     ' update dialog
                     '
                     If Vdialog(2,0)=PS
                        DRTOP=Max(NUMHIST-8,0) : DISPLAY_HISTORY[DRTOP]
                     Else If Dialog(2)
                        DRTOP=Vdialog(2,0) : DISPLAY_HISTORY[DRTOP]
                     End If 
                     Dialog Update 2,1,DRTOP,8,NUMHIST : Inc NF
                     Vdialog(2,1)=NUMHIST : Dialog Update 2,2,0,NF,MX
                  End If : Inc AD : Dec NR : FADR=Leek(FADR+4)
               Wend 
            End If : DELETE_LIST[FLADRS] : ADRS=Leek(ADRS+4)
         Wend 
         '
         ' done; close file and display final report; save/delete history 
         '
         Print #1,BKHDRINF$;"¦END"; : Close 1 : Ink 0,3 : Set Pattern 30
         Bar 0,0 To 639,231 : Set Pattern 0 : SL=Timer/SECS : NR=SL mod 60
         Dialog Close 2 : Wait 5 : Dialog Open 2,1 : RN=Dialog Run(2,3)
         Wait Vbl : GTDATETIME : HISTORYENTRY["Finished backup: "+Param$]
         SL=SL/60 : BYTEREAD=Max(BYTEREAD,100) : TT$=Lsstr$(NUMDISKS,4)
         HISTORYENTRY[Resource$(34)+Space$(10)+TT$] : Print At(55,11);TT$
         TT$=Lsstr$(BYTEWRIT,8) : HISTORYENTRY[Resource$(35)+" "+TT$]
         Print At(51,12);TT$ : TT$=Lsstr$(BYTEWRIT/(BYTEREAD/100),3)+"%"
         HISTORYENTRY[Resource$(36)+Space$(6)+TT$] : Print At(55,13);TT$
         TT$=Lzstr$(SL/60,2)+":"+Lzstr$(SL mod 60,2)+":"+Lzstr$(NR,2)
         HISTORYENTRY[Resource$(37)+Space$(7)+TT$] : Print At(51,14);TT$
         Repeat : SL=Dialog(2) : Until SL>0 : Dialog Close 2 : Wait 5
         If SL=1 : HISTORY_SAVE : End If : DELETE_LIST[HISTROOTADRS]
      End If 
   End If 
   '
   ' delete directory tree
   '
   For YP=0 To 240 Step 8 : Screen Display 2,,YTOP+YP,, : Wait Vbl : Next 
   Screen Close 2 : DELETE_DIRTREE[TREEROOTADRS] : Pop Proc
   '
   '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   ' redraw backup selection display
   '
   DISPLAY:
   NR=SELBYTES : If INSTALL : AD=169824 Else AD=0 : End If 
   If SQUEFF : NR=(NR/100)*Asc(Mid$(SQUEST$,SQUEFF)) : End If 
   Add NR,AD : NUMDDISK=NR/858880+1 : NUMHDISK=NR/1717760+1
   Print At(11,23);Lsstr$(NUMDIRS,6);At(27,23);Lsstr$(NUMFILES,6)
   Print At(43,23);Lsstr$(NUMBYTES,9);At(66,23);Lsstr$(SELBYTES,9)
   Print At(39,25);Lsstr$(NUMDDISK,4);At(48,25);Lsstr$(NUMHDISK,4)
   '
   ' check other updates
   '
   If REDRAW and 1 : DISPLAY_DIRS[DRTOP] : End If 
   If REDRAW and 2 : DISPLAY_FILES[FLTOP] : End If 
   If REDRAW and 4 : Dialog Update 2,1,SOURCE$
      Vdialog(2,3)=MXTREE : Dialog Update 2,5,DRTOP,16,MXTREE
      Vdialog(2,5)=MXFILE : Dialog Update 2,8,FLTOP,16,MXFILE
   End If : REDRAW=0
   Return 
   '
End Proc
Procedure MN_RESTORE
   '
   ' init restore options; appear screen
   '
   Resource Screen Open 2,640,232,0
   Cls 0 : Paper 6 : Pen 2 : Screen Display 2,128,YTOP+248,640,232
   DESTPATH$="" : Dialog Open 2,1 : Vdialog$(2,0)="" : SL=Dialog Run(2,4)
   For YP=240 To 0 Step -8 : Screen Display 2,,YTOP+YP,, : Wait Vbl : Next 
   '
   ' wait for actions; read settings
   '
   Repeat 
      SL=Dialog(2)
      If SL=3 : TT$=Rdialog$(2,3)
         If TT$>"" : OK=Exist(TT$)
            If OK=False : ALERT["Directory not found! Create?"]
               If Param=1 : CREATE_DIR[TT$] : OK=True : End If 
            End If 
            If OK : DESTPATH$=Extpath$(TT$)
               Dialog Update 2,2,1 : Dialog Update 2,3,TT$
            End If 
         End If 
      End If 
   Until SL>4 : NOSIMULATE=(Rdialog(2,1)=0)
   REDIRECT=Rdialog(2,2) : OVRTYP=Rdialog(2,4)/2 : Dialog Close 2
   If Not NOSIMULATE : REDIRECT=False : OVRTYP=2 : End If 
   '
   ' if not aborted: start dialog and request first disc
   '
   If SL=5
      Fade 2 : Wait 30 : Cls 0 : Dialog Open 2,1 : Vdialog(2,0)=0
      Vdialog(2,1)=0 : Vdialog(2,2)=46 : SL=Dialog Run(2,2) : Wait Vbl 
      Dialog Freeze 2 : Fade 3 To 1 : Wait 45 : Dialog Open 7,3
      Vdialog$(7,0)="Please insert first disc into drive DF0:"
      SL=Dialog Run(7) : Repeat : Until Exist("DF0:") : Dialog Close 7
      Wait Vbl : SOURCE$="DF0:Backup0001.DAT0" : TT$=Disc Info$("DF0:")
      BKNAME$=Left$(TT$,Instr(TT$,":")-1) : PS=Instr(BKNAME$,"_#")
      '
      ' check header informations
      '
      If PS : BKNAME$=Left$(BKNAME$,PS-1)
         If Exist(SOURCE$) : Open In 1,SOURCE$ : HD$=Input$(1,8)
            TT$=Input$(1,8) : CHCK=Asc.l(Right$(TT$,4)) xor $C2370E6F
            NUMFILES=Asc.l(Left$(TT$,4)) xor $A45B918D : NUMDISKS=1
            '
            ' if header okay, init history messages
            '
            If HD$="PPBACK10" and NUMFILES=CHCK : Timer=0 : HTOP=0
               HISTROOTADRS=0 : HISTADRS=0 : NUMHIST=0 : SPLITNUM=0
               GTDATETIME : HISTORYENTRY["Starting restore: "+Param$]
               '
               ' check redirection and simulation; unfreeze dialog
               '
               If NOSIMULATE : TT$="Writing data to "
                  If REDIRECT : TT$=TT$+"'"+DESTPATH$+"'."
                  Else : TT$=TT$+"original location." : End If 
               Else : TT$="Simulating file restoration." : End If 
               HISTORYENTRY[TT$+LF$] : DISPLAY_HISTORY[0] : FLOKAY=0
               FLERRS=0 : FLSKIP=0 : FLOVER=0 : Vdialog(2,1)=NUMHIST
               Dialog Update 2,1,DRTOP,8,NUMHIST : Dialog Unfreeze 2
               '
               ' main restore loop; get next file infos; check headers
               '
               For FL=1 To NUMFILES
                  SL=Max(NUMHIST-8,0) : TT$=Input$(1,32)
                  If Left$(TT$,4)=BKHDRINF$
                     INFO$=Mid$(TT$,5,26) : Exit If Left$(INFO$,4)="¦END"
                     FLEN=Asc.w(Right$(TT$,2)) : FILE$=Input$(1,FLEN)
                     If Input$(1,4)=BKHDRDAT$ : OK=True
                        '
                        ' check dir, check existing files, restore file
                        '
                        If NOSIMULATE
                           If REDIRECT
                              FILE$=DESTPATH$+Mid$(FILE$,Instr(FILE$,":")+1)
                           End If 
                           If Exist(FILE$) : OK=(OVRTYP>1)
                              If OVRTYP=1
                                 ALERT["Overwrite file?"] : OK=(Param=1)
                              End If : If OK : Inc FLOVER : End If 
                           End If 
                        End If 
                        If OK : RESTOR_FILE[FILE$,INFO$]
                           If Param : Inc FLOKAY Else Inc FLERRS : End If 
                        Else : Inc FLSKIP
                           FILE$="Skipping '"+FILE$+"'" : Gosub SKIPDATA
                        End If 
                     Else : Inc FLERRS
                        FILE$="Error! Missing data header." : Gosub SKIPDATA
                     End If 
                  Else : Inc FLERRS
                     FILE$="Error! Missing info header." : Gosub SKIPDATA
                  End If 
                  '
                  ' update dialog sliders
                  '
                  If Vdialog(2,0)=SL
                     DRTOP=Max(NUMHIST-8,0) : DISPLAY_HISTORY[DRTOP]
                  Else If Dialog(2)
                     DRTOP=Vdialog(2,0) : DISPLAY_HISTORY[DRTOP]
                  End If 
                  Vdialog(2,1)=NUMHIST : Dialog Update 2,1,DRTOP,8,NUMHIST
                  Dialog Update 2,3,0,FL,NUMFILES : Wait Vbl 
               Next 
               '
               ' done; display final report; save/delete history
               '
               Dialog Freeze 2 : SL=Timer/SECS : GTDATETIME
               HISTORYENTRY["Finished restore: "+Param$] : Ink 0,3
               Set Pattern 30 : Bar 0,0 To 639,231 : Set Pattern 0
               Dialog Open 3,1 : RN=Dialog Run(3,5) : Wait Vbl 
               TT$=Lsstr$(FLOKAY,6) : HISTORYENTRY[Resource$(49)+" "+TT$]
               Print At(51,11);TT$ : TT$=Lsstr$(FLSKIP,6) : NR=SL mod 60
               HISTORYENTRY[Resource$(50)+"    "+TT$] : Print At(51,12);TT$
               TT$=Lsstr$(FLOVER,6) : HISTORYENTRY[Resource$(51)+"   "+TT$]
               Print At(51,13);TT$ : TT$=Lsstr$(FLERRS,6) : SL=SL/60
               HISTORYENTRY[Resource$(52)+"   "+TT$] : Print At(51,14);TT$
               TT$=Lzstr$(SL/60,2)+":"+Lzstr$(SL mod 60,2)+":"+Lzstr$(NR,2)
               HISTORYENTRY[Resource$(53)+" "+TT$] : Print At(49,15);TT$
               Repeat : SL=Dialog(3) : Until SL>0 : Dialog Close 3 : Wait 5
               If SL=1 : HISTORY_SAVE : End If : DELETE_LIST[HISTROOTADRS]
            Else 
               INFO["First backup data has corrupted file header!"]
            End If : Close 1
         Else 
            INFO["Can't find first backup data file!"]
         End If 
      Else 
         INFO["Wrong backup disc name format!"]
      End If 
      Dialog Close 2
   End If 
   '
   ' done; remove screen
   '
   For YP=0 To 240 Step 8 : Screen Display 2,,YTOP+YP,, : Wait Vbl : Next 
   Screen Close 2 : Pop Proc
   '
   ' special sub routine for skipping current data to next file 
   '
   SKIPDATA:
   Change Mouse 5 : Flash 18,SQFLSH$ : Cls 6,56,104 To 584,128
   PS=Len(FILE$) : HISTORYENTRY["("+Ct Time$(Current Time)+") "+FILE$]
   TT$="Searching for header information." : HISTORYENTRY["  "+TT$+LF$]
   If PS>66 : FILE$=Left$(FILE$,10)+Right$(FILE$,56) : End If 
   Reserve As Work 20,2048 : AD=Start(20) : Fill AD To AD+2048,0
   Print At(7,13);FILE$;At(9,14);TT$;".." : PS=Pof(1) : SZ=Lof(1)-PS
   '
   ' read bytes of file; check for end of file; set file position 
   '
   Do 
      If Eof(1) : Inc SPLITNUM
         Close 1 : Wait 5 : Right$(SOURCE$,1)=Chr$(SPLITNUM+48)
         If Not Exist(SOURCE$)
            Inc NUMDISKS : REQUEST_RESTDISC[NUMDISKS]
         End If : Open In 1,SOURCE$ : Wait 5 : PS=0 : SZ=Lof(1)
      End If 
      Loke AD,Leek(AD+2044) : Poke$ AD+4,Input$(1,Min(SZ,2044))
      SZ=Max(SZ-2044,0) : FLEN=Hunt(AD To AD+2048,BKHDRINF$)
      If FLEN>0 : Add PS,FLEN-AD-4 : Exit : End If : Add PS,2044
   Loop : Pof(1)=PS
   Wait 5 : Flash Off : Colour 18,$C60 : Change Mouse 4 : Erase 20
   Return 
   '
End Proc
Procedure GTDATETIME
   '
   ' formated date & time: "www dd-mmm-yyyy at hh:mm:ss." 
   '
   DATE=Current Date : TT$=" at "+Ct Time$(Current Time)+"."
   TT$=Left$(Cd Date$(DATE),11)+Lsstr$(Cd Year(DATE),4)+TT$
End Proc[TT$]
'
' Backup Procedures
'
Procedure DISPLAY_DIRS[DTOP]
   '
   ' display directory tree starting at DTOP
   '
   MOVE_TO_ENTRY[TREEROOTADRS,DTOP] : AD=Param
   If AD : ITEM=0 : YP=6
      '
      ' okay, print next 16 entries
      '
      While ITEM<16
         TT$=Peek$(AD+Deek(AD+24)+28,Leek(AD),C0$) : YY=8*YP
         If AD=FILELISTROOT : Paper 4 Else Paper 0 : End If 
         If Peek(AD+27)=64 : Pen 1 Else Pen 3 : End If : PS=1
         Do 
            A$=Mid$(TT$,PS,1) : Inc PS : XX=16*PS
            Exit If A$="»" : NR=Instr("|+-",A$)
            If NR : Resource Unpack NR+35,XX,YY
            Else Cls 0,XX,YY To XX+16,YY+8 : End If 
         Loop : NR=40-2*PS : A$=Mid$(TT$,Min(PS,NR))
         '
         ' >>> PROBLEM: DirName + DirStruktur zu lang ??
         '
         Print At(2*PS,YP);A$;Paper$(0);Space$(NR-Len(A$));
         Inc YP : Inc ITEM : AD=Leek(AD+4) : Exit If AD=0
      Wend : If ITEM<16 : Cls 0,32,8*YP To 320,176 : End If 
      Wait Vbl : Paper 6 : Pen 2
   End If 
End Proc
Procedure DISPLAY_FILES[FTOP]
   '
   ' display next 16 file names starting at FTOP
   '
   MOVE_TO_ENTRY[Leek(FILELISTROOT+12),FTOP]
   If Param : ADRS=Param
      ITEM=0 : YP=6 : Paper 0 : STRT=FILELISTROOT+FTOP+28
      While ITEM<14
         FL$=Peek$(ADRS+12,22,C0$) : FLEN=Leek(ADRS+8)
         If Peek(STRT+ITEM)=64 : Pen 1 Else Pen 3 : End If 
         Print At(46,YP);FL$;Space$(22-Len(FL$));Lsstr$(FLEN,8)
         Inc YP : ADRS=Leek(ADRS+4) : Exit If ADRS=0 : Inc ITEM
      Wend : If ITEM<14 : Cls 0,368,8*YP To 608,160 : End If 
      Paper 6 : Pen 2
   Else 
      Cls 0,368,48 To 608,160
   End If 
End Proc
Procedure REQUEST_BACKDISC[DN]
   '
   ' wait for next disc 
   '
   TT$=Lzstr$(DN,4) : SPLITNUM=0 : DISKUSED=0 : Dir$="RAM:"
   NAME$=BKNAME$+"_#"+TT$ : DESTFILE$="Backup"+TT$+".DAT0"
   If REDIRECT
      '
      ' redirecting -> create new directory for next floppy
      '
      TT$=DESTPATH$+NAME$ : DISKFREE=MXDISKBYTE
      If Not Exist(TT$) : Mkdir TT$ : End If : Dir$=TT$
   Else 
      '
      ' else wait for a new real floppy disc in drive DF0: 
      '
      TIME=Timer : Get Block 8,0,0,640,232 : Wait Vbl : Ink 0,3
      Set Pattern 30 : Bar 0,0 To 639,231 : Set Pattern 0
      Do 
         '
         ' remove disc from drive DF0: first; ask for next backup disc
         '
         If Exist("DF0:") : Dialog Open 3,3
            Vdialog$(3,0)="Please remove disc from DF0:" : RR=Dialog Run(3)
            Wait Vbl : Repeat : Until Not Exist("DF0:") : Dialog Close 3
         End If : Dialog Open 3,3
         Vdialog$(3,0)="Please insert backup disc"+Str$(DN)+" into DF0:"
         RR=Dialog Run(3) : Wait Vbl : Repeat : Until Exist("DF0:")
         Dialog Close 3 : TT$=Disc Info$("DF0:") : DF$=Right$(TT$,10)-" "
         TT$=Left$(TT$,Len(TT$)-10)
         '
         ' ask to use it; relabel disc (DOS function returns 0 on failure)
         '
         ALERT[DF$+" bytes free on disc '"+TT$+"'. Use this disc?"]
         If Param=1 : DISKFREE=Val(DF$) : TT$="DF0:" : Dreg(1)=Varptr(TT$)
            Dreg(2)=Varptr(NAME$) : Exit If Doscall(Lvo("Relabel"))
            INFO["ERROR! Can't relabel disc in drive DF0:"] : Wait 10
         End If 
      Loop 
      Put Block 8 : Del Block 8 : Wait Vbl : Dir$="DF0:" : Timer=TIME
   End If : RR=Free : Trap Kill DESTFILE$ : Open Out 1,DESTFILE$
   '
End Proc
Procedure BACKUP_FILE[FILE$]
   '
   ' write file FILE$ (with full path) into backup file 
   '
   HISTORYENTRY["("+Ct Time$(Current Time)+") Loading '"+FILE$+"'"]
   Examine Object FILE$ : FLSIZE=Object Size : FLSUMM=0 : SQSIZE=0
   SQSUMM=0 : Cls 6,56,104 To 584,128 : Print At(7,13);Right$(FILE$,66)
   '
   ' check for data to load 
   '
   If FLSIZE>0 : BLEN=(FLSIZE+3) and $FFFFFFFC
      Print At(9,14);"Loading... "; : Trap Reserve As Work 20,BLEN
      If Errtrap=0
         AD=Start(20) : Fill AD To AD+BLEN,0 : Bload FILE$,AD
         Wait 5 : FLSUMM=Bank Checksum(20) : Print "Done."
         '
         ' squash file with powerpacker.library ? 
         '
         If SQUEFF>=0 and FLSIZE>99 : Change Mouse 5
            Flash 18,SQFLSH$ : Print At(11,15);"Squashing... ";
            Wait 5 : Trap Pptodisk "RAM:Temp",20,SQUEFF : Flash Off 
            Colour 18,$C60 : Change Mouse 4 : Wait Vbl : SQSIZE=FLSIZE
            If Errtrap=0
               Open In 2,"Ram:Temp" : SQSIZE=Lof(2) : Close 2 : Wait Vbl 
               If SQSIZE<FLSIZE
                  BLEN=(SQSIZE+3) and $FFFFFFFC : Reserve As Work 20,BLEN
                  AD=Start(20) : Fill AD To AD+BLEN,0 : Bload "Ram:Temp",AD
               End If 
            End If 
         End If 
      End If 
   End If 
   '
   ' print messages and save data from bank 20 to backup file 
   '
   If Length(20)>0
      If FLSIZE=0
         INFO$="Storing empty file" : Locate 9,14 : HMSG$=INFO$
      Else 
         SZ$=" ("+Mid$(Str$(FLSIZE),2)+" bytes)"
         If SQSIZE=0 : INFO$="Saving original file" : Locate 11,15
            HMSG$=INFO$+SZ$ : SQSUMM=FLSUMM : SQSIZE=FLSIZE
         Else If SQSIZE<FLSIZE
            SQSUMM=Bank Checksum(20) : INFO$="Saving squashed file"
            TT$=Mid$(Str$(Int(SQSIZE/(FLSIZE/1000.0))/10.0),2)+"% ="
            HMSG$=INFO$+" ("+TT$+Str$(SQSIZE)+"/"+Mid$(SZ$,3)
         Else 
            INFO$="FAILED! Saving original file" : SQSIZE=FLSIZE
            HMSG$="Squashing "+INFO$+SZ$ : SQSUMM=FLSUMM
         End If 
      End If 
      Print INFO$;"... "; : FL=Len(FILE$) : FLPY=(DESTPATH$="DF0:")
      Add DISKUSED,36+FL : Add BYTEREAD,FLSIZE : Add BYTEWRIT,SQSIZE
      Print #1,BKHDRINF$+Chr.l$(FLSIZE)+Chr.l$(FLSUMM)+Chr.l$(SQSIZE);
      Print #1,Chr.l$(SQSUMM)+Chr.l$(Object Date)+Chr.l$(Object Time);
      Print #1,Chr.w$(Object Protection)+Chr.w$(FL)+FILE$+BKHDRDAT$;
      '
      ' check disc space and file limit (216K); write data to backup file
      '
      While SQSIZE>0
         If FLPY : DISKUSED=DISKFREE-Dfree : End If 
         If DISKFREE-DISKUSED<4096 : Close 1
            Wait 5 : Inc NUMDISKS : REQUEST_BACKDISC[NUMDISKS]
         End If 
         If Lof(1)>221184 : Inc SPLITNUM
            Right$(DESTFILE$,1)=Chr$(SPLITNUM+48) : Close 1
            Wait 5 : Trap Kill DESTFILE$ : Open Out 1,DESTFILE$
         End If : BLEN=Min(SQSIZE,1024)
         Print #1,Peek$(AD,BLEN); : Add AD,BLEN : Add SQSIZE,-BLEN
         Add DISKUSED,BLEN : Dialog Update 2,3,0,DISKUSED/64,DISKFREE/64
      Wend : Print "Done." : Wait 20
   Else 
      HMSG$="FAILED! Not enough memory to load file!" : Print HMSG$
   End If : HISTORYENTRY["  "+HMSG$+LF$]
   '
End Proc
'
' Restore Procedures 
'
Procedure CREATE_DIR[DR$]
   '
   ' create directory structure DR$ if not exist
   '
   DR$=Extpath$(DR$) : PS=Instr(DR$,"/")
   While PS : TT$=Left$(DR$,PS-1)
      If Not Exist(TT$) : Mkdir TT$ : End If : PS=Instr(DR$,"/",PS+1)
   Wend 
End Proc
Procedure REQUEST_RESTDISC[DN]
   '
   ' wait for next backup disc in drive DF0:
   '
   TIME=Timer : Get Block 8,0,0,640,232 : Wait Vbl 
   Ink 0,3 : Set Pattern 30 : Bar 0,0 To 639,231 : Set Pattern 0
   TT$=Lzstr$(DN,4) : SPLITNUM=0 : NAME$=BKNAME$+"_#"+TT$+":"
   Do 
      '
      ' ask for next backup disc; check for backup file
      '
      Dialog Open 3,3 : Vdialog$(3,0)="Please insert '"+NAME$+"' into DF0:"
      RR=Dialog Run(3) : Repeat : Until Exist(NAME$) : Dialog Close 3
      SOURCE$="DF0:Backup"+TT$+".DAT0" : Exit If Exist(SOURCE$) : Wait 10
      '
      ' no backup file found -> remove disc from drive DF0:
      '
      INFO["Hey, can't find backup data I expected!"] : Dialog Open 3,3
      Vdialog$(3,0)="Please remove disc from DF0:" : RR=Dialog Run(3)
      Wait Vbl : Repeat : Until Not Exist("DF0:") : Dialog Close 3
   Loop 
   RR=Free : Put Block 8 : Del Block 8 : Wait Vbl : Timer=TIME
   '
End Proc
Procedure RESTOR_FILE[FILE$,INFO$]
   '
   ' update history; extract file informations
   '
   Reserve As Work 20,26
   AD=Start(20) : Poke$ AD,INFO$ : FLSIZE=Leek(AD) : FLSUMM=Leek(AD+4)
   SQSIZE=Leek(AD+8) : SQSUMM=Leek(AD+12) : FLDATE=Leek(AD+16)
   FLTIME=Leek(AD+20) : FLPROT=Deek(AD+24) : Cls 6,56,104 To 584,128
   Erase 20 : Print At(7,13);Right$(FILE$,66) : Locate 9,14
   HISTORYENTRY["("+Ct Time$(Current Time)+") Loading '"+FILE$+"'"]
   '
   ' special check for empty files
   '
   If FLSIZE=0 : OKAY=True
      MS$="Okay." : HMSG$="Saving empty file... " : Print HMSG$;
      If NOSIMULATE
         TT$=Path$(FILE$) : If Not Exist(TT$) : CREATE_DIR[TT$] : End If 
         MS$="Failed!" : OKAY=False : Trap Open Out 2,FILE$
         If Errtrap=0 : Close 2 : Set Object Date FILE$,FLDATE,FLTIME
            Protect Object FILE$,FLPROT : MS$="Done." : OKAY=True
         End If 
      End If : HMSG$=HMSG$+MS$ : Print MS$ : Wait 5
   Else 
      SZ=SQSIZE : BLEN=(SZ+3) and $FFFFFFFC : Print "Loading data... ";
      Reserve As Work 20,BLEN : AD=Start(20) : Fill AD To AD+BLEN,0
      Repeat 
         '
         ' check next backup file or new disc ? 
         '
         If Eof(1) : Close 1 : Wait 5
            Inc SPLITNUM : Right$(SOURCE$,1)=Chr$(SPLITNUM+48)
            If Not Exist(SOURCE$)
               Inc NUMDISKS : REQUEST_RESTDISC[NUMDISKS]
            End If : Open In 1,SOURCE$
         End If 
         '
         ' writing data into temporary bank 20
         '
         Poke$ AD,Input$(1,Min(SZ,1024)) : Wait Vbl : SZ=Max(SZ-1024,0)
         Add AD,1024 : Dialog Update 2,2,0,(SQSIZE-SZ)/256,SQSIZE/256
      Until SZ<1
      Wait 10 : Print "Done." : Locate 11,15 : HMSG$="" : OKAY=False
      '
      ' check if data are (power) packed -> try to unpack it 
      '
      If SQSIZE<FLSIZE : HMSG$="Unpacking... " : Print HMSG$;
         If Bank Checksum(20)=SQSUMM
            Change Mouse 5 : Flash 0,SQFLSH$ : Trap Ppunpack 20 To 22
            Wait 5 : Flash Off : Colour 0,0 : Change Mouse 4
            '
            ' if unpacking successful, replace original data in bank 20
            '
            If Errtrap=0 : BLEN=(FLSIZE+3) and $FFFFFFFC
               Erase 20 : Reserve As Work 20,BLEN : AD=Start(22)
               Copy AD,AD+BLEN To Start(20) : Wait 5 : Erase 22
            Else : FLSIZE=0
               HMSG$="Unpacking error! (Code:"+Str$(Errtrap)+")"
            End If 
         Else 
            HMSG$="Data checksum error!" : FLSIZE=0
         End If 
      End If 
      '
      ' if (unpacked) data checksum okay, save file
      '
      If FLSIZE
         If Bank Checksum(20)=FLSUMM : MS$="Okay." : OKAY=True
            If NOSIMULATE : OKAY=False : TT$=Path$(FILE$)
               If Not Exist(TT$) : CREATE_DIR[TT$] : End If 
               MS$="Saving... " : Print MS$; : HMSG$=HMSG$+MS$
               AD=Start(20) : Trap Bsave FILE$,AD To AD+FLSIZE
               If Errtrap=0 : Set Object Date FILE$,FLDATE,FLTIME
                  Protect Object FILE$,FLPROT : MS$="Done." : OKAY=True
               Else : MS$="Failed! (Code:"+Str$(Errtrap)+")" : End If 
            End If : HMSG$=HMSG$+MS$ : Print MS$; : Wait 10
         Else 
            HMSG$="File checksum error!"
         End If 
      End If 
   End If : HISTORYENTRY["  "+HMSG$+LF$]
   '
End Proc[OKAY]
'
' History Procedures 
'
Procedure HISTORY_SAVE
   '
   ' save history messages
   '
   FL$=Fsel$("*.TXT",BKNAME$+"Log.TXT","Save history messages")
   If FL$>"" : Open Out 2,FL$ : AD=HISTROOTADRS
      While AD
         Print #2,Peek$(AD+8,Leek(AD),C0$);LF$; : AD=Leek(AD+4)
      Wend : Close 2
   End If 
End Proc
Procedure HISTORYENTRY[MS$]
   '
   ' add new history message (note: GLOBAL address variables!!!)
   '
   ALLOCMEM[Len(MS$)+8]
   If Param : ADRS=Param : Inc NUMHIST
      If HISTADRS : Loke HISTADRS+4,ADRS : End If : Poke$ ADRS+8,MS$
      If HISTROOTADRS=0 : HISTROOTADRS=ADRS : End If : HISTADRS=ADRS
   End If 
End Proc
Procedure DISPLAY_HISTORY[HTOP]
   '
   ' display backup history messages
   '
   MOVE_TO_ENTRY[HISTROOTADRS,HTOP] : AD=Param : Paper 0 : Pen 3
   For YP=4 To 11 : TT$=Peek$(AD+8,64,C0$) : LL=Len(TT$)
      If Right$(TT$,1)=LF$ : Dec LL : TT$=Left$(TT$,LL) : End If 
      Print At(7,YP);TT$;Space$(64-LL) : AD=Leek(AD+4) : Exit If AD=0
   Next : Paper 6 : Pen 2
End Proc
'
' Directory Tree Procedures
'
Procedure MAKE_DIRTREE[ROOT$]
   '
   ' dynamically build directory tree starting at ROOT$ 
   ' returns TREEROOT on success, 0 if user abort, -1 if out of memory
   '
   ' Important vars:
   '   TREEROOT ..... pointer to created directory tree structure 
   '   TREETHIS ..... pointer to current dir item of tree structure 
   '   STACKADR ..... pointer to top element of "dirs-to-do" stack
   '   DRLSTADR ..... pointer to first element of sub dirs list 
   '
   ' Directory stack: 
   '   NALLOC (4,+ 0) ... bytes allocated for this item (ALLOCMEM)
   '   PRVPTR (4.+ 4) ... address to previous item (it's a stack!)
   '   PARPTR (4,+ 8) ... address to parent item of dir tree
   '   DNAME$ (D,+12) ... tree structure, », complete path of dir 
   '
   QUIT=False : TT$="»"+ROOT$ : ALLOCMEM[Len(TT$)+9] : ADRS=Param
   Poke$ ADRS+12,TT$ : STACKADR=ADRS : TREEROOT=0 : TREETHIS=0
   MXTREE=0 : NUMDIRS=0 : NUMFILES=0 : NUMBYTES=0 : SELBYTES=0
   '
   ' as long as there are items on stack read and remove top element
   '
   While STACKADR
      Inc MXTREE : AD=STACKADR : TT$=Peek$(AD+12,Leek(AD),C0$)
      PARNTADR=Leek(AD+8) : PS=Instr(TT$,"»") : TREE$=Left$(TT$,PS-1)+"»"
      DR$=Mid$(TT$,PS+1) : STACKADR=Leek(STACKADR+4) : DEALLOCMEM[AD]
      If TREEROOT : TT$=Filename$(DR$) Else TT$=DR$ : End If : NBYTES=0
      '
      ' read directory, put dirs and files on seperate lists 
      '
      TR$=TREE$+TT$+C0$ : If Right$(DR$,1)<>":" : DR$=DR$+"/" : End If 
      NDIRS=0 : NFILES=0 : DRLSTADR=0 : FLISTADR=0 : Examine Dir DR$
      Do 
         FL$=Examine Next$ : Exit If FL$=""
         If Object Type>0 : Inc NDIRS
            '
            ' sort directory into linked list of sub dirs (descending) 
            '
            ALLOCMEM[Len(FL$)+5] : ADRS=Param : Poke$ ADRS+8,FL$
            If DRLSTADR : STRT=DRLSTADR : PREV=0 : FL$=Upper$(FL$)
               While Upper$(Peek$(STRT+8,Leek(STRT),C0$))>FL$
                  PREV=STRT : STRT=Leek(STRT+4) : Exit If STRT=0
               Wend 
               If PREV
                  Loke PREV+4,ADRS : If STRT : Loke ADRS+4,STRT : End If 
               Else : DRLSTADR=ADRS : Loke ADRS+4,STRT : End If 
            Else 
               DRLSTADR=ADRS
            End If 
         Else 
            '
            ' sort file into linked list (ascending) 
            '
            ALLOCMEM[Len(FL$)+13] : ADRS=Param : Add NBYTES,Object Size
            Loke ADRS+8,Object Size : Poke$ ADRS+12,FL$ : Inc NFILES
            If FLISTADR : STRT=FLISTADR : PREV=0 : FL$=Upper$(FL$)
               While Upper$(Peek$(STRT+12,Leek(STRT),C0$))<FL$
                  PREV=STRT : STRT=Leek(STRT+4) : Exit If STRT=0
               Wend 
               If PREV
                  Loke PREV+4,ADRS : If STRT : Loke ADRS+4,STRT : End If 
               Else : FLISTADR=ADRS : Loke ADRS+4,STRT : End If 
            Else 
               FLISTADR=ADRS
            End If 
         End If 
      Loop 
      '
      ' add new entry to directory tree; update counters 
      '
      ALLOCMEM[Len(TR$)+NFILES+24] : ADRS=Param : NR=NFILES+1
      If TREETHIS : Loke TREETHIS+4,ADRS Else TREEROOT=ADRS : End If 
      Loke ADRS+8,PARNTADR : Loke ADRS+12,FLISTADR : Loke ADRS+16,NBYTES
      Doke ADRS+24,NFILES : Poke ADRS+26,PS : TREETHIS=ADRS : AD=ADRS+27
      While NR : Poke AD,46 : Inc AD : Dec NR : Wend : Poke$ AD,TR$
      Add NUMDIRS,NDIRS : Add NUMFILES,NFILES : Add NUMBYTES,NBYTES
      '
      ' check if any sub dirs found; if so recalcuate tree structure 
      '
      If DRLSTADR
         NR=Len(TREE$)-1 : TR$=""
         While NR : AC=Asc(Mid$(TREE$,NR)) : Dec NR
            If AC=32 or AC=45 : TR$=" "+TR$ Else TR$="|"+TR$ : End If 
         Wend 
         '
         ' put sub dirs on stack; free memory from sub dir items
         '
         STRT=DRLSTADR : DR$="-»"+DR$
         Repeat : TT$=TR$+DR$+Peek$(STRT+8,Leek(STRT),C0$)
            ALLOCMEM[Len(TT$)+9] : ADRS=Param : Left$(DR$,1)="+"
            Loke ADRS+4,STACKADR : Loke ADRS+8,TREETHIS : Poke$ ADRS+12,TT$
            STACKADR=ADRS : AD=STRT : STRT=Leek(STRT+4) : DEALLOCMEM[AD]
         Until STRT=0
      End If 
      '
      ' check abort; check memory: cancel if lower than 64K left 
      '
      Print At(30,14); Using "######";NUMDIRS
      Print At(46,14); Using "########";NUMFILES
      NR=Chip Free+Fast Free : Print At(37,16); Using "########";NR
      QUIT=Dialog(3) : If NR<65536 : QUIT=True : End If : Exit If QUIT
   Wend 
   '
   ' user abort ? -> free dir stack and already created dir tree
   '
   If QUIT : DELETE_DIRTREE[TREEROOT] : DELETE_LIST[STACKADR]
      If QUIT<0 : TREEROOT=-1 Else TREEROOT=0 : End If 
   End If 
   '
End Proc[TREEROOT]
Procedure DELETE_DIRTREE[ADRS]
   '
   ' removes directory tree (with file list) starting at ADRS 
   '
   While ADRS : DELETE_LIST[Leek(ADRS+12)]
      AD=ADRS : ADRS=Leek(ADRS+4) : DEALLOCMEM[AD]
   Wend 
End Proc
Procedure CREATE_DIRTREE_PATH[ADRS]
   '
   ' extract full path name of the entry at ADRS (ends with "/")
   '
   While ADRS
      TT$=Peek$(ADRS+Deek(ADRS+24)+28,Leek(ADRS),C0$)
      If Right$(TT$,1)<>":" : TT$=TT$+"/" : End If 
      PT$=Mid$(TT$,Instr(TT$,"»")+1)+PT$ : ADRS=Leek(ADRS+8)
   Wend 
End Proc[PT$]
Procedure MOVE_TO_ENTRY[ROOTADRS,DTOP]
   '
   ' moves to entry DTOP of directory tree or file list 
   '
   If ROOTADRS>0 : AD=ROOTADRS
      While DTOP : AD=Leek(AD+4) : Dec DTOP : Exit If AD=0 : Wend 
   End If 
End Proc[AD]
'
' System Memory Procedures 
'
Procedure ALLOCMEM[BYTE]
   '
   ' allocate memory from system: 
   '  -> add 4 bytes for number of bytes allocated information
   '  -> attributes: ANY =0 , PUBLIC =1 , CLEAR =65536 ==> $10001 
   '  -> write number of bytes into first 4 bytes (NEVER CHANGE!) 
   ' RETURN: start address, or 0 if memory block not available
   '
   BYTE=(BYTE+7) and $FFFFFFFC : Dreg(0)=BYTE : Dreg(1)=$10001
   ADRS=Execall(-198) : If ADRS : Loke ADRS,BYTE : End If 
   '
End Proc[ADRS]
Procedure DEALLOCMEM[ADRS]
   '
   ' free memory allocated by procedure ALLOCMEM[] above
   '  -> first read allocated bytes from first 4 bytes (CAUTION!) 
   '  -> free memory (read required addresses before calling this)
   '
   If ADRS : Areg(1)=ADRS : Dreg(0)=Leek(ADRS) : RT=Execall(-210) : End If 
   '
End Proc
Procedure DELETE_LIST[ADRS]
   '
   ' remove any linked list created with ALLOCMEM[] using DEALLOCMEM[]
   '  -> IMPORTANT: first 4 bytes are amount of bytes allocated 
   '  -> IMPORTANT: second 4 bytes are address to next item 
   '
   While ADRS>0 : AD=ADRS : ADRS=Leek(ADRS+4) : DEALLOCMEM[AD] : Wend 
   '
End Proc
Procedure FLOPPYADDBUFFERS[NR]
   '
   ' call DOS function: add NR buffers for internal floppy "DF0:" 
   ' RETURN: number of buffers, or 0 if failed (drive maybe busy ?) 
   '
   TT$="DF0:" : Dreg(1)=Varptr(TT$)
   Dreg(2)=NR : RS=Doscall(Lvo("AddBuffers"))
   '
   ' if function returns -1, IoErr has number of buffers (obsolete?)
   '
   If RS<0 : RS=Doscall(Lvo("IoErr")) : End If 
   '
End Proc[RS]
'
' Global Procedures
'
Procedure ALERT[MESS$]
   '
   ' alert program; interrupt Dialog Box with <CTRL>+'C' and Break Off !!!
   '
   Dialog Open 6,2 : Vdialog$(6,0)=MESS$ : SL=Dialog Run(6) : Wait Vbl 
   Repeat : Multi Wait : SL=Vdialog(6,1) : Until SL : Dialog Close 6
End Proc[SL]
Procedure INFO[MESS$]
   '
   ' info box; interrupt Dialog Box with <CTRL>+'C' and Break Off !!! 
   '
   Dialog Open 7,3 : Vdialog$(7,0)=MESS$ : RR=Dialog Run(7) : Wait Vbl 
   Repeat : Multi Wait : Until Mouse Click or Inkey$>"" : Dialog Close 7
End Proc

