Solved

File access with TFileStream

Posted on 2006-06-30
6
352 Views
Last Modified: 2010-04-05
Hey all,

I'm using  a TFileStream to save and load a class to a file.  The following is my class with other records which are used in my class:

------------------------------------------------------------------------------------

type
  THurt = record
    hurt : boolean;
    amount : integer;
  end;

type
  TTileBackgroundProperties = record
    image : integer;
  end;

type
  TTileSpriteProperties = record
    image : integer;
  end;

type
  TSection = record
    enabled : boolean;
    spriteTile : array[1..32,1..24] of TTileSpriteProperties;
    backgroundTile : array[1..32,1..24] of TTileBackgroundProperties;
  end;

type
  TBackgroundImagePath = record
    name, path, section : string;
  end;

type
  TSpriteImagePath = record
    name, path : string;
    walkThrough, bulletThrough : boolean;
    hurt : THurt;
  end;

type
  TMap = class
    private
      password : string; // the password to edit the map
      mapWidth : integer; // in terms of how many sections there are across the map width
      mapHeight : integer; // in terms of how many sections there are down the map height
      backgroundImagePath : array[1..9999] of TBackgroundImagePath;
      spriteImagePath : array[1..9999] of TSpriteImagePath;
      section : array[1..20,1..20] of TSection;
    public
      constructor create(mapWidthIn, mapHeightIn : integer);
      function setupSection : TSection;
      // file handling
      procedure loadFromStream(stream : TStream);
      procedure saveToStream(stream : TStream);
      // sections
      function getSection(x,y : integer) : TSection;
      procedure setSection(x,y : integer; sectionProperties : TSection);
      // background tiles
      function getBackgroundTile(sectionX,sectionY,tileX,tileY : integer) : TTileBackgroundProperties;
      procedure setBackgroundTile(sectionX,sectionY,tileX,tileY : integer; characteristics : TTileBackgroundProperties);
      // sprite tiles
      function getSpriteTile(sectionX,sectionY,tileX,tileY : integer) : TTileSpriteProperties;
      procedure setSpriteTile(sectionX,sectionY,tileX,tileY : integer; characteristics : TTileSpriteProperties);
      // others
      function getMapWidth : integer;
      function getMapHeight : integer;
      procedure setMapWidth(mapWidthIn : integer);
      procedure setMapHeight(mapHeightIn : integer);
      function getPassword : string;
      procedure setPassword(passwordIn : string);
      // image paths
      function getSpriteImagePath(num : integer) : TSpriteImagePath;
      procedure setSpriteImagePath(num : integer; sprite : TSpriteImagePath);
  end;

------------------------------------------------------------------------------------

And here is my load and save procedures in the class:

------------------------------------------------------------------------------------

procedure TMap.loadFromStream(stream : TStream);
// saves the properties to the stream
var
  len : integer;
begin
  { TODO : get map width and height }
  stream.read(len, sizeOf(len)); // get length of password
  setLength(self.password, len); // set length of password
  stream.read(self.password[1], len); // read the password

  stream.Read(self.mapWidth, sizeOf(self.mapWidth)); // get mapWidth
  stream.read(self.mapHeight, sizeOf(self.mapHeight)); // get mapHeight

  stream.read(self.backgroundImagePath, sizeOf(self.backgroundImagePath)); // get background image paths
  stream.read(self.spriteImagePath, sizeOf(self.spriteImagePath)); // get sprite image paths

  stream.read(self.section, sizeOf(self.section)); // get tile
end;

procedure TMap.saveToStream(stream : TStream);
// loads the properties from the stream
var
  len : integer;
begin
  len := length(self.password); // get the length of the password
  stream.write(len, sizeOf(len)); // write length of password
  stream.Write(self.password[1], len); // write the password

  stream.write(self.mapWidth, sizeOf(self.mapWidth)); // set mapWidth
  stream.write(self.mapHeight, sizeOf(self.mapHeight)); // set mapHeight

  stream.write(self.backgroundImagePath, sizeOf(self.backgroundImagePath)); // set background image paths
  stream.write(self.spriteImagePath, sizeOf(self.spriteImagePath)); // set sprite image paths

  stream.write(self.section, sizeOf(self.section)); // write tile
end;

------------------------------------------------------------------------------------

when using these in my main unit i have declared - map : TMap; and thus can access the details of my class.  
In my main unit i save and load the class with the following procedures:

------------------------------------------------------------------------------------

procedure TfrmMain.loadMap;
// loads the modules from the file
var
  fileStream : TStream;
  newItem : TMap;
