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