Read bytes from a file in the right place using Inno Setup

I want to open a 50 MB binary and read only the last 4 bytes and convert it to a string for some purpose.

The only way I found this now is to use LoadStringFromFile to completely load the file in memory and then copy the last 4 bytes, however this method is very slow because the binary is heavy.

Is there a better way to do this in an Inno Setup script?

Update:




This is the final working function that I edited from Martin Prikril.

 function readlast4byte() : AnsiString; var Stream: TFileStream; Buffer: string; Count: Integer; Index: Integer; begin Count := 4; Stream := TFileStream.Create('C:\test.txt', fmOpenRead); try Stream.Seek(-Count, soFromEnd); SetLength(Buffer, 1); SetLength(Result, Count); for Index := 1 to Count do begin Stream.ReadBuffer(Buffer, 1); Result[Index] := Chr(Ord(Buffer[1])) ; end; finally Stream.Free; end; end; 



Update 2:

Also, this is another great work written by TLama and should also be noted as an answer:

 [Code] #IFNDEF Unicode #DEFINE CharSize 1 #ELSE #DEFINE CharSize 2 #ENDIF type TSeekOrigin = ( soBeginning, soCurrent, soEnd ); #IFDEF UNICODE function BufferToAnsi(const Buffer: string): AnsiString; var W: Word; I: Integer; begin SetLength(Result, Length(Buffer) * 2); for I := 1 to Length(Buffer) do begin W := Ord(Buffer[I]); Result[(I * 2)] := Chr(W shr 8); // high byte Result[(I * 2) - 1] := Chr(Byte(W)); // low byte end; end; #ENDIF function ReadStringFromFile(const FileName: string; Origin: TSeekOrigin; Offset, Length: Integer; var S: AnsiString): Boolean; var Buffer: string; Stream: TFileStream; begin Result := True; try Stream := TFileStream.Create(FileName, fmOpenRead); try Stream.Seek(Offset, Ord(Origin)); SetLength(Buffer, Length div {#CharSize}); Stream.ReadBuffer(Buffer, Length); #IFNDEF UNICODE S := Buffer; #ELSE S := BufferToAnsi(Buffer); #ENDIF finally Stream.Free; end; except Result := False; end; end; 
+2
inno-setup pascalscript
Jul 05 '15 at 7:28
source share
1 answer

You can use TFileStream support class :

 TStream = class(TObject) function Read(Buffer: String; Count: Longint): Longint; function Write(Buffer: String; Count: Longint): Longint; function Seek(Offset: Longint; Origin: Word): Longint; procedure ReadBuffer(Buffer: String; Count: Longint); procedure WriteBuffer(Buffer: String; Count: Longint); function CopyFrom(Source: TStream; Count: Longint): Longint; property Position: Longint; read write; property Size: Longint; read write; end; THandleStream = class(TStream) constructor Create(AHandle: Integer); property Handle: Integer; read; end; TFileStream = class(THandleStream) constructor Create(Filename: String; Mode: Word); end; 

Use the .Seek(-4, soFromEnd) to find the read pointer at the desired position.

The complication is that TStream works with characters, not bytes, so you need to convert Unicode strings that you will return to bytes.

When reading only four bytes, it is easier to read bytes by bytes, preventing any multibyte conversions:

 procedure ReadFileEnd; var Stream: TFileStream; Buffer: string; Count: Integer; Index: Integer; begin Count := 4; Stream := TFileStream.Create('my_binary_file.dat', fmOpenRead); try Stream.Seek(-Count, soFromEnd); SetLength(Buffer, 1); for Index := 1 to Count do begin Stream.ReadBuffer(Buffer, 1); Log(Format('Byte %2.2x: %2.2x', [Index, Ord(Buffer[1])])); end; finally Stream.Free; end; end; 



Here is a common alternative from @TLama that works effectively for arbitrary large reading:
https://pastebin.com/nzGEdXVj




By the way, for me, the LoadStringFromFile function seems efficient enough to load a 50 MB file. It only takes 40 ms.

+3
Jul 05 '15 at 15:40
source share



All Articles