1 /*
2  *  readnpcs.cc - Read in NPC's from npc.dat & schedule.dat.  Also writes npc.dat back out.
3  *
4  *  Copyright (C) 1999  Jeffrey S. Freedman
5  *  Copyright (C) 2000-2013  The Exult Team
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #  include <config.h>
24 #endif
25 
26 #include <cstring>
27 
28 #include "gamewin.h"
29 #include "game.h"
30 #include "monsters.h"
31 #include "ucmachine.h"
32 #include "utils.h"
33 #include "fnames.h"
34 #include "schedule.h"
35 #include "databuf.h"
36 #include "miscinf.h"
37 //#include "items.h"            /* Debugging only */
38 
39 using std::cerr;
40 using std::cout;
41 using std::endl;
42 using std::ifstream;
43 using std::ios;
44 using std::ofstream;
45 using std::vector;
46 
47 /*
48  *  Read in the NPC's, plus the monster info.
49  */
50 
read_npcs()51 void Game_window::read_npcs(
52 ) {
53 	npcs.resize(1);         // Create main actor.
54 	Main_actor_shared ava = std::make_shared<Main_actor>("", 0);
55 	npcs[0] = ava;
56 	camera_actor = main_actor = ava.get();
57 	bool fix_unused = false;    // Get set for old savegames.
58 	{
59 		int num_npcs;
60 		IFileDataSource nfile(NPC_DAT);
61 		if (nfile.good()) {
62 			num_npcs1 = nfile.read2();  // Get counts.
63 			num_npcs = num_npcs1 + nfile.read2();
64 			main_actor->read(&nfile, 0, false, fix_unused);
65 		} else {
66 			if (!Game::is_editing())
67 				throw file_read_exception(NPC_DAT);
68 			num_npcs1 = num_npcs = 1;
69 			if (Game::get_avname())
70 				main_actor->set_npc_name(Game::get_avname());
71 			main_actor->set_shape(Shapeinfo_lookup::GetMaleAvShape());
72 			main_actor->set_invalid();  // Put in middle of world.
73 			main_actor->move(c_num_tiles / 2, c_num_tiles / 2, 0);
74 		}
75 		npcs.resize(num_npcs);
76 		bodies.resize(num_npcs);
77 
78 		// Don't like it... no i don't.
79 		center_view(main_actor->get_tile());
80 		for (int i = 1; i < num_npcs; i++) { // Create the rest.
81 			npcs[i] = std::make_shared<Npc_actor>("", 0);
82 			auto& npc = npcs[i];
83 			npc->read(&nfile, i, i < num_npcs1, fix_unused);
84 			if (npc->is_unused()) {
85 				// Not part of the game.
86 				Game_object_shared keep;
87 				npc->remove_this(&keep);
88 				npc->set_schedule_type(Schedule::wait);
89 			} else
90 				npc->restore_schedule();
91 			cycle_load_palette();
92 		}
93 	}
94 	main_actor->set_actor_shape();
95 	{
96 		IFileDataSource nfile(MONSNPCS); // Monsters.
97 		if (nfile.good()) {
98 			// (Won't exist the first time; in this case U7open throws
99 			int cnt = nfile.read2();
100 			nfile.skip(1);// Read 1 ahead to test.
101 			bool okay = nfile.good();
102 			nfile.skip(-1);
103 			while (okay && cnt--) {
104 				// Read ahead to get shape.
105 				nfile.skip(2);
106 				unsigned short shnum = nfile.read2() & 0x3ff;
107 				okay = nfile.good();
108 				nfile.skip(-4);
109 				ShapeID sid(shnum, 0);
110 				if (!okay || sid.get_num_frames() < 16)
111 					break;  // Watch for corrupted file.
112 				Game_object_shared new_monster = Monster_actor::create(shnum);
113 				auto *act = static_cast<Monster_actor*>(new_monster.get());
114 				act->read(&nfile, -1, false, fix_unused);
115 				act->set_schedule_loc(act->get_tile());
116 				act->restore_schedule();
117 				cycle_load_palette();
118 			}
119 		} else {
120 #ifdef DEBUG
121 			cerr << "Error reading saved monsters.  Clearing list." << endl;
122 #endif
123 			Monster_actor::give_up();
124 		}
125 	}
126 	if (moving_barge) {     // Gather all NPC's on barge.
127 		Barge_object *b = moving_barge;
128 		moving_barge = nullptr;
129 		set_moving_barge(b);
130 	}
131 	read_schedules();       // Now get their schedules.
132 	center_view(main_actor->get_tile());
133 }
134 
135 /*
136  *  Write NPC (and monster) data back out.
137  *
138  *  Output: false if error, already reported.
139  */
140 
write_npcs()141 void Game_window::write_npcs(
142 ) {
143 	int num_npcs = npcs.size();
144 	{
145 		OFileDataSource nfile(NPC_DAT);
146 
147 		nfile.write2(num_npcs1);    // Start with counts.
148 		nfile.write2(num_npcs - num_npcs1);
149 		int i;
150 		std::cout << "NPC write " << std::endl;
151 		for (i = 0; i < num_npcs; i++)
152 			get_npc(i)->write(&nfile);
153 		nfile.flush();
154 		if (!nfile.good())
155 			throw file_write_exception(NPC_DAT);
156 	}
157 	write_schedules();      // Write schedules
158 	{
159 		// Now write out monsters in world.
160 		OFileDataSource nfile(MONSNPCS);
161 		int cnt = 0;
162 		nfile.write2(0);        // Write 0 as a place holder.
163 		for (Monster_actor *mact = Monster_actor::get_first_in_world();
164 				mact; mact = mact->get_next_in_world())
165 			if (!mact->is_dead()) { // Alive?
166 				mact->write(&nfile);
167 				cnt++;
168 			}
169 		nfile.seek(0);          // Back to start.
170 		nfile.write2(cnt);      // Write actual count.
171 		nfile.flush();
172 		if (!nfile.good())
173 			throw file_write_exception(MONSNPCS);
174 	}
175 }
176 
177 /*
178  *  Read in offsets.  When done, file is set to start of script names (if
179  *  there are any).
180  */
181 
Set_to_read_schedules(IStreamDataSource & sfile,int & num_npcs,int & entsize,int & num_script_names)182 std::unique_ptr<short[]> Set_to_read_schedules(
183     IStreamDataSource &sfile,
184     int &num_npcs,          // # npc's returnes.
185     int &entsize,           // Entry size returned.
186     int &num_script_names      // # of usecode script names ret'd.
187 ) {
188 	entsize = 4;            // 4 is U7's size.
189 	num_script_names = 0;
190 	num_npcs = sfile.read4();   // # of NPC's, not include Avatar.
191 	if (num_npcs == -1) {       // Exult format?
192 		entsize = 8;
193 		num_npcs = sfile.read4();
194 	} else if (num_npcs == -2) {
195 		entsize = 8;
196 		num_npcs = sfile.read4();
197 		num_script_names = sfile.read2();
198 	}
199 	auto offsets = std::make_unique<short[]>(num_npcs);
200 	int i;              // Read offsets with list of scheds.
201 	for (i = 0; i < num_npcs; i++)
202 		offsets[i] = sfile.read2();
203 	return offsets;
204 }
205 
206 /*
207  *  Read one NPC's schedule.
208  */
209 
Read_a_schedule(IStreamDataSource & sfile,int index,Actor * npc,int entsize,const short * offsets)210 void Read_a_schedule(
211     IStreamDataSource &sfile,
212     int index,
213     Actor *npc,
214     int entsize,
215     const short *offsets
216 ) {
217 	int cnt = offsets[index] - offsets[index - 1];
218 	// Read schedules into this array.
219 	Schedule_change *schedules = cnt ? new Schedule_change[cnt] : nullptr;
220 	unsigned char ent[10];
221 	if (entsize == 4) { // U7 format?
222 		for (int j = 0; j < cnt; j++) {
223 			sfile.read(reinterpret_cast<char *>(ent), 4);
224 			schedules[j].set4(ent);
225 		}
226 	} else {        // Exult formats.
227 		for (int j = 0; j < cnt; j++) {
228 			sfile.read(reinterpret_cast<char *>(ent), 8);
229 			schedules[j].set8(ent);
230 		}
231 	}
232 	if (npc)            // Store in NPC.
233 		npc->set_schedules(schedules, cnt);
234 	else
235 		delete [] schedules;
236 }
237 
238 /*
239  *  Read NPC schedules.
240  */
241 
read_schedules()242 void Game_window::read_schedules(
243 ) {
244 	std::unique_ptr<IFileDataSource> sfile = std::make_unique<IFileDataSource>(GSCHEDULE);
245 	if (!sfile->good()) {
246 #ifdef DEBUG
247 		cerr << "Couldn't open " << GSCHEDULE << ". Falling back to "
248 		     << SCHEDULE_DAT << "." << endl;
249 #endif
250 		sfile = std::make_unique<IFileDataSource>(SCHEDULE_DAT);
251 		if (!sfile->good()) {
252 			if (!Game::is_editing())
253 				throw file_open_exception(get_system_path(SCHEDULE_DAT));
254 			else
255 				return;
256 		}
257 	}
258 	int num_npcs = 0;
259 	int entsize;
260 	int num_script_names;
261 	auto offsets = Set_to_read_schedules(*sfile, num_npcs, entsize, num_script_names);
262 	Schedule_change::clear();
263 	vector<std::string> &script_names = Schedule_change::get_script_names();
264 	if (num_script_names) {
265 		sfile->read2();   // Skip past total size.
266 		script_names.reserve(num_script_names);
267 		for (int i = 0; i < num_script_names; ++i) {
268 			int sz = sfile->read2();
269 			std::string nm;
270 			sfile->read(nm, sz);
271 			script_names.push_back(std::move(nm));
272 		}
273 	}
274 
275 	for (int i = 0; i < num_npcs - 1; i++) { // Do each NPC, except Avatar.
276 		// Avatar isn't included here.
277 		Actor *npc = get_npc(i + 1);
278 		Read_a_schedule(*sfile, i + 1, npc, entsize, offsets.get());
279 		cycle_load_palette();
280 	}
281 	cout.flush();
282 }
283 
284 /*
285  *  Write NPC schedules.
286  */
287 
write_schedules()288 void Game_window::write_schedules() {
289 	Schedule_change *schedules;
290 	int cnt;
291 	short offset = 0;
292 	int i;
293 	int num;
294 
295 	// So do I allow for all NPCs (type1 and type2) - Yes i will
296 	num = npcs.size();
297 
298 	OFileDataSource sfile(GSCHEDULE);
299 	vector<std::string> &script_names = Schedule_change::get_script_names();
300 
301 	sfile.write4(static_cast<unsigned int>(-2));        // Exult version #.
302 	sfile.write4(num);      // # of NPC's, not include Avatar.
303 	sfile.write2(script_names.size());
304 	sfile.write2(0);        // First offset
305 
306 	for (i = 1; i < num; i++) { // write offsets with list of scheds.
307 		get_npc(i)->get_schedules(schedules, cnt);
308 		offset += cnt;
309 		sfile.write2(offset);
310 	}
311 	if (!script_names.empty()) {
312 		int total = 0;      // Figure total size.
313 		for (auto& elem : script_names)
314 			total += 2 + elem.size();
315 		sfile.write2(total);
316 		for (auto& elem : script_names) {
317 			sfile.write2(elem.size());
318 			sfile.write(elem);
319 		}
320 	}
321 	for (i = 1; i < num; i++) { // Do each NPC, except Avatar.
322 		get_npc(i)->get_schedules(schedules, cnt);
323 		for (int j = 0; j < cnt; j++) {
324 			unsigned char ent[20];
325 			schedules[j].write8(ent);
326 			sfile.write(reinterpret_cast<char *>(ent), 8);
327 		}
328 	}
329 }
330 
revert_schedules(Actor * npc)331 void Game_window::revert_schedules(Actor *npc) {
332 	// Can't do this if <= 0
333 	if (npc->get_npc_num() <= 0) return;
334 
335 	IFileDataSource sfile(SCHEDULE_DAT);
336 	if (!sfile.good()) {
337 		throw file_read_exception(SCHEDULE_DAT);
338 	}
339 
340 	int num_npcs;
341 	int entsize;
342 	int num_script_names;
343 	auto offsets = Set_to_read_schedules(sfile, num_npcs, entsize, num_script_names);
344 	if (num_script_names) {
345 		int sz = sfile.read2();
346 		sfile.skip(sz);
347 	}
348 	// Seek to the right place
349 	sfile.skip(offsets[npc->get_npc_num() - 1]*entsize);
350 
351 	Read_a_schedule(sfile, npc->get_npc_num(), npc, entsize, offsets.get());
352 }
353 
354