6
votes

This question is very basic because I am at a very basic level. I have to read the contents of a file with a *.pam extension. It's not common, that's a file generated by a specific program and it has a fixed size of xx KB. There are some numbers and values inside it.

I need to read this file and so I am going to use TStreamReader. Of course I need to know the structure of the file but I don't. By the way I have the picture above that is a part of a table with useful info:

enter image description here

The meaning is the following: once you've opened the file, go to a specific address and get the data. For example, in 0x30 I can find the data about 'Move 1 Current PP'. My question is: how do I jump to a given address?

procedure TForm1.Button1Click(Sender: TObject);
var sw: TStreamReader;
begin

 Memo1.Clear;    
 sw := TStreamReader.Create('C:\aaa\myfile.pam', TEncoding.UTF8);
 try

  sw.Position := 0;

 finally
  sw.Free;
 end;

I have seen in the documentation that there is something like Seek and Position but it seems that I cannot use them. In general I would use

while not(sw.EndOfStream) do
 begin
  //read...
 end;

This starts from the beginning and it reads the data in seqence. How can I jump to a specific address and read the content? I guess that I can pass the hex address to a function like Seek, so I can go to a specific point. Then I can just retrieve the data.

1
TStreamReader is a helper class for reading textual data. Your file is not text, it is binary. Use TFileStream directly instead, then you can use Seek/Position - Remy Lebeau

1 Answers

5
votes

If the file content is binary (as opposite to textual) do not use any kind of text encoding options.

In the following example I use TFileStream to handle the file and to read selected parts of the file. The procedure ReadBuffer() method has overloads that takes either an untyped buffer or a buffer of type TBytes. It raises an EReadError exception if requested number of bytes can not be read.

var
  fn: TFileName;
  fs: TFileStream;
  b: byte;
  bf: array[0..3] of word;
  ReadResult: integer;
begin
  fn := 'c:\tmp\MyMagicalFile.pam';
  fs := TFileStream.Create(fn, fmOpenRead or fmShareDenyWrite);
  try
    fs.Seek($28, soFromBeginning);    // move to offset $28 in the file
    fs.ReadBuffer(bf, 8);             // read 4 x 2 bytes into buffer (bf)
                                      // fs.Position is at this point at $30
    fs.ReadBuffer(b, 1);              // read one byte (at offset $30) into the buffer (b)
  finally
    fs.free;
  end;
  // use b and bf as needed
end;

As in the code above, you jump to a given position in the file by using the Seek() method. The position is automatically updated when reading (or writing). SeekOptions are soFromBeginning, soFromCurrent and soFromEnd. For other details see System.Classes.TFileStream.