Robert has asked for a bigger example of how to use the basic data store from my article. The example code below will hopefully give you a better idea of how it can be used.

This example is more closely related to the base class I use which includes one or two helper functions that cater for including string length checks and loading and saving streams within your object. These methods are as follows:-

Code:
  procedure saveString(writer:TWriter;srcData:string);
  procedure loadString(reader:TReader;var dstData:string);

  procedure saveStream(writer:TWriter;src:TStream);
  procedure loadStream(reader:TReader;dst:TStream);
The exact implementations of these aren't relevant at this time, but they exist as protected methods of TStreamedObject (refer to the article - page 3 - for the example code I provided).

You will undoubtedly notice the clunkiness of loading/saving streams. This is mentioned in the article.

So, lets consider a simple map (TMap)... the maps height and width are variable. The map consists of 3 layers. The first layer (TGroundLayer - Descended from TTileLayer) contains the base tile for each cell, the cells movement costs. The other two layers are TTileLayer objects that simply tell the engine what tiles to draw to create overhanging trees for example.

Code:
unit unitExample;

interface

uses streamedObject, classes, SysUtils;

type
 PWord = ^Word;

 TTileLayer = class(TStreamedObject)
 protected
  fTileData : PWord;
  fWidth    : integer;
  fHeight   : integer;

  function getAddress(base:PWord;x,y:integer):PWord;

  function getTile(x,y:integer):word;
  procedure setTile(x,y:integer;value:word);
 public
  constructor create;
  destructor destroy; override;

  procedure setSize(width,height:integer); virtual;

  property tile[x,y:integer]:word read getTile write setTile;
 published
  procedure load_ver1(reader:TReader); virtual;
  procedure save_ver1(writer:TWriter); virtual;
 end;

 TGroundLayer = class(TTileLayer)
 protected
  fMovementData : PWord;

  function getMovementData(x,y:integer):word;
  procedure setMovementData(x,y:integer;value:word);
 public
  constructor create;
  destructor destroy; override;

  procedure setSize(width,height:integer); override;

  property tile;
  property movementData[x,y:integer]:word read getMovementData write setMovementData;
 published
  procedure load_ver1(reader:TReader); override;
  procedure save_ver1(writer:TWriter); override;
 end;

 TMap = class(TStreamedObject)
 protected
  fGroundLayer : TGroundLayer;
  fLayer1      : TTileLayer;
  fLayer2      : TTileLayer;
  fName        : string;

      fWidth       : integer;
  fHeight      : integer;
 public
  constructor create;
  destructor destroy; override;

  procedure setSize(width,height:integer);

  property groundLayer:TGroundLayer read fGroundLayer;
  property layer1:TTileLayer read fLayer1;
  property layer2:TTileLayer read fLayer2;

  property name:string read fName write fName;

  property width:integer read fWidth;
  property height:integer read fHeight;

 published
  procedure load_ver1(reader:TReader);
  procedure save_ver1(writer:TWriter);
 end;

implementation

(*---TTileLayer------------------------------------------------------------------------*)

function TTileLayer.getAddress(base:PWord;x,y:integer):PWord;
var
 offset : cardinal;
 temp   : PWord;
begin
 if (x>=0) and (x<fWidth>=0) and (y<fHeight) then
 begin
  offset:=y*fWidth+x;
  temp:=base;
  inc(temp,offset);

  result:=temp;
 end
 else
 begin
  raise exception.create('Coordinates out of range in '+
   self.classname+'.getAddress('+intToStr(x)+','+intToStr(y)+
   ').  Should be in the range (0..'+intToStr(fWidth-1)+',0..'+
   intToStr(fHeight-1)+')');
 end;
end;

function TTileLayer.getTile(x,y:integer):word;
begin
 result:=getAddress(fTileData,x,y)^;
end;

procedure TTileLayer.setTile(x,y:integer;value:word);
begin
 getAddress(fTileData,x,y)^:=value;
