mirror of
https://github.com/decompals/wibo.git
synced 2025-10-15 14:45:12 +00:00
Add proper resource implementation
This commit is contained in:
parent
720e6639a9
commit
01ddf95d36
@ -31,6 +31,7 @@ add_executable(wibo
|
||||
files.cpp
|
||||
handles.cpp
|
||||
loader.cpp
|
||||
resources.cpp
|
||||
module_registry.cpp
|
||||
main.cpp
|
||||
processes.cpp
|
||||
|
47
common.h
47
common.h
@ -8,6 +8,7 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
@ -61,6 +62,9 @@ typedef unsigned char BYTE;
|
||||
#define ERROR_BUFFER_OVERFLOW 111
|
||||
#define ERROR_INSUFFICIENT_BUFFER 122
|
||||
#define ERROR_RESOURCE_DATA_NOT_FOUND 1812
|
||||
#define ERROR_RESOURCE_TYPE_NOT_FOUND 1813
|
||||
#define ERROR_RESOURCE_NAME_NOT_FOUND 1814
|
||||
#define ERROR_RESOURCE_LANG_NOT_FOUND 1815
|
||||
#define ERROR_MOD_NOT_FOUND 126
|
||||
#define ERROR_NEGATIVE_SEEK 131
|
||||
#define ERROR_BAD_EXE_FORMAT 193
|
||||
@ -118,6 +122,39 @@ namespace wibo {
|
||||
void *resolveFuncByName(HMODULE module, const char *funcName);
|
||||
void *resolveFuncByOrdinal(HMODULE module, uint16_t ordinal);
|
||||
|
||||
struct ResourceIdentifier {
|
||||
ResourceIdentifier() : isString(false), id(0) {}
|
||||
static ResourceIdentifier fromID(uint32_t value) {
|
||||
ResourceIdentifier ident;
|
||||
ident.isString = false;
|
||||
ident.id = value;
|
||||
return ident;
|
||||
}
|
||||
static ResourceIdentifier fromString(std::u16string value) {
|
||||
ResourceIdentifier ident;
|
||||
ident.isString = true;
|
||||
ident.name = std::move(value);
|
||||
return ident;
|
||||
}
|
||||
bool isString;
|
||||
uint32_t id;
|
||||
std::u16string name;
|
||||
};
|
||||
|
||||
struct ResourceLocation {
|
||||
const void *dataEntry = nullptr;
|
||||
const void *data = nullptr;
|
||||
uint32_t size = 0;
|
||||
uint16_t language = 0;
|
||||
};
|
||||
|
||||
struct ImageResourceDataEntry {
|
||||
uint32_t offsetToData;
|
||||
uint32_t size;
|
||||
uint32_t codePage;
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
struct Executable {
|
||||
Executable();
|
||||
~Executable();
|
||||
@ -127,6 +164,7 @@ namespace wibo {
|
||||
size_t imageSize;
|
||||
void *entryPoint;
|
||||
void *rsrcBase;
|
||||
uint32_t rsrcSize;
|
||||
uintptr_t preferredImageBase;
|
||||
intptr_t relocationDelta;
|
||||
uint32_t exportDirectoryRVA;
|
||||
@ -134,13 +172,18 @@ namespace wibo {
|
||||
uint32_t relocationDirectoryRVA;
|
||||
uint32_t relocationDirectorySize;
|
||||
|
||||
bool findResource(const ResourceIdentifier &type,
|
||||
const ResourceIdentifier &name,
|
||||
std::optional<uint16_t> language,
|
||||
ResourceLocation &out) const;
|
||||
|
||||
template <typename T>
|
||||
T *fromRVA(uint32_t rva) {
|
||||
T *fromRVA(uint32_t rva) const {
|
||||
return (T *) (rva + (uint8_t *) imageBuffer);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *fromRVA(T *rva) {
|
||||
T *fromRVA(T *rva) const {
|
||||
return fromRVA<T>((uint32_t) rva);
|
||||
}
|
||||
};
|
||||
|
138
dll/kernel32.cpp
138
dll/kernel32.cpp
@ -2,6 +2,7 @@
|
||||
#include "files.h"
|
||||
#include "processes.h"
|
||||
#include "handles.h"
|
||||
#include "resources.h"
|
||||
#include <algorithm>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
@ -1674,89 +1675,74 @@ namespace kernel32 {
|
||||
return copyLen;
|
||||
}
|
||||
|
||||
static std::string resource_identifier_to_string(const char *id) {
|
||||
if (!id) {
|
||||
return "";
|
||||
}
|
||||
if ((uintptr_t)id >> 16 == 0) {
|
||||
return std::to_string(static_cast<unsigned int>((uintptr_t)id));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static std::string resource_identifier_to_string(const uint16_t *id) {
|
||||
if (!id) {
|
||||
return "";
|
||||
}
|
||||
if ((uintptr_t)id >> 16 == 0) {
|
||||
return std::to_string(static_cast<unsigned int>((uintptr_t)id));
|
||||
}
|
||||
return wideStringToString(id);
|
||||
}
|
||||
|
||||
static FILE *open_resource_stream(const std::string &type, const std::string &name) {
|
||||
char path[512];
|
||||
snprintf(path, sizeof(path), "resources/%s/%s.res", type.c_str(), name.c_str());
|
||||
DEBUG_LOG("Created path %s\n", path);
|
||||
return fopen(path, "rb");
|
||||
}
|
||||
|
||||
void *WIN_FUNC FindResourceA(void *hModule, const char *lpName, const char *lpType) {
|
||||
DEBUG_LOG("FindResourceA %p %s %s\n", hModule, lpName, lpType);
|
||||
|
||||
static wibo::Executable *module_executable_for_resource(void *hModule) {
|
||||
if (!hModule) {
|
||||
hModule = GetModuleHandleA(nullptr);
|
||||
}
|
||||
return wibo::executableFromModule((HMODULE) hModule);
|
||||
}
|
||||
|
||||
const std::string name = resource_identifier_to_string(lpName);
|
||||
const std::string type = resource_identifier_to_string(lpType);
|
||||
|
||||
FILE *res = open_resource_stream(type, name);
|
||||
if (!res) {
|
||||
static void *find_resource_internal(void *hModule,
|
||||
const wibo::ResourceIdentifier &type,
|
||||
const wibo::ResourceIdentifier &name,
|
||||
std::optional<uint16_t> language) {
|
||||
auto *exe = module_executable_for_resource(hModule);
|
||||
if (!exe) {
|
||||
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
||||
return nullptr;
|
||||
}
|
||||
return res;
|
||||
wibo::ResourceLocation loc;
|
||||
if (!exe->findResource(type, name, language, loc)) {
|
||||
return nullptr;
|
||||
}
|
||||
return const_cast<void *>(loc.dataEntry);
|
||||
}
|
||||
|
||||
void *WIN_FUNC FindResourceA(void *hModule, const char *lpName, const char *lpType) {
|
||||
DEBUG_LOG("FindResourceA %p %p %p\n", hModule, lpName, lpType);
|
||||
auto type = wibo::resourceIdentifierFromAnsi(lpType);
|
||||
auto name = wibo::resourceIdentifierFromAnsi(lpName);
|
||||
return find_resource_internal(hModule, type, name, std::nullopt);
|
||||
}
|
||||
|
||||
void *WIN_FUNC FindResourceExA(void *hModule, const char *lpType, const char *lpName, uint16_t wLanguage) {
|
||||
DEBUG_LOG("FindResourceExA %p %p %p %u\n", hModule, lpName, lpType, wLanguage);
|
||||
auto type = wibo::resourceIdentifierFromAnsi(lpType);
|
||||
auto name = wibo::resourceIdentifierFromAnsi(lpName);
|
||||
return find_resource_internal(hModule, type, name, wLanguage);
|
||||
}
|
||||
|
||||
// https://github.com/reactos/reactos/blob/master/dll/win32/kernelbase/wine/loader.c#L1090
|
||||
// https://github.com/wine-mirror/wine/blob/master/dlls/kernelbase/loader.c#L1097
|
||||
void *WIN_FUNC FindResourceW(void *hModule, const uint16_t *lpName, const uint16_t *lpType) {
|
||||
DEBUG_LOG("FindResourceW %p\n", hModule);
|
||||
auto type = wibo::resourceIdentifierFromWide(lpType);
|
||||
auto name = wibo::resourceIdentifierFromWide(lpName);
|
||||
return find_resource_internal(hModule, type, name, std::nullopt);
|
||||
}
|
||||
|
||||
if (!hModule)
|
||||
hModule = GetModuleHandleW(0);
|
||||
|
||||
const std::string name = resource_identifier_to_string(lpName);
|
||||
const std::string type = resource_identifier_to_string(lpType);
|
||||
|
||||
return open_resource_stream(type, name);
|
||||
void *WIN_FUNC FindResourceExW(void *hModule, const uint16_t *lpType, const uint16_t *lpName, uint16_t wLanguage) {
|
||||
DEBUG_LOG("FindResourceExW %p %u\n", hModule, wLanguage);
|
||||
auto type = wibo::resourceIdentifierFromWide(lpType);
|
||||
auto name = wibo::resourceIdentifierFromWide(lpName);
|
||||
return find_resource_internal(hModule, type, name, wLanguage);
|
||||
}
|
||||
|
||||
void* WIN_FUNC LoadResource(void* hModule, void* res) {
|
||||
DEBUG_LOG("LoadResource %p %p\n", hModule, res);
|
||||
|
||||
if(!hModule || !res) return nullptr;
|
||||
FILE* hRes = (FILE*)res;
|
||||
|
||||
long pos = ftell(hRes);
|
||||
DEBUG_LOG("Pos: %d\n", pos);
|
||||
fseek(hRes, 0, SEEK_END);
|
||||
long size = ftell(hRes);
|
||||
fseek(hRes, pos, SEEK_SET);
|
||||
DEBUG_LOG("Size: %d\n", size);
|
||||
|
||||
if(size <= 0) return nullptr;
|
||||
|
||||
void* buffer = malloc(size);
|
||||
if(!buffer) return nullptr;
|
||||
|
||||
if(fread(buffer, 1, size, hRes) != (size_t)size){
|
||||
free(buffer);
|
||||
if (!res) {
|
||||
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
||||
return nullptr;
|
||||
}
|
||||
return buffer;
|
||||
|
||||
// return (void*)0x100003;
|
||||
auto *exe = module_executable_for_resource(hModule);
|
||||
if (!exe || !exe->rsrcBase) {
|
||||
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
||||
return nullptr;
|
||||
}
|
||||
const auto *entry = reinterpret_cast<const wibo::ImageResourceDataEntry *>(res);
|
||||
if (!wibo::resourceEntryBelongsToExecutable(*exe, entry)) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return nullptr;
|
||||
}
|
||||
return const_cast<void *>(exe->fromRVA<const void>(entry->offsetToData));
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC GetDiskFreeSpaceExW(const uint16_t* lpDirectoryName,
|
||||
@ -1785,12 +1771,26 @@ namespace kernel32 {
|
||||
|
||||
void* WIN_FUNC LockResource(void* res) {
|
||||
DEBUG_LOG("LockResource %p\n", res);
|
||||
return (void*)0x100004;
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned int WIN_FUNC SizeofResource(void* hModule, void* res) {
|
||||
DEBUG_LOG("SizeofResource %p %p\n", hModule, res);
|
||||
return 0;
|
||||
if (!res) {
|
||||
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
||||
return 0;
|
||||
}
|
||||
auto *exe = module_executable_for_resource(hModule);
|
||||
if (!exe || !exe->rsrcBase) {
|
||||
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
||||
return 0;
|
||||
}
|
||||
const auto *entry = reinterpret_cast<const wibo::ImageResourceDataEntry *>(res);
|
||||
if (!wibo::resourceEntryBelongsToExecutable(*exe, entry)) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return 0;
|
||||
}
|
||||
return entry->size;
|
||||
}
|
||||
|
||||
HMODULE WIN_FUNC LoadLibraryA(LPCSTR lpLibFileName) {
|
||||
@ -2712,7 +2712,9 @@ static void *resolveByName(const char *name) {
|
||||
if (strcmp(name, "GetCurrentDirectoryA") == 0) return (void *) kernel32::GetCurrentDirectoryA;
|
||||
if (strcmp(name, "GetCurrentDirectoryW") == 0) return (void *) kernel32::GetCurrentDirectoryW;
|
||||
if (strcmp(name, "FindResourceA") == 0) return (void *) kernel32::FindResourceA;
|
||||
if (strcmp(name, "FindResourceExA") == 0) return (void *) kernel32::FindResourceExA;
|
||||
if (strcmp(name, "FindResourceW") == 0) return (void *) kernel32::FindResourceW;
|
||||
if (strcmp(name, "FindResourceExW") == 0) return (void *) kernel32::FindResourceExW;
|
||||
if (strcmp(name, "SetHandleCount") == 0) return (void *) kernel32::SetHandleCount;
|
||||
if (strcmp(name, "FormatMessageA") == 0) return (void *) kernel32::FormatMessageA;
|
||||
if (strcmp(name, "GetComputerNameA") == 0) return (void *) kernel32::GetComputerNameA;
|
||||
|
130
dll/user32.cpp
130
dll/user32.cpp
@ -1,104 +1,54 @@
|
||||
#include "common.h"
|
||||
#include "strutil.h"
|
||||
|
||||
namespace user32 {
|
||||
struct Resource {
|
||||
uint32_t id;
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
struct ResourceTable {
|
||||
char pad[12];
|
||||
uint16_t nameEntryCount;
|
||||
uint16_t idEntryCount;
|
||||
Resource resources[];
|
||||
};
|
||||
|
||||
static unsigned int searchResourceTableByID(const char *tableAddr, unsigned int id) {
|
||||
ResourceTable* table = (ResourceTable*)tableAddr;
|
||||
for (int i = 0; i < table->idEntryCount; i++) {
|
||||
const Resource& r = table->resources[table->nameEntryCount + i];
|
||||
if (r.id == id) {
|
||||
return r.value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int* getResourceByID(wibo::Executable *mod, unsigned int typeID, unsigned int nameID, unsigned int languageID) {
|
||||
const char *rsrcBase = (const char *)mod->rsrcBase;
|
||||
|
||||
if (rsrcBase == 0) {
|
||||
DEBUG_LOG("getResourceByID: no .rsrc section\n");
|
||||
wibo::lastError = 1812; // ERROR_RESOURCE_DATA_NOT_FOUND
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int typeTable = searchResourceTableByID(rsrcBase, typeID) & 0x7FFFFFFFu;
|
||||
if (typeTable == 0) {
|
||||
DEBUG_LOG("getResourceByID: no type table with id = %s\n", typeID);
|
||||
wibo::lastError = 1813; // ERROR_RESOURCE_TYPE_NOT_FOUND
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int nameTable = searchResourceTableByID(rsrcBase + typeTable, nameID) & 0x7FFFFFFFu;
|
||||
if (nameTable == 0) {
|
||||
DEBUG_LOG("getResourceByID: no name table with id = %s\n", nameID);
|
||||
wibo::lastError = 1814; // ERROR_RESOURCE_NAME_NOT_FOUND
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int langEntry = searchResourceTableByID(rsrcBase + nameTable, languageID);
|
||||
if (langEntry == 0) {
|
||||
DEBUG_LOG("getResourceByID: no lang entry with id = %s\n", languageID);
|
||||
wibo::lastError = 1814; // ERROR_RESOURCE_NAME_NOT_FOUND
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (unsigned int*)(rsrcBase + langEntry);
|
||||
}
|
||||
|
||||
static const char *getStringFromTable(wibo::Executable *mod, unsigned int uID) {
|
||||
unsigned int tableID = (uID >> 4) + 1;
|
||||
unsigned int entryID = uID & 15;
|
||||
unsigned int* stringTable = getResourceByID(mod, 6, tableID, 1033);
|
||||
if (stringTable == 0)
|
||||
return 0;
|
||||
|
||||
// what's in here?
|
||||
const char *str = mod->fromRVA<const char>(stringTable[0]);
|
||||
unsigned int size = stringTable[1];
|
||||
assert(entryID < size);
|
||||
|
||||
// skip over strings to get to the one we want
|
||||
for (unsigned int i = 0; i < entryID; i++) {
|
||||
int stringSize = *(uint16_t*)str;
|
||||
str += 2;
|
||||
str += stringSize * 2;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
constexpr uint32_t RT_STRING_ID = 6;
|
||||
|
||||
int WIN_FUNC LoadStringA(void* hInstance, unsigned int uID, char* lpBuffer, int cchBufferMax) {
|
||||
DEBUG_LOG("LoadStringA %p %d %d\n", hInstance, uID, cchBufferMax);
|
||||
wibo::Executable *mod = wibo::executableFromModule(hInstance);
|
||||
DEBUG_LOG("LoadStringA %p %u %d\n", hInstance, uID, cchBufferMax);
|
||||
if (!lpBuffer || cchBufferMax <= 0) {
|
||||
return 0;
|
||||
}
|
||||
wibo::Executable *mod = wibo::executableFromModule((HMODULE) hInstance);
|
||||
if (!mod) {
|
||||
return 0;
|
||||
}
|
||||
const char* s = getStringFromTable(mod, uID);
|
||||
if (!s) {
|
||||
wibo::ResourceIdentifier type = wibo::ResourceIdentifier::fromID(RT_STRING_ID);
|
||||
wibo::ResourceIdentifier table = wibo::ResourceIdentifier::fromID((uID >> 4) + 1);
|
||||
wibo::ResourceLocation loc;
|
||||
if (!mod->findResource(type, table, std::nullopt, loc)) {
|
||||
return 0;
|
||||
}
|
||||
int len = *(int16_t*)s;
|
||||
s += 2;
|
||||
assert(cchBufferMax != 0);
|
||||
len = (len < cchBufferMax - 1 ? len : cchBufferMax - 1);
|
||||
for (int i = 0; i < len; i++) {
|
||||
lpBuffer[i] = s[i * 2];
|
||||
const uint16_t *cursor = reinterpret_cast<const uint16_t *>(loc.data);
|
||||
const uint16_t *end = cursor + (loc.size / sizeof(uint16_t));
|
||||
unsigned int entryIndex = uID & 0x0Fu;
|
||||
for (unsigned int i = 0; i < entryIndex; ++i) {
|
||||
if (cursor >= end) {
|
||||
return 0;
|
||||
}
|
||||
uint16_t length = *cursor++;
|
||||
if (cursor + length > end) {
|
||||
return 0;
|
||||
}
|
||||
cursor += length;
|
||||
}
|
||||
lpBuffer[len] = 0;
|
||||
DEBUG_LOG("returning: %s\n", lpBuffer);
|
||||
return len;
|
||||
if (cursor >= end) {
|
||||
return 0;
|
||||
}
|
||||
uint16_t length = *cursor++;
|
||||
if (cursor + length > end) {
|
||||
return 0;
|
||||
}
|
||||
int copyLength = length;
|
||||
if (copyLength > cchBufferMax - 1) {
|
||||
copyLength = cchBufferMax - 1;
|
||||
}
|
||||
for (int i = 0; i < copyLength; ++i) {
|
||||
lpBuffer[i] = static_cast<char>(cursor[i] & 0xFF);
|
||||
}
|
||||
lpBuffer[copyLength] = 0;
|
||||
DEBUG_LOG("LoadStringA -> %.*s\n", copyLength, lpBuffer);
|
||||
return copyLength;
|
||||
}
|
||||
|
||||
int WIN_FUNC MessageBoxA(void *hwnd, const char *lpText, const char *lpCaption, unsigned int uType) {
|
||||
|
@ -117,6 +117,7 @@ wibo::Executable::Executable() {
|
||||
imageSize = 0;
|
||||
entryPoint = nullptr;
|
||||
rsrcBase = 0;
|
||||
rsrcSize = 0;
|
||||
preferredImageBase = 0;
|
||||
relocationDelta = 0;
|
||||
exportDirectoryRVA = 0;
|
||||
@ -211,6 +212,7 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
|
||||
if (strcmp(name, ".rsrc") == 0) {
|
||||
rsrcBase = sectionBase;
|
||||
rsrcSize = std::max(section.virtualSize, section.sizeOfRawData);
|
||||
}
|
||||
}
|
||||
|
||||
|
248
resources.cpp
Normal file
248
resources.cpp
Normal file
@ -0,0 +1,248 @@
|
||||
#include "common.h"
|
||||
#include "resources.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
|
13
resources.h
Normal file
13
resources.h
Normal file
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace wibo {
|
||||
|
||||
struct Executable;
|
||||
struct ImageResourceDataEntry;
|
||||
|
||||
bool resourceEntryBelongsToExecutable(const Executable &exe, const ImageResourceDataEntry *entry);
|
||||
ResourceIdentifier resourceIdentifierFromAnsi(const char *id);
|
||||
ResourceIdentifier resourceIdentifierFromWide(const uint16_t *id);
|
||||
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
CC = i686-w64-mingw32-gcc
|
||||
WINDRES = i686-w64-mingw32-windres
|
||||
CFLAGS = -Wall -Wextra -O2
|
||||
|
||||
DLL_SRC = external_exports.c
|
||||
@ -6,15 +7,26 @@ EXE_SRC = test_external_dll.c
|
||||
DLL = external_exports.dll
|
||||
EXE = test_external_dll.exe
|
||||
|
||||
all: $(DLL) $(EXE)
|
||||
RES_EXE_SRC = test_resources.c
|
||||
RES_RC = test_resources.rc
|
||||
RES_OBJ = test_resources_res.o
|
||||
RES_EXE = test_resources.exe
|
||||
|
||||
all: $(DLL) $(EXE) $(RES_EXE)
|
||||
|
||||
$(DLL): $(DLL_SRC)
|
||||
$(CC) $(CFLAGS) -shared -o $@ $<
|
||||
$(CC) $(CFLAGS) -shared -o $@ $<
|
||||
|
||||
$(EXE): $(EXE_SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(RES_OBJ): $(RES_RC)
|
||||
$(WINDRES) $< -O coff -o $@
|
||||
|
||||
$(RES_EXE): $(RES_EXE_SRC) $(RES_OBJ)
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
rm -f $(DLL) $(EXE)
|
||||
rm -f $(DLL) $(EXE) $(RES_EXE) $(RES_OBJ)
|
||||
|
||||
.PHONY: all clean
|
||||
|
25
test/test_resources.c
Normal file
25
test/test_resources.c
Normal file
@ -0,0 +1,25 @@
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
char buffer[128];
|
||||
int copied = LoadStringA(GetModuleHandleA(NULL), 100, buffer, sizeof(buffer));
|
||||
if (copied <= 0) {
|
||||
printf("LoadString failed: %lu\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
printf("STRING[100]=%s\n", buffer);
|
||||
|
||||
HRSRC versionInfo = FindResourceA(NULL, MAKEINTRESOURCEA(1), MAKEINTRESOURCEA(RT_VERSION));
|
||||
if (!versionInfo) {
|
||||
printf("FindResource version failed: %lu\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
DWORD versionSize = SizeofResource(NULL, versionInfo);
|
||||
if (!versionSize) {
|
||||
printf("SizeofResource failed: %lu\n", GetLastError());
|
||||
return 1;
|
||||
}
|
||||
printf("VERSION size=%lu\n", (unsigned long)versionSize);
|
||||
return 0;
|
||||
}
|
31
test/test_resources.rc
Normal file
31
test/test_resources.rc
Normal file
@ -0,0 +1,31 @@
|
||||
#include <windows.h>
|
||||
|
||||
#define IDS_SAMPLE 100
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_SAMPLE "Resource string 100"
|
||||
END
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 1,2,3,4
|
||||
PRODUCTVERSION 1,2,3,4
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEFLAGS 0x0L
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904B0"
|
||||
BEGIN
|
||||
VALUE "FileDescription", "Test Resource Binary\0"
|
||||
VALUE "ProductVersion", "1.2.3-test\0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x0409, 1200
|
||||
END
|
||||
END
|
Loading…
x
Reference in New Issue
Block a user