1/*
2 * Copyright 2002-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7/*!
8	\file ResourceFile.cpp
9	ResourceFile implementation.
10*/
11
12
13#include <ResourceFile.h>
14
15#include <algorithm>
16#include <new>
17#include <stdio.h>
18
19#include <elf32.h>
20#include <elf64.h>
21#include <fatelf.h>
22
23#include <AutoDeleter.h>
24
25#include <Exception.h>
26#include <Pef.h>
27#include <ResourceItem.h>
28#include <ResourcesContainer.h>
29#include <ResourcesDefs.h>
30//#include <Warnings.h>
31
32
33namespace BPrivate {
34namespace Storage {
35
36
37// ELF defs
38static const uint32	kMaxELFHeaderSize
39	= std::max(sizeof(Elf32_Ehdr), sizeof(Elf64_Ehdr)) + 32;
40
41// sanity bounds
42static const uint32	kMaxResourceCount			= 10000;
43static const uint32	kELFMaxResourceAlignment	= 1024 * 1024 * 10;	// 10 MB
44
45
46// recognized file types (indices into kFileTypeNames)
47enum {
48	FILE_TYPE_UNKNOWN		= 0,
49	FILE_TYPE_X86_RESOURCE	= 1,
50	FILE_TYPE_PPC_RESOURCE	= 2,
51	FILE_TYPE_ELF			= 3,
52	FILE_TYPE_FATELF		= 4,
53	FILE_TYPE_PEF			= 5,
54	FILE_TYPE_EMPTY			= 6,
55};
56
57
58const char* kFileTypeNames[] = {
59	"unknown",
60	"x86 resource file",
61	"PPC resource file",
62	"ELF object file",
63	"FatELF object file",
64	"PEF object file",
65	"empty file",
66};
67
68
69// debugging
70//#define DBG(x) x
71#define DBG(x)
72#define OUT	printf
73
74
75// #pragma mark - helper functions/classes
76
77
78static void
79read_exactly(BPositionIO& file, off_t position, void* buffer, size_t size,
80	const char* errorMessage = NULL)
81{
82	ssize_t read = file.ReadAt(position, buffer, size);
83	if (read < 0)
84		throw Exception(read, errorMessage);
85	else if ((size_t)read != size) {
86		if (errorMessage) {
87			throw Exception("%s Read too few bytes (%ld/%lu).", errorMessage,
88							read, size);
89		} else
90			throw Exception("Read too few bytes (%ld/%lu).", read, size);
91	}
92}
93
94
95static void
96write_exactly(BPositionIO& file, off_t position, const void* buffer,
97	size_t size, const char* errorMessage = NULL)
98{
99	ssize_t written = file.WriteAt(position, buffer, size);
100	if (written < 0)
101		throw Exception(written, errorMessage);
102	else if ((size_t)written != size) {
103		if (errorMessage) {
104			throw Exception("%s Wrote too few bytes (%ld/%lu).", errorMessage,
105							written, size);
106		} else
107			throw Exception("Wrote too few bytes (%ld/%lu).", written, size);
108	}
109}
110
111
112template<typename TV, typename TA>
113static inline TV
114align_value(const TV& value, const TA& alignment)
115{
116	return ((value + alignment - 1) / alignment) * alignment;
117}
118
119
120static uint32
121calculate_checksum(const void* data, uint32 size)
122{
123	uint32 checkSum = 0;
124	const uint8* csData = (const uint8*)data;
125	const uint8* dataEnd = csData + size;
126	const uint8* current = csData;
127	for (; current < dataEnd; current += 4) {
128		uint32 word = 0;
129		int32 bytes = std::min((int32)4, (int32)(dataEnd - current));
130		for (int32 i = 0; i < bytes; i++)
131			word = (word << 8) + current[i];
132		checkSum += word;
133	}
134	return checkSum;
135}
136
137
138static inline const void*
139skip_bytes(const void* buffer, int32 offset)
140{
141	return (const char*)buffer + offset;
142}
143
144
145static inline void*
146skip_bytes(void* buffer, int32 offset)
147{
148	return (char*)buffer + offset;
149}
150
151
152static void
153fill_pattern(uint32 byteOffset, void* _buffer, uint32 count)
154{
155	uint32* buffer = (uint32*)_buffer;
156	for (uint32 i = 0; i < count; i++)
157		buffer[i] = kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3];
158}
159
160
161static void
162fill_pattern(const void* dataBegin, void* buffer, uint32 count)
163{
164	fill_pattern((char*)buffer - (const char*)dataBegin, buffer, count);
165}
166
167
168static void
169fill_pattern(const void* dataBegin, void* buffer, const void* bufferEnd)
170{
171	fill_pattern(dataBegin, buffer,
172				 ((const char*)bufferEnd - (char*)buffer) / 4);
173}
174
175
176static bool
177check_pattern(uint32 byteOffset, void* _buffer, uint32 count,
178	bool hostEndianess)
179{
180	bool result = true;
181	uint32* buffer = (uint32*)_buffer;
182	for (uint32 i = 0; result && i < count; i++) {
183		uint32 value = buffer[i];
184		if (!hostEndianess)
185			value = B_SWAP_INT32(value);
186		result
187			= (value == kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3]);
188	}
189	return result;
190}
191
192
193// #pragma mark -
194
195
196struct MemArea {
197	MemArea(const void* data, uint32 size) : data(data), size(size) {}
198
199	inline bool check(const void* _current, uint32 skip = 0) const
200	{
201		const char* start = (const char*)data;
202		const char* current = (const char*)_current;
203		return (start <= current && start + size >= current + skip);
204	}
205
206	const void*	data;
207	uint32		size;
208};
209
210
211struct resource_parse_info {
212	off_t				file_size;
213	int32				resource_count;
214	ResourcesContainer*	container;
215	char*				info_table;
216	uint32				info_table_offset;
217	uint32				info_table_size;
218};
219
220
221// #pragma mark -
222
223
224ResourceFile::ResourceFile()
225	:
226	fFile(),
227	fFileType(FILE_TYPE_UNKNOWN),
228	fHostEndianess(true),
229	fEmptyResources(true)
230{
231}
232
233
234ResourceFile::~ResourceFile()
235{
236	Unset();
237}
238
239
240status_t
241ResourceFile::SetTo(BFile* file, bool clobber)
242{
243	status_t error = (file ? B_OK : B_BAD_VALUE);
244	Unset();
245	if (error == B_OK) {
246		try {
247			_InitFile(*file, clobber);
248		} catch (Exception exception) {
249			Unset();
250			if (exception.Error() != B_OK)
251				error = exception.Error();
252			else
253				error = B_ERROR;
254		}
255	}
256	return error;
257}
258
259
260void
261ResourceFile::Unset()
262{
263	fFile.Unset();
264	fFileType = FILE_TYPE_UNKNOWN;
265	fHostEndianess = true;
266	fEmptyResources = true;
267}
268
269
270status_t
271ResourceFile::InitCheck() const
272{
273	return fFile.InitCheck();
274}
275
276
277status_t
278ResourceFile::InitContainer(ResourcesContainer& container)
279{
280	container.MakeEmpty();
281	status_t error = InitCheck();
282	if (error == B_OK && !fEmptyResources) {
283		resource_parse_info parseInfo;
284		parseInfo.file_size = 0;
285		parseInfo.resource_count = 0;
286		parseInfo.container = &container;
287		parseInfo.info_table = NULL;
288		parseInfo.info_table_offset = 0;
289		parseInfo.info_table_size = 0;
290		try {
291			// get the file size
292			error = fFile.GetSize(&parseInfo.file_size);
293			if (error != B_OK)
294				throw Exception(error, "Failed to get the file size.");
295			_ReadHeader(parseInfo);
296			_ReadIndex(parseInfo);
297			_ReadInfoTable(parseInfo);
298			container.SetModified(false);
299		} catch (Exception exception) {
300			if (exception.Error() != B_OK)
301				error = exception.Error();
302			else
303				error = B_ERROR;
304		}
305		delete[] parseInfo.info_table;
306	}
307	return error;
308}
309
310
311status_t
312ResourceFile::ReadResource(ResourceItem& resource, bool force)
313{
314	status_t error = InitCheck();
315	size_t size = resource.DataSize();
316	if (error == B_OK && (force || !resource.IsLoaded())) {
317		if (error == B_OK)
318			error = resource.SetSize(size);
319		void* data = NULL;
320		if (error == B_OK) {
321			data = resource.Data();
322			ssize_t bytesRead = fFile.ReadAt(resource.Offset(), data, size);
323			if (bytesRead < 0)
324				error = bytesRead;
325			else if ((size_t)bytesRead != size)
326				error = B_IO_ERROR;
327		}
328		if (error == B_OK) {
329			// convert the data, if necessary
330			if (!fHostEndianess)
331				swap_data(resource.Type(), data, size, B_SWAP_ALWAYS);
332			resource.SetLoaded(true);
333			resource.SetModified(false);
334		}
335	}
336	return error;
337}
338
339
340status_t
341ResourceFile::ReadResources(ResourcesContainer& container, bool force)
342{
343	status_t error = InitCheck();
344	int32 count = container.CountResources();
345	for (int32 i = 0; error == B_OK && i < count; i++) {
346		if (ResourceItem* resource = container.ResourceAt(i))
347			error = ReadResource(*resource, force);
348		else
349			error = B_ERROR;
350	}
351	return error;
352}
353
354
355status_t
356ResourceFile::WriteResources(ResourcesContainer& container)
357{
358	status_t error = InitCheck();
359	if (error == B_OK && !fFile.File()->IsWritable())
360		error = B_NOT_ALLOWED;
361	if (error == B_OK && fFileType == FILE_TYPE_EMPTY)
362		error = _MakeEmptyResourceFile();
363	if (error == B_OK)
364		error = _WriteResources(container);
365	if (error == B_OK)
366		fEmptyResources = false;
367	return error;
368}
369
370
371void
372ResourceFile::_InitFile(BFile& file, bool clobber)
373{
374	status_t error = B_OK;
375	fFile.Unset();
376	// get the file size first
377	off_t fileSize = 0;
378	error = file.GetSize(&fileSize);
379	if (error != B_OK)
380		throw Exception(error, "Failed to get the file size.");
381	// read the first four bytes, and check, if they identify a resource file
382	uint32 magic;
383	if (fileSize >= 4)
384		read_exactly(file, 0, &magic, 4, "Failed to read magic number.");
385	else if (fileSize > 0 && !clobber)
386		throw Exception(B_IO_ERROR, "File is not a resource file.");
387	if (fileSize == 0) {
388		// empty file
389		fHostEndianess = true;
390		fFileType = FILE_TYPE_EMPTY;
391		fFile.SetTo(&file, 0);
392		fEmptyResources = true;
393	} else if (!memcmp(&magic, kX86ResourceFileMagic, 4)) {
394		// x86 resource file
395		fHostEndianess = B_HOST_IS_LENDIAN;
396		fFileType = FILE_TYPE_X86_RESOURCE;
397		fFile.SetTo(&file, kX86ResourcesOffset);
398		fEmptyResources = false;
399	} else if (!memcmp(&magic, kPEFFileMagic1, 4)) {
400		PEFContainerHeader pefHeader;
401		read_exactly(file, 0, &pefHeader, kPEFContainerHeaderSize,
402			"Failed to read PEF container header.");
403		if (!memcmp(pefHeader.tag2, kPPCResourceFileMagic, 4)) {
404			// PPC resource file
405			fHostEndianess = B_HOST_IS_BENDIAN;
406			fFileType = FILE_TYPE_PPC_RESOURCE;
407			fFile.SetTo(&file, kPPCResourcesOffset);
408			fEmptyResources = false;
409		} else if (!memcmp(pefHeader.tag2, kPEFFileMagic2, 4)) {
410			// PEF file
411			fFileType = FILE_TYPE_PEF;
412			_InitPEFFile(file, pefHeader);
413		} else
414			throw Exception(B_IO_ERROR, "File is not a resource file.");
415	} else if (!memcmp(&magic, ELF_MAGIC, 4)) {
416		// ELF file
417		fFileType = FILE_TYPE_ELF;
418		_InitELFFile(file);
419	} else if (B_LENDIAN_TO_HOST_INT32(magic) == FATELF_MAGIC) {
420		fFileType = FILE_TYPE_FATELF;
421		_InitFatELFFile(file);
422	} else if (!memcmp(&magic, kX86ResourceFileMagic, 2)) {
423		// x86 resource file with screwed magic?
424//		Warnings::AddCurrentWarning("File magic is 0x%08lx. Should be 0x%08lx "
425//									"for x86 resource file. Try anyway.",
426//									ntohl(*(uint32*)magic),
427//									ntohl(*(uint32*)kX86ResourceFileMagic));
428		fHostEndianess = B_HOST_IS_LENDIAN;
429		fFileType = FILE_TYPE_X86_RESOURCE;
430		fFile.SetTo(&file, kX86ResourcesOffset);
431		fEmptyResources = true;
432	} else {
433		if (clobber) {
434			// make it an x86 resource file
435			fHostEndianess = true;
436			fFileType = FILE_TYPE_EMPTY;
437			fFile.SetTo(&file, 0);
438		} else
439			throw Exception(B_IO_ERROR, "File is not a resource file.");
440	}
441	error = fFile.InitCheck();
442	if (error != B_OK)
443		throw Exception(error, "Failed to initialize resource file.");
444	// clobber, if desired
445	if (clobber) {
446		// just write an empty resources container
447		ResourcesContainer container;
448		WriteResources(container);
449	}
450}
451
452
453void
454ResourceFile::_InitELFFile(BFile& file)
455{
456	status_t error = B_OK;
457
458	// get the file size
459	off_t fileSize = 0;
460	error = file.GetSize(&fileSize);
461	if (error != B_OK)
462		throw Exception(error, "Failed to get the file size.");
463
464	// read the ELF headers e_ident field
465	unsigned char identification[EI_NIDENT];
466	read_exactly(file, 0, identification, EI_NIDENT,
467		"Failed to read ELF identification.");
468
469	// check version
470	if (identification[EI_VERSION] != EV_CURRENT)
471		throw Exception(B_UNSUPPORTED, "Unsupported ELF version.");
472
473	// check data encoding (endianess)
474	switch (identification[EI_DATA]) {
475		case ELFDATA2LSB:
476			fHostEndianess = B_HOST_IS_LENDIAN;
477			break;
478		case ELFDATA2MSB:
479			fHostEndianess = B_HOST_IS_BENDIAN;
480			break;
481		default:
482		case ELFDATANONE:
483			throw Exception(B_UNSUPPORTED, "Unsupported ELF data encoding.");
484	}
485
486	// check class (32/64 bit) and call the respective method handling it
487	switch (identification[EI_CLASS]) {
488		case ELFCLASS32:
489			_InitELFXFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr>(file, fileSize);
490			break;
491		case ELFCLASS64:
492			_InitELFXFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr>(file, fileSize);
493			break;
494		default:
495			throw Exception(B_UNSUPPORTED, "Unsupported ELF class.");
496	}
497}
498
499
500template<typename ElfHeader, typename ElfProgramHeader,
501	typename ElfSectionHeader>
502void
503ResourceFile::_InitELFXFile(BFile& file, uint64 fileSize)
504{
505	// read ELF header
506	ElfHeader fileHeader;
507	read_exactly(file, 0, &fileHeader, sizeof(ElfHeader),
508		"Failed to read ELF header.");
509
510	// get the header values
511	uint32 headerSize				= _GetInt(fileHeader.e_ehsize);
512	uint64 programHeaderTableOffset	= _GetInt(fileHeader.e_phoff);
513	uint32 programHeaderSize		= _GetInt(fileHeader.e_phentsize);
514	uint32 programHeaderCount		= _GetInt(fileHeader.e_phnum);
515	uint64 sectionHeaderTableOffset	= _GetInt(fileHeader.e_shoff);
516	uint32 sectionHeaderSize		= _GetInt(fileHeader.e_shentsize);
517	uint32 sectionHeaderCount		= _GetInt(fileHeader.e_shnum);
518	bool hasProgramHeaderTable = (programHeaderTableOffset != 0);
519	bool hasSectionHeaderTable = (sectionHeaderTableOffset != 0);
520
521	// check the sanity of the header values
522	// ELF header size
523	if (headerSize < sizeof(ElfHeader) || headerSize > kMaxELFHeaderSize) {
524		throw Exception(B_IO_ERROR,
525			"Invalid ELF header: invalid ELF header size: %lu.", headerSize);
526	}
527	uint64 resourceOffset = headerSize;
528	uint64 resourceAlignment = 0;
529
530	// program header table offset and entry count/size
531	uint64 programHeaderTableSize = 0;
532	if (hasProgramHeaderTable) {
533		if (programHeaderTableOffset < headerSize
534			|| programHeaderTableOffset > fileSize) {
535			throw Exception(B_IO_ERROR, "Invalid ELF header: invalid program "
536				"header table offset: %lu.", programHeaderTableOffset);
537		}
538		programHeaderTableSize = (uint64)programHeaderSize * programHeaderCount;
539		if (programHeaderSize < sizeof(ElfProgramHeader)
540			|| programHeaderTableOffset + programHeaderTableSize > fileSize) {
541			throw Exception(B_IO_ERROR, "Invalid ELF header: program header "
542				"table exceeds file: %lu.",
543				programHeaderTableOffset + programHeaderTableSize);
544		}
545		resourceOffset = std::max(resourceOffset,
546			programHeaderTableOffset + programHeaderTableSize);
547
548		// load the program headers into memory
549		uint8* programHeaders = (uint8*)malloc(
550			programHeaderCount * programHeaderSize);
551		if (programHeaders == NULL)
552			throw Exception(B_NO_MEMORY);
553		MemoryDeleter programHeadersDeleter(programHeaders);
554
555		read_exactly(file, programHeaderTableOffset, programHeaders,
556			programHeaderCount * programHeaderSize,
557			"Failed to read ELF program headers.");
558
559		// iterate through the program headers
560		for (uint32 i = 0; i < programHeaderCount; i++) {
561			ElfProgramHeader& programHeader
562				= *(ElfProgramHeader*)(programHeaders + i * programHeaderSize);
563
564			// get the header values
565			uint32 type			= _GetInt(programHeader.p_type);
566			uint64 offset		= _GetInt(programHeader.p_offset);
567			uint64 size			= _GetInt(programHeader.p_filesz);
568			uint64 alignment	= _GetInt(programHeader.p_align);
569
570			// check the values
571			// PT_NULL marks the header unused,
572			if (type != PT_NULL) {
573				if (/*offset < headerSize ||*/ offset > fileSize) {
574					throw Exception(B_IO_ERROR, "Invalid ELF program header: "
575						"invalid program offset: %lu.", offset);
576				}
577				uint64 segmentEnd = offset + size;
578				if (segmentEnd > fileSize) {
579					throw Exception(B_IO_ERROR, "Invalid ELF section header: "
580						"segment exceeds file: %lu.", segmentEnd);
581				}
582				resourceOffset = std::max(resourceOffset, segmentEnd);
583				resourceAlignment = std::max(resourceAlignment, alignment);
584			}
585		}
586	}
587
588	// section header table offset and entry count/size
589	uint64 sectionHeaderTableSize = 0;
590	if (hasSectionHeaderTable) {
591		if (sectionHeaderTableOffset < headerSize
592			|| sectionHeaderTableOffset > fileSize) {
593			throw Exception(B_IO_ERROR, "Invalid ELF header: invalid section "
594				"header table offset: %lu.", sectionHeaderTableOffset);
595		}
596		sectionHeaderTableSize = (uint64)sectionHeaderSize * sectionHeaderCount;
597		if (sectionHeaderSize < sizeof(ElfSectionHeader)
598			|| sectionHeaderTableOffset + sectionHeaderTableSize > fileSize) {
599			throw Exception(B_IO_ERROR, "Invalid ELF header: section header "
600				"table exceeds file: %lu.",
601				sectionHeaderTableOffset + sectionHeaderTableSize);
602		}
603		resourceOffset = std::max(resourceOffset,
604			sectionHeaderTableOffset + sectionHeaderTableSize);
605
606		// load the section headers into memory
607		uint8* sectionHeaders = (uint8*)malloc(
608			sectionHeaderCount * sectionHeaderSize);
609		if (sectionHeaders == NULL)
610			throw Exception(B_NO_MEMORY);
611		MemoryDeleter sectionHeadersDeleter(sectionHeaders);
612
613		read_exactly(file, sectionHeaderTableOffset, sectionHeaders,
614			sectionHeaderCount * sectionHeaderSize,
615			"Failed to read ELF section headers.");
616
617		// iterate through the section headers
618		for (uint32 i = 0; i < sectionHeaderCount; i++) {
619			ElfSectionHeader& sectionHeader
620				= *(ElfSectionHeader*)(sectionHeaders + i * sectionHeaderSize);
621
622			// get the header values
623			uint32 type		= _GetInt(sectionHeader.sh_type);
624			uint64 offset	= _GetInt(sectionHeader.sh_offset);
625			uint64 size		= _GetInt(sectionHeader.sh_size);
626
627			// check the values
628			// SHT_NULL marks the header unused,
629			// SHT_NOBITS sections take no space in the file
630			if (type != SHT_NULL && type != SHT_NOBITS) {
631				if (offset < headerSize || offset > fileSize) {
632					throw Exception(B_IO_ERROR, "Invalid ELF section header: "
633						"invalid section offset: %lu.", offset);
634				}
635				uint64 sectionEnd = offset + size;
636				if (sectionEnd > fileSize) {
637					throw Exception(B_IO_ERROR, "Invalid ELF section header: "
638						"section exceeds file: %lu.", sectionEnd);
639				}
640				resourceOffset = std::max(resourceOffset, sectionEnd);
641			}
642		}
643	}
644
645	// align the offset
646	if (fileHeader.e_ident[EI_CLASS] == ELFCLASS64) {
647		// For ELF64 binaries we use a different alignment behaviour. It is
648		// not necessary to align the position of the resources in the file to
649		// the maximum value of p_align, and in fact on x86_64 this behaviour
650		// causes an undesirable effect: since the default segment alignment is
651		// 2MB, aligning to p_align causes all binaries to be at least 2MB when
652		// resources have been added. So, just align to an 8-byte boundary.
653		resourceAlignment = 8;
654	} else {
655		// Retain previous alignment behaviour for compatibility.
656		if (resourceAlignment < kELFMinResourceAlignment)
657			resourceAlignment = kELFMinResourceAlignment;
658		if (resourceAlignment > kELFMaxResourceAlignment) {
659			throw Exception(B_IO_ERROR, "The ELF object file requires an "
660				"invalid alignment: %lu.", resourceAlignment);
661		}
662	}
663
664	resourceOffset = align_value(resourceOffset, resourceAlignment);
665	if (resourceOffset >= fileSize) {
666//		throw Exception("The ELF object file does not contain resources.");
667		fEmptyResources = true;
668	} else
669		fEmptyResources = false;
670
671	// fine, init the offset file
672	fFile.SetTo(&file, resourceOffset);
673}
674
675
676void
677ResourceFile::_InitFatELFFile(BFile& file)
678{
679	// get the file size
680	off_t fileSize = 0;
681	status_t error = file.GetSize(&fileSize);
682	if (error != B_OK)
683		throw Exception(error, "Failed to get the file size.");
684
685	// read FatELF header
686	FATELF_header fileHeader;
687	read_exactly(file, 0, &fileHeader, sizeof(fileHeader),
688		"Failed to read FatELF header.");
689
690	// read FatELF records
691	FATELF_record* records = (FATELF_record*)malloc(
692		fileHeader.num_records * sizeof(FATELF_record));
693
694	if (records == NULL)
695		throw Exception(B_NO_MEMORY);
696	MemoryDeleter recordsDeleter(records);
697
698	read_exactly(file, sizeof(FATELF_header), records,
699		fileHeader.num_records * sizeof(FATELF_record),
700		"Failed to read FatELF records.");
701
702	// find furthest entry
703	uint64 resourceOffset = 0;
704	for (uint8 i = 0; i < fileHeader.num_records; i++) {
705		FATELF_record *record = records + i;
706		uint64 elfEnd = B_LENDIAN_TO_HOST_INT64(record->offset) +
707			B_LENDIAN_TO_HOST_INT64(record->size);
708		resourceOffset = std::max(resourceOffset, elfEnd);
709	}
710
711
712	resourceOffset = align_value(resourceOffset, kFatELFMinResourceAlignment);
713	if (resourceOffset >= (uint64) fileSize)
714		fEmptyResources = true;
715	else
716		fEmptyResources = false;
717
718	fFile.SetTo(&file, resourceOffset);
719}
720
721
722void
723ResourceFile::_InitPEFFile(BFile& file, const PEFContainerHeader& pefHeader)
724{
725	status_t error = B_OK;
726	// get the file size
727	off_t fileSize = 0;
728	error = file.GetSize(&fileSize);
729	if (error != B_OK)
730		throw Exception(error, "Failed to get the file size.");
731	// check architecture -- we support PPC only
732	if (memcmp(pefHeader.architecture, kPEFArchitecturePPC, 4))
733		throw Exception(B_IO_ERROR, "PEF file architecture is not PPC.");
734	fHostEndianess = B_HOST_IS_BENDIAN;
735	// get the section count
736	uint16 sectionCount = _GetInt(pefHeader.sectionCount);
737	// iterate through the PEF sections headers
738	uint32 sectionHeaderTableOffset = kPEFContainerHeaderSize;
739	uint32 sectionHeaderTableEnd
740		= sectionHeaderTableOffset + sectionCount * kPEFSectionHeaderSize;
741	uint32 resourceOffset = sectionHeaderTableEnd;
742	for (int32 i = 0; i < (int32)sectionCount; i++) {
743		uint32 shOffset = sectionHeaderTableOffset + i * kPEFSectionHeaderSize;
744		PEFSectionHeader sectionHeader;
745		read_exactly(file, shOffset, &sectionHeader, kPEFSectionHeaderSize,
746			"Failed to read PEF section header.");
747		// get the header values
748		uint32 offset	= _GetInt(sectionHeader.containerOffset);
749		uint32 size		= _GetInt(sectionHeader.packedSize);
750		// check the values
751		if (offset < sectionHeaderTableEnd || offset > fileSize) {
752			throw Exception(B_IO_ERROR, "Invalid PEF section header: invalid "
753				"section offset: %lu.", offset);
754		}
755		uint32 sectionEnd = offset + size;
756		if (sectionEnd > fileSize) {
757			throw Exception(B_IO_ERROR, "Invalid PEF section header: section "
758				"exceeds file: %lu.", sectionEnd);
759		}
760		resourceOffset = std::max(resourceOffset, sectionEnd);
761	}
762	if (resourceOffset >= fileSize) {
763//		throw Exception("The PEF object file does not contain resources.");
764		fEmptyResources = true;
765	} else
766		fEmptyResources = false;
767	// init the offset file
768	fFile.SetTo(&file, resourceOffset);
769}
770
771
772void
773ResourceFile::_ReadHeader(resource_parse_info& parseInfo)
774{
775	// read the header
776	resources_header header;
777	read_exactly(fFile, 0, &header, kResourcesHeaderSize,
778		"Failed to read the header.");
779	// check the header
780	// magic
781	uint32 magic = _GetInt(header.rh_resources_magic);
782	if (magic == kResourcesHeaderMagic) {
783		// everything is fine
784	} else if (B_SWAP_INT32(magic) == kResourcesHeaderMagic) {
785//		const char* endianessStr[2] = { "little", "big" };
786//		int32 endianess
787//			= (fHostEndianess == ((bool)B_HOST_IS_LENDIAN ? 0 : 1));
788//		Warnings::AddCurrentWarning("Endianess seems to be %s, although %s "
789//									"was expected.",
790//									endianessStr[1 - endianess],
791//									endianessStr[endianess]);
792		fHostEndianess = !fHostEndianess;
793	} else
794		throw Exception(B_IO_ERROR, "Invalid resources header magic.");
795	// resource count
796	uint32 resourceCount = _GetInt(header.rh_resource_count);
797	if (resourceCount > kMaxResourceCount)
798		throw Exception(B_IO_ERROR, "Bad number of resources.");
799	// index section offset
800	uint32 indexSectionOffset = _GetInt(header.rh_index_section_offset);
801	if (indexSectionOffset != kResourceIndexSectionOffset) {
802		throw Exception(B_IO_ERROR, "Unexpected resource index section "
803			"offset. Is: %lu, should be: %lu.", indexSectionOffset,
804			kResourceIndexSectionOffset);
805	}
806	// admin section size
807	uint32 indexSectionSize = kResourceIndexSectionHeaderSize
808							  + kResourceIndexEntrySize * resourceCount;
809	indexSectionSize = align_value(indexSectionSize,
810								   kResourceIndexSectionAlignment);
811	uint32 adminSectionSize = _GetInt(header.rh_admin_section_size);
812	if (adminSectionSize != indexSectionOffset + indexSectionSize) {
813		throw Exception(B_IO_ERROR, "Unexpected resource admin section size. "
814			"Is: %lu, should be: %lu.", adminSectionSize,
815			indexSectionOffset + indexSectionSize);
816	}
817	// set the resource count
818	parseInfo.resource_count = resourceCount;
819}
820
821
822void
823ResourceFile::_ReadIndex(resource_parse_info& parseInfo)
824{
825	int32& resourceCount = parseInfo.resource_count;
826	off_t& fileSize = parseInfo.file_size;
827	// read the header
828	resource_index_section_header header;
829	read_exactly(fFile, kResourceIndexSectionOffset, &header,
830		kResourceIndexSectionHeaderSize,
831		"Failed to read the resource index section header.");
832	// check the header
833	// index section offset
834	uint32 indexSectionOffset = _GetInt(header.rish_index_section_offset);
835	if (indexSectionOffset != kResourceIndexSectionOffset) {
836		throw Exception(B_IO_ERROR, "Unexpected resource index section "
837			"offset. Is: %lu, should be: %lu.", indexSectionOffset,
838			kResourceIndexSectionOffset);
839	}
840	// index section size
841	uint32 expectedIndexSectionSize = kResourceIndexSectionHeaderSize
842		+ kResourceIndexEntrySize * resourceCount;
843	expectedIndexSectionSize = align_value(expectedIndexSectionSize,
844										   kResourceIndexSectionAlignment);
845	uint32 indexSectionSize = _GetInt(header.rish_index_section_size);
846	if (indexSectionSize != expectedIndexSectionSize) {
847		throw Exception(B_IO_ERROR, "Unexpected resource index section size. "
848			"Is: %lu, should be: %lu.", indexSectionSize,
849			expectedIndexSectionSize);
850	}
851	// unknown section offset
852	uint32 unknownSectionOffset
853		= _GetInt(header.rish_unknown_section_offset);
854	if (unknownSectionOffset != indexSectionOffset + indexSectionSize) {
855		throw Exception(B_IO_ERROR, "Unexpected resource index section size. "
856			"Is: %lu, should be: %lu.", unknownSectionOffset,
857			indexSectionOffset + indexSectionSize);
858	}
859	// unknown section size
860	uint32 unknownSectionSize = _GetInt(header.rish_unknown_section_size);
861	if (unknownSectionSize != kUnknownResourceSectionSize) {
862		throw Exception(B_IO_ERROR, "Unexpected resource index section "
863			"offset. Is: %lu, should be: %lu.",
864			unknownSectionOffset, kUnknownResourceSectionSize);
865	}
866	// info table offset and size
867	uint32 infoTableOffset = _GetInt(header.rish_info_table_offset);
868	uint32 infoTableSize = _GetInt(header.rish_info_table_size);
869	if (infoTableOffset + infoTableSize > fileSize)
870		throw Exception(B_IO_ERROR, "Invalid info table location.");
871	parseInfo.info_table_offset = infoTableOffset;
872	parseInfo.info_table_size = infoTableSize;
873	// read the index entries
874	uint32 indexTableOffset = indexSectionOffset
875		+ kResourceIndexSectionHeaderSize;
876	int32 maxResourceCount = (unknownSectionOffset - indexTableOffset)
877		/ kResourceIndexEntrySize;
878	int32 actualResourceCount = 0;
879	bool tableEndReached = false;
880	for (int32 i = 0; !tableEndReached && i < maxResourceCount; i++) {
881		// read one entry
882		tableEndReached = !_ReadIndexEntry(parseInfo, i, indexTableOffset,
883			(i >= resourceCount));
884		if (!tableEndReached)
885			actualResourceCount++;
886	}
887	// check resource count
888	if (actualResourceCount != resourceCount) {
889		if (actualResourceCount > resourceCount) {
890//			Warnings::AddCurrentWarning("Resource index table contains "
891//										"%ld entries, although it should be "
892//										"%ld only.", actualResourceCount,
893//										resourceCount);
894		}
895		resourceCount = actualResourceCount;
896	}
897}
898
899
900bool
901ResourceFile::_ReadIndexEntry(resource_parse_info& parseInfo, int32 index,
902	uint32 tableOffset, bool peekAhead)
903{
904	off_t& fileSize = parseInfo.file_size;
905	//
906	bool result = true;
907	resource_index_entry entry;
908	// read one entry
909	off_t entryOffset = tableOffset + index * kResourceIndexEntrySize;
910	read_exactly(fFile, entryOffset, &entry, kResourceIndexEntrySize,
911		"Failed to read a resource index entry.");
912	// check, if the end is reached early
913	if (result && check_pattern(entryOffset, &entry,
914			kResourceIndexEntrySize / 4, fHostEndianess)) {
915		if (!peekAhead) {
916//			Warnings::AddCurrentWarning("Unexpected end of resource index "
917//										"table at index: %ld (/%ld).",
918//										index + 1, resourceCount);
919		}
920		result = false;
921	}
922	uint32 offset = _GetInt(entry.rie_offset);
923	uint32 size = _GetInt(entry.rie_size);
924	// check the location
925	if (result && offset + size > fileSize) {
926		if (peekAhead) {
927//			Warnings::AddCurrentWarning("Invalid data after resource index "
928//										"table.");
929		} else {
930			throw Exception(B_IO_ERROR, "Invalid resource index entry: index: "
931				"%ld, offset: %lu (%lx), size: %lu (%lx).", index + 1, offset,
932				offset, size, size);
933		}
934		result = false;
935	}
936	// add the entry
937	if (result) {
938		ResourceItem* item = new(std::nothrow) ResourceItem;
939		if (!item)
940			throw Exception(B_NO_MEMORY);
941		item->SetLocation(offset, size);
942		if (!parseInfo.container->AddResource(item, index, false)) {
943			delete item;
944			throw Exception(B_NO_MEMORY);
945		}
946	}
947	return result;
948}
949
950
951void
952ResourceFile::_ReadInfoTable(resource_parse_info& parseInfo)
953{
954	int32& resourceCount = parseInfo.resource_count;
955	// read the info table
956	// alloc memory for the table
957	char* tableData = new(std::nothrow) char[parseInfo.info_table_size];
958	if (!tableData)
959		throw Exception(B_NO_MEMORY);
960	int32 dataSize = parseInfo.info_table_size;
961	parseInfo.info_table = tableData;	// freed by the info owner
962	read_exactly(fFile, parseInfo.info_table_offset, tableData, dataSize,
963		"Failed to read resource info table.");
964	//
965	bool* readIndices = new(std::nothrow) bool[resourceCount + 1];
966		// + 1 => always > 0
967	if (!readIndices)
968		throw Exception(B_NO_MEMORY);
969	ArrayDeleter<bool> readIndicesDeleter(readIndices);
970	for (int32 i = 0; i < resourceCount; i++)
971		readIndices[i] = false;
972	MemArea area(tableData, dataSize);
973	const void* data = tableData;
974	// check the table end/check sum
975	if (_ReadInfoTableEnd(data, dataSize))
976		dataSize -= kResourceInfoTableEndSize;
977	// read the infos
978	int32 resourceIndex = 1;
979	uint32 minRemainderSize
980		= kMinResourceInfoBlockSize + kResourceInfoSeparatorSize;
981	while (area.check(data, minRemainderSize)) {
982		// read a resource block
983		if (!area.check(data, kMinResourceInfoBlockSize)) {
984			throw Exception(B_IO_ERROR, "Unexpected end of resource info "
985				"table at index %ld.", resourceIndex);
986		}
987		const resource_info_block* infoBlock
988			= (const resource_info_block*)data;
989		type_code type = _GetInt(infoBlock->rib_type);
990		// read the infos of this block
991		const resource_info* info = infoBlock->rib_info;
992		while (info) {
993			data = _ReadResourceInfo(parseInfo, area, info, type, readIndices);
994			// prepare for next iteration, if there is another info
995			if (!area.check(data, kResourceInfoSeparatorSize)) {
996				throw Exception(B_IO_ERROR, "Unexpected end of resource info "
997					"table after index %ld.", resourceIndex);
998			}
999			const resource_info_separator* separator
1000				= (const resource_info_separator*)data;
1001			if (_GetInt(separator->ris_value1) == 0xffffffff
1002				&& _GetInt(separator->ris_value2) == 0xffffffff) {
1003				// info block ends
1004				info = NULL;
1005				data = skip_bytes(data, kResourceInfoSeparatorSize);
1006			} else {
1007				// another info follows
1008				info = (const resource_info*)data;
1009			}
1010			resourceIndex++;
1011		}
1012		// end of the info block
1013	}
1014	// handle special case: empty resource info table
1015	if (resourceIndex == 1) {
1016		if (!area.check(data, kResourceInfoSeparatorSize)) {
1017			throw Exception(B_IO_ERROR, "Unexpected end of resource info "
1018				"table.");
1019		}
1020		const resource_info_separator* tableTerminator
1021			= (const resource_info_separator*)data;
1022		if (_GetInt(tableTerminator->ris_value1) != 0xffffffff
1023			|| _GetInt(tableTerminator->ris_value2) != 0xffffffff) {
1024			throw Exception(B_IO_ERROR, "The resource info table ought to be "
1025				"empty, but is not properly terminated.");
1026		}
1027		data = skip_bytes(data, kResourceInfoSeparatorSize);
1028	}
1029	// Check, if the correct number of bytes are remaining.
1030	uint32 bytesLeft = (const char*)tableData + dataSize - (const char*)data;
1031	if (bytesLeft != 0) {
1032		throw Exception(B_IO_ERROR, "Error at the end of the resource info "
1033			"table: %lu bytes are remaining.", bytesLeft);
1034	}
1035	// check, if all items have been initialized
1036	for (int32 i = resourceCount - 1; i >= 0; i--) {
1037		if (!readIndices[i]) {
1038//			Warnings::AddCurrentWarning("Resource item at index %ld "
1039//										"has no info. Item removed.", i + 1);
1040			if (ResourceItem* item = parseInfo.container->RemoveResource(i))
1041				delete item;
1042			resourceCount--;
1043		}
1044	}
1045}
1046
1047
1048bool
1049ResourceFile::_ReadInfoTableEnd(const void* data, int32 dataSize)
1050{
1051	bool hasTableEnd = true;
1052	if ((uint32)dataSize < kResourceInfoSeparatorSize)
1053		throw Exception(B_IO_ERROR, "Info table is too short.");
1054	if ((uint32)dataSize < kResourceInfoTableEndSize)
1055		hasTableEnd = false;
1056	if (hasTableEnd) {
1057		const resource_info_table_end* tableEnd
1058			= (const resource_info_table_end*)
1059			  skip_bytes(data, dataSize - kResourceInfoTableEndSize);
1060		if (_GetInt(tableEnd->rite_terminator) != 0)
1061			hasTableEnd = false;
1062		if (hasTableEnd) {
1063			dataSize -= kResourceInfoTableEndSize;
1064			// checksum
1065			uint32 checkSum = calculate_checksum(data, dataSize);
1066			uint32 fileCheckSum = _GetInt(tableEnd->rite_check_sum);
1067			if (checkSum != fileCheckSum) {
1068				throw Exception(B_IO_ERROR, "Invalid resource info table check"
1069					" sum: In file: %lx, calculated: %lx.", fileCheckSum,
1070					checkSum);
1071			}
1072		}
1073	}
1074//	if (!hasTableEnd)
1075//		Warnings::AddCurrentWarning("resource info table has no check sum.");
1076	return hasTableEnd;
1077}
1078
1079
1080const void*
1081ResourceFile::_ReadResourceInfo(resource_parse_info& parseInfo,
1082	const MemArea& area, const resource_info* info, type_code type,
1083	bool* readIndices)
1084{
1085	int32& resourceCount = parseInfo.resource_count;
1086	int32 id = _GetInt(info->ri_id);
1087	int32 index = _GetInt(info->ri_index);
1088	uint16 nameSize = _GetInt(info->ri_name_size);
1089	const char* name = info->ri_name;
1090	// check the values
1091	bool ignore = false;
1092	// index
1093	if (index < 1 || index > resourceCount) {
1094//		Warnings::AddCurrentWarning("Invalid index field in resource "
1095//									"info table: %lu.", index);
1096		ignore = true;
1097	}
1098	if (!ignore) {
1099		if (readIndices[index - 1]) {
1100			throw Exception(B_IO_ERROR, "Multiple resource infos with the "
1101				"same index field: %ld.", index);
1102		}
1103		readIndices[index - 1] = true;
1104	}
1105	// name size
1106	if (!area.check(name, nameSize)) {
1107		throw Exception(B_IO_ERROR, "Invalid name size (%d) for index %ld in "
1108			"resource info table.", (int)nameSize, index);
1109	}
1110	// check, if name is null terminated
1111	if (name[nameSize - 1] != 0) {
1112//		Warnings::AddCurrentWarning("Name for index %ld in "
1113//									"resource info table is not null "
1114//									"terminated.", index);
1115	}
1116	// set the values
1117	if (!ignore) {
1118		BString resourceName(name, nameSize);
1119		if (ResourceItem* item = parseInfo.container->ResourceAt(index - 1))
1120			item->SetIdentity(type, id, resourceName.String());
1121		else {
1122			throw Exception(B_IO_ERROR, "Unexpected error: No resource item "
1123				"at index %ld.", index);
1124		}
1125	}
1126	return skip_bytes(name, nameSize);
1127}
1128
1129
1130status_t
1131ResourceFile::_WriteResources(ResourcesContainer& container)
1132{
1133	status_t error = B_OK;
1134	int32 resourceCount = container.CountResources();
1135	char* buffer = NULL;
1136	try {
1137		// calculate sizes and offsets
1138		// header
1139		uint32 size = kResourcesHeaderSize;
1140		size_t bufferSize = size;
1141		// index section
1142		uint32 indexSectionOffset = size;
1143		uint32 indexSectionSize = kResourceIndexSectionHeaderSize
1144			+ resourceCount * kResourceIndexEntrySize;
1145		indexSectionSize = align_value(indexSectionSize,
1146			kResourceIndexSectionAlignment);
1147		size += indexSectionSize;
1148		bufferSize = std::max((uint32)bufferSize, indexSectionSize);
1149		// unknown section
1150		uint32 unknownSectionOffset = size;
1151		uint32 unknownSectionSize = kUnknownResourceSectionSize;
1152		size += unknownSectionSize;
1153		bufferSize = std::max((uint32)bufferSize, unknownSectionSize);
1154		// data
1155		uint32 dataOffset = size;
1156		uint32 dataSize = 0;
1157		for (int32 i = 0; i < resourceCount; i++) {
1158			ResourceItem* item = container.ResourceAt(i);
1159			if (!item->IsLoaded())
1160				throw Exception(B_IO_ERROR, "Resource is not loaded.");
1161			dataSize += item->DataSize();
1162			bufferSize = std::max(bufferSize, item->DataSize());
1163		}
1164		size += dataSize;
1165		// info table
1166		uint32 infoTableOffset = size;
1167		uint32 infoTableSize = 0;
1168		type_code type = 0;
1169		for (int32 i = 0; i < resourceCount; i++) {
1170			ResourceItem* item = container.ResourceAt(i);
1171			if (i == 0 || type != item->Type()) {
1172				if (i != 0)
1173					infoTableSize += kResourceInfoSeparatorSize;
1174				type = item->Type();
1175				infoTableSize += kMinResourceInfoBlockSize;
1176			} else
1177				infoTableSize += kMinResourceInfoSize;
1178
1179			const char* name = item->Name();
1180			if (name && name[0] != '\0')
1181				infoTableSize += strlen(name) + 1;
1182		}
1183		infoTableSize += kResourceInfoSeparatorSize
1184			+ kResourceInfoTableEndSize;
1185		size += infoTableSize;
1186		bufferSize = std::max((uint32)bufferSize, infoTableSize);
1187
1188		// write...
1189		// set the file size
1190		fFile.SetSize(size);
1191		buffer = new(std::nothrow) char[bufferSize];
1192		if (!buffer)
1193			throw Exception(B_NO_MEMORY);
1194		void* data = buffer;
1195		// header
1196		resources_header* resourcesHeader = (resources_header*)data;
1197		resourcesHeader->rh_resources_magic = kResourcesHeaderMagic;
1198		resourcesHeader->rh_resource_count = resourceCount;
1199		resourcesHeader->rh_index_section_offset = indexSectionOffset;
1200		resourcesHeader->rh_admin_section_size = indexSectionOffset
1201												 + indexSectionSize;
1202		for (int32 i = 0; i < 13; i++)
1203			resourcesHeader->rh_pad[i] = 0;
1204		write_exactly(fFile, 0, buffer, kResourcesHeaderSize,
1205			"Failed to write resources header.");
1206		// index section
1207		data = buffer;
1208		// header
1209		resource_index_section_header* indexHeader
1210			= (resource_index_section_header*)data;
1211		indexHeader->rish_index_section_offset = indexSectionOffset;
1212		indexHeader->rish_index_section_size = indexSectionSize;
1213		indexHeader->rish_unknown_section_offset = unknownSectionOffset;
1214		indexHeader->rish_unknown_section_size = unknownSectionSize;
1215		indexHeader->rish_info_table_offset = infoTableOffset;
1216		indexHeader->rish_info_table_size = infoTableSize;
1217		fill_pattern(buffer - indexSectionOffset,
1218			&indexHeader->rish_unused_data1, 1);
1219		fill_pattern(buffer - indexSectionOffset,
1220			indexHeader->rish_unused_data2, 25);
1221		fill_pattern(buffer - indexSectionOffset,
1222			&indexHeader->rish_unused_data3, 1);
1223		// index table
1224		data = skip_bytes(data, kResourceIndexSectionHeaderSize);
1225		resource_index_entry* entry = (resource_index_entry*)data;
1226		uint32 entryOffset = dataOffset;
1227		for (int32 i = 0; i < resourceCount; i++, entry++) {
1228			ResourceItem* item = container.ResourceAt(i);
1229			uint32 entrySize = item->DataSize();
1230			entry->rie_offset = entryOffset;
1231			entry->rie_size = entrySize;
1232			entry->rie_pad = 0;
1233			entryOffset += entrySize;
1234		}
1235		fill_pattern(buffer - indexSectionOffset, entry,
1236			buffer + indexSectionSize);
1237		write_exactly(fFile, indexSectionOffset, buffer, indexSectionSize,
1238			"Failed to write index section.");
1239		// unknown section
1240		fill_pattern(unknownSectionOffset, buffer, unknownSectionSize / 4);
1241		write_exactly(fFile, unknownSectionOffset, buffer, unknownSectionSize,
1242			"Failed to write unknown section.");
1243		// data
1244		uint32 itemOffset = dataOffset;
1245		for (int32 i = 0; i < resourceCount; i++) {
1246			data = buffer;
1247			ResourceItem* item = container.ResourceAt(i);
1248			const void* itemData = item->Data();
1249			uint32 itemSize = item->DataSize();
1250			if (!itemData && itemSize > 0)
1251				throw Exception(error, "Invalid resource item data.");
1252			if (itemData) {
1253				// swap data, if necessary
1254				if (!fHostEndianess) {
1255					memcpy(data, itemData, itemSize);
1256					swap_data(item->Type(), data, itemSize, B_SWAP_ALWAYS);
1257					itemData = data;
1258				}
1259				write_exactly(fFile, itemOffset, itemData, itemSize,
1260					"Failed to write resource item data.");
1261			}
1262			item->SetOffset(itemOffset);
1263			itemOffset += itemSize;
1264		}
1265		// info table
1266		data = buffer;
1267		type = 0;
1268		for (int32 i = 0; i < resourceCount; i++) {
1269			ResourceItem* item = container.ResourceAt(i);
1270			resource_info* info = NULL;
1271			if (i == 0 || type != item->Type()) {
1272				if (i != 0) {
1273					resource_info_separator* separator
1274						= (resource_info_separator*)data;
1275					separator->ris_value1 = 0xffffffff;
1276					separator->ris_value2 = 0xffffffff;
1277					data = skip_bytes(data, kResourceInfoSeparatorSize);
1278				}
1279				type = item->Type();
1280				resource_info_block* infoBlock = (resource_info_block*)data;
1281				infoBlock->rib_type = type;
1282				info = infoBlock->rib_info;
1283			} else
1284				info = (resource_info*)data;
1285			// info
1286			info->ri_id = item->ID();
1287			info->ri_index = i + 1;
1288			info->ri_name_size = 0;
1289			data = info->ri_name;
1290
1291			const char* name = item->Name();
1292			if (name && name[0] != '\0') {
1293				uint32 nameLen = strlen(name);
1294				memcpy(info->ri_name, name, nameLen + 1);
1295				data = skip_bytes(data, nameLen + 1);
1296				info->ri_name_size = nameLen + 1;
1297			}
1298		}
1299		// separator
1300		resource_info_separator* separator = (resource_info_separator*)data;
1301		separator->ris_value1 = 0xffffffff;
1302		separator->ris_value2 = 0xffffffff;
1303		// table end
1304		data = skip_bytes(data, kResourceInfoSeparatorSize);
1305		resource_info_table_end* tableEnd = (resource_info_table_end*)data;
1306		tableEnd->rite_check_sum = calculate_checksum(buffer,
1307			infoTableSize - kResourceInfoTableEndSize);
1308		tableEnd->rite_terminator = 0;
1309		write_exactly(fFile, infoTableOffset, buffer, infoTableSize,
1310			"Failed to write info table.");
1311	} catch (Exception exception) {
1312		if (exception.Error() != B_OK)
1313			error = exception.Error();
1314		else
1315			error = B_ERROR;
1316	}
1317	delete[] buffer;
1318	return error;
1319}
1320
1321
1322status_t
1323ResourceFile::_MakeEmptyResourceFile()
1324{
1325	status_t error = fFile.InitCheck();
1326	if (error == B_OK && !fFile.File()->IsWritable())
1327		error = B_NOT_ALLOWED;
1328	if (error == B_OK) {
1329		try {
1330			BFile* file = fFile.File();
1331			// make it an x86 resource file
1332			error = file->SetSize(4);
1333			if (error != B_OK)
1334				throw Exception(error, "Failed to set file size.");
1335			write_exactly(*file, 0, kX86ResourceFileMagic, 4,
1336				"Failed to write magic number.");
1337			fHostEndianess = B_HOST_IS_LENDIAN;
1338			fFileType = FILE_TYPE_X86_RESOURCE;
1339			fFile.SetTo(file, kX86ResourcesOffset);
1340			fEmptyResources = true;
1341		} catch (Exception exception) {
1342			if (exception.Error() != B_OK)
1343				error = exception.Error();
1344			else
1345				error = B_ERROR;
1346		}
1347	}
1348	return error;
1349}
1350
1351
1352};	// namespace Storage
1353};	// namespace BPrivate
1354