end;

constructor TTileLayer.create;
begin
 inherited;

 fTileData:=nil;
end;

destructor TTileLayer.destroy;
begin
 if (fTileData<>nil) then
 begin
  freeMem(fTileData);
 end;

 inherited;
end;

procedure TTileLayer.setSize(width,height:integer);
begin
 if (fTileData<>nil) then
 begin
  freeMem(fTileData);
 end;

 getMem(fTileData,(width*height*sizeOf(word)));

       fWidth:=width;
 fHeight:=height;
end;

procedure TTileLayer.load_ver1(reader:TReader);
var
 x,y  : integer;
 temp : PWord;
begin
 // You MUST ensure the size is set before attempting to
 // load the data

 temp:=fTileData;

 for y:=1 to fHeight do
 begin
  for x:=1 to fWidth do
  begin
   temp^:=word(reader.readInteger);

   inc(temp);
  end;
 end;
end;

procedure TTileLayer.save_ver1(writer:TWriter);
var
 x,y  : integer;
 temp : PWord;
begin
 if (fTileData<>nil) then
 begin
  temp:=fTileData;

  for y:=1 to fHeight do
  begin
   for x:=1 to fWidth do
   begin
    writer.writeInteger(temp^);

    inc(temp);
   end;
  end;
 end
 else
 begin
  raise exception.create('Layer not initialised in '+self.className+'.save_ver1');
 end;
end;

(*---TGroundLayer------------------------------------------------------------------------*)

function TGroundLayer.getMovementData(x,y:integer):word;
begin
 result:=getAddress(fMovementData,x,y)^;
end;

procedure TGroundLayer.setMovementData(x,y:integer;value:word);
begin
 getAddress(fMovementData,x,y)^:=value;
end;

constructor TGroundLayer.create;
begin
 inherited;

 fMovementData:=nil;
end;

destructor TGroundLayer.destroy;
begin
 if (fMovementData<>nil) then
 begin
  freeMem(fMovementData);
 end;

 inherited;
end;

procedure TGroundLayer.setSize(width,height:integer);
begin
 inherited setSize(width,height);

 if (fMovementData<>nil) then
 begin
  freeMem(fMovementData);
 end;

 getMem(fMovementData,(width*height*sizeOf(word)));
end;

procedure TGroundLayer.load_ver1(reader:TReader);
var
 x,y  : integer;
 temp : PWord;
begin
 inherited load_ver1(reader);

 temp:=fMovementData;

 for y:=1 to fHeight do
 begin
  for x:=1 to fWidth do
  begin
   temp^:=word(reader.readInteger);

   inc(temp);
  end;
 end;
end;

procedure TGroundLayer.save_ver1(writer:TWriter);
var
 x,y  : integer;
 temp : PWord;
begin
 inherited save_ver1(writer);

 temp:=fMovementData;

 for y:=1 to fHeight do
 begin
  for x:=1 to fWidth do
  begin
   writer.writeInteger(temp^);

   inc(temp);
  end;
 end;
end;

(*---TMap------------------------------------------------------------------------*)

constructor TMap.create;
begin
       inherited;

 fGroundLayer:=TGroundLayer.create;
 fLayer1:=TTileLayer.create;
 fLayer2:=TTileLayer.create;

 fName:='';

 setSize(1,1);
end;

destructor TMap.destroy;
begin
 fGroundLayer.Free;

 fLayer1.free;

 fLayer2.free;

 inherited;
end;

procedure TMap.setSize(width,height:integer);
begin
 fGroundLayer.setSize(width,height);
 fLayer1.setSize(width,height);
 fLayer2.setSize(width,height);

 fWidth:=width;
 fHeight:=height;
end;

procedure TMap.load_ver1(reader:TReader);
var
 temp : TMemoryStream;
