From 01ddf95d36802898411ae211ffbba96d4ead93e3 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Fri, 26 Sep 2025 01:51:25 -0600 Subject: [PATCH] Add proper resource implementation --- CMakeLists.txt | 1 + common.h | 47 +++++++- dll/kernel32.cpp | 138 ++++++++++++----------- dll/user32.cpp | 130 +++++++-------------- loader.cpp | 2 + resources.cpp | 248 +++++++++++++++++++++++++++++++++++++++++ resources.h | 13 +++ test/Makefile | 20 +++- test/test_resources.c | 25 +++++ test/test_resources.rc | 31 ++++++ 10 files changed, 491 insertions(+), 164 deletions(-) create mode 100644 resources.cpp create mode 100644 resources.h create mode 100644 test/test_resources.c create mode 100644 test/test_resources.rc diff --git a/CMakeLists.txt b/CMakeLists.txt index 755a4cb..eaed480 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable(wibo files.cpp handles.cpp loader.cpp + resources.cpp module_registry.cpp main.cpp processes.cpp diff --git a/common.h b/common.h index 5f4a8e9..4201116 100644 --- a/common.h +++ b/common.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -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 language, + ResourceLocation &out) const; + template - T *fromRVA(uint32_t rva) { + T *fromRVA(uint32_t rva) const { return (T *) (rva + (uint8_t *) imageBuffer); } template - T *fromRVA(T *rva) { + T *fromRVA(T *rva) const { return fromRVA((uint32_t) rva); } }; diff --git a/dll/kernel32.cpp b/dll/kernel32.cpp index ceaf447..97f05c3 100644 --- a/dll/kernel32.cpp +++ b/dll/kernel32.cpp @@ -2,6 +2,7 @@ #include "files.h" #include "processes.h" #include "handles.h" +#include "resources.h" #include #include #include @@ -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((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((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 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(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(res); + if (!wibo::resourceEntryBelongsToExecutable(*exe, entry)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + return const_cast(exe->fromRVA(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(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; diff --git a/dll/user32.cpp b/dll/user32.cpp index eda8744..b9cbaea 100644 --- a/dll/user32.cpp +++ b/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(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(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(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) { diff --git a/loader.cpp b/loader.cpp index 5d5b80d..b074cf7 100644 --- a/loader.cpp +++ b/loader.cpp @@ -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); } } diff --git a/resources.cpp b/resources.cpp new file mode 100644 index 0000000..380bbbb --- /dev/null +++ b/resources.cpp @@ -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(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(base + offset); + uint16_t length = *lengthPtr; + size_t bytesNeeded = sizeof(uint16_t) + static_cast(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(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(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 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(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(primaryMatch->name & 0xFFFFu); + return primaryMatch; + } + if (neutralMatch) { + chosenLang = 0; + return neutralMatch; + } + if (first) { + chosenLang = static_cast(first->name & 0xFFFFu); + return first; + } + return nullptr; +} + +} // namespace + +namespace wibo { + +bool Executable::findResource(const ResourceIdentifier &type, + const ResourceIdentifier &name, + std::optional language, + ResourceLocation &out) const { + const uint8_t *base = reinterpret_cast(rsrcBase); + if (!base) { + wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; + return false; + } + const auto *root = reinterpret_cast(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(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(exe.rsrcBase); + const auto *ptr = reinterpret_cast(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(*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(reinterpret_cast(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(reinterpret_cast(id))); + } + return ResourceIdentifier::fromString(wideToU16String(id)); +} + +} // namespace wibo diff --git a/resources.h b/resources.h new file mode 100644 index 0000000..28e80d6 --- /dev/null +++ b/resources.h @@ -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); + +} diff --git a/test/Makefile b/test/Makefile index 2b4d545..4b4eebb 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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 diff --git a/test/test_resources.c b/test/test_resources.c new file mode 100644 index 0000000..04c40a1 --- /dev/null +++ b/test/test_resources.c @@ -0,0 +1,25 @@ +#include +#include + +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; +} diff --git a/test/test_resources.rc b/test/test_resources.rc new file mode 100644 index 0000000..bd9c30a --- /dev/null +++ b/test/test_resources.rc @@ -0,0 +1,31 @@ +#include + +#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