1 /**
2  ** Items.cc - Names of items.
3  **
4  ** Written: 11/5/98 - JSF
5  **/
6 
7 /*
8 Copyright (C) 1998  Jeffrey S. Freedman
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 */
24 
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28 
29 // #include <iomanip>           /* Debugging */
30 #include <fstream>
31 #include <sstream>
32 #include <string>
33 #include <vector>
34 #include "items.h"
35 #include "utils.h"
36 #include "msgfile.h"
37 #include "fnames.h"
38 #include "exult_flx.h"
39 #include "data_utils.h"
40 
41 using std::istream;
42 using std::ostream;
43 using std::ifstream;
44 using std::ofstream;
45 using std::stringstream;
46 using std::cerr;
47 using std::endl;
48 using std::string;
49 using std::vector;
50 
51 vector<string> item_names;          // Names of U7 items.
52 vector<string> text_msgs;           // Msgs. (0x400 - in text.flx).
53 vector<string> misc_names;          // Frames, etc (0x500 - 0x5ff/0x685 (BG/SI) in text.flx).
54 
remap_index(bool remap,int index,bool sibeta)55 static inline int remap_index(bool remap, int index, bool sibeta) {
56 	if (!remap)
57 		return index;
58 	if (sibeta) {
59 		// Account for differences between SI Beta and SI Final when remapping
60 		// to SS indices.
61 		if (index >= 0x146)
62 			return index + 17;
63 		else if (index >= 0x135)
64 			return index + 16;
65 		else if (index >= 0x0f6)
66 			return index + 15;
67 		else if (index >= 0x0ea)
68 			return index + 14;
69 		else if (index >= 0x0e5)
70 			return index + 13;
71 		else if (index >= 0x0b1)
72 			return index + 11;
73 		else if (index >= 0x0ae)
74 			return index + 10;
75 		else if (index >= 0x0a2)
76 			return index + 9;
77 		else if (index >= 0x094)
78 			return index + 8;
79 		else if (index >= 0x08b)
80 			return index + 7;
81 		else if (index >= 0x07f)
82 			return index + 2;
83 		else
84 			return index;
85 	} else {
86 		if (index >= 0x0fa)
87 			return index + 11;
88 		else if (index >= 0x0b2)
89 			return index + 10;
90 		else if (index >= 0x0af)
91 			return index + 9;
92 		else if (index >= 0x094)
93 			return index + 8;
94 		else if (index >= 0x08b)
95 			return index + 7;
96 		else if (index >= 0x07f)
97 			return index + 2;
98 		else
99 			return index;
100 	}
101 }
102 
get_text_internal(vector<string> const & src,unsigned num)103 static inline char const *get_text_internal(vector<string> const &src, unsigned num) {
104 	return src[num].c_str();
105 }
106 
add_text_internal(vector<string> & src,unsigned num,char const * name)107 static inline void add_text_internal(vector<string> &src, unsigned num, char const *name) {
108 	if (num >= src.size()) {
109 		src.resize(num + 1);
110 	}
111 	src[num] = name;
112 }
113 
114 /*
115  *  Get how many item names there are.
116  */
117 
get_num_item_names()118 int get_num_item_names() {
119 	return item_names.size();
120 }
121 
122 /*
123  *  Get an item name.
124  */
get_item_name(unsigned num)125 char const *get_item_name(unsigned num) {
126 	return get_text_internal(item_names, num);
127 }
128 
129 /*
130  *  Create an item name.
131  */
Set_item_name(unsigned num,char const * name)132 void Set_item_name(unsigned num, char const *name) {
133 	add_text_internal(item_names, num, name);
134 }
135 
136 /*
137  *  Get how many text messages there are.
138  */
139 
get_num_text_msgs()140 int get_num_text_msgs() {
141 	return text_msgs.size();
142 }
143 
144 /*
145  *  Get a text message.
146  */
get_text_msg(unsigned num)147 char const *get_text_msg(unsigned num) {
148 	return get_text_internal(text_msgs, num);
149 }
150 
151 /*
152  *  Create a text message.
153  */
Set_text_msg(unsigned num,char const * msg)154 void Set_text_msg(unsigned num, char const *msg) {
155 	add_text_internal(text_msgs, num, msg);
156 }
157 
158 /*
159  *  Get how many misc names there are.
160  */
161 
get_num_misc_names()162 int get_num_misc_names() {
163 	return misc_names.size();
164 }
165 
166 /*
167  *  Get a misc name.
168  */
get_misc_name(unsigned num)169 char const *get_misc_name(unsigned num) {
170 	return get_text_internal(misc_names, num);
171 }
172 
173 /*
174  *  Create a misc name.
175  */
Set_misc_name(unsigned num,char const * name)176 void Set_misc_name(unsigned num, char const *name) {
177 	add_text_internal(misc_names, num, name);
178 }
179 
180 /*
181  *  Set up names of items.
182  *
183  *  Msg. names start at 0x400.
184  *  Frame names start at entry 0x500 (reagents,medallions,food,etc.).
185  */
186 
Setup_item_names(istream & items,istream & msgs,bool si,bool expansion,bool sibeta)187 static void Setup_item_names(
188     istream &items,
189     istream &msgs,
190     bool si,
191     bool expansion,
192     bool sibeta
193 ) {
194 	vector<string> msglist;
195 	int first_msg;          // First in exultmsg.txt.  Should
196 	//   follow those in text.flx.
197 	int total_msgs = 0;
198 	int num_item_names = 0;
199 	int num_text_msgs = 0;
200 	int num_misc_names = 0;
201 
202 	items.seekg(0x54);
203 	int flxcnt = Read4(items);
204 	first_msg = num_item_names = flxcnt;
205 	if (flxcnt > 0x400) {
206 		num_item_names = 0x400;
207 		num_text_msgs = flxcnt - 0x400;
208 		if (flxcnt > 0x500) {
209 			num_text_msgs = 0x100;
210 			num_misc_names = flxcnt - 0x500;
211 			int last_name; // Discard all starting from this.
212 			if (si)
213 				last_name = 0x686;
214 			else
215 				last_name = 0x600;
216 			if (flxcnt > last_name) {
217 				num_misc_names = last_name - 0x500;
218 				flxcnt = last_name;
219 			}
220 		}
221 		total_msgs = num_text_msgs;
222 	}
223 	if (msgs.good()) {
224 		// Exult msgs. too?
225 		first_msg = Read_text_msg_file(msgs, msglist);
226 		if (first_msg >= 0) {
227 			first_msg -= 0x400;
228 			if (first_msg < num_text_msgs) {
229 				cerr << "Exult msg. # " << first_msg <<
230 				     " conflicts with 'text.flx'" << endl;
231 				first_msg = num_text_msgs;
232 			}
233 			total_msgs = static_cast<int>(msglist.size() - 0x400);
234 		} else
235 			first_msg = num_text_msgs;
236 	}
237 	item_names.resize(num_item_names);
238 	text_msgs.resize(total_msgs);
239 	misc_names.resize(num_misc_names);
240 	// Hack alert: move SI misc_names around to match those of SS.
241 	bool doremap = si && (!expansion || sibeta);
242 	if (doremap)
243 		flxcnt -= 17;   // Just to be safe.
244 	int i;
245 	for (i = 0; i < flxcnt; i++) {
246 		items.seekg(0x80 + i * 8);
247 		int itemoffs = Read4(items);
248 		if (!itemoffs)
249 			continue;
250 		int itemlen = Read4(items);
251 		items.seekg(itemoffs);
252 		char *newitem = new char[itemlen];
253 		items.read(newitem, itemlen);
254 		if (i < num_item_names)
255 			item_names[i] = newitem;
256 		else if (i - num_item_names < num_text_msgs) {
257 			if (sibeta && (i - num_item_names) >= 0xd2)
258 				text_msgs[i - num_item_names + 1] = newitem;
259 			else
260 				text_msgs[i - num_item_names] = newitem;
261 		} else
262 			misc_names[remap_index(doremap,
263 			                       i - num_item_names - num_text_msgs,
264 			                       sibeta)] = newitem;
265 		delete [] newitem;
266 	}
267 	for (i = first_msg; i < total_msgs; i++)
268 		text_msgs[i] = msglist[i + 0x400];
269 }
270 
271 #define SHAPES_SECT  "shapes"
272 #define MSGS_SECT    "msgs"
273 #define MISC_SECT    "miscnames"
274 
275 /*
276  *  This sets up item names and messages from Exult's new file,
277  *  "textmsgs.txt".
278  */
279 
Setup_text(istream & txtfile,istream & exultmsg)280 static void Setup_text(
281     istream &txtfile,       // All text.
282     istream &exultmsg
283 ) {
284 	// Start by reading from exultmsg
285 	vector<string> msglist;
286 	int first_msg;
287 	first_msg = Read_text_msg_file(exultmsg, msglist);
288 	unsigned total_msgs = static_cast<int>(msglist.size() - 0x400);
289 	if (first_msg >= 0) {
290 		first_msg -= 0x400;
291 	}
292 	text_msgs.resize(total_msgs);
293 	for (unsigned i = first_msg; i < total_msgs; i++)
294 		text_msgs[i] = msglist[i + 0x400];
295 	// Now read in textmsg.txt
296 	Read_text_msg_file(txtfile, item_names, SHAPES_SECT);
297 	Read_text_msg_file(txtfile, text_msgs, MSGS_SECT);
298 	Read_text_msg_file(txtfile, misc_names, MISC_SECT);
299 }
300 
301 /*
302  *  Setup item names and text messages.
303  */
304 
Setup_text(bool si,bool expansion,bool sibeta)305 void Setup_text(bool si, bool expansion, bool sibeta) {
306 	Free_text();
307 	bool is_patch = is_system_path_defined("<PATCH>");
308 	// Always read from exultmsg.txt
309 	// TODO: allow multilingual exultmsg.txt files.
310 	istream *exultmsg;
311 	if (is_patch && U7exists(PATCH_EXULTMSG)) {
312 		auto *exultmsgfile = new ifstream();
313 		exultmsg = exultmsgfile;
314 		U7open(*exultmsgfile, PATCH_EXULTMSG, true);
315 	} else {
316 		auto *exultmsgbuf = new stringstream();
317 		exultmsg = exultmsgbuf;
318 		const char *msgs = BUNDLE_CHECK(BUNDLE_EXULT_FLX, EXULT_FLX);
319 		U7object txtobj(msgs, EXULT_FLX_EXULTMSG_TXT);
320 		size_t len;
321 		auto txt = txtobj.retrieve(len);
322 		if (txt && len > 0) {
323 			exultmsgbuf->str(string(reinterpret_cast<char*>(txt.get()), len));
324 		}
325 	}
326 
327 	// Exult new-style messages?
328 	if (is_patch && U7exists(PATCH_TEXTMSGS)) {
329 		ifstream txtfile;
330 		U7open(txtfile, PATCH_TEXTMSGS, true);
331 		Setup_text(txtfile, *exultmsg);
332 	} else if (U7exists(TEXTMSGS)) {
333 		ifstream txtfile;
334 		U7open(txtfile, TEXTMSGS, true);
335 		Setup_text(txtfile, *exultmsg);
336 	} else {
337 		ifstream textflx;
338 		if (is_patch && U7exists(PATCH_TEXT))
339 			U7open(textflx, PATCH_TEXT);
340 		else
341 			U7open(textflx, TEXT_FLX);
342 		Setup_item_names(textflx, *exultmsg, si, expansion, sibeta);
343 	}
344 	delete exultmsg;
345 }
346 
347 /*
348  *  Free memory.
349  */
350 
Free_text_list(vector<string> & items)351 static void Free_text_list(
352     vector<string> &items
353 ) {
354 	items.clear();
355 }
356 
Free_text()357 void Free_text(
358 ) {
359 	Free_text_list(item_names);
360 	Free_text_list(text_msgs);
361 	Free_text_list(misc_names);
362 }
363 
364 /*
365  *  Write out new-style Exult text file.
366  */
367 
Write_text_file()368 void Write_text_file(
369 ) {
370 	ofstream out;
371 
372 	U7open(out, PATCH_TEXTMSGS, true);  // (It's a text file.)
373 	out << "Exult " << VERSION << " text message file." <<
374 	    "  Written by ExultStudio." << endl;
375 	Write_msg_file_section(out, SHAPES_SECT, item_names);
376 	Write_msg_file_section(out, MSGS_SECT, text_msgs);
377 	Write_msg_file_section(out, MISC_SECT, misc_names);
378 	out.close();
379 }
380