mirror of
https://github.com/decompals/wibo.git
synced 2025-10-15 14:45:12 +00:00
Reimplement kernel32 time functions, fix HeapReAlloc, debug log improvements
This commit is contained in:
parent
02d26c7bb9
commit
ccd79a256a
@ -163,6 +163,17 @@ if(BUILD_TESTING)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_overlapped_io.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WIBO_TEST_BIN_DIR}/test_time.exe
|
||||
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||
-I${CMAKE_CURRENT_SOURCE_DIR}/test
|
||||
-o test_time.exe
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_time.c
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_time.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||
|
||||
add_custom_target(wibo_test_fixtures
|
||||
DEPENDS
|
||||
${WIBO_TEST_BIN_DIR}/external_exports.dll
|
||||
@ -171,7 +182,8 @@ if(BUILD_TESTING)
|
||||
${WIBO_TEST_BIN_DIR}/test_resources.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_threading.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_heap.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe)
|
||||
${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_time.exe)
|
||||
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
set(_wibo_fixture_build_command
|
||||
@ -218,6 +230,12 @@ if(BUILD_TESTING)
|
||||
set_tests_properties(wibo.test_overlapped_io PROPERTIES
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS wibo.build_fixtures)
|
||||
|
||||
add_test(NAME wibo.test_time
|
||||
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_time.exe)
|
||||
set_tests_properties(wibo.test_time PROPERTIES
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS wibo.build_fixtures)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
5
common.h
5
common.h
@ -59,6 +59,7 @@ typedef UCHAR *PUCHAR;
|
||||
typedef size_t SIZE_T;
|
||||
typedef SIZE_T *PSIZE_T;
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned int UINT;
|
||||
|
||||
typedef struct _OVERLAPPED {
|
||||
ULONG_PTR Internal;
|
||||
@ -87,6 +88,10 @@ typedef struct _OVERLAPPED {
|
||||
|
||||
#define MAX_PATH (260)
|
||||
|
||||
#define STD_INPUT_HANDLE ((DWORD) - 10)
|
||||
#define STD_OUTPUT_HANDLE ((DWORD) - 11)
|
||||
#define STD_ERROR_HANDLE ((DWORD) - 12)
|
||||
|
||||
namespace wibo {
|
||||
extern uint32_t lastError;
|
||||
extern char **argv;
|
||||
|
866
dll/kernel32.cpp
866
dll/kernel32.cpp
File diff suppressed because it is too large
Load Diff
@ -769,12 +769,12 @@ namespace msvcrt {
|
||||
}
|
||||
|
||||
char* WIN_ENTRY strcat(char *dest, const char *src) {
|
||||
VERBOSE_LOG("strcat(%s, %s)\n", dest, src);
|
||||
VERBOSE_LOG("strcat(%p, %s)\n", dest, src);
|
||||
return std::strcat(dest, src);
|
||||
}
|
||||
|
||||
char* WIN_ENTRY strcpy(char *dest, const char *src) {
|
||||
VERBOSE_LOG("strcpy(%s, %s)\n", dest, src);
|
||||
VERBOSE_LOG("strcpy(%p, %s)\n", dest, src);
|
||||
return std::strcpy(dest, src);
|
||||
}
|
||||
|
||||
@ -838,7 +838,7 @@ namespace msvcrt {
|
||||
}
|
||||
|
||||
void WIN_ENTRY _mbccpy(unsigned char *dest, const unsigned char *src) {
|
||||
DEBUG_LOG("_mbccpy(%s, %s)\n", dest, src);
|
||||
DEBUG_LOG("_mbccpy(%p, %s)\n", dest, src);
|
||||
if (!dest || !src) {
|
||||
return;
|
||||
}
|
||||
@ -1234,7 +1234,7 @@ namespace msvcrt {
|
||||
}
|
||||
|
||||
unsigned long WIN_ENTRY _ultoa(unsigned long value, char *str, int radix) {
|
||||
DEBUG_LOG("_ultoa(%lu, %s, %d)\n", value, str ? str : "(null)", radix);
|
||||
DEBUG_LOG("_ultoa(%lu, %p, %d)\n", value, str, radix);
|
||||
if (!str || radix < 2 || radix > 36) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
@ -1255,7 +1255,7 @@ namespace msvcrt {
|
||||
}
|
||||
|
||||
char* WIN_ENTRY _ltoa(long value, char *str, int radix) {
|
||||
DEBUG_LOG("_ltoa(%ld, %s, %d)\n", value, str ? str : "(null)", radix);
|
||||
DEBUG_LOG("_ltoa(%ld, %p, %d)\n", value, str, radix);
|
||||
if (!str || radix < 2 || radix > 36) {
|
||||
errno = EINVAL;
|
||||
return nullptr;
|
||||
@ -2239,24 +2239,17 @@ namespace msvcrt {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WIN_ENTRY _get_wpgmptr(uint16_t** pValue){
|
||||
int WIN_ENTRY _get_wpgmptr(uint16_t **pValue) {
|
||||
DEBUG_LOG("_get_wpgmptr(%p)\n", pValue);
|
||||
if(!pValue) return 22;
|
||||
|
||||
char exe_path[PATH_MAX];
|
||||
ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
||||
if(len == -1){
|
||||
return 2;
|
||||
if (!pValue) {
|
||||
return 22;
|
||||
}
|
||||
exe_path[len] = 0;
|
||||
|
||||
std::string exePathStr(exe_path);
|
||||
if (_pgmptr) {
|
||||
free(_pgmptr);
|
||||
if (_wpgmptr) {
|
||||
*pValue = _wpgmptr;
|
||||
return 0;
|
||||
}
|
||||
_pgmptr = ::strdup(exePathStr.c_str());
|
||||
|
||||
std::vector<uint16_t> wStr = stringToWideString(exePathStr.c_str());
|
||||
const auto wStr = stringToWideString(wibo::guestExecutablePath.c_str());
|
||||
if (_wpgmptr) {
|
||||
delete[] _wpgmptr;
|
||||
}
|
||||
@ -2269,6 +2262,8 @@ namespace msvcrt {
|
||||
}
|
||||
|
||||
char** WIN_ENTRY __p__pgmptr() {
|
||||
DEBUG_LOG("__p__pgmptr()\n");
|
||||
_pgmptr = const_cast<char *>(wibo::guestExecutablePath.c_str());
|
||||
return &_pgmptr;
|
||||
}
|
||||
|
||||
@ -2688,7 +2683,6 @@ namespace msvcrt {
|
||||
DEBUG_LOG("_wspawnvp(%d, %s)\n", mode, command.c_str());
|
||||
|
||||
std::vector<std::string> argStorage;
|
||||
argStorage.emplace_back(command);
|
||||
for (const uint16_t *const *cursor = argv; *cursor; ++cursor) {
|
||||
argStorage.emplace_back(wideStringToString(*cursor));
|
||||
}
|
||||
@ -2746,7 +2740,6 @@ namespace msvcrt {
|
||||
DEBUG_LOG("_spawnvp(%d, %s)\n", mode, command.c_str());
|
||||
|
||||
std::vector<std::string> argStorage;
|
||||
argStorage.emplace_back(command);
|
||||
for (const char * const *cursor = argv; *cursor; ++cursor) {
|
||||
argStorage.emplace_back(*cursor);
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
|
||||
namespace ole32 {
|
||||
int WIN_FUNC CoInitialize(void *pvReserved) {
|
||||
DEBUG_LOG("CoInitialize(...)\n");
|
||||
DEBUG_LOG("STUB: CoInitialize(%p)\n", pvReserved);
|
||||
(void) pvReserved;
|
||||
return 0; // S_OK
|
||||
}
|
||||
|
||||
@ -20,7 +21,7 @@ namespace ole32 {
|
||||
const GUID *riid,
|
||||
void **ppv
|
||||
) {
|
||||
DEBUG_LOG("CoCreateInstance 0x%x %p %d 0x%x %p\n", rclsid->Data1, pUnkOuter, dwClsContext, riid->Data1, *ppv);
|
||||
DEBUG_LOG("STUB: CoCreateInstance(0x%x, %p, %d, 0x%x, %p)\n", rclsid->Data1, pUnkOuter, dwClsContext, riid->Data1, *ppv);
|
||||
*ppv = 0;
|
||||
// E_POINTER is returned when ppv is NULL, which isn't true here, but returning 1 results
|
||||
// in a segfault with mwcceppc.exe when it's told to include directories that don't exist
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
namespace psapi {
|
||||
BOOL WIN_FUNC EnumProcessModules(HANDLE hProcess, HMODULE *lphModule, DWORD cb, DWORD *lpcbNeeded) {
|
||||
DEBUG_LOG("EnumProcessModules(hProcess=%p, cb=%u)\n", hProcess, cb);
|
||||
DEBUG_LOG("EnumProcessModules(%p, %p, %u, %p)\n", hProcess, lphModule, cb, lpcbNeeded);
|
||||
|
||||
bool recognizedHandle = false;
|
||||
if (hProcess == (HANDLE)0xFFFFFFFF) {
|
||||
|
@ -206,7 +206,7 @@ static bool loadVersionResource(const char *fileName, std::vector<uint8_t> &buff
|
||||
namespace version {
|
||||
|
||||
unsigned int WIN_FUNC GetFileVersionInfoSizeA(const char *lptstrFilename, unsigned int *lpdwHandle) {
|
||||
DEBUG_LOG("GetFileVersionInfoSizeA %s\n", lptstrFilename);
|
||||
DEBUG_LOG("GetFileVersionInfoSizeA(%s, %p)\n", lptstrFilename, lpdwHandle);
|
||||
if (lpdwHandle)
|
||||
*lpdwHandle = 0;
|
||||
|
||||
@ -218,7 +218,7 @@ unsigned int WIN_FUNC GetFileVersionInfoSizeA(const char *lptstrFilename, unsign
|
||||
|
||||
unsigned int WIN_FUNC GetFileVersionInfoA(const char *lptstrFilename, unsigned int dwHandle, unsigned int dwLen, void *lpData) {
|
||||
(void) dwHandle;
|
||||
DEBUG_LOG("GetFileVersionInfoA %s len=%u\n", lptstrFilename, dwLen);
|
||||
DEBUG_LOG("GetFileVersionInfoA(%s, %u, %p)\n", lptstrFilename, dwLen, lpData);
|
||||
if (!lpData || dwLen == 0) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return 0;
|
||||
@ -245,7 +245,7 @@ static unsigned int VerQueryValueImpl(const void *pBlock, const std::string &sub
|
||||
if (!pBlock)
|
||||
return 0;
|
||||
|
||||
const uint8_t *base = static_cast<const uint8_t *>(pBlock);
|
||||
const auto *base = static_cast<const uint8_t *>(pBlock);
|
||||
uint16_t totalLength = readU16(base);
|
||||
if (totalLength < 6)
|
||||
return 0;
|
||||
@ -279,18 +279,20 @@ static unsigned int VerQueryValueImpl(const void *pBlock, const std::string &sub
|
||||
}
|
||||
|
||||
unsigned int WIN_FUNC VerQueryValueA(const void *pBlock, const char *lpSubBlock, void **lplpBuffer, unsigned int *puLen) {
|
||||
DEBUG_LOG("VerQueryValueA %p %s\n", pBlock, lpSubBlock ? lpSubBlock : "(null)");
|
||||
DEBUG_LOG("VerQueryValueA(%p, %s, %p, %p)\n", pBlock, lpSubBlock ? lpSubBlock : "(null)", lplpBuffer, puLen);
|
||||
if (!lpSubBlock)
|
||||
return 0;
|
||||
return VerQueryValueImpl(pBlock, lpSubBlock, lplpBuffer, puLen);
|
||||
}
|
||||
|
||||
unsigned int WIN_FUNC GetFileVersionInfoSizeW(const uint16_t *lptstrFilename, unsigned int *lpdwHandle) {
|
||||
DEBUG_LOG("GetFileVersionInfoSizeW -> ");
|
||||
auto narrow = wideStringToString(lptstrFilename);
|
||||
return GetFileVersionInfoSizeA(narrow.c_str(), lpdwHandle);
|
||||
}
|
||||
|
||||
unsigned int WIN_FUNC GetFileVersionInfoW(const uint16_t *lptstrFilename, unsigned int dwHandle, unsigned int dwLen, void *lpData) {
|
||||
DEBUG_LOG("GetFileVersionInfoW -> ");
|
||||
auto narrow = wideStringToString(lptstrFilename);
|
||||
return GetFileVersionInfoA(narrow.c_str(), dwHandle, dwLen, lpData);
|
||||
}
|
||||
@ -299,7 +301,7 @@ unsigned int WIN_FUNC VerQueryValueW(const void *pBlock, const uint16_t *lpSubBl
|
||||
if (!lpSubBlock)
|
||||
return 0;
|
||||
auto narrow = wideStringToString(lpSubBlock);
|
||||
DEBUG_LOG("VerQueryValueW %p %s\n", pBlock, narrow.c_str());
|
||||
DEBUG_LOG("VerQueryValueW(%p, %s, %p, %p)\n", pBlock, narrow.c_str(), lplpBuffer, puLen);
|
||||
return VerQueryValueImpl(pBlock, narrow, lplpBuffer, puLen);
|
||||
}
|
||||
|
||||
|
54
files.cpp
54
files.cpp
@ -123,22 +123,18 @@ namespace files {
|
||||
|
||||
FileHandle *fileHandleFromHandle(void *handle) {
|
||||
handles::Data data = handles::dataFromHandle(handle, false);
|
||||
if (data.type != handles::TYPE_FILE) {
|
||||
return nullptr;
|
||||
if (data.type == handles::TYPE_FILE) {
|
||||
return reinterpret_cast<FileHandle *>(data.ptr);
|
||||
}
|
||||
return reinterpret_cast<FileHandle *>(data.ptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FILE *fpFromHandle(void *handle, bool pop) {
|
||||
handles::Data data = handles::dataFromHandle(handle, pop);
|
||||
if (data.type == handles::TYPE_FILE) {
|
||||
return reinterpret_cast<FileHandle *>(data.ptr)->fp;
|
||||
} else if (data.type == handles::TYPE_UNUSED && pop) {
|
||||
return nullptr;
|
||||
} else {
|
||||
printf("Invalid file handle %p\n", handle);
|
||||
assert(0);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *allocFpHandle(FILE *fp, unsigned int desiredAccess, unsigned int shareMode, unsigned int flags, bool closeOnDestroy) {
|
||||
@ -300,32 +296,32 @@ namespace files {
|
||||
return result;
|
||||
}
|
||||
|
||||
void *getStdHandle(uint32_t nStdHandle) {
|
||||
HANDLE getStdHandle(DWORD nStdHandle) {
|
||||
switch (nStdHandle) {
|
||||
case ((uint32_t) -10): // STD_INPUT_HANDLE
|
||||
return stdinHandle;
|
||||
case ((uint32_t) -11): // STD_OUTPUT_HANDLE
|
||||
return stdoutHandle;
|
||||
case ((uint32_t) -12): // STD_ERROR_HANDLE
|
||||
return stderrHandle;
|
||||
default:
|
||||
return (void *) 0xFFFFFFFF;
|
||||
case STD_INPUT_HANDLE:
|
||||
return stdinHandle;
|
||||
case STD_OUTPUT_HANDLE:
|
||||
return stdoutHandle;
|
||||
case STD_ERROR_HANDLE:
|
||||
return stderrHandle;
|
||||
default:
|
||||
return (void *)0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int setStdHandle(uint32_t nStdHandle, void *hHandle) {
|
||||
BOOL setStdHandle(DWORD nStdHandle, HANDLE hHandle) {
|
||||
switch (nStdHandle) {
|
||||
case ((uint32_t) -10): // STD_INPUT_HANDLE
|
||||
stdinHandle = hHandle;
|
||||
break;
|
||||
case ((uint32_t) -11): // STD_OUTPUT_HANDLE
|
||||
stdoutHandle = hHandle;
|
||||
break;
|
||||
case ((uint32_t) -12): // STD_ERROR_HANDLE
|
||||
stderrHandle = hHandle;
|
||||
break;
|
||||
default:
|
||||
return 0; // fail
|
||||
case STD_INPUT_HANDLE:
|
||||
stdinHandle = hHandle;
|
||||
break;
|
||||
case STD_OUTPUT_HANDLE:
|
||||
stdoutHandle = hHandle;
|
||||
break;
|
||||
case STD_ERROR_HANDLE:
|
||||
stderrHandle = hHandle;
|
||||
break;
|
||||
default:
|
||||
return 0; // fail
|
||||
}
|
||||
return 1; // success
|
||||
}
|
||||
|
6
files.h
6
files.h
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
@ -31,8 +33,8 @@ namespace files {
|
||||
FileHandle *fileHandleFromHandle(void *handle);
|
||||
IOResult read(FileHandle *handle, void *buffer, size_t bytesToRead, const std::optional<uint64_t> &offset, bool updateFilePointer);
|
||||
IOResult write(FileHandle *handle, const void *buffer, size_t bytesToWrite, const std::optional<uint64_t> &offset, bool updateFilePointer);
|
||||
void *getStdHandle(uint32_t nStdHandle);
|
||||
unsigned int setStdHandle(uint32_t nStdHandle, void *hHandle);
|
||||
HANDLE getStdHandle(DWORD nStdHandle);
|
||||
BOOL setStdHandle(DWORD nStdHandle, HANDLE hHandle);
|
||||
void init();
|
||||
std::optional<std::filesystem::path> findCaseInsensitiveFile(const std::filesystem::path &directory, const std::string &filename);
|
||||
std::filesystem::path canonicalPath(const std::filesystem::path &path);
|
||||
|
11
handles.cpp
11
handles.cpp
@ -3,24 +3,21 @@
|
||||
#include <utility>
|
||||
|
||||
namespace handles {
|
||||
static Data datas[0x10000];
|
||||
static Data datas[MAX_HANDLES];
|
||||
|
||||
Data dataFromHandle(void *handle, bool pop) {
|
||||
uintptr_t index = (uintptr_t)handle;
|
||||
if (index > 0 && index < 0x10000) {
|
||||
if (index > 0 && index < MAX_HANDLES) {
|
||||
Data ret = datas[index];
|
||||
if (pop)
|
||||
datas[index] = Data{};
|
||||
return ret;
|
||||
}
|
||||
if (pop)
|
||||
return Data{};
|
||||
printf("Invalid file handle %p\n", handle);
|
||||
assert(0);
|
||||
return Data{};
|
||||
}
|
||||
|
||||
void *allocDataHandle(Data data) {
|
||||
for (int i = 1; i < 0x10000; i++) {
|
||||
for (size_t i = 1; i < MAX_HANDLES; i++) {
|
||||
if (datas[i].type == TYPE_UNUSED) {
|
||||
datas[i] = data;
|
||||
return (void*)i;
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include <cstdlib>
|
||||
|
||||
namespace handles {
|
||||
constexpr size_t MAX_HANDLES = 0x10000;
|
||||
|
||||
enum Type {
|
||||
TYPE_UNUSED,
|
||||
TYPE_FILE,
|
||||
|
25
main.cpp
25
main.cpp
@ -348,23 +348,32 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
// Build guest arguments
|
||||
if (guestArgs.empty()) {
|
||||
int argIndex = -1;
|
||||
bool skipProgramName = false;
|
||||
if (programIndex != -1 && argc > programIndex + 1) {
|
||||
argIndex = programIndex + 1;
|
||||
// With "test.exe -- test 1 2 3", treat everything after -- as the full command line
|
||||
if (strcmp(argv[argIndex], "--") == 0) {
|
||||
argIndex++;
|
||||
skipProgramName = true;
|
||||
}
|
||||
}
|
||||
if (guestArgs.empty() && !skipProgramName) {
|
||||
guestArgs.push_back(files::pathToWindows(resolvedGuestPath));
|
||||
}
|
||||
for (int i = programIndex + 1; i < argc; ++i) {
|
||||
guestArgs.emplace_back(argv[i]);
|
||||
if (argIndex != -1) {
|
||||
for (int i = argIndex; i < argc; ++i) {
|
||||
guestArgs.emplace_back(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Build a command line
|
||||
if (cmdLine.empty()) {
|
||||
for (int i = 0; i < guestArgs.size(); ++i) {
|
||||
std::string arg;
|
||||
if (i == 0) {
|
||||
arg = files::pathToWindows(resolvedGuestPath);
|
||||
} else {
|
||||
if (i != 0) {
|
||||
cmdLine += ' ';
|
||||
arg = guestArgs[i];
|
||||
}
|
||||
const std::string& arg = guestArgs[i];
|
||||
bool needQuotes = arg.find_first_of("\" \t\n") != std::string::npos;
|
||||
if (needQuotes)
|
||||
cmdLine += '"';
|
||||
|
194
test/test_time.c
Normal file
194
test/test_time.c
Normal file
@ -0,0 +1,194 @@
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "test_assert.h"
|
||||
|
||||
static uint64_t filetime_to_u64(const FILETIME *ft) {
|
||||
ULARGE_INTEGER li;
|
||||
li.LowPart = ft->dwLowDateTime;
|
||||
li.HighPart = ft->dwHighDateTime;
|
||||
return li.QuadPart;
|
||||
}
|
||||
|
||||
static FILETIME u64_to_filetime(uint64_t value) {
|
||||
ULARGE_INTEGER li;
|
||||
li.QuadPart = value;
|
||||
FILETIME ft;
|
||||
ft.dwLowDateTime = li.LowPart;
|
||||
ft.dwHighDateTime = li.HighPart;
|
||||
return ft;
|
||||
}
|
||||
|
||||
static uint64_t abs_u64_diff(uint64_t a, uint64_t b) {
|
||||
return (a > b) ? (a - b) : (b - a);
|
||||
}
|
||||
|
||||
static void test_systemtime_roundtrip(void) {
|
||||
SYSTEMTIME st = {
|
||||
.wYear = 2023,
|
||||
.wMonth = 7,
|
||||
.wDay = 15,
|
||||
.wHour = 12,
|
||||
.wMinute = 34,
|
||||
.wSecond = 56,
|
||||
.wMilliseconds = 789
|
||||
};
|
||||
|
||||
FILETIME ft;
|
||||
TEST_CHECK(SystemTimeToFileTime(&st, &ft));
|
||||
|
||||
SYSTEMTIME converted = {0};
|
||||
TEST_CHECK(FileTimeToSystemTime(&ft, &converted));
|
||||
|
||||
TEST_CHECK_EQ(st.wYear, converted.wYear);
|
||||
TEST_CHECK_EQ(st.wMonth, converted.wMonth);
|
||||
TEST_CHECK_EQ(st.wDay, converted.wDay);
|
||||
TEST_CHECK_EQ(st.wHour, converted.wHour);
|
||||
TEST_CHECK_EQ(st.wMinute, converted.wMinute);
|
||||
TEST_CHECK_EQ(st.wSecond, converted.wSecond);
|
||||
TEST_CHECK_EQ(st.wMilliseconds, converted.wMilliseconds);
|
||||
|
||||
SetLastError(0);
|
||||
SYSTEMTIME invalid = st;
|
||||
invalid.wMonth = 13;
|
||||
TEST_CHECK(!SystemTimeToFileTime(&invalid, &ft));
|
||||
TEST_CHECK_EQ(ERROR_INVALID_PARAMETER, GetLastError());
|
||||
}
|
||||
|
||||
static void test_filetime_known_timestamp(void) {
|
||||
/* 2023-01-01 00:00:00 UTC */
|
||||
const uint64_t expected_ticks = 133170048000000000ULL;
|
||||
FILETIME ft = u64_to_filetime(expected_ticks);
|
||||
|
||||
SYSTEMTIME st = {0};
|
||||
TEST_CHECK(FileTimeToSystemTime(&ft, &st));
|
||||
TEST_CHECK_EQ(2023, st.wYear);
|
||||
TEST_CHECK_EQ(1, st.wMonth);
|
||||
TEST_CHECK_EQ(1, st.wDay);
|
||||
TEST_CHECK_EQ(0, st.wHour);
|
||||
TEST_CHECK_EQ(0, st.wMinute);
|
||||
TEST_CHECK_EQ(0, st.wSecond);
|
||||
TEST_CHECK_EQ(0, st.wMilliseconds);
|
||||
|
||||
FILETIME back;
|
||||
TEST_CHECK(SystemTimeToFileTime(&st, &back));
|
||||
TEST_CHECK_U64_EQ(expected_ticks, filetime_to_u64(&back));
|
||||
}
|
||||
|
||||
static void test_getsystemtimeasfiletime(void) {
|
||||
FILETIME from_api;
|
||||
GetSystemTimeAsFileTime(&from_api);
|
||||
|
||||
SYSTEMTIME sys_now;
|
||||
GetSystemTime(&sys_now);
|
||||
FILETIME from_system;
|
||||
TEST_CHECK(SystemTimeToFileTime(&sys_now, &from_system));
|
||||
|
||||
uint64_t delta = abs_u64_diff(filetime_to_u64(&from_api), filetime_to_u64(&from_system));
|
||||
/* allow 1 second of skew between calls */
|
||||
TEST_CHECK_MSG(delta < 10000000ULL, "GetSystemTimeAsFileTime skew too large: %llu", (unsigned long long)delta);
|
||||
}
|
||||
|
||||
static void test_gettickcount_progresses(void) {
|
||||
DWORD start = GetTickCount();
|
||||
Sleep(60);
|
||||
DWORD end = GetTickCount();
|
||||
DWORD diff = end - start;
|
||||
|
||||
TEST_CHECK_MSG(diff >= 40, "GetTickCount diff too small: %lu", (unsigned long)diff);
|
||||
TEST_CHECK_MSG(diff <= 5000, "GetTickCount diff too large: %lu", (unsigned long)diff);
|
||||
}
|
||||
|
||||
static void test_setfiletime_roundtrip(void) {
|
||||
char temp_path[MAX_PATH];
|
||||
char temp_file[MAX_PATH];
|
||||
|
||||
DWORD path_len = GetTempPathA(sizeof(temp_path), temp_path);
|
||||
TEST_CHECK(path_len > 0 && path_len < sizeof(temp_path));
|
||||
|
||||
UINT unique = GetTempFileNameA(temp_path, "TST", 0, temp_file);
|
||||
TEST_CHECK(unique != 0);
|
||||
|
||||
HANDLE handle = CreateFileA(temp_file, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
TEST_CHECK(handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
FILETIME original_creation = {0}, original_access = {0}, original_write = {0};
|
||||
TEST_CHECK(GetFileTime(handle, &original_creation, &original_access, &original_write));
|
||||
|
||||
SYSTEMTIME desired = {
|
||||
.wYear = 2022,
|
||||
.wMonth = 12,
|
||||
.wDay = 31,
|
||||
.wHour = 5,
|
||||
.wMinute = 45,
|
||||
.wSecond = 12,
|
||||
.wMilliseconds = 123
|
||||
};
|
||||
FILETIME desired_ft;
|
||||
TEST_CHECK(SystemTimeToFileTime(&desired, &desired_ft));
|
||||
|
||||
TEST_CHECK(SetFileTime(handle, NULL, NULL, &desired_ft));
|
||||
|
||||
FILETIME updated_creation = {0}, updated_access = {0}, updated_write = {0};
|
||||
TEST_CHECK(GetFileTime(handle, &updated_creation, &updated_access, &updated_write));
|
||||
TEST_CHECK_U64_EQ(filetime_to_u64(&desired_ft), filetime_to_u64(&updated_write));
|
||||
|
||||
FILETIME zero = {0, 0};
|
||||
TEST_CHECK(SetFileTime(handle, NULL, &zero, NULL));
|
||||
|
||||
FILETIME final_creation = {0}, final_access = {0}, final_write = {0};
|
||||
TEST_CHECK(GetFileTime(handle, &final_creation, &final_access, &final_write));
|
||||
TEST_CHECK_U64_EQ(filetime_to_u64(&updated_access), filetime_to_u64(&final_access));
|
||||
TEST_CHECK_U64_EQ(filetime_to_u64(&updated_write), filetime_to_u64(&final_write));
|
||||
|
||||
CloseHandle(handle);
|
||||
DeleteFileA(temp_file);
|
||||
}
|
||||
|
||||
static void test_local_filetime_conversions(void) {
|
||||
/* Choose a time likely to be affected by DST in many zones */
|
||||
SYSTEMTIME utc_time = {
|
||||
.wYear = 2021,
|
||||
.wMonth = 6,
|
||||
.wDay = 15,
|
||||
.wHour = 18,
|
||||
.wMinute = 0,
|
||||
.wSecond = 0,
|
||||
.wMilliseconds = 0
|
||||
};
|
||||
|
||||
FILETIME utc_ft;
|
||||
TEST_CHECK(SystemTimeToFileTime(&utc_time, &utc_ft));
|
||||
|
||||
FILETIME local_ft;
|
||||
TEST_CHECK(FileTimeToLocalFileTime(&utc_ft, &local_ft));
|
||||
|
||||
FILETIME roundtrip;
|
||||
TEST_CHECK(LocalFileTimeToFileTime(&local_ft, &roundtrip));
|
||||
TEST_CHECK_U64_EQ(filetime_to_u64(&utc_ft), filetime_to_u64(&roundtrip));
|
||||
|
||||
/* Local filetime should convert back to the original system time when interpreted locally */
|
||||
SYSTEMTIME local_st = {0};
|
||||
TEST_CHECK(FileTimeToSystemTime(&local_ft, &local_st));
|
||||
SYSTEMTIME utc_from_roundtrip = {0};
|
||||
TEST_CHECK(FileTimeToSystemTime(&roundtrip, &utc_from_roundtrip));
|
||||
TEST_CHECK_EQ(utc_time.wYear, utc_from_roundtrip.wYear);
|
||||
TEST_CHECK_EQ(utc_time.wMonth, utc_from_roundtrip.wMonth);
|
||||
TEST_CHECK_EQ(utc_time.wDay, utc_from_roundtrip.wDay);
|
||||
TEST_CHECK_EQ(utc_time.wHour, utc_from_roundtrip.wHour);
|
||||
TEST_CHECK_EQ(utc_time.wMinute, utc_from_roundtrip.wMinute);
|
||||
TEST_CHECK_EQ(utc_time.wSecond, utc_from_roundtrip.wSecond);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_systemtime_roundtrip();
|
||||
test_filetime_known_timestamp();
|
||||
test_getsystemtimeasfiletime();
|
||||
test_gettickcount_progresses();
|
||||
test_setfiletime_roundtrip();
|
||||
test_local_filetime_conversions();
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user