source: trunk/save.cxx @ 14

Last change on this file since 14 was 5, checked in by jtv, 14 years ago

Implemented saving/restoring of game state

File size: 4.1 KB
Line 
1/*
2This file is part of libmines.
3
4Copyright (C) 2005, Jeroen T. Vermeulen <jtv@xs4all.nl>
5
6libmines is free software; you can redistribute it and/or modify it under the
7terms of the GNU General Public License as published by the Free Software
8Foundation; either version 2 of the License, or (at your option) any later
9version.
10
11libmines is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13PARTICULAR PURPOSE.  See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License along with
16libmines; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
17Suite 330, Boston, MA  02111-1307  USA
18*/
19
20/** We encode our data files in a home-rolled base64--see RFC 3548--with 6 bits
21 * of data per character.
22 */
23
24#include <cassert>
25#include <stdexcept>
26#include <string>
27
28#include "save.hxx"
29
30using namespace std;
31
32namespace
33{
34/// Header at start of saved file--we may change the format later
35const string saveheader = "#mines 0.1\n";
36
37unsigned char encode[64], decode[256];
38volatile bool encoding_initialized = false;
39
40int init_range(char first, char last, int n)
41{
42  for (unsigned char i=first; i <= last; ++i, ++n)
43  {
44    encode[n] = i;
45    decode[i] = n;
46  }
47  return n;
48}
49}
50
51
52void initialize_encoding()
53{
54  if (encoding_initialized) return;
55
56  int n = 0;
57  n = init_range('A','Z',n);
58  n = init_range('a','z',n);
59  n = init_range('0','9',n);
60  n = init_range('+','+',n);
61  n = init_range('/','/',n);
62  assert(n == 64);
63
64  encoding_initialized = true;
65}
66
67
68char *write_header(char *here)
69{
70  strcpy(here,saveheader.c_str());
71  return here + saveheader.size();
72}
73
74
75const char *read_header(const char *here)
76{
77  if (strncmp(here,saveheader.c_str(),saveheader.size()) != 0)
78    throw runtime_error("Saved game not in recognized format");
79  return here + saveheader.size();
80}
81
82
83unsigned int extract_char(const char *&here)
84{
85  const unsigned char c = *here++;
86  if (!decode[c] && c != encode[0])
87    throw runtime_error("Unexpected character in data block: '" +
88        string(here-1,here) + "'");
89  return decode[c];
90}
91
92char produce_char(unsigned int x)
93{
94  assert(x < 64);
95  assert(encode[x]);
96  return encode[x];
97}
98
99const char *skip_whitespace(const char *here)
100{
101  while (*here && isspace(*here)) ++here;
102  return here;
103}
104
105int read_int(const char key[], const char *&here)
106{
107  const size_t keylen = 4;
108  assert(strlen(key)==keylen);
109
110  here = skip_whitespace(here);
111  if (!*here) throw runtime_error("Unexpected end of saved-game buffer");
112  if (strncmp(key,here,keylen) != 0)
113    throw runtime_error("Invalid saved game format: "
114        "no " + string(key) + " field "
115        "(found '" + string(here,here+4) + "' instead)");
116  here = skip_whitespace(here + keylen);
117  const int result = atoi(here);
118  while (*here && !isspace(*here)) ++here;
119  return result;
120}
121
122
123char *write_int(const char key[], char *here, int val)
124{
125  const size_t keylen = 4;
126  assert(strlen(key) == keylen);
127
128  // TODO: Set locale to "C" first!
129  sprintf(here, "%s %d\n", key, val);
130  return here + strlen(here);
131}
132
133
134char *write_newline(char *here)
135{
136  *here = '\n';
137  return here + 1;
138}
139
140
141int linepadding(int bitsperline)
142{
143  return ((bitsperline+7)/8) % 3;
144}
145
146
147namespace
148{
149inline const char *padbytes(int padding)
150{
151  static const char pad[3] = "==";
152
153  assert(padding >= 0);
154  assert(padding < 3);
155
156  return pad + (2-padding);
157}
158}
159
160
161char *write_eol(char *here, int padding)
162{
163  strcpy(here,padbytes(padding));
164  here += padding;
165  return write_newline(here);
166}
167
168
169const char *read_eol(const char *here, int padding)
170{
171  if (padding && strncmp(here,padbytes(padding),padding) != 0)
172    throw runtime_error("Saved game format error: incorrect end of data line "
173        "('" + string(here,here+padding) + "' instead of "
174        "'" + padbytes(padding) + "')");
175  here += padding;
176  if (!isspace(*here))
177  {
178    if (!*here) throw runtime_error("Saved game format error: truncated data");
179    throw runtime_error("Unespected data at end of line: '" +
180        string(here,here+1) + "'");
181  }
182
183  return skip_whitespace(here);
184}
185
186
187void terminate(char *here)
188{
189  *here = '\0';
190}
191
Note: See TracBrowser for help on using the repository browser.