2 * Copyright 2002-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
9 ResourceFile implementation.
13 #include <ResourceFile.h>
19 #include <AutoDeleter.h>
22 #include <Exception.h>
24 #include <ResourceItem.h>
25 #include <ResourcesContainer.h>
26 #include <ResourcesDefs.h>
27 //#include <Warnings.h>
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' };
40 static const uint32 kMaxResourceCount
= 10000;
41 static const uint32 kELFMaxResourceAlignment
= 1024 * 1024 * 10; // 10 MB
44 // recognized file types (indices into kFileTypeNames)
46 FILE_TYPE_UNKNOWN
= 0,
47 FILE_TYPE_X86_RESOURCE
= 1,
48 FILE_TYPE_PPC_RESOURCE
= 2,
55 const char* kFileTypeNames
[] = {
70 #define B_VERSION_INFO_TYPE 'APPV'
72 static const uint32 kVersionInfoIntCount
= 5;
74 // #pragma mark - helper functions/classes
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
);
83 throw Exception(read
, errorMessage
);
84 else if ((size_t)read
!= size
) {
86 throw Exception("%s Read too few bytes (%ld/%lu).", errorMessage
,
89 throw Exception("Read too few bytes (%ld/%lu).", read
, size
);
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
);
100 throw Exception(written
, errorMessage
);
101 else if ((size_t)written
!= size
) {
103 throw Exception("%s Wrote too few bytes (%ld/%lu).", errorMessage
,
106 throw Exception("Wrote too few bytes (%ld/%lu).", written
, size
);
111 template<typename TV
, typename TA
>
113 align_value(const TV
& value
, const TA
& alignment
)
115 return ((value
+ alignment
- 1) / alignment
) * alignment
;
120 calculate_checksum(const void* data
, uint32 size
)
123 const uint8
* csData
= (const uint8
*)data
;
124 const uint8
* dataEnd
= csData
+ size
;
125 const uint8
* current
= csData
;
126 for (; current
< dataEnd
; current
+= 4) {
128 int32 bytes
= std::min((int32
)4, (int32
)(dataEnd
- current
));
129 for (int32 i
= 0; i
< bytes
; i
++)
130 word
= (word
<< 8) + current
[i
];
137 static inline const void*
138 skip_bytes(const void* buffer
, int32 offset
)
140 return (const char*)buffer
+ offset
;
145 skip_bytes(void* buffer
, int32 offset
)
147 return (char*)buffer
+ offset
;
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];
161 fill_pattern(const void* dataBegin
, void* buffer
, uint32 count
)
163 fill_pattern((char*)buffer
- (const char*)dataBegin
, buffer
, count
);
168 fill_pattern(const void* dataBegin
, void* buffer
, const void* bufferEnd
)
170 fill_pattern(dataBegin
, buffer
,
171 ((const char*)bufferEnd
- (char*)buffer
) / 4);
176 check_pattern(uint32 byteOffset
, void* _buffer
, uint32 count
,
180 uint32
* buffer
= (uint32
*)_buffer
;
181 for (uint32 i
= 0; result
&& i
< count
; i
++) {
182 uint32 value
= buffer
[i
];
184 value
= B_SWAP_INT32(value
);
186 = (value
== kUnusedResourceDataPattern
[(byteOffset
/ 4 + i
) % 3]);
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
);
210 struct resource_parse_info
{
212 int32 resource_count
;
213 ResourcesContainer
* container
;
215 uint32 info_table_offset
;
216 uint32 info_table_size
;
223 ResourceFile::ResourceFile()
226 fFileType(FILE_TYPE_UNKNOWN
),
227 fHostEndianess(true),
228 fEmptyResources(true)
233 ResourceFile::~ResourceFile()
240 ResourceFile::SetTo(BFile
* file
, bool clobber
)
242 status_t error
= (file
? B_OK
: B_BAD_VALUE
);
246 _InitFile(*file
, clobber
);
247 } catch (Exception exception
) {
249 if (exception
.Error() != B_OK
)
250 error
= exception
.Error();
260 ResourceFile::Unset()
263 fFileType
= FILE_TYPE_UNKNOWN
;
264 fHostEndianess
= true;
265 fEmptyResources
= true;
270 ResourceFile::InitCheck() const
272 return fFile
.InitCheck();
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;
291 error
= fFile
.GetSize(&parseInfo
.file_size
);
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();
304 delete[] parseInfo
.info_table
;
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())) {
317 error
= resource
.SetSize(size
);
320 data
= resource
.Data();
321 ssize_t bytesRead
= fFile
.ReadAt(resource
.Offset(), data
, size
);
324 else if ((size_t)bytesRead
!= size
)
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
),
336 swap_data(resource
.Type(), data
, size
, B_SWAP_ALWAYS
);
338 resource
.SetLoaded(true);
339 resource
.SetModified(false);
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
);
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();
370 error
= _WriteResources(container
);
372 fEmptyResources
= false;
378 ResourceFile::_InitFile(BFile
& file
, bool clobber
)
380 status_t error
= B_OK
;
382 // get the file size first
384 error
= file
.GetSize(&fileSize
);
386 throw Exception(error
, "Failed to get the file size.");
387 // read the first four bytes, and check, if they identify a resource file
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.");
395 fHostEndianess
= true;
396 fFileType
= FILE_TYPE_EMPTY
;
397 fFile
.SetTo(&file
, 0);
398 fEmptyResources
= true;
399 } else if (!memcmp(magic
, kX86ResourceFileMagic
, 4)) {
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)) {
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)) {
417 fFileType
= FILE_TYPE_PEF
;
418 _InitPEFFile(file
, pefHeader
);
420 throw Exception(B_IO_ERROR
, "File is not a resource file.");
421 } else if (!memcmp(magic
, kELFFileMagic
, 4)) {
423 fFileType
= FILE_TYPE_ELF
;
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;
437 // make it an x86 resource file
438 fHostEndianess
= true;
439 fFileType
= FILE_TYPE_EMPTY
;
440 fFile
.SetTo(&file
, 0);
442 throw Exception(B_IO_ERROR
, "File is not a resource file.");
444 error
= fFile
.InitCheck();
446 throw Exception(error
, "Failed to initialize resource file.");
447 // clobber, if desired
449 // just write an empty resources container
450 ResourcesContainer container
;
451 WriteResources(container
);
457 ResourceFile::_InitELFFile(BFile
& file
)
459 status_t error
= B_OK
;
463 error
= file
.GetSize(&fileSize
);
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.");
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
]) {
479 fHostEndianess
= B_HOST_IS_LENDIAN
;
482 fHostEndianess
= B_HOST_IS_BENDIAN
;
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
]) {
492 _InitELFXFile
<Elf32_Ehdr
, Elf32_Phdr
, Elf32_Shdr
>(file
, fileSize
);
495 _InitELFXFile
<Elf64_Ehdr
, Elf64_Phdr
, Elf64_Shdr
>(file
, fileSize
);
498 throw Exception(B_UNSUPPORTED
, "Unsupported ELF class.");
503 template<typename ElfHeader
, typename ElfProgramHeader
,
504 typename ElfSectionHeader
>
506 ResourceFile::_InitELFXFile(BFile
& file
, uint64 fileSize
)
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
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
);
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
);
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
);
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;
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;
672 fEmptyResources
= false;
674 // fine, init the offset file
675 fFile
.SetTo(&file
, resourceOffset
);
680 ResourceFile::_InitPEFFile(BFile
& file
, const PEFContainerHeader
& pefHeader
)
682 status_t error
= B_OK
;
685 error
= file
.GetSize(&fileSize
);
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
, §ionHeader
, 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
);
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;
723 fEmptyResources
= false;
724 // init the offset file
725 fFile
.SetTo(&file
, resourceOffset
);
730 ResourceFile::_ReadHeader(resource_parse_info
& parseInfo
)
733 resources_header header
;
734 read_exactly(fFile
, 0, &header
, kResourcesHeaderSize
,
735 "Failed to read the header.");
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" };
744 // = (fHostEndianess == ((bool)B_HOST_IS_LENDIAN ? 0 : 1));
745 // Warnings::AddCurrentWarning("Endianess seems to be %s, although %s "
747 // endianessStr[1 - endianess],
748 // endianessStr[endianess]);
749 fHostEndianess
= !fHostEndianess
;
751 throw Exception(B_IO_ERROR
, "Invalid resources header magic.");
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
;
780 ResourceFile::_ReadIndex(resource_parse_info
& parseInfo
)
782 int32
& resourceCount
= parseInfo
.resource_count
;
783 off_t
& fileSize
= parseInfo
.file_size
;
785 resource_index_section_header header
;
786 read_exactly(fFile
, kResourceIndexSectionOffset
, &header
,
787 kResourceIndexSectionHeaderSize
,
788 "Failed to read the resource index section 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
++) {
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,
852 resourceCount
= actualResourceCount
;
858 ResourceFile::_ReadIndexEntry(resource_parse_info
& parseInfo
, int32 index
,
859 uint32 tableOffset
, bool peekAhead
)
861 off_t
& fileSize
= parseInfo
.file_size
;
864 resource_index_entry 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
)) {
873 // Warnings::AddCurrentWarning("Unexpected end of resource index "
874 // "table at index: %ld (/%ld).",
875 // index + 1, resourceCount);
879 uint32 offset
= _GetInt(entry
.rie_offset
);
880 uint32 size
= _GetInt(entry
.rie_size
);
881 // check the location
882 if (result
&& offset
+ size
> fileSize
) {
884 // Warnings::AddCurrentWarning("Invalid data after resource index "
887 throw Exception(B_IO_ERROR
, "Invalid resource index entry: index: "
888 "%ld, offset: %lu (%lx), size: %lu (%lx).", index
+ 1, offset
,
895 ResourceItem
* item
= new(std::nothrow
) ResourceItem
;
897 throw Exception(B_NO_MEMORY
);
898 item
->SetLocation(offset
, size
);
899 if (!parseInfo
.container
->AddResource(item
, index
, false)) {
901 throw Exception(B_NO_MEMORY
);
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
];
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];
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
;
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
;
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) {
962 data
= skip_bytes(data
, kResourceInfoSeparatorSize
);
964 // another info follows
965 info
= (const resource_info
*)data
;
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 "
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
))
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;
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;
1020 dataSize
-= kResourceInfoTableEndSize
;
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
,
1031 // if (!hasTableEnd)
1032 // Warnings::AddCurrentWarning("resource info table has no check sum.");
1038 ResourceFile::_ReadResourceInfo(resource_parse_info
& parseInfo
,
1039 const MemArea
& area
, const resource_info
* info
, type_code type
,
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
;
1048 bool ignore
= false;
1050 if (index
< 1 || index
> resourceCount
) {
1051 // Warnings::AddCurrentWarning("Invalid index field in resource "
1052 // "info table: %lu.", index);
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;
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);
1075 BString
resourceName(name
, nameSize
);
1076 if (ResourceItem
* item
= parseInfo
.container
->ResourceAt(index
- 1))
1077 item
->SetIdentity(type
, id
, resourceName
.String());
1079 throw Exception(B_IO_ERROR
, "Unexpected error: No resource item "
1080 "at index %ld.", index
);
1083 return skip_bytes(name
, nameSize
);
1088 ResourceFile::_WriteResources(ResourcesContainer
& container
)
1090 status_t error
= B_OK
;
1091 int32 resourceCount
= container
.CountResources();
1092 char* buffer
= NULL
;
1094 // calculate sizes and offsets
1096 uint32 size
= kResourcesHeaderSize
;
1097 size_t bufferSize
= size
;
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
);
1107 uint32 unknownSectionOffset
= size
;
1108 uint32 unknownSectionSize
= kUnknownResourceSectionSize
;
1109 size
+= unknownSectionSize
;
1110 bufferSize
= std::max((uint32
)bufferSize
, unknownSectionSize
);
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());
1123 uint32 infoTableOffset
= size
;
1124 uint32 infoTableSize
= 0;
1126 for (int32 i
= 0; i
< resourceCount
; i
++) {
1127 ResourceItem
* item
= container
.ResourceAt(i
);
1128 if (i
== 0 || type
!= item
->Type()) {
1130 infoTableSize
+= kResourceInfoSeparatorSize
;
1131 type
= item
->Type();
1132 infoTableSize
+= kMinResourceInfoBlockSize
;
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
);
1146 // set the file size
1147 fFile
.SetSize(size
);
1148 buffer
= new(std::nothrow
) char[bufferSize
];
1150 throw Exception(B_NO_MEMORY
);
1151 void* data
= buffer
;
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
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.");
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);
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
;
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.");
1197 fill_pattern(unknownSectionOffset
, buffer
, unknownSectionSize
/ 4);
1198 write_exactly(fFile
, unknownSectionOffset
, buffer
, unknownSectionSize
,
1199 "Failed to write unknown section.");
1201 uint32 itemOffset
= dataOffset
;
1202 for (int32 i
= 0; i
< resourceCount
; i
++) {
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.");
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
),
1220 swap_data(item
->Type(), data
, itemSize
, B_SWAP_ALWAYS
);
1223 write_exactly(fFile
, itemOffset
, itemData
, itemSize
,
1224 "Failed to write resource item data.");
1226 item
->SetOffset(itemOffset
);
1227 itemOffset
+= itemSize
;
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()) {
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
;
1248 info
= (resource_info
*)data
;
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;
1264 resource_info_separator
* separator
= (resource_info_separator
*)data
;
1265 separator
->ris_value1
= 0xffffffff;
1266 separator
->ris_value2
= 0xffffffff;
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();
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
) {
1294 BFile
* file
= fFile
.File();
1295 // make it an x86 resource file
1296 error
= file
->SetSize(4);
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();
1316 }; // namespace Storage
1317 }; // namespace BPrivate