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
16static const uint32	kMaxELFHeaderSize			= sizeof(Elf32_Ehdr) + 32;
17static const char	kELFFileMagic[4]			= { 0x7f, 'E', 'L', 'F' };
18
19// sanity bounds
20static const uint32	kMaxResourceCount			= 10000;
21static const uint32	kELFMaxResourceAlignment	= 1024 * 1024 * 10;	// 10 MB
22
23// recognized file types (indices into kFileTypeNames)
24enum {
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
32const 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
44static
45void
46read_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
62template<typename TV, typename TA>
63static inline
64TV
65align_value(const TV& value, const TA& alignment)
66{
67	return ((value + alignment - 1) / alignment) * alignment;
68}
69
70// calculate_checksum
71static
72uint32
73calculate_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
90static inline
91const void*
92skip_bytes(const void* buffer, int32 offset)
93{
94	return (const char*)buffer + offset;
95}
96
97// skip_bytes
98static inline
99void*
100skip_bytes(void* buffer, int32 offset)
101{
102	return (char*)buffer + offset;
103}
104
105// fill_pattern
106static
107void
108fill_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
116static
117void
118fill_pattern(const void* dataBegin, void* buffer, uint32 count)
119{
120	fill_pattern((char*)buffer - (const char*)dataBegin, buffer, count);
121}
122
123// fill_pattern
124static
125void
126fill_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
133static
134bool
135check_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
151struct 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
166template<typename C>
167struct 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
186ResourceFile::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
198ResourceFile::~ResourceFile()
199{
200	Unset();
201}
202
203// Init
204void
205ResourceFile::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
220void
221ResourceFile::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
240status_t
241ResourceFile::InitCheck() const
242{
243	return fFile.InitCheck();
244}
245
246// AddItem
247bool
248ResourceFile::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
260ResourceItem*
261ResourceFile::RemoveItem(int32 index)
262{
263	return (ResourceItem*)fItems.RemoveItem(index);
264}
265
266// RemoveItem
267bool
268ResourceFile::RemoveItem(ResourceItem* item)
269{
270	return RemoveItem(IndexOf(item));
271}
272
273// IndexOf
274int32
275ResourceFile::IndexOf(ResourceItem* item) const
276{
277	return fItems.IndexOf(item);
278}
279
280// ItemAt
281ResourceItem*
282ResourceFile::ItemAt(int32 index) const
283{
284	return (ResourceItem*)fItems.ItemAt(index);
285}
286
287// CountItems
288int32
289ResourceFile::CountItems() const
290{
291	return fItems.CountItems();
292}
293
294// GetResourcesSize
295uint32
296ResourceFile::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
339uint32
340ResourceFile::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
492void
493ResourceFile::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 %lld.", 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								"%lld): %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
528void
529ResourceFile::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 (%lld)\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
566void
567ResourceFile::_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
620void
621ResourceFile::_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
780void
781ResourceFile::_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
825void
826ResourceFile::_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
875void
876ResourceFile::_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
951bool
952ResourceFile::_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
994void
995ResourceFile::_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
1083bool
1084ResourceFile::_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
1115const void*
1116ResourceFile::_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