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