1 /*
2 * Seven Kingdoms: Ancient Adversaries
3 *
4 * Copyright 2018 Jesse Allen
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 //Filename : ReplayFile.cpp
22 //Description : Replay File IO
23
24 #include <string.h>
25
26 #include <ReplayFile.h>
27 #include <OCONFIG.h>
28 #include <OERROR.h>
29 #include <ONATIONA.h>
30 #include <OREMOTE.h>
31 #include <OREMOTEQ.h>
32 #include <version.h>
33 #include <ConfigAdv.h>
34 #include <OBOX.h>
35 #include <gettext.h>
36
37 const char file_magic[] = "7KRP";
38
39 const int32_t replay_version = 1;
40 // version 0 original format
41 // version 1
42 // + ver_cksum
43 // + frame_delay
44
45 struct GameVer {
46 uint32_t ver1;
47 uint32_t ver2;
48 uint32_t ver3;
49 uint32_t flags;
50
set_current_versionGameVer51 void set_current_version()
52 {
53 ver1 = SKVERMAJ;
54 ver2 = SKVERMED;
55 ver3 = SKVERMIN;
56 flags = config_adv.flags;
57 }
58
cmpGameVer59 int cmp(GameVer *a)
60 {
61 return ver1 == a->ver1 &&
62 ver2 == a->ver2 &&
63 ver3 == a->ver3 &&
64 flags == a->flags;
65 }
66 };
67
ReplayFile()68 ReplayFile::ReplayFile()
69 {
70 file_size = 0;
71 mode = ReplayFile::DISABLE;
72 }
73
~ReplayFile()74 ReplayFile::~ReplayFile()
75 {
76 }
77
at_eof()78 int ReplayFile::at_eof()
79 {
80 return mode != ReplayFile::READ || file.file_pos() >= file_size;
81 }
82
close()83 void ReplayFile::close()
84 {
85 if( mode == ReplayFile::DISABLE )
86 return;
87 file.file_close();
88 file_size = 0;
89 mode = ReplayFile::DISABLE;
90 }
91
open_read(const char * filePath,NewNationPara * mpGame,int * mpPlayerCount)92 int ReplayFile::open_read(const char* filePath, NewNationPara *mpGame, int *mpPlayerCount)
93 {
94 if( mode != ReplayFile::DISABLE )
95 return 0;
96
97 GameVer current_version;
98 uint32_t ver_cksum = 0;
99 char magic[4];
100 int32_t file_version;
101 GameVer version;
102 current_version.set_current_version();
103 int frame_delay;
104 int random_seed;
105
106 if( !file.file_open(filePath, 0) )
107 return 0;
108 if( !file.file_read(&magic, 4) )
109 goto out;
110 if( memcmp(magic, file_magic, 4) )
111 goto out;
112 file_version = file.file_get_long();
113 if( file_version > replay_version )
114 {
115 box.msg(_("The selected replay file uses an unsupported format."));
116 goto out;
117 }
118 if( !file.file_read(&version, sizeof(GameVer)) )
119 goto out;
120 if( file_version > 0 )
121 ver_cksum = file.file_get_long();
122 if( !current_version.cmp(&version) || ver_cksum != config_adv.checksum )
123 {
124 String msg;
125 sprintf(msg, _("Replay version %u.%u.%u.%u.%u mismatches with current game version %u.%u.%u.%u.%u"), version.ver1, version.ver2, version.ver3, version.flags, ver_cksum, current_version.ver1, current_version.ver2, current_version.ver3, current_version.flags, config_adv.checksum);
126 box.msg(msg);
127 }
128 if( file_version > 0 )
129 frame_delay = file.file_get_long();
130 else
131 frame_delay = 5; // FORCE_MAX_FRAME_DELAY
132 random_seed = file.file_get_long();
133 if( !config.read_file(&file, 1) ) // 1-keep system settings
134 goto out;
135 *mpPlayerCount = file.file_get_short();
136 for( int i = 0; i < *mpPlayerCount; ++i )
137 {
138 mpGame[i].nation_recno = file.file_get_short();
139 mpGame[i].dp_player_id = 0;
140 mpGame[i].color_scheme = file.file_get_short();
141 mpGame[i].race_id = file.file_get_short();
142 file.file_read(&mpGame[i].player_name, HUMAN_NAME_LEN+1);
143 }
144
145 remote.set_process_frame_delay(frame_delay);
146 info.init_random_seed(random_seed);
147
148 file_size = file.file_size();
149
150 mode = ReplayFile::READ;
151 return 1;
152 out:
153 file.file_close();
154 return 0;
155 }
156
open_write(const char * filePath,NewNationPara * mpGame,int mpPlayerCount)157 int ReplayFile::open_write(const char* filePath, NewNationPara *mpGame, int mpPlayerCount)
158 {
159 if( mode != ReplayFile::DISABLE )
160 return 0;
161
162 GameVer current_version;
163 current_version.set_current_version();
164
165 if( !file.file_create(filePath, 0) )
166 return 0;
167 file.file_write((void *)file_magic, 4);
168 file.file_put_long(replay_version);
169 file.file_write(¤t_version, sizeof(GameVer));
170 file.file_put_long(config_adv.checksum);
171 file.file_put_long(remote.get_process_frame_delay());
172 file.file_put_long(info.random_seed);
173 config.write_file(&file);
174 file.file_put_short(mpPlayerCount);
175 for( int i = 0; i < mpPlayerCount; ++i )
176 {
177 file.file_put_short(mpGame[i].nation_recno);
178 file.file_put_short(mpGame[i].color_scheme);
179 file.file_put_short(mpGame[i].race_id);
180 file.file_write(&mpGame[i].player_name, HUMAN_NAME_LEN+1);
181 }
182
183 mode = ReplayFile::WRITE;
184 return 1;
185 }
186
187 // returns number of bytes read into queue
read_queue(RemoteQueue * rq)188 int ReplayFile::read_queue(RemoteQueue *rq)
189 {
190 if( at_eof() )
191 return 0;
192 int size = file.file_get_unsigned_short();
193 if( size <= 0 )
194 return 0;
195 if( size > rq->queue_buf_size )
196 rq->reserve(size - rq->queue_buf_size);
197 file.file_read(rq->queue_buf, size);
198 rq->queue_ptr = rq->queue_buf + size;
199 rq->queued_size = size;
200 return size;
201 }
202
write_queue(RemoteQueue * rq)203 void ReplayFile::write_queue(RemoteQueue *rq)
204 {
205 if( mode != ReplayFile::WRITE )
206 return;
207 if( rq->queued_size <= 0 )
208 return;
209 file.file_put_unsigned_short(rq->queued_size);
210 file.file_write(rq->queue_buf, rq->queued_size);
211 }
212