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