• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 228
  • Last Modified:

Saving Variables

Hi experts, I have challenge for you:

   I was recently re-visiting a game I made a few years ago and now want to put in a save game function, however there is a catch.... I have several hundred variables, including numerous 3 and 4 dimensonal arrays!  and at the moment I cant think on an easy way to save & load the variables....

Is there a quick way? I guess its not possible just to dump the memory to a file or something?
and how would you advise it best to store a 3 or 4 dimensional array?

anyhelp would be great

thanks

David
0
DavidBirch2dotCom
Asked:
DavidBirch2dotCom
  • 5
  • 5
  • 3
  • +2
4 Solutions
 
BlackTigerXCommented:
if you would've put those into structures (records) it would be easy

to store arrays just use a Stream... I'll find a link where I recently answered such thing
0
 
Russell LibbySoftware Engineer, Advisory Commented:
David,
Are the arrays static or dynamic? And do they contain a fixed size data? (eg, byte, bool, word, integer, double) If so, then these arrays could easily be dumped to memory/disk.

Regards,
Russell
0
 
BlackTigerXCommented:
here it is, passing an array to a Stream and viceversa:

http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_21381655.html

from there you can just load/save it to a file
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
Russell LibbySoftware Engineer, Advisory Commented:

As I stated, this is great as long as the data is flat. Now throw an array of string, pchar, etc into this mix and you will have BIG problems.


Russell
0
 
DavidBirch2dotComAuthor Commented:
>>if you would've put those into structures (records) it would be easy
lol indeed, unfortunatly I wrote this monster, (12,000 lines+) several years ago after only 6 months causal programming experiance ;)

BlackTigerX, thanks for the link, but I'd rather not work with database tables for this.

rllibby, if possible that would be great, all but 2 arrays are static integer or real arrays

David
0
 
paulb1989Commented:
Look at the TReader and TWriter classes, they are in the classes unit.

Simply create them with a stream as a parameter (could be a TFileStream) and write/read data to the stream easily with their methods, then save the stream to a file.
0
 
DavidBirch2dotComAuthor Commented:
paulb1989> do you have a demo/ source of this?
0
 
paulb1989Commented:
procedure TForm1.WriteBTNClick(Sender: TObject);
var
  MyInt: Integer;
  MyString: String;
  Stream: TMemoryStream;
  Writer: TWriter;
begin
  Stream := TMemoryStream.Create;
  Writer := TWriter.Create(Stream, 4096);

  try
    Writer.WriteSingle(MyInt);
    Writer.WriteString(MyString);

    Writer.FlushBuffer;

    Stream.SaveToFile('MyFile.ext');
  finally
    Reader.Free;
    Stream.Free;
  end;
end;

procedure TForm1.ReadBTNClick(Sender: TObject);
var
  MyInt: Integer;
  MyString: String;
  Stream: TMemoryStream;
  Reader: TReader;
begin
  Stream := TMemoryStream.Create;
  Reader := TReader.Create(Stream, 4096);

  try
    Stream.LoadFromFile('MyFile.ext');
    MyInt := Round(Reader.ReadSingle);
    MyString := Reader.ReadString;
  finally
    Reader.Free;
    Stream.Free;
  end;
end;
0
 
Slick812Commented:
hello DavidBirch2dotCom , , If you have never used the TFileStream to save and load "Multi-Data" files then you may find this to be less than easy to learn and execute the methods for this. . . You can go to this Web Page  - -

http://www.angelfire.com/hi5/delphizeus/customfiles.html

which will give you information about using TFileStream to make  Multi-Data Files, files with many "Data Segments" of many diferent variables, but it's kind of "Wordy" and long winded. .
However I do not think there is a "One Size Fits All"  method to do this sort of thig, I would think you would need to develop a specific methods for the specific File data you need to store. . . I have done many custom files to store specific data, ask questions if you need more information
0
 
Russell LibbySoftware Engineer, Advisory Commented:
A fairly simple (and generic) way which uses a TMemoryStream descendant. Demonstrates most data types, including fixed size arrays.

Regards,
Russell

---

example of writing/reading/saving

