write/read struct to file using functions , Visual C++ 2008 express

1.5k views Asked by At

I've made three files using Visual C++ 2008 express for a text based RPG game. Before I really dive into the whole thing I want to get the basics ironed out: new game, save game, continue game, quit game. So far I have the make a character section (in this case find a weapon) and quit game all ironed out. I'm stuck on how to pass the weapon stats from a struct to a save file. I appear to pass the members of the struct without issue and check the file to find "junk": Ì and -858993460 in place of my values.

How should I go about fixing my save_game and continue_game functions? I've done a lot of research trying to figure this out and nothing I've tried seems to help.

Here's the important pieces of the code:

struct player_character
{
char type;
int damage;
int stability;
};

void save_game(player_character& pc, ofstream &save);
void continue_game(player_character& pc, ifstream &get);

int main()
{   
player_character pc;
ofstream save;
ifstream get;

//rest of main() goes here.

//pause screen
system("pause");
return 0;
}

//rest of functions go here.

void save_game(player_character &pc, ofstream &save_data) 
{
save_data.open  ("save.dat", ios::binary);
    if (save_data.is_open())
    {
    save_data << "pc.type = " << pc.type << endl;
    save_data << "pc.damage = " << pc.damage << endl;
    save_data << "pc.stability = " << pc.stability << endl;
                //doesn't work
    //save_data.write(reinterpret_cast<char*>(&pc), sizeof(pc));
    save_data.close();
    }
    else
    {
    cout << " Error. Unable to open file.";
    }
}

void continue_game(player_character &pc, ifstream &get_data) 
{
get_data.open("save.dat");
if (get_data.is_open())
    {
                //doesn't work
    //get.read(reinterpret_cast<char*>(&pc), sizeof(pc));
    get.close();
    }
    else
    {
    cout << " Error. Unable to open file.";
    }
}

Thanks for the reponse. I'm trying the following revisions. The continue_game function appears to work. I recieve no errors (yet). When I select save after making a character I recieve the following error: Unhandled exception at 0x69197a28 in Undone.exe: 0xC0000005: Access violation reading location 0xffffffcc.

Google shows it as some sort of Windows issue.

Why is my function causing this error?

void save_game(ofstream &save, player_character const &pc) 
{
save.open  ("save.dat");
    if (save.is_open())
    {
    save.write(reinterpret_cast<char const *>(pc.type), sizeof pc.type); 
    save.write(reinterpret_cast<char const *>(pc.damage), sizeof pc.damage); 
    save.write(reinterpret_cast<char const *>(pc.stability), sizeof pc.stability); 
    save.close();
    }
    else
    {
    cout << " Error. Unable to open file.";
    }
}

int continue_game(ifstream &get) 
{
if (!get.read(reinterpret_cast<char *>(&pc.type), sizeof pc.type)) { /* error */ } 
if (!get.read(reinterpret_cast<char *>(&pc.damage), sizeof pc.damage)) { /* error */ } 
if (!get.read(reinterpret_cast<char *>(&pc.stability), sizeof pc.stability)) { /* error */ } 

return pc.type;
return pc.damage;
return pc.stability;
}

I've changed the save_game and continue_game code and included it here. I also collected the debugger and autos output (small version of autos, not all seven pages). Apparently my values are not being evaluated in save_game so continue_game has nothing to work with and doesn't cause errors.

Here's the code and debugger/autos printouts:

int save_game(ofstream &save, player_character& pc) 
{
save.open  ("save.dat", ios::binary);
if (save.is_open())
{
//the error hits here:
    save.write(reinterpret_cast<char const *>(pc.type), sizeof pc.type); 
    save.write(reinterpret_cast<char const *>(pc.damage), sizeof pc.damage); 
    save.write(reinterpret_cast<char const *>(pc.stability), sizeof pc.stability); 
    save.close();
}
else
{
    cout << " Error. Unable to open file.";
}
return pc.type;
return pc.damage;
return pc.stability;
}

int continue_game(ifstream &get, player_character& pc)
{
get.open  ("save.dat", ios::binary);
if (get.is_open())
{
    if (!get.read(reinterpret_cast<char *>(&pc.type), sizeof pc.type)) { /* error */ } 
    if (!get.read(reinterpret_cast<char *>(&pc.damage), sizeof pc.damage)) { /* error */ } 
    if (!get.read(reinterpret_cast<char *>(&pc.stability), sizeof pc.stability)) { /* error */ } 
    get.close();
}
else
{
    cout << " Error. Unable to open file.";
}
return pc.type;
return pc.damage;
return pc.stability;
}

