Allegro.cc - Online Community

Allegro.cc Forums » Game Design & Concepts » game saves in allegro?

This thread is locked; no one can reply to it. rss feed Print
game saves in allegro?
relay01
Member #6,988
March 2006
avatar

Now this is WAY ahead in my scope of programming and even if this is possible I don't believe I'd be able to do it yet but I figured I'd throw it out there just because... I'm bored and seek conversation...
Does allegro offer anything about game saves? Does this have to be done in something else in order to do game saves? Has anyone wrote a program in allegro that uses game saves? I just figure it would be very lame (especially with an expansive game) if your progress couldn't be saved. I know with the game I'm working on that if I didn't offer a game save that most wouldn't be able to beat it. ???

_____________________________________

Inphernic
Member #1,111
March 2001

No, Allegro doesn't offer game saving (although one could use config files for it, that would be exceedingly ghetto). That doesn't mean it's not possible.

spellcaster
Member #1,493
September 2001
avatar

Allegro packfile functions are exactly what you want. So, define your game data, and then save it. It's as easy as that.

--
There are no stupid questions, but there are a lot of inquisitive idiots.

Inphernic
Member #1,111
March 2001

Quote:

Allegro packfile functions are exactly what you want.

More like "Allegro packfile functions are exactly what you need", considering that if he would know anything about file saving/loading in Whatever++^2, he wouldn't be asking it like this (leading one to assume that what he basically wants are save_game() and load_game()). ;)

relay01
Member #6,988
March 2006
avatar

I'm not entirely familiar with packfiles yet. I'm working in C++ (which i'm new to) and allegro (which i'm also new to) I've been learning as much allegro and c++ as I could at every moment of free time... Which has left me with headaches and sleepless nights for a fortnight. I actually welcome going to calculus class because it's less confusing.

_____________________________________

Thomas Fjellstrom
Member #476
June 2000
avatar

allegro's PACKFILE is almost identical to C's FILE.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

spellcaster
Member #1,493
September 2001
avatar

See, we'd really like to help you, but right now what you're saying is basically:

"I want to do stuff. Can allegro do stuff?"

So, giving you an answer that actually helps you is kinda hard. But, let's say you're using something like this:

typedef struct {
     int level;
     int score;
     int lifes;
} GameData;

for your game data, what you'd do to save the data is to simply open a file, store the data and then close the file. if you're only targeting a single platform, something like:

GameData data;

FILE* f = fopen("game.sav", "wb");
fwrite(&data, sizeof(GameData), 1, f);
fclose(f);

will do the trick.
If you want to target several platforms, you should save each component on it's own:

GameData data;

FILE* f = fopen("game.sav", "wb");
fwrite(& (data.level) , sizeof(int), 1, f);
fwrite(& (data.score) , sizeof(int), 1, f);
fwrite(& (data.lifes) , sizeof(int), 1, f);
fclose(f);

if you don't know the fopen / fwrite / fclose methods - google for them. They are the basic C file IO functions, and you'll see them a lot. Also, allegro's packfile functions are modelled after them, so once you know how to use the std c routines, the allegros fuznctsions will be less intimidating.

--
There are no stupid questions, but there are a lot of inquisitive idiots.

Jonny Cook
Member #4,055
November 2003

Is there any reason why you would want to use Allegro's file IO routines instead of the standard C++ ones?

The face of a child can say it all, especially the mouth part of the face.

BAF
Member #2,981
December 2002
avatar

It's supposed to be more portable, endian safe, supports compression and encryption, and you can chunk it (pack_fopen_chunk).

relay01
Member #6,988
March 2006
avatar

Well that is what I asked... exactly... I wanted to know if allegro could do such saving features and apparently they can with packfiles... now if somebody could explain to me what a packfile is and does and how to use them... that would be awesome squared...

_____________________________________

Matthew Leverton
Supreme Loser
January 1999
avatar

Have you read the manual yet and tried? If you cannot do that, you cannot do anything at all.

