PageRenderTime 40ms CodeModel.GetById 36ms app.highlight 1ms RepoModel.GetById 1ms app.codeStats 0ms

/FileUtils.pas

http://github.com/foxblock/PNDTools
Pascal | 211 lines | 135 code | 17 blank | 59 comment | 20 complexity | 64edd67c587b46ed004dd43a442ab284 MD5 | raw file
  1{******************************************************************************}
  2{                                                                              }
  3{  Functions for byte-level file interaction (looking for and                  }
  4{  appending data/bytes)                                                       }
  5{                                                                              }
  6{  --------------------------------------------------------------------------  }
  7{                                                                              }
  8{  PNDTools is Copyright ©2011-2013 Janek Schäfer                              }
  9{                                                                              }
 10{  This file is part of PNDTools                                               }
 11{                                                                              }
 12{  PNDTools is free software: you can redistribute it and/or modify            }
 13{  it under the terms of the GNU General Public License as published by        }
 14{  the Free Software Foundation, either version 3 of the License, or           }
 15{  (at your option) any later version.                                         }
 16{                                                                              }
 17{  PNDTools is distributed in the hope that it will be useful,                 }
 18{  but WITHOUT ANY WARRANTY; without even the implied warranty of              }
 19{  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               }
 20{  GNU General Public License for more details.                                }
 21{                                                                              }
 22{  You should have received a copy of the GNU General Public License           }
 23{  along with this program.  If not, see <http://www.gnu.org/licenses/>.       }
 24{                                                                              }
 25{******************************************************************************}
 26
 27
 28unit FileUtils;
 29
 30{$IFDEF FPC}
 31  {$MODE DELPHI}
 32{$ENDIF}
 33
 34interface
 35
 36uses Classes, SysUtils;
 37
 38{ Appends the passed file (FileName) to the Stream, uses the current stream
 39  position if DontSeek is false (which may lead to overwrites), the end of the
 40  steam otherwise }
 41procedure AppendDataToFileStream(Stream : TFileStream; const FileName : String;
 42    const DontSeek : Boolean = false);
 43
 44{ Searches for a Data string in the passed byte-stream (will convert the string
 45  to a byte array)
 46  StartPos is the postition to start searching from, pass negative values for
 47  a offset from the end of the stream (passing 0 and Backwards=true will start
 48  at the end)
 49  Backwards specifies the direction in which to search, independant of StartPos
 50  You can pass an optional OutputStream in which all data from StartPos to the
 51  final end of the String to find will be written (only when Backwards is false)
 52  Uses a "rolling" buffer to eliminate the problem of the string to be split into two
 53  Because of that only the first 512 bytes of Data are used for comparison
 54
 55  Returns the starting position of the match (first byte in Data) or -1 if no
 56  match could be found }
 57function FindStringDataInStream(const Data : String; Stream : TFileStream;
 58    const StartPos : Int64 = 0; const Backwards : Boolean = false;
 59    OutputStream : TFileStream = nil) : Int64;
 60    
 61{ Looks for the ISO file header in the passed stream
 62  Returns true if found, false otherwise }
 63function DetectIsoFormat(Stream : TFileStream) : Boolean;
 64
 65implementation
 66
 67uses Math, Forms;
 68
 69procedure AppendDataToFileStream(Stream : TFileStream; const FileName : String;
 70    const DontSeek : Boolean = false);
 71var
 72    Buffer : Array [Word] of Byte; //64KB
 73    NumRead, NumWrite : Integer;
 74    Other : TFileStream;
 75begin
 76    if NOT DontSeek then
 77        Stream.Seek(0,soFromEnd);
 78    Other := TFileStream.Create(FileName,fmOpenRead);
 79    try
 80        repeat
 81            NumRead := Other.Read(Buffer,SizeOf(Buffer));
 82            NumWrite := Stream.Write(Buffer,NumRead);
 83        until (NumRead = 0) OR (NumWrite <> NumRead);
 84    finally
 85        Other.Free;
 86    end;
 87end;
 88
 89function FindStringDataInStream(const Data : String; Stream : TFileStream;
 90    const StartPos : Int64 = 0; const Backwards : Boolean = false;
 91    OutputStream : TFileStream = nil) : Int64;
 92var
 93    NumRead : Word;
 94    DataArray : Array [0..511] of Byte;
 95    Buffer : Array [0..1023] of Byte; // 1KB
 96    I,K : Word;
 97    Size : Word;
 98    Found : Boolean;
 99    Pos : Int64;
100begin
101    // convert string to byte array for comparison
102    Size := Min(Length(Data) * SizeOf(Char),SizeOf(DataArray));
103    Move(Data[1],DataArray,Size);
104
105    // set-up stream correctly
106    if StartPos < 0 then
107        Stream.Seek(StartPos,soFromEnd)
108    else if StartPos > 0 then
109        Stream.Seek(StartPos,soFromBeginning)
110    else
111    begin
112        if Backwards then
113            Stream.Seek(0,soFromEnd)
114        else
115            Stream.Seek(0,soFromBeginning);
116    end;
117    if Backwards then
118    begin
119        // first seek here as we begin with a read operation
120        Stream.Seek(-SizeOf(Buffer),soFromEnd); 
121        // stream output only supported in forwards mode
122        OutputStream := nil;
123    end;
124
125    Result := -1;
126    repeat
127        // save current position for loop check
128        Pos := Stream.Position;
129        NumRead := Stream.Read(Buffer,SizeOf(Buffer));
130        // check buffer against data
131        for I := 0 to SizeOf(Buffer) - Size - 1 do
132        begin
133            if Buffer[I] = DataArray[0] then
134            begin
135                Found := true;
136                for K := 0 to Size - 1 do
137                begin
138                    if Buffer[I+K] <> DataArray[K] then
139                    begin
140                        Found := false;
141                        Break;
142                    end;
143                end;
144                if Found then
145                begin
146                    Result := Stream.Position - NumRead + I;
147                    Break;
148                end;
149            end;
150        end;
151        // save buffer to output stream
152        if OutputStream <> nil then
153        begin
154            if Result = -1 then // not found yet
155                begin
156                if NumRead < SizeOf(Buffer) then
157                    OutputStream.Write(Buffer,NumRead)
158                else
159                    OutputStream.Write(Buffer,SizeOf(Buffer) div 2);
160                end
161            else
162                OutputStream.Write(Buffer,Result + Size + NumRead - Stream.Position);  // I + Size
163        end;
164        // position found -> exit
165        if Result <> -1 then
166            Exit;
167
168        // use a "rolling" buffer to work around data beeing split on two buffers
169        // this also is the reason the data array is half the size of the buffer
170        if Backwards then
171            Stream.Seek(Max(-SizeOf(Buffer) * 3 div 2,-Stream.Position),soFromCurrent)
172        else
173            Stream.Seek(-SizeOf(Buffer) div 2,soFromCurrent);
174    // forward: read less bytes than buffer means end of file
175    // backward: seeking does not change position means beginning of file
176    until (NumRead < SizeOf(Buffer)) OR (Stream.Position = Pos);
177end;
178
179function DetectIsoFormat(Stream : TFileStream) : Boolean;
180const
181    // Source: http://www.mactech.com/articles/develop/issue_03/high_sierra.html
182    ISO_HEADER : Array [0..4] of Byte = (67,68,48,48,49); // CD001
183    OFFSET_BYTES : Array [0..2] of Integer = (32769,34817,36865);
184var
185    Buffer : Array [Word] of Byte;
186    I,K : Integer;
187    Found : Boolean;
188begin
189    Result := false;
190    for I := 0 to High(OFFSET_BYTES) do
191    begin
192        Stream.Seek(OFFSET_BYTES[I],soFromBeginning);
193        Stream.Read(Buffer,Length(Buffer));
194        Found := true;
195        for K := 0 to High(ISO_HEADER) do
196        begin
197            if Buffer[K] <> ISO_HEADER[K] then
198            begin
199                Found := false;
200                Break;
201            end;
202        end;
203        if Found then
204        begin
205            Result := true;
206            Exit;
207        end;
208    end;
209end;
210
211end.