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