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(&current_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