#include "ResourceFile.h"
#include <algobase.h>
#include <stdio.h>
#include "Elf.h"
#include "Exception.h"
#include "Pef.h"
#include "ResourceItem.h"
#include "ResourcesDefs.h"
#include "Warnings.h"
static const uint32 kMaxELFHeaderSize = sizeof(Elf32_Ehdr) + 32;
static const char kELFFileMagic[4] = { 0x7f, 'E', 'L', 'F' };
static const uint32 kMaxResourceCount = 10000;
static const uint32 kELFMaxResourceAlignment = 1024 * 1024 * 10;
enum {
FILE_TYPE_UNKNOWN = 0,
FILE_TYPE_X86_RESOURCE = 1,
FILE_TYPE_PPC_RESOURCE = 2,
FILE_TYPE_ELF = 3,
FILE_TYPE_PEF = 4,
};
const char* kFileTypeNames[] = {
"unknown",
"x86 resource file",
"PPC resource file",
"ELF object file",
"PEF object file",
};
static
void
read_exactly(BPositionIO& file, off_t position, void* buffer, size_t size,
const char* errorMessage = NULL)
{
ssize_t read = file.ReadAt(position, buffer, size);
if (read < 0)
throw Exception(read, errorMessage);
else if ((size_t)read != size) {
if (errorMessage) {
throw Exception("%s Read to few bytes (%ld/%lu).", errorMessage,
read, size);
} else
throw Exception("Read to few bytes (%ld/%lu).", read, size);
}
}
template<typename TV, typename TA>
static inline
TV
align_value(const TV& value, const TA& alignment)
{
return ((value + alignment - 1) / alignment) * alignment;
}
static
uint32
calculate_checksum(const void* data, uint32 size)
{
uint32 checkSum = 0;
const uint8* csData = (const uint8*)data;
const uint8* dataEnd = csData + size;
const uint8* current = csData;
for (; current < dataEnd; current += 4) {
uint32 word = 0;
int32 bytes = min(4L, dataEnd - current);
for (int32 i = 0; i < bytes; i++)
word = (word << 8) + current[i];
checkSum += word;
}
return checkSum;
}
static inline
const void*
skip_bytes(const void* buffer, int32 offset)
{
return (const char*)buffer + offset;
}
static inline
void*
skip_bytes(void* buffer, int32 offset)
{
return (char*)buffer + offset;
}
static
void
fill_pattern(uint32 byteOffset, void* _buffer, uint32 count)
{
uint32* buffer = (uint32*)_buffer;
for (uint32 i = 0; i < count; i++)
buffer[i] = kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3];
}
static
void
fill_pattern(const void* dataBegin, void* buffer, uint32 count)
{
fill_pattern((char*)buffer - (const char*)dataBegin, buffer, count);
}
static
void
fill_pattern(const void* dataBegin, void* buffer, const void* bufferEnd)
{
fill_pattern(dataBegin, buffer,
((const char*)bufferEnd - (char*)buffer) / 4);
}
static
bool
check_pattern(uint32 byteOffset, void* _buffer, uint32 count,
bool hostEndianess)
{
bool result = true;
uint32* buffer = (uint32*)_buffer;
for (uint32 i = 0; result && i < count; i++) {
uint32 value = buffer[i];
if (!hostEndianess)
value = B_SWAP_INT32(value);
result
= (value == kUnusedResourceDataPattern[(byteOffset / 4 + i) % 3]);
}
return result;
}
struct MemArea {
MemArea(const void* data, uint32 size) : data(data), size(size) {}
inline bool check(const void* _current, uint32 skip = 0) const
{
const char* start = (const char*)data;
const char* current = (const char*)_current;
return (start <= current && start + size >= current + skip);
}
const void* data;
uint32 size;
};
template<typename C>
struct AutoDeleter {
AutoDeleter(C* object, bool array = false) : object(object), array(array)
{
}
~AutoDeleter()
{
if (array)
delete[] object;
else
delete object;
}
C* object;
bool array;
};
ResourceFile::ResourceFile()
: fItems(),
fFile(),
fFileType(FILE_TYPE_UNKNOWN),
fFileSize(0),
fResourceCount(0),
fInfoTableItem(NULL),
fHostEndianess(true)
{
}
ResourceFile::~ResourceFile()
{
Unset();
}
void
ResourceFile::Init(BFile& file)
{
Unset();
try {
_InitFile(file);
_ReadHeader();
_ReadIndex();
_ReadInfoTable();
} catch (Exception exception) {
Unset();
throw exception;
}
}
void
ResourceFile::Unset()
{
for (int32 i = 0; ResourceItem* item = ItemAt(i); i++)
delete item;
fItems.MakeEmpty();
fFile.Unset();
fFileType = FILE_TYPE_UNKNOWN;
fFileSize = 0;
fResourceCount = 0;
delete fInfoTableItem;
fInfoTableItem = NULL;
fHostEndianess = true;
}
status_t
ResourceFile::InitCheck() const
{
return fFile.InitCheck();
}
bool
ResourceFile::AddItem(ResourceItem* item, int32 index)
{
bool result = false;
if (item) {
if (index < 0 || index > CountItems())
index = CountItems();
result = fItems.AddItem(item);
}
return result;
}
ResourceItem*
ResourceFile::RemoveItem(int32 index)
{
return (ResourceItem*)fItems.RemoveItem(index);
}
bool
ResourceFile::RemoveItem(ResourceItem* item)
{
return RemoveItem(IndexOf(item));
}
int32
ResourceFile::IndexOf(ResourceItem* item) const
{
return fItems.IndexOf(item);
}
ResourceItem*
ResourceFile::ItemAt(int32 index) const
{
return (ResourceItem*)fItems.ItemAt(index);
}
int32
ResourceFile::CountItems() const
{
return fItems.CountItems();
}
uint32
ResourceFile::GetResourcesSize() const
{
if (!fInfoTableItem || fFile.InitCheck())
throw Exception("Resource file not initialized.");
uint32 size = kResourcesHeaderSize;
uint32 indexSectionSize = kResourceIndexSectionHeaderSize
+ fResourceCount * kResourceIndexEntrySize;
indexSectionSize = align_value(indexSectionSize,
kResourceIndexSectionAlignment);
size += indexSectionSize;
size += kUnknownResourceSectionSize;
uint32 dataSize = 0;
for (int32 i = 0; i < fResourceCount; i++) {
ResourceItem* item = ItemAt(i);
dataSize += item->GetSize();
}
size += dataSize;
uint32 infoTableSize = 0;
type_code type = 0;
for (int32 i = 0; i < fResourceCount; i++) {
ResourceItem* item = ItemAt(i);
if (i == 0 || type != item->GetType()) {
if (i != 0)
infoTableSize += kResourceInfoSeparatorSize;
type = item->GetType();
infoTableSize += kMinResourceInfoBlockSize;
} else
infoTableSize += kMinResourceInfoSize;
uint32 nameLen = strlen(item->GetName());
if (nameLen != 0)
infoTableSize += nameLen + 1;
}
infoTableSize += kResourceInfoSeparatorSize + kResourceInfoTableEndSize;
size += infoTableSize;
return size;
}
uint32
ResourceFile::WriteResources(void* buffer, uint32 bufferSize)
{
uint32 size = kResourcesHeaderSize;
uint32 indexSectionOffset = size;
uint32 indexSectionSize = kResourceIndexSectionHeaderSize
+ fResourceCount * kResourceIndexEntrySize;
indexSectionSize = align_value(indexSectionSize,
kResourceIndexSectionAlignment);
size += indexSectionSize;
uint32 unknownSectionOffset = size;
uint32 unknownSectionSize = kUnknownResourceSectionSize;
size += unknownSectionSize;
uint32 dataOffset = size;
uint32 dataSize = 0;
for (int32 i = 0; i < fResourceCount; i++) {
ResourceItem* item = ItemAt(i);
dataSize += item->GetSize();
}
size += dataSize;
uint32 infoTableOffset = size;
uint32 infoTableSize = 0;
type_code type = 0;
for (int32 i = 0; i < fResourceCount; i++) {
ResourceItem* item = ItemAt(i);
if (i == 0 || type != item->GetType()) {
if (i != 0)
infoTableSize += kResourceInfoSeparatorSize;
type = item->GetType();
infoTableSize += kMinResourceInfoBlockSize;
} else
infoTableSize += kMinResourceInfoSize;
uint32 nameLen = strlen(item->GetName());
if (nameLen != 0)
infoTableSize += nameLen + 1;
}
infoTableSize += kResourceInfoSeparatorSize + kResourceInfoTableEndSize;
size += infoTableSize;
if (!buffer)
throw Exception("Supplied buffer is NULL.");
if (bufferSize < size)
throw Exception("Supplied buffer is too small.");
void* data = buffer;
resources_header* resourcesHeader = (resources_header*)data;
resourcesHeader->rh_resources_magic = kResourcesHeaderMagic;
resourcesHeader->rh_resource_count = fResourceCount;
resourcesHeader->rh_index_section_offset = indexSectionOffset;
resourcesHeader->rh_admin_section_size = indexSectionOffset
+ indexSectionSize;
for (int32 i = 0; i < 13; i++)
resourcesHeader->rh_pad[i] = 0;
data = skip_bytes(buffer, indexSectionOffset);
resource_index_section_header* indexHeader
= (resource_index_section_header*)data;
indexHeader->rish_index_section_offset = indexSectionOffset;
indexHeader->rish_index_section_size = indexSectionSize;
indexHeader->rish_unknown_section_offset = unknownSectionOffset;
indexHeader->rish_unknown_section_size = unknownSectionSize;
indexHeader->rish_info_table_offset = infoTableOffset;
indexHeader->rish_info_table_size = infoTableSize;
fill_pattern(buffer, &indexHeader->rish_unused_data1, 1);
fill_pattern(buffer, indexHeader->rish_unused_data2, 25);
fill_pattern(buffer, &indexHeader->rish_unused_data3, 1);
data = skip_bytes(data, kResourceIndexSectionHeaderSize);
resource_index_entry* entry = (resource_index_entry*)data;
uint32 entryOffset = dataOffset;
for (int32 i = 0; i < fResourceCount; i++, entry++) {
ResourceItem* item = ItemAt(i);
uint32 entrySize = item->GetSize();
entry->rie_offset = entryOffset;
entry->rie_size = entrySize;
entry->rie_pad = 0;
entryOffset += entrySize;
}
data = skip_bytes(buffer, dataOffset);
fill_pattern(buffer, entry, data);
for (int32 i = 0; i < fResourceCount; i++) {
ResourceItem* item = ItemAt(i);
status_t error = item->LoadData(fFile);
if (error != B_OK)
throw Exception(error, "Error loading resource data.");
uint32 entrySize = item->GetSize();
memcpy(data, item->GetData(), entrySize);
data = skip_bytes(data, entrySize);
}
data = skip_bytes(buffer, infoTableOffset);
type = 0;
for (int32 i = 0; i < fResourceCount; i++) {
ResourceItem* item = ItemAt(i);
resource_info* info = NULL;
if (i == 0 || type != item->GetType()) {
if (i != 0) {
resource_info_separator* separator
= (resource_info_separator*)data;
separator->ris_value1 = 0xffffffff;
separator->ris_value2 = 0xffffffff;
data = skip_bytes(data, kResourceInfoSeparatorSize);
}
type = item->GetType();
resource_info_block* infoBlock = (resource_info_block*)data;
infoBlock->rib_type = type;
info = infoBlock->rib_info;
} else
info = (resource_info*)data;
info->ri_id = item->GetID();
info->ri_index = i + 1;
info->ri_name_size = 0;
data = info->ri_name;
uint32 nameLen = strlen(item->GetName());
if (nameLen != 0) {
memcpy(info->ri_name, item->GetName(), nameLen + 1);
data = skip_bytes(data, nameLen + 1);
info->ri_name_size = nameLen + 1;
}
}
resource_info_separator* separator = (resource_info_separator*)data;
separator->ris_value1 = 0xffffffff;
separator->ris_value2 = 0xffffffff;
data = skip_bytes(data, kResourceInfoSeparatorSize);
resource_info_table_end* tableEnd = (resource_info_table_end*)data;
void* infoTable = skip_bytes(buffer, infoTableOffset);
tableEnd->rite_check_sum = calculate_checksum(infoTable,
infoTableSize - kResourceInfoTableEndSize);
tableEnd->rite_terminator = 0;
data = skip_bytes(data, kResourceInfoTableEndSize);
uint32 bytesWritten = (char*)data - (char*)buffer;
if (bytesWritten != size) {
throw Exception("Bad boy error: Wrote %lu bytes, though supposed to "
"write %lu bytes.", bytesWritten, size);
}
return size;
}
void
ResourceFile::WriteTest()
{
uint32 size = GetResourcesSize();
if (size != fFileSize) {
throw Exception("Calculated resources size differs from actual size "
"in file: %lu vs %lld.", size, fFileSize);
}
char* buffer1 = new char[size];
char* buffer2 = new char[size];
try {
WriteResources(buffer1, size);
read_exactly(fFile, 0, buffer2, size,
"Write test: Error reading resources.");
for (uint32 i = 0; i < size; i++) {
if (buffer1[i] != buffer2[i]) {
off_t filePosition = fFile.GetOffset() + i;
throw Exception("Written resources differ from those in file. "
"First difference at byte %lu (file position "
"%lld): %x vs %x.", i, filePosition,
(int)buffer1[i] & 0xff,
(int)buffer2[i] & 0xff);
}
}
} catch (Exception exception) {
delete[] buffer1;
delete[] buffer2;
throw exception;
}
delete[] buffer1;
delete[] buffer2;
}
void
ResourceFile::PrintToStream(bool longInfo)
{
if (longInfo) {
off_t resourcesOffset = fFile.GetOffset();
printf("ResourceFile:\n");
printf("file type : %s\n", kFileTypeNames[fFileType]);
printf("endianess : %s\n",
(fHostEndianess == (bool)B_HOST_IS_LENDIAN) ? "little" : "big");
printf("resource section offset: 0x%08Lx (%lld)\n", resourcesOffset,
resourcesOffset);
if (fInfoTableItem) {
int32 offset = fInfoTableItem->GetOffset();
int32 size = fInfoTableItem->GetSize();
printf("resource info table : offset: 0x%08lx (%ld), "
"size: 0x%08lx (%ld)\n", offset, offset, size, size);
}
printf("number of resources : %ld\n", fResourceCount);
for (int32 i = 0; i < fResourceCount; i++) {
ResourceItem* item = ItemAt(i);
item->PrintToStream();
}
} else {
printf(" Type ID Size Name\n");
printf(" ------ ----- -------- --------------------\n");
for (int32 i = 0; i < fResourceCount; i++) {
ResourceItem* item = ItemAt(i);
type_code type = item->GetType();
char typeName[4] = { type >> 24, (type >> 16) & 0xff,
(type >> 8) & 0xff, type & 0xff };
printf(" '%.4s' %5ld %8lu %s\n", typeName, item->GetID(),
item->GetSize(), item->GetName());
}
}
}
void
ResourceFile::_InitFile(BFile& file)
{
status_t error = B_OK;
fFile.Unset();
char magic[4];
read_exactly(file, 0, magic, 4, "Failed to read magic number.");
if (!memcmp(magic, kX86ResourceFileMagic, 4)) {
fHostEndianess = B_HOST_IS_LENDIAN;
fFileType = FILE_TYPE_X86_RESOURCE;
fFile.SetTo(file, kX86ResourcesOffset);
} else if (!memcmp(magic, kPEFFileMagic1, 4)) {
PEFContainerHeader pefHeader;
read_exactly(file, 0, &pefHeader, kPEFContainerHeaderSize,
"Failed to read PEF container header.");
if (!memcmp(pefHeader.tag2, kPPCResourceFileMagic, 4)) {
fHostEndianess = B_HOST_IS_BENDIAN;
fFileType = FILE_TYPE_PPC_RESOURCE;
fFile.SetTo(file, kPPCResourcesOffset);
} else if (!memcmp(pefHeader.tag2, kPEFFileMagic2, 4)) {
fFileType = FILE_TYPE_PEF;
_InitPEFFile(file, pefHeader);
} else
throw Exception("File is not a resource file.");
} else if (!memcmp(magic, kELFFileMagic, 4)) {
fFileType = FILE_TYPE_ELF;
_InitELFFile(file);
} else if (!memcmp(magic, kX86ResourceFileMagic, 2)) {
Warnings::AddCurrentWarning("File magic is 0x%08lx. Should be 0x%08lx "
"for x86 resource file. Try anyway.",
ntohl(*(uint32*)magic),
ntohl(*(uint32*)kX86ResourceFileMagic));
fHostEndianess = B_HOST_IS_LENDIAN;
fFileType = FILE_TYPE_X86_RESOURCE;
fFile.SetTo(file, kX86ResourcesOffset);
} else
throw Exception("File is not a resource file.");
error = fFile.InitCheck();
if (error != B_OK)
throw Exception(error, "Failed to initialize resource file.");
fFileSize = 0;
error = fFile.GetSize(&fFileSize);
if (error != B_OK)
throw Exception(error, "Failed to get the file size.");
}
void
ResourceFile::_InitELFFile(BFile& file)
{
status_t error = B_OK;
off_t fileSize = 0;
error = file.GetSize(&fileSize);
if (error != B_OK)
throw Exception(error, "Failed to get the file size.");
Elf32_Ehdr fileHeader;
read_exactly(file, 0, &fileHeader, sizeof(Elf32_Ehdr),
"Failed to read ELF header.");
switch (fileHeader.e_ident[EI_DATA]) {
case ELFDATA2LSB:
fHostEndianess = B_HOST_IS_LENDIAN;
break;
case ELFDATA2MSB:
fHostEndianess = B_HOST_IS_BENDIAN;
break;
default:
case ELFDATANONE:
throw Exception("Unsupported ELF data encoding.");
break;
}
uint32 headerSize = _GetUInt16(fileHeader.e_ehsize);
uint32 programHeaderTableOffset = _GetUInt32(fileHeader.e_phoff);
uint32 programHeaderSize = _GetUInt16(fileHeader.e_phentsize);
uint32 programHeaderCount = _GetUInt16(fileHeader.e_phnum);
uint32 sectionHeaderTableOffset = _GetUInt32(fileHeader.e_shoff);
uint32 sectionHeaderSize = _GetUInt16(fileHeader.e_shentsize);
uint32 sectionHeaderCount = _GetUInt16(fileHeader.e_shnum);
bool hasProgramHeaderTable = (programHeaderTableOffset != 0);
bool hasSectionHeaderTable = (sectionHeaderTableOffset != 0);
if (headerSize < sizeof(Elf32_Ehdr) || headerSize > kMaxELFHeaderSize) {
throw Exception("Invalid ELF header: invalid ELF header size: %lu.",
headerSize);
}
uint32 resourceOffset = headerSize;
uint32 resourceAlignment = 0;
uint32 programHeaderTableSize = 0;
if (hasProgramHeaderTable) {
if (programHeaderTableOffset < headerSize
|| programHeaderTableOffset > fileSize) {
throw Exception("Invalid ELF header: invalid program header table "
"offset: %lu.", programHeaderTableOffset);
}
programHeaderTableSize = programHeaderSize * programHeaderCount;
if (programHeaderSize < sizeof(Elf32_Phdr)
|| programHeaderTableOffset + programHeaderTableSize > fileSize) {
throw Exception("Invalid ELF header: program header table exceeds "
"file: %lu.",
programHeaderTableOffset + programHeaderTableSize);
}
resourceOffset = max(resourceOffset, programHeaderTableOffset
+ programHeaderTableSize);
for (int32 i = 0; i < (int32)programHeaderCount; i++) {
uint32 shOffset = programHeaderTableOffset + i * programHeaderSize;
Elf32_Phdr programHeader;
read_exactly(file, shOffset, &programHeader, sizeof(Elf32_Shdr),
"Failed to read ELF program header.");
uint32 type = _GetUInt32(programHeader.p_type);
uint32 offset = _GetUInt32(programHeader.p_offset);
uint32 size = _GetUInt32(programHeader.p_filesz);
uint32 alignment = _GetUInt32(programHeader.p_align);
if (type != PT_NULL) {
if ( offset > fileSize) {
throw Exception("Invalid ELF program header: invalid "
"program offset: %lu.", offset);
}
uint32 segmentEnd = offset + size;
if (segmentEnd > fileSize) {
throw Exception("Invalid ELF section header: segment "
"exceeds file: %lu.", segmentEnd);
}
resourceOffset = max(resourceOffset, segmentEnd);
resourceAlignment = max(resourceAlignment, alignment);
}
}
}
uint32 sectionHeaderTableSize = 0;
if (hasSectionHeaderTable) {
if (sectionHeaderTableOffset < headerSize
|| sectionHeaderTableOffset > fileSize) {
throw Exception("Invalid ELF header: invalid section header table "
"offset: %lu.", sectionHeaderTableOffset);
}
sectionHeaderTableSize = sectionHeaderSize * sectionHeaderCount;
if (sectionHeaderSize < sizeof(Elf32_Shdr)
|| sectionHeaderTableOffset + sectionHeaderTableSize > fileSize) {
throw Exception("Invalid ELF header: section header table exceeds "
"file: %lu.",
sectionHeaderTableOffset + sectionHeaderTableSize);
}
resourceOffset = max(resourceOffset, sectionHeaderTableOffset
+ sectionHeaderTableSize);
for (int32 i = 0; i < (int32)sectionHeaderCount; i++) {
uint32 shOffset = sectionHeaderTableOffset + i * sectionHeaderSize;
Elf32_Shdr sectionHeader;
read_exactly(file, shOffset, §ionHeader, sizeof(Elf32_Shdr),
"Failed to read ELF section header.");
uint32 type = _GetUInt32(sectionHeader.sh_type);
uint32 offset = _GetUInt32(sectionHeader.sh_offset);
uint32 size = _GetUInt32(sectionHeader.sh_size);
if (type != SHT_NULL && type != SHT_NOBITS) {
if (offset < headerSize || offset > fileSize) {
throw Exception("Invalid ELF section header: invalid "
"section offset: %lu.", offset);
}
uint32 sectionEnd = offset + size;
if (sectionEnd > fileSize) {
throw Exception("Invalid ELF section header: section "
"exceeds file: %lu.", sectionEnd);
}
resourceOffset = max(resourceOffset, sectionEnd);
}
}
}
if (resourceAlignment < kELFMinResourceAlignment)
resourceAlignment = kELFMinResourceAlignment;
if (resourceAlignment > kELFMaxResourceAlignment) {
throw Exception("The ELF object file requires an invalid alignment: "
"%lu.", resourceAlignment);
}
resourceOffset = align_value(resourceOffset, resourceAlignment);
if (resourceOffset >= fileSize)
throw Exception("The ELF object file does not contain resources.");
fFile.SetTo(file, resourceOffset);
}
void
ResourceFile::_InitPEFFile(BFile& file, const PEFContainerHeader& pefHeader)
{
status_t error = B_OK;
off_t fileSize = 0;
error = file.GetSize(&fileSize);
if (error != B_OK)
throw Exception(error, "Failed to get the file size.");
if (memcmp(pefHeader.architecture, kPEFArchitecturePPC, 4))
throw Exception("PEF file architecture is not PPC.");
fHostEndianess = B_HOST_IS_BENDIAN;
uint16 sectionCount = _GetUInt16(pefHeader.sectionCount);
uint32 sectionHeaderTableOffset = kPEFContainerHeaderSize;
uint32 sectionHeaderTableEnd
= sectionHeaderTableOffset + sectionCount * kPEFSectionHeaderSize;
uint32 resourceOffset = sectionHeaderTableEnd;
for (int32 i = 0; i < (int32)sectionCount; i++) {
uint32 shOffset = sectionHeaderTableOffset + i * kPEFSectionHeaderSize;
PEFSectionHeader sectionHeader;
read_exactly(file, shOffset, §ionHeader, kPEFSectionHeaderSize,
"Failed to read PEF section header.");
uint32 offset = _GetUInt32(sectionHeader.containerOffset);
uint32 size = _GetUInt32(sectionHeader.packedSize);
if (offset < sectionHeaderTableEnd || offset > fileSize) {
throw Exception("Invalid PEF section header: invalid "
"section offset: %lu.", offset);
}
uint32 sectionEnd = offset + size;
if (sectionEnd > fileSize) {
throw Exception("Invalid PEF section header: section "
"exceeds file: %lu.", sectionEnd);
}
resourceOffset = max(resourceOffset, sectionEnd);
}
fFile.SetTo(file, resourceOffset);
}
void
ResourceFile::_ReadHeader()
{
resources_header header;
read_exactly(fFile, 0, &header, kResourcesHeaderSize,
"Failed to read the header.");
uint32 magic = _GetUInt32(header.rh_resources_magic);
if (magic == kResourcesHeaderMagic) {
} else if (B_SWAP_INT32(magic) == kResourcesHeaderMagic) {
const char* endianessStr[2] = { "little", "big" };
int32 endianess
= (fHostEndianess == ((bool)B_HOST_IS_LENDIAN ? 0 : 1));
Warnings::AddCurrentWarning("Endianess seems to be %s, although %s "
"was expected.",
endianessStr[1 - endianess],
endianessStr[endianess]);
fHostEndianess = !fHostEndianess;
} else
throw Exception("Invalid resources header magic.");
uint32 resourceCount = _GetUInt32(header.rh_resource_count);
if (resourceCount > kMaxResourceCount)
throw Exception("Bad number of resources.");
uint32 indexSectionOffset = _GetUInt32(header.rh_index_section_offset);
if (indexSectionOffset != kResourceIndexSectionOffset) {
throw Exception("Unexpected resource index section offset. Is: %lu, "
"should be: %lu.", indexSectionOffset,
kResourceIndexSectionOffset);
}
uint32 indexSectionSize = kResourceIndexSectionHeaderSize
+ kResourceIndexEntrySize * resourceCount;
indexSectionSize = align_value(indexSectionSize,
kResourceIndexSectionAlignment);
uint32 adminSectionSize = _GetUInt32(header.rh_admin_section_size);
if (adminSectionSize != indexSectionOffset + indexSectionSize) {
throw Exception("Unexpected resource admin section size. Is: %lu, "
"should be: %lu.", adminSectionSize,
indexSectionOffset + indexSectionSize);
}
fResourceCount = resourceCount;
}
void
ResourceFile::_ReadIndex()
{
resource_index_section_header header;
read_exactly(fFile, kResourceIndexSectionOffset, &header,
kResourceIndexSectionHeaderSize,
"Failed to read the resource index section header.");
uint32 indexSectionOffset = _GetUInt32(header.rish_index_section_offset);
if (indexSectionOffset != kResourceIndexSectionOffset) {
throw Exception("Unexpected resource index section offset. Is: %lu, "
"should be: %lu.", indexSectionOffset,
kResourceIndexSectionOffset);
}
uint32 expectedIndexSectionSize = kResourceIndexSectionHeaderSize
+ kResourceIndexEntrySize * fResourceCount;
expectedIndexSectionSize = align_value(expectedIndexSectionSize,
kResourceIndexSectionAlignment);
uint32 indexSectionSize = _GetUInt32(header.rish_index_section_size);
if (indexSectionSize != expectedIndexSectionSize) {
throw Exception("Unexpected resource index section size. Is: %lu, "
"should be: %lu.", indexSectionSize,
expectedIndexSectionSize);
}
uint32 unknownSectionOffset
= _GetUInt32(header.rish_unknown_section_offset);
if (unknownSectionOffset != indexSectionOffset + indexSectionSize) {
throw Exception("Unexpected resource index section size. Is: %lu, "
"should be: %lu.", unknownSectionOffset,
indexSectionOffset + indexSectionSize);
}
uint32 unknownSectionSize = _GetUInt32(header.rish_unknown_section_size);
if (unknownSectionSize != kUnknownResourceSectionSize) {
throw Exception("Unexpected resource index section offset. Is: %lu, "
"should be: %lu.", unknownSectionOffset,
kUnknownResourceSectionSize);
}
uint32 infoTableOffset = _GetUInt32(header.rish_info_table_offset);
uint32 infoTableSize = _GetUInt32(header.rish_info_table_size);
if (infoTableOffset + infoTableSize > fFileSize)
throw Exception("Invalid info table location.");
fInfoTableItem = new ResourceItem;
fInfoTableItem->SetLocation(infoTableOffset, infoTableSize);
uint32 indexTableOffset = indexSectionOffset
+ kResourceIndexSectionHeaderSize;
int32 maxResourceCount = (unknownSectionOffset - indexTableOffset)
/ kResourceIndexEntrySize;
int32 actualResourceCount = 0;
bool tableEndReached = false;
for (int32 i = 0; !tableEndReached && i < maxResourceCount; i++) {
tableEndReached = !_ReadIndexEntry(i, indexTableOffset,
(i >= fResourceCount));
if (!tableEndReached)
actualResourceCount++;
}
if (actualResourceCount != fResourceCount) {
if (actualResourceCount > fResourceCount) {
Warnings::AddCurrentWarning("Resource index table contains "
"%ld entries, although it should be "
"%ld only.", actualResourceCount,
fResourceCount);
}
fResourceCount = actualResourceCount;
}
}
bool
ResourceFile::_ReadIndexEntry(int32 index, uint32 tableOffset, bool peekAhead)
{
bool result = true;
resource_index_entry entry;
off_t entryOffset = tableOffset + index * kResourceIndexEntrySize;
read_exactly(fFile, entryOffset, &entry, kResourceIndexEntrySize,
"Failed to read a resource index entry.");
if (result && check_pattern(entryOffset, &entry,
kResourceIndexEntrySize / 4, fHostEndianess)) {
if (!peekAhead) {
Warnings::AddCurrentWarning("Unexpected end of resource index "
"table at index: %ld (/%ld).",
index + 1, fResourceCount);
}
result = false;
}
uint32 offset = _GetUInt32(entry.rie_offset);
uint32 size = _GetUInt32(entry.rie_size);
if (result && offset + size > fFileSize) {
if (peekAhead) {
Warnings::AddCurrentWarning("Invalid data after resource index "
"table.");
} else {
throw Exception("Invalid resource index entry: index: %ld, "
"offset: %lu (%lx), size: %lu (%lx).", index + 1,
offset, offset, size, size);
}
result = false;
}
if (result) {
ResourceItem* item = new ResourceItem;
item->SetLocation(offset, size);
AddItem(item, index);
}
return result;
}
void
ResourceFile::_ReadInfoTable()
{
status_t error = B_OK;
error = fInfoTableItem->LoadData(fFile);
if (error != B_OK)
throw Exception(error, "Failed to read resource info table.");
const void* tableData = fInfoTableItem->GetData();
int32 dataSize = fInfoTableItem->GetSize();
bool* readIndices = new bool[fResourceCount + 1];
for (int32 i = 0; i < fResourceCount; i++)
readIndices[i] = false;
AutoDeleter<bool> deleter(readIndices, true);
MemArea area(tableData, dataSize);
const void* data = tableData;
if (_ReadInfoTableEnd(data, dataSize))
dataSize -= kResourceInfoTableEndSize;
int32 resourceIndex = 1;
uint32 minRemainderSize
= kMinResourceInfoBlockSize + kResourceInfoSeparatorSize;
while (area.check(data, minRemainderSize)) {
if (!area.check(data, kMinResourceInfoBlockSize)) {
throw Exception("Unexpected end of resource info table at index "
"%ld.", resourceIndex);
}
const resource_info_block* infoBlock
= (const resource_info_block*)data;
type_code type = _GetUInt32(infoBlock->rib_type);
const resource_info* info = infoBlock->rib_info;
while (info) {
data = _ReadResourceInfo(area, info, type, readIndices);
if (!area.check(data, kResourceInfoSeparatorSize)) {
throw Exception("Unexpected end of resource info table after "
"index %ld.", resourceIndex);
}
const resource_info_separator* separator
= (const resource_info_separator*)data;
if (_GetUInt32(separator->ris_value1) == 0xffffffff
&& _GetUInt32(separator->ris_value2) == 0xffffffff) {
info = NULL;
data = skip_bytes(data, kResourceInfoSeparatorSize);
} else {
info = (const resource_info*)data;
}
resourceIndex++;
}
}
if (resourceIndex == 1) {
if (!area.check(data, kResourceInfoSeparatorSize)) {
throw Exception("Unexpected end of resource info table.");
}
const resource_info_separator* tableTerminator
= (const resource_info_separator*)data;
if (_GetUInt32(tableTerminator->ris_value1) != 0xffffffff
|| _GetUInt32(tableTerminator->ris_value2) != 0xffffffff) {
throw Exception("The resource info table ought to be empty, but "
"is not properly terminated.");
}
data = skip_bytes(data, kResourceInfoSeparatorSize);
}
uint32 bytesLeft = (const char*)tableData + dataSize - (const char*)data;
if (bytesLeft != 0) {
throw Exception("Error at the end of the resource info table: %lu "
"bytes are remaining.", bytesLeft);
}
for (int32 i = fResourceCount - 1; i >= 0; i--) {
if (!readIndices[i]) {
Warnings::AddCurrentWarning("Resource item at index %ld "
"has no info. Item removed.", i + 1);
if (ResourceItem* item = RemoveItem(i))
delete item;
fResourceCount--;
}
}
}
bool
ResourceFile::_ReadInfoTableEnd(const void* data, int32 dataSize)
{
bool hasTableEnd = true;
if ((uint32)dataSize < kResourceInfoSeparatorSize)
throw Exception("Info table is too short.");
if ((uint32)dataSize < kResourceInfoTableEndSize)
hasTableEnd = false;
if (hasTableEnd) {
const resource_info_table_end* tableEnd
= (const resource_info_table_end*)
skip_bytes(data, dataSize - kResourceInfoTableEndSize);
if (_GetInt32(tableEnd->rite_terminator) != 0)
hasTableEnd = false;
if (hasTableEnd) {
dataSize -= kResourceInfoTableEndSize;
uint32 checkSum = calculate_checksum(data, dataSize);
uint32 fileCheckSum = _GetUInt32(tableEnd->rite_check_sum);
if (checkSum != fileCheckSum) {
throw Exception("Invalid resource info table check sum: In "
"file: %lx, calculated: %lx.", fileCheckSum,
checkSum);
}
}
}
if (!hasTableEnd)
Warnings::AddCurrentWarning("resource info table has no check sum.");
return hasTableEnd;
}
const void*
ResourceFile::_ReadResourceInfo(const MemArea& area, const resource_info* info,
type_code type, bool* readIndices)
{
int32 id = _GetInt32(info->ri_id);
int32 index = _GetInt32(info->ri_index);
uint16 nameSize = _GetUInt16(info->ri_name_size);
const char* name = info->ri_name;
bool ignore = false;
if (index < 1 || index > fResourceCount) {
Warnings::AddCurrentWarning("Invalid index field in resource "
"info table: %lu.", index);
ignore = true;
}
if (!ignore) {
if (readIndices[index - 1]) {
throw Exception("Multiple resource infos with the same index "
"field: %ld.", index);
}
readIndices[index - 1] = true;
}
if (!area.check(name, nameSize)) {
throw Exception("Invalid name size (%d) for index %ld in "
"resource info table.", (int)nameSize, index);
}
if (name[nameSize - 1] != 0) {
Warnings::AddCurrentWarning("Name for index %ld in "
"resource info table is not null "
"terminated.", index);
}
if (!ignore) {
BString resourceName(name, nameSize);
if (ResourceItem* item = ItemAt(index - 1))
item->SetIdentity(type, id, resourceName.String());
else {
throw Exception("Unexpected error: No resource item at index "
"%ld.", index);
}
}
return skip_bytes(name, nameSize);
}