1 /*
2 
3 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 	and the "Aleph One" developers.
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 3 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 	This license is contained in the file "COPYING",
17 	which is included with this source code; it is available online at
18 	http://www.gnu.org/licenses/gpl.html
19 
20 */
21 
22 /*
23  *  resource_manager.cpp - MacOS resource handling for non-Mac platforms
24  *
25  *  Written in 2000 by Christian Bauer
26  *
27  *  Jan 16, 2003 (Woody Zenfell):
28  *      Reworked stemmed-file opening logic; now using new Logging facility
29  */
30 
31 #include <SDL_endian.h>
32 
33 #include "cseries.h"
34 #include "resource_manager.h"
35 #include "FileHandler.h"
36 #include "Logging.h"
37 
38 #include <stdio.h>
39 #include <vector>
40 #include <list>
41 #include <map>
42 
43 #ifndef NO_STD_NAMESPACE
44 using std::iostream;
45 using std::vector;
46 using std::list;
47 using std::map;
48 #endif
49 
50 /*
51  *  Utility functions
52  */
53 
is_applesingle(SDL_RWops * f,bool rsrc_fork,int32 & offset,int32 & length)54 bool is_applesingle(SDL_RWops *f, bool rsrc_fork, int32 &offset, int32 &length)
55 {
56 	// Check header
57 	SDL_RWseek(f, 0, SEEK_SET);
58 	uint32 id = SDL_ReadBE32(f);
59 	uint32 version = SDL_ReadBE32(f);
60 	if (id != 0x00051600 || version != 0x00020000)
61 		return false;
62 
63 	// Find fork
64 	uint32 req_id = rsrc_fork ? 2 : 1;
65 	SDL_RWseek(f, 0x18, SEEK_SET);
66 	int num_entries = SDL_ReadBE16(f);
67 	while (num_entries--) {
68 		uint32 id = SDL_ReadBE32(f);
69 		int32 ofs = SDL_ReadBE32(f);
70 		int32 len = SDL_ReadBE32(f);
71 		//printf(" entry id %d, offset %d, length %d\n", id, ofs, len);
72 		if (id == req_id) {
73 			offset = ofs;
74 			length = len;
75 			return true;
76 		}
77 	}
78 	return false;
79 }
80 
is_macbinary(SDL_RWops * f,int32 & data_length,int32 & rsrc_length)81 bool is_macbinary(SDL_RWops *f, int32 &data_length, int32 &rsrc_length)
82 {
83 	// This recognizes up to macbinary III (0x81)
84 	SDL_RWseek(f, 0, SEEK_SET);
85 	uint8 header[128];
86 	SDL_RWread(f, header, 1, 128);
87 	if (header[0] || header[1] > 63 || header[74]  || header[123] > 0x81)
88 		return false;
89 
90 	// Check CRC
91 	uint16 crc = 0;
92 	for (int i=0; i<124; i++) {
93 		uint16 data = header[i] << 8;
94 		for (int j=0; j<8; j++) {
95 			if ((data ^ crc) & 0x8000)
96 				crc = (crc << 1) ^ 0x1021;
97 			else
98 				crc <<= 1;
99 			data <<= 1;
100 		}
101 	}
102 	//printf("crc %02x\n", crc);
103 	if (crc != ((header[124] << 8) | header[125]))
104 		return false;
105 
106 	// CRC valid, extract fork sizes
107 	data_length = (header[83] << 24) | (header[84] << 16) | (header[85] << 8) | header[86];
108 	rsrc_length = (header[87] << 24) | (header[88] << 16) | (header[89] << 8) | header[90];
109 	return true;
110 }
111 
112 
113 // Structure for open resource file
114 struct res_file_t {
res_file_tres_file_t115 	res_file_t() : f(NULL) {}
res_file_tres_file_t116 	res_file_t(SDL_RWops *file) : f(file) {}
res_file_tres_file_t117 	res_file_t(const res_file_t &other) {f = other.f;}
~res_file_tres_file_t118 	~res_file_t() {}
119 
operator =res_file_t120 	const res_file_t &operator=(const res_file_t &other)
121 	{
122 		if (this != &other)
123 			f = other.f;
124 		return *this;
125 	}
126 
127 	bool read_map(void);
128 	size_t count_resources(uint32 type) const;
129 	void get_resource_id_list(uint32 type, vector<int> &ids) const;
130 	bool get_resource(uint32 type, int id, LoadedResource &rsrc) const;
131 	bool get_ind_resource(uint32 type, int index, LoadedResource &rsrc) const;
132 	bool has_resource(uint32 type, int id) const;
133 
134 	SDL_RWops *f;		// Opened resource file
135 
136 	typedef map<int, uint32> id_map_t;			// Maps resource ID to offset to resource data
137 	typedef map<uint32, id_map_t> type_map_t;	// Maps resource type to ID map
138 
139 	type_map_t types;	// Map of all resource types found in file
140 };
141 
142 
143 // List of open resource files
144 static list<res_file_t *> res_file_list;
145 static list<res_file_t *>::iterator cur_res_file_t;
146 
147 
148 /*
149  *  Find file in list of opened files
150  */
151 
find_res_file_t(SDL_RWops * f)152 static list<res_file_t *>::iterator find_res_file_t(SDL_RWops *f)
153 {
154 	list<res_file_t *>::iterator i, end = res_file_list.end();
155 	for (i=res_file_list.begin(); i!=end; i++) {
156 		res_file_t *r = *i;
157 		if (r->f == f)
158 			return i;
159 	}
160 	return res_file_list.end();
161 }
162 
163 
164 /*
165  *  Initialize resource management
166  */
167 
initialize_resources(void)168 void initialize_resources(void)
169 {
170 	// nothing to do
171 }
172 
173 
174 /*
175  *  Read and parse resource map from file
176  */
177 
read_map(void)178 bool res_file_t::read_map(void)
179 {
180 	SDL_RWseek(f, 0, SEEK_END);
181 	uint32 file_size = SDL_RWtell(f);
182 	SDL_RWseek(f, 0, SEEK_SET);
183 	uint32 fork_start = 0;
184 
185         if(file_size < 16) {
186             if(file_size == 0)
187                 logNote("file has zero length");
188             else
189                 logAnomaly("file too small (%d bytes) to be valid", file_size);
190             return false;
191         }
192 
193 	// Determine file type (AppleSingle and MacBinary II files are handled transparently)
194 	int32 offset, data_length, rsrc_length;
195 	if (is_applesingle(f, true, offset, rsrc_length)) {
196                 logTrace("file is_applesingle");
197 		fork_start = offset;
198 		file_size = offset + rsrc_length;
199 	} else if (is_macbinary(f, data_length, rsrc_length)) {
200                 logTrace("file is_macbinary");
201 		fork_start = 128 + ((data_length + 0x7f) & ~0x7f);
202 		file_size = fork_start + rsrc_length;
203 	}
204         else
205                 logTrace("file is raw resource fork format");
206 
207 	// Read resource header
208 	SDL_RWseek(f, fork_start, SEEK_SET);
209 	uint32 data_offset = SDL_ReadBE32(f) + fork_start;
210 	uint32 map_offset = SDL_ReadBE32(f) + fork_start;
211 	uint32 data_size = SDL_ReadBE32(f);
212 	uint32 map_size = SDL_ReadBE32(f);
213         logDump("resource header: data offset %d, map_offset %d, data_size %d, map_size %d", data_offset, map_offset, data_size, map_size);
214 
215 	// Verify integrity of resource header
216 	if (data_offset >= file_size || map_offset >= file_size ||
217 	    data_offset + data_size > file_size || map_offset + map_size > file_size) {
218 		logTrace("file's resource header corrupt");
219 		return false;
220 	}
221 
222 	// Read map header
223 	SDL_RWseek(f, map_offset + 24, SEEK_SET);
224 	uint32 type_list_offset = map_offset + SDL_ReadBE16(f);
225 	//uint32 name_list_offset = map_offset + SDL_ReadBE16(f);
226 	//printf(" type_list_offset %d, name_list_offset %d\n", type_list_offset, name_list_offset);
227 
228 	// Verify integrity of map header
229 	if (type_list_offset >= file_size) {
230 		logTrace("file's resource map header corrupt");
231 		return false;
232 	}
233 
234 	// Read resource type list
235 	SDL_RWseek(f, type_list_offset, SEEK_SET);
236 	int num_types = SDL_ReadBE16(f) + 1;
237 	for (int i=0; i<num_types; i++) {
238 
239 		// Read type list item
240 		uint32 type = SDL_ReadBE32(f);
241 		int num_refs = SDL_ReadBE16(f) + 1;
242 		uint32 ref_list_offset = type_list_offset + SDL_ReadBE16(f);
243 		//printf("  type %c%c%c%c, %d refs\n", type >> 24, type >> 16, type >> 8, type, num_refs);
244 
245 		// Verify integrity of item
246 		if (ref_list_offset >= file_size) {
247 			logTrace("file's resource type list corrupt");
248 			return false;
249 		}
250 
251 		// Create ID map for this type
252 		id_map_t &id_map = types[type];
253 
254 		// Read reference list
255 		uint32 cur = SDL_RWtell(f);
256 		SDL_RWseek(f, ref_list_offset, SEEK_SET);
257 		for (int j=0; j<num_refs; j++) {
258 
259 			// Read list item
260 			int id = SDL_ReadBE16(f);
261 			SDL_RWseek(f, 2, SEEK_CUR);
262 			uint32 rsrc_data_offset = data_offset + (SDL_ReadBE32(f) & 0x00ffffff);
263 			//printf("   id %d, rsrc_data_offset %d\n", id, rsrc_data_offset);
264 
265 			// Verify integrify of item
266 			if (rsrc_data_offset >= file_size) {
267 				logTrace("file's resource reference list corrupt");
268 				return false;
269 			}
270 
271 			// Add ID to map
272 			id_map[id] = rsrc_data_offset;
273 
274 			SDL_RWseek(f, 4, SEEK_CUR);
275 		}
276 		SDL_RWseek(f, cur, SEEK_SET);
277 	}
278 	return true;
279 }
280 
281 /*
282  *  Open resource file, set current file to the newly opened one
283  */
284 
285 SDL_RWops*
open_res_file_from_rwops(SDL_RWops * f)286 open_res_file_from_rwops(SDL_RWops* f) {
287     if (f) {
288 
289             // Successful, create res_file_t object and read resource map
290             res_file_t *r = new res_file_t(f);
291             if (r->read_map()) {
292 
293                     // Successful, add file to list of open files
294                     res_file_list.push_back(r);
295                     cur_res_file_t = --res_file_list.end();
296 
297                     // ZZZ: this exists mostly to help the user understand (via logContexts) which of
298                     // potentially several copies of a resource fork is actually being used.
299                     logNote("success, using this resource data (file is %p)", f);
300 
301             } else {
302 
303                     // Error reading resource map
304                     delete r;
305                     SDL_RWclose(f);
306                     return NULL;
307             }
308     }
309     else
310             logNote("file could not be opened");
311     return f;
312 }
313 
314 static SDL_RWops*
open_res_file_from_path(const char * inPath)315 open_res_file_from_path(const char* inPath)
316 {
317 	return open_res_file_from_rwops(SDL_RWFromFile(inPath, "rb"));
318 }
319 
open_res_file(FileSpecifier & file)320 SDL_RWops *open_res_file(FileSpecifier &file)
321 {
322     logContext("opening resource file %s", file.GetPath());
323 /*
324     string theContextString("trying to open resource file ");
325     theContextString += file.GetPath();
326     logContext(theContextString.c_str());
327 */
328 
329     string rsrc_file_name = file.GetPath();
330     string resources_file_name = rsrc_file_name;
331     string darwin_rsrc_file_name = rsrc_file_name;
332     rsrc_file_name += ".rsrc";
333     resources_file_name += ".resources";
334     darwin_rsrc_file_name += "/..namedfork/rsrc";
335 
336     SDL_RWops* f = NULL;
337 
338     // Open file, try <name>.rsrc first, then <name>.resources, then <name>/rsrc then <name>
339     if (f == NULL)
340             f = open_res_file_from_path(rsrc_file_name.c_str());
341     if (f == NULL)
342             f = open_res_file_from_path(resources_file_name.c_str());
343     if (f == NULL)
344 	   f = open_res_file_from_path(file.GetPath());
345     if (f == NULL)
346 	   f = open_res_file_from_path(darwin_rsrc_file_name.c_str());
347 
348     return f;
349 }
350 
351 
352 
353 /*
354  *  Close resource file
355  */
356 
close_res_file(SDL_RWops * file)357 void close_res_file(SDL_RWops *file)
358 {
359 	if (file == NULL)
360 		return;
361 
362 	// Find file in list
363 	list<res_file_t *>::iterator i = find_res_file_t(file);
364 	if (i != res_file_list.end()) {
365 
366 		// Remove it from the list, close the file and delete the res_file_t
367 		res_file_t *r = *i;
368 		SDL_RWclose(r->f);
369 		res_file_list.erase(i);
370 		delete r;
371 
372 		cur_res_file_t = res_file_list.empty() ? decltype(cur_res_file_t){} : --res_file_list.end();
373 	}
374 }
375 
376 
377 /*
378  *  Return current resource file
379  */
380 
cur_res_file(void)381 SDL_RWops *cur_res_file(void)
382 {
383 	res_file_t *r = *cur_res_file_t;
384 	assert(r);
385 	return r->f;
386 }
387 
388 
389 /*
390  *  Set current resource file
391  */
392 
use_res_file(SDL_RWops * file)393 void use_res_file(SDL_RWops *file)
394 {
395 	list<res_file_t *>::iterator i = find_res_file_t(file);
396 	assert(i != res_file_list.end());
397 	cur_res_file_t = i;
398 }
399 
400 
401 /*
402  *  Count number of resources of given type
403  */
404 
count_resources(uint32 type) const405 size_t res_file_t::count_resources(uint32 type) const
406 {
407 	type_map_t::const_iterator i = types.find(type);
408 	if (i == types.end())
409 		return 0;
410 	else
411 		return i->second.size();
412 }
413 
count_1_resources(uint32 type)414 size_t count_1_resources(uint32 type)
415 {
416 	return (*cur_res_file_t)->count_resources(type);
417 }
418 
count_resources(uint32 type)419 size_t count_resources(uint32 type)
420 {
421 	if (!res_file_list.size())
422 		return 0;
423 	size_t count = 0;
424 	list<res_file_t *>::const_iterator i = cur_res_file_t, begin = res_file_list.begin();
425 	while (true) {
426 		count += (*i)->count_resources(type);
427 		if (i == begin)
428 			break;
429 		i--;
430 	}
431 	return count;
432 }
433 
434 
435 /*
436  *  Get list of id of resources of given type
437  */
438 
get_resource_id_list(uint32 type,vector<int> & ids) const439 void res_file_t::get_resource_id_list(uint32 type, vector<int> &ids) const
440 {
441 	type_map_t::const_iterator i = types.find(type);
442 	if (i != types.end()) {
443 		id_map_t::const_iterator j, end = i->second.end();
444 		for (j=i->second.begin(); j!=end; j++)
445 			ids.push_back(j->first);
446 	}
447 }
448 
get_1_resource_id_list(uint32 type,vector<int> & ids)449 void get_1_resource_id_list(uint32 type, vector<int> &ids)
450 {
451 	ids.clear();
452 	(*cur_res_file_t)->get_resource_id_list(type, ids);
453 }
454 
get_resource_id_list(uint32 type,vector<int> & ids)455 void get_resource_id_list(uint32 type, vector<int> &ids)
456 {
457 	ids.clear();
458 	if (!res_file_list.size())
459 		return;
460 	list<res_file_t *>::const_iterator i = cur_res_file_t, begin = res_file_list.begin();
461 	while (true) {
462 		(*i)->get_resource_id_list(type, ids);
463 		if (i == begin)
464 			break;
465 		i--;
466 	}
467 }
468 
469 
470 /*
471  *  Get resource data (must be freed with free())
472  */
473 
get_resource(uint32 type,int id,LoadedResource & rsrc) const474 bool res_file_t::get_resource(uint32 type, int id, LoadedResource &rsrc) const
475 {
476 	rsrc.Unload();
477 
478 	// Find resource in map
479 	type_map_t::const_iterator i = types.find(type);
480 	if (i != types.end()) {
481 		id_map_t::const_iterator j = i->second.find(id);
482 		if (j != i->second.end()) {
483 
484 			// Found, read data size
485 			SDL_RWseek(f, j->second, SEEK_SET);
486 			uint32 size = SDL_ReadBE32(f);
487 
488 			// Allocate memory and read data
489 			void *p = malloc(size);
490 			if (p == NULL)
491 				return false;
492 			SDL_RWread(f, p, 1, size);
493 			rsrc.p = p;
494 			rsrc.size = size;
495 
496 //			fprintf(stderr, "get_resource type %c%c%c%c, id %d -> data %p, size %d\n", type >> 24, type >> 16, type >> 8, type, id, p, size);
497 			return true;
498 		}
499 	}
500 	return false;
501 }
502 
get_1_resource(uint32 type,int id,LoadedResource & rsrc)503 bool get_1_resource(uint32 type, int id, LoadedResource &rsrc)
504 {
505 	return (*cur_res_file_t)->get_resource(type, id, rsrc);
506 }
507 
get_resource(uint32 type,int id,LoadedResource & rsrc)508 bool get_resource(uint32 type, int id, LoadedResource &rsrc)
509 {
510 	if (!res_file_list.size())
511 		return false;
512 	list<res_file_t *>::const_iterator i = cur_res_file_t, begin = res_file_list.begin();
513 	while (true) {
514 		bool found = (*i)->get_resource(type, id, rsrc);
515 		if (found)
516 			return true;
517 		if (i == begin)
518 			break;
519 		i--;
520 	}
521 	return false;
522 }
523 
524 
525 /*
526  *  Get resource data by index (must be freed with free())
527  */
528 
get_ind_resource(uint32 type,int index,LoadedResource & rsrc) const529 bool res_file_t::get_ind_resource(uint32 type, int index, LoadedResource &rsrc) const
530 {
531 	rsrc.Unload();
532 
533 	// Find resource in map
534 	type_map_t::const_iterator i = types.find(type);
535 	if (i != types.end()) {
536 		if (index < 1 || index > int(i->second.size()))
537 			return false;
538 		id_map_t::const_iterator j = i->second.begin();
539 		for (int k=1; k<index; k++)
540 			++j;
541 
542 		// Read data size
543 		SDL_RWseek(f, j->second, SEEK_SET);
544 		uint32 size = SDL_ReadBE32(f);
545 
546 		// Allocate memory and read data
547 		void *p = malloc(size);
548 		if (p == NULL)
549 			return false;
550 		SDL_RWread(f, p, 1, size);
551 		rsrc.p = p;
552 		rsrc.size = size;
553 
554 //		fprintf(stderr, "get_ind_resource type %c%c%c%c, index %d -> data %p, size %d\n", type >> 24, type >> 16, type >> 8, type, index, p, size);
555 		return true;
556 	}
557 	return false;
558 }
559 
get_1_ind_resource(uint32 type,int index,LoadedResource & rsrc)560 bool get_1_ind_resource(uint32 type, int index, LoadedResource &rsrc)
561 {
562 	return (*cur_res_file_t)->get_ind_resource(type, index, rsrc);
563 }
564 
get_ind_resource(uint32 type,int index,LoadedResource & rsrc)565 bool get_ind_resource(uint32 type, int index, LoadedResource &rsrc)
566 {
567 	if (!res_file_list.size())
568 		return false;
569 	list<res_file_t *>::const_iterator i = cur_res_file_t, begin = res_file_list.begin();
570 	while (true) {
571 		bool found = (*i)->get_ind_resource(type, index, rsrc);
572 		if (found)
573 			return true;
574 		if (i == begin)
575 			break;
576 		i--;
577 	}
578 	return false;
579 }
580 
581 
582 /*
583  *  Check if resource is present
584  */
585 
has_resource(uint32 type,int id) const586 bool res_file_t::has_resource(uint32 type, int id) const
587 {
588 	type_map_t::const_iterator i = types.find(type);
589 	if (i != types.end()) {
590 		id_map_t::const_iterator j = i->second.find(id);
591 		if (j != i->second.end())
592 			return true;
593 	}
594 	return false;
595 }
596 
has_1_resource(uint32 type,int id)597 bool has_1_resource(uint32 type, int id)
598 {
599 	return (*cur_res_file_t)->has_resource(type, id);
600 }
601 
has_resource(uint32 type,int id)602 bool has_resource(uint32 type, int id)
603 {
604 	if (!res_file_list.size())
605 		return false;
606 	list<res_file_t *>::const_iterator i = cur_res_file_t, begin = res_file_list.begin();
607 	while (true) {
608 		if ((*i)->has_resource(type, id))
609 			return true;
610 		if (i == begin)
611 			break;
612 		i--;
613 	}
614 	return false;
615 }
616