Add proper resource implementation

This commit is contained in:
Luke Street 2025-09-26 01:51:25 -06:00
parent 720e6639a9
commit 01ddf95d36
10 changed files with 491 additions and 164 deletions

View File

@ -31,6 +31,7 @@ add_executable(wibo
files.cpp
handles.cpp
loader.cpp
resources.cpp
module_registry.cpp
main.cpp
processes.cpp

View File

@ -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);
}
};

View File

@ -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;

View File

@ -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) {

View File

@ -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
View 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
View 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);
}

View File

@ -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
View 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
View 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