diff --git a/.clang-tidy b/.clang-tidy index 0b060b7..7ee5aa7 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -92,7 +92,6 @@ modernize-replace-random-shuffle, modernize-return-braced-init-list, modernize-shrink-to-fit, modernize-unary-static-assert, -modernize-use-auto, modernize-use-bool-literals, modernize-use-emplace, modernize-use-equals-default, @@ -143,4 +142,4 @@ readability-static-accessed-through-instance, readability-static-definition-in-anonymous-namespace, readability-string-compare, readability-uniqueptr-delete-release, -readability-use-anyofallof' \ No newline at end of file +readability-use-anyofallof' diff --git a/AGENTS.md b/AGENTS.md index a8c1723..d11ad4a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,20 +4,19 @@ - Core launcher logic lives in `main.cpp`, `loader.cpp`, `files.cpp`, `handles.cpp` and `module_registry.cpp`; shared interfaces in headers near them. - Windows API shims reside in `dll/`, grouped by emulated DLL name; keep new APIs in the matching file instead of creating ad-hoc helpers. - Reusable utilities sit in `strutil.*`, `processes.*` and `resources.*`; prefer extending these before introducing new singleton modules. -- Sample fixtures for exercising the loader live in `test/`; keep new repros small and self-contained. +- Sample fixtures for exercising the loader live in `test/`. ## Build, Test, and Development Commands -- `cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON` configures a 32-bit toolchain; ensure multilib packages are present. -- `cmake --build build --target wibo` compiles the shim; switch to `-DCMAKE_BUILD_TYPE=Release` for optimised binaries. +- `cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DBUILD_TESTING=ON` configures a 32-bit toolchain; ensure multilib packages are present. +- `cmake --build build --target wibo` compiles the program and tests. - `./build/wibo /path/to/program.exe` runs a Windows binary. Use `WIBO_DEBUG=1` (or `--debug`/`-D`) for verbose logging. Use `--chdir`/`-C` to set the working directory. - `cmake -B build -DBUILD_TESTING=ON` + `ctest --test-dir build --output-on-failure` runs the self-checking WinAPI fixtures (requires `i686-w64-mingw32-gcc` and `i686-w64-mingw32-windres`). - `clang-format -i path/to/file.cpp` and `clang-tidy path/to/file.cpp -p build` keep contributions aligned with the repo's tooling. -- DON'T use `clang-format` on existing files, only new or heavily modified ones; the repo hasn't been fully formatted yet. ## Coding Style & Naming Conventions - Formatting follows `.clang-format` (LLVM base, tabbed indentation width 4, 120 column limit); never hand-wrap differently. -- Prefer PascalCase for emulated Win32 entry points, camelCase for internal helpers, and SCREAMING_SNAKE_CASE for constants or macros. -- Document non-obvious control flow with short comments and keep platform-specific code paths behind descriptive helper functions. +- Use PascalCase for Win32 entry points, camelCase for internal helpers, SCREAMING_SNAKE_CASE for Win32 constants, kCamelCase for internal constants, and g_camelCase for globals. +- Put static functions and variables in anonymous namespaces at the top of the file. ## Shim Implementation Guidelines - Target pre-XP behavior; our binaries are old and don't expect modern WinAPI behavior. @@ -26,14 +25,14 @@ - Add `DEBUG_LOG` calls to trace execution and parameter values; these are invaluable when diagnosing issues with real-world binaries. ## Testing Guidelines -- Fixture binaries live in `test/` and are compiled automatically when `BUILD_TESTING` is enabled; keep new repros small and self-contained (`test_.c`). +- Fixture tests live in `test/` and are compiled automatically with `i686-w64-mingw32-gcc` when `BUILD_TESTING` is enabled. +- Keep new repros small and self-contained (`test_.c`). - All fixtures must self-assert; use `test_assert.h` helpers so `ctest` fails on mismatched WinAPI behaviour. -- Cross-compile new repros with `i686-w64-mingw32-gcc` (and `i686-w64-mingw32-windres` for resources); CMake handles this during the build, but direct invocation is useful while iterating. -- Run `ctest --test-dir build --output-on-failure` after rebuilding to verify changes; ensure failures print actionable diagnostics. +- Update `CMakeLists.txt` to add new fixture sources. +- Rebuild, then run tests with `ctest --test-dir build --output-on-failure`. +- Always run tests against `wine` manually to confirm expected behaviour. If `wine` fails, the expected behaviour is likely wrong. (`wine` is not perfect, but we can assume it's closer to Windows than we are.) ## Debugging Workflow - Reproduce crashes under `gdb` (or `lldb`) with `-q -batch` to capture backtraces, register state, and the faulting instruction without interactive prompts. -- Enable `WIBO_DEBUG=1` and tee output to a log when running the guest binary; loader traces often pinpoint missing imports, resource lookups, or API shims that misbehave. -- Inspect relevant source right away—most issues stem from stubbed shims in `dll/`; compare the guest stack from `gdb` with those implementations. -- When host-side behaviour is suspect (filesystem, execve, etc.), rerun under `strace -f -o `; this highlights missing files or permissions before the shim faults. -- If the `ghidra` MCP tool is available, request that the user import and analyze the guest binary; you can then use it to disassemble/decompile code around the crash site. +- Enable `WIBO_DEBUG=1` and output to a log (i.e. `&>/tmp/wibo.log`) when running the guest binary; loader traces often pinpoint missing imports, resource lookups, or API shims that misbehave. The answer is usually in the last few dozen lines before the crash. +- Inspect relevant source right away—most issues stem from stubbed shims in `dll/`. diff --git a/CMakeLists.txt b/CMakeLists.txt index 706162a..2fb4794 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,9 +38,23 @@ add_executable(wibo dll/kernel32.cpp dll/kernel32/errhandlingapi.cpp dll/kernel32/fibersapi.cpp + dll/kernel32/heapapi.cpp + dll/kernel32/memoryapi.cpp + dll/kernel32/handleapi.cpp + dll/kernel32/fileapi.cpp + dll/kernel32/debugapi.cpp + dll/kernel32/interlockedapi.cpp + dll/kernel32/libloaderapi.cpp + dll/kernel32/winbase.cpp + dll/kernel32/wincon.cpp + dll/kernel32/processenv.cpp + dll/kernel32/profileapi.cpp dll/kernel32/processthreadsapi.cpp + dll/kernel32/stringapiset.cpp dll/kernel32/sysinfoapi.cpp + dll/kernel32/timezoneapi.cpp dll/kernel32/synchapi.cpp + dll/kernel32/winnls.cpp dll/kernel32/wow64apiset.cpp dll/lmgr.cpp dll/mscoree.cpp diff --git a/common.h b/common.h index 9d37dde..75916d4 100644 --- a/common.h +++ b/common.h @@ -9,244 +9,239 @@ #include #include #include -#include -#include #include #include +#include +#include #include // On Windows, the incoming stack is aligned to a 4 byte boundary. // force_align_arg_pointer will realign the stack to match GCC's 16 byte alignment. +#ifdef __clang__ +#define WIN_ENTRY __attribute__((force_align_arg_pointer)) +#else #define WIN_ENTRY __attribute__((force_align_arg_pointer, callee_pop_aggregate_return(0))) +#endif #define WIN_FUNC WIN_ENTRY __attribute__((stdcall)) -#define DEBUG_LOG(...) \ - do { \ - if (wibo::debugEnabled) { \ - wibo::debug_log(__VA_ARGS__); \ - } \ - } while (0) +#define DEBUG_LOG(...) \ + do { \ + if (wibo::debugEnabled) { \ + wibo::debug_log(__VA_ARGS__); \ + } \ + } while (0) #ifndef NDEBUG #define VERBOSE_LOG(...) DEBUG_LOG(__VA_ARGS__) #else #define VERBOSE_LOG(...) ((void)0) #endif -typedef void *HANDLE; -typedef void *HMODULE; -typedef void *PVOID; -typedef void *LPVOID; -typedef void *FARPROC; -typedef uint16_t WORD; -typedef uint32_t DWORD; -typedef DWORD *PDWORD; -typedef DWORD *LPDWORD; -typedef int32_t LONG; -typedef LONG *PLONG; -typedef uint32_t ULONG; -typedef ULONG *PULONG; -typedef int64_t LARGE_INTEGER; -typedef LARGE_INTEGER *PLARGE_INTEGER; -typedef uintptr_t ULONG_PTR; -typedef ULONG_PTR DWORD_PTR; -typedef DWORD_PTR *PDWORD_PTR; -typedef char *LPSTR; -typedef const char *LPCSTR; -typedef uint16_t *LPWSTR; -typedef const uint16_t *LPCWSTR; -typedef int BOOL; -typedef BOOL *PBOOL; -typedef unsigned char UCHAR; -typedef UCHAR *PUCHAR; -typedef size_t SIZE_T; -typedef SIZE_T *PSIZE_T; -typedef unsigned char BYTE; -typedef unsigned int UINT; -typedef void *HKEY; -typedef HKEY *PHKEY; -typedef DWORD REGSAM; -typedef LONG LSTATUS; +using HANDLE = void *; +using HMODULE = void *; +using HGLOBAL = HANDLE; +using HLOCAL = HANDLE; +using HRSRC = HANDLE; +using LPHANDLE = HANDLE *; +using PVOID = void *; +using LPVOID = void *; +using LPCVOID = const void *; +using FARPROC = void *; +using WORD = uint16_t; +using LPWORD = WORD *; +using LANGID = WORD; +using DWORD = uint32_t; +using PDWORD = DWORD *; +using LPDWORD = DWORD *; +using LONG = int32_t; +using PLONG = LONG *; +using ULONG = uint32_t; +using PULONG = ULONG *; +using LARGE_INTEGER = int64_t; +using PLARGE_INTEGER = LARGE_INTEGER *; +using ULONG_PTR = uintptr_t; +using UINT_PTR = uintptr_t; +using DWORD_PTR = ULONG_PTR; +using PDWORD_PTR = DWORD_PTR *; +using SHORT = int16_t; +using LPSTR = char *; +using LPCSTR = const char *; +using LPCCH = const char *; +using LPWSTR = uint16_t *; +using LPCWSTR = const uint16_t *; +using LPCWCH = const uint16_t *; +using WCHAR = uint16_t; +using LPCH = char *; +using LPWCH = uint16_t *; +using BOOL = int; +using PBOOL = BOOL *; +using LPBOOL = BOOL *; +using UCHAR = unsigned char; +using PUCHAR = UCHAR *; +using SIZE_T = size_t; +using PSIZE_T = SIZE_T *; +using BYTE = unsigned char; +using BOOLEAN = unsigned char; +using UINT = unsigned int; +using HKEY = void *; +using PHKEY = HKEY *; +using REGSAM = DWORD; +using LSTATUS = LONG; +using LCID = DWORD; +using LCTYPE = DWORD; -typedef struct _OVERLAPPED { - ULONG_PTR Internal; - ULONG_PTR InternalHigh; - union { - struct { - DWORD Offset; - DWORD OffsetHigh; - }; - PVOID Pointer; - }; - HANDLE hEvent; -} OVERLAPPED, *LPOVERLAPPED; +constexpr BOOL TRUE = 1; +constexpr BOOL FALSE = 0; -#define TRUE 1 -#define FALSE 0 +constexpr DWORD STILL_ACTIVE = 259; -#define STILL_ACTIVE 259 +constexpr DWORD FILE_FLAG_OVERLAPPED = 0x40000000; +constexpr DWORD FILE_FLAG_NO_BUFFERING = 0x20000000; -#define TIME_ZONE_ID_UNKNOWN 0 -#define TIME_ZONE_ID_STANDARD 1 -#define TIME_ZONE_ID_DAYLIGHT 2 - -#define FILE_FLAG_OVERLAPPED 0x40000000 -#define FILE_FLAG_NO_BUFFERING 0x20000000 - -#define MAX_PATH (260) - -#define STD_INPUT_HANDLE ((DWORD) - 10) -#define STD_OUTPUT_HANDLE ((DWORD) - 11) -#define STD_ERROR_HANDLE ((DWORD) - 12) +constexpr DWORD STD_INPUT_HANDLE = ((DWORD)-10); +constexpr DWORD STD_OUTPUT_HANDLE = ((DWORD)-11); +constexpr DWORD STD_ERROR_HANDLE = ((DWORD)-12); namespace wibo { - extern uint32_t lastError; - extern char **argv; - extern int argc; - extern std::filesystem::path guestExecutablePath; - extern std::string executableName; - extern std::string commandLine; - extern std::vector commandLineW; - extern bool debugEnabled; - extern unsigned int debugIndent; - extern uint16_t tibSelector; +extern thread_local uint32_t lastError; +extern char **argv; +extern int argc; +extern std::filesystem::path guestExecutablePath; +extern std::string executableName; +extern std::string commandLine; +extern std::vector commandLineW; +extern bool debugEnabled; +extern unsigned int debugIndent; +extern uint16_t tibSelector; - void debug_log(const char *fmt, ...); +void debug_log(const char *fmt, ...); - using ResolveByName = void *(*)(const char *); - using ResolveByOrdinal = void *(*)(uint16_t); - struct Module { - const char** names; - ResolveByName byName; - ResolveByOrdinal byOrdinal; - }; - struct ModuleInfo; - void initializeModuleRegistry(); - void shutdownModuleRegistry(); - ModuleInfo *moduleInfoFromHandle(HMODULE module); - void setDllDirectoryOverride(const std::filesystem::path &path); - void clearDllDirectoryOverride(); - std::optional dllDirectoryOverride(); - ModuleInfo *findLoadedModule(const char *name); - void registerOnExitTable(void *table); - void addOnExitFunction(void *table, void (*func)()); - void executeOnExitTable(void *table); - void runPendingOnExit(ModuleInfo &info); +using ResolveByName = void *(*)(const char *); +using ResolveByOrdinal = void *(*)(uint16_t); +struct Module { + const char **names; + ResolveByName byName; + ResolveByOrdinal byOrdinal; +}; +struct ModuleInfo; +void initializeModuleRegistry(); +void shutdownModuleRegistry(); +ModuleInfo *moduleInfoFromHandle(HMODULE module); +void setDllDirectoryOverride(const std::filesystem::path &path); +void clearDllDirectoryOverride(); +std::optional dllDirectoryOverride(); +ModuleInfo *findLoadedModule(const char *name); +void registerOnExitTable(void *table); +void addOnExitFunction(void *table, void (*func)()); +void executeOnExitTable(void *table); +void runPendingOnExit(ModuleInfo &info); - ModuleInfo *loadModule(const char *name); - void freeModule(ModuleInfo *info); - void *resolveFuncByName(ModuleInfo *info, const char *funcName); - void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal); - void *resolveMissingImportByName(const char *dllName, const char *funcName); - void *resolveMissingImportByOrdinal(const char *dllName, uint16_t ordinal); +ModuleInfo *loadModule(const char *name); +void freeModule(ModuleInfo *info); +void *resolveFuncByName(ModuleInfo *info, const char *funcName); +void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal); +void *resolveMissingImportByName(const char *dllName, const char *funcName); +void *resolveMissingImportByOrdinal(const char *dllName, 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() = default; - ~Executable(); - bool loadPE(FILE *file, bool exec); - bool resolveImports(); - - void *imageBase = nullptr; - size_t imageSize = 0; - void *entryPoint = nullptr; - void *rsrcBase = nullptr; - uint32_t rsrcSize = 0; - uintptr_t preferredImageBase = 0; - intptr_t relocationDelta = 0; - uint32_t exportDirectoryRVA = 0; - uint32_t exportDirectorySize = 0; - uint32_t relocationDirectoryRVA = 0; - uint32_t relocationDirectorySize = 0; - uint32_t importDirectoryRVA = 0; - uint32_t importDirectorySize = 0; - uint32_t delayImportDirectoryRVA = 0; - uint32_t delayImportDirectorySize = 0; - bool execMapped = false; - bool importsResolved = false; - bool importsResolving = false; - - bool findResource(const ResourceIdentifier &type, const ResourceIdentifier &name, - std::optional language, ResourceLocation &out) const; - - template - T *fromRVA(uintptr_t rva) const { - return (T *) (rva + (uint8_t *) imageBase); - } - - template - T *fromRVA(T *rva) const { - return fromRVA((uintptr_t) rva); - } - }; - - extern ModuleInfo *mainModule; - struct ModuleInfo { - // Windows-style handle to the module. For the main module, this is the image base. - // For other modules, this is a pointer to the ModuleInfo structure. - HMODULE handle; - // Original name used to load the module - std::string originalName; - // Normalized module name - std::string normalizedName; - // Full path to the loaded module - std::filesystem::path resolvedPath; - // Pointer to the built-in module, nullptr if loaded from file - const wibo::Module *module = nullptr; - // Loaded PE executable - std::unique_ptr executable; - // Reference count, or UINT_MAX for built-in modules - unsigned int refCount = 0; - bool processAttachCalled = false; - bool processAttachSucceeded = false; - uint32_t exportOrdinalBase = 0; - std::vector exportsByOrdinal; - std::unordered_map exportNameToOrdinal; - bool exportsInitialized = false; - std::vector onExitFunctions; - }; - - ModuleInfo *registerProcessModule(std::unique_ptr executable, std::filesystem::path resolvedPath, - std::string originalName); - Executable *executableFromModule(HMODULE module); - - /** - * HMODULE will be `nullptr` or `mainModule->imageBase` if it's the main module, - * otherwise it will be a pointer to a `wibo::ModuleInfo`. - */ - inline bool isMainModule(HMODULE hModule) { - return hModule == nullptr || hModule == reinterpret_cast(mainModule) || - (mainModule && mainModule->executable && hModule == mainModule->executable->imageBase); +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() = default; + ~Executable(); + bool loadPE(FILE *file, bool exec); + bool resolveImports(); + + void *imageBase = nullptr; + size_t imageSize = 0; + void *entryPoint = nullptr; + void *rsrcBase = nullptr; + uint32_t rsrcSize = 0; + uintptr_t preferredImageBase = 0; + intptr_t relocationDelta = 0; + uint32_t exportDirectoryRVA = 0; + uint32_t exportDirectorySize = 0; + uint32_t relocationDirectoryRVA = 0; + uint32_t relocationDirectorySize = 0; + uint32_t importDirectoryRVA = 0; + uint32_t importDirectorySize = 0; + uint32_t delayImportDirectoryRVA = 0; + uint32_t delayImportDirectorySize = 0; + bool execMapped = false; + bool importsResolved = false; + bool importsResolving = false; + + bool findResource(const ResourceIdentifier &type, const ResourceIdentifier &name, std::optional language, + ResourceLocation &out) const; + template T *fromRVA(uintptr_t rva) const { return (T *)(rva + (uint8_t *)imageBase); } + template T *fromRVA(T *rva) const { return fromRVA((uintptr_t)rva); } +}; + +extern ModuleInfo *mainModule; +struct ModuleInfo { + // Windows-style handle to the module. For the main module, this is the image base. + // For other modules, this is a pointer to the ModuleInfo structure. + HMODULE handle; + // Original name used to load the module + std::string originalName; + // Normalized module name + std::string normalizedName; + // Full path to the loaded module + std::filesystem::path resolvedPath; + // Pointer to the built-in module, nullptr if loaded from file + const wibo::Module *module = nullptr; + // Loaded PE executable + std::unique_ptr executable; + // Reference count, or UINT_MAX for built-in modules + unsigned int refCount = 0; + bool processAttachCalled = false; + bool processAttachSucceeded = false; + uint32_t exportOrdinalBase = 0; + std::vector exportsByOrdinal; + std::unordered_map exportNameToOrdinal; + bool exportsInitialized = false; + std::vector onExitFunctions; +}; + +ModuleInfo *registerProcessModule(std::unique_ptr executable, std::filesystem::path resolvedPath, + std::string originalName); +Executable *executableFromModule(HMODULE module); + +/** + * HMODULE will be `nullptr` or `mainModule->imageBase` if it's the main module, + * otherwise it will be a pointer to a `wibo::ModuleInfo`. + */ +inline bool isMainModule(HMODULE hModule) { + return hModule == nullptr || hModule == reinterpret_cast(mainModule) || + (mainModule && mainModule->executable && hModule == mainModule->executable->imageBase); +} } // namespace wibo diff --git a/dll/kernel32.cpp b/dll/kernel32.cpp index c32d17e..d1bb369 100644 --- a/dll/kernel32.cpp +++ b/dll/kernel32.cpp @@ -1,5845 +1,1461 @@ #include "common.h" -#include "files.h" #include "errors.h" -#include "processes.h" +#include "files.h" #include "handles.h" -#include "resources.h" -#include "kernel32/kernel32.h" +#include "kernel32/debugapi.h" +#include "kernel32/errhandlingapi.h" +#include "kernel32/fibersapi.h" +#include "kernel32/fileapi.h" +#include "kernel32/handleapi.h" +#include "kernel32/heapapi.h" +#include "kernel32/interlockedapi.h" #include "kernel32/internal.h" +#include "kernel32/libloaderapi.h" +#include "kernel32/memoryapi.h" +#include "kernel32/minwinbase.h" +#include "kernel32/processenv.h" +#include "kernel32/processthreadsapi.h" +#include "kernel32/profileapi.h" +#include "kernel32/stringapiset.h" +#include "kernel32/synchapi.h" +#include "kernel32/sysinfoapi.h" +#include "kernel32/timeutil.h" +#include "kernel32/timezoneapi.h" +#include "kernel32/winbase.h" +#include "kernel32/wincon.h" +#include "kernel32/winnls.h" +#include "kernel32/wow64apiset.h" +#include "processes.h" +#include "resources.h" +#include "strutil.h" #include +#include +#include #include #include #include -#include +#include #include -#include -#include +#include +#include +#include +#include #include +#include +#include #include #include -#include "strutil.h" -#include -#include -#include -#include -#include -#include -#include #include #include #include +#include #include -#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace advapi32 { - void releaseToken(void *tokenPtr); -} - -namespace { - struct MappingObject; - struct ViewInfo { - void *mapBase = nullptr; - size_t mapLength = 0; - MappingObject *owner = nullptr; - }; - - struct MappingObject { - int fd = -1; - size_t maxSize = 0; - unsigned int protect = 0; - bool anonymous = false; - bool closed = false; - size_t refCount = 0; - }; - - void closeMappingIfPossible(MappingObject *mapping); - void tryReleaseMapping(MappingObject *mapping); - std::unordered_map g_viewInfo; - - using DWORD_PTR = uintptr_t; - - void closeMappingIfPossible(MappingObject *mapping) { - if (!mapping) { - return; - } - if (mapping->fd != -1) { - close(mapping->fd); - mapping->fd = -1; - } - delete mapping; - } - - void tryReleaseMapping(MappingObject *mapping) { - if (!mapping) { - return; - } - if (mapping->closed && mapping->refCount == 0) { - closeMappingIfPossible(mapping); - } - } - - -} - -typedef union _RTL_RUN_ONCE { - PVOID Ptr; -} RTL_RUN_ONCE, *PRTL_RUN_ONCE; -typedef PRTL_RUN_ONCE LPINIT_ONCE; namespace kernel32 { - constexpr DWORD HEAP_NO_SERIALIZE = 0x00000001; - constexpr DWORD HEAP_GENERATE_EXCEPTIONS = 0x00000004; - constexpr DWORD HEAP_ZERO_MEMORY = 0x00000008; - constexpr DWORD HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010; - constexpr DWORD HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; - struct HeapRecord { - mi_heap_t *heap = nullptr; - DWORD createFlags = 0; - size_t initialSize = 0; - size_t maximumSize = 0; - DWORD compatibility = 0; - bool isProcessHeap = false; - }; +int64_t getFileSize(HANDLE hFile) { + FILE *fp = files::fpFromHandle(hFile); + if (!fp) { + wibo::lastError = ERROR_INVALID_HANDLE; + return -1; // INVALID_FILE_SIZE + } + struct stat64 st; + fflush(fp); + if (fstat64(fileno(fp), &st) == -1 || !S_ISREG(st.st_mode)) { + setLastErrorFromErrno(); + return -1; // INVALID_FILE_SIZE + } + return st.st_size; +} - static std::once_flag processHeapInitFlag; - static void *processHeapHandle = nullptr; - static HeapRecord *processHeapRecord = nullptr; +static const FILETIME defaultFiletime = {static_cast(UNIX_TIME_ZERO & 0xFFFFFFFFULL), + static_cast(UNIX_TIME_ZERO >> 32)}; - static void ensureProcessHeapInitialized() { - std::call_once(processHeapInitFlag, []() { - mi_heap_t *heap = mi_heap_get_default(); - auto *record = new (std::nothrow) HeapRecord{}; - record->heap = heap; - record->isProcessHeap = true; - processHeapRecord = record; - processHeapHandle = handles::allocDataHandle({handles::TYPE_HEAP, record, 0}); - }); +unsigned int WIN_FUNC GetFileAttributesA(const char *lpFileName) { + auto path = files::pathFromWindows(lpFileName); + DEBUG_LOG("GetFileAttributesA(%s) -> %s\n", lpFileName, path.c_str()); + + // See ole32::CoCreateInstance + if (endsWith(path, "/license.dat")) { + DEBUG_LOG("MWCC license override\n"); + return 0x80; // FILE_ATTRIBUTE_NORMAL } - static HeapRecord *activeHeapRecord(void *hHeap) { - if (!hHeap) { - wibo::lastError = ERROR_INVALID_HANDLE; - return nullptr; - } - ensureProcessHeapInitialized(); - auto data = handles::dataFromHandle(hHeap, false); - if (data.type != handles::TYPE_HEAP || data.ptr == nullptr) { - wibo::lastError = ERROR_INVALID_HANDLE; - return nullptr; - } - wibo::lastError = ERROR_SUCCESS; - return static_cast(data.ptr); + auto status = std::filesystem::status(path); + + wibo::lastError = 0; + + switch (status.type()) { + case std::filesystem::file_type::regular: + DEBUG_LOG("File exists\n"); + return 0x80; // FILE_ATTRIBUTE_NORMAL + case std::filesystem::file_type::directory: + return 0x10; // FILE_ATTRIBUTE_DIRECTORY + case std::filesystem::file_type::not_found: + default: + DEBUG_LOG("File does not exist\n"); + wibo::lastError = 2; // ERROR_FILE_NOT_FOUND + return 0xFFFFFFFF; // INVALID_FILE_ATTRIBUTES + } +} + +unsigned int WIN_FUNC GetFileAttributesW(const uint16_t *lpFileName) { + DEBUG_LOG("GetFileAttributesW -> "); + std::string str = wideStringToString(lpFileName); + return GetFileAttributesA(str.c_str()); +} + +unsigned int WIN_FUNC WriteFile(void *hFile, const void *lpBuffer, unsigned int nNumberOfBytesToWrite, + unsigned int *lpNumberOfBytesWritten, void *lpOverlapped) { + DEBUG_LOG("WriteFile(%p, %u)\n", hFile, nNumberOfBytesToWrite); + wibo::lastError = ERROR_SUCCESS; + + auto file = files::fileHandleFromHandle(hFile); + if (!file || !file->fp) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; } - static HeapRecord *popHeapRecord(void *hHeap) { - ensureProcessHeapInitialized(); - auto preview = handles::dataFromHandle(hHeap, false); - if (preview.type != handles::TYPE_HEAP || preview.ptr == nullptr) { - wibo::lastError = ERROR_INVALID_HANDLE; - return nullptr; - } - auto data = handles::dataFromHandle(hHeap, true); - wibo::lastError = ERROR_SUCCESS; - return static_cast(data.ptr); + bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0; + auto *overlapped = reinterpret_cast(lpOverlapped); + bool usingOverlapped = overlapped != nullptr; + if (!usingOverlapped && lpNumberOfBytesWritten == nullptr) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; } - static bool isExecutableHeap(const HeapRecord *record) { - return record && ((record->createFlags & HEAP_CREATE_ENABLE_EXECUTE) != 0); + std::optional offset; + bool updateFilePointer = true; + if (usingOverlapped) { + offset = (static_cast(overlapped->Offset) | (static_cast(overlapped->OffsetHigh) << 32)); + overlapped->Internal = STATUS_PENDING; + overlapped->InternalHigh = 0; + updateFilePointer = !handleOverlapped; + resetOverlappedEvent(overlapped); } - static void *doAlloc(unsigned int dwBytes, bool zero) { - if (dwBytes == 0) - dwBytes = 1; - void *ret = mi_malloc_aligned(dwBytes, 8); - if (ret && zero) { - memset(ret, 0, mi_usable_size(ret)); + auto io = files::write(file, lpBuffer, nNumberOfBytesToWrite, offset, updateFilePointer); + DWORD completionStatus = STATUS_SUCCESS; + if (io.unixError != 0) { + completionStatus = wibo::winErrorFromErrno(io.unixError); + wibo::lastError = completionStatus; + if (lpNumberOfBytesWritten) { + *lpNumberOfBytesWritten = static_cast(io.bytesTransferred); } - return ret; - } - - static void *doRealloc(void *mem, unsigned int dwBytes, bool zero) { - if (dwBytes == 0) - dwBytes = 1; - size_t oldSize = mi_usable_size(mem); - void *ret = mi_realloc_aligned(mem, dwBytes, 8); - size_t newSize = mi_usable_size(ret); - if (ret && zero && newSize > oldSize) { - memset((char*)ret + oldSize, 0, newSize - oldSize); - } - return ret; - } - - static void maybeMarkExecutable(void *mem) { - if (!mem) { - return; - } - size_t usable = mi_usable_size(mem); - if (usable == 0) { - return; - } - long pageSize = sysconf(_SC_PAGESIZE); - if (pageSize <= 0) { - return; - } - uintptr_t start = reinterpret_cast(mem); - uintptr_t alignedStart = start & ~static_cast(pageSize - 1); - uintptr_t end = (start + usable + pageSize - 1) & ~static_cast(pageSize - 1); - size_t length = static_cast(end - alignedStart); - if (length == 0) { - return; - } - mprotect(reinterpret_cast(alignedStart), length, PROT_READ | PROT_WRITE | PROT_EXEC); - } - - typedef DWORD (WIN_FUNC *LPTHREAD_START_ROUTINE)(LPVOID); - - - struct ThreadStartData { - LPTHREAD_START_ROUTINE startRoutine; - void *parameter; - ThreadObject *threadObject; - }; - - static void destroyThreadObject(ThreadObject *obj) { - if (!obj) { - return; - } - pthread_cond_destroy(&obj->cond); - pthread_mutex_destroy(&obj->mutex); - delete obj; - } - - static ThreadObject *retainThreadObject(ThreadObject *obj); - static void releaseThreadObject(ThreadObject *obj); - static void *threadTrampoline(void *param); - static ThreadObject *ensureCurrentThreadObject(); - static thread_local ThreadObject *currentThreadObject = nullptr; - static constexpr uintptr_t PSEUDO_CURRENT_THREAD_HANDLE_VALUE = static_cast(-2); - - static ThreadObject *threadObjectFromHandle(HANDLE hThread) { - auto raw = reinterpret_cast(hThread); - if (raw == PSEUDO_CURRENT_THREAD_HANDLE_VALUE) { - return ensureCurrentThreadObject(); - } - if (raw == static_cast(-1) || raw == 0) { - return nullptr; - } - auto data = handles::dataFromHandle(hThread, false); - if (data.type != handles::TYPE_THREAD || data.ptr == nullptr) { - return nullptr; - } - return reinterpret_cast(data.ptr); - } - - static ThreadObject *ensureCurrentThreadObject() { - ThreadObject *obj = currentThreadObject; - if (obj) { - return obj; - } - obj = new ThreadObject(); - obj->thread = pthread_self(); - obj->finished = false; - obj->joined = false; - obj->detached = true; - obj->synthetic = false; - obj->exitCode = STILL_ACTIVE; - obj->refCount = 0; - obj->suspendCount = 0; - pthread_mutex_init(&obj->mutex, nullptr); - pthread_cond_init(&obj->cond, nullptr); - currentThreadObject = obj; - return obj; - } - - static ThreadObject *retainThreadObject(ThreadObject *obj) { - if (!obj) { - return nullptr; - } - pthread_mutex_lock(&obj->mutex); - obj->refCount++; - pthread_mutex_unlock(&obj->mutex); - return obj; - } - - static void releaseThreadObject(ThreadObject *obj) { - if (!obj) { - return; - } - pthread_t thread = 0; - bool shouldDelete = false; - bool shouldDetach = false; - bool finished = false; - bool joined = false; - bool detached = false; - bool synthetic = false; - pthread_mutex_lock(&obj->mutex); - obj->refCount--; - finished = obj->finished; - joined = obj->joined; - detached = obj->detached; - synthetic = obj->synthetic; - thread = obj->thread; - if (obj->refCount == 0) { - if (finished || synthetic) { - shouldDelete = true; - } else if (!detached) { - obj->detached = true; - shouldDetach = true; - detached = true; - } - } - pthread_mutex_unlock(&obj->mutex); - - if (shouldDetach && !synthetic) { - pthread_detach(thread); - } - - if (shouldDelete) { - if (!synthetic) { - if (!joined && !detached) { - pthread_join(thread, nullptr); - } - } - destroyThreadObject(obj); - } - } - - static void *threadTrampoline(void *param) { - ThreadStartData *data = static_cast(param); - ThreadObject *obj = data->threadObject; - LPTHREAD_START_ROUTINE startRoutine = data->startRoutine; - void *userParam = data->parameter; - delete data; - - uint16_t previousSegment = 0; - bool tibInstalled = false; - if (wibo::tibSelector) { - asm volatile("mov %%fs, %0" : "=r"(previousSegment)); - asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory"); - tibInstalled = true; - } - - currentThreadObject = obj; - pthread_mutex_lock(&obj->mutex); - while (obj->suspendCount > 0) { - pthread_cond_wait(&obj->cond, &obj->mutex); - } - pthread_mutex_unlock(&obj->mutex); - DWORD result = startRoutine ? startRoutine(userParam) : 0; - pthread_mutex_lock(&obj->mutex); - obj->finished = true; - obj->exitCode = result; - pthread_cond_broadcast(&obj->cond); - bool shouldDelete = (obj->refCount == 0); - bool detached = obj->detached; - pthread_mutex_unlock(&obj->mutex); - currentThreadObject = nullptr; - - if (shouldDelete) { - assert(detached && "ThreadObject must be detached when refCount reaches zero before completion"); - destroyThreadObject(obj); - } - - if (tibInstalled) { - asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory"); - } - return nullptr; - } - - static int doCompareString(const std::string &a, const std::string &b, unsigned int dwCmpFlags) { - for (size_t i = 0; ; i++) { - if (i == a.size()) { - if (i == b.size()) { - return 2; // CSTR_EQUAL - } - return 1; // CSTR_LESS_THAN - } - if (i == b.size()) { - return 3; // CSTR_GREATER_THAN - } - unsigned char c = a[i], d = b[i]; - if (dwCmpFlags & 1) { // NORM_IGNORECASE - if ('a' <= c && c <= 'z') c -= 'a' - 'A'; - if ('a' <= d && d <= 'z') d -= 'a' - 'A'; - } - if (c != d) { - return c < d ? 1 : 3; - } - } - } - - void setLastErrorFromErrno() { - wibo::lastError = wibo::winErrorFromErrno(errno); - } - - int64_t getFileSize(void* hFile) { - FILE *fp = files::fpFromHandle(hFile); - if (!fp) { - wibo::lastError = ERROR_INVALID_HANDLE; - return -1; // INVALID_FILE_SIZE - } - struct stat64 st; - fflush(fp); - if (fstat64(fileno(fp), &st) == -1 || !S_ISREG(st.st_mode)) { - setLastErrorFromErrno(); - return -1; // INVALID_FILE_SIZE - } - return st.st_size; - } - - BOOL WIN_FUNC IsBadReadPtr(const void *lp, uintptr_t ucb) { - DEBUG_LOG("STUB: IsBadReadPtr(ptr=%p, size=%zu)\n", lp, static_cast(ucb)); - if (!lp) { - return TRUE; + if (usingOverlapped) { + overlapped->Internal = completionStatus; + overlapped->InternalHigh = io.bytesTransferred; + signalOverlappedEvent(overlapped); } return FALSE; } - BOOL WIN_FUNC IsBadWritePtr(void *lp, uintptr_t ucb) { - DEBUG_LOG("STUB: IsBadWritePtr(ptr=%p, size=%zu)\n", lp, static_cast(ucb)); - if (!lp && ucb != 0) { - return TRUE; + if (lpNumberOfBytesWritten && (!handleOverlapped || !usingOverlapped)) { + *lpNumberOfBytesWritten = static_cast(io.bytesTransferred); + } + + if (usingOverlapped) { + overlapped->Internal = completionStatus; + overlapped->InternalHigh = io.bytesTransferred; + if (!handleOverlapped) { + uint64_t baseOffset = offset.value_or(0); + uint64_t newOffset = baseOffset + io.bytesTransferred; + overlapped->Offset = static_cast(newOffset & 0xFFFFFFFFu); + overlapped->OffsetHigh = static_cast(newOffset >> 32); + } + signalOverlappedEvent(overlapped); + } + + return (io.bytesTransferred == nNumberOfBytesToWrite); +} + +BOOL WIN_FUNC FlushFileBuffers(HANDLE hFile) { + DEBUG_LOG("FlushFileBuffers(%p)\n", hFile); + auto data = handles::dataFromHandle(hFile, false); + if (data.type != handles::TYPE_FILE || data.ptr == nullptr) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + auto file = reinterpret_cast(data.ptr); + if (!file || !file->fp) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + FILE *fp = file->fp; + if (fflush(fp) != 0) { + wibo::lastError = ERROR_ACCESS_DENIED; + return FALSE; + } + int fd = file->fd; + if (fd >= 0 && fsync(fd) != 0) { + wibo::lastError = ERROR_ACCESS_DENIED; + return FALSE; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +unsigned int WIN_FUNC ReadFile(void *hFile, void *lpBuffer, unsigned int nNumberOfBytesToRead, + unsigned int *lpNumberOfBytesRead, void *lpOverlapped) { + DEBUG_LOG("ReadFile(%p, %u)\n", hFile, nNumberOfBytesToRead); + wibo::lastError = ERROR_SUCCESS; + + auto file = files::fileHandleFromHandle(hFile); + if (!file || !file->fp) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + + bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0; + auto *overlapped = reinterpret_cast(lpOverlapped); + bool usingOverlapped = overlapped != nullptr; + if (!usingOverlapped && lpNumberOfBytesRead == nullptr) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + std::optional offset; + bool updateFilePointer = true; + if (usingOverlapped) { + offset = (static_cast(overlapped->Offset) | (static_cast(overlapped->OffsetHigh) << 32)); + overlapped->Internal = STATUS_PENDING; + overlapped->InternalHigh = 0; + updateFilePointer = !handleOverlapped; + resetOverlappedEvent(overlapped); + } + + auto io = files::read(file, lpBuffer, nNumberOfBytesToRead, offset, updateFilePointer); + DWORD completionStatus = STATUS_SUCCESS; + if (io.unixError != 0) { + completionStatus = wibo::winErrorFromErrno(io.unixError); + wibo::lastError = completionStatus; + if (lpNumberOfBytesRead) { + *lpNumberOfBytesRead = static_cast(io.bytesTransferred); + } + if (usingOverlapped) { + overlapped->Internal = completionStatus; + overlapped->InternalHigh = io.bytesTransferred; + signalOverlappedEvent(overlapped); } return FALSE; } - BOOL WIN_FUNC DisableThreadLibraryCalls(HMODULE hLibModule) { - DEBUG_LOG("DisableThreadLibraryCalls(%p)\n", hLibModule); - (void)hLibModule; + if (io.reachedEnd && io.bytesTransferred == 0 && handleOverlapped) { + completionStatus = ERROR_HANDLE_EOF; + } + + if (lpNumberOfBytesRead && (!handleOverlapped || !usingOverlapped)) { + *lpNumberOfBytesRead = static_cast(io.bytesTransferred); + } + + if (usingOverlapped) { + overlapped->Internal = completionStatus; + overlapped->InternalHigh = io.bytesTransferred; + if (!handleOverlapped) { + uint64_t baseOffset = offset.value_or(0); + uint64_t newOffset = baseOffset + io.bytesTransferred; + overlapped->Offset = static_cast(newOffset & 0xFFFFFFFFu); + overlapped->OffsetHigh = static_cast(newOffset >> 32); + } + signalOverlappedEvent(overlapped); + } + + return TRUE; +} + +enum { + CREATE_NEW = 1, + CREATE_ALWAYS = 2, + OPEN_EXISTING = 3, + OPEN_ALWAYS = 4, + TRUNCATE_EXISTING = 5, +}; +void *WIN_FUNC CreateFileA(const char *lpFileName, unsigned int dwDesiredAccess, unsigned int dwShareMode, + void *lpSecurityAttributes, unsigned int dwCreationDisposition, + unsigned int dwFlagsAndAttributes, void *hTemplateFile) { + std::string path = files::pathFromWindows(lpFileName); + DEBUG_LOG("CreateFileA(filename=%s (%s), desiredAccess=0x%x, shareMode=%u, securityAttributes=%p, " + "creationDisposition=%u, flagsAndAttributes=%u)\n", + lpFileName, path.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, + dwFlagsAndAttributes); + + wibo::lastError = 0; // possibly overwritten later in this function + + // Based on https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#parameters + // and this table: https://stackoverflow.com/a/14469641 + bool fileExists = (access(path.c_str(), F_OK) == 0); + bool shouldTruncate = false; + switch (dwCreationDisposition) { + case CREATE_ALWAYS: + if (fileExists) { + wibo::lastError = 183; // ERROR_ALREADY_EXISTS + shouldTruncate = true; // "The function overwrites the file" + // Function succeeds + } + break; + case CREATE_NEW: + if (fileExists) { + wibo::lastError = 80; // ERROR_FILE_EXISTS + return INVALID_HANDLE_VALUE; + } + break; + case OPEN_ALWAYS: + if (fileExists) { + wibo::lastError = 183; // ERROR_ALREADY_EXISTS + // Function succeeds + } + break; + case OPEN_EXISTING: + if (!fileExists) { + wibo::lastError = 2; // ERROR_FILE_NOT_FOUND + return INVALID_HANDLE_VALUE; + } + break; + case TRUNCATE_EXISTING: + shouldTruncate = true; + if (!fileExists) { + wibo::lastError = 2; // ERROR_FILE_NOT_FOUND + return INVALID_HANDLE_VALUE; + } + break; + default: + assert(0); + } + + FILE *fp; + if (dwDesiredAccess == 0x80000000) { // read + fp = fopen(path.c_str(), "rb"); + } else if (dwDesiredAccess == 0x40000000) { // write + if (shouldTruncate || !fileExists) { + fp = fopen(path.c_str(), "wb"); + } else { + // There is no way to fopen with only write permissions + // and without truncating the file... + fp = fopen(path.c_str(), "rb+"); + } + } else if (dwDesiredAccess == 0xc0000000) { // read/write + if (shouldTruncate || !fileExists) { + fp = fopen(path.c_str(), "wb+"); + } else { + fp = fopen(path.c_str(), "rb+"); + } + } else { + assert(0); + } + + if (fp) { + void *handle = files::allocFpHandle(fp, dwDesiredAccess, dwShareMode, dwFlagsAndAttributes, true); + DEBUG_LOG("-> %p\n", handle); + return handle; + } else { + setLastErrorFromErrno(); + return INVALID_HANDLE_VALUE; + } +} + +void *WIN_FUNC CreateFileW(const uint16_t *lpFileName, unsigned int dwDesiredAccess, unsigned int dwShareMode, + void *lpSecurityAttributes, unsigned int dwCreationDisposition, + unsigned int dwFlagsAndAttributes, void *hTemplateFile) { + DEBUG_LOG("CreateFileW -> "); + const auto lpFileNameA = wideStringToString(lpFileName); + return CreateFileA(lpFileNameA.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, + dwFlagsAndAttributes, hTemplateFile); +} + +BOOL WIN_FUNC DeleteFileA(const char *lpFileName) { + if (!lpFileName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + DEBUG_LOG("DeleteFileA(NULL) -> ERROR_INVALID_PARAMETER\n"); + return FALSE; + } + std::string path = files::pathFromWindows(lpFileName); + DEBUG_LOG("DeleteFileA(%s) -> %s\n", lpFileName, path.c_str()); + if (unlink(path.c_str()) == 0) { + wibo::lastError = ERROR_SUCCESS; return TRUE; } + setLastErrorFromErrno(); + return FALSE; +} - - int WIN_FUNC GetSystemDefaultLangID() { - DEBUG_LOG("STUB: GetSystemDefaultLangID()\n"); - return 0; +BOOL WIN_FUNC DeleteFileW(const uint16_t *lpFileName) { + DEBUG_LOG("DeleteFileW -> "); + if (!lpFileName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + DEBUG_LOG("ERROR_INVALID_PARAMETER\n"); + return FALSE; } + std::string name = wideStringToString(lpFileName); + return DeleteFileA(name.c_str()); +} - struct LIST_ENTRY; - struct LIST_ENTRY { - LIST_ENTRY *Flink; - LIST_ENTRY *Blink; - }; - - struct CRITICAL_SECTION_DEBUG; - struct CRITICAL_SECTION { - CRITICAL_SECTION_DEBUG *DebugInfo; - unsigned int LockCount; - unsigned int RecursionCount; - void *OwningThread; - void *LockSemaphore; - unsigned int SpinCount; - }; - - struct CRITICAL_SECTION_DEBUG { - int Type; - int CreatorBackTraceIndex; - CRITICAL_SECTION *CriticalSection; - LIST_ENTRY ProcessLocksList; - unsigned int EntryCount; - unsigned int ContentionCount; - unsigned int Flags; - int CreatorBackTraceIndexHigh; - int SpareUSHORT; - }; - - void WIN_FUNC InitializeCriticalSection(CRITICAL_SECTION *param) { - VERBOSE_LOG("STUB: InitializeCriticalSection(%p)\n", param); +BOOL WIN_FUNC MoveFileA(const char *lpExistingFileName, const char *lpNewFileName) { + DEBUG_LOG("MoveFileA(%s, %s)\n", lpExistingFileName ? lpExistingFileName : "(null)", + lpNewFileName ? lpNewFileName : "(null)"); + if (!lpExistingFileName || !lpNewFileName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; } - - void WIN_FUNC InitializeCriticalSectionEx(CRITICAL_SECTION *param) { - VERBOSE_LOG("STUB: InitializeCriticalSectionEx(%p)\n", param); + auto fromPath = files::pathFromWindows(lpExistingFileName); + auto toPath = files::pathFromWindows(lpNewFileName); + std::error_code ec; + if (std::filesystem::exists(toPath, ec)) { + wibo::lastError = ERROR_ALREADY_EXISTS; + return FALSE; } - - void WIN_FUNC DeleteCriticalSection(CRITICAL_SECTION *param) { - VERBOSE_LOG("STUB: DeleteCriticalSection(%p)\n", param); + if (ec) { + errno = ec.value(); + setLastErrorFromErrno(); + return FALSE; } - - void WIN_FUNC EnterCriticalSection(CRITICAL_SECTION *param) { - VERBOSE_LOG("STUB: EnterCriticalSection(%p)\n", param); + std::filesystem::rename(fromPath, toPath, ec); + if (ec) { + errno = ec.value(); + setLastErrorFromErrno(); + return FALSE; } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} - void WIN_FUNC LeaveCriticalSection(CRITICAL_SECTION *param) { - VERBOSE_LOG("STUB: LeaveCriticalSection(%p)\n", param); +BOOL WIN_FUNC MoveFileW(const uint16_t *lpExistingFileName, const uint16_t *lpNewFileName) { + DEBUG_LOG("MoveFileW -> "); + if (!lpExistingFileName || !lpNewFileName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + DEBUG_LOG("ERROR_INVALID_PARAMETER\n"); + return FALSE; } + std::string from = wideStringToString(lpExistingFileName); + std::string to = wideStringToString(lpNewFileName); + return MoveFileA(from.c_str(), to.c_str()); +} - unsigned int WIN_FUNC InitializeCriticalSectionAndSpinCount(CRITICAL_SECTION *lpCriticalSection, - unsigned int dwSpinCount) { - DEBUG_LOG("STUB: InitializeCriticalSectionAndSpinCount(%p, %i)\n", lpCriticalSection, dwSpinCount); - memset(lpCriticalSection, 0, sizeof(CRITICAL_SECTION)); - lpCriticalSection->SpinCount = dwSpinCount; - return 1; +DWORD WIN_FUNC SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) { + DEBUG_LOG("SetFilePointer(%p, %d, %d)\n", hFile, lDistanceToMove, dwMoveMethod); + if (hFile == nullptr) { + wibo::lastError = ERROR_INVALID_HANDLE; + return INVALID_SET_FILE_POINTER; } - - int WIN_FUNC InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, LPVOID *lpContext) { - DEBUG_LOG("STUB: InitOnceBeginInitialize(%p, %u, %p, %p)\n", lpInitOnce, dwFlags, fPending, lpContext); - if (fPending != nullptr) { - *fPending = TRUE; - } - return 1; + assert(!lpDistanceToMoveHigh || *lpDistanceToMoveHigh == 0); + FILE *fp = files::fpFromHandle(hFile); + if (!fp) { + wibo::lastError = ERROR_INVALID_HANDLE; + return INVALID_SET_FILE_POINTER; } + wibo::lastError = ERROR_SUCCESS; + int r = fseek(fp, lDistanceToMove, dwMoveMethod == 0 ? SEEK_SET : dwMoveMethod == 1 ? SEEK_CUR : SEEK_END); - BOOL WIN_FUNC InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext) { - DEBUG_LOG("STUB: InitOnceComplete(%p, %u, %p)\n", lpInitOnce, dwFlags, lpContext); - return TRUE; - } - - void WIN_FUNC AcquireSRWLockShared(void *SRWLock) { VERBOSE_LOG("STUB: AcquireSRWLockShared(%p)\n", SRWLock); } - - void WIN_FUNC ReleaseSRWLockShared(void *SRWLock) { VERBOSE_LOG("STUB: ReleaseSRWLockShared(%p)\n", SRWLock); } - - void WIN_FUNC AcquireSRWLockExclusive(void *SRWLock) { - VERBOSE_LOG("STUB: AcquireSRWLockExclusive(%p)\n", SRWLock); - } - - void WIN_FUNC ReleaseSRWLockExclusive(void *SRWLock) { - VERBOSE_LOG("STUB: ReleaseSRWLockExclusive(%p)\n", SRWLock); - } - - int WIN_FUNC TryAcquireSRWLockExclusive(void *SRWLock) { - VERBOSE_LOG("STUB: TryAcquireSRWLockExclusive(%p)\n", SRWLock); - return 1; - } - - /* - * TLS (Thread-Local Storage) - */ - enum { MAX_TLS_VALUES = 100 }; - static bool tlsValuesUsed[MAX_TLS_VALUES] = { false }; - static void *tlsValues[MAX_TLS_VALUES]; - unsigned int WIN_FUNC TlsAlloc() { - VERBOSE_LOG("TlsAlloc()"); - for (size_t i = 0; i < MAX_TLS_VALUES; i++) { - if (tlsValuesUsed[i] == false) { - tlsValuesUsed[i] = true; - tlsValues[i] = 0; - VERBOSE_LOG(" -> %d\n", i); - return i; - } - } - VERBOSE_LOG(" -> -1\n"); - wibo::lastError = 1; - return 0xFFFFFFFF; // TLS_OUT_OF_INDEXES - } - - unsigned int WIN_FUNC TlsFree(unsigned int dwTlsIndex) { - VERBOSE_LOG("TlsFree(%u)\n", dwTlsIndex); - if (dwTlsIndex >= 0 && dwTlsIndex < MAX_TLS_VALUES && tlsValuesUsed[dwTlsIndex]) { - tlsValuesUsed[dwTlsIndex] = false; - return 1; - } else { - wibo::lastError = 1; - return 0; - } - } - - void *WIN_FUNC TlsGetValue(unsigned int dwTlsIndex) { - VERBOSE_LOG("TlsGetValue(%u)\n", dwTlsIndex); - void *result = nullptr; - if (dwTlsIndex >= 0 && dwTlsIndex < MAX_TLS_VALUES && tlsValuesUsed[dwTlsIndex]) { - result = tlsValues[dwTlsIndex]; - // See https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-TlsGetValue#return-value - wibo::lastError = ERROR_SUCCESS; - } else { - wibo::lastError = 1; - } - // DEBUG_LOG(" -> %p\n", result); - return result; - } - - unsigned int WIN_FUNC TlsSetValue(unsigned int dwTlsIndex, void *lpTlsValue) { - VERBOSE_LOG("TlsSetValue(%u, %p)\n", dwTlsIndex, lpTlsValue); - if (dwTlsIndex >= 0 && dwTlsIndex < MAX_TLS_VALUES && tlsValuesUsed[dwTlsIndex]) { - tlsValues[dwTlsIndex] = lpTlsValue; - return 1; - } else { - wibo::lastError = 1; - return 0; - } - } - - /* - * Memory - */ - void *WIN_FUNC GlobalAlloc(uint32_t uFlags, size_t dwBytes) { - VERBOSE_LOG("GlobalAlloc(%x, %zu)\n", uFlags, dwBytes); - if (uFlags & 2) { - // GMEM_MOVEABLE - not implemented rn - assert(0); - return 0; - } else { - // GMEM_FIXED - this is simpler - bool zero = uFlags & 0x40; // GMEM_ZEROINT - return doAlloc(dwBytes, zero); - } - } - void *WIN_FUNC GlobalFree(void *hMem) { - VERBOSE_LOG("GlobalFree(%p)\n", hMem); - free(hMem); - return 0; - } - - void *WIN_FUNC GlobalReAlloc(void *hMem, size_t dwBytes, uint32_t uFlags) { - VERBOSE_LOG("GlobalReAlloc(%p, %zu, %x)\n", hMem, dwBytes, uFlags); - if (uFlags & 0x80) { // GMEM_MODIFY - assert(0); - } else { - bool zero = uFlags & 0x40; // GMEM_ZEROINT - return doRealloc(hMem, dwBytes, zero); - } - } - - unsigned int WIN_FUNC GlobalFlags(void *hMem) { - VERBOSE_LOG("GlobalFlags(%p)\n", hMem); - return 0; - } - - constexpr uint32_t LMEM_MOVEABLE = 0x0002; - constexpr uint32_t LMEM_ZEROINIT = 0x0040; - - void *WIN_FUNC LocalAlloc(uint32_t uFlags, size_t uBytes) { - VERBOSE_LOG("LocalAlloc(%x, %zu)\n", uFlags, uBytes); - bool zero = (uFlags & LMEM_ZEROINIT) != 0; - if ((uFlags & LMEM_MOVEABLE) != 0) { - DEBUG_LOG(" ignoring LMEM_MOVEABLE\n"); - } - void *result = doAlloc(uBytes, zero); - if (!result) { - wibo::lastError = ERROR_NOT_SUPPORTED; - return nullptr; - } - DEBUG_LOG(" -> %p\n", result); - maybeMarkExecutable(result); - wibo::lastError = ERROR_SUCCESS; - return result; - } - - void *WIN_FUNC LocalFree(void *hMem) { - VERBOSE_LOG("LocalFree(%p)\n", hMem); - // Windows returns NULL on success. - free(hMem); - wibo::lastError = ERROR_SUCCESS; - return nullptr; - } - - void *WIN_FUNC LocalReAlloc(void *hMem, size_t uBytes, uint32_t uFlags) { - VERBOSE_LOG("LocalReAlloc(%p, %zu, %x)\n", hMem, uBytes, uFlags); - bool zero = (uFlags & LMEM_ZEROINIT) != 0; - if ((uFlags & LMEM_MOVEABLE) != 0) { - DEBUG_LOG(" ignoring LMEM_MOVEABLE\n"); - } - void *result = doRealloc(hMem, uBytes, zero); - if (!result && uBytes != 0) { - wibo::lastError = ERROR_NOT_SUPPORTED; - return nullptr; - } - DEBUG_LOG(" -> %p\n", result); - maybeMarkExecutable(result); - wibo::lastError = ERROR_SUCCESS; - return result; - } - - void *WIN_FUNC LocalHandle(void *hMem) { - VERBOSE_LOG("LocalHandle(%p)\n", hMem); - return hMem; - } - - void *WIN_FUNC LocalLock(void *hMem) { - VERBOSE_LOG("LocalLock(%p)\n", hMem); - wibo::lastError = ERROR_SUCCESS; - return hMem; - } - - unsigned int WIN_FUNC LocalUnlock(void *hMem) { - VERBOSE_LOG("LocalUnlock(%p)\n", hMem); - (void)hMem; - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - size_t WIN_FUNC LocalSize(void *hMem) { - VERBOSE_LOG("LocalSize(%p)\n", hMem); - return hMem ? mi_usable_size(hMem) : 0; - } - - unsigned int WIN_FUNC LocalFlags(void *hMem) { - VERBOSE_LOG("LocalFlags(%p)\n", hMem); - (void)hMem; - return 0; - } - - /* - * Environment - */ - LPSTR WIN_FUNC GetCommandLineA() { - DEBUG_LOG("GetCommandLineA() -> %s\n", wibo::commandLine.c_str()); - return const_cast(wibo::commandLine.c_str()); - } - - LPWSTR WIN_FUNC GetCommandLineW() { - DEBUG_LOG("GetCommandLineW() -> %s\n", wideStringToString(wibo::commandLineW.data()).c_str()); - return wibo::commandLineW.data(); - } - - char *WIN_FUNC GetEnvironmentStrings() { - DEBUG_LOG("GetEnvironmentStrings()\n"); - // Step 1, figure out the size of the buffer we need. - size_t bufSize = 0; - char **work = environ; - - while (*work) { - bufSize += strlen(*work) + 1; - work++; - } - bufSize++; - - // Step 2, actually build that buffer - char *buffer = (char *) mi_malloc(bufSize); - char *ptr = buffer; - work = environ; - - while (*work) { - size_t strSize = strlen(*work); - memcpy(ptr, *work, strSize); - ptr[strSize] = 0; - ptr += strSize + 1; - work++; - } - *ptr = 0; // an extra null at the end - - return buffer; - } - - uint16_t* WIN_FUNC GetEnvironmentStringsW() { - DEBUG_LOG("GetEnvironmentStringsW()\n"); - // Step 1, figure out the size of the buffer we need. - size_t bufSizeW = 0; - char **work = environ; - - while (*work) { - // "hello|" -> " h e l l o|" - bufSizeW += strlen(*work) + 1; - work++; - } - bufSizeW++; - - // Step 2, actually build that buffer - uint16_t *buffer = (uint16_t *) mi_malloc(bufSizeW * 2); - uint16_t *ptr = buffer; - work = environ; - - while (*work) { - VERBOSE_LOG("-> %s\n", *work); - size_t strSize = strlen(*work); - for (size_t i = 0; i < strSize; i++) { - *ptr++ = (*work)[i] & 0xFF; - } - *ptr++ = 0; // NUL terminate - work++; - } - *ptr = 0; // an extra null at the end - - return buffer; - } - - void WIN_FUNC FreeEnvironmentStringsA(char *buffer) { - DEBUG_LOG("FreeEnvironmentStringsA(%p)\n", buffer); - free(buffer); - } - - /* - * I/O - */ - void *WIN_FUNC GetStdHandle(uint32_t nStdHandle) { - DEBUG_LOG("GetStdHandle(%d)\n", nStdHandle); - return files::getStdHandle(nStdHandle); - } - - unsigned int WIN_FUNC SetStdHandle(uint32_t nStdHandle, void *hHandle) { - DEBUG_LOG("SetStdHandle(%d, %p)\n", nStdHandle, hHandle); - return files::setStdHandle(nStdHandle, hHandle); - } - - unsigned int WIN_FUNC DuplicateHandle(void *hSourceProcessHandle, void *hSourceHandle, void *hTargetProcessHandle, - void **lpTargetHandle, unsigned int dwDesiredAccess, - unsigned int bInheritHandle, unsigned int dwOptions) { - DEBUG_LOG("DuplicateHandle(%p, %p, %p, %p, %x, %d, %x)\n", hSourceProcessHandle, hSourceHandle, - hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions); - (void)dwDesiredAccess; - (void)bInheritHandle; - (void)dwOptions; - if (!lpTargetHandle) { + if (r < 0) { + if (errno == EINVAL) + wibo::lastError = ERROR_NEGATIVE_SEEK; + else wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } + return INVALID_SET_FILE_POINTER; + } - auto validateProcessHandle = [&](void *handle) -> bool { - uintptr_t raw = reinterpret_cast(handle); - if (raw == static_cast(-1)) { - return true; - } - auto data = handles::dataFromHandle(handle, false); - if (data.type != handles::TYPE_PROCESS || data.ptr == nullptr) { - return false; - } - auto *proc = reinterpret_cast(data.ptr); - return proc && proc->pid == getpid(); - }; + r = ftell(fp); + assert(r >= 0); + return r; +} - if (!validateProcessHandle(hSourceProcessHandle) || !validateProcessHandle(hTargetProcessHandle)) { - DEBUG_LOG("DuplicateHandle: unsupported process handle combination (source=%p target=%p)\n", - hSourceProcessHandle, hTargetProcessHandle); - wibo::lastError = ERROR_INVALID_HANDLE; - return 0; - } - - auto file = files::fileHandleFromHandle(hSourceHandle); - if (file && (file->fp == stdin || file->fp == stdout || file->fp == stderr)) { - void *handle = files::duplicateFileHandle(file, false); - DEBUG_LOG("DuplicateHandle: duplicated std handle -> %p\n", handle); - *lpTargetHandle = handle; - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - uintptr_t sourceHandleRaw = reinterpret_cast(hSourceHandle); - if (sourceHandleRaw == static_cast(-1)) { - void *handle = processes::allocProcessHandle(getpid()); - processes::Process *proc = processes::processFromHandle(handle, false); - if (proc) { - proc->exitCode = STILL_ACTIVE; - proc->forcedExitCode = STILL_ACTIVE; - proc->terminationRequested = false; - } - DEBUG_LOG("DuplicateHandle: created process handle for current process -> %p\n", handle); - *lpTargetHandle = handle; - wibo::lastError = ERROR_SUCCESS; - return 1; - } - if (sourceHandleRaw == PSEUDO_CURRENT_THREAD_HANDLE_VALUE) { - ThreadObject *obj = ensureCurrentThreadObject(); - if (obj) { - retainThreadObject(obj); - void *handle = handles::allocDataHandle({handles::TYPE_THREAD, obj, 0}); - DEBUG_LOG("DuplicateHandle: duplicated pseudo current thread -> %p\n", handle); - *lpTargetHandle = handle; - wibo::lastError = ERROR_SUCCESS; - return 1; - } - ThreadObject *syntheticObj = new ThreadObject(); - syntheticObj->thread = pthread_self(); - syntheticObj->finished = false; - syntheticObj->joined = false; - syntheticObj->detached = true; - syntheticObj->synthetic = true; - syntheticObj->exitCode = 0; - syntheticObj->refCount = 1; - syntheticObj->suspendCount = 0; - pthread_mutex_init(&syntheticObj->mutex, nullptr); - pthread_cond_init(&syntheticObj->cond, nullptr); - void *handle = handles::allocDataHandle({handles::TYPE_THREAD, syntheticObj, 0}); - DEBUG_LOG("DuplicateHandle: created synthetic thread handle -> %p\n", handle); - *lpTargetHandle = handle; - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - handles::Data data = handles::dataFromHandle(hSourceHandle, false); - if (data.type == handles::TYPE_PROCESS && data.ptr) { - auto *original = reinterpret_cast(data.ptr); - void *handle = processes::allocProcessHandle(original->pid); - auto *copy = processes::processFromHandle(handle, false); - if (copy) { - *copy = *original; - } - DEBUG_LOG("DuplicateHandle: duplicated process handle -> %p\n", handle); - *lpTargetHandle = handle; - wibo::lastError = ERROR_SUCCESS; - return 1; - } - if (data.type == handles::TYPE_THREAD && data.ptr) { - auto *threadObj = reinterpret_cast(data.ptr); - if (!retainThreadObject(threadObj)) { - wibo::lastError = ERROR_INVALID_HANDLE; - return 0; - } - void *handle = handles::allocDataHandle({handles::TYPE_THREAD, threadObj, 0}); - DEBUG_LOG("DuplicateHandle: duplicated thread handle -> %p\n", handle); - *lpTargetHandle = handle; - wibo::lastError = ERROR_SUCCESS; - return 1; - } - DEBUG_LOG("DuplicateHandle: unsupported handle type for %p\n", hSourceHandle); +BOOL WIN_FUNC SetFilePointerEx(HANDLE hFile, LARGE_INTEGER lDistanceToMove, PLARGE_INTEGER lpDistanceToMoveHigh, + DWORD dwMoveMethod) { + if (hFile == nullptr) { wibo::lastError = ERROR_INVALID_HANDLE; return 0; } - - BOOL WIN_FUNC CloseHandle(HANDLE hObject) { - DEBUG_LOG("CloseHandle(%p)\n", hObject); - auto data = handles::dataFromHandle(hObject, true); - if (data.type == handles::TYPE_UNUSED || data.ptr == nullptr) { - wibo::lastError = ERROR_INVALID_HANDLE; - return FALSE; - } - bool success = true; - if (data.type == handles::TYPE_FILE) { - auto file = reinterpret_cast(data.ptr); - if (file) { - if (file->closeOnDestroy && file->fp && - !(file->fp == stdin || file->fp == stdout || file->fp == stderr)) { - fclose(file->fp); - } - delete file; - } else { - success = false; - } - } else if (data.type == handles::TYPE_MAPPED) { - auto *mapping = reinterpret_cast(data.ptr); - if (mapping) { - mapping->closed = true; - tryReleaseMapping(mapping); - } else { - success = false; - } - } else if (data.type == handles::TYPE_PROCESS) { - auto *proc = reinterpret_cast(data.ptr); - if (proc) { - delete proc; - } else { - success = false; - } - } else if (data.type == handles::TYPE_TOKEN) { - advapi32::releaseToken(data.ptr); - } else if (data.type == handles::TYPE_MUTEX) { - releaseMutexObject(reinterpret_cast(data.ptr)); - } else if (data.type == handles::TYPE_EVENT) { - releaseEventObject(reinterpret_cast(data.ptr)); - } else if (data.type == handles::TYPE_THREAD) { - releaseThreadObject(reinterpret_cast(data.ptr)); - } else if (data.type == handles::TYPE_SEMAPHORE) { - releaseSemaphoreObject(reinterpret_cast(data.ptr)); - } else { - success = false; - } - if (!success) { - wibo::lastError = ERROR_INVALID_HANDLE; - return FALSE; - } - wibo::lastError = ERROR_SUCCESS; - return TRUE; + assert(!lpDistanceToMoveHigh || *lpDistanceToMoveHigh == 0); + DEBUG_LOG("SetFilePointerEx(%p, %ld, %d)\n", hFile, lDistanceToMove, dwMoveMethod); + FILE *fp = files::fpFromHandle(hFile); + if (!fp) { + wibo::lastError = ERROR_INVALID_HANDLE; + return 0; } + wibo::lastError = ERROR_SUCCESS; + int r = fseeko64(fp, lDistanceToMove, dwMoveMethod == 0 ? SEEK_SET : dwMoveMethod == 1 ? SEEK_CUR : SEEK_END); - struct FullPathInfo { - std::string path; - size_t filePartOffset = std::string::npos; - }; - - static bool computeFullPath(const std::string &input, FullPathInfo &outInfo) { - bool endsWithSeparator = false; - if (!input.empty()) { - char last = input.back(); - endsWithSeparator = (last == '\\' || last == '/'); - } - - std::filesystem::path hostPath = files::pathFromWindows(input.c_str()); - std::error_code ec; - std::filesystem::path absPath = std::filesystem::absolute(hostPath, ec); - if (ec) { - errno = ec.value(); - setLastErrorFromErrno(); - return false; - } - - std::string windowsPath = files::pathToWindows(absPath); - if (endsWithSeparator && !windowsPath.empty() && windowsPath.back() != '\\') { - windowsPath.push_back('\\'); - } - - if (!windowsPath.empty() && windowsPath.back() != '\\') { - size_t lastSlash = windowsPath.find_last_of('\\'); - if (lastSlash == std::string::npos) { - outInfo.filePartOffset = 0; - } else if (lastSlash + 1 < windowsPath.size()) { - outInfo.filePartOffset = lastSlash + 1; - } - } else { - outInfo.filePartOffset = std::string::npos; - } - - outInfo.path = std::move(windowsPath); - return true; - } - - DWORD WIN_FUNC GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR *lpFilePart) { - DEBUG_LOG("GetFullPathNameA(%s, %u)\n", lpFileName ? lpFileName : "(null)", nBufferLength); - - if (lpFilePart) { - *lpFilePart = nullptr; - } - - if (!lpFileName) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - FullPathInfo info; - if (!computeFullPath(lpFileName, info)) { - return 0; - } - - DEBUG_LOG(" -> %s\n", info.path.c_str()); - - const size_t pathLen = info.path.size(); - const auto required = static_cast(pathLen + 1); - - if (nBufferLength == 0) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return required; - } - - if (!lpBuffer) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - if (nBufferLength < required) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return required; - } - - memcpy(lpBuffer, info.path.c_str(), pathLen); - lpBuffer[pathLen] = '\0'; - - if (lpFilePart) { - if (info.filePartOffset != std::string::npos && info.filePartOffset < pathLen) { - *lpFilePart = lpBuffer + info.filePartOffset; - } else { - *lpFilePart = nullptr; - } - } - - wibo::lastError = ERROR_SUCCESS; - return static_cast(pathLen); - } - - DWORD WIN_FUNC GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart) { - DEBUG_LOG("GetFullPathNameW(%p, %u)\n", lpFileName, nBufferLength); - - if (lpFilePart) { - *lpFilePart = nullptr; - } - - if (!lpFileName) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - std::string narrow = wideStringToString(lpFileName); - FullPathInfo info; - if (!computeFullPath(narrow, info)) { - return 0; - } - - DEBUG_LOG(" -> %s\n", info.path.c_str()); - - auto widePath = stringToWideString(info.path.c_str()); - const size_t wideLen = widePath.size(); - const auto required = static_cast(wideLen); - - if (nBufferLength == 0) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return required; - } - - if (!lpBuffer) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - if (nBufferLength < required) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return required; - } - - std::copy(widePath.begin(), widePath.end(), lpBuffer); - - if (lpFilePart) { - if (info.filePartOffset != std::string::npos && info.filePartOffset < info.path.size()) { - *lpFilePart = lpBuffer + info.filePartOffset; - } else { - *lpFilePart = nullptr; - } - } - - wibo::lastError = ERROR_SUCCESS; - return static_cast(wideLen - 1); - } - - /** - * @brief GetShortPathNameA: Retrieves the short path form of the specified path - * - * @param[in] lpszLongPath The path string - * @param[out] lpszShortPath A pointer to a buffer to receive - * @param[in] cchBuffer The size of the buffer that lpszShortPath points to - * @return unsigned int - */ - unsigned int WIN_FUNC GetShortPathNameA(const char* lpszLongPath, char* lpszShortPath, unsigned int cchBuffer) { - DEBUG_LOG("GetShortPathNameA(%s)...\n",lpszShortPath); - std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(lpszLongPath)); - std::string absStr = files::pathToWindows(absPath); - - if (absStr.length() + 1 > cchBuffer) - { - return absStr.length()+1; - } + if (r < 0) { + if (errno == EINVAL) + wibo::lastError = ERROR_NEGATIVE_SEEK; else - { - strcpy(lpszShortPath, absStr.c_str()); - return absStr.length(); - } - } - - DWORD WIN_FUNC GetShortPathNameW(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD cchBuffer) { - std::string longPath = wideStringToString(lpszLongPath); - DEBUG_LOG("GetShortPathNameW(%s)\n", longPath.c_str()); - std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(longPath.c_str())); - std::string absStr = files::pathToWindows(absPath); - auto absStrW = stringToWideString(absStr.c_str()); - size_t len = wstrlen(absStrW.data()); - if (cchBuffer == 0 || cchBuffer <= len) { - return len + 1; - } - wstrncpy(lpszShortPath, absStrW.data(), len + 1); - wibo::lastError = ERROR_SUCCESS; - return len; - } - - using random_shorts_engine = std::independent_bits_engine; - - unsigned int WIN_FUNC GetTempFileNameA(LPSTR lpPathName, LPSTR lpPrefixString, unsigned int uUnique, LPSTR lpTempFileName) { - DEBUG_LOG("GetTempFileNameA(%s, %s, %u)\n", lpPathName, lpPrefixString, uUnique); - if (lpPathName == 0) { - return 0; - } - if (strlen(lpPathName) > MAX_PATH - 14) { - wibo::lastError = ERROR_BUFFER_OVERFLOW; - return 0; - } - char uniqueStr[20]; - std::filesystem::path path; - - if (uUnique == 0) { - std::random_device rd; - random_shorts_engine rse(rd()); - while(true) { - uUnique = rse(); - if (uUnique == 0) { - continue; - } - snprintf(uniqueStr, sizeof(uniqueStr), "%.3s%X.TMP", lpPrefixString, uUnique); - path = files::pathFromWindows(lpPathName) / uniqueStr; - // Atomically create it if it doesn't exist - int fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0644); - if (fd >= 0) { - close(fd); - break; - } - } - } - else { - snprintf(uniqueStr, sizeof(uniqueStr), "%.3s%X.TMP", lpPrefixString, uUnique & 0xFFFF); - path = files::pathFromWindows(lpPathName) / uniqueStr; - } - std::string str = files::pathToWindows(path); - DEBUG_LOG(" -> %s\n", str.c_str()); - strncpy(lpTempFileName, str.c_str(), MAX_PATH); - return uUnique; - } - - DWORD WIN_FUNC GetTempPathA(DWORD nBufferLength, LPSTR lpBuffer) { - DEBUG_LOG("GetTempPathA(%u, %p)\n", nBufferLength, lpBuffer); - - if (nBufferLength == 0 || lpBuffer == nullptr) { wibo::lastError = ERROR_INVALID_PARAMETER; - DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n"); - return 0; - } - - const char* path; - if (!(path = getenv("WIBO_TMP_DIR"))) { - path = "Z:\\tmp\\"; - } - size_t len = strlen(path); - if (len + 1 > nBufferLength) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - DEBUG_LOG(" -> ERROR_INSUFFICIENT_BUFFER\n"); - return len + 1; - } - - DEBUG_LOG(" -> %s\n", path); - strncpy(lpBuffer, path, nBufferLength); - lpBuffer[nBufferLength - 1] = '\0'; - wibo::lastError = ERROR_SUCCESS; - return len; - } - - struct FILETIME { - unsigned int dwLowDateTime; - unsigned int dwHighDateTime; - }; - - static const uint64_t UNIX_TIME_ZERO = 11644473600LL * 10000000; - static const FILETIME defaultFiletime = { - (unsigned int)UNIX_TIME_ZERO, - (unsigned int)(UNIX_TIME_ZERO >> 32) - }; - - static FILETIME fileTimeFromDuration(uint64_t ticks100ns) { - FILETIME result; - result.dwLowDateTime = (unsigned int)(ticks100ns & 0xFFFFFFFF); - result.dwHighDateTime = (unsigned int)(ticks100ns >> 32); - return result; - } - - static FILETIME fileTimeFromTimeval(const struct timeval &value) { - uint64_t total = 0; - if (value.tv_sec > 0 || value.tv_usec > 0) { - total = (uint64_t)value.tv_sec * 10000000ULL + (uint64_t)value.tv_usec * 10ULL; - } - return fileTimeFromDuration(total); - } - - static FILETIME fileTimeFromTimespec(const struct timespec &value) { - uint64_t total = 0; - if (value.tv_sec > 0 || value.tv_nsec > 0) { - total = (uint64_t)value.tv_sec * 10000000ULL + (uint64_t)value.tv_nsec / 100ULL; - } - return fileTimeFromDuration(total); - } - - static uint64_t fileTimeToDuration(const FILETIME &value) { - return (static_cast(value.dwHighDateTime) << 32) | value.dwLowDateTime; - } - - - template - struct WIN32_FIND_DATA { - uint32_t dwFileAttributes; - FILETIME ftCreationTime; - FILETIME ftLastAccessTime; - FILETIME ftLastWriteTime; - uint32_t nFileSizeHigh; - uint32_t nFileSizeLow; - uint32_t dwReserved0; - uint32_t dwReserved1; - CharType cFileName[260]; - CharType cAlternateFileName[14]; - }; - - struct FindFirstFileHandle { - std::filesystem::directory_iterator it; - std::string pattern; - }; - - bool findNextFile(FindFirstFileHandle* handle) { - // Check if iterator is valid before using it - if (!handle || handle->it == std::filesystem::directory_iterator()) { - return false; - } - - // Early return if pattern is empty - if (handle->pattern.empty()) { - return false; - } - - // Look for a matching file with the pattern - while (handle->it != std::filesystem::directory_iterator()) { - std::filesystem::path path = *handle->it; - if (fnmatch(handle->pattern.c_str(), path.filename().c_str(), 0) == 0) { - return true; - } - handle->it++; - } - - return false; - } - - void setFindFileDataFromPath(WIN32_FIND_DATA* data, const std::filesystem::path &path) { - auto status = std::filesystem::status(path); - uint64_t fileSize = 0; - data->dwFileAttributes = 0; - if (std::filesystem::is_directory(status)) { - data->dwFileAttributes |= 0x10; - } - if (std::filesystem::is_regular_file(status)) { - data->dwFileAttributes |= 0x80; - fileSize = std::filesystem::file_size(path); - } - data->nFileSizeHigh = (uint32_t)(fileSize >> 32); - data->nFileSizeLow = (uint32_t)fileSize; - auto fileName = path.filename().string(); - assert(fileName.size() < 260); - strcpy(data->cFileName, fileName.c_str()); - strcpy(data->cAlternateFileName, "8P3FMTFN.BAD"); - } - - void setFindFileDataFromPathW(WIN32_FIND_DATA* data, const std::filesystem::path &path){ - auto status = std::filesystem::status(path); - uint64_t fileSize = 0; - data->dwFileAttributes = 0; - if (std::filesystem::is_directory(status)) { - data->dwFileAttributes |= 0x10; - } - if (std::filesystem::is_regular_file(status)) { - data->dwFileAttributes |= 0x80; - fileSize = std::filesystem::file_size(path); - } - data->nFileSizeHigh = (uint32_t)(fileSize >> 32); - data->nFileSizeLow = (uint32_t)fileSize; - auto fileName = path.filename().string(); - assert(fileName.size() < 260); - auto wideFileName = stringToWideString(fileName.c_str()); - wstrcpy(data->cFileName, wideFileName.data()); - auto wideBad = stringToWideString("8P3FMTFN.BAD"); - wstrcpy(data->cAlternateFileName, wideBad.data()); - } - - void *WIN_FUNC FindFirstFileA(const char *lpFileName, WIN32_FIND_DATA *lpFindFileData) { - DEBUG_LOG("FindFirstFileA(%p, %p)\n", lpFileName, lpFindFileData); - // This should handle wildcards too, but whatever. - auto path = files::pathFromWindows(lpFileName); - DEBUG_LOG("FindFirstFileA(%s) -> %s\n", lpFileName, path.c_str()); - - lpFindFileData->ftCreationTime = defaultFiletime; - lpFindFileData->ftLastAccessTime = defaultFiletime; - lpFindFileData->ftLastWriteTime = defaultFiletime; - - auto status = std::filesystem::status(path); - if (status.type() == std::filesystem::file_type::regular) { - setFindFileDataFromPath(lpFindFileData, path); - return (void *) 1; - } - - // If the parent path is empty then we assume the parent path is the current directory. - auto parent_path = path.parent_path(); - if (parent_path == "") { - parent_path = "."; - } - - if (!std::filesystem::exists(parent_path)) { - wibo::lastError = ERROR_PATH_NOT_FOUND; - return INVALID_HANDLE_VALUE; - } - - auto *handle = new FindFirstFileHandle(); - - std::filesystem::directory_iterator it(parent_path); - handle->it = it; - handle->pattern = path.filename().string(); - - if (!findNextFile(handle)) { - wibo::lastError = ERROR_FILE_NOT_FOUND; - delete handle; - return INVALID_HANDLE_VALUE; - } - - setFindFileDataFromPath(lpFindFileData, *handle->it++); - return handle; - } - - void *WIN_FUNC FindFirstFileW(const uint16_t *lpFileName, WIN32_FIND_DATA *lpFindFileData) { - std::string filename = wideStringToString(lpFileName); - // This should handle wildcards too, but whatever. - auto path = files::pathFromWindows(filename.c_str()); - DEBUG_LOG("FindFirstFileW(%s) -> %s\n", filename.c_str(), path.c_str()); - - lpFindFileData->ftCreationTime = defaultFiletime; - lpFindFileData->ftLastAccessTime = defaultFiletime; - lpFindFileData->ftLastWriteTime = defaultFiletime; - - auto status = std::filesystem::status(path); - if (status.type() == std::filesystem::file_type::regular) { - setFindFileDataFromPathW(lpFindFileData, path); - return (void *) 1; - } - - // If the parent path is empty then we assume the parent path is the current directory. - auto parent_path = path.parent_path(); - if (parent_path == "") { - parent_path = "."; - } - - if (!std::filesystem::exists(parent_path)) { - wibo::lastError = ERROR_PATH_NOT_FOUND; - return INVALID_HANDLE_VALUE; - } - - auto *handle = new FindFirstFileHandle(); - - std::filesystem::directory_iterator it(parent_path); - handle->it = it; - handle->pattern = path.filename().string(); - - if (!findNextFile(handle)) { - wibo::lastError = ERROR_FILE_NOT_FOUND; - delete handle; - return INVALID_HANDLE_VALUE; - } - - setFindFileDataFromPathW(lpFindFileData, *handle->it++); - return handle; - } - - typedef enum _FINDEX_INFO_LEVELS { - FindExInfoStandard, - FindExInfoBasic, - FindExInfoMaxInfoLevel - } FINDEX_INFO_LEVELS; - - typedef enum _FINDEX_SEARCH_OPS { - FindExSearchNameMatch, - FindExSearchLimitToDirectories, - FindExSearchLimitToDevices, - FindExSearchMaxSearchOp - } FINDEX_SEARCH_OPS; - - void *WIN_FUNC FindFirstFileExA(const char *lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, void *lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, void *lpSearchFilter, unsigned int dwAdditionalFlags) { - assert(fInfoLevelId == FindExInfoStandard); - - DEBUG_LOG("FindFirstFileExA(%s) -> %s\n", lpFileName, files::pathFromWindows(lpFileName).c_str()); - - return FindFirstFileA(lpFileName, (WIN32_FIND_DATA *) lpFindFileData); - } - - int WIN_FUNC FindNextFileA(void *hFindFile, WIN32_FIND_DATA *lpFindFileData) { - DEBUG_LOG("FindNextFileA(%p, %p)\n", hFindFile, lpFindFileData); - // Special value from FindFirstFileA - if (hFindFile == (void *) 1) { - wibo::lastError = ERROR_NO_MORE_FILES; - return 0; - } - - auto *handle = (FindFirstFileHandle *) hFindFile; - if (!findNextFile(handle)) { - wibo::lastError = ERROR_NO_MORE_FILES; - return 0; - } - - setFindFileDataFromPath(lpFindFileData, *handle->it++); - return 1; - } - - int WIN_FUNC FindNextFileW(void *hFindFile, WIN32_FIND_DATA *lpFindFileData) { - DEBUG_LOG("FindNextFileW(%p, %p)\n", hFindFile, lpFindFileData); - if (hFindFile == (void *)1) { - wibo::lastError = ERROR_NO_MORE_FILES; - return 0; - } - - auto *handle = (FindFirstFileHandle *)hFindFile; - if (!findNextFile(handle)) { - wibo::lastError = ERROR_NO_MORE_FILES; - return 0; - } - - setFindFileDataFromPathW(lpFindFileData, *handle->it++); - return 1; - } - - int WIN_FUNC FindClose(void *hFindFile) { - DEBUG_LOG("FindClose(%p)\n", hFindFile); - if (hFindFile != (void *) 1) { - delete (FindFirstFileHandle *)hFindFile; - } - return 1; - } - - unsigned int WIN_FUNC GetFileAttributesA(const char *lpFileName) { - auto path = files::pathFromWindows(lpFileName); - DEBUG_LOG("GetFileAttributesA(%s) -> %s\n", lpFileName, path.c_str()); - - // See ole32::CoCreateInstance - if (endsWith(path, "/license.dat")) { - DEBUG_LOG("MWCC license override\n"); - return 0x80; // FILE_ATTRIBUTE_NORMAL - } - - auto status = std::filesystem::status(path); - - wibo::lastError = 0; - - switch (status.type()) { - case std::filesystem::file_type::regular: - DEBUG_LOG("File exists\n"); - return 0x80; // FILE_ATTRIBUTE_NORMAL - case std::filesystem::file_type::directory: - return 0x10; // FILE_ATTRIBUTE_DIRECTORY - case std::filesystem::file_type::not_found: - default: - DEBUG_LOG("File does not exist\n"); - wibo::lastError = 2; // ERROR_FILE_NOT_FOUND - return 0xFFFFFFFF; // INVALID_FILE_ATTRIBUTES - } - } - - unsigned int WIN_FUNC GetFileAttributesW(const uint16_t* lpFileName) { - DEBUG_LOG("GetFileAttributesW -> "); - std::string str = wideStringToString(lpFileName); - return GetFileAttributesA(str.c_str()); - } - - unsigned short WIN_FUNC GetUserDefaultUILanguage(){ - DEBUG_LOG("STUB: GetUserDefaultUILanguage()\n"); - return 0; - } - - unsigned int WIN_FUNC WriteFile(void *hFile, const void *lpBuffer, unsigned int nNumberOfBytesToWrite, unsigned int *lpNumberOfBytesWritten, void *lpOverlapped) { - DEBUG_LOG("WriteFile(%p, %u)\n", hFile, nNumberOfBytesToWrite); - wibo::lastError = ERROR_SUCCESS; - - auto file = files::fileHandleFromHandle(hFile); - if (!file || !file->fp) { - wibo::lastError = ERROR_INVALID_HANDLE; - return FALSE; - } - - bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0; - auto *overlapped = reinterpret_cast(lpOverlapped); - bool usingOverlapped = overlapped != nullptr; - if (!usingOverlapped && lpNumberOfBytesWritten == nullptr) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - - std::optional offset; - bool updateFilePointer = true; - if (usingOverlapped) { - offset = (static_cast(overlapped->Offset) | (static_cast(overlapped->OffsetHigh) << 32)); - overlapped->Internal = STATUS_PENDING; - overlapped->InternalHigh = 0; - updateFilePointer = !handleOverlapped; - resetOverlappedEvent(overlapped); - } - - auto io = files::write(file, lpBuffer, nNumberOfBytesToWrite, offset, updateFilePointer); - DWORD completionStatus = STATUS_SUCCESS; - if (io.unixError != 0) { - completionStatus = wibo::winErrorFromErrno(io.unixError); - wibo::lastError = completionStatus; - if (lpNumberOfBytesWritten) { - *lpNumberOfBytesWritten = static_cast(io.bytesTransferred); - } - if (usingOverlapped) { - overlapped->Internal = completionStatus; - overlapped->InternalHigh = io.bytesTransferred; - signalOverlappedEvent(overlapped); - } - return FALSE; - } - - if (lpNumberOfBytesWritten && (!handleOverlapped || !usingOverlapped)) { - *lpNumberOfBytesWritten = static_cast(io.bytesTransferred); - } - - if (usingOverlapped) { - overlapped->Internal = completionStatus; - overlapped->InternalHigh = io.bytesTransferred; - if (!handleOverlapped) { - uint64_t baseOffset = offset.value_or(0); - uint64_t newOffset = baseOffset + io.bytesTransferred; - overlapped->Offset = static_cast(newOffset & 0xFFFFFFFFu); - overlapped->OffsetHigh = static_cast(newOffset >> 32); - } - signalOverlappedEvent(overlapped); - } - - return (io.bytesTransferred == nNumberOfBytesToWrite); - } - - BOOL WIN_FUNC FlushFileBuffers(HANDLE hFile) { - DEBUG_LOG("FlushFileBuffers(%p)\n", hFile); - auto data = handles::dataFromHandle(hFile, false); - if (data.type != handles::TYPE_FILE || data.ptr == nullptr) { - wibo::lastError = ERROR_INVALID_HANDLE; - return FALSE; - } - auto file = reinterpret_cast(data.ptr); - if (!file || !file->fp) { - wibo::lastError = ERROR_INVALID_HANDLE; - return FALSE; - } - FILE *fp = file->fp; - if (fflush(fp) != 0) { - wibo::lastError = ERROR_ACCESS_DENIED; - return FALSE; - } - int fd = file->fd; - if (fd >= 0 && fsync(fd) != 0) { - wibo::lastError = ERROR_ACCESS_DENIED; - return FALSE; - } - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - unsigned int WIN_FUNC ReadFile(void *hFile, void *lpBuffer, unsigned int nNumberOfBytesToRead, unsigned int *lpNumberOfBytesRead, void *lpOverlapped) { - DEBUG_LOG("ReadFile(%p, %u)\n", hFile, nNumberOfBytesToRead); - wibo::lastError = ERROR_SUCCESS; - - auto file = files::fileHandleFromHandle(hFile); - if (!file || !file->fp) { - wibo::lastError = ERROR_INVALID_HANDLE; - return FALSE; - } - - bool handleOverlapped = (file->flags & FILE_FLAG_OVERLAPPED) != 0; - auto *overlapped = reinterpret_cast(lpOverlapped); - bool usingOverlapped = overlapped != nullptr; - if (!usingOverlapped && lpNumberOfBytesRead == nullptr) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - - std::optional offset; - bool updateFilePointer = true; - if (usingOverlapped) { - offset = (static_cast(overlapped->Offset) | (static_cast(overlapped->OffsetHigh) << 32)); - overlapped->Internal = STATUS_PENDING; - overlapped->InternalHigh = 0; - updateFilePointer = !handleOverlapped; - resetOverlappedEvent(overlapped); - } - - auto io = files::read(file, lpBuffer, nNumberOfBytesToRead, offset, updateFilePointer); - DWORD completionStatus = STATUS_SUCCESS; - if (io.unixError != 0) { - completionStatus = wibo::winErrorFromErrno(io.unixError); - wibo::lastError = completionStatus; - if (lpNumberOfBytesRead) { - *lpNumberOfBytesRead = static_cast(io.bytesTransferred); - } - if (usingOverlapped) { - overlapped->Internal = completionStatus; - overlapped->InternalHigh = io.bytesTransferred; - signalOverlappedEvent(overlapped); - } - return FALSE; - } - - if (io.reachedEnd && io.bytesTransferred == 0 && handleOverlapped) { - completionStatus = ERROR_HANDLE_EOF; - } - - if (lpNumberOfBytesRead && (!handleOverlapped || !usingOverlapped)) { - *lpNumberOfBytesRead = static_cast(io.bytesTransferred); - } - - if (usingOverlapped) { - overlapped->Internal = completionStatus; - overlapped->InternalHigh = io.bytesTransferred; - if (!handleOverlapped) { - uint64_t baseOffset = offset.value_or(0); - uint64_t newOffset = baseOffset + io.bytesTransferred; - overlapped->Offset = static_cast(newOffset & 0xFFFFFFFFu); - overlapped->OffsetHigh = static_cast(newOffset >> 32); - } - signalOverlappedEvent(overlapped); - } - - return TRUE; - } - - enum { - CREATE_NEW = 1, - CREATE_ALWAYS = 2, - OPEN_EXISTING = 3, - OPEN_ALWAYS = 4, - TRUNCATE_EXISTING = 5, - }; - void *WIN_FUNC CreateFileA( - const char* lpFileName, - unsigned int dwDesiredAccess, - unsigned int dwShareMode, - void *lpSecurityAttributes, - unsigned int dwCreationDisposition, - unsigned int dwFlagsAndAttributes, - void *hTemplateFile) { - std::string path = files::pathFromWindows(lpFileName); - DEBUG_LOG("CreateFileA(filename=%s (%s), desiredAccess=0x%x, shareMode=%u, securityAttributes=%p, creationDisposition=%u, flagsAndAttributes=%u)\n", - lpFileName, path.c_str(), - dwDesiredAccess, dwShareMode, lpSecurityAttributes, - dwCreationDisposition, dwFlagsAndAttributes); - - wibo::lastError = 0; // possibly overwritten later in this function - - // Based on https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#parameters - // and this table: https://stackoverflow.com/a/14469641 - bool fileExists = (access(path.c_str(), F_OK) == 0); - bool shouldTruncate = false; - switch (dwCreationDisposition) { - case CREATE_ALWAYS: - if (fileExists) { - wibo::lastError = 183; // ERROR_ALREADY_EXISTS - shouldTruncate = true; // "The function overwrites the file" - // Function succeeds - } - break; - case CREATE_NEW: - if (fileExists) { - wibo::lastError = 80; // ERROR_FILE_EXISTS - return INVALID_HANDLE_VALUE; - } - break; - case OPEN_ALWAYS: - if (fileExists) { - wibo::lastError = 183; // ERROR_ALREADY_EXISTS - // Function succeeds - } - break; - case OPEN_EXISTING: - if (!fileExists) { - wibo::lastError = 2; // ERROR_FILE_NOT_FOUND - return INVALID_HANDLE_VALUE; - } - break; - case TRUNCATE_EXISTING: - shouldTruncate = true; - if (!fileExists) { - wibo::lastError = 2; // ERROR_FILE_NOT_FOUND - return INVALID_HANDLE_VALUE; - } - break; - default: - assert(0); - } - - FILE *fp; - if (dwDesiredAccess == 0x80000000) { // read - fp = fopen(path.c_str(), "rb"); - } else if (dwDesiredAccess == 0x40000000) { // write - if (shouldTruncate || !fileExists) { - fp = fopen(path.c_str(), "wb"); - } else { - // There is no way to fopen with only write permissions - // and without truncating the file... - fp = fopen(path.c_str(), "rb+"); - } - } else if (dwDesiredAccess == 0xc0000000) { // read/write - if (shouldTruncate || !fileExists) { - fp = fopen(path.c_str(), "wb+"); - } else { - fp = fopen(path.c_str(), "rb+"); - } - } else { - assert(0); - } - - if (fp) { - void *handle = files::allocFpHandle(fp, dwDesiredAccess, dwShareMode, dwFlagsAndAttributes, true); - DEBUG_LOG("-> %p\n", handle); - return handle; - } else { - setLastErrorFromErrno(); - return INVALID_HANDLE_VALUE; - } - } - - void *WIN_FUNC CreateFileW(const uint16_t *lpFileName, unsigned int dwDesiredAccess, unsigned int dwShareMode, - void *lpSecurityAttributes, unsigned int dwCreationDisposition, unsigned int dwFlagsAndAttributes, - void *hTemplateFile) { - DEBUG_LOG("CreateFileW -> "); - const auto lpFileNameA = wideStringToString(lpFileName); - return CreateFileA(lpFileNameA.c_str(), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, - dwFlagsAndAttributes, hTemplateFile); - } - - void *WIN_FUNC CreateFileMappingA( - void *hFile, - void *lpFileMappingAttributes, - unsigned int flProtect, - unsigned int dwMaximumSizeHigh, - unsigned int dwMaximumSizeLow, - const char *lpName) { - DEBUG_LOG("CreateFileMappingA(%p, %p, %u, %u, %u, %s)\n", hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName ? lpName : "(null)"); - (void) lpFileMappingAttributes; - (void) lpName; - - auto mapping = new MappingObject(); - mapping->protect = flProtect; - - uint64_t size = ((uint64_t) dwMaximumSizeHigh << 32) | dwMaximumSizeLow; - if (flProtect != 0x02 /* PAGE_READONLY */ && flProtect != 0x04 /* PAGE_READWRITE */ && flProtect != 0x08 /* PAGE_WRITECOPY */) { - DEBUG_LOG("CreateFileMappingA: unsupported protection 0x%x\n", flProtect); - wibo::lastError = ERROR_INVALID_PARAMETER; - closeMappingIfPossible(mapping); - return nullptr; - } - - if (hFile == (void *) -1) { - mapping->anonymous = true; - mapping->fd = -1; - if (size == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - closeMappingIfPossible(mapping); - return nullptr; - } - mapping->maxSize = size; - } else { - FILE *fp = files::fpFromHandle(hFile); - if (!fp) { - wibo::lastError = ERROR_INVALID_HANDLE; - closeMappingIfPossible(mapping); - return nullptr; - } - int originalFd = fileno(fp); - if (originalFd == -1) { - setLastErrorFromErrno(); - closeMappingIfPossible(mapping); - return nullptr; - } - int dupFd = fcntl(originalFd, F_DUPFD_CLOEXEC, 0); - if (dupFd == -1) { - setLastErrorFromErrno(); - closeMappingIfPossible(mapping); - return nullptr; - } - mapping->fd = dupFd; - if (size == 0) { - int64_t fileSize = getFileSize(hFile); - if (fileSize < 0) { - closeMappingIfPossible(mapping); - return nullptr; - } - size = static_cast(fileSize); - } - mapping->maxSize = size; - } - - wibo::lastError = ERROR_SUCCESS; - return handles::allocDataHandle({handles::TYPE_MAPPED, mapping, static_cast(mapping->maxSize)}); - } - - void *WIN_FUNC CreateFileMappingW( - void *hFile, - void *lpFileMappingAttributes, - unsigned int flProtect, - unsigned int dwMaximumSizeHigh, - unsigned int dwMaximumSizeLow, - const uint16_t *lpName) { - DEBUG_LOG("CreateFileMappingW -> "); - std::string name = wideStringToString(lpName); - return CreateFileMappingA(hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName ? name.c_str() : nullptr); - } - - constexpr unsigned int FILE_MAP_COPY = 0x00000001; - constexpr unsigned int FILE_MAP_WRITE = 0x00000002; - constexpr unsigned int FILE_MAP_READ = 0x00000004; - constexpr unsigned int FILE_MAP_EXECUTE = 0x00000020; - - void *WIN_FUNC MapViewOfFile( - void *hFileMappingObject, - unsigned int dwDesiredAccess, - unsigned int dwFileOffsetHigh, - unsigned int dwFileOffsetLow, - unsigned int dwNumberOfBytesToMap) { - DEBUG_LOG("MapViewOfFile(%p, 0x%x, %u, %u, %u)\n", hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap); - - handles::Data data = handles::dataFromHandle(hFileMappingObject, false); - if (data.type != handles::TYPE_MAPPED) { - wibo::lastError = ERROR_INVALID_HANDLE; - return nullptr; - } - auto *mapping = reinterpret_cast(data.ptr); - if (!mapping) { - wibo::lastError = ERROR_INVALID_HANDLE; - return nullptr; - } - if (mapping->closed) { - wibo::lastError = ERROR_INVALID_HANDLE; - return nullptr; - } - - uint64_t offset = ((uint64_t) dwFileOffsetHigh << 32) | dwFileOffsetLow; - if (mapping->anonymous && offset != 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - size_t maxSize = mapping->maxSize; - uint64_t length = dwNumberOfBytesToMap; - if (length == 0) { - if (maxSize == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - if (offset > maxSize) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - length = maxSize - offset; - } - if (length == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - if (maxSize && offset + length > maxSize) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - - int prot = PROT_READ; - bool wantWrite = (dwDesiredAccess & FILE_MAP_WRITE) != 0; - bool wantExecute = (dwDesiredAccess & FILE_MAP_EXECUTE) != 0; - - if (mapping->protect == 0x04 /* PAGE_READWRITE */) { - if (wantWrite) { - prot |= PROT_WRITE; - } - } else { // read-only or write copy - if (wantWrite && !(dwDesiredAccess & FILE_MAP_COPY)) { - wibo::lastError = ERROR_ACCESS_DENIED; - return nullptr; - } - } - if (wantExecute) { - prot |= PROT_EXEC; - } - - int flags = 0; - if (mapping->anonymous) { - flags |= MAP_ANONYMOUS; - } - flags |= (dwDesiredAccess & FILE_MAP_COPY) ? MAP_PRIVATE : MAP_SHARED; - - size_t pageSize = static_cast(sysconf(_SC_PAGESIZE)); - off_t alignedOffset = mapping->anonymous ? 0 : static_cast(offset & ~static_cast(pageSize - 1)); - size_t offsetDelta = static_cast(offset - alignedOffset); - size_t mapLength = static_cast(length + offsetDelta); - if (mapLength < length) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - - int mmapFd = mapping->anonymous ? -1 : mapping->fd; - void *mapBase = mmap(nullptr, mapLength, prot, flags, mmapFd, alignedOffset); - if (mapBase == MAP_FAILED) { - setLastErrorFromErrno(); - return nullptr; - } - void *viewPtr = static_cast(mapBase) + offsetDelta; - g_viewInfo[viewPtr] = ViewInfo{mapBase, mapLength, mapping}; - mapping->refCount++; - wibo::lastError = ERROR_SUCCESS; - return viewPtr; - } - - int WIN_FUNC UnmapViewOfFile(void *lpBaseAddress) { - DEBUG_LOG("UnmapViewOfFile(%p)\n", lpBaseAddress); - auto it = g_viewInfo.find(lpBaseAddress); - if (it == g_viewInfo.end()) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - ViewInfo info = it->second; - g_viewInfo.erase(it); - if (info.mapBase && info.mapLength) { - munmap(info.mapBase, info.mapLength); - } - if (info.owner && info.owner->refCount > 0) { - info.owner->refCount--; - tryReleaseMapping(info.owner); - } - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - BOOL WIN_FUNC DeleteFileA(const char* lpFileName) { - if (!lpFileName) { - wibo::lastError = ERROR_INVALID_PARAMETER; - DEBUG_LOG("DeleteFileA(NULL) -> ERROR_INVALID_PARAMETER\n"); - return FALSE; - } - std::string path = files::pathFromWindows(lpFileName); - DEBUG_LOG("DeleteFileA(%s) -> %s\n", lpFileName, path.c_str()); - if (unlink(path.c_str()) == 0) { - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - setLastErrorFromErrno(); return FALSE; } - BOOL WIN_FUNC DeleteFileW(const uint16_t *lpFileName) { - DEBUG_LOG("DeleteFileW -> "); - if (!lpFileName) { - wibo::lastError = ERROR_INVALID_PARAMETER; - DEBUG_LOG("ERROR_INVALID_PARAMETER\n"); - return FALSE; - } - std::string name = wideStringToString(lpFileName); - return DeleteFileA(name.c_str()); + r = ftell(fp); + assert(r >= 0); + return TRUE; +} + +BOOL WIN_FUNC SetEndOfFile(HANDLE hFile) { + DEBUG_LOG("SetEndOfFile(%p)\n", hFile); + FILE *fp = files::fpFromHandle(hFile); + if (!fp) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; } - - BOOL WIN_FUNC MoveFileA(const char *lpExistingFileName, const char *lpNewFileName) { - DEBUG_LOG("MoveFileA(%s, %s)\n", - lpExistingFileName ? lpExistingFileName : "(null)", - lpNewFileName ? lpNewFileName : "(null)"); - if (!lpExistingFileName || !lpNewFileName) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - auto fromPath = files::pathFromWindows(lpExistingFileName); - auto toPath = files::pathFromWindows(lpNewFileName); - std::error_code ec; - if (std::filesystem::exists(toPath, ec)) { - wibo::lastError = ERROR_ALREADY_EXISTS; - return FALSE; - } - if (ec) { - errno = ec.value(); - setLastErrorFromErrno(); - return FALSE; - } - std::filesystem::rename(fromPath, toPath, ec); - if (ec) { - errno = ec.value(); - setLastErrorFromErrno(); - return FALSE; - } - wibo::lastError = ERROR_SUCCESS; - return TRUE; + if (fflush(fp) != 0 || ftruncate(fileno(fp), ftell(fp)) != 0) { + setLastErrorFromErrno(); + return FALSE; } + return TRUE; +} - BOOL WIN_FUNC MoveFileW(const uint16_t *lpExistingFileName, const uint16_t *lpNewFileName) { - DEBUG_LOG("MoveFileW -> "); - if (!lpExistingFileName || !lpNewFileName) { - wibo::lastError = ERROR_INVALID_PARAMETER; - DEBUG_LOG("ERROR_INVALID_PARAMETER\n"); - return FALSE; - } - std::string from = wideStringToString(lpExistingFileName); - std::string to = wideStringToString(lpNewFileName); - return MoveFileA(from.c_str(), to.c_str()); +int WIN_FUNC CreateDirectoryA(const char *lpPathName, void *lpSecurityAttributes) { + std::string path = files::pathFromWindows(lpPathName); + DEBUG_LOG("CreateDirectoryA(%s, %p)\n", path.c_str(), lpSecurityAttributes); + return mkdir(path.c_str(), 0755) == 0; +} + +int WIN_FUNC RemoveDirectoryA(const char *lpPathName) { + std::string path = files::pathFromWindows(lpPathName); + DEBUG_LOG("RemoveDirectoryA(%s)\n", path.c_str()); + return rmdir(path.c_str()) == 0; +} + +int WIN_FUNC SetFileAttributesA(const char *lpPathName, unsigned int dwFileAttributes) { + std::string path = files::pathFromWindows(lpPathName); + DEBUG_LOG("SetFileAttributesA(%s, %u)\n", path.c_str(), dwFileAttributes); + return 1; +} + +DWORD WIN_FUNC GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) { + DEBUG_LOG("GetFileSize(%p, %p) ", hFile, lpFileSizeHigh); + int64_t size = getFileSize(hFile); + if (size == -1) { + DEBUG_LOG("-> INVALID_FILE_SIZE\n"); + return 0xFFFFFFFF; // INVALID_FILE_SIZE } - - DWORD WIN_FUNC SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod) { - DEBUG_LOG("SetFilePointer(%p, %d, %d)\n", hFile, lDistanceToMove, dwMoveMethod); - if (hFile == nullptr) { - wibo::lastError = ERROR_INVALID_HANDLE; - return INVALID_SET_FILE_POINTER; - } - assert(!lpDistanceToMoveHigh || *lpDistanceToMoveHigh == 0); - FILE *fp = files::fpFromHandle(hFile); - if (!fp) { - wibo::lastError = ERROR_INVALID_HANDLE; - return INVALID_SET_FILE_POINTER; - } - wibo::lastError = ERROR_SUCCESS; - int r = fseek(fp, lDistanceToMove, dwMoveMethod == 0 ? SEEK_SET : dwMoveMethod == 1 ? SEEK_CUR : SEEK_END); - - if (r < 0) { - if (errno == EINVAL) - wibo::lastError = ERROR_NEGATIVE_SEEK; - else - wibo::lastError = ERROR_INVALID_PARAMETER; - return INVALID_SET_FILE_POINTER; - } - - r = ftell(fp); - assert(r >= 0); - return r; + DEBUG_LOG("-> %ld\n", size); + if (lpFileSizeHigh != nullptr) { + *lpFileSizeHigh = size >> 32; } + return static_cast(size); +} - BOOL WIN_FUNC SetFilePointerEx(HANDLE hFile, LARGE_INTEGER lDistanceToMove, PLARGE_INTEGER lpDistanceToMoveHigh, - DWORD dwMoveMethod) { - if (hFile == nullptr) { - wibo::lastError = ERROR_INVALID_HANDLE; - return 0; - } - assert(!lpDistanceToMoveHigh || *lpDistanceToMoveHigh == 0); - DEBUG_LOG("SetFilePointerEx(%p, %ld, %d)\n", hFile, lDistanceToMove, dwMoveMethod); - FILE *fp = files::fpFromHandle(hFile); - if (!fp) { - wibo::lastError = ERROR_INVALID_HANDLE; - return 0; - } - wibo::lastError = ERROR_SUCCESS; - int r = fseeko64(fp, lDistanceToMove, dwMoveMethod == 0 ? SEEK_SET : dwMoveMethod == 1 ? SEEK_CUR : SEEK_END); - - if (r < 0) { - if (errno == EINVAL) - wibo::lastError = ERROR_NEGATIVE_SEEK; - else - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - - r = ftell(fp); - assert(r >= 0); - return TRUE; - } - - BOOL WIN_FUNC SetEndOfFile(HANDLE hFile) { - DEBUG_LOG("SetEndOfFile(%p)\n", hFile); - FILE *fp = files::fpFromHandle(hFile); - if (!fp) { - wibo::lastError = ERROR_INVALID_HANDLE; - return FALSE; - } - if (fflush(fp) != 0 || ftruncate(fileno(fp), ftell(fp)) != 0) { - setLastErrorFromErrno(); - return FALSE; - } - return TRUE; - } - - int WIN_FUNC CreateDirectoryA(const char *lpPathName, void *lpSecurityAttributes) { - std::string path = files::pathFromWindows(lpPathName); - DEBUG_LOG("CreateDirectoryA(%s, %p)\n", path.c_str(), lpSecurityAttributes); - return mkdir(path.c_str(), 0755) == 0; - } - - int WIN_FUNC RemoveDirectoryA(const char *lpPathName) { - std::string path = files::pathFromWindows(lpPathName); - DEBUG_LOG("RemoveDirectoryA(%s)\n", path.c_str()); - return rmdir(path.c_str()) == 0; - } - - int WIN_FUNC SetFileAttributesA(const char *lpPathName, unsigned int dwFileAttributes) { - std::string path = files::pathFromWindows(lpPathName); - DEBUG_LOG("SetFileAttributesA(%s, %u)\n", path.c_str(), dwFileAttributes); - return 1; - } - - DWORD WIN_FUNC GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh) { - DEBUG_LOG("GetFileSize(%p, %p) ", hFile, lpFileSizeHigh); - int64_t size = getFileSize(hFile); - if (size == -1) { - DEBUG_LOG("-> INVALID_FILE_SIZE\n"); - return 0xFFFFFFFF; // INVALID_FILE_SIZE - } - DEBUG_LOG("-> %ld\n", size); - if (lpFileSizeHigh != nullptr) { - *lpFileSizeHigh = size >> 32; - } - return static_cast(size); - } - - /* - * Time - */ - int WIN_FUNC GetFileTime(void *hFile, FILETIME *lpCreationTime, FILETIME *lpLastAccessTime, FILETIME *lpLastWriteTime) { - DEBUG_LOG("GetFileTime(%p, %p, %p, %p)\n", hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime); - FILE *fp = files::fpFromHandle(hFile); - if (!fp) { - wibo::lastError = ERROR_INVALID_HANDLE; - return 0; - } - int fd = fileno(fp); - if (fd < 0) { - setLastErrorFromErrno(); - return 0; - } - struct stat st; - if (fstat(fd, &st) != 0) { - setLastErrorFromErrno(); - return 0; - } - auto makeFileTime = [](time_t sec, long nanos) { - uint64_t ticks = UNIX_TIME_ZERO; - ticks += static_cast(sec) * 10000000ULL; - ticks += static_cast(nanos) / 100ULL; - return fileTimeFromDuration(ticks); - }; - if (lpCreationTime) { -#if defined(__APPLE__) - *lpCreationTime = makeFileTime(st.st_ctimespec.tv_sec, st.st_ctimespec.tv_nsec); -#elif defined(__linux__) - *lpCreationTime = makeFileTime(st.st_ctim.tv_sec, st.st_ctim.tv_nsec); -#else - *lpCreationTime = makeFileTime(st.st_ctime, 0); -#endif - } - if (lpLastAccessTime) { -#if defined(__APPLE__) - *lpLastAccessTime = makeFileTime(st.st_atimespec.tv_sec, st.st_atimespec.tv_nsec); -#elif defined(__linux__) - *lpLastAccessTime = makeFileTime(st.st_atim.tv_sec, st.st_atim.tv_nsec); -#else - *lpLastAccessTime = makeFileTime(st.st_atime, 0); -#endif - } - if (lpLastWriteTime) { -#if defined(__APPLE__) - *lpLastWriteTime = makeFileTime(st.st_mtimespec.tv_sec, st.st_mtimespec.tv_nsec); -#elif defined(__linux__) - *lpLastWriteTime = makeFileTime(st.st_mtim.tv_sec, st.st_mtim.tv_nsec); -#else - *lpLastWriteTime = makeFileTime(st.st_mtime, 0); -#endif - } - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - struct SYSTEMTIME { - short wYear; - short wMonth; - short wDayOfWeek; - short wDay; - short wHour; - short wMinute; - short wSecond; - short wMilliseconds; - }; - - static constexpr int64_t HUNDRED_NS_PER_SECOND = 10000000LL; - static constexpr int64_t HUNDRED_NS_PER_MILLISECOND = 10000LL; - static constexpr int64_t SECONDS_PER_DAY = 86400LL; - static constexpr uint64_t TICKS_PER_DAY = static_cast(SECONDS_PER_DAY) * HUNDRED_NS_PER_SECOND; - static constexpr uint64_t MAX_VALID_FILETIME = 0x8000000000000000ULL; - static constexpr int64_t DAYS_TO_UNIX_EPOCH = 134774LL; - - struct CivilDate { - int year; - unsigned month; - unsigned day; - }; - - static int64_t daysFromCivil(int year, unsigned month, unsigned day) { - year -= month <= 2 ? 1 : 0; - const int era = (year >= 0 ? year : year - 399) / 400; - const unsigned yoe = static_cast(year - era * 400); - const unsigned doy = (153 * (month + (month > 2 ? -3 : 9)) + 2) / 5 + day - 1; - const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + yoe / 400 + doy; - return era * 146097 + static_cast(doe) - 719468; - } - - static CivilDate civilFromDays(int64_t z) { - z += 719468; - const int64_t era = (z >= 0 ? z : z - 146096) / 146097; - const unsigned doe = static_cast(z - era * 146097); - const unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; - int year = static_cast(yoe) + static_cast(era) * 400; - const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); - const unsigned mp = (5 * doy + 2) / 153; - const unsigned day = doy - (153 * mp + 2) / 5 + 1; - int month = static_cast(mp) + (mp < 10 ? 3 : -9); - year += (month <= 2) ? 1 : 0; - return {year, static_cast(month), day}; - } - - static bool isLeapYear(int year) { - if (year % 400 == 0) { - return true; - } - if (year % 100 == 0) { - return false; - } - return (year % 4) == 0; - } - - static unsigned daysInMonth(int year, unsigned month) { - static const unsigned baseDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - unsigned idx = month - 1; - unsigned value = baseDays[idx]; - if (month == 2 && isLeapYear(year)) { - value += 1; - } - return value; - } - - static bool validateSystemTime(const SYSTEMTIME &st) { - if (st.wYear < 1601) { - return false; - } - if (st.wMonth < 1 || st.wMonth > 12) { - return false; - } - if (st.wDay < 1 || st.wDay > static_cast(daysInMonth(st.wYear, static_cast(st.wMonth)))) { - return false; - } - if (st.wHour < 0 || st.wHour > 23) { - return false; - } - if (st.wMinute < 0 || st.wMinute > 59) { - return false; - } - if (st.wSecond < 0 || st.wSecond > 59) { - return false; - } - if (st.wMilliseconds < 0 || st.wMilliseconds > 999) { - return false; - } - return true; - } - - static bool systemTimeToUnixParts(const SYSTEMTIME &st, int64_t &secondsOut, uint32_t &hundredsOut) { - if (!validateSystemTime(st)) { - return false; - } - int64_t days = daysFromCivil(st.wYear, static_cast(st.wMonth), static_cast(st.wDay)); - int64_t secondsOfDay = static_cast(st.wHour) * 3600LL + static_cast(st.wMinute) * 60LL + st.wSecond; - secondsOut = days * SECONDS_PER_DAY + secondsOfDay; - hundredsOut = static_cast(st.wMilliseconds) * static_cast(HUNDRED_NS_PER_MILLISECOND); - return true; - } - - static bool fileTimeToUnixParts(const FILETIME &ft, int64_t &secondsOut, uint32_t &hundredsOut) { - uint64_t ticks = fileTimeToDuration(ft); - if (ticks >= UNIX_TIME_ZERO) { - uint64_t diff = ticks - UNIX_TIME_ZERO; - secondsOut = static_cast(diff / HUNDRED_NS_PER_SECOND); - hundredsOut = static_cast(diff % HUNDRED_NS_PER_SECOND); - } - else { - uint64_t diff = UNIX_TIME_ZERO - ticks; - secondsOut = -static_cast(diff / HUNDRED_NS_PER_SECOND); - uint64_t rem = diff % HUNDRED_NS_PER_SECOND; - if (rem != 0) { - secondsOut -= 1; - rem = HUNDRED_NS_PER_SECOND - rem; - } - hundredsOut = static_cast(rem); - } - return true; - } - - static bool unixPartsToFileTime(int64_t seconds, uint32_t hundreds, FILETIME &out) { - if (hundreds >= HUNDRED_NS_PER_SECOND) { - return false; - } -#if defined(__SIZEOF_INT128__) - __int128 total = static_cast<__int128>(seconds) * HUNDRED_NS_PER_SECOND; - total += static_cast<__int128>(hundreds); - total += static_cast<__int128>(UNIX_TIME_ZERO); - if (total < 0 || total > static_cast<__int128>(std::numeric_limits::max())) { - return false; - } - uint64_t ticks = static_cast(total); -#else - long double total = static_cast(seconds) * static_cast(HUNDRED_NS_PER_SECOND); - total += static_cast(hundreds); - total += static_cast(UNIX_TIME_ZERO); - if (total < 0.0L || total > static_cast(std::numeric_limits::max())) { - return false; - } - uint64_t ticks = static_cast(total); -#endif - out = fileTimeFromDuration(ticks); - return true; - } - - static bool unixPartsToTimespec(int64_t seconds, uint32_t hundreds, struct timespec &out) { - if (hundreds >= HUNDRED_NS_PER_SECOND) { - return false; - } - if (seconds > static_cast(std::numeric_limits::max()) || - seconds < static_cast(std::numeric_limits::min())) { - return false; - } - out.tv_sec = static_cast(seconds); - out.tv_nsec = static_cast(hundreds) * 100L; - return true; - } - - static bool tmToUnixSeconds(const struct tm &tmValue, int64_t &secondsOut) { - int year = tmValue.tm_year + 1900; - int month = tmValue.tm_mon + 1; - int day = tmValue.tm_mday; - int hour = tmValue.tm_hour; - int minute = tmValue.tm_min; - int second = tmValue.tm_sec; - if (month < 1 || month > 12) { - return false; - } - if (day < 1 || day > static_cast(daysInMonth(year, static_cast(month)))) { - return false; - } - if (hour < 0 || hour > 23) { - return false; - } - if (minute < 0 || minute > 59) { - return false; - } - if (second < 0 || second > 60) { - return false; - } - if (second == 60) { - second = 59; - } - int64_t days = daysFromCivil(year, static_cast(month), static_cast(day)); - secondsOut = days * SECONDS_PER_DAY + static_cast(hour) * 3600LL + static_cast(minute) * 60LL + second; - return true; - } - - static bool shouldIgnoreFileTimeParam(const FILETIME *ft) { - if (!ft) { - return true; - } - if (ft->dwLowDateTime == 0 && ft->dwHighDateTime == 0) { - return true; - } - if (ft->dwLowDateTime == 0xFFFFFFFF && ft->dwHighDateTime == 0xFFFFFFFF) { - return true; - } - return false; - } - - static struct timespec statAccessTimespec(const struct stat &st) { -#if defined(__APPLE__) - return st.st_atimespec; -#elif defined(__linux__) - return st.st_atim; -#else - struct timespec ts {}; - ts.tv_sec = st.st_atime; - ts.tv_nsec = 0; - return ts; -#endif - } - - static struct timespec statModifyTimespec(const struct stat &st) { -#if defined(__APPLE__) - return st.st_mtimespec; -#elif defined(__linux__) - return st.st_mtim; -#else - struct timespec ts {}; - ts.tv_sec = st.st_mtime; - ts.tv_nsec = 0; - return ts; -#endif - } - - void WIN_FUNC GetSystemTime(SYSTEMTIME *lpSystemTime) { - DEBUG_LOG("GetSystemTime(%p)\n", lpSystemTime); - - time_t t = time(NULL); - struct tm *tm = gmtime(&t); - assert(tm != NULL); - - lpSystemTime->wYear = tm->tm_year + 1900; - lpSystemTime->wMonth = tm->tm_mon + 1; - lpSystemTime->wDayOfWeek = tm->tm_wday; - lpSystemTime->wDay = tm->tm_mday; - lpSystemTime->wHour = tm->tm_hour; - lpSystemTime->wMinute = tm->tm_min; - lpSystemTime->wSecond = tm->tm_sec; - lpSystemTime->wMilliseconds = 0; - } - - void WIN_FUNC GetLocalTime(SYSTEMTIME *lpSystemTime) { - DEBUG_LOG("GetLocalTime(%p)\n", lpSystemTime); - - time_t t = time(NULL); - struct tm *tm = localtime(&t); - assert(tm != NULL); - - lpSystemTime->wYear = tm->tm_year + 1900; - lpSystemTime->wMonth = tm->tm_mon + 1; - lpSystemTime->wDayOfWeek = tm->tm_wday; - lpSystemTime->wDay = tm->tm_mday; - lpSystemTime->wHour = tm->tm_hour; - lpSystemTime->wMinute = tm->tm_min; - lpSystemTime->wSecond = tm->tm_sec; - lpSystemTime->wMilliseconds = 0; - } - - int WIN_FUNC SystemTimeToFileTime(const SYSTEMTIME *lpSystemTime, FILETIME *lpFileTime) { - DEBUG_LOG("SystemTimeToFileTime(%p, %p)\n", lpSystemTime, lpFileTime); - if (!lpSystemTime || !lpFileTime) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - int64_t seconds = 0; - uint32_t hundreds = 0; - if (!systemTimeToUnixParts(*lpSystemTime, seconds, hundreds)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - FILETIME result; - if (!unixPartsToFileTime(seconds, hundreds, result)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - if (fileTimeToDuration(result) >= MAX_VALID_FILETIME) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - *lpFileTime = result; - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - void WIN_FUNC GetSystemTimeAsFileTime(FILETIME *lpSystemTimeAsFileTime) { - DEBUG_LOG("GetSystemTimeAsFileTime(%p)\n", lpSystemTimeAsFileTime); - if (!lpSystemTimeAsFileTime) { - return; - } -#if defined(CLOCK_REALTIME) - struct timespec ts {}; - if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { - uint64_t ticks = UNIX_TIME_ZERO; - ticks += static_cast(ts.tv_sec) * HUNDRED_NS_PER_SECOND; - ticks += static_cast(ts.tv_nsec) / 100ULL; - *lpSystemTimeAsFileTime = fileTimeFromDuration(ticks); - return; - } -#endif - struct timeval tv {}; - if (gettimeofday(&tv, nullptr) == 0) { - uint64_t ticks = UNIX_TIME_ZERO; - ticks += static_cast(tv.tv_sec) * HUNDRED_NS_PER_SECOND; - ticks += static_cast(tv.tv_usec) * 10ULL; - *lpSystemTimeAsFileTime = fileTimeFromDuration(ticks); - return; - } - *lpSystemTimeAsFileTime = defaultFiletime; - } - - DWORD WIN_FUNC GetTickCount() { - DEBUG_LOG("GetTickCount()"); -#if defined(CLOCK_MONOTONIC) - struct timespec ts {}; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - uint64_t milliseconds = static_cast(ts.tv_sec) * 1000ULL + static_cast(ts.tv_nsec) / 1000000ULL; - int ticks = static_cast(milliseconds & 0xFFFFFFFFu); - DEBUG_LOG(" -> %u\n", ticks); - return ticks; - } -#endif - struct timeval tv {}; - if (gettimeofday(&tv, nullptr) == 0) { - uint64_t milliseconds = static_cast(tv.tv_sec) * 1000ULL + static_cast(tv.tv_usec) / 1000ULL; - int ticks = static_cast(milliseconds & 0xFFFFFFFFu); - DEBUG_LOG(" -> %u\n", ticks); - return ticks; - } - DEBUG_LOG(" -> 0\n"); +/* + * Time + */ +int WIN_FUNC GetFileTime(void *hFile, FILETIME *lpCreationTime, FILETIME *lpLastAccessTime, FILETIME *lpLastWriteTime) { + DEBUG_LOG("GetFileTime(%p, %p, %p, %p)\n", hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime); + FILE *fp = files::fpFromHandle(hFile); + if (!fp) { + wibo::lastError = ERROR_INVALID_HANDLE; return 0; } + int fd = fileno(fp); + if (fd < 0) { + setLastErrorFromErrno(); + return 0; + } + struct stat st; + if (fstat(fd, &st) != 0) { + setLastErrorFromErrno(); + return 0; + } + auto makeFileTime = [](time_t sec, long nanos) { + uint64_t ticks = UNIX_TIME_ZERO; + ticks += static_cast(sec) * 10000000ULL; + ticks += static_cast(nanos) / 100ULL; + return fileTimeFromDuration(ticks); + }; + if (lpCreationTime) { +#if defined(__APPLE__) + *lpCreationTime = makeFileTime(st.st_ctimespec.tv_sec, st.st_ctimespec.tv_nsec); +#elif defined(__linux__) + *lpCreationTime = makeFileTime(st.st_ctim.tv_sec, st.st_ctim.tv_nsec); +#else + *lpCreationTime = makeFileTime(st.st_ctime, 0); +#endif + } + if (lpLastAccessTime) { +#if defined(__APPLE__) + *lpLastAccessTime = makeFileTime(st.st_atimespec.tv_sec, st.st_atimespec.tv_nsec); +#elif defined(__linux__) + *lpLastAccessTime = makeFileTime(st.st_atim.tv_sec, st.st_atim.tv_nsec); +#else + *lpLastAccessTime = makeFileTime(st.st_atime, 0); +#endif + } + if (lpLastWriteTime) { +#if defined(__APPLE__) + *lpLastWriteTime = makeFileTime(st.st_mtimespec.tv_sec, st.st_mtimespec.tv_nsec); +#elif defined(__linux__) + *lpLastWriteTime = makeFileTime(st.st_mtim.tv_sec, st.st_mtim.tv_nsec); +#else + *lpLastWriteTime = makeFileTime(st.st_mtime, 0); +#endif + } + wibo::lastError = ERROR_SUCCESS; + return 1; +} - int WIN_FUNC FileTimeToSystemTime(const FILETIME *lpFileTime, SYSTEMTIME *lpSystemTime) { - DEBUG_LOG("FileTimeToSystemTime(%p, %p)\n", lpFileTime, lpSystemTime); - if (!lpFileTime || !lpSystemTime) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - uint64_t ticks = fileTimeToDuration(*lpFileTime); - if (ticks >= MAX_VALID_FILETIME) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - uint64_t daysSince1601 = ticks / TICKS_PER_DAY; - uint64_t ticksOfDay = ticks % TICKS_PER_DAY; - uint32_t secondsOfDay = static_cast(ticksOfDay / HUNDRED_NS_PER_SECOND); - uint32_t hundredNs = static_cast(ticksOfDay % HUNDRED_NS_PER_SECOND); - int64_t daysSince1970 = static_cast(daysSince1601) - DAYS_TO_UNIX_EPOCH; - CivilDate date = civilFromDays(daysSince1970); - lpSystemTime->wYear = static_cast(date.year); - lpSystemTime->wMonth = static_cast(date.month); - lpSystemTime->wDay = static_cast(date.day); - lpSystemTime->wDayOfWeek = static_cast((daysSince1601 + 1ULL) % 7ULL); - lpSystemTime->wHour = static_cast(secondsOfDay / 3600U); - lpSystemTime->wMinute = static_cast((secondsOfDay % 3600U) / 60U); - lpSystemTime->wSecond = static_cast(secondsOfDay % 60U); - lpSystemTime->wMilliseconds = static_cast(hundredNs / HUNDRED_NS_PER_MILLISECOND); +static struct timespec statAccessTimespec(const struct stat &st) { +#if defined(__APPLE__) + return st.st_atimespec; +#elif defined(__linux__) + return st.st_atim; +#else + struct timespec ts{}; + ts.tv_sec = st.st_atime; + ts.tv_nsec = 0; + return ts; +#endif +} + +static struct timespec statModifyTimespec(const struct stat &st) { +#if defined(__APPLE__) + return st.st_mtimespec; +#elif defined(__linux__) + return st.st_mtim; +#else + struct timespec ts{}; + ts.tv_sec = st.st_mtime; + ts.tv_nsec = 0; + return ts; +#endif +} + +int WIN_FUNC SetFileTime(void *hFile, const FILETIME *lpCreationTime, const FILETIME *lpLastAccessTime, + const FILETIME *lpLastWriteTime) { + DEBUG_LOG("SetFileTime(%p, %p, %p, %p)\n", hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime); + FILE *fp = files::fpFromHandle(hFile); + if (!fp) { + wibo::lastError = ERROR_INVALID_HANDLE; + return 0; + } + int fd = fileno(fp); + if (fd < 0) { + setLastErrorFromErrno(); + return 0; + } + bool changeAccess = !shouldIgnoreFileTimeParam(lpLastAccessTime); + bool changeWrite = !shouldIgnoreFileTimeParam(lpLastWriteTime); + if (!changeAccess && !changeWrite) { wibo::lastError = ERROR_SUCCESS; return 1; } - - int WIN_FUNC SetFileTime(void *hFile, const FILETIME *lpCreationTime, const FILETIME *lpLastAccessTime, const FILETIME *lpLastWriteTime) { - DEBUG_LOG("SetFileTime(%p, %p, %p, %p)\n", hFile, lpCreationTime, lpLastAccessTime, lpLastWriteTime); - FILE *fp = files::fpFromHandle(hFile); - if (!fp) { - wibo::lastError = ERROR_INVALID_HANDLE; + struct stat st{}; + if (fstat(fd, &st) != 0) { + setLastErrorFromErrno(); + return 0; + } + struct timespec accessSpec = statAccessTimespec(st); + struct timespec writeSpec = statModifyTimespec(st); + if (changeAccess) { + int64_t seconds = 0; + uint32_t hundreds = 0; + if (!fileTimeToUnixParts(*lpLastAccessTime, seconds, hundreds) || + !unixPartsToTimespec(seconds, hundreds, accessSpec)) { + wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } - int fd = fileno(fp); - if (fd < 0) { - setLastErrorFromErrno(); + } + if (changeWrite) { + int64_t seconds = 0; + uint32_t hundreds = 0; + if (!fileTimeToUnixParts(*lpLastWriteTime, seconds, hundreds) || + !unixPartsToTimespec(seconds, hundreds, writeSpec)) { + wibo::lastError = ERROR_INVALID_PARAMETER; return 0; } - bool changeAccess = !shouldIgnoreFileTimeParam(lpLastAccessTime); - bool changeWrite = !shouldIgnoreFileTimeParam(lpLastWriteTime); - if (!changeAccess && !changeWrite) { - wibo::lastError = ERROR_SUCCESS; - return 1; - } - struct stat st {}; - if (fstat(fd, &st) != 0) { - setLastErrorFromErrno(); - return 0; - } - struct timespec accessSpec = statAccessTimespec(st); - struct timespec writeSpec = statModifyTimespec(st); - if (changeAccess) { - int64_t seconds = 0; - uint32_t hundreds = 0; - if (!fileTimeToUnixParts(*lpLastAccessTime, seconds, hundreds) || !unixPartsToTimespec(seconds, hundreds, accessSpec)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - } - if (changeWrite) { - int64_t seconds = 0; - uint32_t hundreds = 0; - if (!fileTimeToUnixParts(*lpLastWriteTime, seconds, hundreds) || !unixPartsToTimespec(seconds, hundreds, writeSpec)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - } + } #if defined(__APPLE__) || defined(__FreeBSD__) - struct timeval tv[2]; - tv[0].tv_sec = accessSpec.tv_sec; - tv[0].tv_usec = accessSpec.tv_nsec / 1000L; - tv[1].tv_sec = writeSpec.tv_sec; - tv[1].tv_usec = writeSpec.tv_nsec / 1000L; - if (futimes(fd, tv) != 0) { - setLastErrorFromErrno(); - return 0; - } -#else - struct timespec times[2] = {accessSpec, writeSpec}; - if (futimens(fd, times) != 0) { - setLastErrorFromErrno(); - return 0; - } -#endif - if (!shouldIgnoreFileTimeParam(lpCreationTime) && lpCreationTime) { - DEBUG_LOG("SetFileTime: creation time not supported\n"); - } - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - int WIN_FUNC FileTimeToLocalFileTime(const FILETIME *lpFileTime, FILETIME *lpLocalFileTime) { - DEBUG_LOG("FileTimeToLocalFileTime(%p, %p)\n", lpFileTime, lpLocalFileTime); - if (!lpFileTime || !lpLocalFileTime) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - int64_t seconds = 0; - uint32_t hundreds = 0; - if (!fileTimeToUnixParts(*lpFileTime, seconds, hundreds)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - if (seconds > static_cast(std::numeric_limits::max()) || - seconds < static_cast(std::numeric_limits::min())) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - time_t unixTime = static_cast(seconds); - struct tm localTm {}; -#if defined(_POSIX_VERSION) - if (!localtime_r(&unixTime, &localTm)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } -#else - struct tm *tmp = localtime(&unixTime); - if (!tmp) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - localTm = *tmp; -#endif - int64_t localAsUtcSeconds = 0; - if (!tmToUnixSeconds(localTm, localAsUtcSeconds)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - int64_t offsetSeconds = localAsUtcSeconds - seconds; - int64_t localSeconds = seconds + offsetSeconds; - FILETIME result; - if (!unixPartsToFileTime(localSeconds, hundreds, result)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - *lpLocalFileTime = result; - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - int WIN_FUNC LocalFileTimeToFileTime(const FILETIME *lpLocalFileTime, FILETIME *lpFileTime) { - DEBUG_LOG("LocalFileTimeToFileTime(%p, %p)\n", lpLocalFileTime, lpFileTime); - if (!lpLocalFileTime || !lpFileTime) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - uint64_t ticks = fileTimeToDuration(*lpLocalFileTime); - if (ticks >= MAX_VALID_FILETIME) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - uint32_t hundredNs = static_cast(ticks % HUNDRED_NS_PER_SECOND); - uint64_t daysSince1601 = ticks / TICKS_PER_DAY; - uint64_t ticksOfDay = ticks % TICKS_PER_DAY; - uint32_t secondsOfDay = static_cast(ticksOfDay / HUNDRED_NS_PER_SECOND); - int64_t daysSince1970 = static_cast(daysSince1601) - DAYS_TO_UNIX_EPOCH; - CivilDate date = civilFromDays(daysSince1970); - struct tm localTm {}; - localTm.tm_year = date.year - 1900; - localTm.tm_mon = static_cast(date.month) - 1; - localTm.tm_mday = static_cast(date.day); - localTm.tm_hour = static_cast(secondsOfDay / 3600U); - localTm.tm_min = static_cast((secondsOfDay % 3600U) / 60U); - localTm.tm_sec = static_cast(secondsOfDay % 60U); - localTm.tm_isdst = -1; - struct tm tmCopy = localTm; - errno = 0; - time_t utcTime = mktime(&tmCopy); - if (utcTime == static_cast(-1) && errno != 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - FILETIME result; - if (!unixPartsToFileTime(static_cast(utcTime), hundredNs, result)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - *lpFileTime = result; - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - int WIN_FUNC DosDateTimeToFileTime(WORD wFatDate, WORD wFatTime, FILETIME *lpFileTime) { - DEBUG_LOG("DosDateTimeToFileTime(%04x, %04x, %p)\n", wFatDate, wFatTime, lpFileTime); - if (!lpFileTime) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - unsigned day = wFatDate & 0x1F; - unsigned month = (wFatDate >> 5) & 0x0F; - unsigned year = ((wFatDate >> 9) & 0x7F) + 1980; - unsigned second = (wFatTime & 0x1F) * 2; - unsigned minute = (wFatTime >> 5) & 0x3F; - unsigned hour = (wFatTime >> 11) & 0x1F; - if (day == 0 || month == 0 || month > 12 || day > 31 || hour > 23 || minute > 59 || second > 59) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - struct tm tmValue {}; - tmValue.tm_year = static_cast(year) - 1900; - tmValue.tm_mon = static_cast(month) - 1; - tmValue.tm_mday = static_cast(day); - tmValue.tm_hour = static_cast(hour); - tmValue.tm_min = static_cast(minute); - tmValue.tm_sec = static_cast(second); - tmValue.tm_isdst = -1; - time_t localSeconds = mktime(&tmValue); - if (localSeconds == (time_t)-1) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - uint64_t ticks = (static_cast(localSeconds) + 11644473600ULL) * 10000000ULL; - lpFileTime->dwLowDateTime = static_cast(ticks & 0xFFFFFFFFULL); - lpFileTime->dwHighDateTime = static_cast(ticks >> 32); - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - int WIN_FUNC FileTimeToDosDateTime(const FILETIME *lpFileTime, WORD *lpFatDate, WORD *lpFatTime) { - DEBUG_LOG("FileTimeToDosDateTime(%p, %p, %p)\n", lpFileTime, lpFatDate, lpFatTime); - if (!lpFileTime || !lpFatDate || !lpFatTime) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - uint64_t ticks = fileTimeToDuration(*lpFileTime); - if (ticks < UNIX_TIME_ZERO) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - time_t utcSeconds = static_cast((ticks / 10000000ULL) - 11644473600ULL); - struct tm tmValue {}; - if (!localtime_r(&utcSeconds, &tmValue)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - int year = tmValue.tm_year + 1900; - if (year < 1980 || year > 2107) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - *lpFatDate = static_cast(((year - 1980) << 9) | ((tmValue.tm_mon + 1) << 5) | tmValue.tm_mday); - *lpFatTime = static_cast(((tmValue.tm_hour & 0x1F) << 11) | ((tmValue.tm_min & 0x3F) << 5) | ((tmValue.tm_sec / 2) & 0x1F)); - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - struct BY_HANDLE_FILE_INFORMATION { - unsigned long dwFileAttributes; - FILETIME ftCreationTime; - FILETIME ftLastAccessTime; - FILETIME ftLastWriteTime; - unsigned long dwVolumeSerialNumber; - unsigned long nFileSizeHigh; - unsigned long nFileSizeLow; - unsigned long nNumberOfLinks; - unsigned long nFileIndexHigh; - unsigned long nFileIndexLow; - }; - - int WIN_FUNC GetFileInformationByHandle(void *hFile, BY_HANDLE_FILE_INFORMATION *lpFileInformation) { - DEBUG_LOG("GetFileInformationByHandle(%p, %p)\n", hFile, lpFileInformation); - FILE* fp = files::fpFromHandle(hFile); - if (fp == nullptr) { - wibo::lastError = 6; // ERROR_INVALID_HANDLE - return 0; - } - struct stat64 st{}; - if (fstat64(fileno(fp), &st)) { - setLastErrorFromErrno(); - return 0; - } - - if (lpFileInformation != nullptr) { - lpFileInformation->dwFileAttributes = 0; - if (S_ISDIR(st.st_mode)) { - lpFileInformation->dwFileAttributes |= 0x10; - } - if (S_ISREG(st.st_mode)) { - lpFileInformation->dwFileAttributes |= 0x80; - } - lpFileInformation->ftCreationTime = defaultFiletime; - lpFileInformation->ftLastAccessTime = defaultFiletime; - lpFileInformation->ftLastWriteTime = defaultFiletime; - lpFileInformation->dwVolumeSerialNumber = 0; - lpFileInformation->nFileSizeHigh = (unsigned long) (st.st_size >> 32); - lpFileInformation->nFileSizeLow = (unsigned long) st.st_size; - lpFileInformation->nNumberOfLinks = 0; - lpFileInformation->nFileIndexHigh = 0; - lpFileInformation->nFileIndexLow = 0; - } - return 1; - } - - struct TIME_ZONE_INFORMATION { - int Bias; - short StandardName[32]; - SYSTEMTIME StandardDate; - int StandardBias; - short DaylightName[32]; - SYSTEMTIME DaylightDate; - int DaylightBias; - }; - - int WIN_FUNC GetTimeZoneInformation(TIME_ZONE_INFORMATION *lpTimeZoneInformation) { - DEBUG_LOG("GetTimeZoneInformation(%p)\n", lpTimeZoneInformation); - if (!lpTimeZoneInformation) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - memset(lpTimeZoneInformation, 0, sizeof(*lpTimeZoneInformation)); - tzset(); - auto copyName = [](short *dest, const char *src) { - if (!src) { - dest[0] = 0; - return; - } - for (size_t i = 0; i < 31 && src[i]; ++i) { - dest[i] = static_cast(src[i]); - dest[i + 1] = 0; - } - }; - time_t now = time(nullptr); - struct tm localTm{}; -#if defined(_GNU_SOURCE) || defined(__APPLE__) - localtime_r(&now, &localTm); -#else - struct tm *tmp = localtime(&now); - if (tmp) { - localTm = *tmp; - } -#endif - long offsetSeconds = 0; -#if defined(__APPLE__) || defined(__linux__) - offsetSeconds = -localTm.tm_gmtoff; -#else - extern long timezone; - offsetSeconds = timezone; - if (localTm.tm_isdst > 0) { - extern int daylight; - if (daylight) { - offsetSeconds -= 3600; - } - } -#endif - lpTimeZoneInformation->Bias = static_cast(offsetSeconds / 60); - copyName(lpTimeZoneInformation->StandardName, tzname[0]); - const char *daylightName = (daylight && tzname[1]) ? tzname[1] : tzname[0]; - copyName(lpTimeZoneInformation->DaylightName, daylightName); - int result = TIME_ZONE_ID_UNKNOWN; - if (daylight && localTm.tm_isdst > 0) { - lpTimeZoneInformation->DaylightBias = -60; - result = TIME_ZONE_ID_DAYLIGHT; - } else { - result = TIME_ZONE_ID_STANDARD; - } - wibo::lastError = ERROR_SUCCESS; - return result; - } - - /* - * Console Nonsense - */ - BOOL WIN_FUNC GetConsoleMode(HANDLE hConsoleHandle, LPDWORD lpMode) { - DEBUG_LOG("STUB: GetConsoleMode(%p)\n", hConsoleHandle); - *lpMode = 0; - return TRUE; - } - - BOOL WIN_FUNC SetConsoleMode(HANDLE hConsoleHandle, DWORD dwMode) { - DEBUG_LOG("STUB: SetConsoleMode(%p, 0x%x)\n", hConsoleHandle, dwMode); - (void)hConsoleHandle; - (void)dwMode; - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - UINT WIN_FUNC GetConsoleOutputCP() { - DEBUG_LOG("STUB: GetConsoleOutputCP() -> 65001\n"); - return 65001; // UTF-8 - } - - BOOL WIN_FUNC SetConsoleCtrlHandler(void *HandlerRoutine, BOOL Add) { - DEBUG_LOG("STUB: SetConsoleCtrlHandler(%p, %u)\n", HandlerRoutine, Add); - // This is a function that gets called when doing ^C - // We might want to call this later (being mindful that it'll be stdcall I think) - - // For now, just pretend we did the thing - return TRUE; - } - - struct CONSOLE_SCREEN_BUFFER_INFO { - int16_t dwSize_x; - int16_t dwSize_y; - int16_t dwCursorPosition_x; - int16_t dwCursorPosition_y; - uint16_t wAttributes; - int16_t srWindow_left; - int16_t srWindow_top; - int16_t srWindow_right; - int16_t srWindow_bottom; - int16_t dwMaximumWindowSize_x; - int16_t dwMaximumWindowSize_y; - }; - - BOOL WIN_FUNC GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFO *lpConsoleScreenBufferInfo) { - DEBUG_LOG("STUB: GetConsoleScreenBufferInfo(%p, %p)\n", hConsoleOutput, lpConsoleScreenBufferInfo); - // Tell a lie - // mwcc doesn't care about anything else - lpConsoleScreenBufferInfo->dwSize_x = 80; - lpConsoleScreenBufferInfo->dwSize_y = 25; - - return TRUE; - } - - BOOL WIN_FUNC WriteConsoleW(HANDLE hConsoleOutput, LPCWSTR lpBuffer, DWORD nNumberOfCharsToWrite, LPDWORD lpNumberOfCharsWritten, - LPVOID lpReserved) { - DEBUG_LOG("WriteConsoleW(%p, %p, %u, %p, %p)\n", hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten, - lpReserved); - const auto str = wideStringToString(lpBuffer, nNumberOfCharsToWrite); - FILE *fp = files::fpFromHandle(hConsoleOutput); - if (fp == stdout || fp == stderr) { - fprintf(fp, "%s", str.c_str()); - if (lpNumberOfCharsWritten) { - *lpNumberOfCharsWritten = nNumberOfCharsToWrite; - } - return TRUE; - } - if (lpNumberOfCharsWritten) { - *lpNumberOfCharsWritten = 0; - } - return FALSE; - } - - int WIN_FUNC PeekConsoleInputA(void *hConsoleInput, void *lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsRead) { - DEBUG_LOG("STUB: PeekConsoleInputA(%p, %p, %u)\n", hConsoleInput, lpBuffer, nLength); - (void)hConsoleInput; - (void)lpBuffer; - (void)nLength; - if (lpNumberOfEventsRead) { - *lpNumberOfEventsRead = 0; - } - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - int WIN_FUNC ReadConsoleInputA(void *hConsoleInput, void *lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsRead) { - DEBUG_LOG("STUB: ReadConsoleInputA(%p, %p, %u)\n", hConsoleInput, lpBuffer, nLength); - (void)hConsoleInput; - (void)lpBuffer; - (void)nLength; - if (lpNumberOfEventsRead) { - *lpNumberOfEventsRead = 0; - } - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - unsigned int WIN_FUNC GetSystemDirectoryA(char *lpBuffer, unsigned int uSize) { - DEBUG_LOG("GetSystemDirectoryA(%p, %u)\n", lpBuffer, uSize); - if (lpBuffer == nullptr) { - return 0; - } - - const char* systemDir = "C:\\Windows\\System32"; - const auto len = strlen(systemDir); - - // If the buffer is too small, return the required buffer size. - // (Add 1 to include the NUL terminator) - if (uSize < len + 1) { - return len + 1; - } - - strcpy(lpBuffer, systemDir); - return len; - } - - unsigned int WIN_FUNC GetWindowsDirectoryA(char *lpBuffer, unsigned int uSize) { - DEBUG_LOG("GetWindowsDirectoryA(%p, %u)\n", lpBuffer, uSize); - if (lpBuffer == nullptr) { - return 0; - } - - const char* systemDir = "C:\\Windows"; - const auto len = strlen(systemDir); - - // If the buffer is too small, return the required buffer size. - // (Add 1 to include the NUL terminator) - if (uSize < len + 1) { - return len + 1; - } - - strcpy(lpBuffer, systemDir); - return len; - } - - static bool tryGetCurrentDirectoryPath(std::string &outPath) { - std::error_code ec; - std::filesystem::path cwd = std::filesystem::current_path(ec); - if (ec) { - errno = ec.value(); - setLastErrorFromErrno(); - return false; - } - outPath = files::pathToWindows(cwd); - return true; - } - - DWORD WIN_FUNC GetCurrentDirectoryA(DWORD uSize, char *lpBuffer) { - DEBUG_LOG("GetCurrentDirectoryA(%u, %p)\n", uSize, lpBuffer); - - std::string path; - if (!tryGetCurrentDirectoryPath(path)) { - return 0; - } - - DEBUG_LOG(" -> %s\n", path.c_str()); - - const size_t pathLen = path.size(); - const auto required = static_cast(pathLen + 1); - - if (uSize == 0) { - wibo::lastError = ERROR_SUCCESS; - return required; - } - - if (lpBuffer == nullptr) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - if (uSize < required) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return required; - } - - memcpy(lpBuffer, path.c_str(), pathLen); - lpBuffer[pathLen] = '\0'; - - wibo::lastError = ERROR_SUCCESS; - return static_cast(pathLen); - } - - DWORD WIN_FUNC GetCurrentDirectoryW(DWORD uSize, uint16_t *lpBuffer) { - DEBUG_LOG("GetCurrentDirectoryW(%u, %p)\n", uSize, lpBuffer); - - std::string path; - if (!tryGetCurrentDirectoryPath(path)) { - return 0; - } - - DEBUG_LOG(" -> %s\n", path.c_str()); - - auto widePath = stringToWideString(path.c_str()); - const auto required = static_cast(widePath.size()); - - if (uSize == 0) { - wibo::lastError = ERROR_SUCCESS; - return required; - } - - if (lpBuffer == nullptr) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - if (uSize < required) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return required; - } - - std::copy(widePath.begin(), widePath.end(), lpBuffer); - - wibo::lastError = ERROR_SUCCESS; - return required - 1; - } - - static bool computeLongWindowsPath(const std::string &inputPath, std::string &longPath) { - bool hasTrailingSlash = false; - if (!inputPath.empty()) { - char last = inputPath.back(); - hasTrailingSlash = (last == '\\' || last == '/'); - } - - auto hostPath = files::pathFromWindows(inputPath.c_str()); - if (hostPath.empty()) { - wibo::lastError = ERROR_PATH_NOT_FOUND; - return false; - } - - std::error_code ec; - if (!std::filesystem::exists(hostPath, ec)) { - wibo::lastError = ERROR_FILE_NOT_FOUND; - return false; - } - - longPath = files::pathToWindows(hostPath); - if (hasTrailingSlash && !longPath.empty() && longPath.back() != '\\') { - longPath.push_back('\\'); - } - wibo::lastError = ERROR_SUCCESS; - return true; - } - - DWORD WIN_FUNC GetLongPathNameA(const char *lpszShortPath, char *lpszLongPath, DWORD cchBuffer) { - DEBUG_LOG("GetLongPathNameA(%s, %p, %u)\n", lpszShortPath ? lpszShortPath : "(null)", lpszLongPath, cchBuffer); - if (!lpszShortPath) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - std::string input(lpszShortPath); - std::string longPath; - if (!computeLongWindowsPath(input, longPath)) { - return 0; - } - - DWORD required = static_cast(longPath.size() + 1); - if (cchBuffer == 0) { - return required; - } - - if (!lpszLongPath) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - if (cchBuffer < required) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return required; - } - - std::memcpy(lpszLongPath, longPath.c_str(), required); - wibo::lastError = ERROR_SUCCESS; - return required - 1; - } - - DWORD WIN_FUNC GetLongPathNameW(const uint16_t *lpszShortPath, uint16_t *lpszLongPath, DWORD cchBuffer) { - DEBUG_LOG("GetLongPathNameW(%p, %p, %u)\n", lpszShortPath, lpszLongPath, cchBuffer); - if (!lpszShortPath) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - std::string input = wideStringToString(lpszShortPath); - std::string longPath; - if (!computeLongWindowsPath(input, longPath)) { - return 0; - } - - auto wideLong = stringToWideString(longPath.c_str()); - DWORD required = static_cast(wideLong.size()); - if (cchBuffer == 0) { - return required; - } - - if (!lpszLongPath) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - if (cchBuffer < required) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return required; - } - - std::copy(wideLong.begin(), wideLong.end(), lpszLongPath); - wibo::lastError = ERROR_SUCCESS; - return required - 1; - } - - int WIN_FUNC SetCurrentDirectoryA(const char *lpPathName) { - DEBUG_LOG("SetCurrentDirectoryA(%s)\n", lpPathName ? lpPathName : "(null)"); - if (!lpPathName) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - auto hostPath = files::pathFromWindows(lpPathName); - std::error_code ec; - std::filesystem::current_path(hostPath, ec); - if (ec) { - errno = ec.value(); - setLastErrorFromErrno(); - return 0; - } - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - int WIN_FUNC SetCurrentDirectoryW(const uint16_t *lpPathName) { - DEBUG_LOG("SetCurrentDirectoryW\n"); - if (!lpPathName) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - std::string path = wideStringToString(lpPathName); - return SetCurrentDirectoryA(path.c_str()); - } - - HMODULE WIN_FUNC GetModuleHandleA(LPCSTR lpModuleName) { - DEBUG_LOG("GetModuleHandleA(%s)\n", lpModuleName); - const auto* module = wibo::findLoadedModule(lpModuleName); - if (!module) { - wibo::lastError = ERROR_MOD_NOT_FOUND; - return nullptr; - } - wibo::lastError = ERROR_SUCCESS; - return module->handle; - } - - HMODULE WIN_FUNC GetModuleHandleW(LPCWSTR lpModuleName) { - DEBUG_LOG("GetModuleHandleW -> "); - if (lpModuleName) { - const auto lpModuleNameA = wideStringToString(lpModuleName); - return GetModuleHandleA(lpModuleNameA.c_str()); - } else { - return GetModuleHandleA(nullptr); - } - } - - DWORD WIN_FUNC GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize) { - DEBUG_LOG("GetModuleFileNameA(%p, %p, %i)\n", hModule, lpFilename, nSize); - if (lpFilename == nullptr) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - auto *info = wibo::moduleInfoFromHandle(hModule); - if (!info) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - std::string path; - if (!info->resolvedPath.empty()) { - path = files::pathToWindows(info->resolvedPath); - } else { - path = info->originalName; - } - DEBUG_LOG("-> %s\n", path.c_str()); - - const size_t len = path.size(); - if (nSize == 0) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return 0; - } - - const size_t copyLen = std::min(len, nSize - 1); - memcpy(lpFilename, path.c_str(), copyLen); - if (copyLen < nSize) { - lpFilename[copyLen] = 0; - } - if (copyLen < len) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return nSize; - } - - wibo::lastError = ERROR_SUCCESS; - return copyLen; - } - - DWORD WIN_FUNC GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize) { - DEBUG_LOG("GetModuleFileNameW(%p, %s, %i)\n", hModule, wideStringToString(lpFilename).c_str(), nSize); - if (lpFilename == nullptr) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - auto *info = wibo::moduleInfoFromHandle(hModule); - if (!info) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - std::string path; - if (!info->resolvedPath.empty()) { - path = files::pathToWindows(info->resolvedPath); - } else { - path = info->originalName; - } - if (nSize == 0) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return 0; - } - - auto wide = stringToWideString(path.c_str()); - if (wide.empty()) { - wide.push_back(0); - } - const size_t len = wide.size() - 1; - const size_t copyLen = std::min(len, static_cast(nSize - 1)); - for (size_t i = 0; i < copyLen; i++) { - lpFilename[i] = wide[i]; - } - if (copyLen < static_cast(nSize)) { - lpFilename[copyLen] = 0; - } - if (copyLen < len) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return nSize; - } - - wibo::lastError = ERROR_SUCCESS; - return copyLen; - } - - static void *findResourceInternal(HMODULE hModule, const wibo::ResourceIdentifier &type, - const wibo::ResourceIdentifier &name, std::optional language) { - auto *exe = wibo::executableFromModule(hModule); - if (!exe) { - wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; - return nullptr; - } - wibo::ResourceLocation loc; - if (!exe->findResource(type, name, language, loc)) { - return nullptr; - } - return const_cast(loc.dataEntry); - } - - void *WIN_FUNC FindResourceA(HMODULE 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 findResourceInternal(hModule, type, name, std::nullopt); - } - - void *WIN_FUNC FindResourceExA(HMODULE 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 findResourceInternal(hModule, type, name, wLanguage); - } - - void *WIN_FUNC FindResourceW(HMODULE 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 findResourceInternal(hModule, type, name, std::nullopt); - } - - void *WIN_FUNC FindResourceExW(HMODULE 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 findResourceInternal(hModule, type, name, wLanguage); - } - - void *WIN_FUNC LoadResource(HMODULE hModule, void *res) { - DEBUG_LOG("LoadResource %p %p\n", hModule, res); - if (!res) { - wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; - return nullptr; - } - auto *exe = wibo::executableFromModule(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)); - } - - static bool resolveDiskFreeSpaceStat(const char *rootPathName, struct statvfs &outBuf, std::string &resolvedPath) { - std::filesystem::path hostPath; - if (rootPathName && *rootPathName) { - hostPath = files::pathFromWindows(rootPathName); - } else { - std::error_code ec; - hostPath = std::filesystem::current_path(ec); - if (ec) { - wibo::lastError = ERROR_PATH_NOT_FOUND; - return false; - } - } - if (hostPath.empty()) { - wibo::lastError = ERROR_PATH_NOT_FOUND; - return false; - } - - hostPath = hostPath.lexically_normal(); - if (hostPath.empty()) { - hostPath = std::filesystem::path("/"); - } - - std::error_code ec; - if (!hostPath.is_absolute()) { - auto abs = std::filesystem::absolute(hostPath, ec); - if (ec) { - wibo::lastError = ERROR_PATH_NOT_FOUND; - return false; - } - hostPath = abs; - } - - std::filesystem::path queryPath = hostPath; - while (true) { - std::string query = queryPath.empty() ? std::string("/") : queryPath.string(); - if (query.empty()) { - query = "/"; - } - if (statvfs(query.c_str(), &outBuf) == 0) { - resolvedPath = query; - wibo::lastError = ERROR_SUCCESS; - return true; - } - - int savedErrno = errno; - if (savedErrno != ENOENT && savedErrno != ENOTDIR) { - errno = savedErrno; - setLastErrorFromErrno(); - return false; - } - - std::filesystem::path parent = queryPath.parent_path(); - if (parent == queryPath) { - errno = savedErrno; - setLastErrorFromErrno(); - return false; - } - if (parent.empty()) { - parent = std::filesystem::path("/"); - } - queryPath = parent; - } - } - - BOOL WIN_FUNC GetDiskFreeSpaceA(const char *lpRootPathName, unsigned int *lpSectorsPerCluster, - unsigned int *lpBytesPerSector, unsigned int *lpNumberOfFreeClusters, unsigned int *lpTotalNumberOfClusters) { - DEBUG_LOG("GetDiskFreeSpaceA(%s)\n", lpRootPathName ? lpRootPathName : "(null)"); - - struct statvfs buf{}; - std::string resolvedPath; - if (!resolveDiskFreeSpaceStat(lpRootPathName, buf, resolvedPath)) { - return FALSE; - } - - uint64_t blockSize = buf.f_frsize ? buf.f_frsize : buf.f_bsize; - if (blockSize == 0) { - blockSize = 4096; - } - unsigned int bytesPerSector = 512; - if (blockSize % bytesPerSector != 0) { - bytesPerSector = static_cast(std::min(blockSize, std::numeric_limits::max())); - } - unsigned int sectorsPerCluster = static_cast(blockSize / bytesPerSector); - if (sectorsPerCluster == 0) { - sectorsPerCluster = 1; - bytesPerSector = static_cast(std::min(blockSize, std::numeric_limits::max())); - } - - uint64_t totalClusters64 = buf.f_blocks; - uint64_t freeClusters64 = buf.f_bavail; - - if (lpSectorsPerCluster) { - *lpSectorsPerCluster = sectorsPerCluster; - } - if (lpBytesPerSector) { - *lpBytesPerSector = bytesPerSector; - } - if (lpNumberOfFreeClusters) { - uint64_t clamped = std::min(freeClusters64, std::numeric_limits::max()); - *lpNumberOfFreeClusters = static_cast(clamped); - } - if (lpTotalNumberOfClusters) { - uint64_t clamped = std::min(totalClusters64, std::numeric_limits::max()); - *lpTotalNumberOfClusters = static_cast(clamped); - } - - DEBUG_LOG("\t-> host %s, spc %u, bps %u, free clusters %u, total clusters %u\n", - resolvedPath.c_str(), lpSectorsPerCluster ? *lpSectorsPerCluster : 0, - lpBytesPerSector ? *lpBytesPerSector : 0, - lpNumberOfFreeClusters ? *lpNumberOfFreeClusters : 0, - lpTotalNumberOfClusters ? *lpTotalNumberOfClusters : 0); - - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - BOOL WIN_FUNC GetDiskFreeSpaceW(const uint16_t *lpRootPathName, unsigned int *lpSectorsPerCluster, - unsigned int *lpBytesPerSector, unsigned int *lpNumberOfFreeClusters, unsigned int *lpTotalNumberOfClusters) { - std::string rootPath = wideStringToString(lpRootPathName); - return GetDiskFreeSpaceA(lpRootPathName ? rootPath.c_str() : nullptr, - lpSectorsPerCluster, lpBytesPerSector, - lpNumberOfFreeClusters, lpTotalNumberOfClusters); - } - - BOOL WIN_FUNC GetDiskFreeSpaceExA(const char *lpDirectoryName, - uint64_t *lpFreeBytesAvailableToCaller, uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes) { - DEBUG_LOG("GetDiskFreeSpaceExA(%s)\n", lpDirectoryName ? lpDirectoryName : "(null)"); - - struct statvfs buf{}; - std::string resolvedPath; - if (!resolveDiskFreeSpaceStat(lpDirectoryName, buf, resolvedPath)) { - return FALSE; - } - - uint64_t blockSize = buf.f_frsize ? buf.f_frsize : buf.f_bsize; - if (blockSize == 0) { - blockSize = 4096; - } - - uint64_t freeToCaller = static_cast(buf.f_bavail) * blockSize; - uint64_t totalBytes = static_cast(buf.f_blocks) * blockSize; - uint64_t totalFree = static_cast(buf.f_bfree) * blockSize; - - if (lpFreeBytesAvailableToCaller) { - *lpFreeBytesAvailableToCaller = freeToCaller; - } - if (lpTotalNumberOfBytes) { - *lpTotalNumberOfBytes = totalBytes; - } - if (lpTotalNumberOfFreeBytes) { - *lpTotalNumberOfFreeBytes = totalFree; - } - - DEBUG_LOG("\t-> host %s, free %llu, total %llu, total free %llu\n", - resolvedPath.c_str(), (unsigned long long) freeToCaller, - (unsigned long long) totalBytes, (unsigned long long) totalFree); - - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - BOOL WIN_FUNC GetDiskFreeSpaceExW(const uint16_t *lpDirectoryName, - uint64_t *lpFreeBytesAvailableToCaller, uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes) { - DEBUG_LOG("GetDiskFreeSpaceExW -> "); - std::string directoryName = wideStringToString(lpDirectoryName); - return GetDiskFreeSpaceExA(lpDirectoryName ? directoryName.c_str() : nullptr, - lpFreeBytesAvailableToCaller, - lpTotalNumberOfBytes, - lpTotalNumberOfFreeBytes); - } - - void* WIN_FUNC LockResource(void* res) { - DEBUG_LOG("LockResource(%p)\n", res); - return res; - } - - unsigned int WIN_FUNC SizeofResource(HMODULE hModule, void* res) { - DEBUG_LOG("SizeofResource(%p, %p)\n", hModule, res); - if (!res) { - wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; - return 0; - } - auto *exe = wibo::executableFromModule(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) { - DEBUG_LOG("LoadLibraryA(%s)\n", lpLibFileName); - const auto *info = wibo::loadModule(lpLibFileName); - if (!info) { - // loadModule already sets lastError - return nullptr; - } - wibo::lastError = ERROR_SUCCESS; - return info->handle; - } - - HMODULE WIN_FUNC LoadLibraryW(LPCWSTR lpLibFileName) { - if (!lpLibFileName) { - return nullptr; - } - auto filename = wideStringToString(lpLibFileName); - DEBUG_LOG("LoadLibraryW(%s)\n", filename.c_str()); - return LoadLibraryA(filename.c_str()); - } - - HMODULE WIN_FUNC LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) { - assert(!hFile); - DEBUG_LOG("LoadLibraryExW(%x) -> ", dwFlags); - const auto filename = wideStringToString(lpLibFileName); - return LoadLibraryA(filename.c_str()); - } - - BOOL WIN_FUNC FreeLibrary(HMODULE hLibModule) { - DEBUG_LOG("FreeLibrary(%p)\n", hLibModule); - auto *info = wibo::moduleInfoFromHandle(hLibModule); - if (!info) { - wibo::lastError = ERROR_INVALID_HANDLE; - return FALSE; - } - wibo::freeModule(info); - return TRUE; - } - - const unsigned int MAJOR_VER = 6, MINOR_VER = 2, BUILD_NUMBER = 0; // Windows 8 - - unsigned int WIN_FUNC GetVersion() { - DEBUG_LOG("GetVersion()\n"); - return MAJOR_VER | MINOR_VER << 8 | 5 << 16 | BUILD_NUMBER << 24; - } - - typedef struct { - uint32_t dwOSVersionInfoSize; - uint32_t dwMajorVersion; - uint32_t dwMinorVersion; - uint32_t dwBuildNumber; - uint32_t dwPlatformId; - char szCSDVersion[128]; - /** - * If dwOSVersionInfoSize indicates more members (i.e. we have an OSVERSIONINFOEXA): - * uint16_t wServicePackMajor; - * uint16_t wServicePackMinor; - * uint16_t wSuiteMask; - * uint8_t wProductType; - * uint8_t wReserved; - */ - } OSVERSIONINFOA; - - int WIN_FUNC GetVersionExA(OSVERSIONINFOA* lpVersionInformation) { - DEBUG_LOG("GetVersionExA(%p)\n", lpVersionInformation); - memset(lpVersionInformation, 0, lpVersionInformation->dwOSVersionInfoSize); - lpVersionInformation->dwMajorVersion = MAJOR_VER; - lpVersionInformation->dwMinorVersion = MINOR_VER; - lpVersionInformation->dwBuildNumber = BUILD_NUMBER; - lpVersionInformation->dwPlatformId = 2; - return 1; - } - - void *WIN_FUNC HeapCreate(unsigned int flOptions, unsigned int dwInitialSize, unsigned int dwMaximumSize) { - DEBUG_LOG("HeapCreate(%u, %u, %u)\n", flOptions, dwInitialSize, dwMaximumSize); - if (dwMaximumSize != 0 && dwInitialSize > dwMaximumSize) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - - mi_heap_t *heap = mi_heap_new(); - if (!heap) { - wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; - return nullptr; - } - - auto *record = new (std::nothrow) HeapRecord{}; - if (!record) { - mi_heap_delete(heap); - wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; - return nullptr; - } - - record->heap = heap; - record->createFlags = flOptions; - record->initialSize = dwInitialSize; - record->maximumSize = dwMaximumSize; - record->isProcessHeap = false; - - void *handle = handles::allocDataHandle({handles::TYPE_HEAP, record, 0}); - wibo::lastError = ERROR_SUCCESS; - return handle; - } - - BOOL WIN_FUNC HeapDestroy(void *hHeap) { - DEBUG_LOG("HeapDestroy(%p)\n", hHeap); - HeapRecord *record = activeHeapRecord(hHeap); - if (!record) { - return FALSE; - } - if (record->isProcessHeap) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - record = popHeapRecord(hHeap); - if (!record) { - return FALSE; - } - mi_heap_destroy(record->heap); - delete record; - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - constexpr DWORD MEM_COMMIT = 0x00001000; - constexpr DWORD MEM_RESERVE = 0x00002000; - constexpr DWORD MEM_DECOMMIT = 0x00004000; - constexpr DWORD MEM_RELEASE = 0x00008000; - constexpr DWORD MEM_RESET = 0x00080000; - constexpr DWORD MEM_RESET_UNDO = 0x01000000; - constexpr DWORD MEM_TOP_DOWN = 0x00100000; - constexpr DWORD MEM_WRITE_WATCH = 0x00200000; - constexpr DWORD MEM_PHYSICAL = 0x00400000; - constexpr DWORD MEM_PRIVATE = 0x00020000; - constexpr DWORD MEM_LARGE_PAGES = 0x20000000; - constexpr size_t VIRTUAL_ALLOCATION_GRANULARITY = 64 * 1024; - constexpr DWORD MEM_COALESCE_PLACEHOLDERS = 0x00000001; - constexpr DWORD MEM_PRESERVE_PLACEHOLDER = 0x00000002; - - namespace { - struct VirtualAllocation { - uintptr_t base = 0; - size_t size = 0; - DWORD allocationProtect = 0; - std::vector pageProtect; // 0 indicates reserved/uncommitted - }; - - static std::map g_virtualAllocations; - static std::mutex g_virtualAllocMutex; - - static size_t systemPageSize() { - static size_t cached = []() { - long detected = sysconf(_SC_PAGESIZE); - if (detected <= 0) { - return static_cast(4096); - } - return static_cast(detected); - }(); - return cached; - } - - static uintptr_t alignDown(uintptr_t value, size_t alignment) { - const uintptr_t mask = static_cast(alignment) - 1; - return value & ~mask; - } - - static uintptr_t alignUp(uintptr_t value, size_t alignment) { - const uintptr_t mask = static_cast(alignment) - 1; - if (mask == std::numeric_limits::max()) { - return value; - } - if (value > std::numeric_limits::max() - mask) { - return std::numeric_limits::max(); - } - return (value + mask) & ~mask; - } - - static bool addOverflows(uintptr_t base, size_t amount) { - return base > std::numeric_limits::max() - static_cast(amount); - } - - static uintptr_t regionEnd(const VirtualAllocation ®ion) { return region.base + region.size; } - - static bool rangeOverlapsLocked(uintptr_t base, size_t length) { - if (length == 0) { - return false; - } - if (addOverflows(base, length - 1)) { - return true; - } - uintptr_t end = base + length; - auto next = g_virtualAllocations.lower_bound(base); - if (next != g_virtualAllocations.begin()) { - auto prev = std::prev(next); - if (regionEnd(prev->second) > base) { - return true; - } - } - if (next != g_virtualAllocations.end() && next->second.base < end) { - return true; - } - return false; - } - - static std::map::iterator findRegionIterator(uintptr_t address) { - auto it = g_virtualAllocations.upper_bound(address); - if (it == g_virtualAllocations.begin()) { - return g_virtualAllocations.end(); - } - --it; - if (address >= regionEnd(it->second)) { - return g_virtualAllocations.end(); - } - return it; - } - - static VirtualAllocation *lookupRegion(uintptr_t address) { - auto it = findRegionIterator(address); - if (it == g_virtualAllocations.end()) { - return nullptr; - } - return &it->second; - } - - static bool rangeWithinRegion(const VirtualAllocation ®ion, uintptr_t start, size_t length) { - if (length == 0) { - return start >= region.base && start <= regionEnd(region); - } - if (start < region.base) { - return false; - } - if (addOverflows(start, length)) { - return false; - } - return (start + length) <= regionEnd(region); - } - - static void markCommitted(VirtualAllocation ®ion, uintptr_t start, size_t length, DWORD protect) { - if (length == 0) { - return; - } - const size_t pageSize = systemPageSize(); - const size_t firstPage = (start - region.base) / pageSize; - const size_t pageCount = length / pageSize; - for (size_t i = 0; i < pageCount; ++i) { - region.pageProtect[firstPage + i] = protect; - } - } - - static void markDecommitted(VirtualAllocation ®ion, uintptr_t start, size_t length) { - if (length == 0) { - return; - } - const size_t pageSize = systemPageSize(); - const size_t firstPage = (start - region.base) / pageSize; - const size_t pageCount = length / pageSize; - for (size_t i = 0; i < pageCount; ++i) { - region.pageProtect[firstPage + i] = 0; - } - } - - static void *alignedReserve(size_t length, int prot, int flags) { - const size_t granularity = VIRTUAL_ALLOCATION_GRANULARITY; - const size_t request = length + granularity; - void *raw = mmap(nullptr, request, prot, flags, -1, 0); - if (raw == MAP_FAILED) { - return MAP_FAILED; - } - uintptr_t rawAddr = reinterpret_cast(raw); - uintptr_t aligned = alignUp(rawAddr, granularity); - size_t front = aligned - rawAddr; - size_t back = (rawAddr + request) - (aligned + length); - if (front != 0) { - if (munmap(raw, front) != 0) { - munmap(raw, request); - return MAP_FAILED; - } - } - if (back != 0) { - if (munmap(reinterpret_cast(aligned + length), back) != 0) { - munmap(reinterpret_cast(aligned), length); - return MAP_FAILED; - } - } - return reinterpret_cast(aligned); - } - } // namespace - - static int translateProtect(DWORD flProtect) { - switch (flProtect) { - case 0x01: /* PAGE_NOACCESS */ - return PROT_NONE; - case 0x02: /* PAGE_READONLY */ - return PROT_READ; - case 0x04: /* PAGE_READWRITE */ - return PROT_READ | PROT_WRITE; - case 0x08: /* PAGE_WRITECOPY */ - return PROT_READ | PROT_WRITE; - case 0x10: /* PAGE_EXECUTE */ - return PROT_EXEC; - case 0x20: /* PAGE_EXECUTE_READ */ - return PROT_READ | PROT_EXEC; - case 0x40: /* PAGE_EXECUTE_READWRITE */ - return PROT_READ | PROT_WRITE | PROT_EXEC; - case 0x80: /* PAGE_EXECUTE_WRITECOPY */ - return PROT_READ | PROT_WRITE | PROT_EXEC; - default: - DEBUG_LOG("Unhandled flProtect: %u, defaulting to RW\n", flProtect); - return PROT_READ | PROT_WRITE; - } - } - - void *WIN_FUNC VirtualAlloc(void *lpAddress, unsigned int dwSize, unsigned int flAllocationType, - unsigned int flProtect) { - DEBUG_LOG("VirtualAlloc(%p, %u, %u, %u)\n", lpAddress, dwSize, flAllocationType, flProtect); - - if (dwSize == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - - const DWORD unsupportedFlags = - flAllocationType & (MEM_WRITE_WATCH | MEM_PHYSICAL | MEM_LARGE_PAGES | MEM_RESET_UNDO); - if (unsupportedFlags != 0) { - DEBUG_LOG("VirtualAlloc unsupported flags: 0x%x\n", unsupportedFlags); - wibo::lastError = ERROR_NOT_SUPPORTED; - return nullptr; - } - - bool reserve = (flAllocationType & MEM_RESERVE) != 0; - bool commit = (flAllocationType & MEM_COMMIT) != 0; - const bool reset = (flAllocationType & MEM_RESET) != 0; - - if (!reserve && commit && lpAddress == nullptr) { - reserve = true; - } - - if (reset) { - if (reserve || commit) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - if (!lpAddress) { - wibo::lastError = ERROR_INVALID_ADDRESS; - return nullptr; - } - const size_t pageSize = systemPageSize(); - uintptr_t request = reinterpret_cast(lpAddress); - if (addOverflows(request, static_cast(dwSize))) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - uintptr_t start = alignDown(request, pageSize); - uintptr_t end = alignUp(request + static_cast(dwSize), pageSize); - size_t length = static_cast(end - start); - std::unique_lock lock(g_virtualAllocMutex); - VirtualAllocation *region = lookupRegion(start); - if (!region || !rangeWithinRegion(*region, start, length)) { - wibo::lastError = ERROR_INVALID_ADDRESS; - return nullptr; - } -#ifdef MADV_FREE - int advice = MADV_FREE; -#else - int advice = MADV_DONTNEED; -#endif - if (madvise(reinterpret_cast(start), length, advice) != 0) { - wibo::lastError = wibo::winErrorFromErrno(errno); - return nullptr; - } - wibo::lastError = ERROR_SUCCESS; - return reinterpret_cast(start); - } - - if (!reserve && !commit) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - - const size_t pageSize = systemPageSize(); - std::unique_lock lock(g_virtualAllocMutex); - - if (reserve) { - uintptr_t base = 0; - size_t length = 0; - if (lpAddress) { - uintptr_t request = reinterpret_cast(lpAddress); - base = alignDown(request, VIRTUAL_ALLOCATION_GRANULARITY); - size_t offset = static_cast(request - base); - if (addOverflows(offset, static_cast(dwSize))) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - size_t span = static_cast(dwSize) + offset; - uintptr_t alignedSpan = alignUp(span, pageSize); - if (alignedSpan == std::numeric_limits::max()) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - length = static_cast(alignedSpan); - if (length == 0 || rangeOverlapsLocked(base, length)) { - wibo::lastError = ERROR_INVALID_ADDRESS; - return nullptr; - } - } else { - uintptr_t aligned = alignUp(static_cast(dwSize), pageSize); - if (aligned == std::numeric_limits::max() || aligned == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - length = static_cast(aligned); - } - const int prot = commit ? translateProtect(flProtect) : PROT_NONE; - int flags = MAP_PRIVATE | MAP_ANONYMOUS; - if (!commit) { - flags |= MAP_NORESERVE; - } - void *result = MAP_FAILED; - if (lpAddress) { -#ifdef MAP_FIXED_NOREPLACE - flags |= MAP_FIXED_NOREPLACE; -#else - flags |= MAP_FIXED; -#endif - result = mmap(reinterpret_cast(base), length, prot, flags, -1, 0); - } else { - result = alignedReserve(length, prot, flags); - } - if (result == MAP_FAILED) { - wibo::lastError = wibo::winErrorFromErrno(errno); - return nullptr; - } - if (reinterpret_cast(result) >= 0x80000000) { - munmap(result, length); - wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; - return nullptr; - } - uintptr_t actualBase = reinterpret_cast(result); - VirtualAllocation allocation{}; - allocation.base = actualBase; - allocation.size = length; - allocation.allocationProtect = flProtect; - allocation.pageProtect.assign(length / pageSize, commit ? flProtect : 0); - g_virtualAllocations[actualBase] = std::move(allocation); - wibo::lastError = ERROR_SUCCESS; - return result; - } - - uintptr_t request = reinterpret_cast(lpAddress); - if (addOverflows(request, static_cast(dwSize))) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - uintptr_t start = alignDown(request, pageSize); - uintptr_t end = alignUp(request + static_cast(dwSize), pageSize); - size_t length = static_cast(end - start); - if (length == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return nullptr; - } - VirtualAllocation *region = lookupRegion(start); - if (!region || !rangeWithinRegion(*region, start, length)) { - wibo::lastError = ERROR_INVALID_ADDRESS; - return nullptr; - } - const int prot = translateProtect(flProtect); - const size_t firstPage = (start - region->base) / pageSize; - const size_t pageCount = length / pageSize; - DEBUG_LOG("VirtualAlloc commit within region base=%p size=%zu firstPage=%zu pageCount=%zu vectorSize=%zu\n", - reinterpret_cast(region->base), region->size, firstPage, pageCount, - region->pageProtect.size()); - std::vector> committedRuns; - committedRuns.reserve(pageCount); - size_t localIndex = 0; - while (localIndex < pageCount) { - size_t absoluteIndex = firstPage + localIndex; - if (region->pageProtect[absoluteIndex] != 0) { - ++localIndex; - continue; - } - size_t runBeginLocal = localIndex; - while (localIndex < pageCount && region->pageProtect[firstPage + localIndex] == 0) { - ++localIndex; - } - size_t runPages = localIndex - runBeginLocal; - uintptr_t runBase = region->base + (firstPage + runBeginLocal) * pageSize; - size_t runLength = runPages * pageSize; - void *mapped = mmap(reinterpret_cast(runBase), runLength, prot, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); - if (mapped == MAP_FAILED) { - for (const auto &run : committedRuns) { - mmap(reinterpret_cast(run.first), run.second, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_NORESERVE, -1, 0); - markDecommitted(*region, run.first, run.second); - } - wibo::lastError = wibo::winErrorFromErrno(errno); - return nullptr; - } - committedRuns.emplace_back(runBase, runLength); - markCommitted(*region, runBase, runLength, flProtect); - } - wibo::lastError = ERROR_SUCCESS; - DEBUG_LOG("VirtualAlloc commit success -> %p\n", reinterpret_cast(start)); - return reinterpret_cast(start); - } - - unsigned int WIN_FUNC VirtualFree(void *lpAddress, unsigned int dwSize, int dwFreeType) { - DEBUG_LOG("VirtualFree(%p, %u, %i)\n", lpAddress, dwSize, dwFreeType); - if (!lpAddress) { - wibo::lastError = ERROR_INVALID_ADDRESS; - return 0; - } - - DWORD freeType = static_cast(dwFreeType); - if ((freeType & (MEM_COALESCE_PLACEHOLDERS | MEM_PRESERVE_PLACEHOLDER)) != 0) { - wibo::lastError = ERROR_NOT_SUPPORTED; - return 0; - } - - const bool release = (freeType & MEM_RELEASE) != 0; - const bool decommit = (freeType & MEM_DECOMMIT) != 0; - if (release == decommit) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - const size_t pageSize = systemPageSize(); - std::unique_lock lock(g_virtualAllocMutex); - - if (release) { - uintptr_t base = reinterpret_cast(lpAddress); - auto exact = g_virtualAllocations.find(base); - if (exact == g_virtualAllocations.end()) { - auto containing = findRegionIterator(base); - if (dwSize != 0 && containing != g_virtualAllocations.end()) { - wibo::lastError = ERROR_INVALID_PARAMETER; - } else { - wibo::lastError = ERROR_INVALID_ADDRESS; - } - return 0; - } - if (dwSize != 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - size_t length = exact->second.size; - g_virtualAllocations.erase(exact); - lock.unlock(); - if (munmap(lpAddress, length) != 0) { - wibo::lastError = wibo::winErrorFromErrno(errno); - return 0; - } - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - uintptr_t request = reinterpret_cast(lpAddress); - auto regionIt = findRegionIterator(request); - if (regionIt == g_virtualAllocations.end()) { - wibo::lastError = ERROR_INVALID_ADDRESS; - return 0; - } - VirtualAllocation ®ion = regionIt->second; - uintptr_t start = alignDown(request, pageSize); - uintptr_t end = 0; - if (dwSize == 0) { - if (request != region.base) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - start = region.base; - end = region.base + region.size; - } else { - if (addOverflows(request, static_cast(dwSize))) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - end = alignUp(request + static_cast(dwSize), pageSize); - } - if (end <= start) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - size_t length = static_cast(end - start); - if (!rangeWithinRegion(region, start, length)) { - wibo::lastError = ERROR_INVALID_ADDRESS; - return 0; - } - void *result = mmap(reinterpret_cast(start), length, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_NORESERVE, -1, 0); - if (result == MAP_FAILED) { - wibo::lastError = wibo::winErrorFromErrno(errno); - return 0; - } - markDecommitted(region, start, length); - wibo::lastError = ERROR_SUCCESS; - return 1; - } - - BOOL WIN_FUNC VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) { - DEBUG_LOG("VirtualProtect(%p, %zu, %u)\n", lpAddress, dwSize, flNewProtect); - if (!lpAddress || dwSize == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - - const size_t pageSize = systemPageSize(); - uintptr_t request = reinterpret_cast(lpAddress); - uintptr_t start = alignDown(request, pageSize); - uintptr_t end = alignUp(request + static_cast(dwSize), pageSize); - if (end <= start) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - - std::unique_lock lock(g_virtualAllocMutex); - VirtualAllocation *region = lookupRegion(start); - if (!region || !rangeWithinRegion(*region, start, static_cast(end - start))) { - wibo::lastError = ERROR_INVALID_ADDRESS; - return FALSE; - } - - const size_t firstPage = (start - region->base) / pageSize; - const size_t pageCount = (end - start) / pageSize; - if (pageCount == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - - DWORD previousProtect = region->pageProtect[firstPage]; - if (previousProtect == 0) { - wibo::lastError = ERROR_NOACCESS; - return FALSE; - } - for (size_t i = 0; i < pageCount; ++i) { - if (region->pageProtect[firstPage + i] == 0) { - wibo::lastError = ERROR_NOACCESS; - return FALSE; - } - } - - int prot = translateProtect(flNewProtect); - if (mprotect(reinterpret_cast(start), end - start, prot) != 0) { - wibo::lastError = wibo::winErrorFromErrno(errno); - return FALSE; - } - for (size_t i = 0; i < pageCount; ++i) { - region->pageProtect[firstPage + i] = flNewProtect; - } - lock.unlock(); - - if (lpflOldProtect) { - *lpflOldProtect = previousProtect; - } - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - typedef struct _MEMORY_BASIC_INFORMATION { - void *BaseAddress; - void *AllocationBase; - DWORD AllocationProtect; - size_t RegionSize; - DWORD State; - DWORD Protect; - DWORD Type; - } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION; - - SIZE_T WIN_FUNC VirtualQuery(const void *lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) { - DEBUG_LOG("VirtualQuery(%p, %p, %zu)\n", lpAddress, lpBuffer, dwLength); - if (!lpBuffer || dwLength < sizeof(MEMORY_BASIC_INFORMATION) || !lpAddress) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - std::memset(lpBuffer, 0, sizeof(MEMORY_BASIC_INFORMATION)); - const size_t pageSize = systemPageSize(); - uintptr_t request = reinterpret_cast(lpAddress); - uintptr_t pageBase = alignDown(request, pageSize); - - std::unique_lock lock(g_virtualAllocMutex); - VirtualAllocation *region = lookupRegion(pageBase); - if (!region) { - wibo::lastError = ERROR_INVALID_ADDRESS; - return 0; - } - - const size_t pageIndex = (pageBase - region->base) / pageSize; - if (pageIndex >= region->pageProtect.size()) { - wibo::lastError = ERROR_INVALID_ADDRESS; - return 0; - } - const bool committed = region->pageProtect[pageIndex] != 0; - uintptr_t blockStart = pageBase; - uintptr_t blockEnd = pageBase + pageSize; - while (blockStart > region->base) { - size_t idx = (blockStart - region->base) / pageSize - 1; - const bool pageCommitted = region->pageProtect[idx] != 0; - if (pageCommitted != committed) { - break; - } - blockStart -= pageSize; - } - while (blockEnd < region->base + region->size) { - size_t idx = (blockEnd - region->base) / pageSize; - const bool pageCommitted = region->pageProtect[idx] != 0; - if (pageCommitted != committed) { - break; - } - blockEnd += pageSize; - } - - lpBuffer->BaseAddress = reinterpret_cast(blockStart); - lpBuffer->AllocationBase = reinterpret_cast(region->base); - lpBuffer->AllocationProtect = - region->allocationProtect != 0 ? region->allocationProtect : 0x01; // PAGE_NOACCESS fallback - lpBuffer->RegionSize = blockEnd - blockStart; - lpBuffer->State = committed ? MEM_COMMIT : MEM_RESERVE; - lpBuffer->Protect = committed ? region->pageProtect[pageIndex] : 0; - lpBuffer->Type = MEM_PRIVATE; - lock.unlock(); - wibo::lastError = ERROR_SUCCESS; - return sizeof(MEMORY_BASIC_INFORMATION); - } - - unsigned int WIN_FUNC GetProcessWorkingSetSize(void *hProcess, unsigned int *lpMinimumWorkingSetSize, unsigned int *lpMaximumWorkingSetSize) { - DEBUG_LOG("GetProcessWorkingSetSize(%p, %p, %p)\n", hProcess, lpMinimumWorkingSetSize, lpMaximumWorkingSetSize); - // A pointer to a variable that receives the minimum working set size of the specified process, in bytes. - // The virtual memory manager attempts to keep at least this much memory resident in the process whenever the process is active. - *lpMinimumWorkingSetSize = 32*1024*1024; // 32MB - - // A pointer to a variable that receives the maximum working set size of the specified process, in bytes. - // The virtual memory manager attempts to keep no more than this much memory resident in the process whenever - // the process is active when memory is in short supply. - *lpMaximumWorkingSetSize = 128*1024*1024; // 128MB - - // If the function succeeds, the return value is nonzero. - return 1; - } - - unsigned int WIN_FUNC SetProcessWorkingSetSize(void *hProcess, unsigned int dwMinimumWorkingSetSize, unsigned int dwMaximumWorkingSetSize) { - DEBUG_LOG("SetProcessWorkingSetSize(%p, %u, %u)\n", hProcess, dwMinimumWorkingSetSize, dwMaximumWorkingSetSize); - return 1; - } - - constexpr DWORD STARTF_USESHOWWINDOW = 0x00000001; - constexpr DWORD STARTF_USESTDHANDLES = 0x00000100; - constexpr WORD SW_SHOWNORMAL = 1; - - template - static void populateStartupInfo(StartupInfo *info) { - if (!info) { - return; - } - std::memset(info, 0, sizeof(StartupInfo)); - info->cb = sizeof(StartupInfo); - info->dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; - info->wShowWindow = SW_SHOWNORMAL; - info->cbReserved2 = 0; - info->lpReserved2 = nullptr; - info->hStdInput = files::getStdHandle(STD_INPUT_HANDLE); - info->hStdOutput = files::getStdHandle(STD_OUTPUT_HANDLE); - info->hStdError = files::getStdHandle(STD_ERROR_HANDLE); - } - - void WIN_FUNC GetStartupInfoA(STARTUPINFOA *lpStartupInfo) { - DEBUG_LOG("GetStartupInfoA(%p)\n", lpStartupInfo); - populateStartupInfo(lpStartupInfo); - } - - void WIN_FUNC GetStartupInfoW(STARTUPINFOW *lpStartupInfo) { - DEBUG_LOG("GetStartupInfoW(%p)\n", lpStartupInfo); - populateStartupInfo(lpStartupInfo); - } - - BOOL WIN_FUNC SetThreadStackGuarantee(PULONG StackSizeInBytes) { - DEBUG_LOG("STUB: SetThreadStackGuarantee(%p)\n", StackSizeInBytes); - return TRUE; - } - - HANDLE WIN_FUNC GetCurrentThread() { - ThreadObject *obj = ensureCurrentThreadObject(); - (void)obj; - HANDLE pseudoHandle = reinterpret_cast(PSEUDO_CURRENT_THREAD_HANDLE_VALUE); - DEBUG_LOG("GetCurrentThread() -> %p\n", pseudoHandle); - return pseudoHandle; - } - - DWORD_PTR WIN_FUNC SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask) { - DEBUG_LOG("SetThreadAffinityMask(%p, 0x%lx)\n", hThread, (unsigned long)dwThreadAffinityMask); - if (dwThreadAffinityMask == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - uintptr_t rawThreadHandle = reinterpret_cast(hThread); - bool isPseudoHandle = rawThreadHandle == PSEUDO_CURRENT_THREAD_HANDLE_VALUE || - rawThreadHandle == 0 || - rawThreadHandle == static_cast(-1); - if (!isPseudoHandle) { - auto data = handles::dataFromHandle(hThread, false); - if (data.type != handles::TYPE_THREAD) { - wibo::lastError = ERROR_INVALID_HANDLE; - return 0; - } - } - - DWORD_PTR processMask = 0; - DWORD_PTR systemMask = 0; - if (!GetProcessAffinityMask(GetCurrentProcess(), &processMask, &systemMask)) { - return 0; - } - if ((dwThreadAffinityMask & ~systemMask) != 0 || (dwThreadAffinityMask & processMask) == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - wibo::lastError = ERROR_SUCCESS; - return processMask; - } - - DWORD WIN_FUNC ResumeThread(HANDLE hThread) { - DEBUG_LOG("ResumeThread(%p)\n", hThread); - ThreadObject *obj = threadObjectFromHandle(hThread); - if (!obj) { - wibo::lastError = ERROR_INVALID_HANDLE; - return static_cast(-1); - } - pthread_mutex_lock(&obj->mutex); - DWORD previous = obj->suspendCount; - if (obj->suspendCount > 0) { - obj->suspendCount--; - if (obj->suspendCount == 0) { - pthread_cond_broadcast(&obj->cond); - } - } - pthread_mutex_unlock(&obj->mutex); - wibo::lastError = ERROR_SUCCESS; - return previous; - } - - HRESULT WIN_FUNC SetThreadDescription(HANDLE hThread, const void * /* PCWSTR */ lpThreadDescription) { - DEBUG_LOG("STUB: SetThreadDescription(%p, %p)\n", hThread, lpThreadDescription); - return S_OK; - } - - HANDLE WIN_FUNC CreateThread(void *lpThreadAttributes, size_t dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, - void *lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId) { - DEBUG_LOG("CreateThread(%p, %zu, %p, %p, %u, %p)\n", lpThreadAttributes, dwStackSize, lpStartAddress, - lpParameter, dwCreationFlags, lpThreadId); - (void)lpThreadAttributes; - constexpr DWORD CREATE_SUSPENDED = 0x00000004; - constexpr DWORD STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000; - constexpr DWORD SUPPORTED_FLAGS = CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION; - if ((dwCreationFlags & ~SUPPORTED_FLAGS) != 0) { - DEBUG_LOG("CreateThread: unsupported creation flags 0x%x\n", dwCreationFlags); - wibo::lastError = ERROR_NOT_SUPPORTED; - return nullptr; - } - bool startSuspended = (dwCreationFlags & CREATE_SUSPENDED) != 0; - - ThreadObject *obj = new ThreadObject(); - pthread_mutex_init(&obj->mutex, nullptr); - pthread_cond_init(&obj->cond, nullptr); - obj->finished = false; - obj->joined = false; - obj->detached = false; - obj->exitCode = 0; - obj->refCount = 1; - obj->suspendCount = startSuspended ? 1u : 0u; - - ThreadStartData *startData = new ThreadStartData{lpStartAddress, lpParameter, obj}; - - pthread_attr_t attr; - pthread_attr_t *attrPtr = nullptr; - if (dwStackSize != 0) { - pthread_attr_init(&attr); - size_t stackSize = dwStackSize; -#ifdef PTHREAD_STACK_MIN - if (stackSize < static_cast(PTHREAD_STACK_MIN)) { - stackSize = PTHREAD_STACK_MIN; - } -#endif - if (pthread_attr_setstacksize(&attr, stackSize) == 0) { - attrPtr = &attr; - } else { - pthread_attr_destroy(&attr); - } - } - - int rc = pthread_create(&obj->thread, attrPtr, threadTrampoline, startData); - if (attrPtr) { - pthread_attr_destroy(attrPtr); - } - if (rc != 0) { - delete startData; - destroyThreadObject(obj); - errno = rc; - setLastErrorFromErrno(); - return nullptr; - } - - if (lpThreadId) { - std::size_t hashed = std::hash{}(obj->thread); - *lpThreadId = static_cast(hashed & 0xffffffffu); - } - - wibo::lastError = ERROR_SUCCESS; - return handles::allocDataHandle({handles::TYPE_THREAD, obj, 0}); - } - - void WIN_FUNC ExitThread(DWORD dwExitCode) { - DEBUG_LOG("ExitThread(%u)\n", dwExitCode); - ThreadObject *obj = currentThreadObject; - uint16_t previousSegment = 0; - bool tibInstalled = false; - if (wibo::tibSelector) { - asm volatile("mov %%fs, %0" : "=r"(previousSegment)); - asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory"); - tibInstalled = true; - } - if (obj) { - pthread_mutex_lock(&obj->mutex); - obj->finished = true; - obj->exitCode = dwExitCode; - pthread_cond_broadcast(&obj->cond); - bool shouldDelete = (obj->refCount == 0); - bool detached = obj->detached; - pthread_mutex_unlock(&obj->mutex); - currentThreadObject = nullptr; - if (shouldDelete) { - assert(detached && "ThreadObject must be detached when refCount reaches zero before completion"); - destroyThreadObject(obj); - } - } - if (tibInstalled) { - asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory"); - } - pthread_exit(nullptr); - } - - BOOL WIN_FUNC GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) { - DEBUG_LOG("GetExitCodeThread(%p, %p)\n", hThread, lpExitCode); - if (!lpExitCode) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - ThreadObject *obj = threadObjectFromHandle(hThread); - if (!obj) { - wibo::lastError = ERROR_INVALID_HANDLE; - return FALSE; - } - pthread_mutex_lock(&obj->mutex); - DWORD code = obj->finished ? obj->exitCode : STILL_ACTIVE; - pthread_mutex_unlock(&obj->mutex); - *lpExitCode = code; - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - - BOOL WIN_FUNC SetThreadPriority(HANDLE hThread, int nPriority) { - DEBUG_LOG("STUB: SetThreadPriority(%p, %d)\n", hThread, nPriority); - (void) hThread; - (void) nPriority; - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - int WIN_FUNC GetThreadPriority(HANDLE hThread) { - DEBUG_LOG("STUB: GetThreadPriority(%p)\n", hThread); - (void) hThread; - wibo::lastError = ERROR_SUCCESS; - return 0; // THREAD_PRIORITY_NORMAL - } - - - BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, - FILETIME *lpCreationTime, - FILETIME *lpExitTime, - FILETIME *lpKernelTime, - FILETIME *lpUserTime) { - DEBUG_LOG("GetThreadTimes(%p, %p, %p, %p, %p)\n", - hThread, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime); - - if (!lpKernelTime || !lpUserTime) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - - bool isPseudoCurrentThread = reinterpret_cast(hThread) == PSEUDO_CURRENT_THREAD_HANDLE_VALUE || - hThread == (HANDLE)0 || hThread == (HANDLE)0xFFFFFFFF; - if (!isPseudoCurrentThread) { - DEBUG_LOG("GetThreadTimes: unsupported handle %p\n", hThread); - wibo::lastError = ERROR_INVALID_HANDLE; - return FALSE; - } - - if (lpCreationTime) { - *lpCreationTime = defaultFiletime; - } - if (lpExitTime) { - lpExitTime->dwLowDateTime = 0; - lpExitTime->dwHighDateTime = 0; - } - - struct rusage usage; - if (getrusage(RUSAGE_THREAD, &usage) == 0) { - *lpKernelTime = fileTimeFromTimeval(usage.ru_stime); - *lpUserTime = fileTimeFromTimeval(usage.ru_utime); - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - struct timespec cpuTime; - if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuTime) == 0) { - *lpKernelTime = fileTimeFromDuration(0); - *lpUserTime = fileTimeFromTimespec(cpuTime); - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - + struct timeval tv[2]; + tv[0].tv_sec = accessSpec.tv_sec; + tv[0].tv_usec = accessSpec.tv_nsec / 1000L; + tv[1].tv_sec = writeSpec.tv_sec; + tv[1].tv_usec = writeSpec.tv_nsec / 1000L; + if (futimes(fd, tv) != 0) { setLastErrorFromErrno(); - *lpKernelTime = fileTimeFromDuration(0); - *lpUserTime = fileTimeFromDuration(0); - return FALSE; + return 0; } - - constexpr DWORD FILE_TYPE_UNKNOWN = 0x0000; - constexpr DWORD FILE_TYPE_DISK = 0x0001; - constexpr DWORD FILE_TYPE_CHAR = 0x0002; - constexpr DWORD FILE_TYPE_PIPE = 0x0003; - - DWORD WIN_FUNC GetFileType(HANDLE hFile) { - DEBUG_LOG("GetFileType(%p) ", hFile); - - auto *file = files::fileHandleFromHandle(hFile); - if (!file || file->fd < 0) { - wibo::lastError = ERROR_INVALID_HANDLE; - DEBUG_LOG("-> ERROR_INVALID_HANDLE\n"); - return FILE_TYPE_UNKNOWN; - } - - struct stat st{}; - if (fstat(file->fd, &st) != 0) { - setLastErrorFromErrno(); - DEBUG_LOG("-> fstat error\n"); - return FILE_TYPE_UNKNOWN; - } - - wibo::lastError = ERROR_SUCCESS; - DWORD type = FILE_TYPE_UNKNOWN; - if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode)) { - type = FILE_TYPE_DISK; - } - if (S_ISCHR(st.st_mode)) { - type = FILE_TYPE_CHAR; - } - if (S_ISSOCK(st.st_mode) || S_ISFIFO(st.st_mode)) { - type = FILE_TYPE_PIPE; - } - DEBUG_LOG("-> %u\n", type); - return type; +#else + struct timespec times[2] = {accessSpec, writeSpec}; + if (futimens(fd, times) != 0) { + setLastErrorFromErrno(); + return 0; } - - UINT WIN_FUNC SetHandleCount(UINT uNumber) { - DEBUG_LOG("SetHandleCount(%u)\n", uNumber); - return handles::MAX_HANDLES; +#endif + if (!shouldIgnoreFileTimeParam(lpCreationTime) && lpCreationTime) { + DEBUG_LOG("SetFileTime: creation time not supported\n"); } + wibo::lastError = ERROR_SUCCESS; + return 1; +} - void WIN_FUNC Sleep(DWORD dwMilliseconds) { - DEBUG_LOG("Sleep(%u)\n", dwMilliseconds); - usleep(static_cast(dwMilliseconds) * 1000); +struct BY_HANDLE_FILE_INFORMATION { + unsigned long dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + unsigned long dwVolumeSerialNumber; + unsigned long nFileSizeHigh; + unsigned long nFileSizeLow; + unsigned long nNumberOfLinks; + unsigned long nFileIndexHigh; + unsigned long nFileIndexLow; +}; + +int WIN_FUNC GetFileInformationByHandle(void *hFile, BY_HANDLE_FILE_INFORMATION *lpFileInformation) { + DEBUG_LOG("GetFileInformationByHandle(%p, %p)\n", hFile, lpFileInformation); + FILE *fp = files::fpFromHandle(hFile); + if (fp == nullptr) { + wibo::lastError = 6; // ERROR_INVALID_HANDLE + return 0; } - - unsigned int WIN_FUNC GetACP() { - DEBUG_LOG("GetACP() -> %u\n", 28591); - // return 65001; // UTF-8 - // return 1200; // Unicode (BMP of ISO 10646) - return 28591; // Latin1 (ISO/IEC 8859-1) - } - - typedef struct _cpinfo { - unsigned int MaxCharSize; - unsigned char DefaultChar[2]; - unsigned char LeadByte[12]; - } CPINFO, *LPCPINFO; - - unsigned int WIN_FUNC GetCPInfo(unsigned int codePage, CPINFO* lpCPInfo) { - DEBUG_LOG("GetCPInfo(%u, %p)\n", codePage, lpCPInfo); - lpCPInfo->MaxCharSize = 1; - lpCPInfo->DefaultChar[0] = 0; - return 1; // success - } - - unsigned int WIN_FUNC WideCharToMultiByte(unsigned int codePage, unsigned int dwFlags, uint16_t *lpWideCharStr, int cchWideChar, char *lpMultiByteStr, int cbMultiByte, char *lpDefaultChar, unsigned int *lpUsedDefaultChar) { - DEBUG_LOG("WideCharToMultiByte(%u, %u, %p, %d, %p, %d, %p, %p)\n", codePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar); - - if (cchWideChar == -1) { - cchWideChar = wstrlen(lpWideCharStr) + 1; - } - - if (cbMultiByte == 0) { - return cchWideChar; - } - for (int i = 0; i < cchWideChar; i++) { - lpMultiByteStr[i] = lpWideCharStr[i] & 0xFF; - } - - if (wibo::debugEnabled) { - std::string s(lpMultiByteStr, lpMultiByteStr + cchWideChar); - DEBUG_LOG("Converted string: [%s] (len %d)\n", s.c_str(), cchWideChar); - } - - return cchWideChar; - } - - unsigned int WIN_FUNC MultiByteToWideChar(unsigned int codePage, unsigned int dwFlags, const char *lpMultiByteStr, int cbMultiByte, uint16_t *lpWideCharStr, int cchWideChar) { - DEBUG_LOG("MultiByteToWideChar(%u, %u, %d, %d)\n", codePage, dwFlags, cbMultiByte, cchWideChar); - - if (cbMultiByte == -1) { - cbMultiByte = strlen(lpMultiByteStr) + 1; - } - - // assert (dwFlags == 1); // MB_PRECOMPOSED - if (cchWideChar == 0) { - return cbMultiByte; - } - - if (wibo::debugEnabled) { - std::string s(lpMultiByteStr, lpMultiByteStr + cbMultiByte); - DEBUG_LOG("Converting string: [%s] (len %d)\n", s.c_str(), cbMultiByte); - } - - assert(cbMultiByte <= cchWideChar); - for (int i = 0; i < cbMultiByte; i++) { - lpWideCharStr[i] = lpMultiByteStr[i] & 0xFF; - } - return cbMultiByte; - } - - unsigned int WIN_FUNC GetStringTypeW(unsigned int dwInfoType, const uint16_t *lpSrcStr, int cchSrc, uint16_t *lpCharType) { - DEBUG_LOG("GetStringTypeW(%u, %p, %i, %p)\n", dwInfoType, lpSrcStr, cchSrc, lpCharType); - - assert(dwInfoType == 1); // CT_CTYPE1 - - if (cchSrc < 0) - cchSrc = wstrlen(lpSrcStr); - - for (int i = 0; i < cchSrc; i++) { - wint_t c = lpSrcStr[i]; - bool upper = std::iswupper(c); - bool lower = std::iswlower(c); - bool alpha = std::iswalpha(c); - bool digit = std::iswdigit(c); - bool space = std::iswspace(c); - bool blank = (c == L' ' || c == L'\t'); - bool hex = std::iswxdigit(c); - bool cntrl = std::iswcntrl(c); - bool punct = std::iswpunct(c); - lpCharType[i] = (upper ? 1 : 0) | (lower ? 2 : 0) | (digit ? 4 : 0) | (space ? 8 : 0) | - (punct ? 0x10 : 0) | (cntrl ? 0x20 : 0) | (blank ? 0x40 : 0) | - (hex ? 0x80 : 0) | (alpha ? 0x100 : 0); - } - - return 1; - } - - unsigned int WIN_FUNC GetStringTypeA(unsigned int Locale, unsigned int dwInfoType, const char *lpSrcStr, int cchSrc, uint16_t *lpCharType) { - DEBUG_LOG("GetStringTypeA(%u, %u, %p, %d, %p)\n", Locale, dwInfoType, lpSrcStr, cchSrc, lpCharType); - (void) Locale; - - if (!lpSrcStr || !lpCharType) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - if (dwInfoType != 1) { - wibo::lastError = ERROR_NOT_SUPPORTED; - return 0; - } - - int length = cchSrc; - if (length < 0) { - length = static_cast(strlen(lpSrcStr)); - } - if (length < 0) { - length = 0; - } - - std::vector wide; - wide.reserve(length); - for (int i = 0; i < length; ++i) { - wide.push_back(static_cast(lpSrcStr[i])); - } - - unsigned int result = 1; - if (length > 0) { - result = GetStringTypeW(dwInfoType, wide.data(), length, lpCharType); - } else { - // Nothing to classify but Windows returns success. - result = 1; - } - - wibo::lastError = ERROR_SUCCESS; - return result; - } - - unsigned int WIN_FUNC FreeEnvironmentStringsW(void *penv) { - DEBUG_LOG("FreeEnvironmentStringsW(%p)\n", penv); - free(penv); - return 1; - } - - unsigned int WIN_FUNC IsProcessorFeaturePresent(unsigned int processorFeature) { - DEBUG_LOG("IsProcessorFeaturePresent(%u)\n", processorFeature); - - if (processorFeature == 0) // PF_FLOATING_POINT_PRECISION_ERRATA - return 1; - if (processorFeature == 10) // PF_XMMI64_INSTRUCTIONS_AVAILABLE (SSE2) - return 1; - if (processorFeature == 23) // PF_FASTFAIL_AVAILABLE (__fastfail() supported) - return 1; - - // sure.. we have that feature... - DEBUG_LOG(" IsProcessorFeaturePresent: we don't know about feature %u, lying...\n", processorFeature); - return 1; - } - - FARPROC WIN_FUNC GetProcAddress(HMODULE hModule, LPCSTR lpProcName) { - FARPROC result; - const auto info = wibo::moduleInfoFromHandle(hModule); - if (!info) { - DEBUG_LOG("GetProcAddress(%p) -> ERROR_INVALID_HANDLE\n", hModule); - wibo::lastError = ERROR_INVALID_HANDLE; - return nullptr; - } - const auto proc = reinterpret_cast(lpProcName); - if (proc & ~0xFFFF) { - DEBUG_LOG("GetProcAddress(%s, %s) ", info->normalizedName.c_str(), lpProcName); - result = wibo::resolveFuncByName(info, lpProcName); - } else { - DEBUG_LOG("GetProcAddress(%s, %u) ", info->normalizedName.c_str(), proc); - result = wibo::resolveFuncByOrdinal(info, static_cast(proc)); - } - DEBUG_LOG("-> %p\n", result); - if (!result) { - wibo::lastError = ERROR_PROC_NOT_FOUND; - } else { - wibo::lastError = ERROR_SUCCESS; - } - return result; - } - - void *WIN_FUNC HeapAlloc(void *hHeap, unsigned int dwFlags, size_t dwBytes) { - DEBUG_LOG("HeapAlloc(%p, %x, %zu) ", hHeap, dwFlags, dwBytes); - HeapRecord *record = activeHeapRecord(hHeap); - if (!record) { - DEBUG_LOG("-> NULL\n"); - return nullptr; - } - assert(!((record->createFlags | dwFlags) & HEAP_GENERATE_EXCEPTIONS)); // Unsupported - const bool zeroMemory = (dwFlags & HEAP_ZERO_MEMORY) != 0; - const size_t requestSize = std::max(1, dwBytes); - void *mem = zeroMemory ? mi_heap_zalloc(record->heap, requestSize) : mi_heap_malloc(record->heap, requestSize); - if (!mem) { - wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; - return nullptr; - } - if (isExecutableHeap(record)) { - maybeMarkExecutable(mem); - } - wibo::lastError = ERROR_SUCCESS; - DEBUG_LOG("-> %p\n", mem); - return mem; - } - - void *WIN_FUNC HeapReAlloc(void *hHeap, unsigned int dwFlags, void *lpMem, size_t dwBytes) { - DEBUG_LOG("HeapReAlloc(%p, %x, %p, %zu) ", hHeap, dwFlags, lpMem, dwBytes); - HeapRecord *record = activeHeapRecord(hHeap); - if (!record) { - DEBUG_LOG("-> NULL\n"); - return nullptr; - } - if (lpMem == nullptr) { - void *alloc = HeapAlloc(hHeap, dwFlags, dwBytes); - DEBUG_LOG("-> %p (alloc)\n", alloc); - return alloc; - } - if (!mi_heap_check_owned(record->heap, lpMem)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - DEBUG_LOG("-> NULL (not owned)\n"); - return nullptr; - } - assert(!((record->createFlags | dwFlags) & HEAP_GENERATE_EXCEPTIONS)); // Unsupported - const bool inplaceOnly = (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) != 0; - const bool zeroMemory = (dwFlags & HEAP_ZERO_MEMORY) != 0; - if (dwBytes == 0) { - if (!inplaceOnly) { - mi_free(lpMem); - wibo::lastError = ERROR_SUCCESS; - DEBUG_LOG("-> NULL (freed)\n"); - return nullptr; - } - wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; - DEBUG_LOG("-> NULL (zero size with in-place flag)\n"); - return nullptr; - } - - const size_t requestSize = std::max(1, dwBytes); - const size_t oldSize = mi_usable_size(lpMem); - // Force in-place reallocation if the size is <= old size - // pspsnc.exe relies on this behavior - if (inplaceOnly || requestSize <= oldSize) { - if (requestSize > oldSize) { - wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; - DEBUG_LOG("-> NULL (cannot grow in place)\n"); - return nullptr; - } - wibo::lastError = ERROR_SUCCESS; - DEBUG_LOG("-> %p (in-place)\n", lpMem); - return lpMem; - } - - void *ret = mi_heap_realloc(record->heap, lpMem, requestSize); - if (!ret) { - wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; - return nullptr; - } - if (zeroMemory && requestSize > oldSize) { - size_t newUsable = mi_usable_size(ret); - if (newUsable > oldSize) { - size_t zeroLen = std::min(newUsable, requestSize) - oldSize; - memset(static_cast(ret) + oldSize, 0, zeroLen); - } - } - if (isExecutableHeap(record)) { - maybeMarkExecutable(ret); - } - wibo::lastError = ERROR_SUCCESS; - DEBUG_LOG("-> %p\n", ret); - return ret; - } - - unsigned int WIN_FUNC HeapSize(void *hHeap, unsigned int dwFlags, void *lpMem) { - DEBUG_LOG("HeapSize(%p, %x, %p)\n", hHeap, dwFlags, lpMem); - (void) dwFlags; - HeapRecord *record = activeHeapRecord(hHeap); - if (!record) { - return static_cast(-1); - } - if (lpMem == nullptr) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return static_cast(-1); - } - if (!mi_heap_check_owned(record->heap, lpMem)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return static_cast(-1); - } - size_t size = mi_usable_size(lpMem); - wibo::lastError = ERROR_SUCCESS; - return static_cast(size); - } - - void *WIN_FUNC GetProcessHeap() { - ensureProcessHeapInitialized(); - wibo::lastError = ERROR_SUCCESS; - DEBUG_LOG("GetProcessHeap() -> %p\n", processHeapHandle); - return processHeapHandle; - } - - int WIN_FUNC HeapSetInformation(void *HeapHandle, int HeapInformationClass, void *HeapInformation, - size_t HeapInformationLength) { - DEBUG_LOG("HeapSetInformation(%p, %d, %p, %zu)\n", HeapHandle, HeapInformationClass, HeapInformation, - HeapInformationLength); - ensureProcessHeapInitialized(); - switch (HeapInformationClass) { - case 0: { // HeapCompatibilityInformation - if (!HeapInformation || HeapInformationLength < sizeof(unsigned int)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - HeapRecord *target = HeapHandle ? activeHeapRecord(HeapHandle) : processHeapRecord; - if (!target) { - return 0; - } - target->compatibility = *static_cast(HeapInformation); - wibo::lastError = ERROR_SUCCESS; - return 1; - } - case 1: // HeapEnableTerminationOnCorruption - wibo::lastError = ERROR_SUCCESS; - return 1; - case 3: // HeapOptimizeResources - wibo::lastError = ERROR_CALL_NOT_IMPLEMENTED; - return 0; - default: - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - } - - unsigned int WIN_FUNC HeapFree(void *hHeap, unsigned int dwFlags, void *lpMem) { - DEBUG_LOG("HeapFree(%p, %x, %p)\n", hHeap, dwFlags, lpMem); - (void) dwFlags; - if (lpMem == nullptr) { - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - HeapRecord *record = activeHeapRecord(hHeap); - if (!record) { - return FALSE; - } - if (!mi_heap_check_owned(record->heap, lpMem)) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - mi_free(lpMem); - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - unsigned int WIN_FUNC FormatMessageA(unsigned int dwFlags, void *lpSource, unsigned int dwMessageId, - unsigned int dwLanguageId, char *lpBuffer, unsigned int nSize, - va_list *argument) { - DEBUG_LOG("FormatMessageA(%u, %p, %u, %u, %p, %u, %p)\n", dwFlags, lpSource, dwMessageId, dwLanguageId, - lpBuffer, nSize, argument); - - if (dwFlags & 0x00000100) { - // FORMAT_MESSAGE_ALLOCATE_BUFFER - } else if (dwFlags & 0x00002000) { - // FORMAT_MESSAGE_ARGUMENT_ARRAY - } else if (dwFlags & 0x00000800) { - // FORMAT_MESSAGE_FROM_HMODULE - } else if (dwFlags & 0x00000400) { - // FORMAT_MESSAGE_FROM_STRING - } else if (dwFlags & 0x00001000) { - // FORMAT_MESSAGE_FROM_SYSTEM - std::string message = std::system_category().message(dwMessageId); - size_t length = message.length(); - strcpy(lpBuffer, message.c_str()); - return length; - } else if (dwFlags & 0x00000200) { - // FORMAT_MESSAGE_IGNORE_INSERTS - } else { - // unhandled? - } - - *lpBuffer = '\0'; + struct stat64 st{}; + if (fstat64(fileno(fp), &st)) { + setLastErrorFromErrno(); return 0; } - int WIN_FUNC GetComputerNameA(char *lpBuffer, unsigned int *nSize) { - DEBUG_LOG("GetComputerNameA(%p, %p)\n", lpBuffer, nSize); - if (!nSize || !lpBuffer) { - if (nSize) { - *nSize = 0; - } - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; + if (lpFileInformation != nullptr) { + lpFileInformation->dwFileAttributes = 0; + if (S_ISDIR(st.st_mode)) { + lpFileInformation->dwFileAttributes |= 0x10; } - constexpr unsigned int required = 9; // "COMPNAME" + null terminator - if (*nSize < required) { - *nSize = required; - wibo::lastError = ERROR_BUFFER_OVERFLOW; - return 0; + if (S_ISREG(st.st_mode)) { + lpFileInformation->dwFileAttributes |= 0x80; } - strcpy(lpBuffer, "COMPNAME"); - *nSize = required - 1; - wibo::lastError = ERROR_SUCCESS; + lpFileInformation->ftCreationTime = defaultFiletime; + lpFileInformation->ftLastAccessTime = defaultFiletime; + lpFileInformation->ftLastWriteTime = defaultFiletime; + lpFileInformation->dwVolumeSerialNumber = 0; + lpFileInformation->nFileSizeHigh = (unsigned long)(st.st_size >> 32); + lpFileInformation->nFileSizeLow = (unsigned long)st.st_size; + lpFileInformation->nNumberOfLinks = 0; + lpFileInformation->nFileIndexHigh = 0; + lpFileInformation->nFileIndexLow = 0; + } + return 1; +} + +constexpr DWORD FILE_TYPE_UNKNOWN = 0x0000; +constexpr DWORD FILE_TYPE_DISK = 0x0001; +constexpr DWORD FILE_TYPE_CHAR = 0x0002; +constexpr DWORD FILE_TYPE_PIPE = 0x0003; + +DWORD WIN_FUNC GetFileType(HANDLE hFile) { + DEBUG_LOG("GetFileType(%p) ", hFile); + + auto *file = files::fileHandleFromHandle(hFile); + if (!file || file->fd < 0) { + wibo::lastError = ERROR_INVALID_HANDLE; + DEBUG_LOG("-> ERROR_INVALID_HANDLE\n"); + return FILE_TYPE_UNKNOWN; + } + + struct stat st{}; + if (fstat(file->fd, &st) != 0) { + setLastErrorFromErrno(); + DEBUG_LOG("-> fstat error\n"); + return FILE_TYPE_UNKNOWN; + } + + wibo::lastError = ERROR_SUCCESS; + DWORD type = FILE_TYPE_UNKNOWN; + if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode)) { + type = FILE_TYPE_DISK; + } + if (S_ISCHR(st.st_mode)) { + type = FILE_TYPE_CHAR; + } + if (S_ISSOCK(st.st_mode) || S_ISFIFO(st.st_mode)) { + type = FILE_TYPE_PIPE; + } + DEBUG_LOG("-> %u\n", type); + return type; +} + +UINT WIN_FUNC SetHandleCount(UINT uNumber) { + DEBUG_LOG("SetHandleCount(%u)\n", uNumber); + return handles::MAX_HANDLES; +} + +void WIN_FUNC Sleep(DWORD dwMilliseconds) { + DEBUG_LOG("Sleep(%u)\n", dwMilliseconds); + usleep(static_cast(dwMilliseconds) * 1000); +} + +unsigned int WIN_FUNC IsProcessorFeaturePresent(unsigned int processorFeature) { + DEBUG_LOG("IsProcessorFeaturePresent(%u)\n", processorFeature); + + if (processorFeature == 0) // PF_FLOATING_POINT_PRECISION_ERRATA return 1; - } - - int WIN_FUNC GetComputerNameW(uint16_t *lpBuffer, unsigned int *nSize) { - DEBUG_LOG("GetComputerNameW(%p, %p)\n", lpBuffer, nSize); - if (!nSize || !lpBuffer) { - if (nSize) { - *nSize = 0; - } - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - constexpr uint16_t computerName[] = {'C', 'O', 'M', 'P', 'N', 'A', 'M', 'E', 0}; - constexpr unsigned int nameLength = 8; - constexpr unsigned int required = nameLength + 1; - if (*nSize < required) { - *nSize = required; - wibo::lastError = ERROR_BUFFER_OVERFLOW; - return 0; - } - wstrncpy(lpBuffer, computerName, required); - *nSize = nameLength; - wibo::lastError = ERROR_SUCCESS; + if (processorFeature == 10) // PF_XMMI64_INSTRUCTIONS_AVAILABLE (SSE2) return 1; + if (processorFeature == 23) // PF_FASTFAIL_AVAILABLE (__fastfail() supported) + return 1; + + // sure.. we have that feature... + DEBUG_LOG(" IsProcessorFeaturePresent: we don't know about feature %u, lying...\n", processorFeature); + return 1; +} + +unsigned int WIN_FUNC FormatMessageA(unsigned int dwFlags, void *lpSource, unsigned int dwMessageId, + unsigned int dwLanguageId, char *lpBuffer, unsigned int nSize, va_list *argument) { + DEBUG_LOG("FormatMessageA(%u, %p, %u, %u, %p, %u, %p)\n", dwFlags, lpSource, dwMessageId, dwLanguageId, lpBuffer, + nSize, argument); + + if (dwFlags & 0x00000100) { + // FORMAT_MESSAGE_ALLOCATE_BUFFER + } else if (dwFlags & 0x00002000) { + // FORMAT_MESSAGE_ARGUMENT_ARRAY + } else if (dwFlags & 0x00000800) { + // FORMAT_MESSAGE_FROM_HMODULE + } else if (dwFlags & 0x00000400) { + // FORMAT_MESSAGE_FROM_STRING + } else if (dwFlags & 0x00001000) { + // FORMAT_MESSAGE_FROM_SYSTEM + std::string message = std::system_category().message(dwMessageId); + size_t length = message.length(); + strcpy(lpBuffer, message.c_str()); + return length; + } else if (dwFlags & 0x00000200) { + // FORMAT_MESSAGE_IGNORE_INSERTS + } else { + // unhandled? } - void *WIN_FUNC EncodePointer(void *Ptr) { - DEBUG_LOG("EncodePointer(%p)\n", Ptr); - return Ptr; - } + *lpBuffer = '\0'; + return 0; +} - void *WIN_FUNC DecodePointer(void *Ptr) { - DEBUG_LOG("DecodePointer(%p)\n", Ptr); - return Ptr; - } - - BOOL WIN_FUNC SetDllDirectoryA(LPCSTR lpPathName) { - DEBUG_LOG("SetDllDirectoryA(%s)\n", lpPathName); - if (!lpPathName || lpPathName[0] == '\0') { - wibo::clearDllDirectoryOverride(); - wibo::lastError = ERROR_SUCCESS; - return TRUE; +int WIN_FUNC GetComputerNameA(char *lpBuffer, unsigned int *nSize) { + DEBUG_LOG("GetComputerNameA(%p, %p)\n", lpBuffer, nSize); + if (!nSize || !lpBuffer) { + if (nSize) { + *nSize = 0; } + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + constexpr unsigned int required = 9; // "COMPNAME" + null terminator + if (*nSize < required) { + *nSize = required; + wibo::lastError = ERROR_BUFFER_OVERFLOW; + return 0; + } + strcpy(lpBuffer, "COMPNAME"); + *nSize = required - 1; + wibo::lastError = ERROR_SUCCESS; + return 1; +} - auto hostPath = files::pathFromWindows(lpPathName); - if (hostPath.empty() || !std::filesystem::exists(hostPath)) { - wibo::lastError = ERROR_PATH_NOT_FOUND; - return FALSE; +int WIN_FUNC GetComputerNameW(uint16_t *lpBuffer, unsigned int *nSize) { + DEBUG_LOG("GetComputerNameW(%p, %p)\n", lpBuffer, nSize); + if (!nSize || !lpBuffer) { + if (nSize) { + *nSize = 0; } + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + constexpr uint16_t computerName[] = {'C', 'O', 'M', 'P', 'N', 'A', 'M', 'E', 0}; + constexpr unsigned int nameLength = 8; + constexpr unsigned int required = nameLength + 1; + if (*nSize < required) { + *nSize = required; + wibo::lastError = ERROR_BUFFER_OVERFLOW; + return 0; + } + wstrncpy(lpBuffer, computerName, required); + *nSize = nameLength; + wibo::lastError = ERROR_SUCCESS; + return 1; +} - wibo::setDllDirectoryOverride(std::filesystem::absolute(hostPath)); +void *WIN_FUNC EncodePointer(void *Ptr) { + DEBUG_LOG("EncodePointer(%p)\n", Ptr); + return Ptr; +} + +void *WIN_FUNC DecodePointer(void *Ptr) { + DEBUG_LOG("DecodePointer(%p)\n", Ptr); + return Ptr; +} + +BOOL WIN_FUNC SetDllDirectoryA(LPCSTR lpPathName) { + DEBUG_LOG("SetDllDirectoryA(%s)\n", lpPathName); + if (!lpPathName || lpPathName[0] == '\0') { + wibo::clearDllDirectoryOverride(); wibo::lastError = ERROR_SUCCESS; return TRUE; } - int WIN_FUNC CompareStringA(int Locale, unsigned int dwCmpFlags, const char *lpString1, int cchCount1, const char *lpString2, int cchCount2) { - if (cchCount1 < 0) - cchCount1 = strlen(lpString1); - if (cchCount2 < 0) - cchCount2 = strlen(lpString2); - std::string str1(lpString1, lpString1 + cchCount1); - std::string str2(lpString2, lpString2 + cchCount2); - - DEBUG_LOG("CompareStringA(%d, %u, %s, %d, %s, %d)\n", Locale, dwCmpFlags, str1.c_str(), cchCount1, str2.c_str(), cchCount2); - return doCompareString(str1, str2, dwCmpFlags); - } - - int WIN_FUNC CompareStringW(int Locale, unsigned int dwCmpFlags, const uint16_t *lpString1, int cchCount1, const uint16_t *lpString2, int cchCount2) { - std::string str1 = wideStringToString(lpString1, cchCount1); - std::string str2 = wideStringToString(lpString2, cchCount2); - - DEBUG_LOG("CompareStringW(%d, %u, %s, %d, %s, %d)\n", Locale, dwCmpFlags, str1.c_str(), cchCount1, str2.c_str(), cchCount2); - return doCompareString(str1, str2, dwCmpFlags); - } - - int WIN_FUNC IsValidCodePage(unsigned int CodePage) { - DEBUG_LOG("IsValidCodePage(%u)\n", CodePage); - // Returns a nonzero value if the code page is valid, or 0 if the code page is invalid. - return 1; - } - - int WIN_FUNC IsValidLocale(unsigned int Locale, unsigned int dwFlags) { - DEBUG_LOG("IsValidLocale(%u, %u)\n", Locale, dwFlags); - // Yep, this locale is both supported (dwFlags=1) and installed (dwFlags=2) - return 1; - } - - std::string str_for_LCType(int LCType) { - // https://www.pinvoke.net/default.aspx/Enums/LCType.html - if (LCType == 4100) { // LOCALE_IDEFAULTANSICODEPAGE - // Latin1; ref GetACP - return "28591"; - } - if (LCType == 4097) { // LOCALE_SENGLANGUAGE - return "Lang"; - } - if (LCType == 4098) { // LOCALE_SENGCOUNTRY - return "Country"; - } - if (LCType == 0x1) { // LOCALE_ILANGUAGE - return "0001"; - } - if (LCType == 0x15) { // LOCALE_SINTLSYMBOL - return "Currency"; - } - if (LCType == 0x14) { // LOCALE_SCURRENCY - return "sCurrency"; - } - if (LCType == 0x16) { // LOCALE_SMONDECIMALSEP - return "."; - } - if (LCType == 0x17) { // LOCALE_SMONTHOUSANDSEP - return ","; - } - if (LCType == 0x18) { // LOCALE_SMONGROUPING - return ";"; - } - if (LCType == 0x50) { // LOCALE_SPOSITIVESIGN - return ""; - } - if (LCType == 0x51) { // LOCALE_SNEGATIVESIGN - return "-"; - } - if (LCType == 0x1A) { // LOCALE_IINTLCURRDIGITS - return "2"; - } - if (LCType == 0x19) { // LOCALE_ICURRDIGITS - return "2"; - } - - DEBUG_LOG("STUB: LCType 0x%X not implemented\n", LCType); - return ""; - } - - int WIN_FUNC GetLocaleInfoA(unsigned int Locale, int LCType, LPSTR lpLCData, int cchData) { - DEBUG_LOG("GetLocaleInfoA(%u, %d, %p, %d)\n", Locale, LCType, lpLCData, cchData); - std::string ret = str_for_LCType(LCType); - size_t len = ret.size() + 1; - - if (!cchData) { - return len; - } else { - assert(len <= (size_t) cchData); - memcpy(lpLCData, ret.c_str(), len); - return 1; - } - } - - int WIN_FUNC GetLocaleInfoW(unsigned int Locale, int LCType, LPWSTR lpLCData, int cchData) { - DEBUG_LOG("GetLocaleInfoW(%u, %d, %p, %d)\n", Locale, LCType, lpLCData, cchData); - std::string info = str_for_LCType(LCType); - auto ret = stringToWideString(info.c_str()); - size_t len = ret.size(); - - if (!cchData) { - return len; - } else { - assert(len <= (size_t) cchData); - memcpy(lpLCData, ret.data(), len * sizeof(*ret.data())); - return 1; - } - } - - int WIN_FUNC EnumSystemLocalesA(void (*callback)(char *lpLocaleString), int dwFlags) { - DEBUG_LOG("EnumSystemLocalesA(%p, %d)\n", callback, dwFlags); - // e.g. something like: - // callback("en_US"); - // callback("ja_JP"); - return 1; - } - - int WIN_FUNC GetUserDefaultLCID() { - DEBUG_LOG("GetUserDefaultLCID()\n"); - return 1; - } - - BOOL WIN_FUNC IsDBCSLeadByte(BYTE TestChar) { - DEBUG_LOG("IsDBCSLeadByte(%u)\n", TestChar); - return FALSE; // We're not multibyte (yet?) - } - - BOOL WIN_FUNC IsDBCSLeadByteEx(unsigned int CodePage, BYTE TestChar) { - DEBUG_LOG("IsDBCSLeadByteEx(%u, %u)\n", CodePage, TestChar); - - const auto inRanges = [TestChar](std::initializer_list> ranges) -> BOOL { - for (const auto &range : ranges) { - if (TestChar >= range.first && TestChar <= range.second) { - return TRUE; - } - } - return FALSE; - }; - - constexpr unsigned int CP_ACP = 0; - constexpr unsigned int CP_OEMCP = 1; - constexpr unsigned int CP_MACCP = 2; - constexpr unsigned int CP_THREAD_ACP = 3; - - if (CodePage == CP_ACP || CodePage == CP_OEMCP || CodePage == CP_MACCP || CodePage == CP_THREAD_ACP) { - return FALSE; - } - - switch (CodePage) { - case 932: // Japanese Shift-JIS - return inRanges({{0x81, 0x9F}, {0xE0, 0xFC}}); - case 936: // Simplified Chinese (GBK) - case 949: // Korean - case 950: // Traditional Chinese (Big5) - case 1361: // Johab - return inRanges({{0x81, 0xFE}}); - default: - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - } - - constexpr unsigned int LCMAP_LOWERCASE = 0x00000100; - constexpr unsigned int LCMAP_UPPERCASE = 0x00000200; - constexpr unsigned int LCMAP_SORTKEY = 0x00000400; - constexpr unsigned int LCMAP_BYTEREV = 0x00000800; - constexpr unsigned int LCMAP_LINGUISTIC_CASING = 0x01000000; - - int WIN_FUNC LCMapStringW(int Locale, unsigned int dwMapFlags, const uint16_t* lpSrcStr, int cchSrc, uint16_t* lpDestStr, int cchDest) { - DEBUG_LOG("LCMapStringW(%u, %u, %p, %d, %p, %d)\n", Locale, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest); - (void) Locale; - if (!lpSrcStr || cchSrc == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - bool nullTerminated = cchSrc < 0; - size_t srcLen = nullTerminated ? (wstrlen(lpSrcStr) + 1) : static_cast(cchSrc); - if (srcLen == 0) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - if (!lpDestStr || cchDest == 0) { - // Caller is asking for the required length. - wibo::lastError = ERROR_SUCCESS; - return static_cast(srcLen); - } - if (cchDest < static_cast(srcLen)) { - wibo::lastError = ERROR_INSUFFICIENT_BUFFER; - return 0; - } - - unsigned int casingFlags = dwMapFlags & (LCMAP_UPPERCASE | LCMAP_LOWERCASE); - unsigned int ignoredFlags = dwMapFlags & (LCMAP_LINGUISTIC_CASING); - (void) ignoredFlags; - if (dwMapFlags & (LCMAP_SORTKEY | LCMAP_BYTEREV)) { - DEBUG_LOG("LCMapStringW: unsupported mapping flags 0x%x\n", dwMapFlags); - wibo::lastError = ERROR_INVALID_PARAMETER; - return 0; - } - - std::vector buffer(srcLen, 0); - for (size_t i = 0; i < srcLen; ++i) { - uint16_t ch = lpSrcStr[i]; - if (casingFlags == LCMAP_UPPERCASE) { - buffer[i] = static_cast(std::towupper(static_cast(ch))); - } else if (casingFlags == LCMAP_LOWERCASE) { - buffer[i] = static_cast(std::towlower(static_cast(ch))); - } else { - buffer[i] = ch; - } - } - - std::memcpy(lpDestStr, buffer.data(), srcLen * sizeof(uint16_t)); - DEBUG_LOG("-> %s\n", wideStringToString(lpDestStr, srcLen).c_str()); - wibo::lastError = ERROR_SUCCESS; - return static_cast(srcLen); - } - - int WIN_FUNC LCMapStringA(int Locale, unsigned int dwMapFlags, const char* lpSrcStr, int cchSrc, char* lpDestStr, int cchDest) { - DEBUG_LOG("LCMapStringA(%u, %u, %p, %d, %p, %d)\n", Locale, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest); - if (cchSrc < 0) { - cchSrc = strlen(lpSrcStr) + 1; - } - // DEBUG_LOG("lpSrcStr: %s\n", lpSrcStr); - return 0; // fail - } - - - static std::string convertEnvValueForWindows(const std::string &name, const char *rawValue) { - if (!rawValue) { - return std::string(); - } - if (strcasecmp(name.c_str(), "PATH") != 0) { - return rawValue; - } - std::string converted = files::hostPathListToWindows(rawValue); - return converted.empty() ? std::string(rawValue) : converted; - } - - static std::string convertEnvValueToHost(const std::string &name, const char *rawValue) { - if (!rawValue) { - return std::string(); - } - if (strcasecmp(name.c_str(), "PATH") != 0) { - return rawValue; - } - std::string converted = files::windowsPathListToHost(rawValue); - return converted.empty() ? std::string(rawValue) : converted; - } - - DWORD WIN_FUNC GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize) { - DEBUG_LOG("GetEnvironmentVariableA(%s, %p, %d)\n", lpName, lpBuffer, nSize); - if (!lpName) { - return 0; - } - const char *rawValue = getenv(lpName); - if (!rawValue) { - return 0; - } - std::string converted = convertEnvValueForWindows(lpName, rawValue); - const std::string &finalValue = converted.empty() ? std::string(rawValue) : converted; - unsigned int len = finalValue.size(); - if (nSize == 0) { - return len + 1; - } - if (nSize <= len) { - return len; - } - memcpy(lpBuffer, finalValue.c_str(), len + 1); - return len; - } - - BOOL WIN_FUNC SetEnvironmentVariableA(const char *lpName, const char *lpValue) { - DEBUG_LOG("SetEnvironmentVariableA(%s, %s)\n", lpName ? lpName : "(null)", lpValue ? lpValue : "(null)"); - if (!lpName || std::strchr(lpName, '=')) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - int rc = 0; - if (!lpValue) { - rc = unsetenv(lpName); - if (rc != 0) { - setLastErrorFromErrno(); - return FALSE; - } - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - std::string hostValue = convertEnvValueToHost(lpName, lpValue); - const char *valuePtr = hostValue.empty() ? lpValue : hostValue.c_str(); - rc = setenv(lpName, valuePtr, 1 /* overwrite */); - if (rc != 0) { - setLastErrorFromErrno(); - return FALSE; - } - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - - DWORD WIN_FUNC GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize) { - std::string name = wideStringToString(lpName); - DEBUG_LOG("GetEnvironmentVariableW(%s, %p, %d)\n", name.c_str(), lpBuffer, nSize); - const char *rawValue = getenv(name.c_str()); - if (!rawValue) { - return 0; - } - std::string converted = convertEnvValueForWindows(name, rawValue); - const std::string &finalValue = converted.empty() ? std::string(rawValue) : converted; - auto wideValue = stringToWideString(finalValue.c_str()); - const auto len = wideValue.size(); - if (nSize < len) { - return len; - } - wstrncpy(lpBuffer, wideValue.data(), len); - return len - 1; - } - - BOOL WIN_FUNC SetEnvironmentVariableW(const uint16_t *lpName, const uint16_t *lpValue) { - DEBUG_LOG("SetEnvironmentVariableW -> "); - if (!lpName) { - wibo::lastError = ERROR_INVALID_PARAMETER; - DEBUG_LOG("ERROR_INVALID_PARAMETER\n"); - return FALSE; - } - std::string name = wideStringToString(lpName); - std::string value = lpValue ? wideStringToString(lpValue) : std::string(); - return SetEnvironmentVariableA(name.c_str(), lpValue ? value.c_str() : nullptr); - } - - BOOL WIN_FUNC QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount) { - VERBOSE_LOG("STUB: QueryPerformanceCounter(%p)\n", lpPerformanceCount); - *lpPerformanceCount = 0; - return TRUE; - } - - BOOL WIN_FUNC QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency) { - VERBOSE_LOG("STUB: QueryPerformanceFrequency(%p)\n", lpFrequency); - *lpFrequency = 1; - return TRUE; - } - - BOOL WIN_FUNC IsDebuggerPresent() { - DEBUG_LOG("STUB: IsDebuggerPresent()\n"); - // If the current process is not running in the context of a debugger, the return value is zero. + auto hostPath = files::pathFromWindows(lpPathName); + if (hostPath.empty() || !std::filesystem::exists(hostPath)) { + wibo::lastError = ERROR_PATH_NOT_FOUND; return FALSE; } - struct SINGLE_LIST_ENTRY - { - SINGLE_LIST_ENTRY *Next; - }; + wibo::setDllDirectoryOverride(std::filesystem::absolute(hostPath)); + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} - struct SLIST_HEADER - { - union - { - unsigned long Alignment; - struct - { - SINGLE_LIST_ENTRY Next; - int Depth; - int Sequence; - }; - }; - }; +void WIN_FUNC RtlUnwind(void *TargetFrame, void *TargetIp, EXCEPTION_RECORD *ExceptionRecord, void *ReturnValue) { + DEBUG_LOG("RtlUnwind(%p, %p, %p, %p)\n", TargetFrame, TargetIp, ExceptionRecord, ReturnValue); + DEBUG_LOG("WARNING: Silently returning from RtlUnwind - exception handlers and clean up code may not be run"); +} - void WIN_FUNC InitializeSListHead(SLIST_HEADER *ListHead) { - DEBUG_LOG("InitializeSListHead(%p)\n", ListHead); - // All list items must be aligned on a MEMORY_ALLOCATION_ALIGNMENT boundary. - posix_memalign((void**)&ListHead, 16, sizeof(SLIST_HEADER)); - memset(ListHead, 0, sizeof(SLIST_HEADER)); +BOOL WIN_FUNC GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, + BOOL bWait) { + DEBUG_LOG("GetOverlappedResult(%p, %p, %p, %d)\n", hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait); + (void)hFile; + if (!lpOverlapped) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; } - - void WIN_FUNC RtlUnwind(void *TargetFrame, void *TargetIp, EXCEPTION_RECORD *ExceptionRecord, void *ReturnValue) { - DEBUG_LOG("RtlUnwind(%p, %p, %p, %p)\n", TargetFrame, TargetIp, ExceptionRecord, ReturnValue); - DEBUG_LOG("WARNING: Silently returning from RtlUnwind - exception handlers and clean up code may not be run"); - } - - int WIN_FUNC InterlockedIncrement(int *Addend) { - VERBOSE_LOG("InterlockedIncrement(%p)\n", Addend); - return *Addend += 1; - } - - int WIN_FUNC InterlockedDecrement(int *Addend) { - VERBOSE_LOG("InterlockedDecrement(%p)\n", Addend); - return *Addend -= 1; - } - - int WIN_FUNC InterlockedExchange(int *Target, int Value) { - VERBOSE_LOG("InterlockedExchange(%p, %d)\n", Target, Value); - int initial = *Target; - *Target = Value; - return initial; - } - - LONG WIN_FUNC InterlockedCompareExchange(volatile LONG* destination, LONG exchange, LONG comperand) { - VERBOSE_LOG("InterlockedCompareExchange(%p, %ld, %ld)\n", destination, exchange, comperand); - LONG original = *destination; - if (original == comperand) { - *destination = exchange; + if (bWait && lpOverlapped->Internal == STATUS_PENDING) { + if (lpOverlapped->hEvent) { + WaitForSingleObject(lpOverlapped->hEvent, 0xFFFFFFFF); } - return original; - // return __sync_val_compare_and_swap(destination, comperand, exchange); if we want to maintain the atomic behavior } - BOOL WIN_FUNC GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, - BOOL bWait) { - DEBUG_LOG("GetOverlappedResult(%p, %p, %p, %d)\n", hFile, lpOverlapped, lpNumberOfBytesTransferred, bWait); - (void)hFile; - if (!lpOverlapped) { - wibo::lastError = ERROR_INVALID_PARAMETER; - return FALSE; - } - if (bWait && lpOverlapped->Internal == STATUS_PENDING) { - if (lpOverlapped->hEvent) { - WaitForSingleObject(lpOverlapped->hEvent, 0xFFFFFFFF); - } - } - - const auto status = static_cast(lpOverlapped->Internal); - if (status == STATUS_PENDING) { - wibo::lastError = ERROR_IO_INCOMPLETE; - if (lpNumberOfBytesTransferred) { - *lpNumberOfBytesTransferred = static_cast(lpOverlapped->InternalHigh); - } - return FALSE; - } - + const auto status = static_cast(lpOverlapped->Internal); + if (status == STATUS_PENDING) { + wibo::lastError = ERROR_IO_INCOMPLETE; if (lpNumberOfBytesTransferred) { *lpNumberOfBytesTransferred = static_cast(lpOverlapped->InternalHigh); } - - if (status == STATUS_SUCCESS) { - wibo::lastError = ERROR_SUCCESS; - return TRUE; - } - if (status == STATUS_END_OF_FILE || status == ERROR_HANDLE_EOF) { - wibo::lastError = ERROR_HANDLE_EOF; - return FALSE; - } - - wibo::lastError = status; return FALSE; } + + if (lpNumberOfBytesTransferred) { + *lpNumberOfBytesTransferred = static_cast(lpOverlapped->InternalHigh); + } + + if (status == STATUS_SUCCESS) { + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + if (status == STATUS_END_OF_FILE || status == ERROR_HANDLE_EOF) { + wibo::lastError = ERROR_HANDLE_EOF; + return FALSE; + } + + wibo::lastError = status; + return FALSE; } +} // namespace kernel32 static void *resolveByName(const char *name) { // errhandlingapi.h - if (strcmp(name, "GetLastError") == 0) return (void *) kernel32::GetLastError; - if (strcmp(name, "SetLastError") == 0) return (void *) kernel32::SetLastError; - if (strcmp(name, "IsBadReadPtr") == 0) return (void *) kernel32::IsBadReadPtr; - if (strcmp(name, "IsBadWritePtr") == 0) return (void *) kernel32::IsBadWritePtr; - if (strcmp(name, "Wow64DisableWow64FsRedirection") == 0) return (void *) kernel32::Wow64DisableWow64FsRedirection; - if (strcmp(name, "Wow64RevertWow64FsRedirection") == 0) return (void *) kernel32::Wow64RevertWow64FsRedirection; - if (strcmp(name, "IsWow64Process") == 0) return (void *) kernel32::IsWow64Process; - if (strcmp(name, "RaiseException") == 0) return (void *) kernel32::RaiseException; - if (strcmp(name, "AddVectoredExceptionHandler") == 0) return (void *) kernel32::AddVectoredExceptionHandler; + if (strcmp(name, "GetLastError") == 0) + return (void *)kernel32::GetLastError; + if (strcmp(name, "SetLastError") == 0) + return (void *)kernel32::SetLastError; + if (strcmp(name, "IsBadReadPtr") == 0) + return (void *)kernel32::IsBadReadPtr; + if (strcmp(name, "IsBadWritePtr") == 0) + return (void *)kernel32::IsBadWritePtr; + if (strcmp(name, "Wow64DisableWow64FsRedirection") == 0) + return (void *)kernel32::Wow64DisableWow64FsRedirection; + if (strcmp(name, "Wow64RevertWow64FsRedirection") == 0) + return (void *)kernel32::Wow64RevertWow64FsRedirection; + if (strcmp(name, "IsWow64Process") == 0) + return (void *)kernel32::IsWow64Process; + if (strcmp(name, "RaiseException") == 0) + return (void *)kernel32::RaiseException; + if (strcmp(name, "AddVectoredExceptionHandler") == 0) + return (void *)kernel32::AddVectoredExceptionHandler; // processthreadsapi.h - if (strcmp(name, "IsProcessorFeaturePresent") == 0) return (void *) kernel32::IsProcessorFeaturePresent; - if (strcmp(name, "GetCurrentProcess") == 0) return (void *) kernel32::GetCurrentProcess; - if (strcmp(name, "GetCurrentProcessId") == 0) return (void *) kernel32::GetCurrentProcessId; - if (strcmp(name, "GetCurrentThreadId") == 0) return (void *) kernel32::GetCurrentThreadId; - if (strcmp(name, "ExitProcess") == 0) return (void *) kernel32::ExitProcess; - if (strcmp(name, "TerminateProcess") == 0) return (void *) kernel32::TerminateProcess; - if (strcmp(name, "GetExitCodeProcess") == 0) return (void *) kernel32::GetExitCodeProcess; - if (strcmp(name, "CreateProcessW") == 0) return (void *) kernel32::CreateProcessW; - if (strcmp(name, "CreateProcessA") == 0) return (void *) kernel32::CreateProcessA; - if (strcmp(name, "CreateThread") == 0) return (void *) kernel32::CreateThread; - if (strcmp(name, "ExitThread") == 0) return (void *) kernel32::ExitThread; - if (strcmp(name, "GetExitCodeThread") == 0) return (void *) kernel32::GetExitCodeThread; - if (strcmp(name, "TlsAlloc") == 0) return (void *) kernel32::TlsAlloc; - if (strcmp(name, "TlsFree") == 0) return (void *) kernel32::TlsFree; - if (strcmp(name, "TlsGetValue") == 0) return (void *) kernel32::TlsGetValue; - if (strcmp(name, "TlsSetValue") == 0) return (void *) kernel32::TlsSetValue; - if (strcmp(name, "GetStartupInfoA") == 0) return (void *) kernel32::GetStartupInfoA; - if (strcmp(name, "GetStartupInfoW") == 0) return (void *) kernel32::GetStartupInfoW; - if (strcmp(name, "SetThreadStackGuarantee") == 0) return (void *) kernel32::SetThreadStackGuarantee; - if (strcmp(name, "GetCurrentThread") == 0) return (void *) kernel32::GetCurrentThread; - if (strcmp(name, "GetThreadTimes") == 0) return (void *) kernel32::GetThreadTimes; - if (strcmp(name, "SetThreadDescription") == 0) return (void *) kernel32::SetThreadDescription; + if (strcmp(name, "IsProcessorFeaturePresent") == 0) + return (void *)kernel32::IsProcessorFeaturePresent; + if (strcmp(name, "GetCurrentProcess") == 0) + return (void *)kernel32::GetCurrentProcess; + if (strcmp(name, "GetCurrentProcessId") == 0) + return (void *)kernel32::GetCurrentProcessId; + if (strcmp(name, "GetCurrentThreadId") == 0) + return (void *)kernel32::GetCurrentThreadId; + if (strcmp(name, "ExitProcess") == 0) + return (void *)kernel32::ExitProcess; + if (strcmp(name, "TerminateProcess") == 0) + return (void *)kernel32::TerminateProcess; + if (strcmp(name, "GetExitCodeProcess") == 0) + return (void *)kernel32::GetExitCodeProcess; + if (strcmp(name, "CreateProcessW") == 0) + return (void *)kernel32::CreateProcessW; + if (strcmp(name, "CreateProcessA") == 0) + return (void *)kernel32::CreateProcessA; + if (strcmp(name, "CreateThread") == 0) + return (void *)kernel32::CreateThread; + if (strcmp(name, "ExitThread") == 0) + return (void *)kernel32::ExitThread; + if (strcmp(name, "GetExitCodeThread") == 0) + return (void *)kernel32::GetExitCodeThread; + if (strcmp(name, "TlsAlloc") == 0) + return (void *)kernel32::TlsAlloc; + if (strcmp(name, "TlsFree") == 0) + return (void *)kernel32::TlsFree; + if (strcmp(name, "TlsGetValue") == 0) + return (void *)kernel32::TlsGetValue; + if (strcmp(name, "TlsSetValue") == 0) + return (void *)kernel32::TlsSetValue; + if (strcmp(name, "GetStartupInfoA") == 0) + return (void *)kernel32::GetStartupInfoA; + if (strcmp(name, "GetStartupInfoW") == 0) + return (void *)kernel32::GetStartupInfoW; + if (strcmp(name, "SetThreadStackGuarantee") == 0) + return (void *)kernel32::SetThreadStackGuarantee; + if (strcmp(name, "GetCurrentThread") == 0) + return (void *)kernel32::GetCurrentThread; + if (strcmp(name, "GetThreadTimes") == 0) + return (void *)kernel32::GetThreadTimes; + if (strcmp(name, "SetThreadDescription") == 0) + return (void *)kernel32::SetThreadDescription; // winnls.h - if (strcmp(name, "GetSystemDefaultLangID") == 0) return (void *) kernel32::GetSystemDefaultLangID; - if (strcmp(name, "GetUserDefaultUILanguage") == 0) return (void *) kernel32::GetUserDefaultUILanguage; - if (strcmp(name, "GetACP") == 0) return (void *) kernel32::GetACP; - if (strcmp(name, "GetCPInfo") == 0) return (void *) kernel32::GetCPInfo; - if (strcmp(name, "CompareStringA") == 0) return (void *) kernel32::CompareStringA; - if (strcmp(name, "CompareStringW") == 0) return (void *) kernel32::CompareStringW; - if (strcmp(name, "IsValidLocale") == 0) return (void *) kernel32::IsValidLocale; - if (strcmp(name, "IsValidCodePage") == 0) return (void *) kernel32::IsValidCodePage; - if (strcmp(name, "LCMapStringW") == 0) return (void *) kernel32::LCMapStringW; - if (strcmp(name, "LCMapStringA") == 0) return (void *) kernel32::LCMapStringA; - if (strcmp(name, "GetLocaleInfoA") == 0) return (void *) kernel32::GetLocaleInfoA; - if (strcmp(name, "GetLocaleInfoW") == 0) return (void *) kernel32::GetLocaleInfoW; - if (strcmp(name, "EnumSystemLocalesA") == 0) return (void *) kernel32::EnumSystemLocalesA; - if (strcmp(name, "GetUserDefaultLCID") == 0) return (void *) kernel32::GetUserDefaultLCID; - if (strcmp(name, "IsDBCSLeadByte") == 0) return (void *) kernel32::IsDBCSLeadByte; - if (strcmp(name, "IsDBCSLeadByteEx") == 0) return (void *) kernel32::IsDBCSLeadByteEx; + if (strcmp(name, "GetSystemDefaultLangID") == 0) + return (void *)kernel32::GetSystemDefaultLangID; + if (strcmp(name, "GetUserDefaultUILanguage") == 0) + return (void *)kernel32::GetUserDefaultUILanguage; + if (strcmp(name, "GetACP") == 0) + return (void *)kernel32::GetACP; + if (strcmp(name, "GetCPInfo") == 0) + return (void *)kernel32::GetCPInfo; + if (strcmp(name, "CompareStringA") == 0) + return (void *)kernel32::CompareStringA; + if (strcmp(name, "CompareStringW") == 0) + return (void *)kernel32::CompareStringW; + if (strcmp(name, "IsValidLocale") == 0) + return (void *)kernel32::IsValidLocale; + if (strcmp(name, "IsValidCodePage") == 0) + return (void *)kernel32::IsValidCodePage; + if (strcmp(name, "LCMapStringW") == 0) + return (void *)kernel32::LCMapStringW; + if (strcmp(name, "LCMapStringA") == 0) + return (void *)kernel32::LCMapStringA; + if (strcmp(name, "GetLocaleInfoA") == 0) + return (void *)kernel32::GetLocaleInfoA; + if (strcmp(name, "GetLocaleInfoW") == 0) + return (void *)kernel32::GetLocaleInfoW; + if (strcmp(name, "EnumSystemLocalesA") == 0) + return (void *)kernel32::EnumSystemLocalesA; + if (strcmp(name, "GetUserDefaultLCID") == 0) + return (void *)kernel32::GetUserDefaultLCID; + if (strcmp(name, "IsDBCSLeadByte") == 0) + return (void *)kernel32::IsDBCSLeadByte; + if (strcmp(name, "IsDBCSLeadByteEx") == 0) + return (void *)kernel32::IsDBCSLeadByteEx; // synchapi.h - if (strcmp(name, "InitializeCriticalSection") == 0) return (void *) kernel32::InitializeCriticalSection; - if (strcmp(name, "InitializeCriticalSectionEx") == 0) return (void *) kernel32::InitializeCriticalSectionEx; - if (strcmp(name, "InitializeCriticalSectionAndSpinCount") == 0) return (void *) kernel32::InitializeCriticalSectionAndSpinCount; - if (strcmp(name, "DeleteCriticalSection") == 0) return (void *) kernel32::DeleteCriticalSection; - if (strcmp(name, "EnterCriticalSection") == 0) return (void *) kernel32::EnterCriticalSection; - if (strcmp(name, "LeaveCriticalSection") == 0) return (void *) kernel32::LeaveCriticalSection; - if (strcmp(name, "InitOnceBeginInitialize") == 0) return (void *) kernel32::InitOnceBeginInitialize; - if (strcmp(name, "InitOnceComplete") == 0) return (void *) kernel32::InitOnceComplete; - if (strcmp(name, "AcquireSRWLockShared") == 0) return (void *) kernel32::AcquireSRWLockShared; - if (strcmp(name, "ReleaseSRWLockShared") == 0) return (void *) kernel32::ReleaseSRWLockShared; - if (strcmp(name, "AcquireSRWLockExclusive") == 0) return (void *) kernel32::AcquireSRWLockExclusive; - if (strcmp(name, "ReleaseSRWLockExclusive") == 0) return (void *) kernel32::ReleaseSRWLockExclusive; - if (strcmp(name, "TryAcquireSRWLockExclusive") == 0) return (void *) kernel32::TryAcquireSRWLockExclusive; - if (strcmp(name, "WaitForSingleObject") == 0) return (void *) kernel32::WaitForSingleObject; - if (strcmp(name, "CreateMutexA") == 0) return (void *) kernel32::CreateMutexA; - if (strcmp(name, "CreateMutexW") == 0) return (void *) kernel32::CreateMutexW; - if (strcmp(name, "CreateEventA") == 0) return (void *) kernel32::CreateEventA; - if (strcmp(name, "CreateEventW") == 0) return (void *) kernel32::CreateEventW; - if (strcmp(name, "CreateSemaphoreA") == 0) return (void *) kernel32::CreateSemaphoreA; - if (strcmp(name, "CreateSemaphoreW") == 0) return (void *) kernel32::CreateSemaphoreW; - if (strcmp(name, "SetEvent") == 0) return (void *) kernel32::SetEvent; - if (strcmp(name, "ResetEvent") == 0) return (void *) kernel32::ResetEvent; - if (strcmp(name, "ReleaseMutex") == 0) return (void *) kernel32::ReleaseMutex; - if (strcmp(name, "ReleaseSemaphore") == 0) return (void *) kernel32::ReleaseSemaphore; - if (strcmp(name, "SetThreadAffinityMask") == 0) return (void *) kernel32::SetThreadAffinityMask; - if (strcmp(name, "ResumeThread") == 0) return (void *) kernel32::ResumeThread; - if (strcmp(name, "SetThreadPriority") == 0) return (void *) kernel32::SetThreadPriority; - if (strcmp(name, "GetThreadPriority") == 0) return (void *) kernel32::GetThreadPriority; + if (strcmp(name, "InitializeCriticalSection") == 0) + return (void *)kernel32::InitializeCriticalSection; + if (strcmp(name, "InitializeCriticalSectionEx") == 0) + return (void *)kernel32::InitializeCriticalSectionEx; + if (strcmp(name, "InitializeCriticalSectionAndSpinCount") == 0) + return (void *)kernel32::InitializeCriticalSectionAndSpinCount; + if (strcmp(name, "DeleteCriticalSection") == 0) + return (void *)kernel32::DeleteCriticalSection; + if (strcmp(name, "EnterCriticalSection") == 0) + return (void *)kernel32::EnterCriticalSection; + if (strcmp(name, "LeaveCriticalSection") == 0) + return (void *)kernel32::LeaveCriticalSection; + if (strcmp(name, "InitOnceBeginInitialize") == 0) + return (void *)kernel32::InitOnceBeginInitialize; + if (strcmp(name, "InitOnceComplete") == 0) + return (void *)kernel32::InitOnceComplete; + if (strcmp(name, "AcquireSRWLockShared") == 0) + return (void *)kernel32::AcquireSRWLockShared; + if (strcmp(name, "ReleaseSRWLockShared") == 0) + return (void *)kernel32::ReleaseSRWLockShared; + if (strcmp(name, "AcquireSRWLockExclusive") == 0) + return (void *)kernel32::AcquireSRWLockExclusive; + if (strcmp(name, "ReleaseSRWLockExclusive") == 0) + return (void *)kernel32::ReleaseSRWLockExclusive; + if (strcmp(name, "TryAcquireSRWLockExclusive") == 0) + return (void *)kernel32::TryAcquireSRWLockExclusive; + if (strcmp(name, "WaitForSingleObject") == 0) + return (void *)kernel32::WaitForSingleObject; + if (strcmp(name, "CreateMutexA") == 0) + return (void *)kernel32::CreateMutexA; + if (strcmp(name, "CreateMutexW") == 0) + return (void *)kernel32::CreateMutexW; + if (strcmp(name, "CreateEventA") == 0) + return (void *)kernel32::CreateEventA; + if (strcmp(name, "CreateEventW") == 0) + return (void *)kernel32::CreateEventW; + if (strcmp(name, "CreateSemaphoreA") == 0) + return (void *)kernel32::CreateSemaphoreA; + if (strcmp(name, "CreateSemaphoreW") == 0) + return (void *)kernel32::CreateSemaphoreW; + if (strcmp(name, "SetEvent") == 0) + return (void *)kernel32::SetEvent; + if (strcmp(name, "ResetEvent") == 0) + return (void *)kernel32::ResetEvent; + if (strcmp(name, "ReleaseMutex") == 0) + return (void *)kernel32::ReleaseMutex; + if (strcmp(name, "ReleaseSemaphore") == 0) + return (void *)kernel32::ReleaseSemaphore; + if (strcmp(name, "SetThreadAffinityMask") == 0) + return (void *)kernel32::SetThreadAffinityMask; + if (strcmp(name, "ResumeThread") == 0) + return (void *)kernel32::ResumeThread; + if (strcmp(name, "SetThreadPriority") == 0) + return (void *)kernel32::SetThreadPriority; + if (strcmp(name, "GetThreadPriority") == 0) + return (void *)kernel32::GetThreadPriority; // winbase.h - if (strcmp(name, "GlobalAlloc") == 0) return (void *) kernel32::GlobalAlloc; - if (strcmp(name, "GlobalReAlloc") == 0) return (void *) kernel32::GlobalReAlloc; - if (strcmp(name, "GlobalFree") == 0) return (void *) kernel32::GlobalFree; - if (strcmp(name, "GlobalFlags") == 0) return (void *) kernel32::GlobalFlags; - if (strcmp(name, "LocalAlloc") == 0) return (void *) kernel32::LocalAlloc; - if (strcmp(name, "LocalReAlloc") == 0) return (void *) kernel32::LocalReAlloc; - if (strcmp(name, "LocalFree") == 0) return (void *) kernel32::LocalFree; - if (strcmp(name, "LocalHandle") == 0) return (void *) kernel32::LocalHandle; - if (strcmp(name, "LocalLock") == 0) return (void *) kernel32::LocalLock; - if (strcmp(name, "LocalUnlock") == 0) return (void *) kernel32::LocalUnlock; - if (strcmp(name, "LocalSize") == 0) return (void *) kernel32::LocalSize; - if (strcmp(name, "LocalFlags") == 0) return (void *) kernel32::LocalFlags; - if (strcmp(name, "GetCurrentDirectoryA") == 0) return (void *) kernel32::GetCurrentDirectoryA; - if (strcmp(name, "GetCurrentDirectoryW") == 0) return (void *) kernel32::GetCurrentDirectoryW; - if (strcmp(name, "SetCurrentDirectoryA") == 0) return (void *) kernel32::SetCurrentDirectoryA; - if (strcmp(name, "SetCurrentDirectoryW") == 0) return (void *) kernel32::SetCurrentDirectoryW; - 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, "GetProcessAffinityMask") == 0) return (void *) kernel32::GetProcessAffinityMask; - if (strcmp(name, "SetProcessAffinityMask") == 0) return (void *) kernel32::SetProcessAffinityMask; - if (strcmp(name, "FormatMessageA") == 0) return (void *) kernel32::FormatMessageA; - if (strcmp(name, "GetComputerNameA") == 0) return (void *) kernel32::GetComputerNameA; - if (strcmp(name, "GetComputerNameW") == 0) return (void *) kernel32::GetComputerNameW; - if (strcmp(name, "EncodePointer") == 0) return (void *) kernel32::EncodePointer; - if (strcmp(name, "DecodePointer") == 0) return (void *) kernel32::DecodePointer; - if (strcmp(name, "SetDllDirectoryA") == 0) return (void *) kernel32::SetDllDirectoryA; - if (strcmp(name, "Sleep") == 0) return (void *) kernel32::Sleep; + if (strcmp(name, "GlobalAlloc") == 0) + return (void *)kernel32::GlobalAlloc; + if (strcmp(name, "GlobalReAlloc") == 0) + return (void *)kernel32::GlobalReAlloc; + if (strcmp(name, "GlobalFree") == 0) + return (void *)kernel32::GlobalFree; + if (strcmp(name, "GlobalFlags") == 0) + return (void *)kernel32::GlobalFlags; + if (strcmp(name, "LocalAlloc") == 0) + return (void *)kernel32::LocalAlloc; + if (strcmp(name, "LocalReAlloc") == 0) + return (void *)kernel32::LocalReAlloc; + if (strcmp(name, "LocalFree") == 0) + return (void *)kernel32::LocalFree; + if (strcmp(name, "LocalHandle") == 0) + return (void *)kernel32::LocalHandle; + if (strcmp(name, "LocalLock") == 0) + return (void *)kernel32::LocalLock; + if (strcmp(name, "LocalUnlock") == 0) + return (void *)kernel32::LocalUnlock; + if (strcmp(name, "LocalSize") == 0) + return (void *)kernel32::LocalSize; + if (strcmp(name, "LocalFlags") == 0) + return (void *)kernel32::LocalFlags; + if (strcmp(name, "GetCurrentDirectoryA") == 0) + return (void *)kernel32::GetCurrentDirectoryA; + if (strcmp(name, "GetCurrentDirectoryW") == 0) + return (void *)kernel32::GetCurrentDirectoryW; + if (strcmp(name, "SetCurrentDirectoryA") == 0) + return (void *)kernel32::SetCurrentDirectoryA; + if (strcmp(name, "SetCurrentDirectoryW") == 0) + return (void *)kernel32::SetCurrentDirectoryW; + 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, "GetProcessAffinityMask") == 0) + return (void *)kernel32::GetProcessAffinityMask; + if (strcmp(name, "SetProcessAffinityMask") == 0) + return (void *)kernel32::SetProcessAffinityMask; + if (strcmp(name, "FormatMessageA") == 0) + return (void *)kernel32::FormatMessageA; + if (strcmp(name, "GetComputerNameA") == 0) + return (void *)kernel32::GetComputerNameA; + if (strcmp(name, "GetComputerNameW") == 0) + return (void *)kernel32::GetComputerNameW; + if (strcmp(name, "EncodePointer") == 0) + return (void *)kernel32::EncodePointer; + if (strcmp(name, "DecodePointer") == 0) + return (void *)kernel32::DecodePointer; + if (strcmp(name, "SetDllDirectoryA") == 0) + return (void *)kernel32::SetDllDirectoryA; + if (strcmp(name, "Sleep") == 0) + return (void *)kernel32::Sleep; // processenv.h - if (strcmp(name, "GetCommandLineA") == 0) return (void *) kernel32::GetCommandLineA; - if (strcmp(name, "GetCommandLineW") == 0) return (void *) kernel32::GetCommandLineW; - if (strcmp(name, "GetEnvironmentStrings") == 0) return (void *) kernel32::GetEnvironmentStrings; - if (strcmp(name, "FreeEnvironmentStringsA") == 0) return (void *) kernel32::FreeEnvironmentStringsA; - if (strcmp(name, "GetEnvironmentStringsW") == 0) return (void *) kernel32::GetEnvironmentStringsW; - if (strcmp(name, "FreeEnvironmentStringsW") == 0) return (void *) kernel32::FreeEnvironmentStringsW; - if (strcmp(name, "GetEnvironmentVariableA") == 0) return (void *) kernel32::GetEnvironmentVariableA; - if (strcmp(name, "SetEnvironmentVariableA") == 0) return (void *) kernel32::SetEnvironmentVariableA; - if (strcmp(name, "SetEnvironmentVariableW") == 0) return (void *) kernel32::SetEnvironmentVariableW; - if (strcmp(name, "GetEnvironmentVariableW") == 0) return (void *) kernel32::GetEnvironmentVariableW; + if (strcmp(name, "GetCommandLineA") == 0) + return (void *)kernel32::GetCommandLineA; + if (strcmp(name, "GetCommandLineW") == 0) + return (void *)kernel32::GetCommandLineW; + if (strcmp(name, "GetEnvironmentStrings") == 0) + return (void *)kernel32::GetEnvironmentStrings; + if (strcmp(name, "FreeEnvironmentStringsA") == 0) + return (void *)kernel32::FreeEnvironmentStringsA; + if (strcmp(name, "GetEnvironmentStringsW") == 0) + return (void *)kernel32::GetEnvironmentStringsW; + if (strcmp(name, "FreeEnvironmentStringsW") == 0) + return (void *)kernel32::FreeEnvironmentStringsW; + if (strcmp(name, "GetEnvironmentVariableA") == 0) + return (void *)kernel32::GetEnvironmentVariableA; + if (strcmp(name, "SetEnvironmentVariableA") == 0) + return (void *)kernel32::SetEnvironmentVariableA; + if (strcmp(name, "SetEnvironmentVariableW") == 0) + return (void *)kernel32::SetEnvironmentVariableW; + if (strcmp(name, "GetEnvironmentVariableW") == 0) + return (void *)kernel32::GetEnvironmentVariableW; // console api - if (strcmp(name, "GetStdHandle") == 0) return (void *) kernel32::GetStdHandle; - if (strcmp(name, "SetStdHandle") == 0) return (void *) kernel32::SetStdHandle; - if (strcmp(name, "DuplicateHandle") == 0) return (void *) kernel32::DuplicateHandle; - if (strcmp(name, "CloseHandle") == 0) return (void *) kernel32::CloseHandle; - if (strcmp(name, "GetConsoleMode") == 0) return (void *) kernel32::GetConsoleMode; - if (strcmp(name, "SetConsoleMode") == 0) return (void *) kernel32::SetConsoleMode; - if (strcmp(name, "SetConsoleCtrlHandler") == 0) return (void *) kernel32::SetConsoleCtrlHandler; - if (strcmp(name, "GetConsoleScreenBufferInfo") == 0) return (void *) kernel32::GetConsoleScreenBufferInfo; - if (strcmp(name, "WriteConsoleW") == 0) return (void *) kernel32::WriteConsoleW; - if (strcmp(name, "GetConsoleOutputCP") == 0) return (void *) kernel32::GetConsoleOutputCP; - if (strcmp(name, "PeekConsoleInputA") == 0) return (void *) kernel32::PeekConsoleInputA; - if (strcmp(name, "ReadConsoleInputA") == 0) return (void *) kernel32::ReadConsoleInputA; + if (strcmp(name, "GetStdHandle") == 0) + return (void *)kernel32::GetStdHandle; + if (strcmp(name, "SetStdHandle") == 0) + return (void *)kernel32::SetStdHandle; + if (strcmp(name, "DuplicateHandle") == 0) + return (void *)kernel32::DuplicateHandle; + if (strcmp(name, "CloseHandle") == 0) + return (void *)kernel32::CloseHandle; + if (strcmp(name, "GetConsoleMode") == 0) + return (void *)kernel32::GetConsoleMode; + if (strcmp(name, "SetConsoleMode") == 0) + return (void *)kernel32::SetConsoleMode; + if (strcmp(name, "SetConsoleCtrlHandler") == 0) + return (void *)kernel32::SetConsoleCtrlHandler; + if (strcmp(name, "GetConsoleScreenBufferInfo") == 0) + return (void *)kernel32::GetConsoleScreenBufferInfo; + if (strcmp(name, "WriteConsoleW") == 0) + return (void *)kernel32::WriteConsoleW; + if (strcmp(name, "GetConsoleOutputCP") == 0) + return (void *)kernel32::GetConsoleOutputCP; + if (strcmp(name, "PeekConsoleInputA") == 0) + return (void *)kernel32::PeekConsoleInputA; + if (strcmp(name, "ReadConsoleInputA") == 0) + return (void *)kernel32::ReadConsoleInputA; // fileapi.h - if (strcmp(name, "GetFullPathNameA") == 0) return (void *) kernel32::GetFullPathNameA; - if (strcmp(name, "GetFullPathNameW") == 0) return (void *) kernel32::GetFullPathNameW; - if (strcmp(name, "GetShortPathNameA") == 0) return (void *) kernel32::GetShortPathNameA; - if (strcmp(name, "GetShortPathNameW") == 0) return (void *) kernel32::GetShortPathNameW; - if (strcmp(name, "FindFirstFileA") == 0) return (void *) kernel32::FindFirstFileA; - if (strcmp(name, "FindFirstFileW") == 0) return (void *) kernel32::FindFirstFileW; - if (strcmp(name, "FindFirstFileExA") == 0) return (void *) kernel32::FindFirstFileExA; - if (strcmp(name, "FindNextFileA") == 0) return (void *) kernel32::FindNextFileA; - if (strcmp(name, "FindNextFileW") == 0) return (void *) kernel32::FindNextFileW; - if (strcmp(name, "FindClose") == 0) return (void *) kernel32::FindClose; - if (strcmp(name, "GetFileAttributesA") == 0) return (void *) kernel32::GetFileAttributesA; - if (strcmp(name, "GetFileAttributesW") == 0) return (void *) kernel32::GetFileAttributesW; - if (strcmp(name, "WriteFile") == 0) return (void *) kernel32::WriteFile; - if (strcmp(name, "FlushFileBuffers") == 0) return (void *) kernel32::FlushFileBuffers; - if (strcmp(name, "ReadFile") == 0) return (void *) kernel32::ReadFile; - if (strcmp(name, "CreateFileA") == 0) return (void *) kernel32::CreateFileA; - if (strcmp(name, "CreateFileW") == 0) return (void *) kernel32::CreateFileW; - if (strcmp(name, "CreateFileMappingA") == 0) return (void *) kernel32::CreateFileMappingA; - if (strcmp(name, "CreateFileMappingW") == 0) return (void *) kernel32::CreateFileMappingW; - if (strcmp(name, "MapViewOfFile") == 0) return (void *) kernel32::MapViewOfFile; - if (strcmp(name, "UnmapViewOfFile") == 0) return (void *) kernel32::UnmapViewOfFile; - if (strcmp(name, "DeleteFileA") == 0) return (void *) kernel32::DeleteFileA; - if (strcmp(name, "DeleteFileW") == 0) return (void *) kernel32::DeleteFileW; - if (strcmp(name, "MoveFileA") == 0) return (void *) kernel32::MoveFileA; - if (strcmp(name, "MoveFileW") == 0) return (void *) kernel32::MoveFileW; - if (strcmp(name, "SetFilePointer") == 0) return (void *) kernel32::SetFilePointer; - if (strcmp(name, "SetFilePointerEx") == 0) return (void *) kernel32::SetFilePointerEx; - if (strcmp(name, "SetEndOfFile") == 0) return (void *) kernel32::SetEndOfFile; - if (strcmp(name, "CreateDirectoryA") == 0) return (void *) kernel32::CreateDirectoryA; - if (strcmp(name, "RemoveDirectoryA") == 0) return (void *) kernel32::RemoveDirectoryA; - if (strcmp(name, "SetFileAttributesA") == 0) return (void *) kernel32::SetFileAttributesA; - if (strcmp(name, "GetFileSize") == 0) return (void *) kernel32::GetFileSize; - if (strcmp(name, "GetFileTime") == 0) return (void *) kernel32::GetFileTime; - if (strcmp(name, "SetFileTime") == 0) return (void *) kernel32::SetFileTime; - if (strcmp(name, "GetFileType") == 0) return (void *) kernel32::GetFileType; - if (strcmp(name, "FileTimeToLocalFileTime") == 0) return (void *) kernel32::FileTimeToLocalFileTime; - if (strcmp(name, "LocalFileTimeToFileTime") == 0) return (void *) kernel32::LocalFileTimeToFileTime; - if (strcmp(name, "DosDateTimeToFileTime") == 0) return (void *) kernel32::DosDateTimeToFileTime; - if (strcmp(name, "FileTimeToDosDateTime") == 0) return (void *) kernel32::FileTimeToDosDateTime; - if (strcmp(name, "GetFileInformationByHandle") == 0) return (void *) kernel32::GetFileInformationByHandle; - if (strcmp(name, "GetTempFileNameA") == 0) return (void *) kernel32::GetTempFileNameA; - if (strcmp(name, "GetTempPathA") == 0) return (void *) kernel32::GetTempPathA; - if (strcmp(name, "GetLongPathNameA") == 0) return (void *) kernel32::GetLongPathNameA; - if (strcmp(name, "GetLongPathNameW") == 0) return (void *) kernel32::GetLongPathNameW; - if (strcmp(name, "GetDiskFreeSpaceA") == 0) return (void *) kernel32::GetDiskFreeSpaceA; - if (strcmp(name, "GetDiskFreeSpaceW") == 0) return (void *) kernel32::GetDiskFreeSpaceW; - if (strcmp(name, "GetDiskFreeSpaceExA") == 0) return (void*) kernel32::GetDiskFreeSpaceExA; - if (strcmp(name, "GetDiskFreeSpaceExW") == 0) return (void*) kernel32::GetDiskFreeSpaceExW; + if (strcmp(name, "GetFullPathNameA") == 0) + return (void *)kernel32::GetFullPathNameA; + if (strcmp(name, "GetFullPathNameW") == 0) + return (void *)kernel32::GetFullPathNameW; + if (strcmp(name, "GetShortPathNameA") == 0) + return (void *)kernel32::GetShortPathNameA; + if (strcmp(name, "GetShortPathNameW") == 0) + return (void *)kernel32::GetShortPathNameW; + if (strcmp(name, "FindFirstFileA") == 0) + return (void *)kernel32::FindFirstFileA; + if (strcmp(name, "FindFirstFileW") == 0) + return (void *)kernel32::FindFirstFileW; + if (strcmp(name, "FindFirstFileExA") == 0) + return (void *)kernel32::FindFirstFileExA; + if (strcmp(name, "FindNextFileA") == 0) + return (void *)kernel32::FindNextFileA; + if (strcmp(name, "FindNextFileW") == 0) + return (void *)kernel32::FindNextFileW; + if (strcmp(name, "FindClose") == 0) + return (void *)kernel32::FindClose; + if (strcmp(name, "GetFileAttributesA") == 0) + return (void *)kernel32::GetFileAttributesA; + if (strcmp(name, "GetFileAttributesW") == 0) + return (void *)kernel32::GetFileAttributesW; + if (strcmp(name, "WriteFile") == 0) + return (void *)kernel32::WriteFile; + if (strcmp(name, "FlushFileBuffers") == 0) + return (void *)kernel32::FlushFileBuffers; + if (strcmp(name, "ReadFile") == 0) + return (void *)kernel32::ReadFile; + if (strcmp(name, "CreateFileA") == 0) + return (void *)kernel32::CreateFileA; + if (strcmp(name, "CreateFileW") == 0) + return (void *)kernel32::CreateFileW; + if (strcmp(name, "CreateFileMappingA") == 0) + return (void *)kernel32::CreateFileMappingA; + if (strcmp(name, "CreateFileMappingW") == 0) + return (void *)kernel32::CreateFileMappingW; + if (strcmp(name, "MapViewOfFile") == 0) + return (void *)kernel32::MapViewOfFile; + if (strcmp(name, "UnmapViewOfFile") == 0) + return (void *)kernel32::UnmapViewOfFile; + if (strcmp(name, "DeleteFileA") == 0) + return (void *)kernel32::DeleteFileA; + if (strcmp(name, "DeleteFileW") == 0) + return (void *)kernel32::DeleteFileW; + if (strcmp(name, "MoveFileA") == 0) + return (void *)kernel32::MoveFileA; + if (strcmp(name, "MoveFileW") == 0) + return (void *)kernel32::MoveFileW; + if (strcmp(name, "SetFilePointer") == 0) + return (void *)kernel32::SetFilePointer; + if (strcmp(name, "SetFilePointerEx") == 0) + return (void *)kernel32::SetFilePointerEx; + if (strcmp(name, "SetEndOfFile") == 0) + return (void *)kernel32::SetEndOfFile; + if (strcmp(name, "CreateDirectoryA") == 0) + return (void *)kernel32::CreateDirectoryA; + if (strcmp(name, "RemoveDirectoryA") == 0) + return (void *)kernel32::RemoveDirectoryA; + if (strcmp(name, "SetFileAttributesA") == 0) + return (void *)kernel32::SetFileAttributesA; + if (strcmp(name, "GetFileSize") == 0) + return (void *)kernel32::GetFileSize; + if (strcmp(name, "GetFileTime") == 0) + return (void *)kernel32::GetFileTime; + if (strcmp(name, "SetFileTime") == 0) + return (void *)kernel32::SetFileTime; + if (strcmp(name, "GetFileType") == 0) + return (void *)kernel32::GetFileType; + if (strcmp(name, "FileTimeToLocalFileTime") == 0) + return (void *)kernel32::FileTimeToLocalFileTime; + if (strcmp(name, "LocalFileTimeToFileTime") == 0) + return (void *)kernel32::LocalFileTimeToFileTime; + if (strcmp(name, "DosDateTimeToFileTime") == 0) + return (void *)kernel32::DosDateTimeToFileTime; + if (strcmp(name, "FileTimeToDosDateTime") == 0) + return (void *)kernel32::FileTimeToDosDateTime; + if (strcmp(name, "GetFileInformationByHandle") == 0) + return (void *)kernel32::GetFileInformationByHandle; + if (strcmp(name, "GetTempFileNameA") == 0) + return (void *)kernel32::GetTempFileNameA; + if (strcmp(name, "GetTempPathA") == 0) + return (void *)kernel32::GetTempPathA; + if (strcmp(name, "GetLongPathNameA") == 0) + return (void *)kernel32::GetLongPathNameA; + if (strcmp(name, "GetLongPathNameW") == 0) + return (void *)kernel32::GetLongPathNameW; + if (strcmp(name, "GetDiskFreeSpaceA") == 0) + return (void *)kernel32::GetDiskFreeSpaceA; + if (strcmp(name, "GetDiskFreeSpaceW") == 0) + return (void *)kernel32::GetDiskFreeSpaceW; + if (strcmp(name, "GetDiskFreeSpaceExA") == 0) + return (void *)kernel32::GetDiskFreeSpaceExA; + if (strcmp(name, "GetDiskFreeSpaceExW") == 0) + return (void *)kernel32::GetDiskFreeSpaceExW; // sysinfoapi.h - if (strcmp(name, "GetSystemInfo") == 0) return (void *) kernel32::GetSystemInfo; - if (strcmp(name, "GetSystemTime") == 0) return (void *) kernel32::GetSystemTime; - if (strcmp(name, "GetLocalTime") == 0) return (void *) kernel32::GetLocalTime; - if (strcmp(name, "GetSystemTimeAsFileTime") == 0) return (void *) kernel32::GetSystemTimeAsFileTime; - if (strcmp(name, "GetTickCount") == 0) return (void *) kernel32::GetTickCount; - if (strcmp(name, "GetSystemDirectoryA") == 0) return (void *) kernel32::GetSystemDirectoryA; - if (strcmp(name, "GetWindowsDirectoryA") == 0) return (void *) kernel32::GetWindowsDirectoryA; - if (strcmp(name, "GetVersion") == 0) return (void *) kernel32::GetVersion; - if (strcmp(name, "GetVersionExA") == 0) return (void *) kernel32::GetVersionExA; + if (strcmp(name, "GetSystemInfo") == 0) + return (void *)kernel32::GetSystemInfo; + if (strcmp(name, "GetSystemTime") == 0) + return (void *)kernel32::GetSystemTime; + if (strcmp(name, "GetLocalTime") == 0) + return (void *)kernel32::GetLocalTime; + if (strcmp(name, "GetSystemTimeAsFileTime") == 0) + return (void *)kernel32::GetSystemTimeAsFileTime; + if (strcmp(name, "GetTickCount") == 0) + return (void *)kernel32::GetTickCount; + if (strcmp(name, "GetSystemDirectoryA") == 0) + return (void *)kernel32::GetSystemDirectoryA; + if (strcmp(name, "GetWindowsDirectoryA") == 0) + return (void *)kernel32::GetWindowsDirectoryA; + if (strcmp(name, "GetVersion") == 0) + return (void *)kernel32::GetVersion; + if (strcmp(name, "GetVersionExA") == 0) + return (void *)kernel32::GetVersionExA; // timezoneapi.h - if (strcmp(name, "SystemTimeToFileTime") == 0) return (void *) kernel32::SystemTimeToFileTime; - if (strcmp(name, "FileTimeToSystemTime") == 0) return (void *) kernel32::FileTimeToSystemTime; - if (strcmp(name, "GetTimeZoneInformation") == 0) return (void *) kernel32::GetTimeZoneInformation; + if (strcmp(name, "SystemTimeToFileTime") == 0) + return (void *)kernel32::SystemTimeToFileTime; + if (strcmp(name, "FileTimeToSystemTime") == 0) + return (void *)kernel32::FileTimeToSystemTime; + if (strcmp(name, "GetTimeZoneInformation") == 0) + return (void *)kernel32::GetTimeZoneInformation; // libloaderapi.h - if (strcmp(name, "GetModuleHandleA") == 0) return (void *) kernel32::GetModuleHandleA; - if (strcmp(name, "GetModuleHandleW") == 0) return (void *) kernel32::GetModuleHandleW; - if (strcmp(name, "GetModuleFileNameA") == 0) return (void *) kernel32::GetModuleFileNameA; - if (strcmp(name, "GetModuleFileNameW") == 0) return (void *) kernel32::GetModuleFileNameW; - if (strcmp(name, "LoadResource") == 0) return (void *) kernel32::LoadResource; - if (strcmp(name, "LockResource") == 0) return (void *) kernel32::LockResource; - if (strcmp(name, "SizeofResource") == 0) return (void *) kernel32::SizeofResource; - if (strcmp(name, "LoadLibraryA") == 0) return (void *) kernel32::LoadLibraryA; - if (strcmp(name, "LoadLibraryW") == 0) return (void *) kernel32::LoadLibraryW; - if (strcmp(name, "LoadLibraryExW") == 0) return (void *) kernel32::LoadLibraryExW; - if (strcmp(name, "DisableThreadLibraryCalls") == 0) return (void *) kernel32::DisableThreadLibraryCalls; - if (strcmp(name, "FreeLibrary") == 0) return (void *) kernel32::FreeLibrary; - if (strcmp(name, "GetProcAddress") == 0) return (void *) kernel32::GetProcAddress; + if (strcmp(name, "GetModuleHandleA") == 0) + return (void *)kernel32::GetModuleHandleA; + if (strcmp(name, "GetModuleHandleW") == 0) + return (void *)kernel32::GetModuleHandleW; + if (strcmp(name, "GetModuleFileNameA") == 0) + return (void *)kernel32::GetModuleFileNameA; + if (strcmp(name, "GetModuleFileNameW") == 0) + return (void *)kernel32::GetModuleFileNameW; + if (strcmp(name, "LoadResource") == 0) + return (void *)kernel32::LoadResource; + if (strcmp(name, "LockResource") == 0) + return (void *)kernel32::LockResource; + if (strcmp(name, "SizeofResource") == 0) + return (void *)kernel32::SizeofResource; + if (strcmp(name, "LoadLibraryA") == 0) + return (void *)kernel32::LoadLibraryA; + if (strcmp(name, "LoadLibraryW") == 0) + return (void *)kernel32::LoadLibraryW; + if (strcmp(name, "LoadLibraryExW") == 0) + return (void *)kernel32::LoadLibraryExW; + if (strcmp(name, "DisableThreadLibraryCalls") == 0) + return (void *)kernel32::DisableThreadLibraryCalls; + if (strcmp(name, "FreeLibrary") == 0) + return (void *)kernel32::FreeLibrary; + if (strcmp(name, "GetProcAddress") == 0) + return (void *)kernel32::GetProcAddress; // heapapi.h - if (strcmp(name, "HeapCreate") == 0) return (void *) kernel32::HeapCreate; - if (strcmp(name, "GetProcessHeap") == 0) return (void *) kernel32::GetProcessHeap; - if (strcmp(name, "HeapSetInformation") == 0) return (void *) kernel32::HeapSetInformation; - if (strcmp(name, "HeapAlloc") == 0) return (void *) kernel32::HeapAlloc; - if (strcmp(name, "HeapDestroy") == 0) return (void *) kernel32::HeapDestroy; - if (strcmp(name, "HeapReAlloc") == 0) return (void *) kernel32::HeapReAlloc; - if (strcmp(name, "HeapSize") == 0) return (void *) kernel32::HeapSize; - if (strcmp(name, "HeapFree") == 0) return (void *) kernel32::HeapFree; + if (strcmp(name, "HeapCreate") == 0) + return (void *)kernel32::HeapCreate; + if (strcmp(name, "GetProcessHeap") == 0) + return (void *)kernel32::GetProcessHeap; + if (strcmp(name, "HeapSetInformation") == 0) + return (void *)kernel32::HeapSetInformation; + if (strcmp(name, "HeapAlloc") == 0) + return (void *)kernel32::HeapAlloc; + if (strcmp(name, "HeapDestroy") == 0) + return (void *)kernel32::HeapDestroy; + if (strcmp(name, "HeapReAlloc") == 0) + return (void *)kernel32::HeapReAlloc; + if (strcmp(name, "HeapSize") == 0) + return (void *)kernel32::HeapSize; + if (strcmp(name, "HeapFree") == 0) + return (void *)kernel32::HeapFree; // memoryapi.h - if (strcmp(name, "VirtualAlloc") == 0) return (void *) kernel32::VirtualAlloc; - if (strcmp(name, "VirtualFree") == 0) return (void *) kernel32::VirtualFree; - if (strcmp(name, "VirtualProtect") == 0) return (void *) kernel32::VirtualProtect; - if (strcmp(name, "VirtualQuery") == 0) return (void *) kernel32::VirtualQuery; - if (strcmp(name, "GetProcessWorkingSetSize") == 0) return (void *) kernel32::GetProcessWorkingSetSize; - if (strcmp(name, "SetProcessWorkingSetSize") == 0) return (void *) kernel32::SetProcessWorkingSetSize; + if (strcmp(name, "VirtualAlloc") == 0) + return (void *)kernel32::VirtualAlloc; + if (strcmp(name, "VirtualFree") == 0) + return (void *)kernel32::VirtualFree; + if (strcmp(name, "VirtualProtect") == 0) + return (void *)kernel32::VirtualProtect; + if (strcmp(name, "VirtualQuery") == 0) + return (void *)kernel32::VirtualQuery; + if (strcmp(name, "GetProcessWorkingSetSize") == 0) + return (void *)kernel32::GetProcessWorkingSetSize; + if (strcmp(name, "SetProcessWorkingSetSize") == 0) + return (void *)kernel32::SetProcessWorkingSetSize; // stringapiset.h - if (strcmp(name, "WideCharToMultiByte") == 0) return (void *) kernel32::WideCharToMultiByte; - if (strcmp(name, "MultiByteToWideChar") == 0) return (void *) kernel32::MultiByteToWideChar; - if (strcmp(name, "GetStringTypeA") == 0) return (void *) kernel32::GetStringTypeA; - if (strcmp(name, "GetStringTypeW") == 0) return (void *) kernel32::GetStringTypeW; + if (strcmp(name, "WideCharToMultiByte") == 0) + return (void *)kernel32::WideCharToMultiByte; + if (strcmp(name, "MultiByteToWideChar") == 0) + return (void *)kernel32::MultiByteToWideChar; + if (strcmp(name, "GetStringTypeA") == 0) + return (void *)kernel32::GetStringTypeA; + if (strcmp(name, "GetStringTypeW") == 0) + return (void *)kernel32::GetStringTypeW; // profileapi.h - if (strcmp(name, "QueryPerformanceCounter") == 0) return (void *) kernel32::QueryPerformanceCounter; - if (strcmp(name, "QueryPerformanceFrequency") == 0) return (void *) kernel32::QueryPerformanceFrequency; + if (strcmp(name, "QueryPerformanceCounter") == 0) + return (void *)kernel32::QueryPerformanceCounter; + if (strcmp(name, "QueryPerformanceFrequency") == 0) + return (void *)kernel32::QueryPerformanceFrequency; // debugapi.h - if (strcmp(name, "IsDebuggerPresent") == 0) return (void *) kernel32::IsDebuggerPresent; + if (strcmp(name, "IsDebuggerPresent") == 0) + return (void *)kernel32::IsDebuggerPresent; // errhandlingapi.h - if (strcmp(name, "SetUnhandledExceptionFilter") == 0) return (void *) kernel32::SetUnhandledExceptionFilter; - if (strcmp(name, "UnhandledExceptionFilter") == 0) return (void *) kernel32::UnhandledExceptionFilter; - if (strcmp(name, "SetErrorMode") == 0) return (void*)kernel32::SetErrorMode; + if (strcmp(name, "SetUnhandledExceptionFilter") == 0) + return (void *)kernel32::SetUnhandledExceptionFilter; + if (strcmp(name, "UnhandledExceptionFilter") == 0) + return (void *)kernel32::UnhandledExceptionFilter; + if (strcmp(name, "SetErrorMode") == 0) + return (void *)kernel32::SetErrorMode; // interlockedapi.h - if (strcmp(name, "InitializeSListHead") == 0) return (void *) kernel32::InitializeSListHead; + if (strcmp(name, "InitializeSListHead") == 0) + return (void *)kernel32::InitializeSListHead; // winnt.h - if (strcmp(name, "RtlUnwind") == 0) return (void *) kernel32::RtlUnwind; - if (strcmp(name, "InterlockedIncrement") == 0) return (void *) kernel32::InterlockedIncrement; - if (strcmp(name, "InterlockedDecrement") == 0) return (void *) kernel32::InterlockedDecrement; - if (strcmp(name, "InterlockedExchange") == 0) return (void *) kernel32::InterlockedExchange; - if (strcmp(name, "InterlockedCompareExchange") == 0) return (void*) kernel32::InterlockedCompareExchange; + if (strcmp(name, "RtlUnwind") == 0) + return (void *)kernel32::RtlUnwind; + if (strcmp(name, "InterlockedIncrement") == 0) + return (void *)kernel32::InterlockedIncrement; + if (strcmp(name, "InterlockedDecrement") == 0) + return (void *)kernel32::InterlockedDecrement; + if (strcmp(name, "InterlockedExchange") == 0) + return (void *)kernel32::InterlockedExchange; + if (strcmp(name, "InterlockedCompareExchange") == 0) + return (void *)kernel32::InterlockedCompareExchange; // fibersapi.h - if (strcmp(name, "FlsAlloc") == 0) return (void *) kernel32::FlsAlloc; - if (strcmp(name, "FlsFree") == 0) return (void *) kernel32::FlsFree; - if (strcmp(name, "FlsSetValue") == 0) return (void *) kernel32::FlsSetValue; - if (strcmp(name, "FlsGetValue") == 0) return (void *) kernel32::FlsGetValue; + if (strcmp(name, "FlsAlloc") == 0) + return (void *)kernel32::FlsAlloc; + if (strcmp(name, "FlsFree") == 0) + return (void *)kernel32::FlsFree; + if (strcmp(name, "FlsSetValue") == 0) + return (void *)kernel32::FlsSetValue; + if (strcmp(name, "FlsGetValue") == 0) + return (void *)kernel32::FlsGetValue; // ioapiset.h - if (strcmp(name, "GetOverlappedResult") == 0) return (void *) kernel32::GetOverlappedResult; + if (strcmp(name, "GetOverlappedResult") == 0) + return (void *)kernel32::GetOverlappedResult; return 0; } diff --git a/dll/kernel32/debugapi.cpp b/dll/kernel32/debugapi.cpp new file mode 100644 index 0000000..bcae4c7 --- /dev/null +++ b/dll/kernel32/debugapi.cpp @@ -0,0 +1,13 @@ +#include "debugapi.h" +#include "common.h" +#include "errors.h" + +namespace kernel32 { + +BOOL WIN_FUNC IsDebuggerPresent() { + DEBUG_LOG("STUB: IsDebuggerPresent()\n"); + wibo::lastError = ERROR_SUCCESS; + return FALSE; +} + +} // namespace kernel32 diff --git a/dll/kernel32/debugapi.h b/dll/kernel32/debugapi.h new file mode 100644 index 0000000..7a74e44 --- /dev/null +++ b/dll/kernel32/debugapi.h @@ -0,0 +1,9 @@ +#pragma once + +#include "common.h" + +namespace kernel32 { + +BOOL WIN_FUNC IsDebuggerPresent(); + +} // namespace kernel32 diff --git a/dll/kernel32/errhandlingapi.cpp b/dll/kernel32/errhandlingapi.cpp index 9b28418..9e387ab 100644 --- a/dll/kernel32/errhandlingapi.cpp +++ b/dll/kernel32/errhandlingapi.cpp @@ -1,13 +1,18 @@ +#include "errhandlingapi.h" #include "common.h" -#include "kernel32.h" +#include "errors.h" namespace { + LPTOP_LEVEL_EXCEPTION_FILTER g_topLevelExceptionFilter = nullptr; UINT g_processErrorMode = 0; + } // namespace namespace kernel32 { +void setLastErrorFromErrno() { wibo::lastError = wibo::winErrorFromErrno(errno); } + DWORD WIN_FUNC GetLastError() { DEBUG_LOG("GetLastError() -> %u\n", wibo::lastError); return wibo::lastError; diff --git a/dll/kernel32/errhandlingapi.h b/dll/kernel32/errhandlingapi.h new file mode 100644 index 0000000..737284c --- /dev/null +++ b/dll/kernel32/errhandlingapi.h @@ -0,0 +1,44 @@ +#pragma once + +#include "common.h" + +constexpr DWORD EXCEPTION_MAXIMUM_PARAMETERS = 15; + +struct EXCEPTION_RECORD { + DWORD ExceptionCode; + DWORD ExceptionFlags; + EXCEPTION_RECORD *ExceptionRecord; + PVOID ExceptionAddress; + DWORD NumberParameters; + ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; +}; + +using PEXCEPTION_RECORD = EXCEPTION_RECORD *; +using PCONTEXT = void *; + +struct EXCEPTION_POINTERS { + PEXCEPTION_RECORD ExceptionRecord; + PCONTEXT ContextRecord; +}; + +using PEXCEPTION_POINTERS = EXCEPTION_POINTERS *; +using PVECTORED_EXCEPTION_HANDLER = LONG(WIN_FUNC *)(PEXCEPTION_POINTERS ExceptionInfo); +using LPTOP_LEVEL_EXCEPTION_FILTER = LONG(WIN_FUNC *)(PEXCEPTION_POINTERS ExceptionInfo); + +constexpr LONG EXCEPTION_CONTINUE_EXECUTION = static_cast(-1); +constexpr LONG EXCEPTION_CONTINUE_SEARCH = 0; +constexpr LONG EXCEPTION_EXECUTE_HANDLER = 1; + +namespace kernel32 { + +DWORD WIN_FUNC GetLastError(); +void WIN_FUNC SetLastError(DWORD dwErrCode); +void WIN_FUNC RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, + const ULONG_PTR *lpArguments); +PVOID WIN_FUNC AddVectoredExceptionHandler(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler); +LPTOP_LEVEL_EXCEPTION_FILTER WIN_FUNC +SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter); +LONG WIN_FUNC UnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo); +UINT WIN_FUNC SetErrorMode(UINT uMode); + +} // namespace kernel32 diff --git a/dll/kernel32/fibersapi.cpp b/dll/kernel32/fibersapi.cpp index 901090d..5b2e046 100644 --- a/dll/kernel32/fibersapi.cpp +++ b/dll/kernel32/fibersapi.cpp @@ -1,20 +1,24 @@ +#include "fibersapi.h" #include "common.h" #include "errors.h" -#include "kernel32.h" + +namespace { + +constexpr DWORD kMaxFlsValues = 0x100; +bool g_flsValuesUsed[kMaxFlsValues] = {false}; +LPVOID g_flsValues[kMaxFlsValues] = {nullptr}; + +} // namespace namespace kernel32 { -constexpr size_t MAX_FLS_VALUES = 0x100; -static bool flsValuesUsed[MAX_FLS_VALUES] = {false}; -static void *flsValues[MAX_FLS_VALUES]; - DWORD WIN_FUNC FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback) { DEBUG_LOG("FlsAlloc(%p)", lpCallback); // If the function succeeds, the return value is an FLS index initialized to zero. - for (size_t i = 0; i < MAX_FLS_VALUES; i++) { - if (flsValuesUsed[i] == false) { - flsValuesUsed[i] = true; - flsValues[i] = nullptr; + for (DWORD i = 0; i < kMaxFlsValues; i++) { + if (g_flsValuesUsed[i] == false) { + g_flsValuesUsed[i] = true; + g_flsValues[i] = nullptr; DEBUG_LOG(" -> %d\n", i); return i; } @@ -26,8 +30,8 @@ DWORD WIN_FUNC FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback) { BOOL WIN_FUNC FlsFree(DWORD dwFlsIndex) { DEBUG_LOG("FlsFree(%u)\n", dwFlsIndex); - if (dwFlsIndex >= 0 && dwFlsIndex < MAX_FLS_VALUES && flsValuesUsed[dwFlsIndex]) { - flsValuesUsed[dwFlsIndex] = false; + if (dwFlsIndex >= 0 && dwFlsIndex < kMaxFlsValues && g_flsValuesUsed[dwFlsIndex]) { + g_flsValuesUsed[dwFlsIndex] = false; return TRUE; } else { wibo::lastError = 1; @@ -38,8 +42,8 @@ BOOL WIN_FUNC FlsFree(DWORD dwFlsIndex) { PVOID WIN_FUNC FlsGetValue(DWORD dwFlsIndex) { VERBOSE_LOG("FlsGetValue(%u)\n", dwFlsIndex); PVOID result = nullptr; - if (dwFlsIndex >= 0 && dwFlsIndex < MAX_FLS_VALUES && flsValuesUsed[dwFlsIndex]) { - result = flsValues[dwFlsIndex]; + if (dwFlsIndex >= 0 && dwFlsIndex < kMaxFlsValues && g_flsValuesUsed[dwFlsIndex]) { + result = g_flsValues[dwFlsIndex]; // See https://learn.microsoft.com/en-us/windows/win32/api/fibersapi/nf-fibersapi-flsgetvalue wibo::lastError = ERROR_SUCCESS; } else { @@ -51,8 +55,8 @@ PVOID WIN_FUNC FlsGetValue(DWORD dwFlsIndex) { BOOL WIN_FUNC FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData) { VERBOSE_LOG("FlsSetValue(%u, %p)\n", dwFlsIndex, lpFlsData); - if (dwFlsIndex >= 0 && dwFlsIndex < MAX_FLS_VALUES && flsValuesUsed[dwFlsIndex]) { - flsValues[dwFlsIndex] = lpFlsData; + if (dwFlsIndex >= 0 && dwFlsIndex < kMaxFlsValues && g_flsValuesUsed[dwFlsIndex]) { + g_flsValues[dwFlsIndex] = lpFlsData; return TRUE; } else { wibo::lastError = 1; diff --git a/dll/kernel32/fibersapi.h b/dll/kernel32/fibersapi.h new file mode 100644 index 0000000..32af1f3 --- /dev/null +++ b/dll/kernel32/fibersapi.h @@ -0,0 +1,16 @@ +#pragma once + +#include "common.h" + +using PFLS_CALLBACK_FUNCTION = void (*)(void *); +constexpr DWORD FLS_OUT_OF_INDEXES = 0xFFFFFFFF; + +namespace kernel32 { + +DWORD WIN_FUNC FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback); +BOOL WIN_FUNC FlsFree(DWORD dwFlsIndex); +PVOID WIN_FUNC FlsGetValue(DWORD dwFlsIndex); +BOOL WIN_FUNC FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData); + +} // namespace kernel32 + diff --git a/dll/kernel32/fileapi.cpp b/dll/kernel32/fileapi.cpp new file mode 100644 index 0000000..5585026 --- /dev/null +++ b/dll/kernel32/fileapi.cpp @@ -0,0 +1,564 @@ +#include "fileapi.h" + +#include "errors.h" +#include "files.h" +#include "internal.h" +#include "strutil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +using random_shorts_engine = + std::independent_bits_engine; + +constexpr uintptr_t kPseudoFindHandleValue = 1; +const HANDLE kPseudoFindHandle = reinterpret_cast(kPseudoFindHandleValue); + +constexpr uint64_t kWindowsTicksPerSecond = 10000000ULL; +constexpr uint64_t kSecondsBetween1601And1970 = 11644473600ULL; +const FILETIME kDefaultFindFileTime = { + static_cast((kSecondsBetween1601And1970 * kWindowsTicksPerSecond) & 0xFFFFFFFFULL), + static_cast((kSecondsBetween1601And1970 * kWindowsTicksPerSecond) >> 32)}; + +struct FindFirstFileHandle { + std::filesystem::directory_iterator it; + std::filesystem::directory_iterator end; + std::string pattern; +}; + +struct FullPathInfo { + std::string path; + size_t filePartOffset = std::string::npos; +}; + +bool computeFullPath(const std::string &input, FullPathInfo &outInfo) { + bool endsWithSeparator = false; + if (!input.empty()) { + char last = input.back(); + endsWithSeparator = (last == '\\' || last == '/'); + } + + std::filesystem::path hostPath = files::pathFromWindows(input.c_str()); + std::error_code ec; + std::filesystem::path absPath = std::filesystem::absolute(hostPath, ec); + if (ec) { + errno = ec.value(); + kernel32::setLastErrorFromErrno(); + return false; + } + + std::string windowsPath = files::pathToWindows(absPath); + if (endsWithSeparator && !windowsPath.empty() && windowsPath.back() != '\\') { + windowsPath.push_back('\\'); + } + + if (!windowsPath.empty() && windowsPath.back() != '\\') { + size_t lastSlash = windowsPath.find_last_of('\\'); + if (lastSlash == std::string::npos) { + outInfo.filePartOffset = 0; + } else if (lastSlash + 1 < windowsPath.size()) { + outInfo.filePartOffset = lastSlash + 1; + } + } else { + outInfo.filePartOffset = std::string::npos; + } + + outInfo.path = std::move(windowsPath); + return true; +} + +inline bool isPseudoHandle(HANDLE handle) { return reinterpret_cast(handle) == kPseudoFindHandleValue; } + +inline void setCommonFindDataFields(WIN32_FIND_DATAA &data) { + data.ftCreationTime = kDefaultFindFileTime; + data.ftLastAccessTime = kDefaultFindFileTime; + data.ftLastWriteTime = kDefaultFindFileTime; + data.dwFileAttributes = 0; + data.nFileSizeHigh = 0; + data.nFileSizeLow = 0; + data.dwReserved0 = 0; + data.dwReserved1 = 0; + data.cFileName[0] = '\0'; + data.cAlternateFileName[0] = '\0'; +} + +inline void setCommonFindDataFields(WIN32_FIND_DATAW &data) { + data.ftCreationTime = kDefaultFindFileTime; + data.ftLastAccessTime = kDefaultFindFileTime; + data.ftLastWriteTime = kDefaultFindFileTime; + data.dwFileAttributes = 0; + data.nFileSizeHigh = 0; + data.nFileSizeLow = 0; + data.dwReserved0 = 0; + data.dwReserved1 = 0; + data.cFileName[0] = 0; + data.cAlternateFileName[0] = 0; +} + +DWORD computeAttributesAndSize(const std::filesystem::path &path, DWORD &sizeHigh, DWORD &sizeLow) { + std::error_code ec; + auto status = std::filesystem::status(path, ec); + uint64_t fileSize = 0; + DWORD attributes = 0; + if (status.type() == std::filesystem::file_type::directory) { + attributes |= FILE_ATTRIBUTE_DIRECTORY; + } + if (status.type() == std::filesystem::file_type::regular) { + attributes |= FILE_ATTRIBUTE_NORMAL; + fileSize = std::filesystem::file_size(path, ec); + } + sizeHigh = static_cast(fileSize >> 32); + sizeLow = static_cast(fileSize); + return attributes; +} + +void setFindFileDataFromPath(const std::filesystem::path &path, WIN32_FIND_DATAA &data) { + setCommonFindDataFields(data); + data.dwFileAttributes = computeAttributesAndSize(path, data.nFileSizeHigh, data.nFileSizeLow); + std::string fileName = path.filename().string(); + if (fileName.size() >= MAX_PATH) { + fileName.resize(MAX_PATH - 1); + } + std::strncpy(data.cFileName, fileName.c_str(), MAX_PATH); + data.cFileName[MAX_PATH - 1] = '\0'; + std::strncpy(data.cAlternateFileName, "8P3FMTFN.BAD", sizeof(data.cAlternateFileName)); + data.cAlternateFileName[sizeof(data.cAlternateFileName) - 1] = '\0'; +} + +void setFindFileDataFromPath(const std::filesystem::path &path, WIN32_FIND_DATAW &data) { + setCommonFindDataFields(data); + data.dwFileAttributes = computeAttributesAndSize(path, data.nFileSizeHigh, data.nFileSizeLow); + std::string fileName = path.filename().string(); + auto wideName = stringToWideString(fileName.c_str()); + size_t copyLen = std::min(MAX_PATH - 1, wstrlen(wideName.data())); + wstrncpy(data.cFileName, wideName.data(), copyLen); + data.cFileName[copyLen] = 0; + auto wideAlt = stringToWideString("8P3FMTFN.BAD"); + copyLen = std::min(sizeof(data.cAlternateFileName) / sizeof(data.cAlternateFileName[0]) - 1, + wstrlen(wideAlt.data())); + wstrncpy(data.cAlternateFileName, wideAlt.data(), copyLen); + data.cAlternateFileName[copyLen] = 0; +} + +bool nextMatch(FindFirstFileHandle &handle, std::filesystem::path &outPath) { + for (; handle.it != handle.end; ++handle.it) { + const auto current = *handle.it; + if (fnmatch(handle.pattern.c_str(), current.path().filename().c_str(), 0) == 0) { + outPath = current.path(); + ++handle.it; + return true; + } + } + return false; +} + +bool initializeEnumeration(const std::filesystem::path &parent, const std::string &pattern, FindFirstFileHandle &handle, + std::filesystem::path &firstMatch) { + if (pattern.empty()) { + return false; + } + handle = FindFirstFileHandle{std::filesystem::directory_iterator(parent), std::filesystem::directory_iterator(), + pattern}; + return nextMatch(handle, firstMatch); +} + +} // namespace + +namespace kernel32 { + +DWORD WIN_FUNC GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR *lpFilePart) { + DEBUG_LOG("GetFullPathNameA(%s, %u)\n", lpFileName ? lpFileName : "(null)", nBufferLength); + + if (lpFilePart) { + *lpFilePart = nullptr; + } + + if (!lpFileName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + FullPathInfo info; + if (!computeFullPath(lpFileName, info)) { + return 0; + } + + DEBUG_LOG(" -> %s\n", info.path.c_str()); + + const size_t pathLen = info.path.size(); + const auto required = static_cast(pathLen + 1); + + if (nBufferLength == 0) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return required; + } + + if (!lpBuffer) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + if (nBufferLength < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return required; + } + + memcpy(lpBuffer, info.path.c_str(), pathLen); + lpBuffer[pathLen] = '\0'; + + if (lpFilePart) { + if (info.filePartOffset != std::string::npos && info.filePartOffset < pathLen) { + *lpFilePart = lpBuffer + info.filePartOffset; + } else { + *lpFilePart = nullptr; + } + } + + wibo::lastError = ERROR_SUCCESS; + return static_cast(pathLen); +} + +DWORD WIN_FUNC GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart) { + DEBUG_LOG("GetFullPathNameW(%p, %u)\n", lpFileName, nBufferLength); + + if (lpFilePart) { + *lpFilePart = nullptr; + } + + if (!lpFileName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + std::string narrow = wideStringToString(lpFileName); + FullPathInfo info; + if (!computeFullPath(narrow, info)) { + return 0; + } + + DEBUG_LOG(" -> %s\n", info.path.c_str()); + + auto widePath = stringToWideString(info.path.c_str()); + const size_t wideLen = widePath.size(); + const auto required = static_cast(wideLen); + + if (nBufferLength == 0) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return required; + } + + if (!lpBuffer) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + if (nBufferLength < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return required; + } + + std::copy(widePath.begin(), widePath.end(), lpBuffer); + + if (lpFilePart) { + if (info.filePartOffset != std::string::npos && info.filePartOffset < info.path.size()) { + *lpFilePart = lpBuffer + info.filePartOffset; + } else { + *lpFilePart = nullptr; + } + } + + wibo::lastError = ERROR_SUCCESS; + return static_cast(wideLen - 1); +} + +DWORD WIN_FUNC GetShortPathNameA(LPCSTR lpszLongPath, LPSTR lpszShortPath, DWORD cchBuffer) { + DEBUG_LOG("GetShortPathNameA(%s)\n", lpszLongPath ? lpszLongPath : "(null)"); + if (!lpszLongPath || !lpszShortPath) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(lpszLongPath)); + std::string absStr = files::pathToWindows(absPath); + DWORD required = static_cast(absStr.length() + 1); + if (cchBuffer < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return required; + } + + strcpy(lpszShortPath, absStr.c_str()); + wibo::lastError = ERROR_SUCCESS; + return required - 1; +} + +DWORD WIN_FUNC GetShortPathNameW(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD cchBuffer) { + if (!lpszLongPath || !lpszShortPath) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string longPath = wideStringToString(lpszLongPath); + DEBUG_LOG("GetShortPathNameW(%s)\n", longPath.c_str()); + std::filesystem::path absPath = std::filesystem::absolute(files::pathFromWindows(longPath.c_str())); + std::string absStr = files::pathToWindows(absPath); + auto absStrW = stringToWideString(absStr.c_str()); + size_t len = wstrlen(absStrW.data()); + DWORD required = static_cast(len + 1); + if (cchBuffer < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return required; + } + wstrncpy(lpszShortPath, absStrW.data(), len + 1); + wibo::lastError = ERROR_SUCCESS; + return static_cast(len); +} + +UINT WIN_FUNC GetTempFileNameA(LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUnique, LPSTR lpTempFileName) { + DEBUG_LOG("GetTempFileNameA(%s, %s, %u)\n", lpPathName ? lpPathName : "(null)", + lpPrefixString ? lpPrefixString : "(null)", uUnique); + if (!lpPathName || !lpPrefixString || !lpTempFileName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + if (strlen(lpPathName) > MAX_PATH - 14) { + wibo::lastError = ERROR_BUFFER_OVERFLOW; + return 0; + } + char uniqueStr[20]; + std::filesystem::path path; + + if (uUnique == 0) { + std::random_device rd; + random_shorts_engine rse(rd()); + while (true) { + uUnique = rse(); + if (uUnique == 0) { + continue; + } + snprintf(uniqueStr, sizeof(uniqueStr), "%.3s%X.TMP", lpPrefixString, uUnique); + path = files::pathFromWindows(lpPathName) / uniqueStr; + int fd = open(path.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0644); + if (fd >= 0) { + close(fd); + break; + } + } + } else { + snprintf(uniqueStr, sizeof(uniqueStr), "%.3s%X.TMP", lpPrefixString, uUnique & 0xFFFF); + path = files::pathFromWindows(lpPathName) / uniqueStr; + } + std::string str = files::pathToWindows(path); + DEBUG_LOG(" -> %s\n", str.c_str()); + strncpy(lpTempFileName, str.c_str(), MAX_PATH); + lpTempFileName[MAX_PATH - 1] = '\0'; + wibo::lastError = ERROR_SUCCESS; + return uUnique; +} + +DWORD WIN_FUNC GetTempPathA(DWORD nBufferLength, LPSTR lpBuffer) { + DEBUG_LOG("GetTempPathA(%u, %p)\n", nBufferLength, lpBuffer); + + if (nBufferLength == 0 || lpBuffer == nullptr) { + wibo::lastError = ERROR_INVALID_PARAMETER; + DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n"); + return 0; + } + + const char *path = getenv("WIBO_TMP_DIR"); + if (!path) { + path = "Z:\\tmp\\"; + } + size_t len = strlen(path); + if (len + 1 > nBufferLength) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + DEBUG_LOG(" -> ERROR_INSUFFICIENT_BUFFER\n"); + return static_cast(len + 1); + } + + DEBUG_LOG(" -> %s\n", path); + strncpy(lpBuffer, path, nBufferLength); + lpBuffer[nBufferLength - 1] = '\0'; + wibo::lastError = ERROR_SUCCESS; + return static_cast(len); +} + +HANDLE WIN_FUNC FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData) { + DEBUG_LOG("FindFirstFileA(%s, %p)", lpFileName ? lpFileName : "(null)", lpFindFileData); + if (!lpFileName || !lpFindFileData) { + wibo::lastError = ERROR_INVALID_PARAMETER; + DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n"); + return INVALID_HANDLE_VALUE; + } + + std::filesystem::path hostPath = files::pathFromWindows(lpFileName); + DEBUG_LOG(" -> %s\n", hostPath.c_str()); + + std::error_code ec; + auto status = std::filesystem::status(hostPath, ec); + setCommonFindDataFields(*lpFindFileData); + if (status.type() == std::filesystem::file_type::regular) { + setFindFileDataFromPath(hostPath, *lpFindFileData); + wibo::lastError = ERROR_SUCCESS; + return kPseudoFindHandle; + } + + std::filesystem::path parent = hostPath.parent_path(); + if (parent.empty()) { + parent = "."; + } + if (!std::filesystem::exists(parent)) { + wibo::lastError = ERROR_PATH_NOT_FOUND; + return INVALID_HANDLE_VALUE; + } + + std::filesystem::path match; + auto *handle = new FindFirstFileHandle(); + if (!initializeEnumeration(parent, hostPath.filename().string(), *handle, match)) { + delete handle; + wibo::lastError = ERROR_FILE_NOT_FOUND; + return INVALID_HANDLE_VALUE; + } + + setFindFileDataFromPath(match, *lpFindFileData); + wibo::lastError = ERROR_SUCCESS; + return reinterpret_cast(handle); +} + +HANDLE WIN_FUNC FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) { + DEBUG_LOG("FindFirstFileW(%p, %p)", lpFileName, lpFindFileData); + if (!lpFileName || !lpFindFileData) { + wibo::lastError = ERROR_INVALID_PARAMETER; + DEBUG_LOG(" -> ERROR_INVALID_PARAMETER\n"); + return INVALID_HANDLE_VALUE; + } + + std::string narrowName = wideStringToString(lpFileName); + std::filesystem::path hostPath = files::pathFromWindows(narrowName.c_str()); + DEBUG_LOG(", %s -> %s\n", narrowName.c_str(), hostPath.c_str()); + + std::error_code ec; + auto status = std::filesystem::status(hostPath, ec); + setCommonFindDataFields(*lpFindFileData); + if (status.type() == std::filesystem::file_type::regular) { + setFindFileDataFromPath(hostPath, *lpFindFileData); + wibo::lastError = ERROR_SUCCESS; + return kPseudoFindHandle; + } + + std::filesystem::path parent = hostPath.parent_path(); + if (parent.empty()) { + parent = "."; + } + if (!std::filesystem::exists(parent)) { + wibo::lastError = ERROR_PATH_NOT_FOUND; + return INVALID_HANDLE_VALUE; + } + + std::filesystem::path match; + auto *handle = new FindFirstFileHandle(); + if (!initializeEnumeration(parent, hostPath.filename().string(), *handle, match)) { + delete handle; + wibo::lastError = ERROR_FILE_NOT_FOUND; + return INVALID_HANDLE_VALUE; + } + + setFindFileDataFromPath(match, *lpFindFileData); + wibo::lastError = ERROR_SUCCESS; + return reinterpret_cast(handle); +} + +HANDLE WIN_FUNC FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, + FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) { + DEBUG_LOG("FindFirstFileExA(%s, %d, %p, %d, %p, 0x%x) -> ", lpFileName ? lpFileName : "(null)", fInfoLevelId, + lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags); + (void)fInfoLevelId; + (void)fSearchOp; + (void)lpSearchFilter; + (void)dwAdditionalFlags; + return FindFirstFileA(lpFileName, static_cast(lpFindFileData)); +} + +BOOL WIN_FUNC FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData) { + DEBUG_LOG("FindNextFileA(%p, %p)\n", hFindFile, lpFindFileData); + if (!lpFindFileData) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + if (isPseudoHandle(hFindFile)) { + wibo::lastError = ERROR_NO_MORE_FILES; + return FALSE; + } + + auto *handle = reinterpret_cast(hFindFile); + if (!handle) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + + std::filesystem::path match; + if (!nextMatch(*handle, match)) { + wibo::lastError = ERROR_NO_MORE_FILES; + return FALSE; + } + + setFindFileDataFromPath(match, *lpFindFileData); + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData) { + DEBUG_LOG("FindNextFileW(%p, %p)\n", hFindFile, lpFindFileData); + if (!lpFindFileData) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + if (isPseudoHandle(hFindFile)) { + wibo::lastError = ERROR_NO_MORE_FILES; + return FALSE; + } + + auto *handle = reinterpret_cast(hFindFile); + if (!handle) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + + std::filesystem::path match; + if (!nextMatch(*handle, match)) { + wibo::lastError = ERROR_NO_MORE_FILES; + return FALSE; + } + + setFindFileDataFromPath(match, *lpFindFileData); + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC FindClose(HANDLE hFindFile) { + DEBUG_LOG("FindClose(%p)\n", hFindFile); + if (isPseudoHandle(hFindFile) || hFindFile == nullptr) { + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + auto *handle = reinterpret_cast(hFindFile); + if (!handle) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + delete handle; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +} // namespace kernel32 diff --git a/dll/kernel32/fileapi.h b/dll/kernel32/fileapi.h new file mode 100644 index 0000000..9d07385 --- /dev/null +++ b/dll/kernel32/fileapi.h @@ -0,0 +1,22 @@ +#pragma once + +#include "common.h" +#include "minwinbase.h" + +namespace kernel32 { + +DWORD WIN_FUNC GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR *lpFilePart); +DWORD WIN_FUNC GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart); +DWORD WIN_FUNC GetShortPathNameA(LPCSTR lpszLongPath, LPSTR lpszShortPath, DWORD cchBuffer); +DWORD WIN_FUNC GetShortPathNameW(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD cchBuffer); +UINT WIN_FUNC GetTempFileNameA(LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUnique, LPSTR lpTempFileName); +DWORD WIN_FUNC GetTempPathA(DWORD nBufferLength, LPSTR lpBuffer); +HANDLE WIN_FUNC FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData); +HANDLE WIN_FUNC FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData); +HANDLE WIN_FUNC FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, + FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags); +BOOL WIN_FUNC FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData); +BOOL WIN_FUNC FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData); +BOOL WIN_FUNC FindClose(HANDLE hFindFile); + +} // namespace kernel32 diff --git a/dll/kernel32/handleapi.cpp b/dll/kernel32/handleapi.cpp new file mode 100644 index 0000000..34adb41 --- /dev/null +++ b/dll/kernel32/handleapi.cpp @@ -0,0 +1,181 @@ +#include "handleapi.h" + +#include "errors.h" +#include "files.h" +#include "handles.h" +#include "internal.h" +#include "processes.h" + +#include +#include + +namespace advapi32 { +void releaseToken(void *tokenPtr); +} + +namespace kernel32 { + +BOOL WIN_FUNC DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, + LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions) { + DEBUG_LOG("DuplicateHandle(%p, %p, %p, %p, %x, %d, %x)\n", hSourceProcessHandle, hSourceHandle, + hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions); + (void)dwDesiredAccess; + (void)bInheritHandle; + (void)dwOptions; + if (!lpTargetHandle) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + auto validateProcessHandle = [&](HANDLE handle) -> bool { + uintptr_t raw = reinterpret_cast(handle); + if (raw == static_cast(-1)) { + return true; + } + auto data = handles::dataFromHandle(handle, false); + if (data.type != handles::TYPE_PROCESS || data.ptr == nullptr) { + return false; + } + auto *proc = reinterpret_cast(data.ptr); + return proc && proc->pid == getpid(); + }; + + if (!validateProcessHandle(hSourceProcessHandle) || !validateProcessHandle(hTargetProcessHandle)) { + DEBUG_LOG("DuplicateHandle: unsupported process handle combination (source=%p target=%p)\n", + hSourceProcessHandle, hTargetProcessHandle); + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + + auto file = files::fileHandleFromHandle(hSourceHandle); + if (file && (file->fp == stdin || file->fp == stdout || file->fp == stderr)) { + HANDLE handle = files::duplicateFileHandle(file, false); + DEBUG_LOG("DuplicateHandle: duplicated std handle -> %p\n", handle); + *lpTargetHandle = handle; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + uintptr_t sourceHandleRaw = reinterpret_cast(hSourceHandle); + if (sourceHandleRaw == static_cast(-1)) { + HANDLE handle = processes::allocProcessHandle(getpid()); + processes::Process *proc = processes::processFromHandle(handle, false); + if (proc) { + proc->exitCode = STILL_ACTIVE; + proc->forcedExitCode = STILL_ACTIVE; + proc->terminationRequested = false; + } + DEBUG_LOG("DuplicateHandle: created process handle for current process -> %p\n", handle); + *lpTargetHandle = handle; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + if (sourceHandleRaw == kPseudoCurrentThreadHandleValue) { + ThreadObject *obj = ensureCurrentThreadObject(); + if (obj) { + retainThreadObject(obj); + HANDLE handle = handles::allocDataHandle({handles::TYPE_THREAD, obj, 0}); + DEBUG_LOG("DuplicateHandle: duplicated pseudo current thread -> %p\n", handle); + *lpTargetHandle = handle; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + ThreadObject *syntheticObj = new ThreadObject(); + syntheticObj->thread = pthread_self(); + syntheticObj->finished = false; + syntheticObj->joined = false; + syntheticObj->detached = true; + syntheticObj->synthetic = true; + syntheticObj->exitCode = 0; + syntheticObj->refCount = 1; + syntheticObj->suspendCount = 0; + pthread_mutex_init(&syntheticObj->mutex, nullptr); + pthread_cond_init(&syntheticObj->cond, nullptr); + HANDLE handle = handles::allocDataHandle({handles::TYPE_THREAD, syntheticObj, 0}); + DEBUG_LOG("DuplicateHandle: created synthetic thread handle -> %p\n", handle); + *lpTargetHandle = handle; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + handles::Data data = handles::dataFromHandle(hSourceHandle, false); + if (data.type == handles::TYPE_PROCESS && data.ptr) { + auto *original = reinterpret_cast(data.ptr); + HANDLE handle = processes::allocProcessHandle(original->pid); + auto *copy = processes::processFromHandle(handle, false); + if (copy) { + *copy = *original; + } + DEBUG_LOG("DuplicateHandle: duplicated process handle -> %p\n", handle); + *lpTargetHandle = handle; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + if (data.type == handles::TYPE_THREAD && data.ptr) { + auto *threadObj = reinterpret_cast(data.ptr); + if (!retainThreadObject(threadObj)) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + HANDLE handle = handles::allocDataHandle({handles::TYPE_THREAD, threadObj, 0}); + DEBUG_LOG("DuplicateHandle: duplicated thread handle -> %p\n", handle); + *lpTargetHandle = handle; + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + DEBUG_LOG("DuplicateHandle: unsupported handle type for %p\n", hSourceHandle); + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; +} + +BOOL WIN_FUNC CloseHandle(HANDLE hObject) { + DEBUG_LOG("CloseHandle(%p)\n", hObject); + auto data = handles::dataFromHandle(hObject, true); + if (data.type == handles::TYPE_UNUSED || data.ptr == nullptr) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + bool success = true; + if (data.type == handles::TYPE_FILE) { + auto file = reinterpret_cast(data.ptr); + if (file) { + if (file->closeOnDestroy && file->fp && !(file->fp == stdin || file->fp == stdout || file->fp == stderr)) { + fclose(file->fp); + } + delete file; + } else { + success = false; + } + } else if (data.type == handles::TYPE_MAPPED) { + if (!closeFileMappingHandle(data.ptr)) { + success = false; + } + } else if (data.type == handles::TYPE_PROCESS) { + auto *proc = reinterpret_cast(data.ptr); + if (proc) { + delete proc; + } else { + success = false; + } + } else if (data.type == handles::TYPE_TOKEN) { + advapi32::releaseToken(data.ptr); + } else if (data.type == handles::TYPE_MUTEX) { + releaseMutexObject(reinterpret_cast(data.ptr)); + } else if (data.type == handles::TYPE_EVENT) { + releaseEventObject(reinterpret_cast(data.ptr)); + } else if (data.type == handles::TYPE_THREAD) { + releaseThreadObject(reinterpret_cast(data.ptr)); + } else if (data.type == handles::TYPE_SEMAPHORE) { + releaseSemaphoreObject(reinterpret_cast(data.ptr)); + } else { + success = false; + } + if (!success) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +} // namespace kernel32 diff --git a/dll/kernel32/handleapi.h b/dll/kernel32/handleapi.h new file mode 100644 index 0000000..c92e032 --- /dev/null +++ b/dll/kernel32/handleapi.h @@ -0,0 +1,11 @@ +#pragma once + +#include "common.h" + +namespace kernel32 { + +BOOL WIN_FUNC CloseHandle(HANDLE hObject); +BOOL WIN_FUNC DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, + LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions); + +} // namespace kernel32 diff --git a/dll/kernel32/heapapi.cpp b/dll/kernel32/heapapi.cpp new file mode 100644 index 0000000..de69309 --- /dev/null +++ b/dll/kernel32/heapapi.cpp @@ -0,0 +1,310 @@ +#include "heapapi.h" +#include "common.h" +#include "errors.h" +#include "handles.h" +#include "internal.h" + +#include +#include +#include +#include +#include + +namespace { + +struct HeapRecord { + mi_heap_t *heap = nullptr; + DWORD createFlags = 0; + SIZE_T initialSize = 0; + SIZE_T maximumSize = 0; + DWORD compatibility = 0; + bool isProcessHeap = false; +}; + +std::once_flag g_processHeapInitFlag; +HANDLE g_processHeapHandle = nullptr; +HeapRecord *g_processHeapRecord = nullptr; + +void ensureProcessHeapInitialized() { + std::call_once(g_processHeapInitFlag, []() { + mi_heap_t *heap = mi_heap_get_default(); + auto *record = new (std::nothrow) HeapRecord{}; + if (!record) { + return; + } + record->heap = heap; + record->isProcessHeap = true; + g_processHeapRecord = record; + g_processHeapHandle = handles::allocDataHandle({handles::TYPE_HEAP, record, 0}); + }); +} + +HeapRecord *activeHeapRecord(HANDLE hHeap) { + if (!hHeap) { + wibo::lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + ensureProcessHeapInitialized(); + auto data = handles::dataFromHandle(hHeap, false); + if (data.type != handles::TYPE_HEAP || data.ptr == nullptr) { + wibo::lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + wibo::lastError = ERROR_SUCCESS; + return static_cast(data.ptr); +} + +HeapRecord *popHeapRecord(HANDLE hHeap) { + ensureProcessHeapInitialized(); + auto preview = handles::dataFromHandle(hHeap, false); + if (preview.type != handles::TYPE_HEAP || preview.ptr == nullptr) { + wibo::lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + auto data = handles::dataFromHandle(hHeap, true); + wibo::lastError = ERROR_SUCCESS; + return static_cast(data.ptr); +} + +bool isExecutableHeap(const HeapRecord *record) { + return record && ((record->createFlags & HEAP_CREATE_ENABLE_EXECUTE) != 0); +} + +LPVOID heapAllocFromRecord(HeapRecord *record, DWORD dwFlags, SIZE_T dwBytes) { + if (!record) { + return nullptr; + } + if ((record->createFlags | dwFlags) & HEAP_GENERATE_EXCEPTIONS) { + DEBUG_LOG("HeapAlloc: HEAP_GENERATE_EXCEPTIONS not supported\n"); + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + const bool zeroMemory = (dwFlags & HEAP_ZERO_MEMORY) != 0; + const SIZE_T requestSize = std::max(1, dwBytes); + void *mem = zeroMemory ? mi_heap_zalloc(record->heap, requestSize) : mi_heap_malloc(record->heap, requestSize); + if (!mem) { + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + return nullptr; + } + if (isExecutableHeap(record)) { + kernel32::tryMarkExecutable(mem); + } + wibo::lastError = ERROR_SUCCESS; + return mem; +} + +} // namespace + +namespace kernel32 { + +HANDLE WIN_FUNC HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize) { + DEBUG_LOG("HeapCreate(%u, %zu, %zu)\n", flOptions, dwInitialSize, dwMaximumSize); + if (dwMaximumSize != 0 && dwInitialSize > dwMaximumSize) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + + mi_heap_t *heap = mi_heap_new(); + if (!heap) { + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + return nullptr; + } + + auto *record = new (std::nothrow) HeapRecord{}; + if (!record) { + mi_heap_delete(heap); + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + return nullptr; + } + + record->heap = heap; + record->createFlags = flOptions; + record->initialSize = dwInitialSize; + record->maximumSize = dwMaximumSize; + record->isProcessHeap = false; + + HANDLE handle = handles::allocDataHandle({handles::TYPE_HEAP, record, 0}); + wibo::lastError = ERROR_SUCCESS; + return handle; +} + +BOOL WIN_FUNC HeapDestroy(HANDLE hHeap) { + DEBUG_LOG("HeapDestroy(%p)\n", hHeap); + HeapRecord *record = activeHeapRecord(hHeap); + if (!record) { + return FALSE; + } + if (record->isProcessHeap) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + record = popHeapRecord(hHeap); + if (!record) { + return FALSE; + } + mi_heap_destroy(record->heap); + delete record; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +HANDLE WIN_FUNC GetProcessHeap() { + ensureProcessHeapInitialized(); + wibo::lastError = ERROR_SUCCESS; + DEBUG_LOG("GetProcessHeap() -> %p\n", g_processHeapHandle); + return g_processHeapHandle; +} + +BOOL WIN_FUNC HeapSetInformation(HANDLE HeapHandle, HEAP_INFORMATION_CLASS HeapInformationClass, PVOID HeapInformation, + SIZE_T HeapInformationLength) { + DEBUG_LOG("HeapSetInformation(%p, %d, %p, %zu)\n", HeapHandle, static_cast(HeapInformationClass), + HeapInformation, HeapInformationLength); + ensureProcessHeapInitialized(); + switch (HeapInformationClass) { + case HeapCompatibilityInformation: { + if (!HeapInformation || HeapInformationLength < sizeof(ULONG)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + HeapRecord *target = HeapHandle ? activeHeapRecord(HeapHandle) : g_processHeapRecord; + if (!target) { + return FALSE; + } + target->compatibility = *static_cast(HeapInformation); + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + case HeapEnableTerminationOnCorruption: + wibo::lastError = ERROR_SUCCESS; + return TRUE; + case HeapOptimizeResources: + wibo::lastError = ERROR_CALL_NOT_IMPLEMENTED; + return FALSE; + default: + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } +} + +LPVOID WIN_FUNC HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) { + DEBUG_LOG("HeapAlloc(%p, 0x%x, %zu) ", hHeap, dwFlags, dwBytes); + HeapRecord *record = activeHeapRecord(hHeap); + if (!record) { + DEBUG_LOG("-> NULL\n"); + return nullptr; + } + void *mem = heapAllocFromRecord(record, dwFlags, dwBytes); + DEBUG_LOG("-> %p\n", mem); + return mem; +} + +LPVOID WIN_FUNC HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes) { + DEBUG_LOG("HeapReAlloc(%p, 0x%x, %p, %zu) ", hHeap, dwFlags, lpMem, dwBytes); + HeapRecord *record = activeHeapRecord(hHeap); + if (!record) { + DEBUG_LOG("-> NULL\n"); + return nullptr; + } + if (lpMem == nullptr) { + void *alloc = heapAllocFromRecord(record, dwFlags, dwBytes); + DEBUG_LOG("-> %p (alloc)\n", alloc); + return alloc; + } + if (!mi_heap_check_owned(record->heap, lpMem)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + DEBUG_LOG("-> NULL (not owned)\n"); + return nullptr; + } + if ((record->createFlags | dwFlags) & HEAP_GENERATE_EXCEPTIONS) { + DEBUG_LOG("-> NULL (exceptions unsupported)\n"); + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + const bool inplaceOnly = (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY) != 0; + const bool zeroMemory = (dwFlags & HEAP_ZERO_MEMORY) != 0; + if (dwBytes == 0) { + if (!inplaceOnly) { + mi_free(lpMem); + wibo::lastError = ERROR_SUCCESS; + DEBUG_LOG("-> NULL (freed)\n"); + return nullptr; + } + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + DEBUG_LOG("-> NULL (zero size with in-place flag)\n"); + return nullptr; + } + + const SIZE_T requestSize = std::max(1, dwBytes); + const SIZE_T oldSize = mi_usable_size(lpMem); + if (inplaceOnly || requestSize <= oldSize) { + if (requestSize > oldSize) { + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + DEBUG_LOG("-> NULL (cannot grow in place)\n"); + return nullptr; + } + wibo::lastError = ERROR_SUCCESS; + DEBUG_LOG("-> %p (in-place)\n", lpMem); + return lpMem; + } + + void *ret = mi_heap_realloc(record->heap, lpMem, requestSize); + if (!ret) { + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + return nullptr; + } + if (zeroMemory && requestSize > oldSize) { + size_t newUsable = mi_usable_size(ret); + if (newUsable > oldSize) { + size_t zeroLen = std::min(newUsable, requestSize) - oldSize; + memset(static_cast(ret) + oldSize, 0, zeroLen); + } + } + if (isExecutableHeap(record)) { + tryMarkExecutable(ret); + } + wibo::lastError = ERROR_SUCCESS; + DEBUG_LOG("-> %p\n", ret); + return ret; +} + +SIZE_T WIN_FUNC HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem) { + DEBUG_LOG("HeapSize(%p, 0x%x, %p)\n", hHeap, dwFlags, lpMem); + (void)dwFlags; + HeapRecord *record = activeHeapRecord(hHeap); + if (!record) { + return static_cast(-1); + } + if (!lpMem) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return static_cast(-1); + } + if (!mi_heap_check_owned(record->heap, const_cast(lpMem))) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return static_cast(-1); + } + size_t size = mi_usable_size(lpMem); + wibo::lastError = ERROR_SUCCESS; + return static_cast(size); +} + +BOOL WIN_FUNC HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) { + DEBUG_LOG("HeapFree(%p, 0x%x, %p)\n", hHeap, dwFlags, lpMem); + (void)dwFlags; + if (lpMem == nullptr) { + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + HeapRecord *record = activeHeapRecord(hHeap); + if (!record) { + return FALSE; + } + if (!mi_heap_check_owned(record->heap, lpMem)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + mi_free(lpMem); + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +} // namespace kernel32 diff --git a/dll/kernel32/heapapi.h b/dll/kernel32/heapapi.h new file mode 100644 index 0000000..4efa532 --- /dev/null +++ b/dll/kernel32/heapapi.h @@ -0,0 +1,29 @@ +#pragma once + +#include "common.h" + +constexpr DWORD HEAP_NO_SERIALIZE = 0x00000001; +constexpr DWORD HEAP_GENERATE_EXCEPTIONS = 0x00000004; +constexpr DWORD HEAP_ZERO_MEMORY = 0x00000008; +constexpr DWORD HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010; +constexpr DWORD HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; + +enum HEAP_INFORMATION_CLASS { + HeapCompatibilityInformation = 0, + HeapEnableTerminationOnCorruption = 1, + HeapOptimizeResources = 3, +}; + +namespace kernel32 { + +HANDLE WIN_FUNC HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize); +BOOL WIN_FUNC HeapDestroy(HANDLE hHeap); +HANDLE WIN_FUNC GetProcessHeap(); +BOOL WIN_FUNC HeapSetInformation(HANDLE HeapHandle, HEAP_INFORMATION_CLASS HeapInformationClass, PVOID HeapInformation, + SIZE_T HeapInformationLength); +LPVOID WIN_FUNC HeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes); +LPVOID WIN_FUNC HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes); +SIZE_T WIN_FUNC HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem); +BOOL WIN_FUNC HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem); + +} // namespace kernel32 diff --git a/dll/kernel32/interlockedapi.cpp b/dll/kernel32/interlockedapi.cpp new file mode 100644 index 0000000..8ff7b57 --- /dev/null +++ b/dll/kernel32/interlockedapi.cpp @@ -0,0 +1,43 @@ +#include "interlockedapi.h" +#include "common.h" + +#include + +namespace kernel32 { + +LONG WIN_FUNC InterlockedIncrement(LONG volatile *Addend) { + VERBOSE_LOG("InterlockedIncrement(%p)\n", Addend); + return ++(*Addend); +} + +LONG WIN_FUNC InterlockedDecrement(LONG volatile *Addend) { + VERBOSE_LOG("InterlockedDecrement(%p)\n", Addend); + return --(*Addend); +} + +LONG WIN_FUNC InterlockedExchange(LONG volatile *Target, LONG Value) { + VERBOSE_LOG("InterlockedExchange(%p, %ld)\n", Target, static_cast(Value)); + LONG initial = *Target; + *Target = Value; + return initial; +} + +LONG WIN_FUNC InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG Comperand) { + VERBOSE_LOG("InterlockedCompareExchange(%p, %ld, %ld)\n", Destination, static_cast(Exchange), + static_cast(Comperand)); + LONG original = *Destination; + if (original == Comperand) { + *Destination = Exchange; + } + return original; +} + +void WIN_FUNC InitializeSListHead(PSLIST_HEADER ListHead) { + DEBUG_LOG("InitializeSListHead(%p)\n", ListHead); + if (!ListHead) { + return; + } + std::memset(ListHead, 0, sizeof(*ListHead)); +} + +} // namespace kernel32 diff --git a/dll/kernel32/interlockedapi.h b/dll/kernel32/interlockedapi.h new file mode 100644 index 0000000..ccb574c --- /dev/null +++ b/dll/kernel32/interlockedapi.h @@ -0,0 +1,27 @@ +#pragma once + +#include "common.h" + +namespace kernel32 { + +struct SLIST_ENTRY { + SLIST_ENTRY *Next; +}; + +using PSLIST_ENTRY = SLIST_ENTRY *; + +struct SLIST_HEADER { + SLIST_ENTRY *Head; + unsigned short Depth; + unsigned short Sequence; +}; + +using PSLIST_HEADER = SLIST_HEADER *; + +LONG WIN_FUNC InterlockedIncrement(LONG volatile *Addend); +LONG WIN_FUNC InterlockedDecrement(LONG volatile *Addend); +LONG WIN_FUNC InterlockedExchange(LONG volatile *Target, LONG Value); +LONG WIN_FUNC InterlockedCompareExchange(LONG volatile *Destination, LONG Exchange, LONG Comperand); +void WIN_FUNC InitializeSListHead(PSLIST_HEADER ListHead); + +} // namespace kernel32 diff --git a/dll/kernel32/internal.h b/dll/kernel32/internal.h index 782d740..7cf7115 100644 --- a/dll/kernel32/internal.h +++ b/dll/kernel32/internal.h @@ -1,7 +1,8 @@ #pragma once -#include "kernel32.h" +#include "common.h" #include +#include namespace kernel32 { @@ -45,10 +46,19 @@ struct SemaphoreObject { int refCount = 1; }; +inline constexpr uintptr_t kPseudoCurrentThreadHandleValue = static_cast(-2); + void releaseMutexObject(MutexObject *obj); void releaseEventObject(EventObject *obj); void releaseSemaphoreObject(SemaphoreObject *obj); void resetOverlappedEvent(OVERLAPPED *ov); void signalOverlappedEvent(OVERLAPPED *ov); +void tryMarkExecutable(void *mem); +void setLastErrorFromErrno(); +bool closeFileMappingHandle(void *mappingPtr); +ThreadObject *ensureCurrentThreadObject(); +ThreadObject *threadObjectFromHandle(HANDLE hThread); +ThreadObject *retainThreadObject(ThreadObject *obj); +void releaseThreadObject(ThreadObject *obj); } // namespace kernel32 diff --git a/dll/kernel32/kernel32.h b/dll/kernel32/kernel32.h deleted file mode 100644 index 71d2d95..0000000 --- a/dll/kernel32/kernel32.h +++ /dev/null @@ -1,193 +0,0 @@ -#pragma once - -#include "common.h" - -// errhandlingapi.h -constexpr DWORD EXCEPTION_MAXIMUM_PARAMETERS = 15; - -struct EXCEPTION_RECORD { - DWORD ExceptionCode; - DWORD ExceptionFlags; - EXCEPTION_RECORD *ExceptionRecord; - PVOID ExceptionAddress; - DWORD NumberParameters; - ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; -}; - -using PEXCEPTION_RECORD = EXCEPTION_RECORD *; -using PCONTEXT = void *; - -struct EXCEPTION_POINTERS { - PEXCEPTION_RECORD ExceptionRecord; - PCONTEXT ContextRecord; -}; - -using PEXCEPTION_POINTERS = EXCEPTION_POINTERS *; -using PVECTORED_EXCEPTION_HANDLER = LONG(WIN_FUNC *)(PEXCEPTION_POINTERS ExceptionInfo); -using LPTOP_LEVEL_EXCEPTION_FILTER = LONG(WIN_FUNC *)(PEXCEPTION_POINTERS ExceptionInfo); - -constexpr LONG EXCEPTION_CONTINUE_EXECUTION = static_cast(-1); -constexpr LONG EXCEPTION_CONTINUE_SEARCH = 0; -constexpr LONG EXCEPTION_EXECUTE_HANDLER = 1; - -// synchapi.h constants -constexpr DWORD WAIT_OBJECT_0 = 0x00000000; -constexpr DWORD WAIT_ABANDONED = 0x00000080; -constexpr DWORD WAIT_TIMEOUT = 0x00000102; -constexpr DWORD WAIT_FAILED = 0xFFFFFFFF; -constexpr DWORD INFINITE = 0xFFFFFFFF; - -// minwinbase.h -struct SECURITY_ATTRIBUTES { - DWORD nLength; - LPVOID lpSecurityDescriptor; - BOOL bInheritHandle; -}; - -using PSECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES *; -using LPSECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES *; - -// sysinfoapi.h -struct SYSTEM_INFO { - union { - DWORD dwOemId; - struct { - WORD wProcessorArchitecture; - WORD wReserved; - }; - }; - DWORD dwPageSize; - LPVOID lpMinimumApplicationAddress; - LPVOID lpMaximumApplicationAddress; - DWORD_PTR dwActiveProcessorMask; - DWORD dwNumberOfProcessors; - DWORD dwProcessorType; - DWORD dwAllocationGranularity; - WORD wProcessorLevel; - WORD wProcessorRevision; -}; - -using LPSYSTEM_INFO = SYSTEM_INFO *; - -// processthreadsapi.h -struct PROCESS_INFORMATION { - HANDLE hProcess; - HANDLE hThread; - DWORD dwProcessId; - DWORD dwThreadId; -}; - -using PPROCESS_INFORMATION = PROCESS_INFORMATION *; -using LPPROCESS_INFORMATION = PROCESS_INFORMATION *; - -struct STARTUPINFOA { - DWORD cb; - LPSTR lpReserved; - LPSTR lpDesktop; - LPSTR lpTitle; - DWORD dwX; - DWORD dwY; - DWORD dwXSize; - DWORD dwYSize; - DWORD dwXCountChars; - DWORD dwYCountChars; - DWORD dwFillAttribute; - DWORD dwFlags; - WORD wShowWindow; - WORD cbReserved2; - unsigned char *lpReserved2; - HANDLE hStdInput; - HANDLE hStdOutput; - HANDLE hStdError; -}; - -using LPSTARTUPINFOA = STARTUPINFOA *; - -struct STARTUPINFOW { - DWORD cb; - LPWSTR lpReserved; - LPWSTR lpDesktop; - LPWSTR lpTitle; - DWORD dwX; - DWORD dwY; - DWORD dwXSize; - DWORD dwYSize; - DWORD dwXCountChars; - DWORD dwYCountChars; - DWORD dwFillAttribute; - DWORD dwFlags; - WORD wShowWindow; - WORD cbReserved2; - unsigned char *lpReserved2; - HANDLE hStdInput; - HANDLE hStdOutput; - HANDLE hStdError; -}; - -using LPSTARTUPINFOW = STARTUPINFOW *; - -// fibersapi.h -using PFLS_CALLBACK_FUNCTION = void (*)(void *); -constexpr DWORD FLS_OUT_OF_INDEXES = 0xFFFFFFFF; - -namespace kernel32 { - -// fibersapi.h -DWORD WIN_FUNC FlsAlloc(PFLS_CALLBACK_FUNCTION lpCallback); -BOOL WIN_FUNC FlsFree(DWORD dwFlsIndex); -PVOID WIN_FUNC FlsGetValue(DWORD dwFlsIndex); -BOOL WIN_FUNC FlsSetValue(DWORD dwFlsIndex, PVOID lpFlsData); - -// errhandlingapi.h -DWORD WIN_FUNC GetLastError(); -void WIN_FUNC SetLastError(DWORD dwErrCode); -void WIN_FUNC RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, - const ULONG_PTR *lpArguments); -PVOID WIN_FUNC AddVectoredExceptionHandler(ULONG First, PVECTORED_EXCEPTION_HANDLER Handler); -LPTOP_LEVEL_EXCEPTION_FILTER WIN_FUNC -SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter); -LONG WIN_FUNC UnhandledExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo); -UINT WIN_FUNC SetErrorMode(UINT uMode); - -// wow64apiset.h -BOOL WIN_FUNC Wow64DisableWow64FsRedirection(PVOID *OldValue); -BOOL WIN_FUNC Wow64RevertWow64FsRedirection(PVOID OldValue); -BOOL WIN_FUNC IsWow64Process(HANDLE hProcess, PBOOL Wow64Process); - -// sysinfoapi.h -void WIN_FUNC GetSystemInfo(LPSYSTEM_INFO lpSystemInfo); - -// processthreadsapi.h -HANDLE WIN_FUNC GetCurrentProcess(); -DWORD WIN_FUNC GetCurrentProcessId(); -DWORD WIN_FUNC GetCurrentThreadId(); -BOOL WIN_FUNC GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR lpProcessAffinityMask, PDWORD_PTR lpSystemAffinityMask); -BOOL WIN_FUNC SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinityMask); -void WIN_FUNC ExitProcess(UINT uExitCode); -BOOL WIN_FUNC TerminateProcess(HANDLE hProcess, UINT uExitCode); -BOOL WIN_FUNC GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode); -BOOL WIN_FUNC CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, - LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation); -BOOL WIN_FUNC CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, - LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, - LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, - LPPROCESS_INFORMATION lpProcessInformation); - -// synchapi.h -HANDLE WIN_FUNC CreateMutexA(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCSTR lpName); -HANDLE WIN_FUNC CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName); -BOOL WIN_FUNC ReleaseMutex(HANDLE hMutex); -HANDLE WIN_FUNC CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName); -HANDLE WIN_FUNC CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName); -BOOL WIN_FUNC SetEvent(HANDLE hEvent); -BOOL WIN_FUNC ResetEvent(HANDLE hEvent); -HANDLE WIN_FUNC CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, - LPCSTR lpName); -HANDLE WIN_FUNC CreateSemaphoreW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, - LPCWSTR lpName); -BOOL WIN_FUNC ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG lpPreviousCount); -DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); - -} // namespace kernel32 diff --git a/dll/kernel32/libloaderapi.cpp b/dll/kernel32/libloaderapi.cpp new file mode 100644 index 0000000..4305cfb --- /dev/null +++ b/dll/kernel32/libloaderapi.cpp @@ -0,0 +1,269 @@ +#include "libloaderapi.h" + +#include "errors.h" +#include "files.h" +#include "resources.h" +#include "strutil.h" + +#include +#include +#include +#include + +namespace { + +HRSRC findResourceInternal(HMODULE hModule, const wibo::ResourceIdentifier &type, const wibo::ResourceIdentifier &name, + std::optional language) { + auto *exe = wibo::executableFromModule(hModule); + if (!exe) { + wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; + return nullptr; + } + wibo::ResourceLocation loc; + if (!exe->findResource(type, name, language, loc)) { + return nullptr; + } + return reinterpret_cast(const_cast(loc.dataEntry)); +} + +} // namespace + +namespace kernel32 { + +BOOL WIN_FUNC DisableThreadLibraryCalls(HMODULE hLibModule) { + DEBUG_LOG("DisableThreadLibraryCalls(%p)\n", hLibModule); + (void)hLibModule; + return TRUE; +} + +HMODULE WIN_FUNC GetModuleHandleA(LPCSTR lpModuleName) { + DEBUG_LOG("GetModuleHandleA(%s)\n", lpModuleName); + const auto *module = wibo::findLoadedModule(lpModuleName); + if (!module) { + wibo::lastError = ERROR_MOD_NOT_FOUND; + return nullptr; + } + wibo::lastError = ERROR_SUCCESS; + return module->handle; +} + +HMODULE WIN_FUNC GetModuleHandleW(LPCWSTR lpModuleName) { + DEBUG_LOG("GetModuleHandleW -> "); + if (lpModuleName) { + const auto lpModuleNameA = wideStringToString(lpModuleName); + return GetModuleHandleA(lpModuleNameA.c_str()); + } + return GetModuleHandleA(nullptr); +} + +DWORD WIN_FUNC GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize) { + DEBUG_LOG("GetModuleFileNameA(%p, %p, %u)\n", hModule, lpFilename, nSize); + if (!lpFilename) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + auto *info = wibo::moduleInfoFromHandle(hModule); + if (!info) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string path; + if (!info->resolvedPath.empty()) { + path = files::pathToWindows(info->resolvedPath); + } else { + path = info->originalName; + } + DEBUG_LOG("-> %s\n", path.c_str()); + if (nSize == 0) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return 0; + } + const size_t len = path.size(); + const size_t copyLen = std::min(len, static_cast(nSize - 1)); + std::memcpy(lpFilename, path.c_str(), copyLen); + if (copyLen < nSize) { + lpFilename[copyLen] = '\0'; + } + if (copyLen < len) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return nSize; + } + wibo::lastError = ERROR_SUCCESS; + return static_cast(copyLen); +} + +DWORD WIN_FUNC GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize) { + DEBUG_LOG("GetModuleFileNameW(%p, %s, %u)\n", hModule, wideStringToString(lpFilename).c_str(), nSize); + if (!lpFilename) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + auto *info = wibo::moduleInfoFromHandle(hModule); + if (!info) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string path; + if (!info->resolvedPath.empty()) { + path = files::pathToWindows(info->resolvedPath); + } else { + path = info->originalName; + } + if (nSize == 0) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return 0; + } + auto wide = stringToWideString(path.c_str()); + if (wide.empty()) { + wide.push_back(0); + } + const size_t len = wide.size() - 1; + const size_t copyLen = std::min(len, static_cast(nSize - 1)); + for (size_t i = 0; i < copyLen; ++i) { + lpFilename[i] = wide[i]; + } + if (copyLen < static_cast(nSize)) { + lpFilename[copyLen] = 0; + } + if (copyLen < len) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return nSize; + } + wibo::lastError = ERROR_SUCCESS; + return static_cast(copyLen); +} + +HRSRC WIN_FUNC FindResourceA(HMODULE hModule, LPCSTR lpName, LPCSTR lpType) { + DEBUG_LOG("FindResourceA %p %p %p\n", hModule, lpName, lpType); + auto type = wibo::resourceIdentifierFromAnsi(lpType); + auto name = wibo::resourceIdentifierFromAnsi(lpName); + return findResourceInternal(hModule, type, name, std::nullopt); +} + +HRSRC WIN_FUNC FindResourceExA(HMODULE hModule, LPCSTR lpType, LPCSTR lpName, WORD wLanguage) { + DEBUG_LOG("FindResourceExA %p %p %p %u\n", hModule, lpName, lpType, wLanguage); + auto type = wibo::resourceIdentifierFromAnsi(lpType); + auto name = wibo::resourceIdentifierFromAnsi(lpName); + return findResourceInternal(hModule, type, name, wLanguage); +} + +HRSRC WIN_FUNC FindResourceW(HMODULE hModule, LPCWSTR lpName, LPCWSTR lpType) { + DEBUG_LOG("FindResourceW %p\n", hModule); + auto type = wibo::resourceIdentifierFromWide(lpType); + auto name = wibo::resourceIdentifierFromWide(lpName); + return findResourceInternal(hModule, type, name, std::nullopt); +} + +HRSRC WIN_FUNC FindResourceExW(HMODULE hModule, LPCWSTR lpType, LPCWSTR lpName, WORD wLanguage) { + DEBUG_LOG("FindResourceExW %p %u\n", hModule, wLanguage); + auto type = wibo::resourceIdentifierFromWide(lpType); + auto name = wibo::resourceIdentifierFromWide(lpName); + return findResourceInternal(hModule, type, name, wLanguage); +} + +HGLOBAL WIN_FUNC LoadResource(HMODULE hModule, HRSRC hResInfo) { + DEBUG_LOG("LoadResource %p %p\n", hModule, hResInfo); + if (!hResInfo) { + wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; + return nullptr; + } + auto *exe = wibo::executableFromModule(hModule); + if (!exe || !exe->rsrcBase) { + wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; + return nullptr; + } + const auto *entry = reinterpret_cast(hResInfo); + if (!wibo::resourceEntryBelongsToExecutable(*exe, entry)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + return const_cast(exe->fromRVA(entry->offsetToData)); +} + +LPVOID WIN_FUNC LockResource(HGLOBAL hResData) { + DEBUG_LOG("LockResource(%p)\n", hResData); + return hResData; +} + +DWORD WIN_FUNC SizeofResource(HMODULE hModule, HRSRC hResInfo) { + DEBUG_LOG("SizeofResource(%p, %p)\n", hModule, hResInfo); + if (!hResInfo) { + wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; + return 0; + } + auto *exe = wibo::executableFromModule(hModule); + if (!exe || !exe->rsrcBase) { + wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; + return 0; + } + const auto *entry = reinterpret_cast(hResInfo); + if (!wibo::resourceEntryBelongsToExecutable(*exe, entry)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + return entry->size; +} + +HMODULE WIN_FUNC LoadLibraryA(LPCSTR lpLibFileName) { + DEBUG_LOG("LoadLibraryA(%s)\n", lpLibFileName); + const auto *info = wibo::loadModule(lpLibFileName); + if (!info) { + return nullptr; + } + wibo::lastError = ERROR_SUCCESS; + return info->handle; +} + +HMODULE WIN_FUNC LoadLibraryW(LPCWSTR lpLibFileName) { + if (!lpLibFileName) { + return nullptr; + } + auto filename = wideStringToString(lpLibFileName); + DEBUG_LOG("LoadLibraryW(%s)\n", filename.c_str()); + return LoadLibraryA(filename.c_str()); +} + +HMODULE WIN_FUNC LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags) { + assert(!hFile); + DEBUG_LOG("LoadLibraryExW(%x) -> ", dwFlags); + auto filename = wideStringToString(lpLibFileName); + return LoadLibraryA(filename.c_str()); +} + +BOOL WIN_FUNC FreeLibrary(HMODULE hLibModule) { + DEBUG_LOG("FreeLibrary(%p)\n", hLibModule); + auto *info = wibo::moduleInfoFromHandle(hLibModule); + if (!info) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + wibo::freeModule(info); + return TRUE; +} + +FARPROC WIN_FUNC GetProcAddress(HMODULE hModule, LPCSTR lpProcName) { + FARPROC result; + const auto info = wibo::moduleInfoFromHandle(hModule); + if (!info) { + DEBUG_LOG("GetProcAddress(%p) -> ERROR_INVALID_HANDLE\n", hModule); + wibo::lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + const auto proc = reinterpret_cast(lpProcName); + if (proc & ~0xFFFFu) { + DEBUG_LOG("GetProcAddress(%s, %s) ", info->normalizedName.c_str(), lpProcName); + result = wibo::resolveFuncByName(info, lpProcName); + } else { + DEBUG_LOG("GetProcAddress(%s, %u) ", info->normalizedName.c_str(), proc); + result = wibo::resolveFuncByOrdinal(info, static_cast(proc)); + } + DEBUG_LOG("-> %p\n", result); + if (!result) { + wibo::lastError = ERROR_PROC_NOT_FOUND; + } else { + wibo::lastError = ERROR_SUCCESS; + } + return result; +} + +} // namespace kernel32 diff --git a/dll/kernel32/libloaderapi.h b/dll/kernel32/libloaderapi.h new file mode 100644 index 0000000..deb967c --- /dev/null +++ b/dll/kernel32/libloaderapi.h @@ -0,0 +1,25 @@ +#pragma once + +#include "common.h" + +namespace kernel32 { + +BOOL WIN_FUNC DisableThreadLibraryCalls(HMODULE hLibModule); +HMODULE WIN_FUNC GetModuleHandleA(LPCSTR lpModuleName); +HMODULE WIN_FUNC GetModuleHandleW(LPCWSTR lpModuleName); +DWORD WIN_FUNC GetModuleFileNameA(HMODULE hModule, LPSTR lpFilename, DWORD nSize); +DWORD WIN_FUNC GetModuleFileNameW(HMODULE hModule, LPWSTR lpFilename, DWORD nSize); +HRSRC WIN_FUNC FindResourceA(HMODULE hModule, LPCSTR lpName, LPCSTR lpType); +HRSRC WIN_FUNC FindResourceExA(HMODULE hModule, LPCSTR lpType, LPCSTR lpName, WORD wLanguage); +HRSRC WIN_FUNC FindResourceW(HMODULE hModule, LPCWSTR lpName, LPCWSTR lpType); +HRSRC WIN_FUNC FindResourceExW(HMODULE hModule, LPCWSTR lpType, LPCWSTR lpName, WORD wLanguage); +HGLOBAL WIN_FUNC LoadResource(HMODULE hModule, HRSRC hResInfo); +LPVOID WIN_FUNC LockResource(HGLOBAL hResData); +DWORD WIN_FUNC SizeofResource(HMODULE hModule, HRSRC hResInfo); +HMODULE WIN_FUNC LoadLibraryA(LPCSTR lpLibFileName); +HMODULE WIN_FUNC LoadLibraryW(LPCWSTR lpLibFileName); +HMODULE WIN_FUNC LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags); +BOOL WIN_FUNC FreeLibrary(HMODULE hLibModule); +FARPROC WIN_FUNC GetProcAddress(HMODULE hModule, LPCSTR lpProcName); + +} // namespace kernel32 diff --git a/dll/kernel32/memoryapi.cpp b/dll/kernel32/memoryapi.cpp new file mode 100644 index 0000000..49d30bd --- /dev/null +++ b/dll/kernel32/memoryapi.cpp @@ -0,0 +1,858 @@ +#include "memoryapi.h" +#include "common.h" +#include "errors.h" +#include "files.h" +#include "handles.h" +#include "internal.h" +#include "strutil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace kernel32 { +int64_t getFileSize(HANDLE hFile); +} + +namespace { + +constexpr size_t kVirtualAllocationGranularity = 64 * 1024; + +struct MappingObject { + int fd = -1; + size_t maxSize = 0; + DWORD protect = 0; + bool anonymous = false; + bool closed = false; + size_t refCount = 0; +}; + +struct ViewInfo { + void *mapBase = nullptr; + size_t mapLength = 0; + MappingObject *owner = nullptr; +}; + +std::unordered_map g_viewInfo; + +void closeMappingIfPossible(MappingObject *mapping) { + if (!mapping) { + return; + } + if (mapping->fd != -1) { + close(mapping->fd); + mapping->fd = -1; + } + delete mapping; +} + +void tryReleaseMapping(MappingObject *mapping) { + if (!mapping) { + return; + } + if (mapping->closed && mapping->refCount == 0) { + closeMappingIfPossible(mapping); + } +} + +struct VirtualAllocation { + uintptr_t base = 0; + size_t size = 0; + DWORD allocationProtect = 0; + std::vector pageProtect; +}; + +std::map g_virtualAllocations; +std::mutex g_virtualAllocMutex; + +size_t systemPageSize() { + static size_t cached = []() { + long detected = sysconf(_SC_PAGESIZE); + if (detected <= 0) { + return static_cast(4096); + } + return static_cast(detected); + }(); + return cached; +} + +uintptr_t alignDown(uintptr_t value, size_t alignment) { + const uintptr_t mask = static_cast(alignment) - 1; + return value & ~mask; +} + +uintptr_t alignUp(uintptr_t value, size_t alignment) { + const uintptr_t mask = static_cast(alignment) - 1; + if (mask == std::numeric_limits::max()) { + return value; + } + if (value > std::numeric_limits::max() - mask) { + return std::numeric_limits::max(); + } + return (value + mask) & ~mask; +} + +bool addOverflows(uintptr_t base, size_t amount) { + return base > std::numeric_limits::max() - static_cast(amount); +} + +uintptr_t regionEnd(const VirtualAllocation ®ion) { return region.base + region.size; } + +bool rangeOverlapsLocked(uintptr_t base, size_t length) { + if (length == 0) { + return false; + } + if (addOverflows(base, length - 1)) { + return true; + } + uintptr_t end = base + length; + auto next = g_virtualAllocations.lower_bound(base); + if (next != g_virtualAllocations.begin()) { + auto prev = std::prev(next); + if (regionEnd(prev->second) > base) { + return true; + } + } + if (next != g_virtualAllocations.end() && next->second.base < end) { + return true; + } + return false; +} + +std::map::iterator findRegionIterator(uintptr_t address) { + auto it = g_virtualAllocations.upper_bound(address); + if (it == g_virtualAllocations.begin()) { + return g_virtualAllocations.end(); + } + --it; + if (address >= regionEnd(it->second)) { + return g_virtualAllocations.end(); + } + return it; +} + +VirtualAllocation *lookupRegion(uintptr_t address) { + auto it = findRegionIterator(address); + if (it == g_virtualAllocations.end()) { + return nullptr; + } + return &it->second; +} + +bool rangeWithinRegion(const VirtualAllocation ®ion, uintptr_t start, size_t length) { + if (length == 0) { + return start >= region.base && start <= regionEnd(region); + } + if (start < region.base) { + return false; + } + if (addOverflows(start, length)) { + return false; + } + return (start + length) <= regionEnd(region); +} + +void markCommitted(VirtualAllocation ®ion, uintptr_t start, size_t length, DWORD protect) { + if (length == 0) { + return; + } + const size_t pageSize = systemPageSize(); + const size_t firstPage = (start - region.base) / pageSize; + const size_t pageCount = length / pageSize; + for (size_t i = 0; i < pageCount; ++i) { + region.pageProtect[firstPage + i] = protect; + } +} + +void markDecommitted(VirtualAllocation ®ion, uintptr_t start, size_t length) { + if (length == 0) { + return; + } + const size_t pageSize = systemPageSize(); + const size_t firstPage = (start - region.base) / pageSize; + const size_t pageCount = length / pageSize; + for (size_t i = 0; i < pageCount; ++i) { + region.pageProtect[firstPage + i] = 0; + } +} + +void *alignedReserve(size_t length, int prot, int flags) { + const size_t granularity = kVirtualAllocationGranularity; + const size_t request = length + granularity; + void *raw = mmap(nullptr, request, prot, flags, -1, 0); + if (raw == MAP_FAILED) { + return MAP_FAILED; + } + uintptr_t rawAddr = reinterpret_cast(raw); + uintptr_t aligned = alignUp(rawAddr, granularity); + size_t front = aligned - rawAddr; + size_t back = (rawAddr + request) - (aligned + length); + if (front != 0) { + if (munmap(raw, front) != 0) { + munmap(raw, request); + return MAP_FAILED; + } + } + if (back != 0) { + if (munmap(reinterpret_cast(aligned + length), back) != 0) { + munmap(reinterpret_cast(aligned), length); + return MAP_FAILED; + } + } + return reinterpret_cast(aligned); +} + +int translateProtect(DWORD flProtect) { + switch (flProtect) { + case PAGE_NOACCESS: + return PROT_NONE; + case PAGE_READONLY: + return PROT_READ; + case PAGE_READWRITE: + case PAGE_WRITECOPY: + return PROT_READ | PROT_WRITE; + case PAGE_EXECUTE: + return PROT_EXEC; + case PAGE_EXECUTE_READ: + return PROT_READ | PROT_EXEC; + case PAGE_EXECUTE_READWRITE: + case PAGE_EXECUTE_WRITECOPY: + return PROT_READ | PROT_WRITE | PROT_EXEC; + default: + DEBUG_LOG("Unhandled flProtect: %u, defaulting to RW\n", flProtect); + return PROT_READ | PROT_WRITE; + } +} + +} // namespace + +namespace kernel32 { + +HANDLE WIN_FUNC CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName) { + DEBUG_LOG("CreateFileMappingA(%p, %p, %u, %u, %u, %s)\n", hFile, lpFileMappingAttributes, flProtect, + dwMaximumSizeHigh, dwMaximumSizeLow, lpName ? lpName : "(null)"); + (void)lpFileMappingAttributes; + (void)lpName; + + auto *mapping = new MappingObject(); + mapping->protect = flProtect; + + uint64_t size = (static_cast(dwMaximumSizeHigh) << 32) | dwMaximumSizeLow; + if (flProtect != PAGE_READONLY && flProtect != PAGE_READWRITE && flProtect != PAGE_WRITECOPY) { + DEBUG_LOG("CreateFileMappingA: unsupported protection 0x%x\n", flProtect); + wibo::lastError = ERROR_INVALID_PARAMETER; + closeMappingIfPossible(mapping); + return nullptr; + } + + if (hFile == INVALID_HANDLE_VALUE) { + mapping->anonymous = true; + mapping->fd = -1; + if (size == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + closeMappingIfPossible(mapping); + return nullptr; + } + mapping->maxSize = size; + } else { + FILE *fp = files::fpFromHandle(hFile); + if (!fp) { + wibo::lastError = ERROR_INVALID_HANDLE; + closeMappingIfPossible(mapping); + return nullptr; + } + int originalFd = fileno(fp); + if (originalFd == -1) { + setLastErrorFromErrno(); + closeMappingIfPossible(mapping); + return nullptr; + } + int dupFd = fcntl(originalFd, F_DUPFD_CLOEXEC, 0); + if (dupFd == -1) { + setLastErrorFromErrno(); + closeMappingIfPossible(mapping); + return nullptr; + } + mapping->fd = dupFd; + if (size == 0) { + int64_t fileSize = getFileSize(hFile); + if (fileSize < 0) { + closeMappingIfPossible(mapping); + return nullptr; + } + size = static_cast(fileSize); + } + mapping->maxSize = size; + } + + wibo::lastError = ERROR_SUCCESS; + return handles::allocDataHandle({handles::TYPE_MAPPED, mapping, static_cast(mapping->maxSize)}); +} + +HANDLE WIN_FUNC CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName) { + DEBUG_LOG("CreateFileMappingW -> "); + std::string name = wideStringToString(lpName); + return CreateFileMappingA(hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, + lpName ? name.c_str() : nullptr); +} + +LPVOID WIN_FUNC MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, + DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap) { + DEBUG_LOG("MapViewOfFile(%p, 0x%x, %u, %u, %zu)\n", hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, + dwFileOffsetLow, dwNumberOfBytesToMap); + + handles::Data data = handles::dataFromHandle(hFileMappingObject, false); + if (data.type != handles::TYPE_MAPPED) { + wibo::lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + auto *mapping = reinterpret_cast(data.ptr); + if (!mapping) { + wibo::lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + if (mapping->closed) { + wibo::lastError = ERROR_INVALID_HANDLE; + return nullptr; + } + + uint64_t offset = (static_cast(dwFileOffsetHigh) << 32) | dwFileOffsetLow; + if (mapping->anonymous && offset != 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + size_t maxSize = mapping->maxSize; + uint64_t length = static_cast(dwNumberOfBytesToMap); + if (length == 0) { + if (maxSize == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + if (offset > maxSize) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + length = maxSize - offset; + } + if (length == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + if (maxSize != 0 && offset + length > maxSize) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + + int prot = PROT_READ; + bool wantWrite = (dwDesiredAccess & FILE_MAP_WRITE) != 0; + bool wantExecute = (dwDesiredAccess & FILE_MAP_EXECUTE) != 0; + + if (mapping->protect == PAGE_READWRITE) { + if (wantWrite) { + prot |= PROT_WRITE; + } + } else { + if (wantWrite && (dwDesiredAccess & FILE_MAP_COPY) == 0) { + wibo::lastError = ERROR_ACCESS_DENIED; + return nullptr; + } + } + if (wantExecute) { + prot |= PROT_EXEC; + } + + int flags = 0; + if (mapping->anonymous) { + flags |= MAP_ANONYMOUS; + } + flags |= (dwDesiredAccess & FILE_MAP_COPY) ? MAP_PRIVATE : MAP_SHARED; + + size_t pageSize = static_cast(sysconf(_SC_PAGESIZE)); + off_t alignedOffset = mapping->anonymous ? 0 : static_cast(offset & ~static_cast(pageSize - 1)); + size_t offsetDelta = static_cast(offset - static_cast(alignedOffset)); + uint64_t requestedLength = length + offsetDelta; + if (requestedLength < length) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + size_t mapLength = static_cast(requestedLength); + if (static_cast(mapLength) != requestedLength) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + + int mmapFd = mapping->anonymous ? -1 : mapping->fd; + void *mapBase = mmap(nullptr, mapLength, prot, flags, mmapFd, alignedOffset); + if (mapBase == MAP_FAILED) { + setLastErrorFromErrno(); + return nullptr; + } + void *viewPtr = static_cast(mapBase) + offsetDelta; + g_viewInfo[viewPtr] = ViewInfo{mapBase, mapLength, mapping}; + mapping->refCount++; + wibo::lastError = ERROR_SUCCESS; + return viewPtr; +} + +BOOL WIN_FUNC UnmapViewOfFile(LPCVOID lpBaseAddress) { + DEBUG_LOG("UnmapViewOfFile(%p)\n", lpBaseAddress); + auto it = g_viewInfo.find(const_cast(lpBaseAddress)); + if (it == g_viewInfo.end()) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + ViewInfo info = it->second; + g_viewInfo.erase(it); + if (info.mapBase && info.mapLength) { + munmap(info.mapBase, info.mapLength); + } + if (info.owner && info.owner->refCount > 0) { + info.owner->refCount--; + tryReleaseMapping(info.owner); + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +bool closeFileMappingHandle(void *mappingPtr) { + auto *mapping = reinterpret_cast(mappingPtr); + if (!mapping) { + return false; + } + mapping->closed = true; + tryReleaseMapping(mapping); + return true; +} + +LPVOID WIN_FUNC VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) { + DEBUG_LOG("VirtualAlloc(%p, %zu, %u, %u)\n", lpAddress, dwSize, flAllocationType, flProtect); + + if (dwSize == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + + DWORD unsupportedFlags = flAllocationType & (MEM_WRITE_WATCH | MEM_PHYSICAL | MEM_LARGE_PAGES | MEM_RESET_UNDO); + if (unsupportedFlags != 0) { + DEBUG_LOG("VirtualAlloc unsupported flags: 0x%x\n", unsupportedFlags); + wibo::lastError = ERROR_NOT_SUPPORTED; + return nullptr; + } + + bool reserve = (flAllocationType & MEM_RESERVE) != 0; + bool commit = (flAllocationType & MEM_COMMIT) != 0; + bool reset = (flAllocationType & MEM_RESET) != 0; + + if (!reserve && commit && lpAddress == nullptr) { + reserve = true; + } + + if (reset) { + if (reserve || commit) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + if (!lpAddress) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return nullptr; + } + const size_t pageSize = systemPageSize(); + uintptr_t request = reinterpret_cast(lpAddress); + if (addOverflows(request, static_cast(dwSize))) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + uintptr_t start = alignDown(request, pageSize); + uintptr_t end = alignUp(request + static_cast(dwSize), pageSize); + size_t length = static_cast(end - start); + std::unique_lock lock(g_virtualAllocMutex); + VirtualAllocation *region = lookupRegion(start); + if (!region || !rangeWithinRegion(*region, start, length)) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return nullptr; + } +#ifdef MADV_FREE + int advice = MADV_FREE; +#else + int advice = MADV_DONTNEED; +#endif + if (madvise(reinterpret_cast(start), length, advice) != 0) { + wibo::lastError = wibo::winErrorFromErrno(errno); + return nullptr; + } + wibo::lastError = ERROR_SUCCESS; + return reinterpret_cast(start); + } + + if (!reserve && !commit) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + + const size_t pageSize = systemPageSize(); + std::unique_lock lock(g_virtualAllocMutex); + + if (reserve) { + uintptr_t base = 0; + size_t length = 0; + if (lpAddress) { + uintptr_t request = reinterpret_cast(lpAddress); + base = alignDown(request, kVirtualAllocationGranularity); + size_t offset = static_cast(request - base); + if (addOverflows(offset, static_cast(dwSize))) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + size_t span = static_cast(dwSize) + offset; + uintptr_t alignedSpan = alignUp(span, pageSize); + if (alignedSpan == std::numeric_limits::max()) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + length = static_cast(alignedSpan); + if (length == 0 || rangeOverlapsLocked(base, length)) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return nullptr; + } + } else { + uintptr_t aligned = alignUp(static_cast(dwSize), pageSize); + if (aligned == std::numeric_limits::max() || aligned == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + length = static_cast(aligned); + } + const int prot = commit ? translateProtect(flProtect) : PROT_NONE; + int flags = MAP_PRIVATE | MAP_ANONYMOUS; + if (!commit) { + flags |= MAP_NORESERVE; + } + void *result = MAP_FAILED; + if (lpAddress) { +#ifdef MAP_FIXED_NOREPLACE + flags |= MAP_FIXED_NOREPLACE; +#else + flags |= MAP_FIXED; +#endif + result = mmap(reinterpret_cast(base), length, prot, flags, -1, 0); + } else { + result = alignedReserve(length, prot, flags); + } + if (result == MAP_FAILED) { + wibo::lastError = wibo::winErrorFromErrno(errno); + return nullptr; + } + if (reinterpret_cast(result) >= 0x80000000) { + munmap(result, length); + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + return nullptr; + } + uintptr_t actualBase = reinterpret_cast(result); + VirtualAllocation allocation{}; + allocation.base = actualBase; + allocation.size = length; + allocation.allocationProtect = flProtect; + allocation.pageProtect.assign(length / pageSize, commit ? flProtect : 0); + g_virtualAllocations[actualBase] = std::move(allocation); + wibo::lastError = ERROR_SUCCESS; + return result; + } + + uintptr_t request = reinterpret_cast(lpAddress); + if (addOverflows(request, static_cast(dwSize))) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + uintptr_t start = alignDown(request, pageSize); + uintptr_t end = alignUp(request + static_cast(dwSize), pageSize); + size_t length = static_cast(end - start); + if (length == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + VirtualAllocation *region = lookupRegion(start); + if (!region || !rangeWithinRegion(*region, start, length)) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return nullptr; + } + const size_t pageCount = length / pageSize; + std::vector> committedRuns; + committedRuns.reserve(pageCount); + for (size_t i = 0; i < pageCount; ++i) { + size_t pageIndex = ((start - region->base) / pageSize) + i; + if (pageIndex >= region->pageProtect.size()) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return nullptr; + } + if (region->pageProtect[pageIndex] != 0) { + continue; + } + uintptr_t runBase = start + i * pageSize; + size_t runLength = pageSize; + while (i + 1 < pageCount) { + size_t nextIndex = ((start - region->base) / pageSize) + i + 1; + if (region->pageProtect[nextIndex] != 0) { + break; + } + ++i; + runLength += pageSize; + } + committedRuns.emplace_back(runBase, runLength); + } + for (const auto &run : committedRuns) { + void *result = mmap(reinterpret_cast(run.first), run.second, translateProtect(flProtect), + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); + if (result == MAP_FAILED) { + wibo::lastError = wibo::winErrorFromErrno(errno); + return nullptr; + } + markCommitted(*region, run.first, run.second, flProtect); + } + wibo::lastError = ERROR_SUCCESS; + DEBUG_LOG("VirtualAlloc commit success -> %p\n", reinterpret_cast(start)); + return reinterpret_cast(start); +} + +BOOL WIN_FUNC VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) { + DEBUG_LOG("VirtualFree(%p, %zu, %u)\n", lpAddress, dwSize, dwFreeType); + if (!lpAddress) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return FALSE; + } + + if ((dwFreeType & (MEM_COALESCE_PLACEHOLDERS | MEM_PRESERVE_PLACEHOLDER)) != 0) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + + const bool release = (dwFreeType & MEM_RELEASE) != 0; + const bool decommit = (dwFreeType & MEM_DECOMMIT) != 0; + if (release == decommit) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + const size_t pageSize = systemPageSize(); + std::unique_lock lock(g_virtualAllocMutex); + + if (release) { + uintptr_t base = reinterpret_cast(lpAddress); + auto exact = g_virtualAllocations.find(base); + if (exact == g_virtualAllocations.end()) { + auto containing = findRegionIterator(base); + if (dwSize != 0 && containing != g_virtualAllocations.end()) { + wibo::lastError = ERROR_INVALID_PARAMETER; + } else { + wibo::lastError = ERROR_INVALID_ADDRESS; + } + return FALSE; + } + if (dwSize != 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + size_t length = exact->second.size; + g_virtualAllocations.erase(exact); + lock.unlock(); + if (munmap(lpAddress, length) != 0) { + wibo::lastError = wibo::winErrorFromErrno(errno); + return FALSE; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + uintptr_t request = reinterpret_cast(lpAddress); + auto regionIt = findRegionIterator(request); + if (regionIt == g_virtualAllocations.end()) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return FALSE; + } + VirtualAllocation ®ion = regionIt->second; + uintptr_t start = alignDown(request, pageSize); + uintptr_t end = 0; + if (dwSize == 0) { + if (request != region.base) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + start = region.base; + end = region.base + region.size; + } else { + if (addOverflows(request, static_cast(dwSize))) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + end = alignUp(request + static_cast(dwSize), pageSize); + } + if (end <= start) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + size_t length = static_cast(end - start); + if (!rangeWithinRegion(region, start, length)) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return FALSE; + } + void *result = mmap(reinterpret_cast(start), length, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_NORESERVE, -1, 0); + if (result == MAP_FAILED) { + wibo::lastError = wibo::winErrorFromErrno(errno); + return FALSE; + } + markDecommitted(region, start, length); + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) { + DEBUG_LOG("VirtualProtect(%p, %zu, %u)\n", lpAddress, dwSize, flNewProtect); + if (!lpAddress || dwSize == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + const size_t pageSize = systemPageSize(); + uintptr_t request = reinterpret_cast(lpAddress); + uintptr_t start = alignDown(request, pageSize); + uintptr_t end = alignUp(request + static_cast(dwSize), pageSize); + if (end <= start) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + std::unique_lock lock(g_virtualAllocMutex); + VirtualAllocation *region = lookupRegion(start); + if (!region || !rangeWithinRegion(*region, start, static_cast(end - start))) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return FALSE; + } + + const size_t firstPage = (start - region->base) / pageSize; + const size_t pageCount = (end - start) / pageSize; + if (pageCount == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + DWORD previousProtect = region->pageProtect[firstPage]; + if (previousProtect == 0) { + wibo::lastError = ERROR_NOACCESS; + return FALSE; + } + for (size_t i = 0; i < pageCount; ++i) { + if (region->pageProtect[firstPage + i] == 0) { + wibo::lastError = ERROR_NOACCESS; + return FALSE; + } + } + + int prot = translateProtect(flNewProtect); + if (mprotect(reinterpret_cast(start), end - start, prot) != 0) { + wibo::lastError = wibo::winErrorFromErrno(errno); + return FALSE; + } + for (size_t i = 0; i < pageCount; ++i) { + region->pageProtect[firstPage + i] = flNewProtect; + } + lock.unlock(); + + if (lpflOldProtect) { + *lpflOldProtect = previousProtect; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +SIZE_T WIN_FUNC VirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) { + DEBUG_LOG("VirtualQuery(%p, %p, %zu)\n", lpAddress, lpBuffer, dwLength); + if (!lpBuffer || dwLength < sizeof(MEMORY_BASIC_INFORMATION) || !lpAddress) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + std::memset(lpBuffer, 0, sizeof(MEMORY_BASIC_INFORMATION)); + const size_t pageSize = systemPageSize(); + uintptr_t request = reinterpret_cast(lpAddress); + uintptr_t pageBase = alignDown(request, pageSize); + + std::unique_lock lock(g_virtualAllocMutex); + VirtualAllocation *region = lookupRegion(pageBase); + if (!region) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return 0; + } + + const size_t pageIndex = (pageBase - region->base) / pageSize; + if (pageIndex >= region->pageProtect.size()) { + wibo::lastError = ERROR_INVALID_ADDRESS; + return 0; + } + const bool committed = region->pageProtect[pageIndex] != 0; + uintptr_t blockStart = pageBase; + uintptr_t blockEnd = pageBase + pageSize; + while (blockStart > region->base) { + size_t idx = (blockStart - region->base) / pageSize - 1; + bool pageCommitted = region->pageProtect[idx] != 0; + if (pageCommitted != committed) { + break; + } + blockStart -= pageSize; + } + while (blockEnd < region->base + region->size) { + size_t idx = (blockEnd - region->base) / pageSize; + bool pageCommitted = region->pageProtect[idx] != 0; + if (pageCommitted != committed) { + break; + } + blockEnd += pageSize; + } + + lpBuffer->BaseAddress = reinterpret_cast(blockStart); + lpBuffer->AllocationBase = reinterpret_cast(region->base); + lpBuffer->AllocationProtect = region->allocationProtect != 0 ? region->allocationProtect : PAGE_NOACCESS; + lpBuffer->RegionSize = blockEnd - blockStart; + lpBuffer->State = committed ? MEM_COMMIT : MEM_RESERVE; + lpBuffer->Protect = committed ? region->pageProtect[pageIndex] : 0; + lpBuffer->Type = MEM_PRIVATE; + lock.unlock(); + wibo::lastError = ERROR_SUCCESS; + return sizeof(MEMORY_BASIC_INFORMATION); +} + +BOOL WIN_FUNC GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T lpMinimumWorkingSetSize, + PSIZE_T lpMaximumWorkingSetSize) { + DEBUG_LOG("GetProcessWorkingSetSize(%p, %p, %p)\n", hProcess, lpMinimumWorkingSetSize, lpMaximumWorkingSetSize); + (void)hProcess; + if (!lpMinimumWorkingSetSize || !lpMaximumWorkingSetSize) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + *lpMinimumWorkingSetSize = 32 * 1024 * 1024; // 32 MiB stub + *lpMaximumWorkingSetSize = 128 * 1024 * 1024; // 128 MiB stub + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T dwMinimumWorkingSetSize, + SIZE_T dwMaximumWorkingSetSize) { + DEBUG_LOG("SetProcessWorkingSetSize(%p, %zu, %zu)\n", hProcess, dwMinimumWorkingSetSize, dwMaximumWorkingSetSize); + (void)hProcess; + (void)dwMinimumWorkingSetSize; + (void)dwMaximumWorkingSetSize; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +} // namespace kernel32 diff --git a/dll/kernel32/memoryapi.h b/dll/kernel32/memoryapi.h new file mode 100644 index 0000000..09be4b4 --- /dev/null +++ b/dll/kernel32/memoryapi.h @@ -0,0 +1,66 @@ +#pragma once + +#include "common.h" +#include "minwinbase.h" + +// Allocation type flags +constexpr DWORD MEM_COMMIT = 0x00001000; +constexpr DWORD MEM_RESERVE = 0x00002000; +constexpr DWORD MEM_DECOMMIT = 0x00004000; +constexpr DWORD MEM_RELEASE = 0x00008000; +constexpr DWORD MEM_RESET = 0x00080000; +constexpr DWORD MEM_RESET_UNDO = 0x01000000; +constexpr DWORD MEM_TOP_DOWN = 0x00100000; +constexpr DWORD MEM_WRITE_WATCH = 0x00200000; +constexpr DWORD MEM_PHYSICAL = 0x00400000; +constexpr DWORD MEM_PRIVATE = 0x00020000; +constexpr DWORD MEM_LARGE_PAGES = 0x20000000; +constexpr DWORD MEM_COALESCE_PLACEHOLDERS = 0x00000001; +constexpr DWORD MEM_PRESERVE_PLACEHOLDER = 0x00000002; + +// Page protection constants +constexpr DWORD PAGE_NOACCESS = 0x01; +constexpr DWORD PAGE_READONLY = 0x02; +constexpr DWORD PAGE_READWRITE = 0x04; +constexpr DWORD PAGE_WRITECOPY = 0x08; +constexpr DWORD PAGE_EXECUTE = 0x10; +constexpr DWORD PAGE_EXECUTE_READ = 0x20; +constexpr DWORD PAGE_EXECUTE_READWRITE = 0x40; +constexpr DWORD PAGE_EXECUTE_WRITECOPY = 0x80; + +constexpr DWORD FILE_MAP_COPY = 0x00000001; +constexpr DWORD FILE_MAP_WRITE = 0x00000002; +constexpr DWORD FILE_MAP_READ = 0x00000004; +constexpr DWORD FILE_MAP_EXECUTE = 0x00000020; + +struct MEMORY_BASIC_INFORMATION { + PVOID BaseAddress; + PVOID AllocationBase; + DWORD AllocationProtect; + SIZE_T RegionSize; + DWORD State; + DWORD Protect; + DWORD Type; +}; + +using PMEMORY_BASIC_INFORMATION = MEMORY_BASIC_INFORMATION *; + +namespace kernel32 { + +HANDLE WIN_FUNC CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName); +HANDLE WIN_FUNC CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName); +LPVOID WIN_FUNC MapViewOfFile(HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, + DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap); +BOOL WIN_FUNC UnmapViewOfFile(LPCVOID lpBaseAddress); + +LPVOID WIN_FUNC VirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); +BOOL WIN_FUNC VirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); +BOOL WIN_FUNC VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); +SIZE_T WIN_FUNC VirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength); +BOOL WIN_FUNC GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T lpMinimumWorkingSetSize, + PSIZE_T lpMaximumWorkingSetSize); +BOOL WIN_FUNC SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T dwMinimumWorkingSetSize, SIZE_T dwMaximumWorkingSetSize); + +} // namespace kernel32 diff --git a/dll/kernel32/minwinbase.h b/dll/kernel32/minwinbase.h new file mode 100644 index 0000000..07ce777 --- /dev/null +++ b/dll/kernel32/minwinbase.h @@ -0,0 +1,92 @@ +#pragma once + +#include "common.h" + +struct SECURITY_ATTRIBUTES { + DWORD nLength; + LPVOID lpSecurityDescriptor; + BOOL bInheritHandle; +}; + +using PSECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES *; +using LPSECURITY_ATTRIBUTES = SECURITY_ATTRIBUTES *; + +struct FILETIME { + DWORD dwLowDateTime; + DWORD dwHighDateTime; +}; + +using PFILETIME = FILETIME *; +using LPFILETIME = FILETIME *; + +struct SYSTEMTIME { + WORD wYear; + WORD wMonth; + WORD wDayOfWeek; + WORD wDay; + WORD wHour; + WORD wMinute; + WORD wSecond; + WORD wMilliseconds; +}; + +using PSYSTEMTIME = SYSTEMTIME *; +using LPSYSTEMTIME = SYSTEMTIME *; + +enum FINDEX_INFO_LEVELS { FindExInfoStandard, FindExInfoBasic, FindExInfoMaxInfoLevel }; + +enum FINDEX_SEARCH_OPS { + FindExSearchNameMatch, + FindExSearchLimitToDirectories, + FindExSearchLimitToDevices, + FindExSearchMaxSearchOp +}; + +constexpr DWORD FILE_ATTRIBUTE_DIRECTORY = 0x00000010; +constexpr DWORD FILE_ATTRIBUTE_NORMAL = 0x00000080; + +constexpr DWORD MAX_PATH = 260; + +struct WIN32_FIND_DATAA { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + DWORD dwReserved0; + DWORD dwReserved1; + char cFileName[MAX_PATH]; + char cAlternateFileName[14]; +}; + +struct WIN32_FIND_DATAW { + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; + DWORD dwReserved0; + DWORD dwReserved1; + WCHAR cFileName[MAX_PATH]; + WCHAR cAlternateFileName[14]; +}; + +using PWIN32_FIND_DATAA = WIN32_FIND_DATAA *; +using LPWIN32_FIND_DATAA = WIN32_FIND_DATAA *; +using PWIN32_FIND_DATAW = WIN32_FIND_DATAW *; +using LPWIN32_FIND_DATAW = WIN32_FIND_DATAW *; + +typedef struct _OVERLAPPED { + ULONG_PTR Internal; + ULONG_PTR InternalHigh; + union { + struct { + DWORD Offset; + DWORD OffsetHigh; + }; + PVOID Pointer; + }; + HANDLE hEvent; +} OVERLAPPED, *LPOVERLAPPED; diff --git a/dll/kernel32/processenv.cpp b/dll/kernel32/processenv.cpp new file mode 100644 index 0000000..70178a5 --- /dev/null +++ b/dll/kernel32/processenv.cpp @@ -0,0 +1,258 @@ +#include "processenv.h" + +#include "errors.h" +#include "files.h" +#include "internal.h" +#include "strutil.h" + +#include +#include +#include +#include +#include +#include + +namespace { + +std::string convertEnvValueForWindows(const std::string &name, const char *rawValue) { + if (!rawValue) { + return {}; + } + if (strcasecmp(name.c_str(), "PATH") != 0) { + return rawValue; + } + std::string converted = files::hostPathListToWindows(rawValue); + return converted.empty() ? std::string(rawValue) : converted; +} + +std::string convertEnvValueToHost(const std::string &name, const char *rawValue) { + if (!rawValue) { + return {}; + } + if (strcasecmp(name.c_str(), "PATH") != 0) { + return rawValue; + } + std::string converted = files::windowsPathListToHost(rawValue); + return converted.empty() ? std::string(rawValue) : converted; +} + +} // namespace + +namespace kernel32 { + +LPSTR WIN_FUNC GetCommandLineA() { + DEBUG_LOG("GetCommandLineA() -> %s\n", wibo::commandLine.c_str()); + wibo::lastError = ERROR_SUCCESS; + return const_cast(wibo::commandLine.c_str()); +} + +LPWSTR WIN_FUNC GetCommandLineW() { + DEBUG_LOG("GetCommandLineW() -> %s\n", wideStringToString(wibo::commandLineW.data()).c_str()); + wibo::lastError = ERROR_SUCCESS; + return wibo::commandLineW.data(); +} + +HANDLE WIN_FUNC GetStdHandle(DWORD nStdHandle) { + DEBUG_LOG("GetStdHandle(%u)\n", nStdHandle); + return files::getStdHandle(nStdHandle); +} + +BOOL WIN_FUNC SetStdHandle(DWORD nStdHandle, HANDLE hHandle) { + DEBUG_LOG("SetStdHandle(%u, %p)\n", nStdHandle, hHandle); + return files::setStdHandle(nStdHandle, hHandle); +} + +LPCH WIN_FUNC GetEnvironmentStrings() { + DEBUG_LOG("GetEnvironmentStrings()\n"); + + size_t bufSize = 0; + char **work = environ; + + while (*work) { + bufSize += strlen(*work) + 1; + work++; + } + bufSize++; + + char *buffer = static_cast(mi_malloc(bufSize)); + if (!buffer) { + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + return nullptr; + } + char *ptr = buffer; + work = environ; + + while (*work) { + size_t strSize = strlen(*work); + memcpy(ptr, *work, strSize); + ptr[strSize] = 0; + ptr += strSize + 1; + work++; + } + *ptr = 0; + + wibo::lastError = ERROR_SUCCESS; + return buffer; +} + +LPWCH WIN_FUNC GetEnvironmentStringsW() { + DEBUG_LOG("GetEnvironmentStringsW()\n"); + + size_t bufSizeW = 0; + char **work = environ; + + while (*work) { + bufSizeW += strlen(*work) + 1; + work++; + } + bufSizeW++; + + uint16_t *buffer = static_cast(mi_malloc(bufSizeW * sizeof(uint16_t))); + if (!buffer) { + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + return nullptr; + } + uint16_t *ptr = buffer; + work = environ; + + while (*work) { + VERBOSE_LOG("-> %s\n", *work); + size_t strSize = strlen(*work); + for (size_t i = 0; i < strSize; i++) { + *ptr++ = static_cast((*work)[i]); + } + *ptr++ = 0; + work++; + } + *ptr = 0; + + wibo::lastError = ERROR_SUCCESS; + return buffer; +} + +BOOL WIN_FUNC FreeEnvironmentStringsA(LPCH penv) { + DEBUG_LOG("FreeEnvironmentStringsA(%p)\n", penv); + if (!penv) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + free(penv); + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC FreeEnvironmentStringsW(LPWCH penv) { + DEBUG_LOG("FreeEnvironmentStringsW(%p)\n", penv); + if (!penv) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + free(penv); + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +DWORD WIN_FUNC GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize) { + DEBUG_LOG("GetEnvironmentVariableA(%s, %p, %u)\n", lpName ? lpName : "(null)", lpBuffer, nSize); + if (!lpName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + const char *rawValue = getenv(lpName); + if (!rawValue) { + wibo::lastError = ERROR_ENVVAR_NOT_FOUND; + return 0; + } + std::string converted = convertEnvValueForWindows(lpName, rawValue); + const std::string &finalValue = converted.empty() ? std::string(rawValue) : converted; + DWORD len = static_cast(finalValue.size()); + if (nSize == 0) { + wibo::lastError = ERROR_SUCCESS; + return len + 1; + } + if (!lpBuffer) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + if (nSize <= len) { + wibo::lastError = ERROR_SUCCESS; + return len + 1; + } + memcpy(lpBuffer, finalValue.c_str(), len + 1); + wibo::lastError = ERROR_SUCCESS; + return len; +} + +DWORD WIN_FUNC GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize) { + std::string name = lpName ? wideStringToString(lpName) : std::string(); + DEBUG_LOG("GetEnvironmentVariableW(%s, %p, %u)\n", name.c_str(), lpBuffer, nSize); + if (name.empty()) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + const char *rawValue = getenv(name.c_str()); + if (!rawValue) { + wibo::lastError = ERROR_ENVVAR_NOT_FOUND; + return 0; + } + std::string converted = convertEnvValueForWindows(name, rawValue); + const std::string &finalValue = converted.empty() ? std::string(rawValue) : converted; + auto wideValue = stringToWideString(finalValue.c_str()); + DWORD required = static_cast(wideValue.size()); + if (nSize == 0) { + wibo::lastError = ERROR_SUCCESS; + return required; + } + if (!lpBuffer) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + if (nSize < required) { + wibo::lastError = ERROR_SUCCESS; + return required; + } + std::copy(wideValue.begin(), wideValue.end(), lpBuffer); + wibo::lastError = ERROR_SUCCESS; + return required - 1; +} + +BOOL WIN_FUNC SetEnvironmentVariableA(LPCSTR lpName, LPCSTR lpValue) { + DEBUG_LOG("SetEnvironmentVariableA(%s, %s)\n", lpName ? lpName : "(null)", lpValue ? lpValue : "(null)"); + if (!lpName || std::strchr(lpName, '=')) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + int rc = 0; + if (!lpValue) { + rc = unsetenv(lpName); + if (rc != 0) { + setLastErrorFromErrno(); + return FALSE; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + std::string hostValue = convertEnvValueToHost(lpName, lpValue); + const char *valuePtr = hostValue.empty() ? lpValue : hostValue.c_str(); + rc = setenv(lpName, valuePtr, 1); + if (rc != 0) { + setLastErrorFromErrno(); + return FALSE; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC SetEnvironmentVariableW(LPCWSTR lpName, LPCWSTR lpValue) { + DEBUG_LOG("SetEnvironmentVariableW -> "); + if (!lpName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + DEBUG_LOG("ERROR_INVALID_PARAMETER\n"); + return FALSE; + } + std::string name = wideStringToString(lpName); + std::string value = lpValue ? wideStringToString(lpValue) : std::string(); + return SetEnvironmentVariableA(name.c_str(), lpValue ? value.c_str() : nullptr); +} + +} // namespace kernel32 diff --git a/dll/kernel32/processenv.h b/dll/kernel32/processenv.h new file mode 100644 index 0000000..5f957fd --- /dev/null +++ b/dll/kernel32/processenv.h @@ -0,0 +1,20 @@ +#pragma once + +#include "common.h" + +namespace kernel32 { + +LPSTR WIN_FUNC GetCommandLineA(); +LPWSTR WIN_FUNC GetCommandLineW(); +HANDLE WIN_FUNC GetStdHandle(DWORD nStdHandle); +BOOL WIN_FUNC SetStdHandle(DWORD nStdHandle, HANDLE hHandle); +LPCH WIN_FUNC GetEnvironmentStrings(); +LPWCH WIN_FUNC GetEnvironmentStringsW(); +BOOL WIN_FUNC FreeEnvironmentStringsA(LPCH penv); +BOOL WIN_FUNC FreeEnvironmentStringsW(LPWCH penv); +DWORD WIN_FUNC GetEnvironmentVariableA(LPCSTR lpName, LPSTR lpBuffer, DWORD nSize); +DWORD WIN_FUNC GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize); +BOOL WIN_FUNC SetEnvironmentVariableA(LPCSTR lpName, LPCSTR lpValue); +BOOL WIN_FUNC SetEnvironmentVariableW(LPCWSTR lpName, LPCWSTR lpValue); + +} // namespace kernel32 diff --git a/dll/kernel32/processthreadsapi.cpp b/dll/kernel32/processthreadsapi.cpp index 72db1b7..ba49862 100644 --- a/dll/kernel32/processthreadsapi.cpp +++ b/dll/kernel32/processthreadsapi.cpp @@ -1,21 +1,76 @@ +#include "processthreadsapi.h" #include "common.h" #include "errors.h" +#include "files.h" #include "handles.h" -#include "kernel32.h" +#include "internal.h" #include "processes.h" #include "strutil.h" +#include "timeutil.h" #include #include #include #include +#include +#include #include #include #include +#include +#include #include #include namespace { + +using kernel32::ThreadObject; + +constexpr DWORD kMaxTlsValues = 100; +bool g_tlsSlotsUsed[kMaxTlsValues] = {false}; +LPVOID g_tlsSlots[kMaxTlsValues] = {nullptr}; + +DWORD_PTR g_processAffinityMask = 0; +bool g_processAffinityMaskInitialized = false; + +const FILETIME kDefaultThreadFileTime = {static_cast(UNIX_TIME_ZERO & 0xFFFFFFFFULL), + static_cast(UNIX_TIME_ZERO >> 32)}; + +constexpr DWORD STARTF_USESHOWWINDOW = 0x00000001; +constexpr DWORD STARTF_USESTDHANDLES = 0x00000100; +constexpr WORD SW_SHOWNORMAL = 1; + +struct ThreadStartData { + LPTHREAD_START_ROUTINE startRoutine; + void *parameter; + ThreadObject *threadObject; +}; + +FILETIME fileTimeFromTimeval(const struct timeval &value) { + uint64_t total = 0; + if (value.tv_sec > 0 || value.tv_usec > 0) { + total = static_cast(value.tv_sec) * 10000000ULL + static_cast(value.tv_usec) * 10ULL; + } + return fileTimeFromDuration(total); +} + +FILETIME fileTimeFromTimespec(const struct timespec &value) { + uint64_t total = 0; + if (value.tv_sec > 0 || value.tv_nsec > 0) { + total = static_cast(value.tv_sec) * 10000000ULL + static_cast(value.tv_nsec) / 100ULL; + } + return fileTimeFromDuration(total); +} + +void destroyThreadObject(ThreadObject *obj) { + if (!obj) { + return; + } + pthread_cond_destroy(&obj->cond); + pthread_mutex_destroy(&obj->mutex); + delete obj; +} + DWORD_PTR computeSystemAffinityMask() { long reported = sysconf(_SC_NPROCESSORS_ONLN); if (reported <= 0) { @@ -29,12 +84,153 @@ DWORD_PTR computeSystemAffinityMask() { return (static_cast(1) << usable) - 1; } -DWORD_PTR g_processAffinityMask = 0; -bool g_processAffinityMaskInitialized = false; +template void populateStartupInfo(StartupInfo *info) { + if (!info) { + return; + } + std::memset(info, 0, sizeof(StartupInfo)); + info->cb = sizeof(StartupInfo); + info->dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + info->wShowWindow = SW_SHOWNORMAL; + info->cbReserved2 = 0; + info->lpReserved2 = nullptr; + info->hStdInput = files::getStdHandle(STD_INPUT_HANDLE); + info->hStdOutput = files::getStdHandle(STD_OUTPUT_HANDLE); + info->hStdError = files::getStdHandle(STD_ERROR_HANDLE); +} + } // namespace namespace kernel32 { +thread_local ThreadObject *g_currentThreadObject = nullptr; + +ThreadObject *ensureCurrentThreadObject() { + ThreadObject *obj = g_currentThreadObject; + if (obj) { + return obj; + } + obj = new ThreadObject(); + obj->thread = pthread_self(); + obj->finished = false; + obj->joined = false; + obj->detached = true; + obj->synthetic = false; + obj->exitCode = STILL_ACTIVE; + obj->refCount = 0; + obj->suspendCount = 0; + pthread_mutex_init(&obj->mutex, nullptr); + pthread_cond_init(&obj->cond, nullptr); + g_currentThreadObject = obj; + return obj; +} + +ThreadObject *threadObjectFromHandle(HANDLE hThread) { + auto raw = reinterpret_cast(hThread); + if (raw == kPseudoCurrentThreadHandleValue) { + return ensureCurrentThreadObject(); + } + if (raw == static_cast(-1) || raw == 0) { + return nullptr; + } + auto data = handles::dataFromHandle(hThread, false); + if (data.type != handles::TYPE_THREAD || data.ptr == nullptr) { + return nullptr; + } + return reinterpret_cast(data.ptr); +} + +ThreadObject *retainThreadObject(ThreadObject *obj) { + if (!obj) { + return nullptr; + } + pthread_mutex_lock(&obj->mutex); + obj->refCount++; + pthread_mutex_unlock(&obj->mutex); + return obj; +} + +void releaseThreadObject(ThreadObject *obj) { + if (!obj) { + return; + } + pthread_t thread = 0; + pthread_mutex_lock(&obj->mutex); + obj->refCount--; + bool shouldDelete = false; + bool shouldDetach = false; + bool finished = obj->finished; + bool joined = obj->joined; + bool detached = obj->detached; + bool synthetic = obj->synthetic; + thread = obj->thread; + if (obj->refCount == 0) { + if (finished || synthetic) { + shouldDelete = true; + } else if (!detached) { + obj->detached = true; + shouldDetach = true; + detached = true; + } + } + pthread_mutex_unlock(&obj->mutex); + + if (shouldDetach && !synthetic) { + pthread_detach(thread); + } + + if (shouldDelete) { + if (!synthetic) { + if (!joined && !detached) { + pthread_join(thread, nullptr); + } + } + destroyThreadObject(obj); + } +} + +static void *threadTrampoline(void *param) { + ThreadStartData *data = static_cast(param); + ThreadObject *obj = data->threadObject; + LPTHREAD_START_ROUTINE startRoutine = data->startRoutine; + void *userParam = data->parameter; + delete data; + + uint16_t previousSegment = 0; + bool tibInstalled = false; + if (wibo::tibSelector) { + asm volatile("mov %%fs, %0" : "=r"(previousSegment)); + asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory"); + tibInstalled = true; + } + + g_currentThreadObject = obj; + pthread_mutex_lock(&obj->mutex); + while (obj->suspendCount > 0) { + pthread_cond_wait(&obj->cond, &obj->mutex); + } + pthread_mutex_unlock(&obj->mutex); + DWORD result = startRoutine ? startRoutine(userParam) : 0; + pthread_mutex_lock(&obj->mutex); + obj->finished = true; + obj->exitCode = result; + pthread_cond_broadcast(&obj->cond); + bool shouldDelete = (obj->refCount == 0); + bool detached = obj->detached; + pthread_mutex_unlock(&obj->mutex); + g_currentThreadObject = nullptr; + + if (shouldDelete) { + assert(detached && "ThreadObject must be detached when refCount reaches zero before completion"); + destroyThreadObject(obj); + } + + if (tibInstalled) { + asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory"); + } + return nullptr; +} + HANDLE WIN_FUNC GetCurrentProcess() { DEBUG_LOG("GetCurrentProcess() -> %p\n", reinterpret_cast(static_cast(-1))); return reinterpret_cast(static_cast(-1)); @@ -53,6 +249,14 @@ DWORD WIN_FUNC GetCurrentThreadId() { return threadId; } +HANDLE WIN_FUNC GetCurrentThread() { + ThreadObject *obj = ensureCurrentThreadObject(); + (void)obj; + HANDLE pseudoHandle = reinterpret_cast(kPseudoCurrentThreadHandleValue); + DEBUG_LOG("GetCurrentThread() -> %p\n", pseudoHandle); + return pseudoHandle; +} + BOOL WIN_FUNC GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR lpProcessAffinityMask, PDWORD_PTR lpSystemAffinityMask) { DEBUG_LOG("GetProcessAffinityMask(%p, %p, %p)\n", hProcess, lpProcessAffinityMask, lpSystemAffinityMask); @@ -116,6 +320,38 @@ BOOL WIN_FUNC SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinit return TRUE; } +DWORD_PTR WIN_FUNC SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask) { + DEBUG_LOG("SetThreadAffinityMask(%p, 0x%lx)\n", hThread, static_cast(dwThreadAffinityMask)); + if (dwThreadAffinityMask == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + uintptr_t rawThreadHandle = reinterpret_cast(hThread); + bool isPseudoHandle = rawThreadHandle == kPseudoCurrentThreadHandleValue || rawThreadHandle == 0 || + rawThreadHandle == static_cast(-1); + if (!isPseudoHandle) { + auto data = handles::dataFromHandle(hThread, false); + if (data.type != handles::TYPE_THREAD) { + wibo::lastError = ERROR_INVALID_HANDLE; + return 0; + } + } + + DWORD_PTR processMask = 0; + DWORD_PTR systemMask = 0; + if (!GetProcessAffinityMask(GetCurrentProcess(), &processMask, &systemMask)) { + return 0; + } + if ((dwThreadAffinityMask & ~systemMask) != 0 || (dwThreadAffinityMask & processMask) == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + wibo::lastError = ERROR_SUCCESS; + return processMask; +} + void WIN_FUNC ExitProcess(UINT uExitCode) { DEBUG_LOG("ExitProcess(%u)\n", uExitCode); std::exit(static_cast(uExitCode)); @@ -169,6 +405,258 @@ BOOL WIN_FUNC GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode) { return TRUE; } +DWORD WIN_FUNC TlsAlloc() { + VERBOSE_LOG("TlsAlloc()\n"); + for (DWORD i = 0; i < kMaxTlsValues; ++i) { + if (!g_tlsSlotsUsed[i]) { + g_tlsSlotsUsed[i] = true; + g_tlsSlots[i] = nullptr; + wibo::lastError = ERROR_SUCCESS; + return i; + } + } + wibo::lastError = ERROR_NOT_ENOUGH_MEMORY; + return TLS_OUT_OF_INDEXES; +} + +BOOL WIN_FUNC TlsFree(DWORD dwTlsIndex) { + VERBOSE_LOG("TlsFree(%u)\n", dwTlsIndex); + if (dwTlsIndex >= kMaxTlsValues || !g_tlsSlotsUsed[dwTlsIndex]) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + g_tlsSlotsUsed[dwTlsIndex] = false; + g_tlsSlots[dwTlsIndex] = nullptr; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +LPVOID WIN_FUNC TlsGetValue(DWORD dwTlsIndex) { + VERBOSE_LOG("TlsGetValue(%u)\n", dwTlsIndex); + if (dwTlsIndex >= kMaxTlsValues || !g_tlsSlotsUsed[dwTlsIndex]) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return nullptr; + } + wibo::lastError = ERROR_SUCCESS; + return g_tlsSlots[dwTlsIndex]; +} + +BOOL WIN_FUNC TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) { + VERBOSE_LOG("TlsSetValue(%u, %p)\n", dwTlsIndex, lpTlsValue); + if (dwTlsIndex >= kMaxTlsValues || !g_tlsSlotsUsed[dwTlsIndex]) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + g_tlsSlots[dwTlsIndex] = lpTlsValue; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +DWORD WIN_FUNC ResumeThread(HANDLE hThread) { + DEBUG_LOG("ResumeThread(%p)\n", hThread); + ThreadObject *obj = threadObjectFromHandle(hThread); + if (!obj) { + wibo::lastError = ERROR_INVALID_HANDLE; + return static_cast(-1); + } + pthread_mutex_lock(&obj->mutex); + DWORD previous = obj->suspendCount; + if (obj->suspendCount > 0) { + obj->suspendCount--; + if (obj->suspendCount == 0) { + pthread_cond_broadcast(&obj->cond); + } + } + pthread_mutex_unlock(&obj->mutex); + wibo::lastError = ERROR_SUCCESS; + return previous; +} + +HRESULT WIN_FUNC SetThreadDescription(HANDLE hThread, LPCWSTR lpThreadDescription) { + DEBUG_LOG("STUB: SetThreadDescription(%p, %p)\n", hThread, lpThreadDescription); + (void)hThread; + (void)lpThreadDescription; + return S_OK; +} + +HANDLE WIN_FUNC CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, + LPDWORD lpThreadId) { + DEBUG_LOG("CreateThread(%p, %zu, %p, %p, %u, %p)\n", lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, + dwCreationFlags, lpThreadId); + (void)lpThreadAttributes; + constexpr DWORD CREATE_SUSPENDED = 0x00000004; + constexpr DWORD STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000; + constexpr DWORD SUPPORTED_FLAGS = CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION; + if ((dwCreationFlags & ~SUPPORTED_FLAGS) != 0) { + DEBUG_LOG("CreateThread: unsupported creation flags 0x%x\n", dwCreationFlags); + wibo::lastError = ERROR_NOT_SUPPORTED; + return nullptr; + } + bool startSuspended = (dwCreationFlags & CREATE_SUSPENDED) != 0; + + ThreadObject *obj = new ThreadObject(); + pthread_mutex_init(&obj->mutex, nullptr); + pthread_cond_init(&obj->cond, nullptr); + obj->finished = false; + obj->joined = false; + obj->detached = false; + obj->exitCode = 0; + obj->refCount = 1; + obj->suspendCount = startSuspended ? 1u : 0u; + obj->synthetic = false; + + ThreadStartData *startData = new ThreadStartData{lpStartAddress, lpParameter, obj}; + + pthread_attr_t attr; + pthread_attr_t *attrPtr = nullptr; + if (dwStackSize != 0) { + pthread_attr_init(&attr); + size_t stackSize = dwStackSize; +#ifdef PTHREAD_STACK_MIN + if (stackSize < static_cast(PTHREAD_STACK_MIN)) { + stackSize = PTHREAD_STACK_MIN; + } +#endif + if (pthread_attr_setstacksize(&attr, stackSize) == 0) { + attrPtr = &attr; + } else { + pthread_attr_destroy(&attr); + } + } + + int rc = pthread_create(&obj->thread, attrPtr, threadTrampoline, startData); + if (attrPtr) { + pthread_attr_destroy(attrPtr); + } + if (rc != 0) { + delete startData; + destroyThreadObject(obj); + errno = rc; + setLastErrorFromErrno(); + return nullptr; + } + + if (lpThreadId) { + std::size_t hashed = std::hash{}(obj->thread); + *lpThreadId = static_cast(hashed & 0xffffffffu); + } + + wibo::lastError = ERROR_SUCCESS; + return handles::allocDataHandle({handles::TYPE_THREAD, obj, 0}); +} + +void WIN_FUNC ExitThread(DWORD dwExitCode) { + DEBUG_LOG("ExitThread(%u)\n", dwExitCode); + ThreadObject *obj = g_currentThreadObject; + uint16_t previousSegment = 0; + bool tibInstalled = false; + if (wibo::tibSelector) { + asm volatile("mov %%fs, %0" : "=r"(previousSegment)); + asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory"); + tibInstalled = true; + } + if (obj) { + pthread_mutex_lock(&obj->mutex); + obj->finished = true; + obj->exitCode = dwExitCode; + pthread_cond_broadcast(&obj->cond); + bool shouldDelete = (obj->refCount == 0); + bool detached = obj->detached; + pthread_mutex_unlock(&obj->mutex); + g_currentThreadObject = nullptr; + if (shouldDelete) { + assert(detached && "ThreadObject must be detached when refCount reaches zero before completion"); + destroyThreadObject(obj); + } + } + if (tibInstalled) { + asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory"); + } + pthread_exit(nullptr); +} + +BOOL WIN_FUNC GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode) { + DEBUG_LOG("GetExitCodeThread(%p, %p)\n", hThread, lpExitCode); + if (!lpExitCode) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + ThreadObject *obj = threadObjectFromHandle(hThread); + if (!obj) { + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + pthread_mutex_lock(&obj->mutex); + DWORD code = obj->finished ? obj->exitCode : STILL_ACTIVE; + pthread_mutex_unlock(&obj->mutex); + *lpExitCode = code; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC SetThreadPriority(HANDLE hThread, int nPriority) { + DEBUG_LOG("STUB: SetThreadPriority(%p, %d)\n", hThread, nPriority); + (void)hThread; + (void)nPriority; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +int WIN_FUNC GetThreadPriority(HANDLE hThread) { + DEBUG_LOG("STUB: GetThreadPriority(%p)\n", hThread); + (void)hThread; + wibo::lastError = ERROR_SUCCESS; + return 0; +} + +BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME *lpExitTime, FILETIME *lpKernelTime, + FILETIME *lpUserTime) { + DEBUG_LOG("GetThreadTimes(%p, %p, %p, %p, %p)\n", hThread, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime); + + if (!lpKernelTime || !lpUserTime) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + bool isPseudoCurrentThread = reinterpret_cast(hThread) == kPseudoCurrentThreadHandleValue || + hThread == nullptr || hThread == reinterpret_cast(static_cast(-1)); + if (!isPseudoCurrentThread) { + DEBUG_LOG("GetThreadTimes: unsupported handle %p\n", hThread); + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; + } + + if (lpCreationTime) { + *lpCreationTime = kDefaultThreadFileTime; + } + if (lpExitTime) { + lpExitTime->dwLowDateTime = 0; + lpExitTime->dwHighDateTime = 0; + } + + struct rusage usage; + if (getrusage(RUSAGE_THREAD, &usage) == 0) { + *lpKernelTime = fileTimeFromTimeval(usage.ru_stime); + *lpUserTime = fileTimeFromTimeval(usage.ru_utime); + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + struct timespec cpuTime; + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuTime) == 0) { + *lpKernelTime = fileTimeFromDuration(0); + *lpUserTime = fileTimeFromTimespec(cpuTime); + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + setLastErrorFromErrno(); + *lpKernelTime = fileTimeFromDuration(0); + *lpUserTime = fileTimeFromDuration(0); + return FALSE; +} + BOOL WIN_FUNC CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, @@ -256,4 +744,20 @@ BOOL WIN_FUNC CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LP lpProcessInformation); } +void WIN_FUNC GetStartupInfoA(LPSTARTUPINFOA lpStartupInfo) { + DEBUG_LOG("GetStartupInfoA(%p)\n", lpStartupInfo); + populateStartupInfo(lpStartupInfo); +} + +void WIN_FUNC GetStartupInfoW(LPSTARTUPINFOW lpStartupInfo) { + DEBUG_LOG("GetStartupInfoW(%p)\n", lpStartupInfo); + populateStartupInfo(lpStartupInfo); +} + +BOOL WIN_FUNC SetThreadStackGuarantee(PULONG StackSizeInBytes) { + DEBUG_LOG("STUB: SetThreadStackGuarantee(%p)\n", StackSizeInBytes); + (void)StackSizeInBytes; + return TRUE; +} + } // namespace kernel32 diff --git a/dll/kernel32/processthreadsapi.h b/dll/kernel32/processthreadsapi.h new file mode 100644 index 0000000..1a776ed --- /dev/null +++ b/dll/kernel32/processthreadsapi.h @@ -0,0 +1,107 @@ +#pragma once + +#include "common.h" +#include "errors.h" +#include "minwinbase.h" + +struct PROCESS_INFORMATION { + HANDLE hProcess; + HANDLE hThread; + DWORD dwProcessId; + DWORD dwThreadId; +}; + +using PPROCESS_INFORMATION = PROCESS_INFORMATION *; +using LPPROCESS_INFORMATION = PROCESS_INFORMATION *; + +struct STARTUPINFOA { + DWORD cb; + LPSTR lpReserved; + LPSTR lpDesktop; + LPSTR lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + unsigned char *lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; +}; + +using LPSTARTUPINFOA = STARTUPINFOA *; + +struct STARTUPINFOW { + DWORD cb; + LPWSTR lpReserved; + LPWSTR lpDesktop; + LPWSTR lpTitle; + DWORD dwX; + DWORD dwY; + DWORD dwXSize; + DWORD dwYSize; + DWORD dwXCountChars; + DWORD dwYCountChars; + DWORD dwFillAttribute; + DWORD dwFlags; + WORD wShowWindow; + WORD cbReserved2; + unsigned char *lpReserved2; + HANDLE hStdInput; + HANDLE hStdOutput; + HANDLE hStdError; +}; + +using LPSTARTUPINFOW = STARTUPINFOW *; + +constexpr DWORD TLS_OUT_OF_INDEXES = 0xFFFFFFFFu; + +typedef DWORD(WIN_FUNC *LPTHREAD_START_ROUTINE)(LPVOID); + +namespace kernel32 { + +HANDLE WIN_FUNC GetCurrentProcess(); +DWORD WIN_FUNC GetCurrentProcessId(); +DWORD WIN_FUNC GetCurrentThreadId(); +HANDLE WIN_FUNC GetCurrentThread(); +BOOL WIN_FUNC GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR lpProcessAffinityMask, + PDWORD_PTR lpSystemAffinityMask); +BOOL WIN_FUNC SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinityMask); +DWORD_PTR WIN_FUNC SetThreadAffinityMask(HANDLE hThread, DWORD_PTR dwThreadAffinityMask); +DWORD WIN_FUNC ResumeThread(HANDLE hThread); +HRESULT WIN_FUNC SetThreadDescription(HANDLE hThread, LPCWSTR lpThreadDescription); +void WIN_FUNC ExitProcess(UINT uExitCode); +BOOL WIN_FUNC TerminateProcess(HANDLE hProcess, UINT uExitCode); +BOOL WIN_FUNC GetExitCodeProcess(HANDLE hProcess, LPDWORD lpExitCode); +DWORD WIN_FUNC TlsAlloc(); +BOOL WIN_FUNC TlsFree(DWORD dwTlsIndex); +LPVOID WIN_FUNC TlsGetValue(DWORD dwTlsIndex); +BOOL WIN_FUNC TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue); +HANDLE WIN_FUNC CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, + LPDWORD lpThreadId); +void WIN_FUNC ExitThread(DWORD dwExitCode); +BOOL WIN_FUNC GetExitCodeThread(HANDLE hThread, LPDWORD lpExitCode); +BOOL WIN_FUNC SetThreadPriority(HANDLE hThread, int nPriority); +int WIN_FUNC GetThreadPriority(HANDLE hThread); +BOOL WIN_FUNC GetThreadTimes(HANDLE hThread, FILETIME *lpCreationTime, FILETIME *lpExitTime, FILETIME *lpKernelTime, + FILETIME *lpUserTime); +BOOL WIN_FUNC CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, + LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); +BOOL WIN_FUNC CreateProcessW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, + LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation); +void WIN_FUNC GetStartupInfoA(LPSTARTUPINFOA lpStartupInfo); +void WIN_FUNC GetStartupInfoW(LPSTARTUPINFOW lpStartupInfo); +BOOL WIN_FUNC SetThreadStackGuarantee(PULONG StackSizeInBytes); + +} // namespace kernel32 diff --git a/dll/kernel32/profileapi.cpp b/dll/kernel32/profileapi.cpp new file mode 100644 index 0000000..ceaaf84 --- /dev/null +++ b/dll/kernel32/profileapi.cpp @@ -0,0 +1,29 @@ +#include "profileapi.h" +#include "common.h" +#include "errors.h" + +namespace kernel32 { + +BOOL WIN_FUNC QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount) { + VERBOSE_LOG("STUB: QueryPerformanceCounter(%p)\n", lpPerformanceCount); + if (!lpPerformanceCount) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + *lpPerformanceCount = 0; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency) { + VERBOSE_LOG("STUB: QueryPerformanceFrequency(%p)\n", lpFrequency); + if (!lpFrequency) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + *lpFrequency = 1; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +} // namespace kernel32 diff --git a/dll/kernel32/profileapi.h b/dll/kernel32/profileapi.h new file mode 100644 index 0000000..e35b678 --- /dev/null +++ b/dll/kernel32/profileapi.h @@ -0,0 +1,10 @@ +#pragma once + +#include "common.h" + +namespace kernel32 { + +BOOL WIN_FUNC QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount); +BOOL WIN_FUNC QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency); + +} // namespace kernel32 diff --git a/dll/kernel32/stringapiset.cpp b/dll/kernel32/stringapiset.cpp new file mode 100644 index 0000000..bf1ead2 --- /dev/null +++ b/dll/kernel32/stringapiset.cpp @@ -0,0 +1,141 @@ +#include "stringapiset.h" + +#include "errors.h" +#include "strutil.h" + +#include +#include +#include +#include + +namespace kernel32 { + +int WIN_FUNC WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, + LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar) { + DEBUG_LOG("WideCharToMultiByte(%u, %u, %p, %d, %p, %d, %p, %p)\n", CodePage, dwFlags, lpWideCharStr, cchWideChar, + lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar); + + (void)CodePage; + (void)dwFlags; + (void)lpDefaultChar; + if (lpUsedDefaultChar) { + *lpUsedDefaultChar = FALSE; + } + + if (cchWideChar == -1) { + cchWideChar = static_cast(wstrlen(lpWideCharStr)) + 1; + } + + if (cbMultiByte == 0) { + return cchWideChar; + } + for (int i = 0; i < cchWideChar; i++) { + lpMultiByteStr[i] = static_cast(lpWideCharStr[i] & 0xFF); + } + + if (wibo::debugEnabled) { + std::string s(lpMultiByteStr, lpMultiByteStr + cchWideChar); + DEBUG_LOG("Converted string: [%s] (len %d)\n", s.c_str(), cchWideChar); + } + + return cchWideChar; +} + +int WIN_FUNC MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, int cbMultiByte, + LPWSTR lpWideCharStr, int cchWideChar) { + DEBUG_LOG("MultiByteToWideChar(%u, %u, %d, %d)\n", CodePage, dwFlags, cbMultiByte, cchWideChar); + + (void)CodePage; + (void)dwFlags; + + if (cbMultiByte == -1) { + cbMultiByte = static_cast(strlen(lpMultiByteStr)) + 1; + } + + if (cchWideChar == 0) { + return cbMultiByte; + } + if (wibo::debugEnabled) { + std::string s(lpMultiByteStr, lpMultiByteStr + cbMultiByte); + DEBUG_LOG("Converting string: [%s] (len %d)\n", s.c_str(), cbMultiByte); + } + + assert(cbMultiByte <= cchWideChar); + for (int i = 0; i < cbMultiByte; i++) { + lpWideCharStr[i] = static_cast(lpMultiByteStr[i] & 0xFF); + } + return cbMultiByte; +} + +BOOL WIN_FUNC GetStringTypeW(DWORD dwInfoType, LPCWCH lpSrcStr, int cchSrc, LPWORD lpCharType) { + DEBUG_LOG("GetStringTypeW(%u, %p, %i, %p)\n", dwInfoType, lpSrcStr, cchSrc, lpCharType); + + assert(dwInfoType == 1); // CT_CTYPE1 + + if (!lpSrcStr || !lpCharType) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + if (cchSrc < 0) { + cchSrc = static_cast(wstrlen(lpSrcStr)); + } + + for (int i = 0; i < cchSrc; i++) { + wint_t c = lpSrcStr[i]; + bool upper = std::iswupper(c); + bool lower = std::iswlower(c); + bool alpha = std::iswalpha(c); + bool digit = std::iswdigit(c); + bool space = std::iswspace(c); + bool blank = (c == L' ' || c == L'\t'); + bool hex = std::iswxdigit(c); + bool cntrl = std::iswcntrl(c); + bool punct = std::iswpunct(c); + lpCharType[i] = (upper ? 1 : 0) | (lower ? 2 : 0) | (digit ? 4 : 0) | (space ? 8 : 0) | (punct ? 0x10 : 0) | + (cntrl ? 0x20 : 0) | (blank ? 0x40 : 0) | (hex ? 0x80 : 0) | (alpha ? 0x100 : 0); + } + + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC GetStringTypeA(LCID Locale, DWORD dwInfoType, LPCSTR lpSrcStr, int cchSrc, LPWORD lpCharType) { + DEBUG_LOG("GetStringTypeA(%u, %u, %p, %d, %p)\n", Locale, dwInfoType, lpSrcStr, cchSrc, lpCharType); + (void)Locale; + + if (!lpSrcStr || !lpCharType) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + if (dwInfoType != 1) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return FALSE; + } + + int length = cchSrc; + if (length < 0) { + length = static_cast(strlen(lpSrcStr)); + } + if (length < 0) { + length = 0; + } + + std::vector wide; + wide.reserve(static_cast(length)); + for (int i = 0; i < length; ++i) { + wide.push_back(static_cast(lpSrcStr[i])); + } + + BOOL result = TRUE; + if (length > 0) { + result = GetStringTypeW(dwInfoType, wide.data(), length, lpCharType); + } else { + result = TRUE; + } + + wibo::lastError = result ? ERROR_SUCCESS : wibo::lastError; + return result; +} + +} // namespace kernel32 diff --git a/dll/kernel32/stringapiset.h b/dll/kernel32/stringapiset.h new file mode 100644 index 0000000..974b8f6 --- /dev/null +++ b/dll/kernel32/stringapiset.h @@ -0,0 +1,14 @@ +#pragma once + +#include "common.h" + +namespace kernel32 { + +int WIN_FUNC WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, + LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar); +int WIN_FUNC MultiByteToWideChar(UINT CodePage, DWORD dwFlags, LPCCH lpMultiByteStr, int cbMultiByte, + LPWSTR lpWideCharStr, int cchWideChar); +BOOL WIN_FUNC GetStringTypeW(DWORD dwInfoType, LPCWCH lpSrcStr, int cchSrc, LPWORD lpCharType); +BOOL WIN_FUNC GetStringTypeA(LCID Locale, DWORD dwInfoType, LPCSTR lpSrcStr, int cchSrc, LPWORD lpCharType); + +} // namespace kernel32 diff --git a/dll/kernel32/synchapi.cpp b/dll/kernel32/synchapi.cpp index 9ec6abc..bbaa2a1 100644 --- a/dll/kernel32/synchapi.cpp +++ b/dll/kernel32/synchapi.cpp @@ -1,12 +1,13 @@ +#include "synchapi.h" #include "common.h" #include "errors.h" #include "handles.h" #include "internal.h" -#include "kernel32.h" #include "processes.h" #include "strutil.h" #include +#include #include #include #include @@ -489,6 +490,105 @@ DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) { } } +void WIN_FUNC InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection) { + VERBOSE_LOG("STUB: InitializeCriticalSection(%p)\n", lpCriticalSection); + if (!lpCriticalSection) { + return; + } + std::memset(lpCriticalSection, 0, sizeof(*lpCriticalSection)); +} + +BOOL WIN_FUNC InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD Flags) { + DEBUG_LOG("STUB: InitializeCriticalSectionEx(%p, %u, 0x%x)\n", lpCriticalSection, dwSpinCount, Flags); + if (!lpCriticalSection) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + if (Flags & ~CRITICAL_SECTION_NO_DEBUG_INFO) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + std::memset(lpCriticalSection, 0, sizeof(*lpCriticalSection)); + lpCriticalSection->SpinCount = dwSpinCount; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount) { + DEBUG_LOG("STUB: InitializeCriticalSectionAndSpinCount(%p, %u)\n", lpCriticalSection, dwSpinCount); + if (!lpCriticalSection) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + std::memset(lpCriticalSection, 0, sizeof(*lpCriticalSection)); + lpCriticalSection->SpinCount = dwSpinCount; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +void WIN_FUNC DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection) { + VERBOSE_LOG("STUB: DeleteCriticalSection(%p)\n", lpCriticalSection); + (void)lpCriticalSection; +} + +void WIN_FUNC EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) { + VERBOSE_LOG("STUB: EnterCriticalSection(%p)\n", lpCriticalSection); + (void)lpCriticalSection; +} + +void WIN_FUNC LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection) { + VERBOSE_LOG("STUB: LeaveCriticalSection(%p)\n", lpCriticalSection); + (void)lpCriticalSection; +} + +BOOL WIN_FUNC InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, LPVOID *lpContext) { + DEBUG_LOG("STUB: InitOnceBeginInitialize(%p, %u, %p, %p)\n", lpInitOnce, dwFlags, fPending, lpContext); + if (!lpInitOnce) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + if (dwFlags & ~(INIT_ONCE_CHECK_ONLY | INIT_ONCE_ASYNC)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + if (fPending) { + *fPending = TRUE; + } + if (lpContext) { + *lpContext = nullptr; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext) { + DEBUG_LOG("STUB: InitOnceComplete(%p, %u, %p)\n", lpInitOnce, dwFlags, lpContext); + if (!lpInitOnce) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + if ((dwFlags & INIT_ONCE_INIT_FAILED) && (dwFlags & INIT_ONCE_ASYNC)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + wibo::lastError = ERROR_SUCCESS; + (void)lpContext; + return TRUE; +} + +void WIN_FUNC AcquireSRWLockShared(PSRWLOCK SRWLock) { VERBOSE_LOG("STUB: AcquireSRWLockShared(%p)\n", SRWLock); } + +void WIN_FUNC ReleaseSRWLockShared(PSRWLOCK SRWLock) { VERBOSE_LOG("STUB: ReleaseSRWLockShared(%p)\n", SRWLock); } + +void WIN_FUNC AcquireSRWLockExclusive(PSRWLOCK SRWLock) { VERBOSE_LOG("STUB: AcquireSRWLockExclusive(%p)\n", SRWLock); } + +void WIN_FUNC ReleaseSRWLockExclusive(PSRWLOCK SRWLock) { VERBOSE_LOG("STUB: ReleaseSRWLockExclusive(%p)\n", SRWLock); } + +BOOLEAN WIN_FUNC TryAcquireSRWLockExclusive(PSRWLOCK SRWLock) { + VERBOSE_LOG("STUB: TryAcquireSRWLockExclusive(%p)\n", SRWLock); + return TRUE; +} + void resetOverlappedEvent(OVERLAPPED *ov) { if (!ov || !ov->hEvent) { return; diff --git a/dll/kernel32/synchapi.h b/dll/kernel32/synchapi.h new file mode 100644 index 0000000..67242b0 --- /dev/null +++ b/dll/kernel32/synchapi.h @@ -0,0 +1,105 @@ +#pragma once + +#include "common.h" +#include "minwinbase.h" + +constexpr DWORD WAIT_OBJECT_0 = 0x00000000; +constexpr DWORD WAIT_ABANDONED = 0x00000080; +constexpr DWORD WAIT_TIMEOUT = 0x00000102; +constexpr DWORD WAIT_FAILED = 0xFFFFFFFF; +constexpr DWORD INFINITE = 0xFFFFFFFF; + +constexpr DWORD INIT_ONCE_CHECK_ONLY = 0x00000001UL; +constexpr DWORD INIT_ONCE_ASYNC = 0x00000002UL; +constexpr DWORD INIT_ONCE_INIT_FAILED = 0x00000004UL; +constexpr DWORD INIT_ONCE_CTX_RESERVED_BITS = 2; + +constexpr DWORD CRITICAL_SECTION_NO_DEBUG_INFO = 0x01000000UL; + +struct LIST_ENTRY { + LIST_ENTRY *Flink; + LIST_ENTRY *Blink; +}; + +struct RTL_CRITICAL_SECTION; + +struct RTL_CRITICAL_SECTION_DEBUG { + WORD Type; + WORD CreatorBackTraceIndex; + RTL_CRITICAL_SECTION *CriticalSection; + LIST_ENTRY ProcessLocksList; + DWORD EntryCount; + DWORD ContentionCount; + DWORD Flags; + WORD CreatorBackTraceIndexHigh; + WORD SpareWORD; + DWORD Spare[2]; +}; + +struct RTL_CRITICAL_SECTION { + RTL_CRITICAL_SECTION_DEBUG *DebugInfo; + LONG LockCount; + LONG RecursionCount; + HANDLE OwningThread; + HANDLE LockSemaphore; + ULONG_PTR SpinCount; +}; + +using PRTL_CRITICAL_SECTION = RTL_CRITICAL_SECTION *; +using LPCRITICAL_SECTION = RTL_CRITICAL_SECTION *; +using PCRITICAL_SECTION = RTL_CRITICAL_SECTION *; +using PRTL_CRITICAL_SECTION_DEBUG = RTL_CRITICAL_SECTION_DEBUG *; + +union RTL_RUN_ONCE { + PVOID Ptr; +}; + +using PRTL_RUN_ONCE = RTL_RUN_ONCE *; +using INIT_ONCE = RTL_RUN_ONCE; +using PINIT_ONCE = INIT_ONCE *; +using LPINIT_ONCE = INIT_ONCE *; + +constexpr INIT_ONCE INIT_ONCE_STATIC_INIT{nullptr}; + +union RTL_SRWLOCK { + PVOID Ptr; +}; + +using SRWLOCK = RTL_SRWLOCK; +using PSRWLOCK = SRWLOCK *; +using PRTL_SRWLOCK = SRWLOCK *; + +constexpr SRWLOCK SRWLOCK_INIT{nullptr}; + +namespace kernel32 { + +HANDLE WIN_FUNC CreateMutexA(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCSTR lpName); +HANDLE WIN_FUNC CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName); +BOOL WIN_FUNC ReleaseMutex(HANDLE hMutex); +HANDLE WIN_FUNC CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, + LPCSTR lpName); +HANDLE WIN_FUNC CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, + LPCWSTR lpName); +BOOL WIN_FUNC SetEvent(HANDLE hEvent); +BOOL WIN_FUNC ResetEvent(HANDLE hEvent); +HANDLE WIN_FUNC CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, + LPCSTR lpName); +HANDLE WIN_FUNC CreateSemaphoreW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, + LPCWSTR lpName); +BOOL WIN_FUNC ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, PLONG lpPreviousCount); +DWORD WIN_FUNC WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); +void WIN_FUNC InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); +BOOL WIN_FUNC InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD Flags); +BOOL WIN_FUNC InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount); +void WIN_FUNC DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection); +void WIN_FUNC EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); +void WIN_FUNC LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); +BOOL WIN_FUNC InitOnceBeginInitialize(LPINIT_ONCE lpInitOnce, DWORD dwFlags, PBOOL fPending, LPVOID *lpContext); +BOOL WIN_FUNC InitOnceComplete(LPINIT_ONCE lpInitOnce, DWORD dwFlags, LPVOID lpContext); +void WIN_FUNC AcquireSRWLockShared(PSRWLOCK SRWLock); +void WIN_FUNC ReleaseSRWLockShared(PSRWLOCK SRWLock); +void WIN_FUNC AcquireSRWLockExclusive(PSRWLOCK SRWLock); +void WIN_FUNC ReleaseSRWLockExclusive(PSRWLOCK SRWLock); +BOOLEAN WIN_FUNC TryAcquireSRWLockExclusive(PSRWLOCK SRWLock); + +} // namespace kernel32 diff --git a/dll/kernel32/sysinfoapi.cpp b/dll/kernel32/sysinfoapi.cpp index dd471c3..2ffcb2f 100644 --- a/dll/kernel32/sysinfoapi.cpp +++ b/dll/kernel32/sysinfoapi.cpp @@ -1,12 +1,22 @@ +#include "sysinfoapi.h" #include "common.h" -#include "kernel32.h" +#include "errors.h" +#include "timeutil.h" #include +#include +#include namespace { + constexpr WORD PROCESSOR_ARCHITECTURE_INTEL = 0; constexpr DWORD PROCESSOR_INTEL_PENTIUM = 586; +constexpr uint64_t kUnixTimeZero = 11644473600ULL * 10000000ULL; +constexpr DWORD kMajorVersion = 6; +constexpr DWORD kMinorVersion = 2; +constexpr DWORD kBuildNumber = 0; + DWORD_PTR computeSystemProcessorMask(unsigned int cpuCount) { const auto maskWidth = static_cast(sizeof(DWORD_PTR) * 8); if (cpuCount >= maskWidth) { @@ -15,6 +25,7 @@ DWORD_PTR computeSystemProcessorMask(unsigned int cpuCount) { DWORD_PTR mask = (static_cast(1) << cpuCount) - 1; return mask == 0 ? 1 : mask; } + } // namespace namespace kernel32 { @@ -55,4 +66,135 @@ void WIN_FUNC GetSystemInfo(LPSYSTEM_INFO lpSystemInfo) { lpSystemInfo->dwAllocationGranularity = 0x10000; } +void WIN_FUNC GetSystemTime(LPSYSTEMTIME lpSystemTime) { + DEBUG_LOG("GetSystemTime(%p)\n", lpSystemTime); + if (!lpSystemTime) { + return; + } + + time_t now = time(nullptr); + struct tm tmUtc{}; +#if defined(_GNU_SOURCE) || defined(__APPLE__) + gmtime_r(&now, &tmUtc); +#else + struct tm *tmp = gmtime(&now); + if (!tmp) { + return; + } + tmUtc = *tmp; +#endif + + lpSystemTime->wYear = static_cast(tmUtc.tm_year + 1900); + lpSystemTime->wMonth = static_cast(tmUtc.tm_mon + 1); + lpSystemTime->wDayOfWeek = static_cast(tmUtc.tm_wday); + lpSystemTime->wDay = static_cast(tmUtc.tm_mday); + lpSystemTime->wHour = static_cast(tmUtc.tm_hour); + lpSystemTime->wMinute = static_cast(tmUtc.tm_min); + lpSystemTime->wSecond = static_cast(tmUtc.tm_sec); + lpSystemTime->wMilliseconds = 0; +} + +void WIN_FUNC GetLocalTime(LPSYSTEMTIME lpSystemTime) { + DEBUG_LOG("GetLocalTime(%p)\n", lpSystemTime); + if (!lpSystemTime) { + return; + } + + time_t now = time(nullptr); + struct tm tmLocal{}; +#if defined(_GNU_SOURCE) || defined(__APPLE__) + localtime_r(&now, &tmLocal); +#else + struct tm *tmp = localtime(&now); + if (!tmp) { + return; + } + tmLocal = *tmp; +#endif + + lpSystemTime->wYear = static_cast(tmLocal.tm_year + 1900); + lpSystemTime->wMonth = static_cast(tmLocal.tm_mon + 1); + lpSystemTime->wDayOfWeek = static_cast(tmLocal.tm_wday); + lpSystemTime->wDay = static_cast(tmLocal.tm_mday); + lpSystemTime->wHour = static_cast(tmLocal.tm_hour); + lpSystemTime->wMinute = static_cast(tmLocal.tm_min); + lpSystemTime->wSecond = static_cast(tmLocal.tm_sec); + lpSystemTime->wMilliseconds = 0; +} + +void WIN_FUNC GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) { + DEBUG_LOG("GetSystemTimeAsFileTime(%p)\n", lpSystemTimeAsFileTime); + if (!lpSystemTimeAsFileTime) { + return; + } + +#if defined(CLOCK_REALTIME) + struct timespec ts{}; + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { + uint64_t ticks = kUnixTimeZero; + ticks += static_cast(ts.tv_sec) * 10000000ULL; + ticks += static_cast(ts.tv_nsec) / 100ULL; + *lpSystemTimeAsFileTime = fileTimeFromDuration(ticks); + return; + } +#endif + + struct timeval tv{}; + if (gettimeofday(&tv, nullptr) == 0) { + uint64_t ticks = kUnixTimeZero; + ticks += static_cast(tv.tv_sec) * 10000000ULL; + ticks += static_cast(tv.tv_usec) * 10ULL; + *lpSystemTimeAsFileTime = fileTimeFromDuration(ticks); + return; + } + + const FILETIME fallback = {static_cast(kUnixTimeZero & 0xFFFFFFFFULL), + static_cast(kUnixTimeZero >> 32)}; + *lpSystemTimeAsFileTime = fallback; +} + +DWORD WIN_FUNC GetTickCount() { + DEBUG_LOG("GetTickCount()\n"); +#if defined(CLOCK_MONOTONIC) + struct timespec ts{}; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + uint64_t milliseconds = + static_cast(ts.tv_sec) * 1000ULL + static_cast(ts.tv_nsec) / 1000000ULL; + DWORD result = static_cast(milliseconds & 0xFFFFFFFFULL); + DEBUG_LOG(" -> %u\n", result); + return result; + } +#endif + struct timeval tv{}; + if (gettimeofday(&tv, nullptr) == 0) { + uint64_t milliseconds = + static_cast(tv.tv_sec) * 1000ULL + static_cast(tv.tv_usec) / 1000ULL; + DWORD result = static_cast(milliseconds & 0xFFFFFFFFULL); + DEBUG_LOG(" -> %u\n", result); + return result; + } + DEBUG_LOG(" -> 0\n"); + return 0; +} + +DWORD WIN_FUNC GetVersion() { + DEBUG_LOG("GetVersion()\n"); + return kMajorVersion | (kMinorVersion << 8) | (5 << 16) | (kBuildNumber << 24); +} + +BOOL WIN_FUNC GetVersionExA(LPOSVERSIONINFOA lpVersionInformation) { + DEBUG_LOG("GetVersionExA(%p)\n", lpVersionInformation); + if (!lpVersionInformation) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + std::memset(lpVersionInformation, 0, lpVersionInformation->dwOSVersionInfoSize); + lpVersionInformation->dwMajorVersion = kMajorVersion; + lpVersionInformation->dwMinorVersion = kMinorVersion; + lpVersionInformation->dwBuildNumber = kBuildNumber; + lpVersionInformation->dwPlatformId = 2; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + } // namespace kernel32 diff --git a/dll/kernel32/sysinfoapi.h b/dll/kernel32/sysinfoapi.h new file mode 100644 index 0000000..b195261 --- /dev/null +++ b/dll/kernel32/sysinfoapi.h @@ -0,0 +1,48 @@ +#pragma once + +#include "common.h" +#include "minwinbase.h" + +struct SYSTEM_INFO { + union { + DWORD dwOemId; + struct { + WORD wProcessorArchitecture; + WORD wReserved; + }; + }; + DWORD dwPageSize; + LPVOID lpMinimumApplicationAddress; + LPVOID lpMaximumApplicationAddress; + DWORD_PTR dwActiveProcessorMask; + DWORD dwNumberOfProcessors; + DWORD dwProcessorType; + DWORD dwAllocationGranularity; + WORD wProcessorLevel; + WORD wProcessorRevision; +}; + +using LPSYSTEM_INFO = SYSTEM_INFO *; + +struct OSVERSIONINFOA { + DWORD dwOSVersionInfoSize; + DWORD dwMajorVersion; + DWORD dwMinorVersion; + DWORD dwBuildNumber; + DWORD dwPlatformId; + char szCSDVersion[128]; +}; + +using LPOSVERSIONINFOA = OSVERSIONINFOA *; + +namespace kernel32 { + +void WIN_FUNC GetSystemInfo(LPSYSTEM_INFO lpSystemInfo); +void WIN_FUNC GetSystemTime(LPSYSTEMTIME lpSystemTime); +void WIN_FUNC GetLocalTime(LPSYSTEMTIME lpSystemTime); +void WIN_FUNC GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime); +DWORD WIN_FUNC GetTickCount(); +DWORD WIN_FUNC GetVersion(); +BOOL WIN_FUNC GetVersionExA(LPOSVERSIONINFOA lpVersionInformation); + +} // namespace kernel32 diff --git a/dll/kernel32/timeutil.h b/dll/kernel32/timeutil.h new file mode 100644 index 0000000..3492c3b --- /dev/null +++ b/dll/kernel32/timeutil.h @@ -0,0 +1,212 @@ +#pragma once + +#include "minwinbase.h" + +#include +#include +#include + +inline constexpr int64_t HUNDRED_NS_PER_SECOND = 10000000LL; +inline constexpr int64_t HUNDRED_NS_PER_MILLISECOND = 10000LL; +inline constexpr int64_t SECONDS_PER_DAY = 86400LL; +inline constexpr uint64_t TICKS_PER_DAY = static_cast(SECONDS_PER_DAY) * HUNDRED_NS_PER_SECOND; +inline constexpr uint64_t UNIX_TIME_ZERO = 11644473600ULL * 10000000ULL; +inline constexpr uint64_t MAX_VALID_FILETIME = 0x8000000000000000ULL; +inline constexpr int64_t DAYS_TO_UNIX_EPOCH = 134774LL; + +struct CivilDate { + int year; + unsigned month; + unsigned day; +}; + +inline int64_t daysFromCivil(int year, unsigned month, unsigned day) { + year -= month <= 2 ? 1 : 0; + const int era = (year >= 0 ? year : year - 399) / 400; + const unsigned yoe = static_cast(year - era * 400); + const unsigned doy = (153 * (month + (month > 2 ? -3 : 9)) + 2) / 5 + day - 1; + const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + yoe / 400 + doy; + return era * 146097 + static_cast(doe) - 719468; +} + +inline CivilDate civilFromDays(int64_t z) { + z += 719468; + const int64_t era = (z >= 0 ? z : z - 146096) / 146097; + const unsigned doe = static_cast(z - era * 146097); + const unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; + const int64_t y = static_cast(yoe) + era * 400; + const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); + const unsigned mp = (5 * doy + 2) / 153; + const unsigned d = doy - (153 * mp + 2) / 5 + 1; + const unsigned m = mp + (mp < 10 ? 3 : -9); + return {static_cast(y + (m <= 2)), m, d}; +} + +inline bool isLeapYear(int year) { + if ((year % 4) != 0) { + return false; + } + if ((year % 100) != 0) { + return true; + } + return (year % 400) == 0; +} + +inline unsigned daysInMonth(int year, unsigned month) { + static const unsigned baseDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + unsigned idx = month - 1; + unsigned value = baseDays[idx]; + if (month == 2 && isLeapYear(year)) { + value += 1; + } + return value; +} + +inline bool validateSystemTime(const SYSTEMTIME &st) { + if (st.wYear < 1601) { + return false; + } + if (st.wMonth < 1 || st.wMonth > 12) { + return false; + } + if (st.wDay < 1 || st.wDay > static_cast(daysInMonth(st.wYear, static_cast(st.wMonth)))) { + return false; + } + if (st.wHour < 0 || st.wHour > 23) { + return false; + } + if (st.wMinute < 0 || st.wMinute > 59) { + return false; + } + if (st.wSecond < 0 || st.wSecond > 59) { + return false; + } + if (st.wMilliseconds < 0 || st.wMilliseconds > 999) { + return false; + } + return true; +} + +inline bool systemTimeToUnixParts(const SYSTEMTIME &st, int64_t &secondsOut, uint32_t &hundredsOut) { + if (!validateSystemTime(st)) { + return false; + } + int64_t days = daysFromCivil(st.wYear, static_cast(st.wMonth), static_cast(st.wDay)); + int64_t secondsOfDay = + static_cast(st.wHour) * 3600LL + static_cast(st.wMinute) * 60LL + st.wSecond; + secondsOut = days * SECONDS_PER_DAY + secondsOfDay; + hundredsOut = static_cast(st.wMilliseconds) * static_cast(HUNDRED_NS_PER_MILLISECOND); + return true; +} + +inline uint64_t fileTimeToDuration(const FILETIME &value) { + return (static_cast(value.dwHighDateTime) << 32) | value.dwLowDateTime; +} + +inline bool fileTimeToUnixParts(const FILETIME &ft, int64_t &secondsOut, uint32_t &hundredsOut) { + uint64_t ticks = fileTimeToDuration(ft); + if (ticks >= UNIX_TIME_ZERO) { + uint64_t diff = ticks - UNIX_TIME_ZERO; + secondsOut = static_cast(diff / HUNDRED_NS_PER_SECOND); + hundredsOut = static_cast(diff % HUNDRED_NS_PER_SECOND); + } else { + uint64_t diff = UNIX_TIME_ZERO - ticks; + secondsOut = -static_cast(diff / HUNDRED_NS_PER_SECOND); + uint64_t rem = diff % HUNDRED_NS_PER_SECOND; + if (rem != 0) { + secondsOut -= 1; + rem = HUNDRED_NS_PER_SECOND - rem; + } + hundredsOut = static_cast(rem); + } + return true; +} + +inline FILETIME fileTimeFromDuration(uint64_t ticks100ns) { + FILETIME result; + result.dwLowDateTime = static_cast(ticks100ns & 0xFFFFFFFFULL); + result.dwHighDateTime = static_cast(ticks100ns >> 32); + return result; +} + +inline bool unixPartsToFileTime(int64_t seconds, uint32_t hundreds, FILETIME &out) { + if (hundreds >= HUNDRED_NS_PER_SECOND) { + return false; + } +#if defined(__SIZEOF_INT128__) + __int128 total = static_cast<__int128>(seconds) * HUNDRED_NS_PER_SECOND; + total += static_cast<__int128>(hundreds); + total += static_cast<__int128>(UNIX_TIME_ZERO); + if (total < 0 || total > static_cast<__int128>(std::numeric_limits::max())) { + return false; + } + uint64_t ticks = static_cast(total); +#else + long double total = static_cast(seconds) * static_cast(HUNDRED_NS_PER_SECOND); + total += static_cast(hundreds); + total += static_cast(UNIX_TIME_ZERO); + if (total < 0.0L || total > static_cast(std::numeric_limits::max())) { + return false; + } + uint64_t ticks = static_cast(total); +#endif + out = fileTimeFromDuration(ticks); + return true; +} + +inline bool unixPartsToTimespec(int64_t seconds, uint32_t hundreds, struct timespec &out) { + if (hundreds >= HUNDRED_NS_PER_SECOND) { + return false; + } + if (seconds > static_cast(std::numeric_limits::max()) || + seconds < static_cast(std::numeric_limits::min())) { + return false; + } + out.tv_sec = static_cast(seconds); + out.tv_nsec = static_cast(hundreds) * 100L; + return true; +} + +inline bool tmToUnixSeconds(const struct tm &tmValue, int64_t &secondsOut) { + int year = tmValue.tm_year + 1900; + int month = tmValue.tm_mon + 1; + int day = tmValue.tm_mday; + int hour = tmValue.tm_hour; + int minute = tmValue.tm_min; + int second = tmValue.tm_sec; + if (month < 1 || month > 12) { + return false; + } + if (day < 1 || day > static_cast(daysInMonth(year, static_cast(month)))) { + return false; + } + if (hour < 0 || hour > 23) { + return false; + } + if (minute < 0 || minute > 59) { + return false; + } + if (second < 0 || second > 60) { + return false; + } + if (second == 60) { + second = 59; + } + int64_t days = daysFromCivil(year, static_cast(month), static_cast(day)); + secondsOut = + days * SECONDS_PER_DAY + static_cast(hour) * 3600LL + static_cast(minute) * 60LL + second; + return true; +} + +inline bool shouldIgnoreFileTimeParam(const FILETIME *ft) { + if (!ft) { + return true; + } + if (ft->dwLowDateTime == 0 && ft->dwHighDateTime == 0) { + return true; + } + if (ft->dwLowDateTime == 0xFFFFFFFF && ft->dwHighDateTime == 0xFFFFFFFF) { + return true; + } + return false; +} diff --git a/dll/kernel32/timezoneapi.cpp b/dll/kernel32/timezoneapi.cpp new file mode 100644 index 0000000..4575ddc --- /dev/null +++ b/dll/kernel32/timezoneapi.cpp @@ -0,0 +1,278 @@ +#include "timezoneapi.h" + +#include "errors.h" +#include "timeutil.h" + +#include +#include +#include + +namespace kernel32 { + +BOOL WIN_FUNC SystemTimeToFileTime(const SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime) { + DEBUG_LOG("SystemTimeToFileTime(%p, %p)\n", lpSystemTime, lpFileTime); + if (!lpSystemTime || !lpFileTime) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + int64_t seconds = 0; + uint32_t hundreds = 0; + if (!systemTimeToUnixParts(*lpSystemTime, seconds, hundreds)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + FILETIME result; + if (!unixPartsToFileTime(seconds, hundreds, result)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + if (fileTimeToDuration(result) >= MAX_VALID_FILETIME) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + *lpFileTime = result; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC FileTimeToSystemTime(const FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime) { + DEBUG_LOG("FileTimeToSystemTime(%p, %p)\n", lpFileTime, lpSystemTime); + if (!lpFileTime || !lpSystemTime) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + uint64_t ticks = fileTimeToDuration(*lpFileTime); + if (ticks >= MAX_VALID_FILETIME) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + uint64_t daysSince1601 = ticks / TICKS_PER_DAY; + uint64_t ticksOfDay = ticks % TICKS_PER_DAY; + uint32_t secondsOfDay = static_cast(ticksOfDay / HUNDRED_NS_PER_SECOND); + uint32_t hundredNs = static_cast(ticksOfDay % HUNDRED_NS_PER_SECOND); + int64_t daysSince1970 = static_cast(daysSince1601) - DAYS_TO_UNIX_EPOCH; + CivilDate date = civilFromDays(daysSince1970); + lpSystemTime->wYear = static_cast(date.year); + lpSystemTime->wMonth = static_cast(date.month); + lpSystemTime->wDay = static_cast(date.day); + lpSystemTime->wDayOfWeek = static_cast((daysSince1601 + 1ULL) % 7ULL); + lpSystemTime->wHour = static_cast(secondsOfDay / 3600U); + lpSystemTime->wMinute = static_cast((secondsOfDay % 3600U) / 60U); + lpSystemTime->wSecond = static_cast(secondsOfDay % 60U); + lpSystemTime->wMilliseconds = static_cast(hundredNs / HUNDRED_NS_PER_MILLISECOND); + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC FileTimeToLocalFileTime(const FILETIME *lpFileTime, LPFILETIME lpLocalFileTime) { + DEBUG_LOG("FileTimeToLocalFileTime(%p, %p)\n", lpFileTime, lpLocalFileTime); + if (!lpFileTime || !lpLocalFileTime) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + int64_t seconds = 0; + uint32_t hundreds = 0; + if (!fileTimeToUnixParts(*lpFileTime, seconds, hundreds)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + if (seconds > static_cast(std::numeric_limits::max()) || + seconds < static_cast(std::numeric_limits::min())) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + time_t unixTime = static_cast(seconds); + struct tm localTm{}; +#if defined(_POSIX_VERSION) + if (!localtime_r(&unixTime, &localTm)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } +#else + struct tm *tmp = localtime(&unixTime); + if (!tmp) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + localTm = *tmp; +#endif + int64_t localAsUtcSeconds = 0; + if (!tmToUnixSeconds(localTm, localAsUtcSeconds)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + int64_t offsetSeconds = localAsUtcSeconds - seconds; + int64_t localSeconds = seconds + offsetSeconds; + FILETIME result; + if (!unixPartsToFileTime(localSeconds, hundreds, result)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + *lpLocalFileTime = result; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC LocalFileTimeToFileTime(const FILETIME *lpLocalFileTime, LPFILETIME lpFileTime) { + DEBUG_LOG("LocalFileTimeToFileTime(%p, %p)\n", lpLocalFileTime, lpFileTime); + if (!lpLocalFileTime || !lpFileTime) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + uint64_t ticks = fileTimeToDuration(*lpLocalFileTime); + if (ticks >= MAX_VALID_FILETIME) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + uint32_t hundredNs = static_cast(ticks % HUNDRED_NS_PER_SECOND); + uint64_t daysSince1601 = ticks / TICKS_PER_DAY; + uint64_t ticksOfDay = ticks % TICKS_PER_DAY; + uint32_t secondsOfDay = static_cast(ticksOfDay / HUNDRED_NS_PER_SECOND); + int64_t daysSince1970 = static_cast(daysSince1601) - DAYS_TO_UNIX_EPOCH; + CivilDate date = civilFromDays(daysSince1970); + struct tm localTm{}; + localTm.tm_year = date.year - 1900; + localTm.tm_mon = static_cast(date.month) - 1; + localTm.tm_mday = static_cast(date.day); + localTm.tm_hour = static_cast(secondsOfDay / 3600U); + localTm.tm_min = static_cast((secondsOfDay % 3600U) / 60U); + localTm.tm_sec = static_cast(secondsOfDay % 60U); + localTm.tm_isdst = -1; + struct tm tmCopy = localTm; + errno = 0; + time_t utcTime = mktime(&tmCopy); + if (utcTime == static_cast(-1) && errno != 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + FILETIME result; + if (!unixPartsToFileTime(static_cast(utcTime), hundredNs, result)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + *lpFileTime = result; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC DosDateTimeToFileTime(WORD wFatDate, WORD wFatTime, LPFILETIME lpFileTime) { + DEBUG_LOG("DosDateTimeToFileTime(%04x, %04x, %p)\n", wFatDate, wFatTime, lpFileTime); + if (!lpFileTime) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + unsigned day = wFatDate & 0x1F; + unsigned month = (wFatDate >> 5) & 0x0F; + unsigned year = ((wFatDate >> 9) & 0x7F) + 1980; + unsigned second = (wFatTime & 0x1F) * 2; + unsigned minute = (wFatTime >> 5) & 0x3F; + unsigned hour = (wFatTime >> 11) & 0x1F; + if (day == 0 || month == 0 || month > 12 || day > 31 || hour > 23 || minute > 59 || second > 59) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + struct tm tmValue{}; + tmValue.tm_year = static_cast(year) - 1900; + tmValue.tm_mon = static_cast(month) - 1; + tmValue.tm_mday = static_cast(day); + tmValue.tm_hour = static_cast(hour); + tmValue.tm_min = static_cast(minute); + tmValue.tm_sec = static_cast(second); + tmValue.tm_isdst = -1; + time_t localSeconds = mktime(&tmValue); + if (localSeconds == static_cast(-1)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + uint64_t ticks = (static_cast(localSeconds) + UNIX_TIME_ZERO / HUNDRED_NS_PER_SECOND) * 10000000ULL; + *lpFileTime = fileTimeFromDuration(ticks); + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC FileTimeToDosDateTime(const FILETIME *lpFileTime, LPWORD lpFatDate, LPWORD lpFatTime) { + DEBUG_LOG("FileTimeToDosDateTime(%p, %p, %p)\n", lpFileTime, lpFatDate, lpFatTime); + if (!lpFileTime || !lpFatDate || !lpFatTime) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + uint64_t ticks = fileTimeToDuration(*lpFileTime); + if (ticks < UNIX_TIME_ZERO) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + time_t utcSeconds = static_cast((ticks / 10000000ULL) - (UNIX_TIME_ZERO / HUNDRED_NS_PER_SECOND)); + struct tm tmValue{}; + if (!localtime_r(&utcSeconds, &tmValue)) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + int year = tmValue.tm_year + 1900; + if (year < 1980 || year > 2107) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + *lpFatDate = static_cast(((year - 1980) << 9) | ((tmValue.tm_mon + 1) << 5) | tmValue.tm_mday); + *lpFatTime = static_cast(((tmValue.tm_hour & 0x1F) << 11) | ((tmValue.tm_min & 0x3F) << 5) | + ((tmValue.tm_sec / 2) & 0x1F)); + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +DWORD WIN_FUNC GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation) { + DEBUG_LOG("GetTimeZoneInformation(%p)\n", lpTimeZoneInformation); + if (!lpTimeZoneInformation) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return TIME_ZONE_ID_INVALID; + } + std::memset(lpTimeZoneInformation, 0, sizeof(*lpTimeZoneInformation)); + tzset(); + auto copyName = [](WCHAR *dest, const char *src) { + if (!src) { + dest[0] = 0; + return; + } + for (size_t i = 0; i < 31 && src[i]; ++i) { + dest[i] = static_cast(src[i]); + dest[i + 1] = 0; + } + }; + time_t now = time(nullptr); + struct tm localTm{}; +#if defined(_GNU_SOURCE) || defined(__APPLE__) + localtime_r(&now, &localTm); +#else + struct tm *tmp = localtime(&now); + if (tmp) { + localTm = *tmp; + } +#endif + long offsetSeconds = 0; +#if defined(__APPLE__) || defined(__linux__) + offsetSeconds = -localTm.tm_gmtoff; +#else + extern long timezone; + offsetSeconds = timezone; + if (localTm.tm_isdst > 0) { + extern int daylight; + if (daylight) { + offsetSeconds -= 3600; + } + } +#endif + lpTimeZoneInformation->Bias = static_cast(offsetSeconds / 60); + copyName(lpTimeZoneInformation->StandardName, tzname[0]); + const char *daylightName = (daylight && tzname[1]) ? tzname[1] : tzname[0]; + copyName(lpTimeZoneInformation->DaylightName, daylightName); + DWORD result = TIME_ZONE_ID_UNKNOWN; + if (daylight && localTm.tm_isdst > 0) { + lpTimeZoneInformation->DaylightBias = -60; + result = TIME_ZONE_ID_DAYLIGHT; + } else { + result = TIME_ZONE_ID_STANDARD; + } + wibo::lastError = ERROR_SUCCESS; + return result; +} + +} // namespace kernel32 diff --git a/dll/kernel32/timezoneapi.h b/dll/kernel32/timezoneapi.h new file mode 100644 index 0000000..8fb1192 --- /dev/null +++ b/dll/kernel32/timezoneapi.h @@ -0,0 +1,33 @@ +#pragma once + +#include "common.h" +#include "minwinbase.h" + +struct TIME_ZONE_INFORMATION { + LONG Bias; + WCHAR StandardName[32]; + SYSTEMTIME StandardDate; + LONG StandardBias; + WCHAR DaylightName[32]; + SYSTEMTIME DaylightDate; + LONG DaylightBias; +}; + +using LPTIME_ZONE_INFORMATION = TIME_ZONE_INFORMATION *; + +constexpr DWORD TIME_ZONE_ID_UNKNOWN = 0; +constexpr DWORD TIME_ZONE_ID_STANDARD = 1; +constexpr DWORD TIME_ZONE_ID_DAYLIGHT = 2; +constexpr DWORD TIME_ZONE_ID_INVALID = 0xFFFFFFFFu; + +namespace kernel32 { + +BOOL WIN_FUNC SystemTimeToFileTime(const SYSTEMTIME *lpSystemTime, LPFILETIME lpFileTime); +BOOL WIN_FUNC FileTimeToSystemTime(const FILETIME *lpFileTime, LPSYSTEMTIME lpSystemTime); +BOOL WIN_FUNC FileTimeToLocalFileTime(const FILETIME *lpFileTime, LPFILETIME lpLocalFileTime); +BOOL WIN_FUNC LocalFileTimeToFileTime(const FILETIME *lpLocalFileTime, LPFILETIME lpFileTime); +BOOL WIN_FUNC DosDateTimeToFileTime(WORD wFatDate, WORD wFatTime, LPFILETIME lpFileTime); +BOOL WIN_FUNC FileTimeToDosDateTime(const FILETIME *lpFileTime, LPWORD lpFatDate, LPWORD lpFatTime); +DWORD WIN_FUNC GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation); + +} // namespace kernel32 diff --git a/dll/kernel32/winbase.cpp b/dll/kernel32/winbase.cpp new file mode 100644 index 0000000..6db448c --- /dev/null +++ b/dll/kernel32/winbase.cpp @@ -0,0 +1,575 @@ +#include "winbase.h" + +#include "errors.h" +#include "files.h" +#include "internal.h" +#include "strutil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +constexpr UINT GMEM_MOVEABLE = 0x0002; +constexpr UINT GMEM_ZEROINIT = 0x0040; +constexpr UINT GMEM_MODIFY = 0x0080; + +constexpr UINT LMEM_MOVEABLE = 0x0002; +constexpr UINT LMEM_ZEROINIT = 0x0040; + +void *doAlloc(UINT dwBytes, bool zero) { + if (dwBytes == 0) { + dwBytes = 1; + } + void *ret = mi_malloc_aligned(dwBytes, 8); + if (ret && zero) { + std::memset(ret, 0, mi_usable_size(ret)); + } + return ret; +} + +void *doRealloc(void *mem, UINT dwBytes, bool zero) { + if (dwBytes == 0) { + dwBytes = 1; + } + size_t oldSize = mi_usable_size(mem); + void *ret = mi_realloc_aligned(mem, dwBytes, 8); + size_t newSize = mi_usable_size(ret); + if (ret && zero && newSize > oldSize) { + std::memset(static_cast(ret) + oldSize, 0, newSize - oldSize); + } + return ret; +} + +bool tryGetCurrentDirectoryPath(std::string &outPath) { + std::error_code ec; + std::filesystem::path cwd = std::filesystem::current_path(ec); + if (ec) { + errno = ec.value(); + kernel32::setLastErrorFromErrno(); + return false; + } + outPath = files::pathToWindows(cwd); + return true; +} + +bool computeLongWindowsPath(const std::string &inputPath, std::string &longPath) { + bool hasTrailingSlash = false; + if (!inputPath.empty()) { + char last = inputPath.back(); + hasTrailingSlash = (last == '\\' || last == '/'); + } + + auto hostPath = files::pathFromWindows(inputPath.c_str()); + if (hostPath.empty()) { + wibo::lastError = ERROR_PATH_NOT_FOUND; + return false; + } + + std::error_code ec; + if (!std::filesystem::exists(hostPath, ec)) { + wibo::lastError = ERROR_FILE_NOT_FOUND; + return false; + } + + longPath = files::pathToWindows(hostPath); + if (hasTrailingSlash && !longPath.empty() && longPath.back() != '\\') { + longPath.push_back('\\'); + } + wibo::lastError = ERROR_SUCCESS; + return true; +} + +bool resolveDiskFreeSpaceStat(const char *rootPathName, struct statvfs &outBuf, std::string &resolvedPath) { + std::filesystem::path hostPath; + if (rootPathName && *rootPathName) { + hostPath = files::pathFromWindows(rootPathName); + } else { + std::error_code ec; + hostPath = std::filesystem::current_path(ec); + if (ec) { + wibo::lastError = ERROR_PATH_NOT_FOUND; + return false; + } + } + if (hostPath.empty()) { + wibo::lastError = ERROR_PATH_NOT_FOUND; + return false; + } + + hostPath = hostPath.lexically_normal(); + if (hostPath.empty()) { + hostPath = std::filesystem::path("/"); + } + + std::error_code ec; + if (!hostPath.is_absolute()) { + auto abs = std::filesystem::absolute(hostPath, ec); + if (ec) { + wibo::lastError = ERROR_PATH_NOT_FOUND; + return false; + } + hostPath = abs; + } + + std::filesystem::path queryPath = hostPath; + while (true) { + std::string query = queryPath.empty() ? std::string("/") : queryPath.string(); + if (query.empty()) { + query = "/"; + } + if (statvfs(query.c_str(), &outBuf) == 0) { + resolvedPath = query; + wibo::lastError = ERROR_SUCCESS; + return true; + } + + int savedErrno = errno; + if (savedErrno != ENOENT && savedErrno != ENOTDIR) { + errno = savedErrno; + kernel32::setLastErrorFromErrno(); + return false; + } + + std::filesystem::path parent = queryPath.parent_path(); + if (parent == queryPath) { + errno = savedErrno; + kernel32::setLastErrorFromErrno(); + return false; + } + if (parent.empty()) { + parent = std::filesystem::path("/"); + } + queryPath = parent; + } +} + +} // namespace + +namespace kernel32 { + +void tryMarkExecutable(void *mem) { + if (!mem) { + return; + } + size_t usable = mi_usable_size(mem); + if (usable == 0) { + return; + } + long pageSize = sysconf(_SC_PAGESIZE); + if (pageSize <= 0) { + return; + } + uintptr_t start = reinterpret_cast(mem); + uintptr_t alignedStart = start & ~static_cast(pageSize - 1); + uintptr_t end = (start + usable + pageSize - 1) & ~static_cast(pageSize - 1); + size_t length = static_cast(end - alignedStart); + if (length == 0) { + return; + } + mprotect(reinterpret_cast(alignedStart), length, PROT_READ | PROT_WRITE | PROT_EXEC); +} + +BOOL WIN_FUNC IsBadReadPtr(LPCVOID lp, UINT_PTR ucb) { + DEBUG_LOG("STUB: IsBadReadPtr(ptr=%p, size=%zu)\n", lp, static_cast(ucb)); + if (!lp) { + return TRUE; + } + return FALSE; +} + +BOOL WIN_FUNC IsBadWritePtr(LPVOID lp, UINT_PTR ucb) { + DEBUG_LOG("STUB: IsBadWritePtr(ptr=%p, size=%zu)\n", lp, static_cast(ucb)); + if (!lp && ucb != 0) { + return TRUE; + } + return FALSE; +} + +HGLOBAL WIN_FUNC GlobalAlloc(UINT uFlags, SIZE_T dwBytes) { + VERBOSE_LOG("GlobalAlloc(%x, %zu)\n", uFlags, static_cast(dwBytes)); + if (uFlags & GMEM_MOVEABLE) { + // not implemented rn + assert(0); + return nullptr; + } + bool zero = (uFlags & GMEM_ZEROINIT) != 0; + return doAlloc(static_cast(dwBytes), zero); +} + +HGLOBAL WIN_FUNC GlobalFree(HGLOBAL hMem) { + VERBOSE_LOG("GlobalFree(%p)\n", hMem); + std::free(hMem); + return nullptr; +} + +HGLOBAL WIN_FUNC GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags) { + VERBOSE_LOG("GlobalReAlloc(%p, %zu, %x)\n", hMem, static_cast(dwBytes), uFlags); + if (uFlags & GMEM_MODIFY) { + assert(0); + return nullptr; + } + bool zero = (uFlags & GMEM_ZEROINIT) != 0; + return doRealloc(hMem, static_cast(dwBytes), zero); +} + +UINT WIN_FUNC GlobalFlags(HGLOBAL hMem) { + VERBOSE_LOG("GlobalFlags(%p)\n", hMem); + (void)hMem; + return 0; +} + +HLOCAL WIN_FUNC LocalAlloc(UINT uFlags, SIZE_T uBytes) { + VERBOSE_LOG("LocalAlloc(%x, %zu)\n", uFlags, static_cast(uBytes)); + bool zero = (uFlags & LMEM_ZEROINIT) != 0; + if ((uFlags & LMEM_MOVEABLE) != 0) { + DEBUG_LOG(" ignoring LMEM_MOVEABLE\n"); + } + void *result = doAlloc(static_cast(uBytes), zero); + if (!result) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return nullptr; + } + // Legacy Windows applications (pre-NX and DEP) may expect executable memory from LocalAlloc. + tryMarkExecutable(result); + DEBUG_LOG(" -> %p\n", result); + wibo::lastError = ERROR_SUCCESS; + return result; +} + +HLOCAL WIN_FUNC LocalFree(HLOCAL hMem) { + VERBOSE_LOG("LocalFree(%p)\n", hMem); + // Windows returns NULL on success. + std::free(hMem); + wibo::lastError = ERROR_SUCCESS; + return nullptr; +} + +HLOCAL WIN_FUNC LocalReAlloc(HLOCAL hMem, SIZE_T uBytes, UINT uFlags) { + VERBOSE_LOG("LocalReAlloc(%p, %zu, %x)\n", hMem, static_cast(uBytes), uFlags); + bool zero = (uFlags & LMEM_ZEROINIT) != 0; + if ((uFlags & LMEM_MOVEABLE) != 0) { + DEBUG_LOG(" ignoring LMEM_MOVEABLE\n"); + } + void *result = doRealloc(hMem, static_cast(uBytes), zero); + if (!result && uBytes != 0) { + wibo::lastError = ERROR_NOT_SUPPORTED; + return nullptr; + } + // Legacy Windows applications (pre-NX and DEP) may expect executable memory from LocalReAlloc. + tryMarkExecutable(result); + DEBUG_LOG(" -> %p\n", result); + wibo::lastError = ERROR_SUCCESS; + return result; +} + +HLOCAL WIN_FUNC LocalHandle(LPCVOID pMem) { + VERBOSE_LOG("LocalHandle(%p)\n", pMem); + return const_cast(pMem); +} + +LPVOID WIN_FUNC LocalLock(HLOCAL hMem) { + VERBOSE_LOG("LocalLock(%p)\n", hMem); + wibo::lastError = ERROR_SUCCESS; + return hMem; +} + +BOOL WIN_FUNC LocalUnlock(HLOCAL hMem) { + VERBOSE_LOG("LocalUnlock(%p)\n", hMem); + (void)hMem; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +SIZE_T WIN_FUNC LocalSize(HLOCAL hMem) { + VERBOSE_LOG("LocalSize(%p)\n", hMem); + return hMem ? mi_usable_size(hMem) : 0; +} + +UINT WIN_FUNC LocalFlags(HLOCAL hMem) { + VERBOSE_LOG("LocalFlags(%p)\n", hMem); + (void)hMem; + return 0; +} + +UINT WIN_FUNC GetSystemDirectoryA(LPSTR lpBuffer, UINT uSize) { + DEBUG_LOG("GetSystemDirectoryA(%p, %u)\n", lpBuffer, uSize); + if (!lpBuffer) { + return 0; + } + + const char *systemDir = "C:\\Windows\\System32"; + const auto len = std::strlen(systemDir); + if (uSize < len + 1) { + return static_cast(len + 1); + } + std::strcpy(lpBuffer, systemDir); + return static_cast(len); +} + +UINT WIN_FUNC GetWindowsDirectoryA(LPSTR lpBuffer, UINT uSize) { + DEBUG_LOG("GetWindowsDirectoryA(%p, %u)\n", lpBuffer, uSize); + if (!lpBuffer) { + return 0; + } + + const char *windowsDir = "C:\\Windows"; + const auto len = std::strlen(windowsDir); + if (uSize < len + 1) { + return static_cast(len + 1); + } + std::strcpy(lpBuffer, windowsDir); + return static_cast(len); +} + +DWORD WIN_FUNC GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer) { + DEBUG_LOG("GetCurrentDirectoryA(%u, %p)\n", nBufferLength, lpBuffer); + + std::string path; + if (!tryGetCurrentDirectoryPath(path)) { + return 0; + } + + const DWORD required = static_cast(path.size() + 1); + if (nBufferLength == 0) { + return required; + } + if (!lpBuffer) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + if (nBufferLength < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return required; + } + std::memcpy(lpBuffer, path.c_str(), required); + wibo::lastError = ERROR_SUCCESS; + return required - 1; +} + +DWORD WIN_FUNC GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer) { + DEBUG_LOG("GetCurrentDirectoryW(%u, %p)\n", nBufferLength, lpBuffer); + + std::string path; + if (!tryGetCurrentDirectoryPath(path)) { + return 0; + } + auto widePath = stringToWideString(path.c_str()); + const DWORD required = static_cast(widePath.size()); + if (nBufferLength == 0) { + return required; + } + if (!lpBuffer) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + if (nBufferLength < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return required; + } + std::copy(widePath.begin(), widePath.end(), lpBuffer); + wibo::lastError = ERROR_SUCCESS; + return required - 1; +} + +int WIN_FUNC SetCurrentDirectoryA(LPCSTR lpPathName) { + DEBUG_LOG("SetCurrentDirectoryA(%s)\n", lpPathName ? lpPathName : "(null)"); + if (!lpPathName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + auto hostPath = files::pathFromWindows(lpPathName); + std::error_code ec; + std::filesystem::current_path(hostPath, ec); + if (ec) { + errno = ec.value(); + kernel32::setLastErrorFromErrno(); + return 0; + } + wibo::lastError = ERROR_SUCCESS; + return 1; +} + +int WIN_FUNC SetCurrentDirectoryW(LPCWSTR lpPathName) { + DEBUG_LOG("SetCurrentDirectoryW\n"); + if (!lpPathName) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string path = wideStringToString(lpPathName); + return SetCurrentDirectoryA(path.c_str()); +} + +DWORD WIN_FUNC GetLongPathNameA(LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer) { + DEBUG_LOG("GetLongPathNameA(%s, %p, %u)\n", lpszShortPath ? lpszShortPath : "(null)", lpszLongPath, cchBuffer); + if (!lpszShortPath) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + std::string input(lpszShortPath); + std::string longPath; + if (!computeLongWindowsPath(input, longPath)) { + return 0; + } + + DWORD required = static_cast(longPath.size() + 1); + if (cchBuffer == 0) { + return required; + } + if (!lpszLongPath) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + if (cchBuffer < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return required; + } + std::memcpy(lpszLongPath, longPath.c_str(), required); + wibo::lastError = ERROR_SUCCESS; + return required - 1; +} + +DWORD WIN_FUNC GetLongPathNameW(LPCWSTR lpszShortPath, LPWSTR lpszLongPath, DWORD cchBuffer) { + DEBUG_LOG("GetLongPathNameW(%p, %p, %u)\n", lpszShortPath, lpszLongPath, cchBuffer); + if (!lpszShortPath) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + std::string input = wideStringToString(lpszShortPath); + std::string longPath; + if (!computeLongWindowsPath(input, longPath)) { + return 0; + } + auto wideLong = stringToWideString(longPath.c_str()); + DWORD required = static_cast(wideLong.size()); + if (cchBuffer == 0) { + return required; + } + if (!lpszLongPath) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + if (cchBuffer < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return required; + } + std::copy(wideLong.begin(), wideLong.end(), lpszLongPath); + wibo::lastError = ERROR_SUCCESS; + return required - 1; +} + +BOOL WIN_FUNC GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, + LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters) { + DEBUG_LOG("GetDiskFreeSpaceA(%s)\n", lpRootPathName ? lpRootPathName : "(null)"); + struct statvfs buf{}; + std::string resolvedPath; + if (!resolveDiskFreeSpaceStat(lpRootPathName, buf, resolvedPath)) { + return FALSE; + } + + uint64_t blockSize = buf.f_frsize ? buf.f_frsize : buf.f_bsize; + if (blockSize == 0) { + blockSize = 4096; + } + unsigned int bytesPerSector = 512; + if (blockSize % bytesPerSector != 0) { + bytesPerSector = + static_cast(std::min(blockSize, std::numeric_limits::max())); + } + unsigned int sectorsPerCluster = static_cast(blockSize / bytesPerSector); + if (sectorsPerCluster == 0) { + sectorsPerCluster = 1; + bytesPerSector = + static_cast(std::min(blockSize, std::numeric_limits::max())); + } + + uint64_t totalClusters64 = buf.f_blocks; + uint64_t freeClusters64 = buf.f_bavail; + + if (lpSectorsPerCluster) { + *lpSectorsPerCluster = sectorsPerCluster; + } + if (lpBytesPerSector) { + *lpBytesPerSector = bytesPerSector; + } + if (lpNumberOfFreeClusters) { + uint64_t clamped = std::min(freeClusters64, std::numeric_limits::max()); + *lpNumberOfFreeClusters = static_cast(clamped); + } + if (lpTotalNumberOfClusters) { + uint64_t clamped = std::min(totalClusters64, std::numeric_limits::max()); + *lpTotalNumberOfClusters = static_cast(clamped); + } + + DEBUG_LOG("\t-> host %s, spc %u, bps %u, free clusters %u, total clusters %u\n", resolvedPath.c_str(), + lpSectorsPerCluster ? *lpSectorsPerCluster : 0, lpBytesPerSector ? *lpBytesPerSector : 0, + lpNumberOfFreeClusters ? *lpNumberOfFreeClusters : 0, + lpTotalNumberOfClusters ? *lpTotalNumberOfClusters : 0); + + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, + LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters) { + std::string rootPath = wideStringToString(lpRootPathName); + return GetDiskFreeSpaceA(lpRootPathName ? rootPath.c_str() : nullptr, lpSectorsPerCluster, lpBytesPerSector, + lpNumberOfFreeClusters, lpTotalNumberOfClusters); +} + +BOOL WIN_FUNC GetDiskFreeSpaceExA(LPCSTR lpDirectoryName, uint64_t *lpFreeBytesAvailableToCaller, + uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes) { + DEBUG_LOG("GetDiskFreeSpaceExA(%s)\n", lpDirectoryName ? lpDirectoryName : "(null)"); + struct statvfs buf{}; + std::string resolvedPath; + if (!resolveDiskFreeSpaceStat(lpDirectoryName, buf, resolvedPath)) { + return FALSE; + } + + uint64_t blockSize = buf.f_frsize ? buf.f_frsize : buf.f_bsize; + if (blockSize == 0) { + blockSize = 4096; + } + + uint64_t freeToCaller = static_cast(buf.f_bavail) * blockSize; + uint64_t totalBytes = static_cast(buf.f_blocks) * blockSize; + uint64_t totalFree = static_cast(buf.f_bfree) * blockSize; + + if (lpFreeBytesAvailableToCaller) { + *lpFreeBytesAvailableToCaller = freeToCaller; + } + if (lpTotalNumberOfBytes) { + *lpTotalNumberOfBytes = totalBytes; + } + if (lpTotalNumberOfFreeBytes) { + *lpTotalNumberOfFreeBytes = totalFree; + } + + DEBUG_LOG("\t-> host %s, free %llu, total %llu, total free %llu\n", resolvedPath.c_str(), + static_cast(freeToCaller), static_cast(totalBytes), + static_cast(totalFree)); + + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName, uint64_t *lpFreeBytesAvailableToCaller, + uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes) { + DEBUG_LOG("GetDiskFreeSpaceExW -> "); + std::string directoryName = wideStringToString(lpDirectoryName); + return GetDiskFreeSpaceExA(lpDirectoryName ? directoryName.c_str() : nullptr, lpFreeBytesAvailableToCaller, + lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes); +} + +} // namespace kernel32 diff --git a/dll/kernel32/winbase.h b/dll/kernel32/winbase.h new file mode 100644 index 0000000..f4ef751 --- /dev/null +++ b/dll/kernel32/winbase.h @@ -0,0 +1,41 @@ +#pragma once + +#include "common.h" + +namespace kernel32 { + +BOOL WIN_FUNC IsBadReadPtr(LPCVOID lp, UINT_PTR ucb); +BOOL WIN_FUNC IsBadWritePtr(LPVOID lp, UINT_PTR ucb); + +HGLOBAL WIN_FUNC GlobalAlloc(UINT uFlags, SIZE_T dwBytes); +HGLOBAL WIN_FUNC GlobalFree(HGLOBAL hMem); +HGLOBAL WIN_FUNC GlobalReAlloc(HGLOBAL hMem, SIZE_T dwBytes, UINT uFlags); +UINT WIN_FUNC GlobalFlags(HGLOBAL hMem); + +HLOCAL WIN_FUNC LocalAlloc(UINT uFlags, SIZE_T uBytes); +HLOCAL WIN_FUNC LocalFree(HLOCAL hMem); +HLOCAL WIN_FUNC LocalReAlloc(HLOCAL hMem, SIZE_T uBytes, UINT uFlags); +HLOCAL WIN_FUNC LocalHandle(LPCVOID pMem); +LPVOID WIN_FUNC LocalLock(HLOCAL hMem); +BOOL WIN_FUNC LocalUnlock(HLOCAL hMem); +SIZE_T WIN_FUNC LocalSize(HLOCAL hMem); +UINT WIN_FUNC LocalFlags(HLOCAL hMem); + +UINT WIN_FUNC GetSystemDirectoryA(LPSTR lpBuffer, UINT uSize); +UINT WIN_FUNC GetWindowsDirectoryA(LPSTR lpBuffer, UINT uSize); +DWORD WIN_FUNC GetCurrentDirectoryA(DWORD nBufferLength, LPSTR lpBuffer); +DWORD WIN_FUNC GetCurrentDirectoryW(DWORD nBufferLength, LPWSTR lpBuffer); +int WIN_FUNC SetCurrentDirectoryA(LPCSTR lpPathName); +int WIN_FUNC SetCurrentDirectoryW(LPCWSTR lpPathName); +DWORD WIN_FUNC GetLongPathNameA(LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer); +DWORD WIN_FUNC GetLongPathNameW(LPCWSTR lpszShortPath, LPWSTR lpszLongPath, DWORD cchBuffer); +BOOL WIN_FUNC GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, + LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters); +BOOL WIN_FUNC GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster, LPDWORD lpBytesPerSector, + LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters); +BOOL WIN_FUNC GetDiskFreeSpaceExA(LPCSTR lpDirectoryName, uint64_t *lpFreeBytesAvailableToCaller, + uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes); +BOOL WIN_FUNC GetDiskFreeSpaceExW(LPCWSTR lpDirectoryName, uint64_t *lpFreeBytesAvailableToCaller, + uint64_t *lpTotalNumberOfBytes, uint64_t *lpTotalNumberOfFreeBytes); + +} // namespace kernel32 diff --git a/dll/kernel32/wincon.cpp b/dll/kernel32/wincon.cpp new file mode 100644 index 0000000..5376d78 --- /dev/null +++ b/dll/kernel32/wincon.cpp @@ -0,0 +1,112 @@ +#include "wincon.h" + +#include "errors.h" +#include "files.h" +#include "strutil.h" + +#include + +namespace kernel32 { + +BOOL WIN_FUNC GetConsoleMode(HANDLE hConsoleHandle, LPDWORD lpMode) { + DEBUG_LOG("STUB: GetConsoleMode(%p)\n", hConsoleHandle); + if (lpMode) { + *lpMode = 0; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC SetConsoleMode(HANDLE hConsoleHandle, DWORD dwMode) { + DEBUG_LOG("STUB: SetConsoleMode(%p, 0x%x)\n", hConsoleHandle, dwMode); + (void)hConsoleHandle; + (void)dwMode; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +UINT WIN_FUNC GetConsoleOutputCP() { + DEBUG_LOG("STUB: GetConsoleOutputCP() -> 65001\n"); + wibo::lastError = ERROR_SUCCESS; + return 65001; // UTF-8 +} + +BOOL WIN_FUNC SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine, BOOL Add) { + DEBUG_LOG("STUB: SetConsoleCtrlHandler(%p, %u)\n", reinterpret_cast(HandlerRoutine), Add); + (void)HandlerRoutine; + (void)Add; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFO *lpConsoleScreenBufferInfo) { + DEBUG_LOG("STUB: GetConsoleScreenBufferInfo(%p, %p)\n", hConsoleOutput, lpConsoleScreenBufferInfo); + (void)hConsoleOutput; + if (!lpConsoleScreenBufferInfo) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + lpConsoleScreenBufferInfo->dwSize = {80, 25}; + lpConsoleScreenBufferInfo->dwCursorPosition = {0, 0}; + lpConsoleScreenBufferInfo->wAttributes = 0; + lpConsoleScreenBufferInfo->srWindow = {0, 0, 79, 24}; + lpConsoleScreenBufferInfo->dwMaximumWindowSize = {80, 25}; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC WriteConsoleW(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite, + LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved) { + DEBUG_LOG("WriteConsoleW(%p, %p, %u, %p, %p)\n", hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, + lpNumberOfCharsWritten, lpReserved); + (void)lpReserved; + if (lpNumberOfCharsWritten) { + *lpNumberOfCharsWritten = 0; + } + if (!lpBuffer && nNumberOfCharsToWrite != 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + FILE *fp = files::fpFromHandle(hConsoleOutput); + if (fp == stdout || fp == stderr) { + auto str = wideStringToString(static_cast(lpBuffer), nNumberOfCharsToWrite); + fprintf(fp, "%s", str.c_str()); + if (lpNumberOfCharsWritten) { + *lpNumberOfCharsWritten = nNumberOfCharsToWrite; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; + } + + wibo::lastError = ERROR_INVALID_HANDLE; + return FALSE; +} + +BOOL WIN_FUNC PeekConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength, + LPDWORD lpNumberOfEventsRead) { + DEBUG_LOG("STUB: PeekConsoleInputA(%p, %p, %u)\n", hConsoleInput, lpBuffer, nLength); + (void)hConsoleInput; + (void)lpBuffer; + (void)nLength; + if (lpNumberOfEventsRead) { + *lpNumberOfEventsRead = 0; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC ReadConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength, + LPDWORD lpNumberOfEventsRead) { + DEBUG_LOG("STUB: ReadConsoleInputA(%p, %p, %u)\n", hConsoleInput, lpBuffer, nLength); + (void)hConsoleInput; + (void)lpBuffer; + (void)nLength; + if (lpNumberOfEventsRead) { + *lpNumberOfEventsRead = 0; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +} // namespace kernel32 diff --git a/dll/kernel32/wincon.h b/dll/kernel32/wincon.h new file mode 100644 index 0000000..0570698 --- /dev/null +++ b/dll/kernel32/wincon.h @@ -0,0 +1,43 @@ +#pragma once + +#include "common.h" + +struct COORD { + SHORT X; + SHORT Y; +}; + +struct SMALL_RECT { + SHORT Left; + SHORT Top; + SHORT Right; + SHORT Bottom; +}; + +struct CONSOLE_SCREEN_BUFFER_INFO { + COORD dwSize; + COORD dwCursorPosition; + WORD wAttributes; + SMALL_RECT srWindow; + COORD dwMaximumWindowSize; +}; + +struct INPUT_RECORD; + +using PHANDLER_ROUTINE = BOOL(WIN_FUNC *)(DWORD CtrlType); + +namespace kernel32 { + +BOOL WIN_FUNC GetConsoleMode(HANDLE hConsoleHandle, LPDWORD lpMode); +BOOL WIN_FUNC SetConsoleMode(HANDLE hConsoleHandle, DWORD dwMode); +UINT WIN_FUNC GetConsoleOutputCP(); +BOOL WIN_FUNC SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine, BOOL Add); +BOOL WIN_FUNC GetConsoleScreenBufferInfo(HANDLE hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFO *lpConsoleScreenBufferInfo); +BOOL WIN_FUNC WriteConsoleW(HANDLE hConsoleOutput, LPCVOID lpBuffer, DWORD nNumberOfCharsToWrite, + LPDWORD lpNumberOfCharsWritten, LPVOID lpReserved); +BOOL WIN_FUNC PeekConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength, + LPDWORD lpNumberOfEventsRead); +BOOL WIN_FUNC ReadConsoleInputA(HANDLE hConsoleInput, INPUT_RECORD *lpBuffer, DWORD nLength, + LPDWORD lpNumberOfEventsRead); + +} // namespace kernel32 diff --git a/dll/kernel32/winnls.cpp b/dll/kernel32/winnls.cpp new file mode 100644 index 0000000..0a359e1 --- /dev/null +++ b/dll/kernel32/winnls.cpp @@ -0,0 +1,366 @@ +#include "winnls.h" + +#include "errors.h" +#include "strutil.h" + +#include +#include +#include +#include +#include +#include + +namespace { + +constexpr DWORD kNormIgnoreCase = 0x00000001; +constexpr DWORD LCID_INSTALLED = 0x00000001; +constexpr DWORD LCID_SUPPORTED = 0x00000002; +constexpr DWORD LCID_ALTERNATE_SORTS = 0x00000004; + +int compareStrings(const std::string &a, const std::string &b, DWORD dwCmpFlags) { + for (size_t i = 0;; ++i) { + if (i == a.size()) { + if (i == b.size()) { + return 2; // CSTR_EQUAL + } + return 1; // CSTR_LESS_THAN + } + if (i == b.size()) { + return 3; // CSTR_GREATER_THAN + } + unsigned char c = static_cast(a[i]); + unsigned char d = static_cast(b[i]); + if (dwCmpFlags & kNormIgnoreCase) { + if (c >= 'a' && c <= 'z') { + c = static_cast(c - ('a' - 'A')); + } + if (d >= 'a' && d <= 'z') { + d = static_cast(d - ('a' - 'A')); + } + } + if (c != d) { + return (c < d) ? 1 : 3; + } + } +} + +std::string localeInfoString(int LCType) { + switch (LCType) { + case 4100: // LOCALE_IDEFAULTANSICODEPAGE + return "28591"; + case 4097: // LOCALE_SENGLANGUAGE + return "Lang"; + case 4098: // LOCALE_SENGCOUNTRY + return "Country"; + case 0x1: // LOCALE_ILANGUAGE + return "0001"; + case 0x15: // LOCALE_SINTLSYMBOL + return "Currency"; + case 0x14: // LOCALE_SCURRENCY + return "sCurrency"; + case 0x16: // LOCALE_SMONDECIMALSEP + return "."; + case 0x17: // LOCALE_SMONTHOUSANDSEP + return ","; + case 0x18: // LOCALE_SMONGROUPING + return ";"; + case 0x50: // LOCALE_SPOSITIVESIGN + return ""; + case 0x51: // LOCALE_SNEGATIVESIGN + return "-"; + case 0x1A: // LOCALE_IINTLCURRDIGITS + case 0x19: // LOCALE_ICURRDIGITS + return "2"; + default: + DEBUG_LOG("STUB: GetLocaleInfo LCType 0x%x not implemented\n", LCType); + return ""; + } +} + +} // namespace + +namespace kernel32 { + +UINT WIN_FUNC GetACP() { + DEBUG_LOG("GetACP() -> %u\n", 28591); + wibo::lastError = ERROR_SUCCESS; + return 28591; // Latin1 (ISO/IEC 8859-1) +} + +LANGID WIN_FUNC GetSystemDefaultLangID() { + DEBUG_LOG("STUB: GetSystemDefaultLangID()\n"); + return 0; +} + +LANGID WIN_FUNC GetUserDefaultUILanguage() { + DEBUG_LOG("STUB: GetUserDefaultUILanguage()\n"); + return 0; +} + +BOOL WIN_FUNC GetCPInfo(UINT CodePage, LPCPINFO lpCPInfo) { + DEBUG_LOG("GetCPInfo(%u, %p)\n", CodePage, lpCPInfo); + (void)CodePage; + + if (!lpCPInfo) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + + lpCPInfo->MaxCharSize = 1; + std::fill(lpCPInfo->DefaultChar, lpCPInfo->DefaultChar + MAX_DEFAULTCHAR, 0); + lpCPInfo->DefaultChar[0] = '?'; + std::fill(lpCPInfo->LeadByte, lpCPInfo->LeadByte + MAX_LEADBYTES, 0); + + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +int WIN_FUNC CompareStringA(LCID Locale, DWORD dwCmpFlags, LPCSTR lpString1, int cchCount1, LPCSTR lpString2, + int cchCount2) { + DEBUG_LOG("CompareStringA(%u, %u, %s, %d, %s, %d)\n", Locale, dwCmpFlags, lpString1 ? lpString1 : "(null)", + cchCount1, lpString2 ? lpString2 : "(null)", cchCount2); + (void)Locale; + if (!lpString1 || !lpString2) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + if (cchCount1 < 0) { + cchCount1 = static_cast(strlen(lpString1)); + } + if (cchCount2 < 0) { + cchCount2 = static_cast(strlen(lpString2)); + } + + std::string str1(lpString1, lpString1 + cchCount1); + std::string str2(lpString2, lpString2 + cchCount2); + wibo::lastError = ERROR_SUCCESS; + return compareStrings(str1, str2, dwCmpFlags); +} + +int WIN_FUNC CompareStringW(LCID Locale, DWORD dwCmpFlags, LPCWCH lpString1, int cchCount1, LPCWCH lpString2, + int cchCount2) { + DEBUG_LOG("CompareStringW(%u, %u, %p, %d, %p, %d)\n", Locale, dwCmpFlags, lpString1, cchCount1, lpString2, + cchCount2); + (void)Locale; + if (!lpString1 || !lpString2) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + std::string str1 = wideStringToString(lpString1, cchCount1); + std::string str2 = wideStringToString(lpString2, cchCount2); + wibo::lastError = ERROR_SUCCESS; + return compareStrings(str1, str2, dwCmpFlags); +} + +BOOL WIN_FUNC IsValidCodePage(UINT CodePage) { + DEBUG_LOG("IsValidCodePage(%u)\n", CodePage); + (void)CodePage; + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +BOOL WIN_FUNC IsValidLocale(LCID Locale, DWORD dwFlags) { + DEBUG_LOG("IsValidLocale(%u, 0x%x)\n", Locale, dwFlags); + (void)Locale; + if (dwFlags != 0 && (dwFlags & ~(LCID_INSTALLED | LCID_SUPPORTED | LCID_ALTERNATE_SORTS)) != 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + wibo::lastError = ERROR_SUCCESS; + return TRUE; +} + +int WIN_FUNC GetLocaleInfoA(LCID Locale, LCTYPE LCType, LPSTR lpLCData, int cchData) { + DEBUG_LOG("GetLocaleInfoA(%u, %u, %p, %d)\n", Locale, LCType, lpLCData, cchData); + (void)Locale; + + std::string value = localeInfoString(static_cast(LCType)); + size_t required = value.size() + 1; + + if (cchData == 0) { + wibo::lastError = ERROR_SUCCESS; + return static_cast(required); + } + if (!lpLCData || cchData < 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + if (static_cast(cchData) < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return 0; + } + + std::memcpy(lpLCData, value.c_str(), required); + wibo::lastError = ERROR_SUCCESS; + return static_cast(required); +} + +int WIN_FUNC GetLocaleInfoW(LCID Locale, LCTYPE LCType, LPWSTR lpLCData, int cchData) { + DEBUG_LOG("GetLocaleInfoW(%u, %u, %p, %d)\n", Locale, LCType, lpLCData, cchData); + (void)Locale; + + std::string info = localeInfoString(static_cast(LCType)); + auto wide = stringToWideString(info.c_str()); + size_t required = wide.size(); + + if (cchData == 0) { + wibo::lastError = ERROR_SUCCESS; + return static_cast(required); + } + if (!lpLCData || cchData < 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + if (static_cast(cchData) < required) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return 0; + } + + std::memcpy(lpLCData, wide.data(), required * sizeof(uint16_t)); + wibo::lastError = ERROR_SUCCESS; + return static_cast(required); +} + +BOOL WIN_FUNC EnumSystemLocalesA(LOCALE_ENUMPROCA lpLocaleEnumProc, DWORD dwFlags) { + DEBUG_LOG("EnumSystemLocalesA(%p, 0x%x)\n", lpLocaleEnumProc, dwFlags); + (void)dwFlags; + if (!lpLocaleEnumProc) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return FALSE; + } + char localeId[] = "00000409"; // en-US + BOOL callbackResult = lpLocaleEnumProc(localeId); + wibo::lastError = ERROR_SUCCESS; + return callbackResult; +} + +LCID WIN_FUNC GetUserDefaultLCID() { + DEBUG_LOG("GetUserDefaultLCID()\n"); + wibo::lastError = ERROR_SUCCESS; + return 0x0409; // en-US +} + +BOOL WIN_FUNC IsDBCSLeadByte(BYTE TestChar) { + DEBUG_LOG("IsDBCSLeadByte(%u)\n", TestChar); + (void)TestChar; + wibo::lastError = ERROR_SUCCESS; + return FALSE; +} + +BOOL WIN_FUNC IsDBCSLeadByteEx(UINT CodePage, BYTE TestChar) { + DEBUG_LOG("IsDBCSLeadByteEx(%u, %u)\n", CodePage, TestChar); + + auto inRanges = [TestChar](std::initializer_list> ranges) -> BOOL { + for (const auto &range : ranges) { + if (TestChar >= range.first && TestChar <= range.second) { + return TRUE; + } + } + return FALSE; + }; + + switch (CodePage) { + case 932: // Shift-JIS + wibo::lastError = ERROR_SUCCESS; + return inRanges({{0x81, 0x9F}, {0xE0, 0xFC}}); + case 936: // GBK + case 949: // Korean + case 950: // Big5 + case 1361: // Johab + wibo::lastError = ERROR_SUCCESS; + return inRanges({{0x81, 0xFE}}); + case 0: // CP_ACP + case 1: // CP_OEMCP + case 2: // CP_MACCP + case 3: // CP_THREAD_ACP + default: + wibo::lastError = ERROR_SUCCESS; + return FALSE; + } +} + +int WIN_FUNC LCMapStringW(LCID Locale, DWORD dwMapFlags, LPCWCH lpSrcStr, int cchSrc, LPWSTR lpDestStr, int cchDest) { + DEBUG_LOG("LCMapStringW(%u, 0x%x, %p, %d, %p, %d)\n", Locale, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest); + (void)Locale; + if (!lpSrcStr || cchSrc == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + bool nullTerminated = cchSrc < 0; + size_t srcLen = nullTerminated ? (wstrlen(lpSrcStr) + 1) : static_cast(cchSrc); + if (srcLen == 0) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + if (!lpDestStr || cchDest == 0) { + wibo::lastError = ERROR_SUCCESS; + return static_cast(srcLen); + } + if (cchDest < static_cast(srcLen)) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return 0; + } + + if (dwMapFlags & (0x00000400u | 0x00000800u)) { // LCMAP_SORTKEY | LCMAP_BYTEREV + DEBUG_LOG("LCMapStringW: unsupported mapping flags 0x%x\n", dwMapFlags); + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + + unsigned int casingFlags = dwMapFlags & (0x00000200u | 0x00000100u); // UPPERCASE | LOWERCASE + std::vector buffer(srcLen, 0); + for (size_t i = 0; i < srcLen; ++i) { + uint16_t ch = lpSrcStr[i]; + if (casingFlags == 0x00000200u) { + buffer[i] = static_cast(std::towupper(static_cast(ch))); + } else if (casingFlags == 0x00000100u) { + buffer[i] = static_cast(std::towlower(static_cast(ch))); + } else { + buffer[i] = ch; + } + } + + std::memcpy(lpDestStr, buffer.data(), srcLen * sizeof(uint16_t)); + wibo::lastError = ERROR_SUCCESS; + return static_cast(srcLen); +} + +int WIN_FUNC LCMapStringA(LCID Locale, DWORD dwMapFlags, LPCCH lpSrcStr, int cchSrc, LPSTR lpDestStr, int cchDest) { + DEBUG_LOG("LCMapStringA(%u, 0x%x, %p, %d, %p, %d)\n", Locale, dwMapFlags, lpSrcStr, cchSrc, lpDestStr, cchDest); + if (!lpSrcStr) { + wibo::lastError = ERROR_INVALID_PARAMETER; + return 0; + } + int length = cchSrc; + if (length < 0) { + length = static_cast(strlen(lpSrcStr)) + 1; + } + + auto wideSrc = stringToWideString(lpSrcStr, static_cast(length)); + std::vector wideDest(std::max(cchDest, 0)); + int wideResult = + LCMapStringW(Locale, dwMapFlags, wideSrc.data(), length, wideDest.empty() ? nullptr : wideDest.data(), cchDest); + if (wideResult == 0) { + return 0; + } + + if (!lpDestStr || cchDest == 0) { + return wideResult; + } + + auto mapped = wideStringToString(wideDest.data(), wideResult); + size_t bytesToCopy = mapped.size() + 1; + if (static_cast(cchDest) < bytesToCopy) { + wibo::lastError = ERROR_INSUFFICIENT_BUFFER; + return 0; + } + std::memcpy(lpDestStr, mapped.c_str(), bytesToCopy); + wibo::lastError = ERROR_SUCCESS; + return wideResult; +} + +} // namespace kernel32 diff --git a/dll/kernel32/winnls.h b/dll/kernel32/winnls.h new file mode 100644 index 0000000..21369b5 --- /dev/null +++ b/dll/kernel32/winnls.h @@ -0,0 +1,38 @@ +#pragma once + +#include "common.h" + +constexpr UINT MAX_DEFAULTCHAR = 2; +constexpr UINT MAX_LEADBYTES = 12; + +struct CPINFO { + UINT MaxCharSize; + BYTE DefaultChar[MAX_DEFAULTCHAR]; + BYTE LeadByte[MAX_LEADBYTES]; +}; + +using LPCPINFO = CPINFO *; +using LOCALE_ENUMPROCA = BOOL(WIN_FUNC *)(LPSTR); + +namespace kernel32 { + +UINT WIN_FUNC GetACP(); +LANGID WIN_FUNC GetSystemDefaultLangID(); +LANGID WIN_FUNC GetUserDefaultUILanguage(); +BOOL WIN_FUNC GetCPInfo(UINT CodePage, LPCPINFO lpCPInfo); +int WIN_FUNC CompareStringA(LCID Locale, DWORD dwCmpFlags, LPCSTR lpString1, int cchCount1, LPCSTR lpString2, + int cchCount2); +int WIN_FUNC CompareStringW(LCID Locale, DWORD dwCmpFlags, LPCWCH lpString1, int cchCount1, LPCWCH lpString2, + int cchCount2); +BOOL WIN_FUNC IsValidCodePage(UINT CodePage); +BOOL WIN_FUNC IsValidLocale(LCID Locale, DWORD dwFlags); +int WIN_FUNC GetLocaleInfoA(LCID Locale, LCTYPE LCType, LPSTR lpLCData, int cchData); +int WIN_FUNC GetLocaleInfoW(LCID Locale, LCTYPE LCType, LPWSTR lpLCData, int cchData); +BOOL WIN_FUNC EnumSystemLocalesA(LOCALE_ENUMPROCA lpLocaleEnumProc, DWORD dwFlags); +LCID WIN_FUNC GetUserDefaultLCID(); +BOOL WIN_FUNC IsDBCSLeadByte(BYTE TestChar); +BOOL WIN_FUNC IsDBCSLeadByteEx(UINT CodePage, BYTE TestChar); +int WIN_FUNC LCMapStringW(LCID Locale, DWORD dwMapFlags, LPCWCH lpSrcStr, int cchSrc, LPWSTR lpDestStr, int cchDest); +int WIN_FUNC LCMapStringA(LCID Locale, DWORD dwMapFlags, LPCCH lpSrcStr, int cchSrc, LPSTR lpDestStr, int cchDest); + +} // namespace kernel32 diff --git a/dll/kernel32/wow64apiset.cpp b/dll/kernel32/wow64apiset.cpp index 3c42d9b..8d1ed09 100644 --- a/dll/kernel32/wow64apiset.cpp +++ b/dll/kernel32/wow64apiset.cpp @@ -1,7 +1,7 @@ +#include "wow64apiset.h" #include "common.h" #include "errors.h" #include "handles.h" -#include "kernel32.h" namespace kernel32 { @@ -28,7 +28,7 @@ BOOL WIN_FUNC IsWow64Process(HANDLE hProcess, PBOOL Wow64Process) { return FALSE; } - const auto rawHandle = reinterpret_cast(hProcess); + uintptr_t rawHandle = reinterpret_cast(hProcess); bool isPseudoHandle = rawHandle == static_cast(-1); if (!isPseudoHandle) { if (!hProcess) { diff --git a/dll/kernel32/wow64apiset.h b/dll/kernel32/wow64apiset.h new file mode 100644 index 0000000..23b72b1 --- /dev/null +++ b/dll/kernel32/wow64apiset.h @@ -0,0 +1,11 @@ +#pragma once + +#include "common.h" + +namespace kernel32 { + +BOOL WIN_FUNC Wow64DisableWow64FsRedirection(PVOID *OldValue); +BOOL WIN_FUNC Wow64RevertWow64FsRedirection(PVOID OldValue); +BOOL WIN_FUNC IsWow64Process(HANDLE hProcess, PBOOL Wow64Process); + +} // namespace kernel32 diff --git a/errors.h b/errors.h index b1a29a9..e2bed44 100644 --- a/errors.h +++ b/errors.h @@ -16,6 +16,7 @@ #define ERROR_BROKEN_PIPE 109 #define ERROR_NOT_SUPPORTED 50 #define ERROR_INVALID_PARAMETER 87 +#define ERROR_ENVVAR_NOT_FOUND 203 #define ERROR_CALL_NOT_IMPLEMENTED 120 #define ERROR_BUFFER_OVERFLOW 111 #define ERROR_INSUFFICIENT_BUFFER 122 diff --git a/main.cpp b/main.cpp index 6da611f..2fde129 100644 --- a/main.cpp +++ b/main.cpp @@ -12,10 +12,11 @@ #include #include #include +#include #include #include -uint32_t wibo::lastError = 0; +thread_local uint32_t wibo::lastError = 0; char **wibo::argv; int wibo::argc; std::filesystem::path wibo::guestExecutablePath; @@ -369,7 +370,7 @@ int main(int argc, char **argv) { // Build a command line if (cmdLine.empty()) { - for (int i = 0; i < guestArgs.size(); ++i) { + for (size_t i = 0; i < guestArgs.size(); ++i) { if (i != 0) { cmdLine += ' '; } diff --git a/strutil.cpp b/strutil.cpp index b6c8e9d..3a542d4 100644 --- a/strutil.cpp +++ b/strutil.cpp @@ -2,20 +2,20 @@ #include "common.h" #include #include -#include #include +#include #include #include #include void toLowerInPlace(std::string &str) { std::transform(str.begin(), str.end(), str.begin(), - [](unsigned char c) { return static_cast(std::tolower(c)); }); + [](unsigned char c) { return static_cast(std::tolower(c)); }); } void toUpperInPlace(std::string &str) { std::transform(str.begin(), str.end(), str.begin(), - [](unsigned char c) { return static_cast(std::toupper(c)); }); + [](unsigned char c) { return static_cast(std::toupper(c)); }); } std::string stringToLower(std::string_view str) { @@ -205,15 +205,16 @@ std::string wideStringToString(const uint16_t *src, int len) { return result; } -std::vector stringToWideString(const char *src) { - if (!src) +std::vector stringToWideString(const char *src, size_t length) { + if (!src) { return std::vector{0}; - size_t len = strlen(src); - std::vector res(len + 1); - for (size_t i = 0; i < res.size(); i++) { - res[i] = static_cast(src[i] & 0xFF); } - res[len] = 0; // NUL terminate + size_t len = length == static_cast(-1) ? strlen(src) : length; + std::vector res(len + 1); + for (size_t i = 0; i < len; ++i) { + res[i] = static_cast(static_cast(src[i])); + } + res[len] = 0; // ensure NUL termination return res; } diff --git a/strutil.h b/strutil.h index 69289f4..43f1446 100644 --- a/strutil.h +++ b/strutil.h @@ -16,7 +16,7 @@ uint16_t *wstrncat(uint16_t *dest, const uint16_t *src, size_t count); uint16_t *wstrcpy(uint16_t *dest, const uint16_t *src); size_t wstrncpy(uint16_t *dst, const uint16_t *src, size_t n); std::string wideStringToString(const uint16_t *src, int len = -1); -std::vector stringToWideString(const char *src); +std::vector stringToWideString(const char *src, size_t length = static_cast(-1)); long wstrtol(const uint16_t *string, uint16_t **end_ptr, int base); unsigned long wstrtoul(const uint16_t *string, uint16_t **end_ptr, int base); void toLowerInPlace(std::string &str);