miran
Member #2,407
June 2002

Quote:

now if somebody could explain to me what a packfile is and does and how to use them

Spellcaster already did that. Read his post again, but replace FILE with PACKFILE and all the fsomething() functions with pack_fsomething(). Everything else is the same or at least very similar. The manual explains most things that need explaining in detail.

--
sig used to be here

m c
Member #5,337
December 2004
avatar

I'm away from my dev pc now (and will be for hours yet) but packfiles != grabber datafiles do they?

(\ /)_____#_____####__#
(O.o)¯¯¯#¯¯¯¯¯#¯¯¯#¯¯#
(> <)__####__####__####
Megabytes are where I keep my Data.

miran
Member #2,407
June 2002

packfiles != datafiles

--
sig used to be here

Tobias Dammers
Member #2,604
August 2002
avatar

datafiles == subset_of(packfiles)

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

Shawn Hargreaves
The Progenitor
April 2000
avatar

At the risk of confusing things further, I have to say that fwrite (or pack_fwrite) are horrible API's. Totally untypesafe, error prone, and not portable across different endianess or processor word sizes.

I'd strongly recommend using individual component write functions (putw, putf, etc) instead.

Tobias Dammers
Member #2,604
August 2002
avatar

pack_mputXXX() is what you're looking for.

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

relay01
Member #6,988
March 2006
avatar

Tobias... you changed your avatar so for a second I couldn't recognize you (I'm a visual learner) but luckily you have a funny name so I could quickly catch on...
My game is so far from having the ablility to save anything so all this doesn't really mean anything to me yet... but what can I say... I like to spark conversation...
And I'm bored at work...

_____________________________________

Jonny Cook
Member #4,055
November 2003

Quote:

At the risk of confusing things further, I have to say that fwrite (or pack_fwrite) are horrible API's. Totally untypesafe, error prone, and not portable across different endianess or processor word sizes.

I'd strongly recommend using individual component write functions (putw, putf, etc) instead.

But isn't pack_frwite just essentially pack_putc, except for an array of characters instead of an individual character?

I thought pack_fwrite would look similar to this:

void pack_fwrite(void *data, int size, PACKFILE *pf) {
    for (int i = 0; i < size; ++ i) {
        pack_putc(data<i>, pf);
    }
}

So what's so unsafe about that? I'm sorry, but my knowledge endianess is pretty limited.

Is this something I need to consider whenever I read/write to/from files?

The face of a child can say it all, especially the mouth part of the face.

Shawn Hargreaves
The Progenitor
April 2000
avatar

Yes, that's exactly how pack_fwrite is implemented.

The problem is, this is taking the address of some other type (eg. an integer), treating that as a void *, then casting the void * to a byte pointer and reading the data as individual bytes.

This is not a safe thing to do.

For one thing, it is very error prone. How does it know how many bytes to process? You have to tell it. If you tell it wrong, it does the wrong thing.

And also, how many bytes there are in an int can be different on other machines. And which order those bytes are in can be different (that is called endianess). So a file you write on one computer will not be able to be read on a different one.

Solution: don't use API's that take void * parameters (or at least, not unless you absolutely have to). They're evil and not typesafe.

In this case you can easily just call the strongly typed methods for bytes, ints, floats, etc. Your code becomes portable, less error prone, and also shorter and more readable: what's not to like about that?

Tobias Dammers
Member #2,604
August 2002
avatar

Quote:

but luckily you have a funny name so I could quickly catch on...

I take this as an offence.

Quote:

So what's so unsafe about that? I'm sorry, but my knowledge endianess is pretty limited.

What Shawn said. Or, to rephrase:
Suppose you have a 16-bit short. Since a byte is only 8 bits wide, you have to split you number up into two 8-bit parts, a "high" byte (the MSB, Most Significant Byte) and a "low" byte (the LSB or Least Significant Byte). When storing these two bytes in a byte-sequence (e.g. RAM, a hard disk, a network stream), you have 2 choices: MSB first or LSB first. Which one you choose is a matter of convention, and differs between platforms. MSB-first is referred to as "little-endian", LSB-first as "big-endian".