begin
 // Set the size
 setSize(reader.readInteger,reader.readInteger);

 // Read the name
 loadString(reader,fName);

 // Create a temporary stream
 temp:=TMemoryStream.create;

 // Load the stream containing the layers
 loadStream(reader,temp);

 // This is important- Return the position to 0 once you have loaded the stream
 temp.position:=0;

 // Load the layers
 fGroundLayer.loadFromStream(temp);
 fLayer1.loadFromStream(temp);
 fLayer2.loadFromStream(temp);

 // Get rid of our temporary stream
 temp.free;
end;

procedure TMap.save_ver1(writer:TWriter);
var
 temp : TMemoryStream;
begin
 // Save the size
 writer.writeInteger(fWidth);
 writer.writeInteger(fHeight);

 // Save the name
 saveString(writer,fname);

 // Create a temporary stream
 temp:=TMemoryStream.create;

       // Save the layers
 fGroundLayer.saveToStream(temp);
 fLayer1.saveToStream(temp);
 fLayer2.saveToStream(temp);

 // Save the temporary stream
 saveStream(writer,temp);

 // Get rid of our temporary stream
 temp.free;
end;

end.
A note about TGroundLayer.load_ver1 and TGroundLayer.save_ver1... this arrangement is not optimal since you will iterate through the map twice (once in the inherited load/save from TTileLayer and then once in TGroundLayers routines), however it does mean that if you add/remove data to TTileLayer you only have to update TTileLayer and the changes will be reflected without having to change the code for TGroundLayer. You could reimplement the code from TTileLayer in the TGroundLayer routines like this:-

Code:
procedure TGroundLayer.load_ver1(reader:TReader);
var
 tempTile : PWord;
 tempMove : PWord;
 x,y      : integer;
begin
 tempTile:=fTileData;
 tempMove:=fMovementData;
 for y:=1 to fHeight do
 begin
  for x:=1 to fWidth do
  begin
   tempTile^:=word(reader.readInteger);
   tempMove^:=word(reader.readInteger);

   inc(tempTile);
   inc(tempMove);
  end;
 end;
end;
This would be quicker since you only iterate through the layers data once, BUT, the two lots of data become mixed and you must then take care to ensure that the load/save routines in TGroundLayer reflect any changes you might make to TTileLayer. This is easy with only a small number of fields and a single version... imagine a lot of fields and multiple versions and you could quickly get in a mess. So, I would advise that (whilst it is not optimal from a speed point of view) you keep the data seperated into their classes as I have done in the example (this is optimal from the point of view of ease of maintenance... not to mention the OOP paradigm).

So how would you use these objects...

Code:
var
 myMap : TMap;
begin
 myMap:=TMap.create;
 myMap.name:='Test Map';
 myMap.setSize(10,10);

 myMap.groundLayer.tile[0,0]:=1;
 myMap.groundLayer.tile[0,1]:=2;
  
 // I'm sure you get the idea...
Then loading and saving your entire map is as simple as using TMap's loadFromStream and saveToStream routines.

A map that is 10 x 10 (all data set to 0) with the name 'Test Map' occupies 832 bytes on the disk. Consider that the raw data we are storing is 10 x 10 x 2 x 4 (width x height x sizeOf(word) x (ground tile + ground movement + layer 1 tile + layer 2 tile)) = 800 bytes... its pretty good at keeping the size of your data stores down because it doesn't add too much baggage. In reality if all of your map cells (tiles and movement data) were set to 256, then an approximate size would be 10 x 10 x 3 x 4 (width x height x data size x number of layers/data sets) = 1200 bytes. The reason that the data size is 3 (as stored in the stream) rather than 2 (as stored in memory) is that TReader and TWriter store a byte that indicates the size of the data and then the actual bytes that represent the data, but this is based on the actual numerical value and NOT the variable size, so 0 will always be represented by 2 bytes whether it is stored by your object as a byte, integer, word or cardinal. 255 is represented as 2 bytes, but 256 thats 3 bytes... 1 for the size and 2 for the data itself.

Hope this helps clarify the usage of the base class.