PDA

View Full Version : how to make a save function



neafriem
04-01-2005, 09:42 PM
I wonder how to make a easy save function so I can save variables and later reitreve them. And if it's not to hard to do, so the savefiles cannot be opened exept by my programm?
I'm a beginner so try to show me a easy way if there are any
:)

tux
04-01-2005, 10:08 PM
you could save it in a binary file.

im assuming the things you want to save arent dynamic arrays etc


var
f: File; // this is the file handle

b: Byte; // value to write
dw: DWord; // value to write

Begin
// first we need to assign a filename to the file handle
AssignFile(F, 'C:\this is the name of our file');

// now create (and reset to 0 bytes if it allready exists) and open
ReWrite(F, 1);

// assign some values to
b := 100;
dw := 2000;

// the data i will write is 1 byte and 2 dwords (creating a 9 byte file)

// this writes the actual data
BlockWrite(F, b, 1); // a byte has a size of 1
BlockWrite(F, dw, 4); // a dword has a size of 4

// if you dont know the size of something you can also do this
BlockWrite(F, dw, SizeOf(DWord);

// we must free all handles when finished
CloseFile(F);
end;

to read a file, you replace ReWrite with Reset and BlockWrite with BlockRead

Sly
04-01-2005, 10:45 PM
// if you dont know the size of something you can also do this
BlockWrite(F, dw, SizeOf(DWord);


I would use SizeOf() all the time, except use the variable name as the parameter instead the type. Then it still works even if the variable type changes.

BlockWrite(F, dw, SizeOf(dw));

tux
04-01-2005, 10:56 PM
good point

WILL
05-01-2005, 01:45 AM
Also, if you use strings to store names or others text data... you'll want to limit your string that is read. Or if you wish to store an undeterminate amount of text, be sure to store an index of the length of that string.

Variable string length
var CharName: String;
CharNameLen: Integer;

{Manipulate the data in your game/app...}

{Time to save it!}
CharNameLen := Length(CharName);
if (CharNameLen > 0) then
BlockWrite(FileStream, CharName, SizeOf(CharNameLen));


Fixed string length
var CharName: String[12];
// Notice that CharNameLen is not declared?

{Manipulate the data in your game/app...}

{Time to save it!}
BlockWrite(FileStream, CharName, SizeOf(CharName));

Sly
05-01-2005, 03:13 AM
Can you pass a String type to BlockWrite()? I thought you would have had to do some trickery such as

BlockWrite(F, PChar(MyString)^, Length(MyString));

Oh, and I believe you got the variable length string example wrong.


var
CharName: String;
CharNameLen: Integer;

{ To write a variable-length string }
begin
CharNameLen := Length(CharName);
{ Write the length of the string }
BlockWrite(F, CharNameLen, SizeOf(CharNameLen));
{ Now write the contents of the string }
if CharNameLen > 0 then
BlockWrite(F, PChar(CharName)^, CharNameLen);
end;

{ To read a variable-length string }
begin
{ Read the length of the string }
BlockRead(F, CharNameLen, SizeOf(CharNameLen));
{ Set the string to the required size }
SetLength(CharName, CharNameLen);
{ Read the contents of the string }
if CharNameLen > 0 then
BlockRead(F, PChar(CharName)^, CharNameLen);
end;

Robert Kosek
05-01-2005, 04:19 AM
Correct Sly, that's the process to write a string. However the mentioned string beforehand was a 12 byte array, so technically his method was right as well.

Sly
05-01-2005, 06:15 AM
Correct Sly, that's the process to write a string. However the mentioned string beforehand was a 12 byte array, so technically his method was right as well.

WILL's second example was a fixed-length string. His first example was a long string which cannot just be passed to BlockWrite() as-is.

WILL
05-01-2005, 10:22 AM
Yes, you are right Sly. I forgot to write the CharNameLen value first.

However you do not have to play with PChar's Pascal isn't evil that way. ;) I've never had an issue with writting straight from a string to a BlockWrite function.

Of course I have in the past seen how some have used interesting methods of Null terminating the string stored using a #0 value and you know to move on to the next thing. But such a thing is reminicent of thold DOS days where that extra space was vital with such memory restrictions that no longer exist.

Sly
05-01-2005, 11:48 AM
Maybe it is just with the stream methods that you can not pass a long string directly to them. Since BlockWrite is in the System unit, it probably knows how to handle long strings like the ReadLn and WriteLn functions do. TStream.Write() and related methods however do not know how to handle long strings, so you have to do the PChar trickery.

As a side note, I tried to look up BlockWrite in the Delphi 2005 help file. It has no separate help page any more. It no longer even tells you the parameter list for it. Just a list of I/O functions in the System unit. I had to load up Delphi 7 and use its help file to get more than a one line description of the function.

neafriem
05-01-2005, 08:17 PM
thanks, I think it's going to work now!!! :D

WILL
06-01-2005, 12:59 AM
One last nag. ;)

Be very careful with your savegame and loadgame routeens. Make sure if you change the data being stored you change the data being recalled. I cannot count the amount of times in the past that I've scalped myself crazy trying to find out what was wrong with my routeens only to find out that I didn't load the data properly. :roll:

Best of luck with your code. ;)

plugwash
06-01-2005, 01:34 AM
a few points here

1: shortstrings can be written direct to a file safely (as they are just arrays with a little compiler magic applied to allow operations that would not normally be allowed for arrays) this can be somewhat ineffeciant though especially if you use full length shortstrings.
2: ansistrings cannot be written direct to a file. you can write all the text with a typecast to pchar and derefernceing or by passing index 1 of the string. but you will have to make your own provisions to store length or store 1 byte past the end of the string to give a null terminator (yes this is safe with ansistrings) reading null terminated strings back in is quite tricky though.

in tp and D1 string is a shortstring and ansistrings don't exist

in later versions of delphi and in freepascal string may be a shortstring or an ansistring depending on settings

string(length) is always a shortstring and allows custom lengths.

3: if you wan't to be able to port your app to cpus of different endian and have savegame compatibility you have to consider byte order of integers in the save

4: make sure you use types that have a FIXED size (ie shortint/smallint/longint not integer) or you will have problems when changing compiler

5: whilst less storage effeciant text files can be a LOT easier to maintain and add things to without breaking compatibility you should at least give them serious consideration.

MrLobster
22-07-2005, 02:32 PM
you could save it in a binary file.

im assuming the things you want to save arent dynamic arrays etc

to read a file, you replace ReWrite with Reset and BlockWrite with BlockRead

Ok how about a solution to a dynamic array? because i have a dynamic array of a Class, which also holds a dynamic array, here a quick example.



tPlanet = class
Size : integer;
procedure Create;
end;

tSystem = class
Planets : array of tPlanets;
procedure Create;
end;

tGalaxy = class
Systems : array of tSystem;
procedure Create;
end;


Now how would I save this? And could my code be better?

Dan [/code]

tux
22-07-2005, 02:44 PM
write the length of the array and then loop through the array writing each variable.

rinse and repeat :lol:

cairnswm
22-07-2005, 02:56 PM
If you are doing classes - consider moving your parent class to tpersistant and then using WriteComponent to save it to disk.