xref: /haiku/src/tools/restest/ResourceFile.cpp (revision 1d9d47fc72028bb71b5f232a877231e59cfe2438)
1 // ResourceFile.cpp
2 
3 #include "ResourceFile.h"
4 
5 #include <algobase.h>
6 #include <stdio.h>
7 
8 #include "Elf.h"
9 #include "Exception.h"
10 #include "Pef.h"
11 #include "ResourceItem.h"
12 #include "ResourcesDefs.h"
13 #include "Warnings.h"
14 
15 // ELF defs
16 static const uint32	kMaxELFHeaderSize			= sizeof(Elf32_Ehdr) + 32;
17 static const char	kELFFileMagic[4]			= { 0x7f, 'E', 'L', 'F' };
18 
19 // sanity bounds
20 static const uint32	kMaxResourceCount			= 10000;
21 static const uint32	kELFMaxResourceAlignment	= 1024 * 1024 * 10;	// 10 MB
22 
23 // recognized file types (indices into kFileTypeNames)
24 enum {
25 	FILE_TYPE_UNKNOWN		= 0,
26 	FILE_TYPE_X86_RESOURCE	= 1,
27 	FILE_TYPE_PPC_RESOURCE	= 2,
28 	FILE_TYPE_ELF			= 3,
29 	FILE_TYPE_PEF			= 4,
30 };
31 
32 const char* kFileTypeNames[] = {
33 	"unknown",
34 	"x86 resource file",
35 	"PPC resource file",
36 	"ELF object file",
37 	"PEF object file",
38 };
39 
40 
41 // helper functions/classes
42 
43 // read_exactly
44 static
45 void
46 read_exactly(BPositionIO& file, off_t position, void* buffer, size_t size,
47 			 const char* errorMessage = NULL)
48 {
49 	ssize_t read = file.ReadAt(position, buffer, size);
50 	if (read < 0)
51 		throw Exception(read, errorMessage);
52 	else if ((size_t)read != size) {
53 		if (errorMessage) {
54 			throw Exception("%s Read to few bytes (%ld/%lu).", errorMessage,
55 							read, size);
56 		} else
57 			throw Exception("Read to few bytes (%ld/%lu).", read, size);
58 	}
59 }
60 
61 // align_value
62 template<typename TV, typename TA>
63 static inline
64 TV
65 align_value(const TV& value, const TA& alignment)
66 {
67 	return ((value + alignment - 1) / alignment) * alignment;
68 }
69 
70 // calculate_checksum
71 static
72 uint32
73 calculate_checksum(const void* data, uint32 size)
74 {
75 	uint32 checkSum = 0;
76 	const uint8* csData = (const uint8*)data;
77 	const uint8* dataEnd = csData + size;
78 	const uint8* current = csData;
79 	for (; current < dataEnd; current += 4) {
80 		uint32 word = 0;
81 		int32 bytes = min(4L, dataEnd - current);
82 		for (int32 i = 0; i < bytes; i++)
83 			word = (word << 8) + current[i];
84 		checkSum += word;
85 	}
86 	return checkSum;
87 }
88 
89 // skip_bytes
90 static inline
91 const void*
92 skip_bytes(const void* buffer, int32 offset)
93 {
94 	return (const char*)buffer + offset;
95 }
96 
97 // skip_bytes
98 static inline
99 void*
100 skip_bytes(void* buffer, int32 offset)
101 {
102 	return (char*)buffer + offset;
103 }
104 
105 // fill_pattern
106 static
107 void
108 fill_pattern(uint32 byteOffset, void* _buffer, uint32 count)
109 {
110 	uint32* buffer = (uint32*)_buffer;
111 	for (uint32 i = 0; i < count; i++)
112 		buffer[i] = kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3];
113 }
114 
115 // fill_pattern
116 static
117 void
118 fill_pattern(const void* dataBegin, void* buffer, uint32 count)
119 {
120 	fill_pattern((char*)buffer - (const char*)dataBegin, buffer, count);
121 }
122 
123 // fill_pattern
124 static
125 void
126 fill_pattern(const void* dataBegin, void* buffer, const void* bufferEnd)
127 {
128 	fill_pattern(dataBegin, buffer,
129 				 ((const char*)bufferEnd - (char*)buffer) / 4);
130 }
131 
132 // check_pattern
133 static
134 bool
135 check_pattern(uint32 byteOffset, void* _buffer, uint32 count,
136 			  bool hostEndianess)
137 {
138 	bool result = true;
139 	uint32* buffer = (uint32*)_buffer;
140 	for (uint32 i = 0; result && i < count; i++) {
141 		uint32 value = buffer[i];
142 		if (!hostEndianess)
143 			value = B_SWAP_INT32(value);
144 		result
145 			= (value == kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3]);
146 	}
147 	return result;
148 }
149 
150 // MemArea
151 struct MemArea {
152 	MemArea(const void* data, uint32 size) : data(data), size(size) {}
153 
154 	inline bool check(const void* _current, uint32 skip = 0) const
155 	{
156 		const char* start = (const char*)data;
157 		const char* current = (const char*)_current;
158 		return (start <= current && start + size >= current + skip);
159 	}
160 
161 	const void*	data;
162 	uint32		size;
163 };
164 
165 // AutoDeleter
166 template<typename C>
167 struct AutoDeleter {
168 	AutoDeleter(C* object, bool array = false) : object(object), array(array)
169 	{
170 	}
171 
172 	~AutoDeleter()
173 	{
174 		if (array)
175 			delete[] object;
176 		else
177 			delete object;
178 	}
179 
180 	C*		object;
181 	bool	array;
182 };
183 
184 
185 // constructor
186 ResourceFile::ResourceFile()
187 			: fItems(),
188 			  fFile(),
189 			  fFileType(FILE_TYPE_UNKNOWN),
190 			  fFileSize(0),
191 			  fResourceCount(0),
192 			  fInfoTableItem(NULL),
193 			  fHostEndianess(true)
194 {
195 }
196 
197 // destructor
198 ResourceFile::~ResourceFile()
199 {
200 	Unset();
201 }
202 
203 // Init
204 void
205 ResourceFile::Init(BFile& file)
206 {
207 	Unset();
208 	try {
209 		_InitFile(file);
210 		_ReadHeader();
211 		_ReadIndex();
212 		_ReadInfoTable();
213 	} catch (Exception exception) {
214 		Unset();
215 		throw exception;
216 	}
217 }
218 
219 // Unset
220 void
221 ResourceFile::Unset()
222 {
223 	// items
224 	for (int32 i = 0; ResourceItem* item = ItemAt(i); i++)
225 		delete item;
226 	fItems.MakeEmpty();
227 	// file
228 	fFile.Unset();
229 	fFileType = FILE_TYPE_UNKNOWN;
230 	fFileSize = 0;
231 	// resource count
232 	fResourceCount = 0;
233 	// info table
234 	delete fInfoTableItem;
235 	fInfoTableItem = NULL;
236 	fHostEndianess = true;
237 }
238 
239 // InitCheck
240 status_t
241 ResourceFile::InitCheck() const
242 {
243 	return fFile.InitCheck();
244 }
245 
246 // AddItem
247 bool
248 ResourceFile::AddItem(ResourceItem* item, int32 index)
249 {
250 	bool result = false;
251 	if (item) {
252 		if (index < 0 || index > CountItems())
253 			index = CountItems();
254 		result = fItems.AddItem(item);
255 	}
256 	return result;
257 }
258 
259 // RemoveItem
260 ResourceItem*
261 ResourceFile::RemoveItem(int32 index)
262 {
263 	return (ResourceItem*)fItems.RemoveItem(index);
264 }
265 
266 // RemoveItem
267 bool
268 ResourceFile::RemoveItem(ResourceItem* item)
269 {
270 	return RemoveItem(IndexOf(item));
271 }
272 
273 // IndexOf
274 int32
275 ResourceFile::IndexOf(ResourceItem* item) const
276 {
277 	return fItems.IndexOf(item);
278 }
279 
280 // ItemAt
281 ResourceItem*
282 ResourceFile::ItemAt(int32 index) const
283 {
284 	return (ResourceItem*)fItems.ItemAt(index);
285 }
286 
287 // CountItems
288 int32
289 ResourceFile::CountItems() const
290 {
291 	return fItems.CountItems();
292 }
293 
294 // GetResourcesSize
295 uint32
296 ResourceFile::GetResourcesSize() const
297 {
298 	if (!fInfoTableItem || fFile.InitCheck())
299 		throw Exception("Resource file not initialized.");
300 	// header
301 	uint32 size = kResourcesHeaderSize;
302 	// index section
303 	uint32 indexSectionSize = kResourceIndexSectionHeaderSize
304 		+ fResourceCount * kResourceIndexEntrySize;
305 	indexSectionSize = align_value(indexSectionSize,
306 								   kResourceIndexSectionAlignment);
307 	size += indexSectionSize;
308 	// unknown section
309 	size += kUnknownResourceSectionSize;
310 	// data
311 	uint32 dataSize = 0;
312 	for (int32 i = 0; i < fResourceCount; i++) {
313 		ResourceItem* item = ItemAt(i);
314 		dataSize += item->GetSize();
315 	}
316 	size += dataSize;
317 	// info table
318 	uint32 infoTableSize = 0;
319 	type_code type = 0;
320 	for (int32 i = 0; i < fResourceCount; i++) {
321 		ResourceItem* item = ItemAt(i);
322 		if (i == 0 || type != item->GetType()) {
323 			if (i != 0)
324 				infoTableSize += kResourceInfoSeparatorSize;
325 			type = item->GetType();
326 			infoTableSize += kMinResourceInfoBlockSize;
327 		} else
328 			infoTableSize += kMinResourceInfoSize;
329 		uint32 nameLen = strlen(item->GetName());
330 		if (nameLen != 0)
331 			infoTableSize += nameLen + 1;
332 	}
333 	infoTableSize += kResourceInfoSeparatorSize + kResourceInfoTableEndSize;
334 	size += infoTableSize;
335 	return size;
336 }
337 
338 // WriteResources
339 uint32
340 ResourceFile::WriteResources(void* buffer, uint32 bufferSize)
341 {
342 	// calculate sizes and offsets
343 	// header
344 	uint32 size = kResourcesHeaderSize;
345 	// index section
346 	uint32 indexSectionOffset = size;
347 	uint32 indexSectionSize = kResourceIndexSectionHeaderSize
348 		+ fResourceCount * kResourceIndexEntrySize;
349 	indexSectionSize = align_value(indexSectionSize,
350 								   kResourceIndexSectionAlignment);
351 	size += indexSectionSize;
352 	// unknown section
353 	uint32 unknownSectionOffset = size;
354 	uint32 unknownSectionSize = kUnknownResourceSectionSize;
355 	size += unknownSectionSize;
356 	// data
357 	uint32 dataOffset = size;
358 	uint32 dataSize = 0;
359 	for (int32 i = 0; i < fResourceCount; i++) {
360 		ResourceItem* item = ItemAt(i);
361 		dataSize += item->GetSize();
362 	}
363 	size += dataSize;
364 	// info table
365 	uint32 infoTableOffset = size;
366 	uint32 infoTableSize = 0;
367 	type_code type = 0;
368 	for (int32 i = 0; i < fResourceCount; i++) {
369 		ResourceItem* item = ItemAt(i);
370 		if (i == 0 || type != item->GetType()) {
371 			if (i != 0)
372 				infoTableSize += kResourceInfoSeparatorSize;
373 			type = item->GetType();
374 			infoTableSize += kMinResourceInfoBlockSize;
375 		} else
376 			infoTableSize += kMinResourceInfoSize;
377 		uint32 nameLen = strlen(item->GetName());
378 		if (nameLen != 0)
379 			infoTableSize += nameLen + 1;
380 	}
381 	infoTableSize += kResourceInfoSeparatorSize + kResourceInfoTableEndSize;
382 	size += infoTableSize;
383 	// check whether the buffer is large enough
384 	if (!buffer)
385 		throw Exception("Supplied buffer is NULL.");
386 	if (bufferSize < size)
387 		throw Exception("Supplied buffer is too small.");
388 	// write...
389 	void* data = buffer;
390 	// header
391 	resources_header* resourcesHeader = (resources_header*)data;
392 	resourcesHeader->rh_resources_magic = kResourcesHeaderMagic;
393 	resourcesHeader->rh_resource_count = fResourceCount;
394 	resourcesHeader->rh_index_section_offset = indexSectionOffset;
395 	resourcesHeader->rh_admin_section_size = indexSectionOffset
396 											 + indexSectionSize;
397 	for (int32 i = 0; i < 13; i++)
398 		resourcesHeader->rh_pad[i] = 0;
399 	// index section
400 	// header
401 	data = skip_bytes(buffer, indexSectionOffset);
402 	resource_index_section_header* indexHeader
403 		= (resource_index_section_header*)data;
404 	indexHeader->rish_index_section_offset = indexSectionOffset;
405 	indexHeader->rish_index_section_size = indexSectionSize;
406 	indexHeader->rish_unknown_section_offset = unknownSectionOffset;
407 	indexHeader->rish_unknown_section_size = unknownSectionSize;
408 	indexHeader->rish_info_table_offset = infoTableOffset;
409 	indexHeader->rish_info_table_size = infoTableSize;
410 	fill_pattern(buffer, &indexHeader->rish_unused_data1, 1);
411 	fill_pattern(buffer, indexHeader->rish_unused_data2, 25);
412 	fill_pattern(buffer, &indexHeader->rish_unused_data3, 1);
413 	// index table
414 	data = skip_bytes(data, kResourceIndexSectionHeaderSize);
415 	resource_index_entry* entry = (resource_index_entry*)data;
416 	uint32 entryOffset = dataOffset;
417 	for (int32 i = 0; i < fResourceCount; i++, entry++) {
418 		ResourceItem* item = ItemAt(i);
419 		uint32 entrySize = item->GetSize();
420 		entry->rie_offset = entryOffset;
421 		entry->rie_size = entrySize;
422 		entry->rie_pad = 0;
423 		entryOffset += entrySize;
424 	}
425 	// padding + unknown section
426 	data = skip_bytes(buffer, dataOffset);
427 	fill_pattern(buffer, entry, data);
428 	// data
429 	for (int32 i = 0; i < fResourceCount; i++) {
430 		ResourceItem* item = ItemAt(i);
431 		status_t error = item->LoadData(fFile);
432 		if (error != B_OK)
433 			throw Exception(error, "Error loading resource data.");
434 		uint32 entrySize = item->GetSize();
435 		memcpy(data, item->GetData(), entrySize);
436 		data = skip_bytes(data, entrySize);
437 	}
438 	// info table
439 	data = skip_bytes(buffer, infoTableOffset);
440 	type = 0;
441 	for (int32 i = 0; i < fResourceCount; i++) {
442 		ResourceItem* item = ItemAt(i);
443 		resource_info* info = NULL;
444 		if (i == 0 || type != item->GetType()) {
445 			if (i != 0) {
446 				resource_info_separator* separator
447 					= (resource_info_separator*)data;
448 				separator->ris_value1 = 0xffffffff;
449 				separator->ris_value2 = 0xffffffff;
450 				data = skip_bytes(data, kResourceInfoSeparatorSize);
451 			}
452 			type = item->GetType();
453 			resource_info_block* infoBlock = (resource_info_block*)data;
454 			infoBlock->rib_type = type;
455 			info = infoBlock->rib_info;
456 		} else
457 			info = (resource_info*)data;
458 		// info
459 		info->ri_id = item->GetID();
460 		info->ri_index = i + 1;
461 		info->ri_name_size = 0;
462 		data = info->ri_name;
463 		uint32 nameLen = strlen(item->GetName());
464 		if (nameLen != 0) {
465 			memcpy(info->ri_name, item->GetName(), nameLen + 1);
466 			data = skip_bytes(data, nameLen + 1);
467 			info->ri_name_size = nameLen + 1;
468 		}
469 	}
470 	// separator
471 	resource_info_separator* separator = (resource_info_separator*)data;
472 	separator->ris_value1 = 0xffffffff;
473 	separator->ris_value2 = 0xffffffff;
474 	// table end
475 	data = skip_bytes(data, kResourceInfoSeparatorSize);
476 	resource_info_table_end* tableEnd = (resource_info_table_end*)data;
477 	void* infoTable = skip_bytes(buffer, infoTableOffset);
478 	tableEnd->rite_check_sum = calculate_checksum(infoTable,
479 		infoTableSize - kResourceInfoTableEndSize);
480 	tableEnd->rite_terminator = 0;
481 	// final check
482 	data = skip_bytes(data, kResourceInfoTableEndSize);
483 	uint32 bytesWritten = (char*)data - (char*)buffer;
484 	if (bytesWritten != size) {
485 		throw Exception("Bad boy error: Wrote %lu bytes, though supposed to "
486 						"write %lu bytes.", bytesWritten, size);
487 	}
488 	return size;
489 }
490 
491 // WriteTest
492 void
493 ResourceFile::WriteTest()
494 {
495 	uint32 size = GetResourcesSize();
496 	if (size != fFileSize) {
497 		throw Exception("Calculated resources size differs from actual size "
498 						"in file: %lu vs %Ld.", size, fFileSize);
499 	}
500 	char* buffer1 = new char[size];
501 	char* buffer2 = new char[size];
502 	try {
503 		WriteResources(buffer1, size);
504 		read_exactly(fFile, 0, buffer2, size,
505 					 "Write test: Error reading resources.");
506 		for (uint32 i = 0; i < size; i++) {
507 			if (buffer1[i] != buffer2[i]) {
508 				off_t filePosition = fFile.GetOffset() + i;
509 				throw Exception("Written resources differ from those in file. "
510 								"First difference at byte %lu (file position "
511 								"%Ld): %x vs %x.", i, filePosition,
512 								(int)buffer1[i] & 0xff,
513 								(int)buffer2[i] & 0xff);
514 			}
515 		}
516 	} catch (Exception exception) {
517 		delete[] buffer1;
518 		delete[] buffer2;
519 		throw exception;
520 	}
521 	delete[] buffer1;
522 	delete[] buffer2;
523 }
524 
525 
526 
527 // PrintToStream
528 void
529 ResourceFile::PrintToStream(bool longInfo)
530 {
531 	if (longInfo) {
532 		off_t resourcesOffset = fFile.GetOffset();
533 		printf("ResourceFile:\n");
534 		printf("file type              : %s\n", kFileTypeNames[fFileType]);
535 		printf("endianess              : %s\n",
536 			   (fHostEndianess == (bool)B_HOST_IS_LENDIAN) ? "little" : "big");
537 		printf("resource section offset: 0x%08Lx (%Ld)\n", resourcesOffset,
538 			   resourcesOffset);
539 		if (fInfoTableItem) {
540 			int32 offset = fInfoTableItem->GetOffset();
541 			int32 size = fInfoTableItem->GetSize();
542 			printf("resource info table    : offset: 0x%08lx (%ld), "
543 				   "size: 0x%08lx (%ld)\n", offset, offset, size, size);
544 		}
545 		printf("number of resources    : %ld\n", fResourceCount);
546 		for (int32 i = 0; i < fResourceCount; i++) {
547 			ResourceItem* item = ItemAt(i);
548 			item->PrintToStream();
549 		}
550 	} else {
551 		printf("   Type     ID     Size  Name\n");
552 		printf("  ------ ----- --------  --------------------\n");
553 		for (int32 i = 0; i < fResourceCount; i++) {
554 			ResourceItem* item = ItemAt(i);
555 			type_code type = item->GetType();
556 			char typeName[4] = { type >> 24, (type >> 16) & 0xff,
557 								 (type >> 8) & 0xff, type & 0xff };
558 			printf("  '%.4s' %5ld %8lu  %s\n", typeName, item->GetID(),
559 				   item->GetSize(), item->GetName());
560 		}
561 	}
562 }
563 
564 
565 // _InitFile
566 void
567 ResourceFile::_InitFile(BFile& file)
568 {
569 	status_t error = B_OK;
570 	fFile.Unset();
571 	// read the first four bytes, and check, if they identify a resource file
572 	char magic[4];
573 	read_exactly(file, 0, magic, 4, "Failed to read magic number.");
574 	if (!memcmp(magic, kX86ResourceFileMagic, 4)) {
575 		// x86 resource file
576 		fHostEndianess = B_HOST_IS_LENDIAN;
577 		fFileType = FILE_TYPE_X86_RESOURCE;
578 		fFile.SetTo(file, kX86ResourcesOffset);
579 	} else if (!memcmp(magic, kPEFFileMagic1, 4)) {
580 		PEFContainerHeader pefHeader;
581 		read_exactly(file, 0, &pefHeader, kPEFContainerHeaderSize,
582 					 "Failed to read PEF container header.");
583 		if (!memcmp(pefHeader.tag2, kPPCResourceFileMagic, 4)) {
584 			// PPC resource file
585 			fHostEndianess = B_HOST_IS_BENDIAN;
586 			fFileType = FILE_TYPE_PPC_RESOURCE;
587 			fFile.SetTo(file, kPPCResourcesOffset);
588 		} else if (!memcmp(pefHeader.tag2, kPEFFileMagic2, 4)) {
589 			// PEF file
590 			fFileType = FILE_TYPE_PEF;
591 			_InitPEFFile(file, pefHeader);
592 		} else
593 			throw Exception("File is not a resource file.");
594 	} else if (!memcmp(magic, kELFFileMagic, 4)) {
595 		// ELF file
596 		fFileType = FILE_TYPE_ELF;
597 		_InitELFFile(file);
598 	} else if (!memcmp(magic, kX86ResourceFileMagic, 2)) {
599 		// x86 resource file with screwed magic?
600 		Warnings::AddCurrentWarning("File magic is 0x%08lx. Should be 0x%08lx "
601 									"for x86 resource file. Try anyway.",
602 									ntohl(*(uint32*)magic),
603 									ntohl(*(uint32*)kX86ResourceFileMagic));
604 		fHostEndianess = B_HOST_IS_LENDIAN;
605 		fFileType = FILE_TYPE_X86_RESOURCE;
606 		fFile.SetTo(file, kX86ResourcesOffset);
607 	} else
608 		throw Exception("File is not a resource file.");
609 	error = fFile.InitCheck();
610 	if (error != B_OK)
611 		throw Exception(error, "Failed to initialize resource file.");
612 	// get the file size
613 	fFileSize = 0;
614 	error = fFile.GetSize(&fFileSize);
615 	if (error != B_OK)
616 		throw Exception(error, "Failed to get the file size.");
617 }
618 
619 // _InitELFFile
620 void
621 ResourceFile::_InitELFFile(BFile& file)
622 {
623 	status_t error = B_OK;
624 	// get the file size
625 	off_t fileSize = 0;
626 	error = file.GetSize(&fileSize);
627 	if (error != B_OK)
628 		throw Exception(error, "Failed to get the file size.");
629 	// read ELF header
630 	Elf32_Ehdr fileHeader;
631 	read_exactly(file, 0, &fileHeader, sizeof(Elf32_Ehdr),
632 				 "Failed to read ELF header.");
633 	// check data encoding (endianess)
634 	switch (fileHeader.e_ident[EI_DATA]) {
635 		case ELFDATA2LSB:
636 			fHostEndianess = B_HOST_IS_LENDIAN;
637 			break;
638 		case ELFDATA2MSB:
639 			fHostEndianess = B_HOST_IS_BENDIAN;
640 			break;
641 		default:
642 		case ELFDATANONE:
643 			throw Exception("Unsupported ELF data encoding.");
644 			break;
645 	}
646 	// get the header values
647 	uint32 headerSize				= _GetUInt16(fileHeader.e_ehsize);
648 	uint32 programHeaderTableOffset	= _GetUInt32(fileHeader.e_phoff);
649 	uint32 programHeaderSize		= _GetUInt16(fileHeader.e_phentsize);
650 	uint32 programHeaderCount		= _GetUInt16(fileHeader.e_phnum);
651 	uint32 sectionHeaderTableOffset	= _GetUInt32(fileHeader.e_shoff);
652 	uint32 sectionHeaderSize		= _GetUInt16(fileHeader.e_shentsize);
653 	uint32 sectionHeaderCount		= _GetUInt16(fileHeader.e_shnum);
654 	bool hasProgramHeaderTable = (programHeaderTableOffset != 0);
655 	bool hasSectionHeaderTable = (sectionHeaderTableOffset != 0);
656 //printf("headerSize              : %lu\n", headerSize);
657 //printf("programHeaderTableOffset: %lu\n", programHeaderTableOffset);
658 //printf("programHeaderSize       : %lu\n", programHeaderSize);
659 //printf("programHeaderCount      : %lu\n", programHeaderCount);
660 //printf("sectionHeaderTableOffset: %lu\n", sectionHeaderTableOffset);
661 //printf("sectionHeaderSize       : %lu\n", sectionHeaderSize);
662 //printf("sectionHeaderCount      : %lu\n", sectionHeaderCount);
663 	// check the sanity of the header values
664 	// ELF header size
665 	if (headerSize < sizeof(Elf32_Ehdr) || headerSize > kMaxELFHeaderSize) {
666 		throw Exception("Invalid ELF header: invalid ELF header size: %lu.",
667 						headerSize);
668 	}
669 	uint32 resourceOffset = headerSize;
670 	uint32 resourceAlignment = 0;
671 	// program header table offset and entry count/size
672 	uint32 programHeaderTableSize = 0;
673 	if (hasProgramHeaderTable) {
674 		if (programHeaderTableOffset < headerSize
675 			|| programHeaderTableOffset > fileSize) {
676 			throw Exception("Invalid ELF header: invalid program header table "
677 							"offset: %lu.", programHeaderTableOffset);
678 		}
679 		programHeaderTableSize = programHeaderSize * programHeaderCount;
680 		if (programHeaderSize < sizeof(Elf32_Phdr)
681 			|| programHeaderTableOffset + programHeaderTableSize > fileSize) {
682 			throw Exception("Invalid ELF header: program header table exceeds "
683 							"file: %lu.",
684 							programHeaderTableOffset + programHeaderTableSize);
685 		}
686 		resourceOffset = max(resourceOffset, programHeaderTableOffset
687 											 + programHeaderTableSize);
688 		// iterate through the program headers
689 		for (int32 i = 0; i < (int32)programHeaderCount; i++) {
690 			uint32 shOffset = programHeaderTableOffset + i * programHeaderSize;
691 			Elf32_Phdr programHeader;
692 			read_exactly(file, shOffset, &programHeader, sizeof(Elf32_Shdr),
693 						 "Failed to read ELF program header.");
694 			// get the header values
695 			uint32 type			= _GetUInt32(programHeader.p_type);
696 			uint32 offset		= _GetUInt32(programHeader.p_offset);
697 			uint32 size			= _GetUInt32(programHeader.p_filesz);
698 			uint32 alignment	= _GetUInt32(programHeader.p_align);
699 //printf("segment: type: %ld, offset: %lu, size: %lu, alignment: %lu\n",
700 //type, offset, size, alignment);
701 			// check the values
702 			// PT_NULL marks the header unused,
703 			if (type != PT_NULL) {
704 				if (/*offset < headerSize ||*/ offset > fileSize) {
705 					throw Exception("Invalid ELF program header: invalid "
706 									"program offset: %lu.", offset);
707 				}
708 				uint32 segmentEnd = offset + size;
709 				if (segmentEnd > fileSize) {
710 					throw Exception("Invalid ELF section header: segment "
711 									"exceeds file: %lu.", segmentEnd);
712 				}
713 				resourceOffset = max(resourceOffset, segmentEnd);
714 				resourceAlignment = max(resourceAlignment, alignment);
715 			}
716 		}
717 	}
718 	// section header table offset and entry count/size
719 	uint32 sectionHeaderTableSize = 0;
720 	if (hasSectionHeaderTable) {
721 		if (sectionHeaderTableOffset < headerSize
722 			|| sectionHeaderTableOffset > fileSize) {
723 			throw Exception("Invalid ELF header: invalid section header table "
724 							"offset: %lu.", sectionHeaderTableOffset);
725 		}
726 		sectionHeaderTableSize = sectionHeaderSize * sectionHeaderCount;
727 		if (sectionHeaderSize < sizeof(Elf32_Shdr)
728 			|| sectionHeaderTableOffset + sectionHeaderTableSize > fileSize) {
729 			throw Exception("Invalid ELF header: section header table exceeds "
730 							"file: %lu.",
731 							sectionHeaderTableOffset + sectionHeaderTableSize);
732 		}
733 		resourceOffset = max(resourceOffset, sectionHeaderTableOffset
734 											 + sectionHeaderTableSize);
735 		// iterate through the section headers
736 		for (int32 i = 0; i < (int32)sectionHeaderCount; i++) {
737 			uint32 shOffset = sectionHeaderTableOffset + i * sectionHeaderSize;
738 			Elf32_Shdr sectionHeader;
739 			read_exactly(file, shOffset, &sectionHeader, sizeof(Elf32_Shdr),
740 						 "Failed to read ELF section header.");
741 			// get the header values
742 			uint32 type		= _GetUInt32(sectionHeader.sh_type);
743 			uint32 offset	= _GetUInt32(sectionHeader.sh_offset);
744 			uint32 size		= _GetUInt32(sectionHeader.sh_size);
745 //printf("section: type: %ld, offset: %lu, size: %lu\n", type, offset, size);
746 			// check the values
747 			// SHT_NULL marks the header unused,
748 			// SHT_NOBITS sections take no space in the file
749 			if (type != SHT_NULL && type != SHT_NOBITS) {
750 				if (offset < headerSize || offset > fileSize) {
751 					throw Exception("Invalid ELF section header: invalid "
752 									"section offset: %lu.", offset);
753 				}
754 				uint32 sectionEnd = offset + size;
755 				if (sectionEnd > fileSize) {
756 					throw Exception("Invalid ELF section header: section "
757 									"exceeds file: %lu.", sectionEnd);
758 				}
759 				resourceOffset = max(resourceOffset, sectionEnd);
760 			}
761 		}
762 	}
763 //printf("resourceOffset: %lu\n", resourceOffset);
764 	// align the offset
765 	if (resourceAlignment < kELFMinResourceAlignment)
766 		resourceAlignment = kELFMinResourceAlignment;
767 	if (resourceAlignment > kELFMaxResourceAlignment) {
768 		throw Exception("The ELF object file requires an invalid alignment: "
769 						"%lu.", resourceAlignment);
770 	}
771 	resourceOffset = align_value(resourceOffset, resourceAlignment);
772 //printf("resourceOffset: %lu\n", resourceOffset);
773 	if (resourceOffset >= fileSize)
774 		throw Exception("The ELF object file does not contain resources.");
775 	// fine, init the offset file
776 	fFile.SetTo(file, resourceOffset);
777 }
778 
779 // _InitPEFFile
780 void
781 ResourceFile::_InitPEFFile(BFile& file, const PEFContainerHeader& pefHeader)
782 {
783 	status_t error = B_OK;
784 	// get the file size
785 	off_t fileSize = 0;
786 	error = file.GetSize(&fileSize);
787 	if (error != B_OK)
788 		throw Exception(error, "Failed to get the file size.");
789 	// check architecture -- we support PPC only
790 	if (memcmp(pefHeader.architecture, kPEFArchitecturePPC, 4))
791 		throw Exception("PEF file architecture is not PPC.");
792 	fHostEndianess = B_HOST_IS_BENDIAN;
793 	// get the section count
794 	uint16 sectionCount = _GetUInt16(pefHeader.sectionCount);
795 	// iterate through the PEF sections headers
796 	uint32 sectionHeaderTableOffset = kPEFContainerHeaderSize;
797 	uint32 sectionHeaderTableEnd
798 		= sectionHeaderTableOffset + sectionCount * kPEFSectionHeaderSize;
799 	uint32 resourceOffset = sectionHeaderTableEnd;
800 	for (int32 i = 0; i < (int32)sectionCount; i++) {
801 		uint32 shOffset = sectionHeaderTableOffset + i * kPEFSectionHeaderSize;
802 		PEFSectionHeader sectionHeader;
803 		read_exactly(file, shOffset, &sectionHeader, kPEFSectionHeaderSize,
804 					 "Failed to read PEF section header.");
805 		// get the header values
806 		uint32 offset	= _GetUInt32(sectionHeader.containerOffset);
807 		uint32 size		= _GetUInt32(sectionHeader.packedSize);
808 		// check the values
809 		if (offset < sectionHeaderTableEnd || offset > fileSize) {
810 			throw Exception("Invalid PEF section header: invalid "
811 							"section offset: %lu.", offset);
812 		}
813 		uint32 sectionEnd = offset + size;
814 		if (sectionEnd > fileSize) {
815 			throw Exception("Invalid PEF section header: section "
816 							"exceeds file: %lu.", sectionEnd);
817 		}
818 		resourceOffset = max(resourceOffset, sectionEnd);
819 	}
820 	// init the offset file
821 	fFile.SetTo(file, resourceOffset);
822 }
823 
824 // _ReadHeader
825 void
826 ResourceFile::_ReadHeader()
827 {
828 	// read the header
829 	resources_header header;
830 	read_exactly(fFile, 0, &header, kResourcesHeaderSize,
831 				 "Failed to read the header.");
832 	// check the header
833 	// magic
834 	uint32 magic = _GetUInt32(header.rh_resources_magic);
835 	if (magic == kResourcesHeaderMagic) {
836 		// everything is fine
837 	} else if (B_SWAP_INT32(magic) == kResourcesHeaderMagic) {
838 		const char* endianessStr[2] = { "little", "big" };
839 		int32 endianess
840 			= (fHostEndianess == ((bool)B_HOST_IS_LENDIAN ? 0 : 1));
841 		Warnings::AddCurrentWarning("Endianess seems to be %s, although %s "
842 									"was expected.",
843 									endianessStr[1 - endianess],
844 									endianessStr[endianess]);
845 		fHostEndianess = !fHostEndianess;
846 	} else
847 		throw Exception("Invalid resources header magic.");
848 	// resource count
849 	uint32 resourceCount = _GetUInt32(header.rh_resource_count);
850 	if (resourceCount > kMaxResourceCount)
851 		throw Exception("Bad number of resources.");
852 	// index section offset
853 	uint32 indexSectionOffset = _GetUInt32(header.rh_index_section_offset);
854 	if (indexSectionOffset != kResourceIndexSectionOffset) {
855 		throw Exception("Unexpected resource index section offset. Is: %lu, "
856 						"should be: %lu.", indexSectionOffset,
857 						kResourceIndexSectionOffset);
858 	}
859 	// admin section size
860 	uint32 indexSectionSize = kResourceIndexSectionHeaderSize
861 							  + kResourceIndexEntrySize * resourceCount;
862 	indexSectionSize = align_value(indexSectionSize,
863 								   kResourceIndexSectionAlignment);
864 	uint32 adminSectionSize = _GetUInt32(header.rh_admin_section_size);
865 	if (adminSectionSize != indexSectionOffset + indexSectionSize) {
866 		throw Exception("Unexpected resource admin section size. Is: %lu, "
867 						"should be: %lu.", adminSectionSize,
868 						indexSectionOffset + indexSectionSize);
869 	}
870 	// set the resource count
871 	fResourceCount = resourceCount;
872 }
873 
874 // _ReadIndex
875 void
876 ResourceFile::_ReadIndex()
877 {
878 	// read the header
879 	resource_index_section_header header;
880 	read_exactly(fFile, kResourceIndexSectionOffset, &header,
881 				 kResourceIndexSectionHeaderSize,
882 				 "Failed to read the resource index section header.");
883 	// check the header
884 	// index section offset
885 	uint32 indexSectionOffset = _GetUInt32(header.rish_index_section_offset);
886 	if (indexSectionOffset != kResourceIndexSectionOffset) {
887 		throw Exception("Unexpected resource index section offset. Is: %lu, "
888 						"should be: %lu.", indexSectionOffset,
889 						kResourceIndexSectionOffset);
890 	}
891 	// index section size
892 	uint32 expectedIndexSectionSize = kResourceIndexSectionHeaderSize
893 		+ kResourceIndexEntrySize * fResourceCount;
894 	expectedIndexSectionSize = align_value(expectedIndexSectionSize,
895 										   kResourceIndexSectionAlignment);
896 	uint32 indexSectionSize = _GetUInt32(header.rish_index_section_size);
897 	if (indexSectionSize != expectedIndexSectionSize) {
898 		throw Exception("Unexpected resource index section size. Is: %lu, "
899 						"should be: %lu.", indexSectionSize,
900 						expectedIndexSectionSize);
901 	}
902 	// unknown section offset
903 	uint32 unknownSectionOffset
904 		= _GetUInt32(header.rish_unknown_section_offset);
905 	if (unknownSectionOffset != indexSectionOffset + indexSectionSize) {
906 		throw Exception("Unexpected resource index section size. Is: %lu, "
907 						"should be: %lu.", unknownSectionOffset,
908 						indexSectionOffset + indexSectionSize);
909 	}
910 	// unknown section size
911 	uint32 unknownSectionSize = _GetUInt32(header.rish_unknown_section_size);
912 	if (unknownSectionSize != kUnknownResourceSectionSize) {
913 		throw Exception("Unexpected resource index section offset. Is: %lu, "
914 						"should be: %lu.", unknownSectionOffset,
915 						kUnknownResourceSectionSize);
916 	}
917 	// info table offset and size
918 	uint32 infoTableOffset = _GetUInt32(header.rish_info_table_offset);
919 	uint32 infoTableSize = _GetUInt32(header.rish_info_table_size);
920 	if (infoTableOffset + infoTableSize > fFileSize)
921 		throw Exception("Invalid info table location.");
922 	fInfoTableItem = new ResourceItem;
923 	fInfoTableItem->SetLocation(infoTableOffset, infoTableSize);
924 	// read the index entries
925 	uint32 indexTableOffset = indexSectionOffset
926 							  + kResourceIndexSectionHeaderSize;
927 	int32 maxResourceCount = (unknownSectionOffset - indexTableOffset)
928 							 / kResourceIndexEntrySize;
929 	int32 actualResourceCount = 0;
930 	bool tableEndReached = false;
931 	for (int32 i = 0; !tableEndReached && i < maxResourceCount; i++) {
932 		// read one entry
933 		tableEndReached = !_ReadIndexEntry(i, indexTableOffset,
934 										   (i >= fResourceCount));
935 		if (!tableEndReached)
936 			actualResourceCount++;
937 	}
938 	// check resource count
939 	if (actualResourceCount != fResourceCount) {
940 		if (actualResourceCount > fResourceCount) {
941 			Warnings::AddCurrentWarning("Resource index table contains "
942 										"%ld entries, although it should be "
943 										"%ld only.", actualResourceCount,
944 										fResourceCount);
945 		}
946 		fResourceCount = actualResourceCount;
947 	}
948 }
949 
950 // _ReadIndexEntry
951 bool
952 ResourceFile::_ReadIndexEntry(int32 index, uint32 tableOffset, bool peekAhead)
953 {
954 	bool result = true;
955 	resource_index_entry entry;
956 	// read one entry
957 	off_t entryOffset = tableOffset + index * kResourceIndexEntrySize;
958 	read_exactly(fFile, entryOffset, &entry, kResourceIndexEntrySize,
959 				 "Failed to read a resource index entry.");
960 	// check, if the end is reached early
961 	if (result && check_pattern(entryOffset, &entry,
962 								kResourceIndexEntrySize / 4, fHostEndianess)) {
963 		if (!peekAhead) {
964 			Warnings::AddCurrentWarning("Unexpected end of resource index "
965 										"table at index: %ld (/%ld).",
966 										index + 1, fResourceCount);
967 		}
968 		result = false;
969 	}
970 	uint32 offset = _GetUInt32(entry.rie_offset);
971 	uint32 size = _GetUInt32(entry.rie_size);
972 	// check the location
973 	if (result && offset + size > fFileSize) {
974 		if (peekAhead) {
975 			Warnings::AddCurrentWarning("Invalid data after resource index "
976 										"table.");
977 		} else {
978 			throw Exception("Invalid resource index entry: index: %ld, "
979 							"offset: %lu (%lx), size: %lu (%lx).", index + 1,
980 							offset, offset, size, size);
981 		}
982 		result = false;
983 	}
984 	// add the entry
985 	if (result) {
986 		ResourceItem* item = new ResourceItem;
987 		item->SetLocation(offset, size);
988 		AddItem(item, index);
989 	}
990 	return result;
991 }
992 
993 // _ReadInfoTable
994 void
995 ResourceFile::_ReadInfoTable()
996 {
997 	status_t error = B_OK;
998 	error = fInfoTableItem->LoadData(fFile);
999 	if (error != B_OK)
1000 		throw Exception(error, "Failed to read resource info table.");
1001 	const void* tableData = fInfoTableItem->GetData();
1002 	int32 dataSize = fInfoTableItem->GetSize();
1003 	//
1004 	bool* readIndices = new bool[fResourceCount + 1];	// + 1 => always > 0
1005 	for (int32 i = 0; i < fResourceCount; i++)
1006 		readIndices[i] = false;
1007 	AutoDeleter<bool> deleter(readIndices, true);
1008 	MemArea area(tableData, dataSize);
1009 	const void* data = tableData;
1010 	// check the table end/check sum
1011 	if (_ReadInfoTableEnd(data, dataSize))
1012 		dataSize -= kResourceInfoTableEndSize;
1013 	// read the infos
1014 	int32 resourceIndex = 1;
1015 	uint32 minRemainderSize
1016 		= kMinResourceInfoBlockSize + kResourceInfoSeparatorSize;
1017 	while (area.check(data, minRemainderSize)) {
1018 		// read a resource block
1019 		if (!area.check(data, kMinResourceInfoBlockSize)) {
1020 			throw Exception("Unexpected end of resource info table at index "
1021 							"%ld.", resourceIndex);
1022 		}
1023 		const resource_info_block* infoBlock
1024 			= (const resource_info_block*)data;
1025 		type_code type = _GetUInt32(infoBlock->rib_type);
1026 		// read the infos of this block
1027 		const resource_info* info = infoBlock->rib_info;
1028 		while (info) {
1029 			data = _ReadResourceInfo(area, info, type, readIndices);
1030 			// prepare for next iteration, if there is another info
1031 			if (!area.check(data, kResourceInfoSeparatorSize)) {
1032 				throw Exception("Unexpected end of resource info table after "
1033 								"index %ld.", resourceIndex);
1034 			}
1035 			const resource_info_separator* separator
1036 				= (const resource_info_separator*)data;
1037 			if (_GetUInt32(separator->ris_value1) == 0xffffffff
1038 				&& _GetUInt32(separator->ris_value2) == 0xffffffff) {
1039 				// info block ends
1040 				info = NULL;
1041 				data = skip_bytes(data, kResourceInfoSeparatorSize);
1042 			} else {
1043 				// another info follows
1044 				info = (const resource_info*)data;
1045 			}
1046 			resourceIndex++;
1047 		}
1048 		// end of the info block
1049 	}
1050 	// handle special case: empty resource info table
1051 	if (resourceIndex == 1) {
1052 		if (!area.check(data, kResourceInfoSeparatorSize)) {
1053 			throw Exception("Unexpected end of resource info table.");
1054 		}
1055 		const resource_info_separator* tableTerminator
1056 			= (const resource_info_separator*)data;
1057 		if (_GetUInt32(tableTerminator->ris_value1) != 0xffffffff
1058 			|| _GetUInt32(tableTerminator->ris_value2) != 0xffffffff) {
1059 			throw Exception("The resource info table ought to be empty, but "
1060 							"is not properly terminated.");
1061 		}
1062 		data = skip_bytes(data, kResourceInfoSeparatorSize);
1063 	}
1064 	// Check, if the correct number of bytes are remaining.
1065 	uint32 bytesLeft = (const char*)tableData + dataSize - (const char*)data;
1066 	if (bytesLeft != 0) {
1067 		throw Exception("Error at the end of the resource info table: %lu "
1068 						"bytes are remaining.", bytesLeft);
1069 	}
1070 	// check, if all items have been initialized
1071 	for (int32 i = fResourceCount - 1; i >= 0; i--) {
1072 		if (!readIndices[i]) {
1073 			Warnings::AddCurrentWarning("Resource item at index %ld "
1074 										"has no info. Item removed.", i + 1);
1075 			if (ResourceItem* item = RemoveItem(i))
1076 				delete item;
1077 			fResourceCount--;
1078 		}
1079 	}
1080 }
1081 
1082 // _ReadInfoTableEnd
1083 bool
1084 ResourceFile::_ReadInfoTableEnd(const void* data, int32 dataSize)
1085 {
1086 	bool hasTableEnd = true;
1087 	if ((uint32)dataSize < kResourceInfoSeparatorSize)
1088 		throw Exception("Info table is too short.");
1089 	if ((uint32)dataSize < kResourceInfoTableEndSize)
1090 		hasTableEnd = false;
1091 	if (hasTableEnd) {
1092 		const resource_info_table_end* tableEnd
1093 			= (const resource_info_table_end*)
1094 			  skip_bytes(data, dataSize - kResourceInfoTableEndSize);
1095 		if (_GetInt32(tableEnd->rite_terminator) != 0)
1096 			hasTableEnd = false;
1097 		if (hasTableEnd) {
1098 			dataSize -= kResourceInfoTableEndSize;
1099 			// checksum
1100 			uint32 checkSum = calculate_checksum(data, dataSize);
1101 			uint32 fileCheckSum = _GetUInt32(tableEnd->rite_check_sum);
1102 			if (checkSum != fileCheckSum) {
1103 				throw Exception("Invalid resource info table check sum: In "
1104 								"file: %lx, calculated: %lx.", fileCheckSum,
1105 								checkSum);
1106 			}
1107 		}
1108 	}
1109 	if (!hasTableEnd)
1110 		Warnings::AddCurrentWarning("resource info table has no check sum.");
1111 	return hasTableEnd;
1112 }
1113 
1114 // _ReadResourceInfo
1115 const void*
1116 ResourceFile::_ReadResourceInfo(const MemArea& area, const resource_info* info,
1117 								type_code type, bool* readIndices)
1118 {
1119 	int32 id = _GetInt32(info->ri_id);
1120 	int32 index = _GetInt32(info->ri_index);
1121 	uint16 nameSize = _GetUInt16(info->ri_name_size);
1122 	const char* name = info->ri_name;
1123 	// check the values
1124 	bool ignore = false;
1125 	// index
1126 	if (index < 1 || index > fResourceCount) {
1127 		Warnings::AddCurrentWarning("Invalid index field in resource "
1128 									"info table: %lu.", index);
1129 		ignore = true;
1130 	}
1131 	if (!ignore) {
1132 		if (readIndices[index - 1]) {
1133 			throw Exception("Multiple resource infos with the same index "
1134 							"field: %ld.", index);
1135 		}
1136 		readIndices[index - 1] = true;
1137 	}
1138 	// name size
1139 	if (!area.check(name, nameSize)) {
1140 		throw Exception("Invalid name size (%d) for index %ld in "
1141 						"resource info table.", (int)nameSize, index);
1142 	}
1143 	// check, if name is null terminated
1144 	if (name[nameSize - 1] != 0) {
1145 		Warnings::AddCurrentWarning("Name for index %ld in "
1146 									"resource info table is not null "
1147 									"terminated.", index);
1148 	}
1149 	// set the values
1150 	if (!ignore) {
1151 		BString resourceName(name, nameSize);
1152 		if (ResourceItem* item = ItemAt(index - 1))
1153 			item->SetIdentity(type, id, resourceName.String());
1154 		else {
1155 			throw Exception("Unexpected error: No resource item at index "
1156 							"%ld.", index);
1157 		}
1158 	}
1159 	return skip_bytes(name, nameSize);
1160 }
1161