diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c341626..9ea391b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: run: | wget https://cdn.discordapp.com/attachments/727918646525165659/917185027656286218/GC_WII_COMPILERS.zip unzip GC_WII_COMPILERS.zip - MWCIncludes=. build/wibo GC/2.7/mwcceppc.exe -c test/test.c + MWCIncludes=. build/wibo GC/2.7/mwcceppc.exe -c test/test.c -Itest file test.o - name: Upload build diff --git a/CMakeLists.txt b/CMakeLists.txt index 60b9973..3e9962a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable(wibo dll/user32.cpp dll/version.cpp files.cpp + handles.cpp loader.cpp main.cpp ) diff --git a/dll/kernel32.cpp b/dll/kernel32.cpp index ce6006d..95f4846 100644 --- a/dll/kernel32.cpp +++ b/dll/kernel32.cpp @@ -1,12 +1,15 @@ #include "common.h" #include "files.h" +#include "handles.h" #include #include #include +#include #include #include #include #include +#include #include namespace kernel32 { @@ -85,6 +88,17 @@ namespace kernel32 { } } + int64_t getFileSize(void* hFile) { + FILE *fp = files::fpFromHandle(hFile); + struct stat64 st; + fflush(fp); + if (fstat64(fileno(fp), &st) == -1 || !S_ISREG(st.st_mode)) { + wibo::lastError = 2; // ERROR_FILE_NOT_FOUND (?) + return -1; // INVALID_FILE_SIZE + } + return st.st_size; + } + uint32_t WIN_FUNC GetLastError() { return wibo::lastError; } @@ -384,9 +398,14 @@ namespace kernel32 { int WIN_FUNC CloseHandle(void *hObject) { DEBUG_LOG("CloseHandle %p\n", hObject); - FILE *fp = files::fpFromHandle(hObject, true); - if (fp && fp != stdin && fp != stdout && fp != stderr) { - fclose(fp); + auto data = handles::dataFromHandle(hObject, true); + if (data.type == handles::TYPE_FILE) { + FILE *fp = (FILE *) data.ptr; + if (!(fp == stdin || fp == stdout || fp == stderr)) { + fclose(fp); + } + } else if (data.type == handles::TYPE_MAPPED) { + munmap(data.ptr, data.size); } return 1; } @@ -442,6 +461,41 @@ namespace kernel32 { CharType cAlternateFileName[14]; }; + struct FindFirstFileHandle { + std::filesystem::directory_iterator it; + std::string pattern; + }; + + bool findNextFile(FindFirstFileHandle *handle) { + 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 *WIN_FUNC FindFirstFileA(const char *lpFileName, WIN32_FIND_DATA *lpFindFileData) { // This should handle wildcards too, but whatever. auto path = files::pathFromWindows(lpFileName); @@ -453,23 +507,40 @@ namespace kernel32 { auto status = std::filesystem::status(path); if (status.type() == std::filesystem::file_type::regular) { - lpFindFileData->dwFileAttributes = 0x80; // FILE_ATTRIBUTE_NORMAL - auto fileSize = std::filesystem::file_size(path); - lpFindFileData->nFileSizeHigh = (uint32_t)(fileSize >> 32); - lpFindFileData->nFileSizeLow = (uint32_t)fileSize; - auto fileName = path.filename().string(); - assert(fileName.size() < 260); - strcpy(lpFindFileData->cFileName, fileName.c_str()); - strcpy(lpFindFileData->cAlternateFileName, "8P3FMTFN.BAD"); + setFindFileDataFromPath(lpFindFileData, path); return (void *) 1; } - wibo::lastError = 2; // ERROR_FILE_NOT_FOUND - return (void *) 0xFFFFFFFF; + FindFirstFileHandle *handle = new FindFirstFileHandle(); + std::filesystem::directory_iterator it(path.parent_path()); + handle->it = it; + handle->pattern = path.filename().string(); + + if (!findNextFile(handle)) { + wibo::lastError = 2; // ERROR_FILE_NOT_FOUND + delete handle; + return (void *) 0xFFFFFFFF; + } + + setFindFileDataFromPath(lpFindFileData, *handle->it++); + return handle; + } + + int WIN_FUNC FindNextFileA(void *hFindFile, WIN32_FIND_DATA *lpFindFileData) { + FindFirstFileHandle *handle = (FindFirstFileHandle *) hFindFile; + if (!findNextFile(handle)) { + return 0; + } + + setFindFileDataFromPath(lpFindFileData, *handle->it++); + return 1; } int WIN_FUNC FindClose(void *hFindFile) { DEBUG_LOG("FindClose\n"); + if (hFindFile != (void *) 1) { + delete (FindFirstFileHandle *)hFindFile; + } return 1; } @@ -580,6 +651,55 @@ namespace kernel32 { } } + 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); + + int64_t size = (int64_t) dwMaximumSizeHigh << 32 | dwMaximumSizeLow; + + void* mmapped; + + if (hFile == (void*) -1) { // INVALID_HANDLE_VALUE + mmapped = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + } else { + int fd = fileno(files::fpFromHandle(hFile)); + + if (size == 0) { + size = getFileSize(hFile); + if (size == -1) { + return (void*) -1; + } + } + mmapped = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + } + + assert(mmapped != MAP_FAILED); + return handles::allocDataHandle({handles::TYPE_MAPPED, mmapped, (unsigned int) size}); + } + + void *WIN_FUNC MapViewOfFile( + void *hFileMappingObject, + unsigned int dwDesiredAccess, + unsigned int dwFileOffsetHigh, + unsigned int dwFileOffsetLow, + unsigned int dwNumberOfBytesToMap) { + DEBUG_LOG("MapViewOfFile(%p, %u, %u, %u, %u)\n", hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap); + + handles::Data data = handles::dataFromHandle(hFileMappingObject, false); + assert(data.type == handles::TYPE_MAPPED); + return (void*)((unsigned int) data.ptr + dwFileOffsetLow); + } + + int WIN_FUNC UnmapViewOfFile(void *lpBaseAddress) { + DEBUG_LOG("UnmapViewOfFile(%p)\n", lpBaseAddress); + return 1; + } + int WIN_FUNC DeleteFileA(const char* lpFileName) { std::string path = files::pathFromWindows(lpFileName); DEBUG_LOG("DeleteFileA %s (%s)\n", lpFileName, path.c_str()); @@ -637,18 +757,15 @@ namespace kernel32 { unsigned int WIN_FUNC GetFileSize(void *hFile, unsigned int *lpFileSizeHigh) { DEBUG_LOG("GetFileSize\n"); - struct stat64 st; - FILE *fp = files::fpFromHandle(hFile); - fflush(fp); - if (fstat64(fileno(fp), &st) == -1 || !S_ISREG(st.st_mode)) { - wibo::lastError = 2; // ERROR_FILE_NOT_FOUND (?) - return ~0u; // INVALID_FILE_SIZE + int64_t size = getFileSize(hFile); + if (size == -1) { + return 0xFFFFFFFF; // INVALID_FILE_SIZE } - DEBUG_LOG("-> %ld\n", st.st_size); + DEBUG_LOG("-> %ld\n", size); if (lpFileSizeHigh != nullptr) { - *lpFileSizeHigh = st.st_size >> 32; + *lpFileSizeHigh = size >> 32; } - return st.st_size; + return size; } /* @@ -1478,11 +1595,15 @@ void *wibo::resolveKernel32(const char *name) { // fileapi.h if (strcmp(name, "GetFullPathNameA") == 0) return (void *) kernel32::GetFullPathNameA; if (strcmp(name, "FindFirstFileA") == 0) return (void *) kernel32::FindFirstFileA; + if (strcmp(name, "FindNextFileA") == 0) return (void *) kernel32::FindNextFileA; if (strcmp(name, "FindClose") == 0) return (void *) kernel32::FindClose; if (strcmp(name, "GetFileAttributesA") == 0) return (void *) kernel32::GetFileAttributesA; if (strcmp(name, "WriteFile") == 0) return (void *) kernel32::WriteFile; if (strcmp(name, "ReadFile") == 0) return (void *) kernel32::ReadFile; if (strcmp(name, "CreateFileA") == 0) return (void *) kernel32::CreateFileA; + if (strcmp(name, "CreateFileMappingA") == 0) return (void *) kernel32::CreateFileMappingA; + 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, "SetFilePointer") == 0) return (void *) kernel32::SetFilePointer; if (strcmp(name, "SetEndOfFile") == 0) return (void *) kernel32::SetEndOfFile; diff --git a/files.cpp b/files.cpp index 0783e9d..b395e75 100644 --- a/files.cpp +++ b/files.cpp @@ -1,9 +1,10 @@ #include "common.h" #include "files.h" +#include "handles.h" #include +#include namespace files { - static FILE *handleFps[0x10000]; static void *stdinHandle; static void *stdoutHandle; @@ -68,28 +69,19 @@ namespace files { } FILE *fpFromHandle(void *handle, bool pop) { - uintptr_t index = (uintptr_t)handle; - if (index > 0 && index < 0x10000) { - FILE *ret = handleFps[index]; - if (pop) - handleFps[index] = 0; - return ret; - } - if (pop) + handles::Data data = handles::dataFromHandle(handle, pop); + if (data.type == handles::TYPE_FILE) { + return (FILE*)data.ptr; + } else if (data.type == handles::TYPE_UNUSED && pop) { return 0; - printf("Invalid file handle %p\n", handle); - assert(0); + } else { + printf("Invalid file handle %p\n", handle); + assert(0); + } } void *allocFpHandle(FILE *fp) { - for (int i = 1; i < 0x10000; i++) { - if (!handleFps[i]) { - handleFps[i] = fp; - return (void*)i; - } - } - printf("Out of file handles\n"); - assert(0); + return handles::allocDataHandle(handles::Data{handles::TYPE_FILE, fp, 0}); } void *getStdHandle(uint32_t nStdHandle) { diff --git a/handles.cpp b/handles.cpp new file mode 100644 index 0000000..66919ae --- /dev/null +++ b/handles.cpp @@ -0,0 +1,32 @@ +#include "common.h" +#include "handles.h" +#include + +namespace handles { + static Data datas[0x10000]; + + Data dataFromHandle(void *handle, bool pop) { + uintptr_t index = (uintptr_t)handle; + if (index > 0 && index < 0x10000) { + Data ret = datas[index]; + if (pop) + datas[index] = Data{}; + return ret; + } + if (pop) + return Data{}; + printf("Invalid file handle %p\n", handle); + assert(0); + } + + void *allocDataHandle(Data data) { + for (int i = 1; i < 0x10000; i++) { + if (datas[i].type == TYPE_UNUSED) { + datas[i] = data; + return (void*)i; + } + } + printf("Out of handles\n"); + assert(0); + } +} diff --git a/handles.h b/handles.h new file mode 100644 index 0000000..6d6c9f7 --- /dev/null +++ b/handles.h @@ -0,0 +1,18 @@ +#include + +namespace handles { + enum Type { + TYPE_UNUSED, + TYPE_FILE, + TYPE_MAPPED, + }; + + struct Data { + Type type = TYPE_UNUSED; + void *ptr; + size_t size; + }; + + Data dataFromHandle(void *handle, bool pop); + void *allocDataHandle(Data data); +} diff --git a/test/another.inc.c b/test/another.inc.c new file mode 100644 index 0000000..46f06ed --- /dev/null +++ b/test/another.inc.c @@ -0,0 +1 @@ +#define SOMETHING 3 diff --git a/test/test.c b/test/test.c index 8cdc6ed..2e4b6a4 100644 --- a/test/test.c +++ b/test/test.c @@ -1,6 +1,8 @@ +#include "another.inc.c" + float apple = 3.0f; float banana = 65.32f; int something(void) { - return (int)(apple * banana) % 11; + return (int)(apple * banana) % 11 + SOMETHING; }