begin
  fileStream := TFileStream.create(fileName, fmOpenRead); // create the stream
  try
    newItem := TMap.create(mapWidth,mapHeight);
    newItem.loadFromStream(fileStream); // load the map
  finally
    fileStream.free;
  end;
  map := newItem;
end;

procedure TfrmMain.saveMap;
// saves the modules to the file
var
  fileStream : TStream;
begin
  fileStream := TFileStream.create(fileName, fmCreate); // create the stream
  try
    map.saveToStream(fileStream); // save the map
  finally
    fileStream.free;
  end;
end;

------------------------------------------------------------------------------------

Ok so thats my code set up.  Up until about 1 hour ago i was only concerned with the "section : array[1..20,1..20] of TSection;" part of my map class and the loading and saving was working completely fine, my load and save procedures in the class looked like the following:

------------------------------------------------------------------------------------

procedure TMap.loadFromStream(stream : TStream);
// saves the properties to the stream
var
  len : integer;
begin
  stream.read(self.section, sizeOf(self.section)); // get tile
end;

procedure TMap.saveToStream(stream : TStream);
// loads the properties from the stream
var
  len : integer;
begin
  stream.write(self.section, sizeOf(self.section)); // write tile
end;

------------------------------------------------------------------------------------

This worked fine and i could save and load the class and still access my section;  but as soon as i tried to start saving and loading the other parts i started having errors with the loadFromStream & saveToStream at the very top which loads and saves all the details in my class.

Now when i edit my map variable, save close my programme and load it again it works fine if i edit like map.section - but as soon as i edit map.spriteImagePath save and load it it doesn't work.  I get pointer access errors, file access errors And when i display the contents of map.spriteImagePath i get random characters liek characters!  The strings work and integers at the top of the class like the password but spriteImagePath and backgroundImagePath doesn't work.

Any suggestions? sorry i can only offer 25 points it's all ive got left.
0
Comment
Question by:James_h1023
  • 4
  • 2
6 Comments
 
LVL 6

Expert Comment

by:House_of_Dexter
ID: 17017898
k...Here's your problem

You can Write the SizeOf ...backgroundImagePath and spriteImagePath...

But you Can't Read the SizeOf backgroundImagePath and spriteImagePath...

Why you ask...because they use strings...which is special pointer to an array of chars...so you don't know what the size of your backgroundImagePath and  spriteImagePath

You need to handle any string reads the same way you handled the password and save the len info so that you can read the correct size...
0
 
LVL 6

Expert Comment

by:House_of_Dexter
ID: 17018041
so something like this in SaveToStream...

var
  iSizeOf: integer  

iSizeOf := sizeOf(self.backgroundImagePath);
stream.write(iSizeOf,SizeOf(iSizeOf));
 stream.write(self.backgroundImagePath, iSizeOf); // set background image paths


...an LoadFromStream add change to something like this...

stream.read(iSizeOf, sizeOf(iSizeOf));
stream.read(self.backgroundImagePath,iSizeOf));
0
 
LVL 6

Expert Comment

by:House_of_Dexter
ID: 17018152
ignore my last code example...your going to have to store and read the length of each string...because they are dynamic...and sizeof only returns the sizeof the pointer...and not the string...
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 4

Author Comment

by:James_h1023
ID: 17018197
hmm i see - so maybe because it's a record or strings do i need like a little bit of code to store each string in the record and ten put that in a loop to store the whole array of records?
Can't quite get my head around that - do i need to chagne my load and save procedures in my main unit aswell, cause i would hvae to like construct the map while loading each string bit by bit!
Thakns for helping
0
 
LVL 6

Accepted Solution

by:
House_of_Dexter earned 25 total points
ID: 17018458
heh...the easy and cheating way is to change string to shortstring...and you don't have to worry about len...the bad...lots of memory useage...255 bytes per Shortstring unless you do something like this var MyString: string[40];

otherwise your going to have to do something like this for each string in a record:

  stream.read(len, sizeOf(len)); // get length of password
  setLength(self.password, len); // set length of password
  stream.read(self.password[1], len); // read the password

and

  len := length(self.password); // get the length of the password
  stream.write(len, sizeOf(len)); // write length of password
  stream.Write(self.password[1], len); // write the password

0
 
LVL 4

Author Comment

by:James_h1023
ID: 17018504
oh right yeh - i am dumb
cheers tarly, i think i might set the length to like [40] or something like u suggested.
Thanks a lot, hope to return the favour sometime
James
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now