var  b1, b2:        Boolean;
     cb1, cb2:      Byte;
     c1, c2:        Char;
     w1, w2:        Word;
     i1, i2:        Integer;
     ui1, ui2:      LongWord;
     f1, f2:        Single;
     r1, r2:        Real;
     d1, d2:        Double;
     s1, s2:        String;
     ia1, ia2:      Array [0..9, 0..9] of Integer;
     ra1, ra2:      Array [0..9, 0..9] of Real;
begin

  // Set the starting values for writing (to use for comparison later)
  b1:=True;
  cb1:=203;
  c1:='D';
  w1:=32767;
  i1:=1234567;
  ui1:=76543210;
  f1:=100.123;
  r1:=200.567;
  d1:=100234.123536;
  s1:='Hello world!';
  FillMemory(@ia1, SizeOf(ia1), 3);
  FillMemory(@ra1, SizeOf(ra1), 2);

  // Create the var stream
  with TVarStream.Create do
  begin
     // Write the data variables to the stream
     WriteBool(b1);
     WriteByte(cb1);
     WriteChar(c1);
     WriteWord(w1);
     WriteInteger(i1);
     WriteCardinal(ui1);
     WriteSingle(f1);
     WriteReal(r1);
     WriteDouble(d1);
     WriteString(s1);
     WriteBinary(@ia1, SizeOf(ia1));
     WriteBinary(@ra1, SizeOf(ra1));
     // Reset the stream to start
     Position:=0;
     // Read the variables back
     b2:=ReadBool;
     cb2:=ReadByte;
     c2:=ReadChar;
     w2:=ReadWord;
     i2:=ReadInteger;
     ui2:=ReadCardinal;
     f2:=ReadSingle;
     r2:=ReadReal;
     d2:=ReadDouble;
     s2:=ReadString;
     ReadBinary(@ia2, SizeOf(ia2));
     ReadBinary(@ra2, SizeOf(ra2));
     // Compare the values
     Assert(b1 = b2, 'Value mismatch');
     Assert(cb1 = cb2, 'Value mismatch');
     Assert(c1 = c2, 'Value mismatch');
     Assert(w1 = w2, 'Value mismatch');
     Assert(i1 = i2, 'Value mismatch');
     Assert(ui1 = ui2, 'Value mismatch');
     Assert(f1 = f2, 'Value mismatch');
     Assert(r1 = r2, 'Value mismatch');
     Assert(d1 = d2, 'Value mismatch');
     Assert(s1 = s2, 'Value mismatch');
     Assert(CompareMem(@ia1, @ia2, SizeOf(ia1)), 'Value mismatch');
     Assert(CompareMem(@ra1, @ra2, SizeOf(ra1)), 'Value mismatch');
     // Example of save to file
     SaveToFile('c:\test.dat');
     // Free the stream
     Free;
  end;

end;

----- source code ------


unit VarStream;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit           :  VARSTREAM
//   Author         :  rllibby
//   Date           :  04.20.2005
//
//   Description    :  Memory stream descendant that allows for both variable
//                     storing and loading. LoadFromFile and SaveToFile are already
//                     exposed from the parent class.
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Classes;

////////////////////////////////////////////////////////////////////////////////
//   VarStream data types
////////////////////////////////////////////////////////////////////////////////
const
  VS_BOOL           =  #1;
  VS_CHAR           =  #2;
  VS_BYTE           =  #3;
  VS_WORD           =  #4;
  VS_INT            =  #5;
  VS_UINT           =  #6;
  VS_FLOAT          =  #7;
  VS_REAL           =  #8;
  VS_DOUBLE         =  #9;
  VS_STR            =  #10;
  VS_BINARY         =  #11;

////////////////////////////////////////////////////////////////////////////////
//   VarStream exception class
////////////////////////////////////////////////////////////////////////////////
type
  EVarStream        =  class(Exception);

////////////////////////////////////////////////////////////////////////////////
//   VarStream resource strings
////////////////////////////////////////////////////////////////////////////////
resourcestring
  resWriteError     =  'Failed to write data to the stream!';
  resReadError      =  'Failed to read data from the stream!';
  resInvalidType    =  'Data type specified does not match what is stored in the stream!';
  resInvalidSize    =  'Data size specified does not match what is stored in the stream!';

