Unit m_FileIO; {$I M_OPS.PAS} Interface { FILE ACCESS FUNCTIONS } Function ioReset (Var F: File; RecSize: Word; Mode: Byte) : Boolean; Function ioReWrite (Var F: File; RecSize: Word; Mode: Byte) : Boolean; Function ioSeek (Var F: File; FPos: LongInt) : Boolean; Function ioRead (Var F: File; Var Rec) : Boolean; Function ioWrite (Var F: File; Var Rec) : Boolean; Function ioBlockRead (Var F: File; Var Rec; dSize: LongInt; Var Res: LongInt) : Boolean; Function ioBlockWrite (Var F: File; Var Rec; dSize: LongInt; Var Res: LongInt) : Boolean; { FILE MANIPULATION FUNCTIONS } Function FileExist (Str: String) : Boolean; Function FileErase (Str: String) : Boolean; Function JustFileName (Str: String) : String; Function JustFile (Str: String) : String; Function JustFileExt (Str: String) : String; Function JustPath (Str: String) : String; Function WildMatch (WildCard, FName: String; IgnoreCase: Boolean) : Boolean; Function DirCreate (Str: String) : Boolean; Function DirExists (Str: String) : Boolean; Function DirSlash (Str: String) : String; Function DirLast (CurPath: String) : String; Function DirChange (Dir: String) : Boolean; Procedure DirClean (Path: String; Exempt: String); Function DirFiles (Str: String) : LongInt; Function FileRename (OldFN, NewFN: String) : Boolean; Function FileCopy (Source, Target: String) : Boolean; Function FileFind (FN: String) : String; Function FileByteSize (FN: String) : Int64; Function FileNewExt (FN, NewExt: String) : String; { GLOBAL FILEIO VARIABLES AND CONSTANTS } Var ioCode : LongInt; Const fmReadOnly = 0; fmWriteOnly = 1; fmReadWrite = 2; fmDenyAll = 16; fmDenyWrite = 32; fmDenyRead = 48; fmDenyNone = 64; fmNoInherit = 128; fmRWDN = 66; fmRWDR = 50; fmRWDW = 34; { STREAMING CLASS OPTIONS } Const MaxFileBufferSize = 64 * 1024; Type TFileBufferOpenType = ( fmOpen, fmOpenCreate, fmCreate ); PFileBufferRec = ^TFileBufferRec; TFileBufferRec = Array[0..MaxFileBufferSize - 1] of Char; TFileBuffer = Class BufSize : LongInt; Buffer : PFileBufferRec; BufRead : LongInt; BufStart : LongInt; BufEnd : LongInt; BufPos : LongInt; InFile : File; BufEOF : Boolean; BufDirty : Boolean; IsOpened : Boolean; Constructor Create (BufferSize: LongInt); Destructor Destroy; Override; Function OpenStream (FN: String; OpenType: TFileBufferOpenType; OpenMode: Byte) : Boolean; Procedure CloseStream; Function Read : Char; Procedure BlockRead (Var Buf; Size: LongInt; Var Count: LongInt); Overload; Procedure BlockRead (Var Buf; Size: LongInt); Overload; Procedure BlockWrite (Var Buf; Size: LongInt); Procedure Seek (FP : LongInt); Function FilePos : LongInt; Function FileSize : LongInt; Function EOF : Boolean; Procedure FillBuffer; Procedure FlushBuffer; End; Implementation Uses {$IFDEF WINDOWS} // FileErase (FPC Erase) hardly EVER FUCKING WORKS. Windows, {$ENDIF} DOS, m_Types, m_Strings, m_DateTime; Const ioRetries = 20; ioWaitTime = 100; Function ioReset (Var F: File; RecSize: Word; Mode: Byte) : Boolean; Var Count : Word; Begin FileMode := Mode; Count := 0; ioCode := 5; While (Count < ioRetries) and (ioCode = 5) Do Begin {$I-} Reset (F, RecSize); {$I+} ioCode := IoResult; Inc (Count); If ioCode = 5 Then WaitMS(ioWaitTime); End; Result := (ioCode = 0); End; Function ioReWrite (Var F: File; RecSize: Word; Mode: Byte) : Boolean; Var Count : Word; Begin FileMode := Mode; Count := 0; ioCode := 5; While (Count < ioRetries) and (ioCode = 5) Do Begin ReWrite (F, RecSize); ioCode := IoResult; Inc (Count); If ioCode = 5 Then WaitMS(ioWaitTime); End; ioReWrite := (ioCode = 0); End; Function ioSeek (Var F: File; FPos: LongInt) : Boolean; Var Count : Word; Begin Count := 0; ioCode := 5; While (Count < ioRetries) and (ioCode = 5) Do Begin {$I-} Seek (F, FPos); {$I+} ioCode := IoResult; Inc (Count); If ioCode = 5 Then WaitMS(ioWaitTime); End; ioSeek := (ioCode = 0); End; Function ioBlockRead (Var F: File; Var Rec; dSize: LongInt; Var Res: LongInt) : Boolean; Var Count : Word; Begin Count := 0; ioCode := 5; While (Count < ioRetries) and (ioCode = 5) Do Begin BlockRead (F, Rec, dSize, Res); ioCode := IoResult; Inc (Count); If ioCode = 5 Then WaitMS(ioWaitTime); End; ioBlockRead := (ioCode = 0); End; Function ioBlockWrite (Var F: File; Var Rec; dSize: LongInt; Var Res: LongInt) : Boolean; Var Count : Word; Begin Count := 0; ioCode := 5; While (Count < ioRetries) and (ioCode = 5) Do Begin BlockWrite (F, Rec, dSize, Res); ioCode := IoResult; Inc (Count); If ioCode = 5 Then WaitMS(ioWaitTime); End; ioBlockWrite := (ioCode = 0); End; Function ioRead (Var F: File; Var Rec) : Boolean; Var Count : Word; Begin Count := 0; ioCode := 5; While (Count < ioRetries) and (ioCode = 5) Do Begin {$I-} BlockRead (F, Rec, 1); {$I+} ioCode := IoResult; Inc (Count); If ioCode = 5 Then WaitMS(ioWaitTime); End; ioRead := (ioCode = 0); End; Function ioWrite (Var F: File; Var Rec) : Boolean; Var Count : Word; Begin Count := 0; ioCode := 5; While (Count < ioRetries) and (ioCode = 5) Do Begin BlockWrite (F, Rec, 1); ioCode := IoResult; Inc (Count); If ioCode = 5 Then WaitMS(ioWaitTime); End; ioWrite := (ioCode = 0); End; Function FileCopy (Source, Target: String) : Boolean; Var SF : File; TF : File; BRead : LongInt; BWrite : LongInt; FileBuf : Array[1..4096] of Char; Begin Result := False; FileMode := 66; Assign (SF, Source); {$I-} Reset(SF, 1); {$I+} If IOResult <> 0 Then Exit; Assign (TF, Target); {$I-} ReWrite(TF, 1); {$I+} If IOResult <> 0 then Exit; Repeat BlockRead (SF, FileBuf, SizeOf(FileBuf), BRead); BlockWrite (TF, FileBuf, BRead, BWrite); Until (BRead = 0) or (BRead <> BWrite); Close(SF); Close(TF); Result := BRead = BWrite; End; Function FileRename (OldFN, NewFN: String) : Boolean; Var OldF : File; Begin Assign (OldF, NewFN); {$I-} Erase (OldF); {$I+} If IoResult = 0 Then; Assign (OldF, OldFN); {$I-} ReName (OldF, NewFN); {$I+} Result := (IoResult = 0); End; Function DirCreate (Str: String) : Boolean; Var Count : Byte; CurDir : String; Prefix : String; Begin Result := True; Prefix := ''; Str := DirSlash(Str); If DirExists(Str) Then Exit; Count := Pos(PathSep, Str); While (Count > 0) Do Begin CurDir := Copy(Str, 1, Count); Delete (Str, 1, Count); Prefix := Prefix + CurDir; If Not DirExists(Prefix) Then Begin {$I-} MkDIR (Prefix); {$I+} If IoResult <> 0 Then Begin Result := False; Exit; End; End; Count := Pos(PathSep, Str); End; End; Procedure DirClean (Path: String; Exempt: String); Var DirInfo: SearchRec; Begin FindFirst(Path + '*', Archive, DirInfo); While DosError = 0 Do Begin If strUpper(Exempt) <> strUpper(DirInfo.Name) Then FileErase(Path + DirInfo.Name); FindNext(DirInfo); End; FindClose(DirInfo); End; Function DirChange (Dir: String) : Boolean; Begin While Dir[Length(Dir)] = PathSep Do Dec(Dir[0]); Dir := Dir + PathSep; {$I-} ChDir(Dir); {$I+} Result := IoResult = 0; End; Function DirSlash (Str: String) : String; Begin If Copy(Str, Length(Str), 1) <> PathSep Then Str := Str + PathSep; Result := Str; End; Function DirLast (CurPath: String) : String; Begin If CurPath[Length(CurPath)] = PathSep Then Delete (CurPath, Length(CurPath), 1); While (CurPath[Length(CurPath)] <> PathSep) and (CurPath <> '') Do Delete (CurPath, Length(CurPath), 1); Result := DirSlash(CurPath); End; Function JustPath (Str: String) : String; Var Count : Byte; Begin For Count := Ord(Str[0]) DownTo 1 Do If (Str[Count] = '/') or (Str[Count] = '\') Then Begin Delete (Str, Count + 1, 255); Break; End; Result := Str; End; Function JustFile (Str: String) : String; Var Count : Byte; Begin For Count := Length(Str) DownTo 1 Do If (Str[Count] = '/') or (Str[Count] = '\') Then Begin Delete (Str, 1, Count); Break; End; Result := Str; End; Function JustFileName (Str: String) : String; Var Temp : Byte; Begin Temp := Pos ('.', Str); If Temp > 0 Then Delete (Str, Temp, Ord(Str[0])); Result := Str; End; Function JustFileExt (Str: String) : String; Var Temp : Byte; Begin Result := ''; For Temp := Length(Str) DownTo 1 Do If Str[Temp] = '.' Then Begin Result := Copy(Str, Temp + 1, Length(Str)); Exit; End; End; Function WildMatch (WildCard, FName: String; IgnoreCase: Boolean) : Boolean; Begin Result := False; If FName = '' Then Exit; If IgnoreCase Then Begin WildCard := strUpper(WildCard); FName := strUpper(FName); End; Case Wildcard[1] of '*' : Begin If FName[1] = '.' Then Exit; If Length(Wildcard) = 1 Then Result := True; If (Length(Wildcard) > 1) and (Wildcard[2] = '.') and (Length(FName) > 0) Then Result := WildMatch(Copy(Wildcard, 3, Length(Wildcard) - 2), Copy(FName, Pos('.', FName) + 1, Length(FName)-Pos('.', FName)), False); End; '?' : If Ord(Wildcard[0]) = 1 Then Result := True Else Result := WildMatch(Copy(Wildcard, 2, Length(Wildcard) - 1), Copy(FName, 2, Length(FName) - 1), False); Else If FName[1] = Wildcard[1] Then If Length(Wildcard) > 1 Then Result := WildMatch(Copy(Wildcard, 2, Length(Wildcard) - 1), Copy(FName, 2, Length(FName) - 1), False) Else Result := (Length(FName) = 1) And (Length(Wildcard) = 1); End; End; {$IFDEF WINDOWS} Function FileErase (Str: String) : Boolean; Begin Str := Str + #0; Result := Windows.DeleteFile(PChar(@Str[1])); End; {$ELSE} Function FileErase (Str: String) : Boolean; Var F : File; Begin {$I-} Assign (F, Str); Erase (F); Result := IoResult = 0; End; {$ENDIF} Function FileExist (Str: String) : Boolean; Var DF : File; Attr : Word; Begin Assign (DF, Str); GetFattr (DF, Attr); Result := (DosError = 0) and (Attr And Directory = 0); End; Function DirExists (Str: String) : Boolean; Var F : File; Attr : Word; Begin Result := False; If Str = '' Then Exit; While Str[Length(Str)] = PathSep Do Dec(Str[0]); Str := Str + PathSep + '.'; Assign (F, Str); GetFAttr (F, Attr); Result := ((Attr And Directory) = Directory); End; Function FileFind (FN: String) : String; Var Dir : SearchRec; Begin Result := FN; FindFirst (JustPath(FN) + '*', AnyFile, Dir); While DosError = 0 Do Begin If strUpper(Dir.Name) = strUpper(JustFile(FN)) Then Begin Result := JustPath(FN) + Dir.Name; Break; End; FindNext(Dir); End; FindClose(Dir); End; Function FileByteSize (FN: String) : Int64; Var Dir : SearchRec; Begin Result := 0; FindFirst (FN, AnyFile, Dir); If DosError = 0 Then Result := Dir.Size; FindClose(Dir); End; Function DirFiles (Str: String) : LongInt; Var DirInfo : SearchRec; Begin Result := 0; FindFirst (Str + '*', AnyFile, DirInfo); While DosError = 0 Do Begin If DirInfo.Attr And Directory = 0 Then Inc (Result); FindNext(DirInfo); End; FindClose (DirInfo); End; Function FileNewExt (FN, NewExt: String) : String; Var Temp : Byte; Begin For Temp := Length(FN) DownTo 1 Do If FN[Temp] = '.' Then Begin Result := Copy(FN, 1, Temp) + NewExt; Exit; End; Result := FN + '.' + NewExt; End; { FILE STREAMING FUNCTIONS } Constructor TFileBuffer.Create (BufferSize: LongInt); Begin Inherited Create; BufSize := BufferSize; BufStart := 0; BufEnd := 0; BufPos := 0; BufEOF := False; BufRead := 0; Buffer := NIL; BufDirty := False; IsOpened := False; End; Destructor TFileBuffer.Destroy; Begin If IsOpened Then CloseStream; End; Function TFileBuffer.OpenStream (FN: String; OpenType: TFileBufferOpenType; OpenMode: Byte) : Boolean; Begin Result := False; If IsOpened Then CloseStream; Assign (InFile, FN); Case OpenType of fmOpen : If Not ioReset (InFile, 1, OpenMode) Then Exit; fmOpenCreate : If Not ioReset (InFile, 1, OpenMode) Then If Not FileExist(FN) Then Begin If Not ioReWrite (InFile, 1, OpenMode) Then Exit; End Else Exit; fmCreate : If Not ioReWrite (InFile, 1, OpenMode) Then Exit; End; GetMem (Buffer, BufSize); FillBuffer; BufDirty := False; IsOpened := True; Result := True; End; Procedure TFileBuffer.CloseStream; Begin If IsOpened Then Begin If BufDirty Then FlushBuffer; System.Close (InFile); End; If Assigned(Buffer) Then Begin FreeMem (Buffer, BufSize); Buffer := NIL; End; IsOpened := False; End; Function TFileBuffer.FilePos : LongInt; Begin FilePos := BufStart + BufPos; End; Procedure TFileBuffer.FillBuffer; Var Start : LongInt; Begin Start := System.FilePos(InFile); System.BlockRead (InFile, Buffer^[0], BufSize, BufRead); BufStart := Start; BufEnd := Start + BufRead; BufPos := 0; BufEOF := System.EOF(InFile); End; Function TFileBuffer.Read : Char; Begin If BufPos >= BufSize Then FillBuffer; Read := Buffer^[BufPos]; Inc (BufPos); End; Procedure TFileBuffer.BlockWrite (Var Buf; Size: LongInt); Var Offset : LongInt; Begin If BufPos + Size > BufSize Then Begin Offset := BufSize - BufPos; If Offset > 0 Then Move(Buf, Buffer^[BufPos], Offset); FlushBuffer; FillBuffer; Move (TFileBufferRec(Buf)[Offset], Buffer^[BufPos], Size - Offset); BufPos := BufPos + Size - Offset; End Else Begin Move (Buf, Buffer^[BufPos], Size); Inc (BufPos, Size); End; If BufPos > BufEnd Then BufEnd := BufPos; If BufPos > BufRead Then BufRead := BufPos; BufDirty := True; End; Procedure TFileBuffer.BlockRead (Var Buf; Size: LongInt); Var Res : LongInt; Begin BlockRead(Buf, Size, Res); End; Procedure TFileBuffer.BlockRead (Var Buf; Size: LongInt; Var Count: LongInt); Begin If BufPos + Size >= BufRead Then Begin If BufDirty Then FlushBuffer; If Size > BufSize Then Size := BufSize; System.Seek(InFile, BufStart + BufPos); FillBuffer; If BufRead < Size Then Size := BufRead; End; Move (Buffer^[BufPos], Buf, Size); Inc (BufPos, Size); Count := Size; End; Procedure TFileBuffer.Seek (FP : LongInt); Begin If (FP >= BufStart) and (FP < BufEnd) Then BufPos := (BufEnd - (BufEnd - FP)) - BufStart Else Begin If BufDirty Then FlushBuffer; System.Seek(InFile, FP); FillBuffer; End; End; Function TFileBuffer.EOF : Boolean; Begin EOF := (BufStart + BufPos >= BufEnd) and BufEOF; End; Function TFileBuffer.FileSize : LongInt; Begin FileSize := System.FileSize(InFile); End; Procedure TFileBuffer.FlushBuffer; Var Res : LongInt; Begin System.Seek (InFile, BufStart); System.BlockWrite (InFile, Buffer^, BufRead, Res); BufDirty := False; End; End.