mirror of
https://github.com/decompals/wibo.git
synced 2025-10-15 22:55:11 +00:00
243 lines
7.5 KiB
C++
243 lines
7.5 KiB
C++
#include "resources.h"
|
|
#include "errors.h"
|
|
#include "common.h"
|
|
|
|
namespace {
|
|
|
|
struct ImageResourceDirectory {
|
|
uint32_t characteristics;
|
|
uint32_t timeDateStamp;
|
|
uint16_t majorVersion;
|
|
uint16_t minorVersion;
|
|
uint16_t numberOfNamedEntries;
|
|
uint16_t numberOfIdEntries;
|
|
};
|
|
|
|
struct ImageResourceDirectoryEntry {
|
|
uint32_t name;
|
|
uint32_t offsetToData;
|
|
};
|
|
|
|
constexpr uint32_t RESOURCE_NAME_IS_STRING = 0x80000000u;
|
|
constexpr uint32_t RESOURCE_DATA_IS_DIRECTORY = 0x80000000u;
|
|
|
|
const ImageResourceDirectoryEntry *resourceEntries(const ImageResourceDirectory *dir) {
|
|
return reinterpret_cast<const ImageResourceDirectoryEntry *>(dir + 1);
|
|
}
|
|
|
|
bool resourceOffsetInRange(uint32_t offset, size_t needed, uint32_t available) {
|
|
if (available == 0)
|
|
return true;
|
|
if (offset > available)
|
|
return false;
|
|
if (available - offset < needed)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool resourceNameEquals(const uint8_t *base, uint32_t nameField, const std::u16string &value, uint32_t rsrcSize) {
|
|
if (!(nameField & RESOURCE_NAME_IS_STRING))
|
|
return false;
|
|
uint32_t offset = nameField & ~RESOURCE_NAME_IS_STRING;
|
|
if (!resourceOffsetInRange(offset, sizeof(uint16_t), rsrcSize))
|
|
return false;
|
|
const auto *lengthPtr = reinterpret_cast<const uint16_t *>(base + offset);
|
|
uint16_t length = *lengthPtr;
|
|
size_t bytesNeeded = sizeof(uint16_t) + static_cast<size_t>(length) * sizeof(uint16_t);
|
|
if (!resourceOffsetInRange(offset, bytesNeeded, rsrcSize))
|
|
return false;
|
|
if (length != value.size())
|
|
return false;
|
|
const uint16_t *str = lengthPtr + 1;
|
|
for (uint16_t i = 0; i < length; ++i) {
|
|
if (str[i] != value[i])
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const ImageResourceDirectoryEntry *findEntry(const uint8_t *base, const ImageResourceDirectory *dir,
|
|
const wibo::ResourceIdentifier &ident, uint32_t rsrcSize) {
|
|
const auto *entries = resourceEntries(dir);
|
|
if (ident.isString) {
|
|
for (uint16_t i = 0; i < dir->numberOfNamedEntries; ++i) {
|
|
const auto &entry = entries[i];
|
|
if (resourceNameEquals(base, entry.name, ident.name, rsrcSize))
|
|
return &entry;
|
|
}
|
|
return nullptr;
|
|
}
|
|
for (uint16_t i = 0; i < dir->numberOfIdEntries; ++i) {
|
|
const auto &entry = entries[dir->numberOfNamedEntries + i];
|
|
if (!(entry.name & RESOURCE_NAME_IS_STRING) && (entry.name & 0xFFFFu) == (ident.id & 0xFFFFu))
|
|
return &entry;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const ImageResourceDirectory *entryAsDirectory(const uint8_t *base, const ImageResourceDirectoryEntry *entry,
|
|
uint32_t rsrcSize) {
|
|
if (!(entry->offsetToData & RESOURCE_DATA_IS_DIRECTORY))
|
|
return nullptr;
|
|
uint32_t offset = entry->offsetToData & ~RESOURCE_DATA_IS_DIRECTORY;
|
|
if (!resourceOffsetInRange(offset, sizeof(ImageResourceDirectory), rsrcSize))
|
|
return nullptr;
|
|
return reinterpret_cast<const ImageResourceDirectory *>(base + offset);
|
|
}
|
|
|
|
const wibo::ImageResourceDataEntry *entryAsData(const uint8_t *base, const ImageResourceDirectoryEntry *entry,
|
|
uint32_t rsrcSize) {
|
|
if (entry->offsetToData & RESOURCE_DATA_IS_DIRECTORY)
|
|
return nullptr;
|
|
uint32_t offset = entry->offsetToData;
|
|
if (!resourceOffsetInRange(offset, sizeof(wibo::ImageResourceDataEntry), rsrcSize))
|
|
return nullptr;
|
|
return reinterpret_cast<const wibo::ImageResourceDataEntry *>(base + offset);
|
|
}
|
|
|
|
uint16_t primaryLang(uint16_t lang) { return lang & 0x3FFu; }
|
|
|
|
const ImageResourceDirectoryEntry *selectLanguageEntry(const ImageResourceDirectory *dir, const uint8_t *base,
|
|
uint32_t rsrcSize, std::optional<uint16_t> desired,
|
|
uint16_t &chosenLang) {
|
|
const auto *entries = resourceEntries(dir);
|
|
uint16_t total = dir->numberOfNamedEntries + dir->numberOfIdEntries;
|
|
const ImageResourceDirectoryEntry *primaryMatch = nullptr;
|
|
const ImageResourceDirectoryEntry *neutralMatch = nullptr;
|
|
const ImageResourceDirectoryEntry *first = nullptr;
|
|
for (uint16_t i = 0; i < total; ++i) {
|
|
const auto &entry = entries[i];
|
|
if (entry.name & RESOURCE_NAME_IS_STRING)
|
|
continue;
|
|
uint16_t lang = static_cast<uint16_t>(entry.name & 0xFFFFu);
|
|
if (!first)
|
|
first = &entry;
|
|
if (desired && lang == desired.value()) {
|
|
chosenLang = lang;
|
|
return &entry;
|
|
}
|
|
if (!primaryMatch && desired && primaryLang(lang) == primaryLang(desired.value())) {
|
|
primaryMatch = &entry;
|
|
}
|
|
if (!neutralMatch && lang == 0)
|
|
neutralMatch = &entry;
|
|
}
|
|
if (primaryMatch) {
|
|
chosenLang = static_cast<uint16_t>(primaryMatch->name & 0xFFFFu);
|
|
return primaryMatch;
|
|
}
|
|
if (neutralMatch) {
|
|
chosenLang = 0;
|
|
return neutralMatch;
|
|
}
|
|
if (first) {
|
|
chosenLang = static_cast<uint16_t>(first->name & 0xFFFFu);
|
|
return first;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace wibo {
|
|
|
|
bool Executable::findResource(const ResourceIdentifier &type, const ResourceIdentifier &name,
|
|
std::optional<uint16_t> language, ResourceLocation &out) const {
|
|
const uint8_t *base = reinterpret_cast<const uint8_t *>(rsrcBase);
|
|
if (!base) {
|
|
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
|
return false;
|
|
}
|
|
const auto *root = reinterpret_cast<const ImageResourceDirectory *>(base);
|
|
const auto *typeEntry = findEntry(base, root, type, rsrcSize);
|
|
if (!typeEntry) {
|
|
wibo::lastError = ERROR_RESOURCE_TYPE_NOT_FOUND;
|
|
return false;
|
|
}
|
|
const auto *nameDir = entryAsDirectory(base, typeEntry, rsrcSize);
|
|
if (!nameDir) {
|
|
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
|
return false;
|
|
}
|
|
const auto *nameEntry = findEntry(base, nameDir, name, rsrcSize);
|
|
if (!nameEntry) {
|
|
wibo::lastError = ERROR_RESOURCE_NAME_NOT_FOUND;
|
|
return false;
|
|
}
|
|
const auto *langDir = entryAsDirectory(base, nameEntry, rsrcSize);
|
|
if (!langDir) {
|
|
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
|
return false;
|
|
}
|
|
uint16_t chosenLang = language.value_or(0);
|
|
const auto *langEntry = selectLanguageEntry(langDir, base, rsrcSize, language, chosenLang);
|
|
if (!langEntry) {
|
|
wibo::lastError = ERROR_RESOURCE_LANG_NOT_FOUND;
|
|
return false;
|
|
}
|
|
const auto *dataEntry = entryAsData(base, langEntry, rsrcSize);
|
|
if (!dataEntry) {
|
|
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
|
return false;
|
|
}
|
|
out.dataEntry = dataEntry;
|
|
out.data = fromRVA<const void>(dataEntry->offsetToData);
|
|
out.size = dataEntry->size;
|
|
out.language = chosenLang;
|
|
return true;
|
|
}
|
|
|
|
bool resourceEntryBelongsToExecutable(const Executable &exe, const ImageResourceDataEntry *entry) {
|
|
if (!entry || !exe.rsrcBase)
|
|
return false;
|
|
const auto *base = reinterpret_cast<const uint8_t *>(exe.rsrcBase);
|
|
const auto *ptr = reinterpret_cast<const uint8_t *>(entry);
|
|
if (exe.rsrcSize == 0)
|
|
return true;
|
|
return ptr >= base && (ptr + sizeof(*entry)) <= (base + exe.rsrcSize);
|
|
}
|
|
|
|
static bool isIntegerIdentifier(const void *ptr) { return ((uintptr_t)ptr >> 16) == 0; }
|
|
|
|
static std::u16string ansiToU16String(const char *str) {
|
|
std::u16string result;
|
|
if (!str)
|
|
return result;
|
|
while (*str) {
|
|
result.push_back(static_cast<unsigned char>(*str++));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static std::u16string wideToU16String(const uint16_t *str) {
|
|
std::u16string result;
|
|
if (!str)
|
|
return result;
|
|
while (*str) {
|
|
result.push_back(*str++);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ResourceIdentifier resourceIdentifierFromAnsi(const char *id) {
|
|
if (!id) {
|
|
return ResourceIdentifier::fromID(0);
|
|
}
|
|
if (isIntegerIdentifier(id)) {
|
|
return ResourceIdentifier::fromID(static_cast<uint32_t>(reinterpret_cast<uintptr_t>(id)));
|
|
}
|
|
return ResourceIdentifier::fromString(ansiToU16String(id));
|
|
}
|
|
|
|
ResourceIdentifier resourceIdentifierFromWide(const uint16_t *id) {
|
|
if (!id) {
|
|
return ResourceIdentifier::fromID(0);
|
|
}
|
|
if (isIntegerIdentifier(id)) {
|
|
return ResourceIdentifier::fromID(static_cast<uint32_t>(reinterpret_cast<uintptr_t>(id)));
|
|
}
|
|
return ResourceIdentifier::fromString(wideToU16String(id));
|
|
}
|
|
|
|
} // namespace wibo
|