Quote:

Is this something I need to consider whenever I read/write to/from files?

Yes, unless you store everything as single bytes or strings (which are arrays of bytes).

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

CursedTyrant
Member #7,080
April 2006
avatar

Something like this perhaps?

1#include <iostream>
2#include <fstream>
3using namespace std;
4 
5typedef struct PLAYER
6{
7 int x, y;
8} PLAYER;
9 
10int main()
11{
12 PLAYER *p1 = new PLAYER();
13 p1->x = 0; p1->y = 5;
14
15 ofstream os("file.dat", ios::binary);
16 os.write((char *)p1, sizeof(struct PLAYER));
17 os.close();
18 std::cout << "X1: " << p1->x << "\t" << "Y1: " << p1->y << endl;
19
20 PLAYER *p2 = new PLAYER();
21 ifstream is("file.dat", ios::binary);
22 is.read((char *)p2, sizeof(struct PLAYER));
23 is.close();
24 std::cout << "X2: " << p2->x << "\t" << "Y2: " << p2->y << endl;
25
26 std::cout << "\n\n";
27 system("pause");
28 return 0;
29}

---------
Signature.
----
[My Website] | [My YouTube Channel]

Shawn Hargreaves
The Progenitor
April 2000
avatar

That's the bad way to save things. It is just blindly dumping out whatever order of bytes happen to be in your PLAYER struct, which depends on what processor you happen to be running on today, and also how the compiler happened to lay out the struct in memory. The resulting files are fragile, non portable, and not future proof.

The only safe way to do this is to write your fields one at a time through some kind of interface that has well defined endianess rules:

output.WriteInt(p1->x);
output.WriteInt(p1->y);

Yes, it's a bit more typing for structures with lots of fields, but there are no shortcuts that don't lose safety.

And this extra typing is often actually a good thing because it provides a clear reference about exactly what the format of your file is: very handy if you need to extend it in the future, or if your types grow to include some fields that shouldn't be saved to disk, or if you add things that can't be saved at all by a simple binary dump (for instance pointers to other objects).

Actually I lied when I said there are no shortcuts: in languages with dynamic reflection capabilities (Python, C#, Java) you can use an automatic serializer to locate all the members for you and figure out the appropriate types. That's not possible in C++ though.

Tobias Dammers
Member #2,604
August 2002
avatar

...and in fact, automatic serialization has its downsides.
For example, in my current project, I use chunk files to store my object data. Sometimes I want to extend the functionality of an object, which means I have to add more fields to the class. The loader, however, checks for end-of-chunk, and if it can't read any more, it fills the rest of the class with default values. This way, I can still open older files without any data loss. This is pretty hard to achieve with an automatic serialization process.
And finally, often you don't need to store all data, but just a few, while the rest of the fields is calculated at run-time. In fact, if your objects reference other objects (for example, a pointer representing the current target of a space ship), then you need to save these references as something other than pointers (since pointers vary from one run to the next).

---
Me make music: Triofobie
---
"We need Tobias and his awesome trombone, too." - Johan Halmén

Shawn Hargreaves
The Progenitor
April 2000
avatar

It's true, the simple "just dump every single field" type serialization is pretty limited.

But there are smarter ways, especially if your language allows you to add reflection metadata on a per-field basis. For instance in C# you could write a serializer that understood things like:

class MyType
{
    public int ThisWillBeSerialized;
    public OtherType AndSoWillThis;

    [NoSerialize]
    public int ButThisOneWillNot;

    [SerializeOptional(Default="Hello World")]
    public string ThisMayNotBeThereInOldFileVersions;
}

ie. most stuff just works automatically, but you can decorate the type to indicate where you want other things to happen.

I'm a huge fan of reflection and attributes. It's a real shame that C++ doesn't support anything like that!

Go to: