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