////////////////////////////////////////////////////////////////////////////////
//   TVarStream class
////////////////////////////////////////////////////////////////////////////////
type
  TVarStream        =  class(TMemoryStream)
  private
     // Private declarations
  protected
     // Protected declarations
     procedure      ReadStrData(var Data: String);
     procedure      ReadData(Ident: Char; Data: Pointer);
     procedure      WriteData(Ident: Char; Data: Pointer; Size: Integer);
  public
     // Public declarations
     function       ReadBool: Boolean;
     function       ReadChar: Char;
     function       ReadByte: Byte;
     function       ReadWord: Word;
     function       ReadInteger: Integer;
     function       ReadCardinal: LongWord;
     function       ReadSingle: Single;
     function       ReadReal: Real;
     function       ReadDouble: Double;
     function       ReadString: String;
     procedure      ReadBinary(Value: Pointer; Size: Integer);
     procedure      WriteBool(Value: Boolean);
     procedure      WriteChar(Value: Char);
     procedure      WriteByte(Value: Byte);
     procedure      WriteWord(Value: Word);
     procedure      WriteInteger(Value: Integer);
     procedure      WriteCardinal(Value: LongWord);
     procedure      WriteSingle(Value: Single);
     procedure      WriteReal(Value: Real);
     procedure      WriteDouble(Value: Double);
     procedure      WriteString(Value: String);
     procedure      WriteBinary(Value: Pointer; Size: Integer);
  end;

implementation

procedure TVarStream.WriteData(Ident: Char; Data: Pointer; Size: Integer);
var  dwPosition:    Integer;
begin

  // Save current position for rewinding on exception
  dwPosition:=Position;

  // Exception trap
  try
     // Write the identifier
     if (Write(Ident, SizeOf(Char)) <> SizeOf(Char)) then raise EVarStream.CreateRes(@resWriteError);
     // Check identifier
     if (Ident in [VS_STR, VS_BINARY]) then
     begin
        // Store variable size
        if (Write(Size, SizeOf(Integer)) <> SizeOf(Integer)) then raise EVarStream.CreateRes(@resWriteError);
     end;
     // Store the actual data
     if (Write(Data^, Size) <> Size) then raise EVarStream.CreateRes(@resWriteError);
  except
     // Rewind stream position to location before the write
     Position:=dwPosition;
     // Re raise the exception
     raise;
  end;

end;

procedure TVarStream.ReadStrData(var Data: String);
var  dwPosition:    Integer;
     dwSize:        Integer;
     cIdent:        Char;
begin

  // Save current position for rewinding on exception
  dwPosition:=Position;

  // Exception trap
  try
     // Read the  stored identifier
     if (Read(cIdent, SizeOf(Char)) <> SizeOf(Char)) then raise EVarStream.CreateRes(@resReadError);
     // Compare identifiers
     if (cIdent <> VS_STR) then raise EVarStream.CreateRes(@resInvalidType);
     // Read in the variable size
     if (Read(dwSize, SizeOf(Integer)) <> SizeOf(Integer)) then raise EVarStream.CreateRes(@resReadError);
     // Set the size of the result string
     SetLength(Data, dwSize);
     // Read data if more than zero bytes
     if (dwSize > 0) then
     begin
        // Read the actual data
        if (Read(Data[1], dwSize) <> dwSize) then raise EVarStream.CreateRes(@resReadError);
     end;
  except
     // Rewind stream position to location before the read
     Position:=dwPosition;
     // Re raise the exception
     raise;
  end;

end;

procedure TVarStream.ReadBinary(Value: Pointer; Size: Integer);
var  dwPosition:    Integer;
     dwSize:        Integer;
     cIdent:        Char;
