'===========================================================================
' Subject: RECOVER FILES                      Date: 10-18-00 (11:29)       
'  Author: Randall L. Glass                   Code: PB                     
'  Origin: rlglass@yahoo.com                Packet: DOS.ABC
'===========================================================================
'----------------------------------------------------------------------------
'                                   RCOPY
'                             By Randall L Glass
'                               CopyRight 2000
'
'----------------------------------------------------------------------------
'
' This program is freeware. You may use it anyway you want as long as you
' give me credit in your program or documentation.
'
'----------------------------------------------------------------------------
' This Program will recover files from an crashed hard drive. As long as you
' have defrag your hard drive often;it does a good job of recovering files.
' This program is for fat16 formated hard drives only.
'----------------------------------------------------------------------------
' Be extremely carefull on changing this program. If you do the wrong thing
' like use the interrrupt for write instead of the interrupt for reading;
' You could WIPE OUT out your hard drive.
' (Especially since the two interrupts are alot alike. int 21 ah = 25 OR,
' int ah = 26)
'----------------------------------------------------------------------------
' A good project would be to convert it to fat32. Probably would be easy.
' Send me a copy if you do.
'
' You can contact me at rlglass@yahoo.com
'
'-----------------------------------------------------------------------------
'  Credits:
'  Marc van den Dikkenberg                     
'  Andrew S. Gibson             
'  Christy Gemmell
'  Rick Elbers
'----------------------------------------------------------------------------
$STACK 32000
DEFINT A-Z
TYPE DirType
     FileName AS STRING * 8
     Ext AS STRING * 3
     Attribute AS BYTE
     Reserved AS STRING * 8
     DirClusters AS WORD
     DirTime AS WORD
     DirDate AS WORD
     Cluster AS WORD
     FileSize AS DWORD
END TYPE

TYPE DataRef
      SectorNumber AS DWORD
      NoOfSectors AS WORD
      Buffer AS DWORD
END TYPE

TYPE DiskBoot
 Jump AS String *3
 Dos AS STRING *8
 BytesPerSector AS WORD
 SectorsPerCluster AS Byte
 ReservedSectors As WORD
 Fats AS Byte
 RootDirEntries AS WORD
 SmallTotalSectors As WORD
 Media AS BYTE
 SectorsPerFat AS WORD
 SectorsPerTrack AS WORD
 Heads AS WORD
 HiddenSectors AS DWORD
 TotalSectors AS DWORD
 DriveNo AS WORD
 Signature As Byte
 SerialNo as DWORD
 Label AS STRING *11
 DiskType AS STRING *8
 Buffer AS STRING *450
END TYPE

DECLARE SUB GetEntries(SbDir() AS DirType, SbDirNo%,FlCluster??)
DECLARE SUB FindDate(DirE AS DirType,DirYears??,DirMonths??,DirDays??)
DECLARE SUB FindTime(DirE AS DirType,DirHours??,DirMinutes??,DirSeconds??)

DIM Boot AS SHARED DiskBoot
DIM DiskDir AS SHARED STRING * 512
DIM BufferArea AS SHARED STRING * 512
DIM ClusterArea AS STRING * 2048
DIM FAT??(300)
DIM DirString$(512)
DIM DskSector as STRING * 512
DIM SubDirScore(512) AS SHARED WORD
SHARED TotalClusters??
SHARED InValid$,ReadDrive$,OutDrive$,Seperator$
InValid$ = "+=|\[]:;,<>/"+CHR$(34)
print LEN(Boot)

arg% = INSTR(1,COMMAND$,"/")
IF arg% = 0 then
	DrivePath$ = RTRIM$(COMMAND$)
	OutDrive$ = ""
	Seperator$ = ""
ELSE
	OutDrive$ = MID$(Command$,arg%+1)
	DrivePath$ = RTRIM$(Left$(COMMAND$,Arg%-1))
	Seperator$ = "\"
END IF
GetFile DrivePath$
END


