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