1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
|
/*
* Seven Kingdoms: Ancient Adversaries
*
* Copyright 2019 Steven Lavoie
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
//Filename : PlayerStats.h
//Description : Handles file IO for player statistics information
#ifndef __PLAYERSTATS_H
#define __PLAYERSTATS_H
#include <OGAME.h>
#ifndef __ODYNARR_H
#include <ODYNARR.h>
#endif
namespace nsPlayerStats {
enum PlayStatus : uint32_t { UNPLAYED = 0, PLAYED = 1, COMPLETED = 2 };
enum RecordType : uint32_t { ScenarioPlayStatus = 0 };
namespace detail {
char const * const scn_dat_file = "PLAYSTAT.DAT";
enum { MAX_FILE_PATH = 260 }; //HACK: This is repeated all over. Should be global constant.
//
// These structures are written as-is to PLAYSTAT.DAT and cannot be changed
// without breaking the format. To prevent breaking changes, just add a new
// record type that includes whatever additional data.
//
//
// Dictates the reading/writing of the file. Every type of statistic
// must be preceded by a RecordHeader. This struct is written as-is
// to/from an array and file so ensure any changes are 4-byte aligned.
//
struct RecordHeader {
RecordType rec_type;
uint32_t rec_count;
uint32_t rec_size;
};
static_assert(sizeof(RecordHeader) == 12, "Changing RecordHeader is a breaking change for PLAYSTAT.DAT");
//
// Tracks whether a scenario has been played/completed. This
// struct is written as-is to/from an array and file so ensure
// any changes are 4-byte aligned.
//
struct ScenStat {
//
// The SAV format actually has MAX_FILE_PATH + 1, which was stupid. Since
// this field is not actively used and even legacy scenario files only contain
// an 8.3 filename, we'll dispense with the extra byte. Windows includes /0 in
// its MAX_PATH's 260 bytes anyway.
//
char game_name[MAX_FILE_PATH];
PlayStatus status;
};
static_assert(sizeof(ScenStat::game_name) == MAX_FILE_PATH, "Changing ScenStat is a breaking change for PLAYSTAT.DAT");
static_assert(sizeof(ScenStat) == 264, "Changing ScenStat is a breaking change for PLAYSTAT.DAT");
}
class PlayerStats
{
private:
detail::ScenStat * scn_stat_arr;
size_t scn_stat_arr_len;
bool write_player_stats();
public:
PlayStatus get_scenario_play_status(char const * game_name);
bool set_scenario_play_status(char const * game_name, PlayStatus status);
// If force_reload==true, the stats will be reloaded from the file or,
// if the file is deleted, the UI will be updated to reflect that
bool load_player_stats(bool force_reload = false);
PlayerStats();
~PlayerStats();
};
} // nsPlayerStats
extern nsPlayerStats::PlayerStats playerStats; // in AM.cpp
#endif
|