FUNCTION DirScore%(SubFile AS DirType)
  DirFile$ = SubFile.Filename+SubFile.Ext
  IF Left$(DirFile$,1) = CHR$(229) THEN DirScore% = -1:EXIT FUNCTION
  IF INSTR(DirFile$,ANY Invalid$)  THEN DirScore% = 0:EXIT FUNCTION
  IF Left$(DirFile$,1) = CHR$(05) THEN DirScore% = -1:EXIT FUNCTION
  FOR i?? = 1 to 11
	IF ASCII(MID$(DirFile$,i??,1)) = 0 THEN DirScore% = 0:EXIT FUNCTION
	Char? = ASCII(MID$(DirFile$,i??,1))
	IF Char? >31 AND Char? < 97 THEN INCR Score??
  NEXT i??
  'FOR i?? = 13 to 17
  '    Char? = ASCII(MID$(DirFile$,i??,1))
  '    IF Char? > 0 THEN DirScore% = 0:EXIT FUNCTION
  '    INCR Score??
  'NEXT i??
  'FindTime SubFile,Hours??,Minute??,Seconds??
  'FindDate SubFile,Years??,Months??,Days??
  'IF Years?? < 1980 OR Seconds??*2 > 59 THEN DirScore% = 0:EXIT FUNCTION
  IF SubFile.DirClusters > 0 THEN Score?? = 0
  DirScore% = Score??
END FUNCTION


FUNCTION ValidName?(SubFile AS DirType)
DIM FCB AS STRING * 64
DsReg?? = VARSEG(SubFile)
SiReg?? = VARPTR(SubFile)
EsReg?? = VARSEG(FCB)
DiReg?? = VARPTR(FCB)
! MOV AH,&H29
! MOV AL,0
! MOV ES,EsReg??
! MOV DI,DiReg??
! MOV SI,SiReg??
! PUSH DS
! MOV DS,DsReg??
! INT &H21
! POP DS
! MOV Success?,AL
IF Success? = 0 THEN ValidName? = 1
END FUNCTION

SUB ShowFatSubDirectory(Cluster??)
Cluster?? = FileCluster??(FoundIt%)
BytesLeft???=FileSize???(FoundIt%)

DO
   DiskSector??? = 1 + (Cluster??-2) * Boot.SectorsPerCluster + Boot.Fats * Boot.SectorsPerFat + Boot.RootDirEntries*32\512
   ReadSectors DiskSector???+i%,VARPTR32(BufferArea$),4
   Offset?? = (Cluster??*2) AND &B0000000111111111
   DiskSector??? = Cluster??\256 +1
   CALL ReadSector (DiskSector???,VARPTR32(Fat??(0)))
   Cluster?? = Fat??(Offset??\2)
Loop UNTIL Cluster?? > 65527

END SUB


FUNCTION DEC$(Number??)
IF Number?? < 10 THEN
	DEC$="0"+Ltrim$(STR$(Number??))
ELSE
	DEC$=LTRIM$(STR$(Number??))
END IF
END FUNCTION

SUB ReadSector(SectorNo???,BufferAdress???)
 DIM IO AS DataRef
 Drive? =ASC(ReadDrive$) -65
 IO.SectorNumber = SectorNo???
 IO.NoOfSectors  = 1
 IO.Buffer = BufferAdress???

 BufferSeg?? = VARSEG(IO)
 BufferOff?? = VARPTR(IO)

 !MOV   CX,&HFFFF
 !MOV   AL,Drive?
 !PUSH  DS
 !MOV   DS,BufferSeg??
 !MOV   BX,BufferOff??
 !INT   &H25
 !POP   BX
 !POP   DS
END SUB


SUB ReadSectors(SectorNo???,BufferAdress???,Number??)
 DIM IO AS DataRef
 Drive? =ASC(ReadDrive$) -65

 IO.SectorNumber = SectorNo???
 IO.NoOfSectors  = Number??
 IO.Buffer = BufferAdress???

 BufferSeg?? = VARSEG(IO)
 BufferOff?? = VARPTR(IO)

 !MOV   CX,&HFFFF
 !MOV   AL,Drive?
 !PUSH  DS
 !MOV   DS,BufferSeg??
 !MOV   BX,BufferOff??
 !INT   &H25
 !POP   BX
 !POP   DS
END SUB


FUNCTION AddMem32&&(I???,Clt???)
  Offset&& = I??? MOD 65535
  Segment&& = I???\65536
  IF Offset&& + Clt??? > 65535 THEN
  	Segment&& = Segment&& + (Offset&& + Clt???)\16
      Offset&& = (Offset&& + Clt???) MOD 65535
  ELSE
      Offset&& = Offset&& + Clt???
  END IF
  AddMem32&& = Segment&& * 65536 +Offset&&
END FUNCTION

SUB ClearKeyboard()
	DO:LOOP UNTIL inkey$ = ""
END SUB

SUB ReadBoot()
ReadSector 0,VARPTR32(BOOT)
END SUB