Debugger Output Window: First-chance exception at 0x644e7a28 in Undone.exe: 0xC0000005: Access violation reading location 0xffffffcc. Unhandled exception at 0x644e7a28 in Undone.exe: 0xC0000005: Access violation reading location 0xffffffcc.

Autos: - pc {type='Ì' damage=-858993460 stability=-858993460 } player_character & type -52 'Ì' char damage -858993460 int stability -858993460 int pc.type -52 'Ì' char - save {_Filebuffer={...} } std::basic_ofstream > & + std::basic_ostream > {...} std::basic_ostream > + _Filebuffer {_Pcvt=0x00000000 _Mychar='Ì' _Wrotesome=false ...} std::basic_filebuf >


Well I'vve managed to make some progress. I discovered I needed to place arguments into my main_menu function (mind you I'm not taking about the main() function, but one I made) so that they would be passed on to my save_game function. I was also able to stop the access error by adding an & into my write function. So this:

save.write(reinterpret_cast<char const *>(&pc.type), sizeof pc.type); 
save.write(reinterpret_cast<char const *>(&pc.damage), sizeof pc.damage); 
save.write(reinterpret_cast<char const *>(&pc.stability), sizeof pc.stability); 

instead of:

save.write(reinterpret_cast<char const *>(pc.type), sizeof pc.type); 
save.write(reinterpret_cast<char const *>(pc.damage), sizeof pc.damage); 
save.write(reinterpret_cast<char const *>(pc.stability), sizeof pc.stability); 

The savve_game code still doesn't work properly yet when it comes to putting data into a file but it does printout to the screen.

3

There are 3 answers

0
m e On BEST ANSWER

It looks like I figured it out! Here's what I've come up with: struct in array...and it appears to work just fine.

void save_game(fstream& file, player_type& pc)
{
    file.open("save.dat");
    if (file.is_open())
        for (int i = 0; i < 1; i++)
        {
            file << pc.w_name << endl;
            file << pc.d_rate << endl;
            file << pc.s_rate << endl;
            file.close();
        }
        else
            cout << " Error. Unable to open file.";
    menu(pc);
}//end save function

void continue_game(fstream& file, player_type player[])
{
    file.open("save.dat");
    if (file.is_open())
        for (int i = 0; i < 1; i++)
        {
            file >> player[i].w_name;
            file >> player[i].d_rate;
            file >> player[i].s_rate;
            file.close();
        }                   
        else
            cout << " Error. Unable to open file.";
    for (int i = 0; i < 1; i++)
    {
        cout << "You are continuing the game with the following weapon: " << player[i].w_name << endl;
        cout << "Which has a damage rating of " << player[i].d_rate << " and a stability rating of " << player[i].s_rate << endl; 
    }
    cout << endl;
    menu(pc);
}//end continue_game
2
Kerrek SB On

You have to read and write each primitive data type, one by one, and use unformatted I/O only. Example:

void serialize(std::ostream & o, Player const & p)
{
    o.write(reinterpret_cast<char const *>(p.type), sizeof p.type);
    o.write(reinterpret_cast<char const *>(p.damage), sizeof p.damage);
    o.write(reinterpret_cast<char const *>(p.stability), sizeof p.stability);
}

Player deserialize(std::istream & i)
{
    Player p;
    char pt;
    int pd, ps;

    if (!i.read(reinterpret_cast<char *>(&pt), sizeof pt)) { /* error */ }
    if (!i.read(reinterpret_cast<char *>(&pd), sizeof pd)) { /* error */ }
    if (!i.read(reinterpret_cast<char *>(&ps), sizeof ps)) { /* error */ }

    p.type = pt; p.damage = pd; p.stability = ps;
    return p;
}
1
std''OrgnlDave On

Follow-up to the previous answer and edited question.

Judging by your continue_game signature,

int continue_game(ifstream &get)

pc must be some form of global struct that holds the character you're trying to resume. If pc is allocated at run-time, then you are using it incorrectly.

When you do this:

    return pc.type;
    return pc.damage;
    return pc.stability;

A function can only have one return-type; only pc.type would be returned. This is not the cause of the error, but rather a design flaw you should be aware of.

Your initial approach to continue_game, wherein you passed in the relevant struct as a parameter,

void continue_game(player_character &pc, ifstream &get_data) 

was a much better idea. I believe that if you combine the two you will find yourself without the error.

If the error continues, please use a debugger and paste the line. In addition, I can provide a sample of the code I described if you wish me to.