begin

  // Save current position for rewinding on exception
  dwPosition:=Position;

  // Exception trap
  try
     // Read the  stored identifier
     if (Read(cIdent, SizeOf(Char)) <> SizeOf(Char)) then raise EVarStream.CreateRes(@resReadError);
     // Compare identifiers
     if (cIdent <> VS_BINARY) then raise EVarStream.CreateRes(@resInvalidType);
     // Read in the variable size
     if (Read(dwSize, SizeOf(Integer)) <> SizeOf(Integer)) then raise EVarStream.CreateRes(@resReadError);
     // Check the stored size with the passed size (MUST MATCH)
     if (dwSize <> Size) then raise EVarStream.CreateRes(@resInvalidSize);
     // Read data if more than zero bytes
     if (dwSize > 0) then
     begin
        // Read the actual data
        if (Read(Value^, dwSize) <> dwSize) then raise EVarStream.CreateRes(@resReadError);
     end;
  except
     // Rewind stream position to location before the read
     Position:=dwPosition;
     // Re raise the exception
     raise;
  end;

end;

procedure TVarStream.ReadData(Ident: Char; Data: Pointer);
var  dwPosition:    Integer;
     dwSize:        Integer;
     cIdent:        Char;
begin

  // Save current position for rewinding on exception
  dwPosition:=Position;

  // Exception trap
  try
     // Read the  stored identifier
     if (Read(cIdent, SizeOf(Char)) <> SizeOf(Char)) then raise EVarStream.CreateRes(@resReadError);
     // Compare identifiers
     if (cIdent <> Ident) then raise EVarStream.CreateRes(@resInvalidType);
     // Calculate the data size (can't handle string or binary data here)
     case cIdent of
        VS_BOOL     :  dwSize:=SizeOf(Boolean);
        VS_CHAR     :  dwSize:=SizeOf(Char);
        VS_BYTE     :  dwSize:=SizeOf(Byte);
        VS_WORD     :  dwSize:=SizeOf(Word);
        VS_INT      :  dwSize:=SizeOf(Integer);
        VS_UINT     :  dwSize:=SizeOf(LongWord);
        VS_FLOAT    :  dwSize:=SizeOf(Single);
        VS_REAL     :  dwSize:=SizeOf(Real);
        VS_DOUBLE   :  dwSize:=SizeOf(Double);
     else
        // Raise exception
        raise EVarStream.CreateRes(@resInvalidType);
     end;
     // Read the actual data
     if (Read(Data^, dwSize) <> dwSize) then raise EVarStream.CreateRes(@resReadError);
  except
     // Rewind stream position to location before the read
     Position:=dwPosition;
     // Re raise the exception
     raise;
  end;

end;

function TVarStream.ReadBool: Boolean;
begin
  ReadData(VS_BOOL, @Result);
end;

function TVarStream.ReadChar: Char;
begin
  ReadData(VS_CHAR, @Result);
end;

function TVarStream.ReadByte: Byte;
begin
  ReadData(VS_BYTE, @Result);
end;

function TVarStream.ReadWord: Word;
begin
  ReadData(VS_WORD, @Result);
end;

function TVarStream.ReadInteger: Integer;
begin
  ReadData(VS_INT, @Result);
end;

function TVarStream.ReadCardinal: LongWord;
begin
  ReadData(VS_UINT, @Result);
end;

function TVarStream.ReadSingle: Single;
begin
  ReadData(VS_FLOAT, @Result);
end;

function TVarStream.ReadReal: Real;
begin
  ReadData(VS_REAL, @Result);
end;

function TVarStream.ReadDouble: Double;
begin
  ReadData(VS_DOUBLE, @Result);
end;

function TVarStream.ReadString: String;
begin
  ReadStrData(Result);
end;

procedure TVarStream.WriteBool(Value: Boolean);
begin
  WriteData(VS_BOOL, @Value, SizeOf(Boolean));
end;

procedure TVarStream.WriteChar(Value: Char);
begin
  WriteData(VS_CHAR, @Value, SizeOf(Char));
end;

procedure TVarStream.WriteByte(Value: Byte);
begin
  WriteData(VS_BYTE, @Value, SizeOf(Byte));
end;

procedure TVarStream.WriteWord(Value: Word);
begin
  WriteData(VS_WORD, @Value, SizeOf(Word));
end;

procedure TVarStream.WriteInteger(Value: Integer);
begin
  WriteData(VS_INT, @Value, SizeOf(Integer));
end;

procedure TVarStream.WriteCardinal(Value: LongWord);
begin
  WriteData(VS_UINT, @Value, SizeOf(LongWord));
end;

procedure TVarStream.WriteSingle(Value: Single);
begin
  WriteData(VS_FLOAT, @Value, SizeOf(Single));
end;

procedure TVarStream.WriteReal(Value: Real);
begin
  WriteData(VS_REAL, @Value, SizeOf(Real));
end;

procedure TVarStream.WriteDouble(Value: Double);
begin
  WriteData(VS_DOUBLE, @Value, SizeOf(Double));
end;

procedure TVarStream.WriteString(Value: String);
begin
  if (Length(Value) > 0) then
     WriteData(VS_STR, @Value[1], Length(Value))
  else
     WriteData(VS_STR, PChar(Value), 0);
end;

procedure TVarStream.WriteBinary(Value: Pointer; Size: Integer);
begin
  WriteData(VS_BINARY, Value, Size);
end;

end.
0
 
DavidBirch2dotComAuthor Commented:
Update, I have read the link posted by Slick812  with interest, and am going to try it out over the weekend, I have a question though,

would something like this work ?

intarray: array[1..10,1..10,1..10] of Integer;
...
  FileStream1.WriteBuffer(intarray, SizeOf(intarray));

work ?

Thanks for the code rllibby, I am sure it will come in useful, but I'd prefur to have a go at writing my own code at least the first time so that I learn how it works.

David
0
 
Russell LibbySoftware Engineer, Advisory Commented:
David,

No problem, I just wrapped up the variable type handling for you. It is all stream based, just in a wrapper. Regarding your question, the answer is yes:

FileStream1.WriteBuffer(intarray, SizeOf(intarray));

Will work fine "as is".

Russell
0
 
Slick812Commented:
since you ask the question, you may be unsure about the properties of the array?

since your array has only integers in it (integers have a Fixed memory allocation for it's Data block) and your array definition is Static (it gives the sizes of the array and sub arrays and does not change, then it also uses a Fixed memory allocation for it's Data block (Dynamic Arrays do not), so you can use the  SizeOf(intarray), without problem

I have made myself use the first array member in the Buffer parameter, it is not nessary for your code but it is nessary for Dynamic arrays, so I try and default my code writting to that, ,  to try and avoid code errors when using Dynamic arrays and being half (or fully) alssep at the time.

FileStream1.WriteBuffer(intarray[1,1,1], SizeOf(intarray));

if you are using a dynamic array, or just want to write a part of an array, you can do the math to get the size of the array (or partial array)


FileStream1.WriteBuffer(intarray[1,1,1],  Length(intarray) * Length(intarray[1]) * Length(intarray[1,1])  * SizeOf(Integer));

the above code is untested, just from head, may have a syntax error, but should give you the Idea.
0
 
DavidBirch2dotComAuthor Commented:
Thanks for your help guys, it works :) see http://www.davidbirch2.com/Games/Interplanetary.shtml if your interested :)  I was quite suprised at the speed of saving in this maner, it saved nearly 200,000 integers into a 1/2 meg file in erm 'no time' is this the fasted method of saving?

Thanks again

David
0
 
Slick812Commented:
I guess it is as fast as any other? the "Speed" is more of a Hard Disk Write time, than a program code method speed, usually the disk write always takes more time than the code completion time. . . A half a meg of file is usually always fast, compared to say 100 meg file

I'm suprised you think  11,000  lines of code is huge   :-)

I'm not sure anyone but programmers would be interested (or understand) in the amout of lines in a code mess?

I'm glad you got it to work!

Good luck
0
 
DavidBirch2dotComAuthor Commented:
Slick812, I meant fast as compared to writing the same data to a text file, since you avoid conversion to strings.

>I'm suprised you think  11,000  lines of code is huge   :-)
well its the largest I have yet writen, but i dont program for a living (yet) ;)

David
0
 
Russell LibbySoftware Engineer, Advisory Commented:

David,

Persistence (to disk) tends to be the slowest part of the overall "saving" operation, but right after that would follow any conversion routines that needed to be performed. (eg string<->integer). By removing the conversion and storing natively, you end up with a vert fast persistence mechanism.

Russell
 

0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 5
  • 5
  • 3
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now