SUB FileSpecs()
         PRINT "Boot Sector for Drive "; ReadDrive$
         PRINT "========================"
         PRINT "Boot Sector Volume Label = ";Boot.Label
         PRINT "Serial number            = ";Boot.SerialNo
         PRINT "OEM Identifier           = ";Boot.DOS
         PRINT "File system              = ";Boot.DiskType
         PRINT "Partion Size             = ";Boot.TotalSectors
         PRINT "Media descriptor         = ";Boot.Media
         PRINT "Bytes/Sector             = ";Boot.BytesPerSector
         PRINT "Sectors/Cluster          = ";Boot.SectorsPerCluster
         PRINT "Reserved Sectors         = ";Boot.ReservedSectors
         PRINT "Number of Fats           = ";Boot.Fats
         PRINT "Root Directory Enteries  = ";Boot.RootDirEntries
         PRINT "Sectors/Fat              = ";Boot.SectorsPerFat
         PRINT "Sectors/Tract            = ";Boot.SectorsPerTrack
         PRINT "Number of Heads          = ";Boot.Heads
         PRINT "Hiden sectors            = ";Boot.HiddenSectors
         PRINT "
	   PRINT "-----------------------------------------------------------"
END SUB

SUB GetEntries(SbDir() AS DirType, SbDirNo%,FlCluster??)
 SubDiskSector??? = 1 +(FlCluster??-2) * Boot.SectorsPerCluster + Boot.Fats * Boot.SectorsPerFat + Boot.RootDirEntries*32\512

 FOR I??? = SubDiskSector??? TO SubDiskSector??? + 3
	 ReadSector I???,VARPTR32(DiskDir)
       FOR J% = 0 TO 15
       	INCR SbDirNo%
		LSET SbDir(SbDirNo%) = Mid$(DiskDir,j%*32+1,32)
       NEXT J%
 NEXT I???
END SUB



SUB FindTime(DirEntry AS DirType,DirHours??,DirMinutes??,DirSeconds??)
  DirHours?? = DirEntry.DirTime AND &B1100000000000000
  SHIFT RIGHT DirHours??,11
  DirMinutes?? = DirEntry.DirTime AND &B0000011111100000
  SHIFT RIGHT DirMinutes??,5
  DirSeconds?? = DirEntry.DirTime AND &B0000000000011111
END SUB

SUB FindDate(DirEntry AS DirType,DirYears??,DirMonths??,DirDays??)
  DirYears?? = DirEntry.DirDate AND &B1111111000000000
  SHIFT RIGHT DirYears??,9
  DirYears?? = DirYears?? + 1980
  DirMonths?? = DirEntry.DirDate AND &B0000000111100000
  SHIFT RIGHT DirMonths??,5
  DirDays?? = DirEntry.DirDate AND &B0000000000011111
END SUB


Function GetDirString$(DirEntry AS DirType)
  FindDate DirEntry,DirYears??,DirMonths??,DirDays??
  FindTime DirEntry,DirHours??,DirMinutes??,DirSeconds??
  GetDirString$ = DirEntry.FileName +DirEntry.Ext+" "+DEC$(DirYears??)+"-"+DEC$(DirMonths??)+"-"+DEC$(DirDays??)+" "+_
  DEC$(DirHours??)+":"+DEC$(DirMinutes??)+":"+DEC$(DirSeconds??*2)+" "+USING$("###,###,###",DirEntry.FileSize)+" "+_
  USING$("###,###,###",DirEntry.Cluster)
END FUNCTION

Sub GetFile(DrivePath$)
 DIM DriveFile(20) AS SHARED STRING,DrivePathNo AS SHARED INTEGER
 DIM RootDir AS DirType
 OutPath$ = DrivePath$
 ReadDrive$ = Extract$(UCASE$(DrivePath$),":")
 IF ReadDrive$ <> "" THEN
 	DrivePath$ = MID$(DrivePath$,4)
 ELSE
 	ReadDrive$ = "C"
 END IF
 ReadBoot
 filespecs
 IF ASC(ReadDrive$) < 67 THEN Boot.TotalSectors = Boot.SmallTotalSectors
 FatStart??? =  Boot.ReservedSectors
 DirSectors?? = Boot.RootDirEntries*32\512
 DirArea??? = 1 + Boot.Fats * Boot.SectorsPerFat
 TotalClusters?? =Boot.TotalSectors/Boot.SectorsPerCluster
 Pathdirno% = 1
 Parse DrivePath$,DriveFile$(),DrivePathNo%
 IF DrivePath% > -1 THEN
 	FOR I??? = DirArea??? to DirArea??? + DirSectors?? -1
		 ReadSector I???,VARPTR32(DiskDir)
	       FOR J% = 0 TO 15
	       	INCR DirNo%
			LSET RootDir = Mid$(DiskDir,j%*32+1,32)
                  DirExt$  = LTRIM$(RootDir.Ext)
                  IF DirExt$ <> "" THEN
                  	DirectoryName$ = RTRIM$(RootDir.Filename) + "." + DirExt$
                  ELSE
                  	DirectoryName$ = RTRIM$(RootDir.Filename)
                  END IF

                  IF DirectoryName$ = DriveFile$(Pathdirno%) THEN
		            IF DirScore%(RootDir) > 2 THEN
			            DirByte? = RootDir.Attribute
					IF BIT(DirByte?,4) THEN
			            	GetGuessSubDirectory RootDir.Cluster??,DriveFile$(),DrivePathNo%,Pathdirno%,OutPath$
                                    EXIT SUB
			            ELSE
      					PrintDirInfo RootDir:EXIT SUB
			            END IF
		            END IF
                   END IF
	       NEXT J%
	NEXT I???
END IF
END SUB

SUB Parse(DrivePath$,FilePath$(),DirNo%)
DrivePath$ = UCASE$(DrivePath$)
FilePath$(0) = DrivePath$
IF DrivePath$ = "" THEN DirNo% = -1:EXIT SUB
i = 1: j = 1
DO UNTIL i >= LEN(DrivePath$)
    WHILE MID$(DrivePath$, i, 1) <> "\" AND MID$(DrivePath$, i, 1) <> ""
        FilePath$(j) = FilePath$(j) + MID$(DrivePath$, i, 1)
        i = i + 1
    WEND
    i = i + 1: j = j + 1
LOOP
END SUB

SUB GetGuessSubDirectory(RootDirCluster??,DriveFile$(),DrivePathNo%,PathDirNo%,OutPath$)
   DIM ClusterArea AS STRING * 2048
   DIM SubGuessFile AS DirType
   INCR PathDirNo%
   ThisDir?? = 1
   FilesPerCluster?? = Boot.SectorsPerCluster*Boot.BytesPerSector\32
   LSET SubGuessFile = Left$(ClusterArea$,32)
   j??=0
   ThisCluster?? = RootDirCluster??
	               DO
                     CALL ReadSectors(1 + (ThisCluster??-2) * Boot.SectorsPerCluster + Boot.Fats * Boot.SectorsPerFat + Boot.RootDirEntries*32\512,VARPTR32(ClusterArea$),4)
		               WHILE j?? < FilesPerCluster??
					LSET SubGuessFile = Mid$(ClusterArea$,j??*32+1,32)
		                  FileExt$  = LTRIM$(SubGuessFile.Ext)
		                  IF FileExt$ <> "" THEN
		                  	FileName$ = RTRIM$(SubGuessFile.Filename) + "." + FileExt$
		                  ELSE
		                  	FileName$ = RTRIM$(SubGuessFile.Filename)
		                  END IF
					IF DirScore%(SubGuessFile) > 2 THEN
                                    IF FileName$ = DriveFile$(PathDirNo%) THEN
	                                    IF BIT(SubGuessFile.Attribute,4) THEN
		                                    GetGuessSubDirectory SubGuessFile.Cluster??,DriveFile$(),DrivePathNo%,PathDirNo%,OutPath$
                                                EXIT SUB
	                                    ELSEIF SubGuessFile.Attribute AND &B00100111 THEN
		      					PrintDirInfo SubGuessFile:EXIT,EXIT
	                              	END IF
	                              END IF
                              ELSE
                                    IF DirScore%(SubGuessFile) >= 0 AND DirScore%(SubGuessFile) <3 THEN
                                             PRINT "Can't Find the File"
                                             EXIT SUB
                                    END IF
		                  END IF
		                  INCR j??
	                        INCR ThisDir??
		                WEND
	                      j?? = 0
	                      INCR ThisCluster??
	                      INCR SubDirSize???,512
	               LOOP
END SUB


FUNCTION TRUENAME$(TheFile$)
ln% = LEN(TheFile$)
 FOR I% = ln% to 1 STEP -1
	Char$ = MID$(TheFile$,I%,1)
	IF Char$ = "\" then
		TRUENAME$ = MID$(TheFile$,I%+1)
	      EXIT FUNCTION
	END IF
NEXT I%
TRUENAME$ = TheFile$
END FUNCTION


SUB PrintDirInfo(D As DirType)
PRINT  D.FileName;D.Ext;D.Attribute;D.Reserved;D.DirClusters;D.DirTime;D.DirDate;D.Cluster;D.FileSize
END SUB
