From e1e11cf75e82b0ef8a1bcbe21459dcd68f30b4c2 Mon Sep 17 00:00:00 2001 From: invertego Date: Sat, 17 Dec 2022 09:22:24 -0800 Subject: [PATCH] nall: split implementation into .cpp files Begin the process of splitting nall into separate translation units. It can now be used in two ways: 1) as a header-only library by defining NALL_HEADER_ONLY 2) by compiling nall/nall.cpp sourcery has been updated to demonstrate the header-only use and everything else now builds nall.cpp. So far the only things moved into .cpp files are implementations that call Windows APIs. This allows most of ares to build without including windows.h and enables the removal of the abomination that was windows/guard.hpp. The things that actually need windows.h (hiro, ruby) must avoid using names reserved by Windows headers, but the rest of ares is free to use names like "far", "boolean", and "interface" with reckless abandon. This work is done in preparation for building against MSVC and Windows SDK headers/libraries an an alternative to MinGW-w64. --- desktop-ui/GNUmakefile | 4 +- genius/GNUmakefile | 9 +- hiro/gtk/header.hpp | 4 +- hiro/windows/header.hpp | 5 +- mia/GNUmakefile | 13 ++- nall/GNUmakefile | 5 + nall/arguments.hpp | 4 +- nall/decode/url.hpp | 2 + nall/directory.cpp | 86 +++++++++++++++++ nall/directory.hpp | 81 +--------------- nall/dl.cpp | 39 ++++++++ nall/dl.hpp | 34 +------ nall/file-map.cpp | 80 ++++++++++++++++ nall/file-map.hpp | 75 ++------------- nall/http/client.cpp | 36 ++++++++ nall/http/client.hpp | 31 +------ nall/http/response.hpp | 2 + nall/http/server.cpp | 185 +++++++++++++++++++++++++++++++++++++ nall/http/server.hpp | 188 +++----------------------------------- nall/inode.cpp | 15 +++ nall/inode.hpp | 14 +-- nall/intrinsics.hpp | 6 ++ nall/main.cpp | 44 +++++++++ nall/main.hpp | 35 +------ nall/memory.cpp | 47 ++++++++++ nall/memory.hpp | 46 ++-------- nall/nall.cpp | 32 +++++++ nall/path.cpp | 144 +++++++++++++++++++++++++++++ nall/path.hpp | 139 +++------------------------- nall/platform.cpp | 25 +++++ nall/platform.hpp | 44 ++++----- nall/random.cpp | 40 ++++++++ nall/random.hpp | 47 ++-------- nall/run.cpp | 102 +++++++++++++++++++++ nall/run.hpp | 92 ++----------------- nall/smtp.cpp | 149 ++++++++++++++++++++++++++++++ nall/smtp.hpp | 154 ++----------------------------- nall/thread.cpp | 53 +++++++++++ nall/thread.hpp | 48 +--------- nall/traits.hpp | 1 + nall/windows/detour.cpp | 69 ++++++++++++++ nall/windows/detour.hpp | 144 +++++++++-------------------- nall/windows/guard.hpp | 32 ------- nall/windows/guid.cpp | 17 ++++ nall/windows/guid.hpp | 14 +-- nall/windows/launcher.cpp | 93 +++++++++++++++++++ nall/windows/launcher.hpp | 90 ++---------------- nall/windows/registry.cpp | 113 +++++++++++++++++++++++ nall/windows/registry.hpp | 112 ++--------------------- nall/windows/utf8.cpp | 37 ++++++++ nall/windows/utf8.hpp | 36 ++------ nall/windows/windows.hpp | 17 ++++ ruby/audio/xaudio2.cpp | 21 ++--- ruby/ruby.cpp | 9 +- ruby/video/direct3d.cpp | 3 - sourcery/GNUmakefile | 2 +- 56 files changed, 1665 insertions(+), 1304 deletions(-) create mode 100644 nall/directory.cpp create mode 100644 nall/dl.cpp create mode 100644 nall/file-map.cpp create mode 100644 nall/http/client.cpp create mode 100644 nall/http/server.cpp create mode 100644 nall/inode.cpp create mode 100644 nall/main.cpp create mode 100644 nall/memory.cpp create mode 100644 nall/nall.cpp create mode 100644 nall/path.cpp create mode 100644 nall/platform.cpp create mode 100644 nall/random.cpp create mode 100644 nall/run.cpp create mode 100644 nall/smtp.cpp create mode 100644 nall/thread.cpp create mode 100644 nall/windows/detour.cpp delete mode 100644 nall/windows/guard.hpp create mode 100644 nall/windows/guid.cpp create mode 100644 nall/windows/launcher.cpp create mode 100644 nall/windows/registry.cpp create mode 100644 nall/windows/utf8.cpp create mode 100644 nall/windows/windows.hpp diff --git a/desktop-ui/GNUmakefile b/desktop-ui/GNUmakefile index 2e49d61cf..01f1418a3 100644 --- a/desktop-ui/GNUmakefile +++ b/desktop-ui/GNUmakefile @@ -65,8 +65,8 @@ $(object.path)/desktop-ui-presentation.o: $(desktop-ui.path)/presentation/presen $(object.path)/desktop-ui-settings.o: $(desktop-ui.path)/settings/settings.cpp $(object.path)/desktop-ui-tools.o: $(desktop-ui.path)/tools/tools.cpp -all.objects := $(libco.objects) $(sljit.objects) $(libchdr.objects) $(ruby.objects) $(hiro.objects) $(ares.objects) $(mia.objects) $(desktop-ui.objects) -all.options := $(libco.options) $(sljit.options) $(libchdr.options) $(ruby.options) $(hiro.options) $(ares.options) $(mia.options) $(desktop-ui.options) $(options) +all.objects := $(libco.objects) $(sljit.objects) $(libchdr.objects) $(nall.objects) $(ruby.objects) $(hiro.objects) $(ares.objects) $(mia.objects) $(desktop-ui.objects) +all.options := $(libco.options) $(sljit.options) $(libchdr.options) $(nall.options) $(ruby.options) $(hiro.options) $(ares.options) $(mia.options) $(desktop-ui.options) $(options) $(all.objects): | $(object.path) diff --git a/genius/GNUmakefile b/genius/GNUmakefile index 5287309a8..332d356d6 100644 --- a/genius/GNUmakefile +++ b/genius/GNUmakefile @@ -13,11 +13,14 @@ objects := $(object.path)/genius.o $(object.path)/genius.o: genius.cpp -$(objects): | $(object.path) +all.objects := $(nall.objects) $(hiro.objects) $(objects) +all.options := $(nall.options) $(hiro.options) $(options) -all: $(hiro.objects) $(objects) | $(output.path) +$(all.objects): | $(object.path) + +all: $(all.objects) | $(output.path) $(info Linking $(output.path)/$(name) ...) - +@$(compiler) -o $(output.path)/$(name) $(hiro.objects) $(objects) $(hiro.options) $(options) + +@$(compiler) -o $(output.path)/$(name) $(all.objects) $(all.options) ifeq ($(platform),macos) rm -rf $(output.path)/$(name).app mkdir -p $(output.path)/$(name).app/Contents/MacOS/ diff --git a/hiro/gtk/header.hpp b/hiro/gtk/header.hpp index 65b67f2ac..5252df680 100644 --- a/hiro/gtk/header.hpp +++ b/hiro/gtk/header.hpp @@ -1,9 +1,8 @@ #if defined(DISPLAY_WINDOWS) #define TBS_TRANSPARENTBKGND 0x1000 - #include + #include #include - #include #include #include #include @@ -23,7 +22,6 @@ #include #endif #endif - #include #include #include #endif diff --git a/hiro/windows/header.hpp b/hiro/windows/header.hpp index f6417d490..e7cd59d54 100644 --- a/hiro/windows/header.hpp +++ b/hiro/windows/header.hpp @@ -1,14 +1,13 @@ -#include +#include #include -#include #include #include #include #include #include #include +#include #include -#include #include #include diff --git a/mia/GNUmakefile b/mia/GNUmakefile index b6930b6df..d1088f9fa 100644 --- a/mia/GNUmakefile +++ b/mia/GNUmakefile @@ -7,6 +7,10 @@ flags += -I. -I.. -I../ares -I../thirdparty nall.path := ../nall include $(nall.path)/GNUmakefile +thirdparty.path := ../thirdparty +libchdr.path := $(thirdparty.path)/libchdr +include $(thirdparty.path)/GNUmakefile + hiro.path := ../hiro hiro.resource := resource/$(name).rc include $(hiro.path)/GNUmakefile @@ -21,11 +25,14 @@ $(object.path)/ares.o: $(ares.path)/ares/ares.cpp $(object.path)/mia.o: $(mia.path)/mia.cpp $(object.path)/resource.o: $(mia.path)/resource/resource.cpp -$(objects): | $(object.path) +all.objects := $(libchdr.objects) $(nall.objects) $(hiro.objects) $(objects) +all.options := $(libchdr.options) $(nall.options) $(hiro.options) $(options) -all: $(hiro.objects) $(objects) | $(output.path) +$(all.objects): | $(object.path) + +all: $(all.objects) | $(output.path) $(info Linking $(output.path)/$(name) ...) - +@$(compiler) -o $(output.path)/$(name) $(hiro.objects) $(objects) $(hiro.options) $(options) + +@$(compiler) -o $(output.path)/$(name) $(all.objects) $(all.options) ifeq ($(platform),macos) rm -rf $(output.path)/$(name).app mkdir -p $(output.path)/$(name).app/Contents/MacOS/ diff --git a/nall/GNUmakefile b/nall/GNUmakefile index 88b56bb78..40e879116 100644 --- a/nall/GNUmakefile +++ b/nall/GNUmakefile @@ -329,3 +329,8 @@ ifeq ($(platform),windows) else prefix := $(HOME)/.local endif + +# objects +nall.objects := $(object.path)/nall.o + +$(object.path)/nall.o: $(nall.path)/nall.cpp diff --git a/nall/arguments.hpp b/nall/arguments.hpp index 7ecb536bf..1bf704379 100644 --- a/nall/arguments.hpp +++ b/nall/arguments.hpp @@ -1,8 +1,10 @@ #pragma once +#include +#include +#include #include #include -#include #include namespace nall { diff --git a/nall/decode/url.hpp b/nall/decode/url.hpp index 18382d1c9..62a99e248 100644 --- a/nall/decode/url.hpp +++ b/nall/decode/url.hpp @@ -1,5 +1,7 @@ #pragma once +#include + namespace nall::Decode { //returns empty string on malformed content diff --git a/nall/directory.cpp b/nall/directory.cpp new file mode 100644 index 000000000..31eb783f8 --- /dev/null +++ b/nall/directory.cpp @@ -0,0 +1,86 @@ +#include + +namespace nall { + +#if defined(PLATFORM_WINDOWS) + +NALL_HEADER_INLINE auto directory::exists(const string& pathname) -> bool { + if(!pathname) return false; + string name = pathname; + name.trim("\"", "\""); + DWORD result = GetFileAttributes(utf16_t(name)); + if(result == INVALID_FILE_ATTRIBUTES) return false; + return (result & FILE_ATTRIBUTE_DIRECTORY); +} + +NALL_HEADER_INLINE auto directory::ufolders(const string& pathname, const string& pattern) -> vector { + if(!pathname) { + //special root pseudo-folder (return list of drives) + wchar_t drives[PATH_MAX] = {0}; + GetLogicalDriveStrings(PATH_MAX, drives); + wchar_t* p = drives; + while(*p || *(p + 1)) { + if(!*p) *p = ';'; + p++; + } + return string{(const char*)utf8_t(drives)}.replace("\\", "/").split(";"); + } + + vector list; + string path = pathname; + path.transform("/", "\\"); + if(!path.endsWith("\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = (const char*)utf8_t(data.cFileName); + if(name.match(pattern)) list.append(name); + } + } + while(FindNextFile(handle, &data) != false) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + string name = (const char*)utf8_t(data.cFileName); + if(name.match(pattern)) list.append(name); + } + } + } + FindClose(handle); + } + return list; +} + +NALL_HEADER_INLINE auto directory::ufiles(const string& pathname, const string& pattern) -> vector { + if(!pathname) return {}; + + vector list; + string path = pathname; + path.transform("/", "\\"); + if(!path.endsWith("\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = (const char*)utf8_t(data.cFileName); + if(name.match(pattern)) list.append(name); + } + while(FindNextFile(handle, &data) != false) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + string name = (const char*)utf8_t(data.cFileName); + if(name.match(pattern)) list.append(name); + } + } + FindClose(handle); + } + return list; +} + +#endif + +} diff --git a/nall/directory.hpp b/nall/directory.hpp index c3a2d3aa6..86fd1ca48 100644 --- a/nall/directory.hpp +++ b/nall/directory.hpp @@ -192,83 +192,6 @@ inline auto directory::copy(const string& source, const string& target) -> bool } return _wrmdir(utf16_t(pathname)) == 0; } - - inline auto directory::exists(const string& pathname) -> bool { - if(!pathname) return false; - string name = pathname; - name.trim("\"", "\""); - DWORD result = GetFileAttributes(utf16_t(name)); - if(result == INVALID_FILE_ATTRIBUTES) return false; - return (result & FILE_ATTRIBUTE_DIRECTORY); - } - - inline auto directory::ufolders(const string& pathname, const string& pattern) -> vector { - if(!pathname) { - //special root pseudo-folder (return list of drives) - wchar_t drives[PATH_MAX] = {0}; - GetLogicalDriveStrings(PATH_MAX, drives); - wchar_t* p = drives; - while(*p || *(p + 1)) { - if(!*p) *p = ';'; - p++; - } - return string{(const char*)utf8_t(drives)}.replace("\\", "/").split(";"); - } - - vector list; - string path = pathname; - path.transform("/", "\\"); - if(!path.endsWith("\\")) path.append("\\"); - path.append("*"); - HANDLE handle; - WIN32_FIND_DATA data; - handle = FindFirstFile(utf16_t(path), &data); - if(handle != INVALID_HANDLE_VALUE) { - if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { - if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - string name = (const char*)utf8_t(data.cFileName); - if(name.match(pattern)) list.append(name); - } - } - while(FindNextFile(handle, &data) != false) { - if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { - if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - string name = (const char*)utf8_t(data.cFileName); - if(name.match(pattern)) list.append(name); - } - } - } - FindClose(handle); - } - return list; - } - - inline auto directory::ufiles(const string& pathname, const string& pattern) -> vector { - if(!pathname) return {}; - - vector list; - string path = pathname; - path.transform("/", "\\"); - if(!path.endsWith("\\")) path.append("\\"); - path.append("*"); - HANDLE handle; - WIN32_FIND_DATA data; - handle = FindFirstFile(utf16_t(path), &data); - if(handle != INVALID_HANDLE_VALUE) { - if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { - string name = (const char*)utf8_t(data.cFileName); - if(name.match(pattern)) list.append(name); - } - while(FindNextFile(handle, &data) != false) { - if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { - string name = (const char*)utf8_t(data.cFileName); - if(name.match(pattern)) list.append(name); - } - } - FindClose(handle); - } - return list; - } #else inline auto directoryIsFolder(DIR* dp, struct dirent* ep) -> bool { if(ep->d_type == DT_DIR) return true; @@ -353,3 +276,7 @@ inline auto directory::copy(const string& source, const string& target) -> bool #endif } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/dl.cpp b/nall/dl.cpp new file mode 100644 index 000000000..71c3ed50b --- /dev/null +++ b/nall/dl.cpp @@ -0,0 +1,39 @@ +#include + +namespace nall { + +#if defined(PLATFORM_WINDOWS) + +NALL_HEADER_INLINE auto library::open(const string& name, const string& path) -> bool { + if(handle) close(); + if(path) { + string filepath = {path, name, ".dll"}; + handle = (uintptr)LoadLibraryW(utf16_t(filepath)); + } + if(!handle) { + string filepath = {name, ".dll"}; + handle = (uintptr)LoadLibraryW(utf16_t(filepath)); + } + return handle; +} + +NALL_HEADER_INLINE auto library::openAbsolute(const string& name) -> bool { + if(handle) close(); + handle = (uintptr)LoadLibraryW(utf16_t(name)); + return handle; +} + +NALL_HEADER_INLINE auto library::sym(const string& name) -> void* { + if(!handle) return nullptr; + return (void*)GetProcAddress((HMODULE)handle, name); +} + +NALL_HEADER_INLINE auto library::close() -> void { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; +} + +#endif + +} diff --git a/nall/dl.hpp b/nall/dl.hpp index 8116d3492..942511b20 100644 --- a/nall/dl.hpp +++ b/nall/dl.hpp @@ -87,35 +87,7 @@ inline auto library::close() -> void { handle = 0; } #elif defined(PLATFORM_WINDOWS) -inline auto library::open(const string& name, const string& path) -> bool { - if(handle) close(); - if(path) { - string filepath = {path, name, ".dll"}; - handle = (uintptr)LoadLibraryW(utf16_t(filepath)); - } - if(!handle) { - string filepath = {name, ".dll"}; - handle = (uintptr)LoadLibraryW(utf16_t(filepath)); - } - return handle; -} - -inline auto library::openAbsolute(const string& name) -> bool { - if(handle) close(); - handle = (uintptr)LoadLibraryW(utf16_t(name)); - return handle; -} - -inline auto library::sym(const string& name) -> void* { - if(!handle) return nullptr; - return (void*)GetProcAddress((HMODULE)handle, name); -} - -inline auto library::close() -> void { - if(!handle) return; - FreeLibrary((HMODULE)handle); - handle = 0; -} +//defined in dl.cpp #else inline auto library::open(const string&, const string&) -> bool { return false; } inline auto library::openAbsolute(const string&) -> bool { return false; } @@ -124,3 +96,7 @@ inline auto library::close() -> void {} #endif } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/file-map.cpp b/nall/file-map.cpp new file mode 100644 index 000000000..c579f0c60 --- /dev/null +++ b/nall/file-map.cpp @@ -0,0 +1,80 @@ +#include + +namespace nall { + +#if defined(API_WINDOWS) + +NALL_HEADER_INLINE auto file_map::open(const string& filename, u32 mode_) -> bool { + close(); + if(file::exists(filename) && file::size(filename) == 0) return _open = true; + + s32 desiredAccess, creationDisposition, protection, mapAccess; + + switch(mode_) { + default: return false; + case mode::read: + desiredAccess = GENERIC_READ; + creationDisposition = OPEN_EXISTING; + protection = PAGE_READONLY; + mapAccess = FILE_MAP_READ; + break; + case mode::write: + //write access requires read access + desiredAccess = GENERIC_WRITE; + creationDisposition = CREATE_ALWAYS; + protection = PAGE_READWRITE; + mapAccess = FILE_MAP_ALL_ACCESS; + break; + case mode::modify: + desiredAccess = GENERIC_READ | GENERIC_WRITE; + creationDisposition = OPEN_EXISTING; + protection = PAGE_READWRITE; + mapAccess = FILE_MAP_ALL_ACCESS; + break; + case mode::append: + desiredAccess = GENERIC_READ | GENERIC_WRITE; + creationDisposition = CREATE_NEW; + protection = PAGE_READWRITE; + mapAccess = FILE_MAP_ALL_ACCESS; + break; + } + + _file = CreateFileW(utf16_t(filename), desiredAccess, FILE_SHARE_READ, nullptr, + creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr); + if(_file == INVALID_HANDLE_VALUE) return false; + + _size = GetFileSize(_file, nullptr); + + _map = CreateFileMapping(_file, nullptr, protection, 0, _size, nullptr); + if(_map == INVALID_HANDLE_VALUE) { + CloseHandle(_file); + _file = INVALID_HANDLE_VALUE; + return false; + } + + _data = (u8*)MapViewOfFile(_map, mapAccess, 0, 0, _size); + return _open = true; +} + +NALL_HEADER_INLINE auto file_map::close() -> void { + if(_data) { + UnmapViewOfFile(_data); + _data = nullptr; + } + + if(_map != INVALID_HANDLE_VALUE) { + CloseHandle(_map); + _map = INVALID_HANDLE_VALUE; + } + + if(_file != INVALID_HANDLE_VALUE) { + CloseHandle(_file); + _file = INVALID_HANDLE_VALUE; + } + + _open = false; +} + +#endif + +} diff --git a/nall/file-map.hpp b/nall/file-map.hpp index 2f7e506e5..0a7ec7009 100644 --- a/nall/file-map.hpp +++ b/nall/file-map.hpp @@ -73,76 +73,9 @@ public: return *this; } - auto open(const string& filename, u32 mode_) -> bool { - close(); - if(file::exists(filename) && file::size(filename) == 0) return _open = true; + auto open(const string& filename, u32 mode_) -> bool; - s32 desiredAccess, creationDisposition, protection, mapAccess; - - switch(mode_) { - default: return false; - case mode::read: - desiredAccess = GENERIC_READ; - creationDisposition = OPEN_EXISTING; - protection = PAGE_READONLY; - mapAccess = FILE_MAP_READ; - break; - case mode::write: - //write access requires read access - desiredAccess = GENERIC_WRITE; - creationDisposition = CREATE_ALWAYS; - protection = PAGE_READWRITE; - mapAccess = FILE_MAP_ALL_ACCESS; - break; - case mode::modify: - desiredAccess = GENERIC_READ | GENERIC_WRITE; - creationDisposition = OPEN_EXISTING; - protection = PAGE_READWRITE; - mapAccess = FILE_MAP_ALL_ACCESS; - break; - case mode::append: - desiredAccess = GENERIC_READ | GENERIC_WRITE; - creationDisposition = CREATE_NEW; - protection = PAGE_READWRITE; - mapAccess = FILE_MAP_ALL_ACCESS; - break; - } - - _file = CreateFileW(utf16_t(filename), desiredAccess, FILE_SHARE_READ, nullptr, - creationDisposition, FILE_ATTRIBUTE_NORMAL, nullptr); - if(_file == INVALID_HANDLE_VALUE) return false; - - _size = GetFileSize(_file, nullptr); - - _map = CreateFileMapping(_file, nullptr, protection, 0, _size, nullptr); - if(_map == INVALID_HANDLE_VALUE) { - CloseHandle(_file); - _file = INVALID_HANDLE_VALUE; - return false; - } - - _data = (u8*)MapViewOfFile(_map, mapAccess, 0, 0, _size); - return _open = true; - } - - auto close() -> void { - if(_data) { - UnmapViewOfFile(_data); - _data = nullptr; - } - - if(_map != INVALID_HANDLE_VALUE) { - CloseHandle(_map); - _map = INVALID_HANDLE_VALUE; - } - - if(_file != INVALID_HANDLE_VALUE) { - CloseHandle(_file); - _file = INVALID_HANDLE_VALUE; - } - - _open = false; - } + auto close() -> void; #else @@ -229,3 +162,7 @@ public: }; } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/http/client.cpp b/nall/http/client.cpp new file mode 100644 index 000000000..9d5c98ff7 --- /dev/null +++ b/nall/http/client.cpp @@ -0,0 +1,36 @@ +#include + +#if defined(PLATFORM_WINDOWS) + #include +#endif + +namespace nall::HTTP { + +NALL_HEADER_INLINE auto Client::open(const string& hostname, u16 port) -> bool { + addrinfo hint = {}; + hint.ai_family = AF_UNSPEC; + hint.ai_socktype = SOCK_STREAM; + hint.ai_flags = AI_ADDRCONFIG; + + if(getaddrinfo(hostname, string{port}, &hint, &info) != 0) return close(), false; + + fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol); + if(fd < 0) return close(), false; + + if(connect(fd, info->ai_addr, info->ai_addrlen) < 0) return close(), false; + return true; +} + +NALL_HEADER_INLINE auto Client::close() -> void { + if(fd) { + ::close(fd); + fd = -1; + } + + if(info) { + freeaddrinfo(info); + info = nullptr; + } +} + +} diff --git a/nall/http/client.hpp b/nall/http/client.hpp index 1fbf54a04..8e9641792 100644 --- a/nall/http/client.hpp +++ b/nall/http/client.hpp @@ -2,6 +2,8 @@ #include +struct addrinfo; + namespace nall::HTTP { struct Client : Role { @@ -16,21 +18,6 @@ private: addrinfo* info = nullptr; }; -inline auto Client::open(const string& hostname, u16 port) -> bool { - addrinfo hint = {}; - hint.ai_family = AF_UNSPEC; - hint.ai_socktype = SOCK_STREAM; - hint.ai_flags = AI_ADDRCONFIG; - - if(getaddrinfo(hostname, string{port}, &hint, &info) != 0) return close(), false; - - fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol); - if(fd < 0) return close(), false; - - if(connect(fd, info->ai_addr, info->ai_addrlen) < 0) return close(), false; - return true; -} - inline auto Client::upload(const Request& request) -> bool { return Role::upload(fd, request); } @@ -41,16 +28,8 @@ inline auto Client::download(const Request& request) -> Response { return response; } -inline auto Client::close() -> void { - if(fd) { - ::close(fd); - fd = -1; - } - - if(info) { - freeaddrinfo(info); - info = nullptr; - } } -} +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/http/response.hpp b/nall/http/response.hpp index 93a3b7a7b..bc5aa1206 100644 --- a/nall/http/response.hpp +++ b/nall/http/response.hpp @@ -1,5 +1,7 @@ #pragma once +#include +#include #include namespace nall::HTTP { diff --git a/nall/http/server.cpp b/nall/http/server.cpp new file mode 100644 index 000000000..98f225b72 --- /dev/null +++ b/nall/http/server.cpp @@ -0,0 +1,185 @@ +#include +#include + +#if defined(PLATFORM_WINDOWS) + #include +#endif + +namespace nall::HTTP { + +NALL_HEADER_INLINE auto Server::open(u16 port, const string& serviceName, const string& command) -> bool { + if(serviceName) { + if(!service::command(serviceName, command)) return false; + } + + fd4 = socket(AF_INET, SOCK_STREAM, 0); + fd6 = socket(AF_INET6, SOCK_STREAM, 0); + if(!ipv4() && !ipv6()) return false; + + { + #if defined(SO_RCVTIMEO) + if(settings.timeoutReceive) { + struct timeval rcvtimeo; + rcvtimeo.tv_sec = settings.timeoutReceive / 1000; + rcvtimeo.tv_usec = settings.timeoutReceive % 1000 * 1000; + if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval)); + if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval)); + } + #endif + + #if defined(SO_SNDTIMEO) + if(settings.timeoutSend) { + struct timeval sndtimeo; + sndtimeo.tv_sec = settings.timeoutSend / 1000; + sndtimeo.tv_usec = settings.timeoutSend % 1000 * 1000; + if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval)); + if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval)); + } + #endif + + #if defined(SO_NOSIGPIPE) //BSD, OSX + s32 nosigpipe = 1; + if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(s32)); + if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(s32)); + #endif + + #if defined(SO_REUSEADDR) //BSD, Linux, OSX + s32 reuseaddr = 1; + if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(s32)); + if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(s32)); + #endif + + #if defined(SO_REUSEPORT) //BSD, OSX + s32 reuseport = 1; + if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(s32)); + if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(s32)); + #endif + } + + addrin4.sin_family = AF_INET; + addrin4.sin_addr.s_addr = htonl(INADDR_ANY); + addrin4.sin_port = htons(port); + + addrin6.sin6_family = AF_INET6; + addrin6.sin6_addr = in6addr_any; + addrin6.sin6_port = htons(port); + + if(bind(fd4, (struct sockaddr*)&addrin4, sizeof(addrin4)) < 0 || listen(fd4, SOMAXCONN) < 0) ipv4_close(); + if(bind(fd6, (struct sockaddr*)&addrin6, sizeof(addrin6)) < 0 || listen(fd6, SOMAXCONN) < 0) ipv6_close(); + return ipv4() || ipv6(); +} + +NALL_HEADER_INLINE auto Server::ipv4_scan() -> bool { + struct pollfd query = {0}; + query.fd = fd4; + query.events = POLLIN; + poll(&query, 1, 0); + + if(query.fd == fd4 && query.revents & POLLIN) { + ++connections; + + thread::create([&](uintptr) { + thread::detach(); + + s32 clientfd = -1; + struct sockaddr_in settings = {0}; + socklen_t socklen = sizeof(sockaddr_in); + + clientfd = accept(fd4, (struct sockaddr*)&settings, &socklen); + if(clientfd < 0) return; + + u32 ip = ntohl(settings.sin_addr.s_addr); + + Request request; + request._ipv6 = false; + request._ip = { + (u8)(ip >> 24), ".", + (u8)(ip >> 16), ".", + (u8)(ip >> 8), ".", + (u8)(ip >> 0) + }; + + if(download(clientfd, request) && callback) { + auto response = callback(request); + upload(clientfd, response); + } else { + upload(clientfd, Response()); //"501 Not Implemented" + } + + ::close(clientfd); + --connections; + }, 0, settings.threadStackSize); + + return true; + } + + return false; +} + +NALL_HEADER_INLINE auto Server::ipv6_scan() -> bool { + struct pollfd query = {0}; + query.fd = fd6; + query.events = POLLIN; + poll(&query, 1, 0); + + if(query.fd == fd6 && query.revents & POLLIN) { + ++connections; + + thread::create([&](uintptr) { + thread::detach(); + + s32 clientfd = -1; + struct sockaddr_in6 settings = {0}; + socklen_t socklen = sizeof(sockaddr_in6); + + clientfd = accept(fd6, (struct sockaddr*)&settings, &socklen); + if(clientfd < 0) return; + + u8* ip = settings.sin6_addr.s6_addr; + u16 ipSegment[8]; + for(auto n : range(8)) ipSegment[n] = ip[n * 2 + 0] * 256 + ip[n * 2 + 1]; + + Request request; + request._ipv6 = true; + //RFC5952 IPv6 encoding: the first longest 2+ consecutive zero-sequence is compressed to "::" + s32 zeroOffset = -1; + s32 zeroLength = 0; + s32 zeroCounter = 0; + for(auto n : range(8)) { + u16 value = ipSegment[n]; + if(value == 0) zeroCounter++; + if(zeroCounter > zeroLength) { + zeroLength = zeroCounter; + zeroOffset = 1 + n - zeroLength; + } + if(value != 0) zeroCounter = 0; + } + if(zeroLength == 1) zeroOffset = -1; + for(u32 n = 0; n < 8;) { + if(n == zeroOffset) { + request._ip.append(n == 0 ? "::" : ":"); + n += zeroLength; + } else { + u16 value = ipSegment[n]; + request._ip.append(hex(value), n++ != 7 ? ":" : ""); + } + } + + if(download(clientfd, request) && callback) { + auto response = callback(request); + upload(clientfd, response); + } else { + upload(clientfd, Response()); //"501 Not Implemented" + } + + ::close(clientfd); + --connections; + }, 0, settings.threadStackSize); + + return true; + } + + return false; +} + +} diff --git a/nall/http/server.hpp b/nall/http/server.hpp index 5560fec27..94469ffba 100644 --- a/nall/http/server.hpp +++ b/nall/http/server.hpp @@ -3,6 +3,9 @@ #include #include +struct sockaddr_in; +struct sockaddr_in6; + namespace nall::HTTP { struct Server : Role, service { @@ -18,8 +21,10 @@ private: s32 fd4 = -1; s32 fd6 = -1; - struct sockaddr_in addrin4 = {0}; - struct sockaddr_in6 addrin6 = {0}; + u64 addrin4_storage[16] = {0}; //sizeof(sockaddr_storage) = 128 + u64 addrin6_storage[16] = {0}; + sockaddr_in& addrin4 = (sockaddr_in&)addrin4_storage; + sockaddr_in6& addrin6 = (sockaddr_in6&)addrin6_storage; auto ipv4() const -> bool { return fd4 >= 0; } auto ipv6() const -> bool { return fd6 >= 0; } @@ -31,68 +36,6 @@ private: auto ipv6_scan() -> bool; }; -inline auto Server::open(u16 port, const string& serviceName, const string& command) -> bool { - if(serviceName) { - if(!service::command(serviceName, command)) return false; - } - - fd4 = socket(AF_INET, SOCK_STREAM, 0); - fd6 = socket(AF_INET6, SOCK_STREAM, 0); - if(!ipv4() && !ipv6()) return false; - - { - #if defined(SO_RCVTIMEO) - if(settings.timeoutReceive) { - struct timeval rcvtimeo; - rcvtimeo.tv_sec = settings.timeoutReceive / 1000; - rcvtimeo.tv_usec = settings.timeoutReceive % 1000 * 1000; - if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval)); - if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_RCVTIMEO, &rcvtimeo, sizeof(struct timeval)); - } - #endif - - #if defined(SO_SNDTIMEO) - if(settings.timeoutSend) { - struct timeval sndtimeo; - sndtimeo.tv_sec = settings.timeoutSend / 1000; - sndtimeo.tv_usec = settings.timeoutSend % 1000 * 1000; - if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval)); - if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_SNDTIMEO, &sndtimeo, sizeof(struct timeval)); - } - #endif - - #if defined(SO_NOSIGPIPE) //BSD, OSX - s32 nosigpipe = 1; - if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(s32)); - if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(s32)); - #endif - - #if defined(SO_REUSEADDR) //BSD, Linux, OSX - s32 reuseaddr = 1; - if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(s32)); - if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(s32)); - #endif - - #if defined(SO_REUSEPORT) //BSD, OSX - s32 reuseport = 1; - if(ipv4()) setsockopt(fd4, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(s32)); - if(ipv6()) setsockopt(fd6, SOL_SOCKET, SO_REUSEPORT, &reuseport, sizeof(s32)); - #endif - } - - addrin4.sin_family = AF_INET; - addrin4.sin_addr.s_addr = htonl(INADDR_ANY); - addrin4.sin_port = htons(port); - - addrin6.sin6_family = AF_INET6; - addrin6.sin6_addr = in6addr_any; - addrin6.sin6_port = htons(port); - - if(bind(fd4, (struct sockaddr*)&addrin4, sizeof(addrin4)) < 0 || listen(fd4, SOMAXCONN) < 0) ipv4_close(); - if(bind(fd6, (struct sockaddr*)&addrin6, sizeof(addrin6)) < 0 || listen(fd6, SOMAXCONN) < 0) ipv6_close(); - return ipv4() || ipv6(); -} - inline auto Server::main(const function& function) -> void { callback = function; } @@ -105,122 +48,13 @@ inline auto Server::scan() -> string { return "idle"; } -inline auto Server::ipv4_scan() -> bool { - struct pollfd query = {0}; - query.fd = fd4; - query.events = POLLIN; - poll(&query, 1, 0); - - if(query.fd == fd4 && query.revents & POLLIN) { - ++connections; - - thread::create([&](uintptr) { - thread::detach(); - - s32 clientfd = -1; - struct sockaddr_in settings = {0}; - socklen_t socklen = sizeof(sockaddr_in); - - clientfd = accept(fd4, (struct sockaddr*)&settings, &socklen); - if(clientfd < 0) return; - - u32 ip = ntohl(settings.sin_addr.s_addr); - - Request request; - request._ipv6 = false; - request._ip = { - (u8)(ip >> 24), ".", - (u8)(ip >> 16), ".", - (u8)(ip >> 8), ".", - (u8)(ip >> 0) - }; - - if(download(clientfd, request) && callback) { - auto response = callback(request); - upload(clientfd, response); - } else { - upload(clientfd, Response()); //"501 Not Implemented" - } - - ::close(clientfd); - --connections; - }, 0, settings.threadStackSize); - - return true; - } - - return false; -} - -inline auto Server::ipv6_scan() -> bool { - struct pollfd query = {0}; - query.fd = fd6; - query.events = POLLIN; - poll(&query, 1, 0); - - if(query.fd == fd6 && query.revents & POLLIN) { - ++connections; - - thread::create([&](uintptr) { - thread::detach(); - - s32 clientfd = -1; - struct sockaddr_in6 settings = {0}; - socklen_t socklen = sizeof(sockaddr_in6); - - clientfd = accept(fd6, (struct sockaddr*)&settings, &socklen); - if(clientfd < 0) return; - - u8* ip = settings.sin6_addr.s6_addr; - u16 ipSegment[8]; - for(auto n : range(8)) ipSegment[n] = ip[n * 2 + 0] * 256 + ip[n * 2 + 1]; - - Request request; - request._ipv6 = true; - //RFC5952 IPv6 encoding: the first longest 2+ consecutive zero-sequence is compressed to "::" - s32 zeroOffset = -1; - s32 zeroLength = 0; - s32 zeroCounter = 0; - for(auto n : range(8)) { - u16 value = ipSegment[n]; - if(value == 0) zeroCounter++; - if(zeroCounter > zeroLength) { - zeroLength = zeroCounter; - zeroOffset = 1 + n - zeroLength; - } - if(value != 0) zeroCounter = 0; - } - if(zeroLength == 1) zeroOffset = -1; - for(u32 n = 0; n < 8;) { - if(n == zeroOffset) { - request._ip.append(n == 0 ? "::" : ":"); - n += zeroLength; - } else { - u16 value = ipSegment[n]; - request._ip.append(hex(value), n++ != 7 ? ":" : ""); - } - } - - if(download(clientfd, request) && callback) { - auto response = callback(request); - upload(clientfd, response); - } else { - upload(clientfd, Response()); //"501 Not Implemented" - } - - ::close(clientfd); - --connections; - }, 0, settings.threadStackSize); - - return true; - } - - return false; -} - inline auto Server::close() -> void { ipv4_close(); ipv6_close(); } } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/inode.cpp b/nall/inode.cpp new file mode 100644 index 000000000..48bef1876 --- /dev/null +++ b/nall/inode.cpp @@ -0,0 +1,15 @@ +#include + +namespace nall { + +NALL_HEADER_INLINE auto inode::hidden(const string& name) -> bool { + #if defined(PLATFORM_WINDOWS) + auto attributes = GetFileAttributes(utf16_t(name)); + return attributes & FILE_ATTRIBUTE_HIDDEN; + #else + //todo: is this really the best way to do this? stat doesn't have S_ISHIDDEN ... + return name.split("/").last().beginsWith("."); + #endif +} + +} diff --git a/nall/inode.hpp b/nall/inode.hpp index 491fdd161..6080ac195 100644 --- a/nall/inode.hpp +++ b/nall/inode.hpp @@ -31,15 +31,7 @@ struct inode { return access(name, X_OK) == 0; } - static auto hidden(const string& name) -> bool { - #if defined(PLATFORM_WINDOWS) - auto attributes = GetFileAttributes(utf16_t(name)); - return attributes & FILE_ATTRIBUTE_HIDDEN; - #else - //todo: is this really the best way to do this? stat doesn't have S_ISHIDDEN ... - return name.split("/").last().beginsWith("."); - #endif - } + static auto hidden(const string& name) -> bool; static auto mode(const string& name) -> u32 { struct stat data{}; @@ -161,3 +153,7 @@ struct inode { }; } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/intrinsics.hpp b/nall/intrinsics.hpp index 29c65da4e..8c6963c2a 100644 --- a/nall/intrinsics.hpp +++ b/nall/intrinsics.hpp @@ -1,5 +1,11 @@ #pragma once +#if defined(NALL_HEADER_ONLY) + #define NALL_HEADER_INLINE inline +#else + #define NALL_HEADER_INLINE +#endif + #if defined(__APPLE__) #include #elif defined(linux) || defined(__linux__) diff --git a/nall/main.cpp b/nall/main.cpp new file mode 100644 index 000000000..2c29dac3f --- /dev/null +++ b/nall/main.cpp @@ -0,0 +1,44 @@ +#include + +#if defined(PLATFORM_WINDOWS) + #include + #include + #include +#endif + +namespace nall { + +auto main(int argc, char** argv) -> int { + #if defined(PLATFORM_WINDOWS) + CoInitialize(0); + WSAData wsaData{0}; + WSAStartup(MAKEWORD(2, 2), &wsaData); + _setmode(_fileno(stdin ), O_BINARY); + _setmode(_fileno(stdout), O_BINARY); + _setmode(_fileno(stderr), O_BINARY); + #endif + + main(Arguments{argc, argv}); + + #if !defined(PLATFORM_WINDOWS) + //when a program is running, input on the terminal queues in stdin + //when terminating the program, the shell proceeds to try and execute all stdin data + //this is annoying behavior: this code tries to minimize the impact as much as it can + //we can flush all of stdin up to the last line feed, preventing spurious commands from executing + //however, even with setvbuf(_IONBF), we can't stop the last line from echoing to the terminal + auto flags = fcntl(fileno(stdin), F_GETFL, 0); + fcntl(fileno(stdin), F_SETFL, flags | O_NONBLOCK); //don't allow read() to block when empty + char buffer[4096], data = false; + while(read(fileno(stdin), buffer, sizeof(buffer)) > 0) data = true; + fcntl(fileno(stdin), F_SETFL, flags); //restore original flags for the terminal + if(data) putchar('\r'); //ensures PS1 is printed at the start of the line + #endif + + return EXIT_SUCCESS; +} + +} + +auto main(int argc, char** argv) -> int { + return nall::main(argc, argv); +} diff --git a/nall/main.hpp b/nall/main.hpp index 273e43f60..f50cc3753 100644 --- a/nall/main.hpp +++ b/nall/main.hpp @@ -7,36 +7,9 @@ namespace nall { auto main(Arguments arguments) -> void; - auto main(int argc, char** argv) -> int { - #if defined(PLATFORM_WINDOWS) - CoInitialize(0); - WSAData wsaData{0}; - WSAStartup(MAKEWORD(2, 2), &wsaData); - _setmode(_fileno(stdin ), O_BINARY); - _setmode(_fileno(stdout), O_BINARY); - _setmode(_fileno(stderr), O_BINARY); - #endif - - main(Arguments{argc, argv}); - - #if !defined(PLATFORM_WINDOWS) - //when a program is running, input on the terminal queues in stdin - //when terminating the program, the shell proceeds to try and execute all stdin data - //this is annoying behavior: this code tries to minimize the impact as much as it can - //we can flush all of stdin up to the last line feed, preventing spurious commands from executing - //however, even with setvbuf(_IONBF), we can't stop the last line from echoing to the terminal - auto flags = fcntl(fileno(stdin), F_GETFL, 0); - fcntl(fileno(stdin), F_SETFL, flags | O_NONBLOCK); //don't allow read() to block when empty - char buffer[4096], data = false; - while(read(fileno(stdin), buffer, sizeof(buffer)) > 0) data = true; - fcntl(fileno(stdin), F_SETFL, flags); //restore original flags for the terminal - if(data) putchar('\r'); //ensures PS1 is printed at the start of the line - #endif - - return EXIT_SUCCESS; - } + auto main(int argc, char** argv) -> int; } -auto main(int argc, char** argv) -> int { - return nall::main(argc, argv); -} +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/memory.cpp b/nall/memory.cpp new file mode 100644 index 000000000..8479cff96 --- /dev/null +++ b/nall/memory.cpp @@ -0,0 +1,47 @@ +#include + +namespace nall::memory { + +NALL_HEADER_INLINE auto map(u32 size, bool executable) -> void* { + #if defined(API_WINDOWS) + DWORD protect = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; + return VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, protect); + #elif defined(API_POSIX) + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_ANON | MAP_PRIVATE; + if(executable) { + prot |= PROT_EXEC; + #if defined(PLATFORM_MACOS) + flags |= MAP_JIT; + #endif + } + return mmap(nullptr, size, prot, flags, -1, 0); + #else + return nullptr; + #endif +} + +NALL_HEADER_INLINE auto unmap(void* target, u32 size) -> void { + #if defined(API_WINDOWS) + VirtualFree(target, 0, MEM_RELEASE); + #elif defined(API_POSIX) + munmap(target, size); + #endif +} + +NALL_HEADER_INLINE auto protect(void* target, u32 size, bool executable) -> void { + #if defined(API_WINDOWS) + DWORD protect = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; + DWORD oldProtect; + VirtualProtect(target, size, protect, &oldProtect); + #elif defined(API_POSIX) + int prot = PROT_READ | PROT_WRITE; + if(executable) { + prot |= PROT_EXEC; + } + int ret = mprotect(target, size, prot); + assert(ret == 0); + #endif +} + +} diff --git a/nall/memory.hpp b/nall/memory.hpp index 01161f538..c932702ba 100644 --- a/nall/memory.hpp +++ b/nall/memory.hpp @@ -195,47 +195,11 @@ template auto writem(void* target, T data) -> void { for(s32 n = size - 1; n >= 0; n--) *p++ = data >> n * 8; } -inline auto map(u32 size, bool executable) -> void* { - #if defined(API_WINDOWS) - DWORD protect = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; - return VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, protect); - #elif defined(API_POSIX) - int prot = PROT_READ | PROT_WRITE; - int flags = MAP_ANON | MAP_PRIVATE; - if(executable) { - prot |= PROT_EXEC; - #if defined(PLATFORM_MACOS) - flags |= MAP_JIT; - #endif - } - return mmap(nullptr, size, prot, flags, -1, 0); - #else - return nullptr; - #endif -} +auto map(u32 size, bool executable) -> void*; -inline auto unmap(void* target, u32 size) -> void { - #if defined(API_WINDOWS) - VirtualFree(target, 0, MEM_RELEASE); - #elif defined(API_POSIX) - munmap(target, size); - #endif -} +auto unmap(void* target, u32 size) -> void; -inline auto protect(void* target, u32 size, bool executable) -> void { - #if defined(API_WINDOWS) - DWORD protect = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; - DWORD oldProtect; - VirtualProtect(target, size, protect, &oldProtect); - #elif defined(API_POSIX) - int prot = PROT_READ | PROT_WRITE; - if(executable) { - prot |= PROT_EXEC; - } - int ret = mprotect(target, size, prot); - assert(ret == 0); - #endif -} +auto protect(void* target, u32 size, bool executable) -> void; inline auto jitprotect(bool executable) -> void { #if defined(PLATFORM_MACOS) @@ -246,3 +210,7 @@ inline auto jitprotect(bool executable) -> void { } } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/nall.cpp b/nall/nall.cpp new file mode 100644 index 000000000..e0a650055 --- /dev/null +++ b/nall/nall.cpp @@ -0,0 +1,32 @@ +#if !defined(NALL_HEADER_ONLY) + +#include + +#if defined(PLATFORM_WINDOWS) + #include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//currently unused by ares +//#include +//#include +//#include +#if defined(PLATFORM_WINDOWS) + //#include + //#include + //#include + #include + #include +#endif + +#endif diff --git a/nall/path.cpp b/nall/path.cpp new file mode 100644 index 000000000..02a12b9f3 --- /dev/null +++ b/nall/path.cpp @@ -0,0 +1,144 @@ +#include + +#if defined(PLATFORM_WINDOWS) + #include +#endif + +namespace nall::Path { + +NALL_HEADER_INLINE auto program() -> string { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + GetModuleFileName(nullptr, path, PATH_MAX); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + return Path::real(result); + #else + Dl_info info; + dladdr((void*)&program, &info); + return Path::real(info.dli_fname); + #endif +} + +NALL_HEADER_INLINE auto root() -> string { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + SHGetFolderPathW(nullptr, CSIDL_WINDOWS | CSIDL_FLAG_CREATE, nullptr, 0, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + return slice(result, 0, 3); + #else + return "/"; + #endif +} + +NALL_HEADER_INLINE auto user() -> string { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + SHGetFolderPathW(nullptr, CSIDL_PROFILE | CSIDL_FLAG_CREATE, nullptr, 0, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + #else + struct passwd* userinfo = getpwuid(getuid()); + string result = userinfo->pw_dir; + #endif + if(!result) result = "."; + if(!result.endsWith("/")) result.append("/"); + return result; +} + +NALL_HEADER_INLINE auto desktop(string_view name) -> string { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + SHGetFolderPathW(nullptr, CSIDL_DESKTOP | CSIDL_FLAG_CREATE, nullptr, 0, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + #elif defined(PLATFORM_MACOS) + string result = {user(), "Desktop/"}; + #else + string result; + if(const char *env = getenv("XDG_DESKTOP_DIR")) { + result = string(env); + } else { + result = {user(), "Desktop/"}; + } + #endif + if(!result) result = "."; + if(!result.endsWith("/")) result.append("/"); + return result.append(name); +} + +NALL_HEADER_INLINE auto userSettings() -> string { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + #elif defined(PLATFORM_MACOS) + string result = {Path::user(), "Library/Application Support/"}; + #else + string result; + if(const char *env = getenv("XDG_CONFIG_HOME")) { + result = string(env); + } else { + result = {Path::user(), ".config/"}; + } + #endif + if(!result) result = "."; + if(!result.endsWith("/")) result.append("/"); + return result; +} + +NALL_HEADER_INLINE auto userData() -> string { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + #elif defined(PLATFORM_MACOS) + string result = {Path::user(), "Library/Application Support/"}; + #else + string result; + if(const char* env = getenv("XDG_DATA_HOME")) { + result = string(env); + } else { + result = {Path::user(), ".local/share/"}; + } + #endif + if(!result) result = "."; + if(!result.endsWith("/")) result.append("/"); + return result; +} + +NALL_HEADER_INLINE auto sharedData() -> string { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + #elif defined(PLATFORM_MACOS) + string result = "/Library/Application Support/"; + #else + string result = "/usr/share/"; + #endif + if(!result) result = "."; + if(!result.endsWith("/")) result.append("/"); + return result; +} + +NALL_HEADER_INLINE auto temporary() -> string { + #if defined(PLATFORM_WINDOWS) + wchar_t path[PATH_MAX] = L""; + GetTempPathW(PATH_MAX, path); + string result = (const char*)utf8_t(path); + result.transform("\\", "/"); + #elif defined(P_tmpdir) + string result = P_tmpdir; + #else + string result = "/tmp/"; + #endif + if(!result.endsWith("/")) result.append("/"); + return result; +} + +} diff --git a/nall/path.hpp b/nall/path.hpp index f63eed2dd..b9b602a68 100644 --- a/nall/path.hpp +++ b/nall/path.hpp @@ -24,73 +24,19 @@ inline auto real(string_view name) -> string { return result; } -inline auto program() -> string { - #if defined(PLATFORM_WINDOWS) - wchar_t path[PATH_MAX] = L""; - GetModuleFileName(nullptr, path, PATH_MAX); - string result = (const char*)utf8_t(path); - result.transform("\\", "/"); - return Path::real(result); - #else - Dl_info info; - dladdr((void*)&program, &info); - return Path::real(info.dli_fname); - #endif -} +auto program() -> string; // / // c:/ -inline auto root() -> string { - #if defined(PLATFORM_WINDOWS) - wchar_t path[PATH_MAX] = L""; - SHGetFolderPathW(nullptr, CSIDL_WINDOWS | CSIDL_FLAG_CREATE, nullptr, 0, path); - string result = (const char*)utf8_t(path); - result.transform("\\", "/"); - return slice(result, 0, 3); - #else - return "/"; - #endif -} +auto root() -> string; // /home/username/ // c:/users/username/ -inline auto user() -> string { - #if defined(PLATFORM_WINDOWS) - wchar_t path[PATH_MAX] = L""; - SHGetFolderPathW(nullptr, CSIDL_PROFILE | CSIDL_FLAG_CREATE, nullptr, 0, path); - string result = (const char*)utf8_t(path); - result.transform("\\", "/"); - #else - struct passwd* userinfo = getpwuid(getuid()); - string result = userinfo->pw_dir; - #endif - if(!result) result = "."; - if(!result.endsWith("/")) result.append("/"); - return result; -} +auto user() -> string; // /home/username/Desktop/ // c:/users/username/Desktop/ -inline auto desktop(string_view name = {}) -> string { - #if defined(PLATFORM_WINDOWS) - wchar_t path[PATH_MAX] = L""; - SHGetFolderPathW(nullptr, CSIDL_DESKTOP | CSIDL_FLAG_CREATE, nullptr, 0, path); - string result = (const char*)utf8_t(path); - result.transform("\\", "/"); - #elif defined(PLATFORM_MACOS) - string result = {user(), "Desktop/"}; - #else - string result; - if(const char *env = getenv("XDG_DESKTOP_DIR")) { - result = string(env); - } else { - result = {user(), "Desktop/"}; - } - #endif - if(!result) result = "."; - if(!result.endsWith("/")) result.append("/"); - return result.append(name); -} +auto desktop(string_view name = {}) -> string; //todo: MacOS uses the same location for userData() and userSettings() //... is there a better option here? @@ -98,85 +44,24 @@ inline auto desktop(string_view name = {}) -> string { // /home/username/.config/ // ~/Library/Application Support/ // c:/users/username/appdata/roaming/ -inline auto userSettings() -> string { - #if defined(PLATFORM_WINDOWS) - wchar_t path[PATH_MAX] = L""; - SHGetFolderPathW(nullptr, CSIDL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); - string result = (const char*)utf8_t(path); - result.transform("\\", "/"); - #elif defined(PLATFORM_MACOS) - string result = {Path::user(), "Library/Application Support/"}; - #else - string result; - if(const char *env = getenv("XDG_CONFIG_HOME")) { - result = string(env); - } else { - result = {Path::user(), ".config/"}; - } - #endif - if(!result) result = "."; - if(!result.endsWith("/")) result.append("/"); - return result; -} +auto userSettings() -> string; // /home/username/.local/share/ // ~/Library/Application Support/ // c:/users/username/appdata/local/ -inline auto userData() -> string { - #if defined(PLATFORM_WINDOWS) - wchar_t path[PATH_MAX] = L""; - SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); - string result = (const char*)utf8_t(path); - result.transform("\\", "/"); - #elif defined(PLATFORM_MACOS) - string result = {Path::user(), "Library/Application Support/"}; - #else - string result; - if(const char* env = getenv("XDG_DATA_HOME")) { - result = string(env); - } else { - result = {Path::user(), ".local/share/"}; - } - #endif - if(!result) result = "."; - if(!result.endsWith("/")) result.append("/"); - return result; -} +auto userData() -> string; // /usr/share // /Library/Application Support/ // c:/ProgramData/ -inline auto sharedData() -> string { - #if defined(PLATFORM_WINDOWS) - wchar_t path[PATH_MAX] = L""; - SHGetFolderPathW(nullptr, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, nullptr, 0, path); - string result = (const char*)utf8_t(path); - result.transform("\\", "/"); - #elif defined(PLATFORM_MACOS) - string result = "/Library/Application Support/"; - #else - string result = "/usr/share/"; - #endif - if(!result) result = "."; - if(!result.endsWith("/")) result.append("/"); - return result; -} +auto sharedData() -> string; // /tmp // c:/users/username/AppData/Local/Temp/ -inline auto temporary() -> string { - #if defined(PLATFORM_WINDOWS) - wchar_t path[PATH_MAX] = L""; - GetTempPathW(PATH_MAX, path); - string result = (const char*)utf8_t(path); - result.transform("\\", "/"); - #elif defined(P_tmpdir) - string result = P_tmpdir; - #else - string result = "/tmp/"; - #endif - if(!result.endsWith("/")) result.append("/"); - return result; -} +auto temporary() -> string; } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/platform.cpp b/nall/platform.cpp new file mode 100644 index 000000000..ab3c29138 --- /dev/null +++ b/nall/platform.cpp @@ -0,0 +1,25 @@ +#include + +#if defined(PLATFORM_WINDOWS) + +#include + +NALL_HEADER_INLINE auto poll(struct pollfd fds[], unsigned long nfds, int timeout) -> int { return WSAPoll(fds, nfds, timeout); } + +namespace nall { + +NALL_HEADER_INLINE auto recv(int socket, void* buffer, size_t length, int flags) -> ssize_t { + return ::recv(socket, (char*)buffer, length, flags); +} + +NALL_HEADER_INLINE auto send(int socket, const void* buffer, size_t length, int flags) -> ssize_t { + return ::send(socket, (const char*)buffer, length, flags); +} + +NALL_HEADER_INLINE auto setsockopt(int socket, int level, int option_name, const void* option_value, int option_len) -> int { + return ::setsockopt(socket, level, option_name, (const char*)option_value, option_len); +} + +} + +#endif diff --git a/nall/platform.hpp b/nall/platform.hpp index 9b7b0b035..b51badcd4 100644 --- a/nall/platform.hpp +++ b/nall/platform.hpp @@ -9,18 +9,12 @@ namespace Math { } #if defined(PLATFORM_WINDOWS) - #include - #include - #include - #include - #include - #include + #if defined(NALL_HEADER_ONLY) + #include + #endif #include #include #include - #include - #include - #include #include #endif @@ -73,20 +67,20 @@ namespace Math { #endif #if defined(PLATFORM_WINDOWS) - #undef IN - #undef OUT - #undef interface #define dllexport __declspec(dllexport) #define MSG_NOSIGNAL 0 + #define PATH_MAX 260 - extern "C" { - using pollfd = WSAPOLLFD; - } + #if !defined(INVALID_HANDLE_VALUE) + #define INVALID_HANDLE_VALUE ((HANDLE)-1) + #endif + + typedef void* HANDLE; inline auto access(const char* path, int amode) -> int { return _waccess(nall::utf16_t(path), amode); } inline auto getcwd(char* buf, size_t size) -> char* { wchar_t wpath[PATH_MAX] = L""; if(!_wgetcwd(wpath, size)) return nullptr; strcpy(buf, nall::utf8_t(wpath)); return buf; } inline auto mkdir(const char* path, int mode) -> int { return _wmkdir(nall::utf16_t(path)); } - inline auto poll(struct pollfd fds[], unsigned long nfds, int timeout) -> int { return WSAPoll(fds, nfds, timeout); } + inline auto poll(struct pollfd fds[], unsigned long nfds, int timeout) -> int; inline auto putenv(const char* value) -> int { return _wputenv(nall::utf16_t(value)); } inline auto realpath(const char* file_name, char* resolved_name) -> char* { wchar_t wfile_name[PATH_MAX] = L""; if(!_wfullpath(wfile_name, nall::utf16_t(file_name), PATH_MAX)) return nullptr; strcpy(resolved_name, nall::utf8_t(wfile_name)); return resolved_name; } inline auto rename(const char* oldname, const char* newname) -> int { return _wrename(nall::utf16_t(oldname), nall::utf16_t(newname)); } @@ -94,17 +88,9 @@ namespace Math { namespace nall { //network functions take void*, not char*. this allows them to be used without casting - inline auto recv(int socket, void* buffer, size_t length, int flags) -> ssize_t { - return ::recv(socket, (char*)buffer, length, flags); - } - - inline auto send(int socket, const void* buffer, size_t length, int flags) -> ssize_t { - return ::send(socket, (const char*)buffer, length, flags); - } - - inline auto setsockopt(int socket, int level, int option_name, const void* option_value, socklen_t option_len) -> int { - return ::setsockopt(socket, level, option_name, (const char*)option_value, option_len); - } + auto recv(int socket, void* buffer, size_t length, int flags) -> ssize_t; + auto send(int socket, const void* buffer, size_t length, int flags) -> ssize_t; + auto setsockopt(int socket, int level, int option_name, const void* option_value, int option_len) -> int; } #else #define dllexport @@ -194,3 +180,7 @@ inline auto spinloop() -> void { #define export $export #define register $register + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/random.cpp b/nall/random.cpp new file mode 100644 index 000000000..02ef580cf --- /dev/null +++ b/nall/random.cpp @@ -0,0 +1,40 @@ +#include + +#if defined(PLATFORM_LINUX) && __has_include() + #include +#elif defined(PLATFORM_ANDROID) && __has_include() + #include +#elif defined(PLATFORM_WINDOWS) && __has_include() + #include +#else + #include +#endif + +namespace nall { + +NALL_HEADER_INLINE auto RNGBase::randomSeed() -> u256 { + u256 seed = 0; + #if defined(PLATFORM_BSD) || defined(PLATFORM_MACOS) + for(u32 n : range(8)) seed = seed << 32 | (u32)arc4random(); + #elif defined(PLATFORM_LINUX) && __has_include() + getrandom(&seed, 32, GRND_NONBLOCK); + #elif defined(PLATFORM_ANDROID) && __has_include() + syscall(__NR_getrandom, &seed, 32, 0x0001); //GRND_NONBLOCK + #elif defined(PLATFORM_WINDOWS) && __has_include() + HCRYPTPROV provider; + if(CryptAcquireContext(&provider, nullptr, MS_STRONG_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + CryptGenRandom(provider, 32, (BYTE*)&seed); + CryptReleaseContext(provider, 0); + } + #else + srand(time(nullptr)); + for(u32 n : range(32)) seed = seed << 8 | (u8)rand(); + if(auto fp = fopen("/dev/urandom", "rb")) { + fread(&seed, 32, 1, fp); + fclose(fp); + } + #endif + return seed; +} + +} diff --git a/nall/random.hpp b/nall/random.hpp index d93532f6e..c8114ba28 100644 --- a/nall/random.hpp +++ b/nall/random.hpp @@ -9,19 +9,14 @@ #include #endif -#if defined(PLATFORM_LINUX) && __has_include() - #include -#elif defined(PLATFORM_ANDROID) && __has_include() - #include -#elif defined(PLATFORM_WINDOWS) && __has_include() - #include -#else - #include -#endif - namespace nall { -template struct RNG { +struct RNGBase { +protected: + auto randomSeed() -> u256; +}; + +template struct RNG : RNGBase { template auto random() -> T { u64 value = 0; for(u32 n : range((sizeof(T) + 3) / 4)) { @@ -37,32 +32,6 @@ template struct RNG { if(value >= threshold) return value % range; } } - -protected: - auto randomSeed() -> u256 { - u256 seed = 0; - #if defined(PLATFORM_BSD) || defined(PLATFORM_MACOS) - for(u32 n : range(8)) seed = seed << 32 | (u32)arc4random(); - #elif defined(PLATFORM_LINUX) && __has_include() - getrandom(&seed, 32, GRND_NONBLOCK); - #elif defined(PLATFORM_ANDROID) && __has_include() - syscall(__NR_getrandom, &seed, 32, 0x0001); //GRND_NONBLOCK - #elif defined(PLATFORM_WINDOWS) && __has_include() - HCRYPTPROV provider; - if(CryptAcquireContext(&provider, nullptr, MS_STRONG_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { - CryptGenRandom(provider, 32, (BYTE*)&seed); - CryptReleaseContext(provider, 0); - } - #else - srand(time(nullptr)); - for(u32 n : range(32)) seed = seed << 8 | (u8)rand(); - if(auto fp = fopen("/dev/urandom", "rb")) { - fread(&seed, 32, 1, fp); - fclose(fp); - } - #endif - return seed; - } }; namespace PRNG { @@ -170,3 +139,7 @@ template inline auto random() -> T { } } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/run.cpp b/nall/run.cpp new file mode 100644 index 000000000..a4f566f5b --- /dev/null +++ b/nall/run.cpp @@ -0,0 +1,102 @@ +#include +#include + +#if defined(PLATFORM_WINDOWS) + #include +#endif + +namespace nall { + +#if defined(PLATFORM_WINDOWS) + +NALL_HEADER_INLINE auto execute(const string& name, vector argl) -> execute_result_t { + for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""}; + string arguments = argl.merge(" "); + + SECURITY_ATTRIBUTES sa; + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = true; + sa.lpSecurityDescriptor = nullptr; + + HANDLE stdoutRead; + HANDLE stdoutWrite; + if(!CreatePipe(&stdoutRead, &stdoutWrite, &sa, 0)) return {}; + if(!SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0)) return {}; + + HANDLE stderrRead; + HANDLE stderrWrite; + if(!CreatePipe(&stderrRead, &stderrWrite, &sa, 0)) return {}; + if(!SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0)) return {}; + + HANDLE stdinRead; + HANDLE stdinWrite; + if(!CreatePipe(&stdinRead, &stdinWrite, &sa, 0)) return {}; + if(!SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0)) return {}; + + STARTUPINFO si; + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.hStdOutput = stdoutWrite; + si.hStdError = stderrWrite; + si.hStdInput = stdinRead; + si.dwFlags = STARTF_USESTDHANDLES; + + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + + if(!CreateProcess( + nullptr, utf16_t(arguments), + nullptr, nullptr, true, CREATE_NO_WINDOW, + nullptr, nullptr, &si, &pi + )) return {}; + + DWORD exitCode = EXIT_FAILURE; + if(WaitForSingleObject(pi.hProcess, INFINITE)) return {}; + if(!GetExitCodeProcess(pi.hProcess, &exitCode)) return {}; + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + char buffer[256]; + execute_result_t result; + result.code = exitCode; + + while(true) { + DWORD read, available, remaining; + if(!PeekNamedPipe(stdoutRead, nullptr, sizeof(buffer), &read, &available, &remaining)) break; + if(read == 0) break; + + if(!ReadFile(stdoutRead, buffer, sizeof(buffer), &read, nullptr)) break; + if(read == 0) break; + + auto offset = result.output.size(); + result.output.resize(offset + read); + memory::copy(result.output.get() + offset, buffer, read); + } + + while(true) { + DWORD read, available, remaining; + if(!PeekNamedPipe(stderrRead, nullptr, sizeof(buffer), &read, &available, &remaining)) break; + if(read == 0) break; + + if(!ReadFile(stderrRead, buffer, sizeof(buffer), &read, nullptr)) break; + if(read == 0) break; + + auto offset = result.error.size(); + result.error.resize(offset + read); + memory::copy(result.error.get() + offset, buffer, read); + } + + return result; +} + +NALL_HEADER_INLINE auto invoke(const string& name, vector argl) -> void { + for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""}; + string arguments = argl.merge(" "); + string directory = Path::program().replace("/", "\\"); + ShellExecute(nullptr, nullptr, utf16_t(name), utf16_t(arguments), utf16_t(directory), SW_SHOWNORMAL); +} + +#endif + +} diff --git a/nall/run.hpp b/nall/run.hpp index 17a2733f3..0dae8713a 100644 --- a/nall/run.hpp +++ b/nall/run.hpp @@ -110,94 +110,18 @@ template inline auto invoke(const string& name, P&&... p) -> void #elif defined(PLATFORM_WINDOWS) +auto execute(const string& name, vector argl) -> execute_result_t; + template inline auto execute(const string& name, P&&... p) -> execute_result_t { vector argl(name, std::forward

(p)...); - for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""}; - string arguments = argl.merge(" "); - - SECURITY_ATTRIBUTES sa; - ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); - sa.nLength = sizeof(SECURITY_ATTRIBUTES); - sa.bInheritHandle = true; - sa.lpSecurityDescriptor = nullptr; - - HANDLE stdoutRead; - HANDLE stdoutWrite; - if(!CreatePipe(&stdoutRead, &stdoutWrite, &sa, 0)) return {}; - if(!SetHandleInformation(stdoutRead, HANDLE_FLAG_INHERIT, 0)) return {}; - - HANDLE stderrRead; - HANDLE stderrWrite; - if(!CreatePipe(&stderrRead, &stderrWrite, &sa, 0)) return {}; - if(!SetHandleInformation(stderrRead, HANDLE_FLAG_INHERIT, 0)) return {}; - - HANDLE stdinRead; - HANDLE stdinWrite; - if(!CreatePipe(&stdinRead, &stdinWrite, &sa, 0)) return {}; - if(!SetHandleInformation(stdinWrite, HANDLE_FLAG_INHERIT, 0)) return {}; - - STARTUPINFO si; - ZeroMemory(&si, sizeof(STARTUPINFO)); - si.cb = sizeof(STARTUPINFO); - si.hStdOutput = stdoutWrite; - si.hStdError = stderrWrite; - si.hStdInput = stdinRead; - si.dwFlags = STARTF_USESTDHANDLES; - - PROCESS_INFORMATION pi; - ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); - - if(!CreateProcess( - nullptr, utf16_t(arguments), - nullptr, nullptr, true, CREATE_NO_WINDOW, - nullptr, nullptr, &si, &pi - )) return {}; - - DWORD exitCode = EXIT_FAILURE; - if(WaitForSingleObject(pi.hProcess, INFINITE)) return {}; - if(!GetExitCodeProcess(pi.hProcess, &exitCode)) return {}; - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - - char buffer[256]; - execute_result_t result; - result.code = exitCode; - - while(true) { - DWORD read, available, remaining; - if(!PeekNamedPipe(stdoutRead, nullptr, sizeof(buffer), &read, &available, &remaining)) break; - if(read == 0) break; - - if(!ReadFile(stdoutRead, buffer, sizeof(buffer), &read, nullptr)) break; - if(read == 0) break; - - auto offset = result.output.size(); - result.output.resize(offset + read); - memory::copy(result.output.get() + offset, buffer, read); - } - - while(true) { - DWORD read, available, remaining; - if(!PeekNamedPipe(stderrRead, nullptr, sizeof(buffer), &read, &available, &remaining)) break; - if(read == 0) break; - - if(!ReadFile(stderrRead, buffer, sizeof(buffer), &read, nullptr)) break; - if(read == 0) break; - - auto offset = result.error.size(); - result.error.resize(offset + read); - memory::copy(result.error.get() + offset, buffer, read); - } - - return result; + return execute(name, std::move(argl)); } +auto invoke(const string& name, vector argl) -> void; + template inline auto invoke(const string& name, P&&... p) -> void { vector argl(std::forward

(p)...); - for(auto& arg : argl) if(arg.find(" ")) arg = {"\"", arg, "\""}; - string arguments = argl.merge(" "); - string directory = Path::program().replace("/", "\\"); - ShellExecute(nullptr, nullptr, utf16_t(name), utf16_t(arguments), utf16_t(directory), SW_SHOWNORMAL); + invoke(name, std::move(argl)); } #else @@ -212,3 +136,7 @@ template inline auto invoke(const string& name, P&&... p) -> void #endif } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/smtp.cpp b/nall/smtp.cpp new file mode 100644 index 000000000..36739c3c8 --- /dev/null +++ b/nall/smtp.cpp @@ -0,0 +1,149 @@ +#include + +#if defined(PLATFORM_WINDOWS) + #include +#endif + +namespace nall { + +NALL_HEADER_INLINE auto SMTP::send() -> bool { + info.message.append("From: =?UTF-8?B?", Encode::Base64(contact(info.from)), "?=\r\n"); + info.message.append("To: =?UTF-8?B?", Encode::Base64(contacts(info.to)), "?=\r\n"); + info.message.append("Cc: =?UTF-8?B?", Encode::Base64(contacts(info.cc)), "?=\r\n"); + info.message.append("Subject: =?UTF-8?B?", Encode::Base64(info.subject), "?=\r\n"); + + string uniqueID = boundary(); + + info.message.append("MIME-Version: 1.0\r\n"); + info.message.append("Content-Type: multipart/mixed; boundary=", uniqueID, "\r\n"); + info.message.append("\r\n"); + + string format = (info.format == Format::Plain ? "text/plain" : "text/html"); + + info.message.append("--", uniqueID, "\r\n"); + info.message.append("Content-Type: ", format, "; charset=UTF-8\r\n"); + info.message.append("Content-Transfer-Encoding: base64\r\n"); + info.message.append("\r\n"); + info.message.append(split(Encode::Base64(info.body)), "\r\n"); + info.message.append("\r\n"); + + for(auto& attachment : info.attachments) { + info.message.append("--", uniqueID, "\r\n"); + info.message.append("Content-Type: application/octet-stream\r\n"); + info.message.append("Content-Transfer-Encoding: base64\r\n"); + info.message.append("Content-Disposition: attachment; size=", attachment.buffer.size(), "; filename*=UTF-8''", filename(attachment.name), "\r\n"); + info.message.append("\r\n"); + info.message.append(split(Encode::Base64(attachment.buffer)), "\r\n"); + info.message.append("\r\n"); + } + + info.message.append("--", uniqueID, "--\r\n"); + + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + addrinfo* serverinfo; + s32 status = getaddrinfo(info.server, string(info.port), &hints, &serverinfo); + if(status != 0) return false; + + s32 sock = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol); + if(sock == -1) return false; + + s32 result = connect(sock, serverinfo->ai_addr, serverinfo->ai_addrlen); + if(result == -1) return false; + + string response; + info.response.append(response = recv(sock)); + if(!response.beginsWith("220 ")) { close(sock); return false; } + + send(sock, {"HELO ", info.server, "\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginsWith("250 ")) { close(sock); return false; } + + send(sock, {"MAIL FROM: <", info.from.mail, ">\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginsWith("250 ")) { close(sock); return false; } + + for(auto& contact : info.to) { + send(sock, {"RCPT TO: <", contact.mail, ">\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginsWith("250 ")) { close(sock); return false; } + } + + for(auto& contact : info.cc) { + send(sock, {"RCPT TO: <", contact.mail, ">\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginsWith("250 ")) { close(sock); return false; } + } + + for(auto& contact : info.bcc) { + send(sock, {"RCPT TO: <", contact.mail, ">\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginsWith("250 ")) { close(sock); return false; } + } + + send(sock, {"DATA\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginsWith("354 ")) { close(sock); return false; } + + send(sock, {info.message, "\r\n", ".\r\n"}); + info.response.append(response = recv(sock)); + if(!response.beginsWith("250 ")) { close(sock); return false; } + + send(sock, {"QUIT\r\n"}); + info.response.append(response = recv(sock)); +//if(!response.beginsWith("221 ")) { close(sock); return false; } + + close(sock); + return true; +} + +NALL_HEADER_INLINE auto SMTP::send(s32 sock, const string& text) -> bool { + const char* data = text.data(); + u32 size = text.size(); + while(size) { + s32 length = ::send(sock, (const char*)data, size, 0); + if(length == -1) return false; + data += length; + size -= length; + } + return true; +} + +NALL_HEADER_INLINE auto SMTP::recv(s32 sock) -> string { + vector buffer; + while(true) { + char c; + if(::recv(sock, &c, sizeof(char), 0) < 1) break; + buffer.append(c); + if(c == '\n') break; + } + buffer.append(0); + return buffer; +} + +#if defined(API_WINDOWS) + +NALL_HEADER_INLINE auto SMTP::close(s32 sock) -> s32 { + return closesocket(sock); +} + +NALL_HEADER_INLINE SMTP::SMTP() { + s32 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) { + WSADATA wsaData; + if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + WSACleanup(); + return; + } + } else { + close(sock); + } +} + +#endif + +} diff --git a/nall/smtp.hpp b/nall/smtp.hpp index 82f2d7d2c..d19b4b1c6 100644 --- a/nall/smtp.hpp +++ b/nall/smtp.hpp @@ -1,17 +1,17 @@ #pragma once -#include #include #include +#include +#include +#include +#include #if !defined(PLATFORM_WINDOWS) #include #include #include #include -#else - #include - #include #endif namespace nall { @@ -102,7 +102,7 @@ inline auto SMTP::attachment(const u8* data, u32 size, string name) -> void { inline auto SMTP::attachment(string filename, string name) -> bool { if(!file::exists(filename)) return false; - if(name == "") name = notdir(filename); + if(name == "") name = Location::file(filename); auto buffer = file::read(filename); info.attachments.append({std::move(buffer), name}); return true; @@ -117,101 +117,6 @@ inline auto SMTP::body(string body, Format format) -> void { info.format = format; } -inline auto SMTP::send() -> bool { - info.message.append("From: =?UTF-8?B?", Base64::encode(contact(info.from)), "?=\r\n"); - info.message.append("To: =?UTF-8?B?", Base64::encode(contacts(info.to)), "?=\r\n"); - info.message.append("Cc: =?UTF-8?B?", Base64::encode(contacts(info.cc)), "?=\r\n"); - info.message.append("Subject: =?UTF-8?B?", Base64::encode(info.subject), "?=\r\n"); - - string uniqueID = boundary(); - - info.message.append("MIME-Version: 1.0\r\n"); - info.message.append("Content-Type: multipart/mixed; boundary=", uniqueID, "\r\n"); - info.message.append("\r\n"); - - string format = (info.format == Format::Plain ? "text/plain" : "text/html"); - - info.message.append("--", uniqueID, "\r\n"); - info.message.append("Content-Type: ", format, "; charset=UTF-8\r\n"); - info.message.append("Content-Transfer-Encoding: base64\r\n"); - info.message.append("\r\n"); - info.message.append(split(Base64::encode(info.body)), "\r\n"); - info.message.append("\r\n"); - - for(auto& attachment : info.attachments) { - info.message.append("--", uniqueID, "\r\n"); - info.message.append("Content-Type: application/octet-stream\r\n"); - info.message.append("Content-Transfer-Encoding: base64\r\n"); - info.message.append("Content-Disposition: attachment; size=", attachment.buffer.size(), "; filename*=UTF-8''", filename(attachment.name), "\r\n"); - info.message.append("\r\n"); - info.message.append(split(Base64::encode(attachment.buffer)), "\r\n"); - info.message.append("\r\n"); - } - - info.message.append("--", uniqueID, "--\r\n"); - - addrinfo hints; - memset(&hints, 0, sizeof(addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_PASSIVE; - - addrinfo* serverinfo; - s32 status = getaddrinfo(info.server, string(info.port), &hints, &serverinfo); - if(status != 0) return false; - - s32 sock = socket(serverinfo->ai_family, serverinfo->ai_socktype, serverinfo->ai_protocol); - if(sock == -1) return false; - - s32 result = connect(sock, serverinfo->ai_addr, serverinfo->ai_addrlen); - if(result == -1) return false; - - string response; - info.response.append(response = recv(sock)); - if(!response.beginswith("220 ")) { close(sock); return false; } - - send(sock, {"HELO ", info.server, "\r\n"}); - info.response.append(response = recv(sock)); - if(!response.beginswith("250 ")) { close(sock); return false; } - - send(sock, {"MAIL FROM: <", info.from.mail, ">\r\n"}); - info.response.append(response = recv(sock)); - if(!response.beginswith("250 ")) { close(sock); return false; } - - for(auto& contact : info.to) { - send(sock, {"RCPT TO: <", contact.mail, ">\r\n"}); - info.response.append(response = recv(sock)); - if(!response.beginswith("250 ")) { close(sock); return false; } - } - - for(auto& contact : info.cc) { - send(sock, {"RCPT TO: <", contact.mail, ">\r\n"}); - info.response.append(response = recv(sock)); - if(!response.beginswith("250 ")) { close(sock); return false; } - } - - for(auto& contact : info.bcc) { - send(sock, {"RCPT TO: <", contact.mail, ">\r\n"}); - info.response.append(response = recv(sock)); - if(!response.beginswith("250 ")) { close(sock); return false; } - } - - send(sock, {"DATA\r\n"}); - info.response.append(response = recv(sock)); - if(!response.beginswith("354 ")) { close(sock); return false; } - - send(sock, {info.message, "\r\n", ".\r\n"}); - info.response.append(response = recv(sock)); - if(!response.beginswith("250 ")) { close(sock); return false; } - - send(sock, {"QUIT\r\n"}); - info.response.append(response = recv(sock)); -//if(!response.beginswith("221 ")) { close(sock); return false; } - - close(sock); - return true; -} - inline auto SMTP::message() -> string { return info.message; } @@ -220,42 +125,18 @@ inline auto SMTP::response() -> string { return info.response; } -inline auto SMTP::send(s32 sock, const string& text) -> bool { - const char* data = text.data(); - u32 size = text.size(); - while(size) { - s32 length = ::send(sock, (const char*)data, size, 0); - if(length == -1) return false; - data += length; - size -= length; - } - return true; -} - -inline auto SMTP::recv(s32 sock) -> string { - vector buffer; - while(true) { - char c; - if(::recv(sock, &c, sizeof(char), 0) < 1) break; - buffer.append(c); - if(c == '\n') break; - } - buffer.append(0); - return buffer; -} - inline auto SMTP::boundary() -> string { - random_lfsr random; + PRNG::LFSR random; random.seed(time(0)); string boundary; - for(u32 n = 0; n < 16; n++) boundary.append(hex<2>(random())); + for(u32 n = 0; n < 16; n++) boundary.append(hex(random.random(), 2L)); return boundary; } inline auto SMTP::filename(const string& filename) -> string { string result; for(auto& n : filename) { - if(n <= 32 || n >= 127) result.append("%", hex<2>(n)); + if(n <= 32 || n >= 127) result.append("%", hex(n, 2L)); else result.append(n); } return result; @@ -292,23 +173,8 @@ inline auto SMTP::split(const string& text) -> string { return result; } -#if defined(API_WINDOWS) -inline auto SMTP::close(s32 sock) -> s32 { - return closesocket(sock); } -inline SMTP::SMTP() { - s32 sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if(sock == INVALID_SOCKET && WSAGetLastError() == WSANOTINITIALISED) { - WSADATA wsaData; - if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { - WSACleanup(); - return; - } - } else { - close(sock); - } -} +#if defined(NALL_HEADER_ONLY) + #include #endif - -} diff --git a/nall/thread.cpp b/nall/thread.cpp new file mode 100644 index 000000000..73e3cc311 --- /dev/null +++ b/nall/thread.cpp @@ -0,0 +1,53 @@ +#include + +namespace nall { + +#if defined(API_WINDOWS) + +NALL_HEADER_INLINE auto WINAPI _threadCallback(void* parameter) -> DWORD { + auto context = (thread::context*)parameter; + context->callback(context->parameter); + delete context; + return 0; +} + +NALL_HEADER_INLINE auto thread::close() -> void { + if(handle) { + CloseHandle(handle); + handle = 0; + } +} + +NALL_HEADER_INLINE auto thread::join() -> void { + if(handle) { + //wait until the thread has finished executing ... + WaitForSingleObject(handle, INFINITE); + CloseHandle(handle); + handle = 0; + } +} + +NALL_HEADER_INLINE auto thread::create(const function& callback, uintptr parameter, u32 stacksize) -> thread { + thread instance; + + auto context = new thread::context; + context->callback = callback; + context->parameter = parameter; + + instance.handle = CreateThread(nullptr, stacksize, _threadCallback, (void*)context, 0, nullptr); + return instance; +} + +NALL_HEADER_INLINE auto thread::detach() -> void { + //Windows threads do not use this concept: + //~thread() frees resources via CloseHandle() + //thread continues to run even after handle is closed +} + +NALL_HEADER_INLINE auto thread::exit() -> void { + ExitThread(0); +} + +#endif + +} diff --git a/nall/thread.hpp b/nall/thread.hpp index f7204e825..a76896f9a 100644 --- a/nall/thread.hpp +++ b/nall/thread.hpp @@ -128,50 +128,10 @@ private: HANDLE handle = 0; }; -inline auto WINAPI _threadCallback(void* parameter) -> DWORD { - auto context = (thread::context*)parameter; - context->callback(context->parameter); - delete context; - return 0; -} - -inline auto thread::close() -> void { - if(handle) { - CloseHandle(handle); - handle = 0; - } -} - -inline auto thread::join() -> void { - if(handle) { - //wait until the thread has finished executing ... - WaitForSingleObject(handle, INFINITE); - CloseHandle(handle); - handle = 0; - } -} - -inline auto thread::create(const function& callback, uintptr parameter, u32 stacksize) -> thread { - thread instance; - - auto context = new thread::context; - context->callback = callback; - context->parameter = parameter; - - instance.handle = CreateThread(nullptr, stacksize, _threadCallback, (void*)context, 0, nullptr); - return instance; -} - -inline auto thread::detach() -> void { - //Windows threads do not use this concept: - //~thread() frees resources via CloseHandle() - //thread continues to run even after handle is closed -} - -inline auto thread::exit() -> void { - ExitThread(0); -} - } #endif + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/traits.hpp b/nall/traits.hpp index 0e810e66b..35333a6ca 100644 --- a/nall/traits.hpp +++ b/nall/traits.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include diff --git a/nall/windows/detour.cpp b/nall/windows/detour.cpp new file mode 100644 index 000000000..b5a261271 --- /dev/null +++ b/nall/windows/detour.cpp @@ -0,0 +1,69 @@ +#include + +namespace nall { + +NALL_HEADER_INLINE auto detour::insert(const string& moduleName, const string& functionName, void*& source, void* target) -> bool { + #if defined(ARCHITECTURE_X86) + HMODULE module = GetModuleHandleW(utf16_t(moduleName)); + if(!module) return false; + + u8* sourceData = (u8*)GetProcAddress(module, functionName); + if(!sourceData) return false; + + u32 sourceLength = detour::length(sourceData); + if(sourceLength < 5) { + //unable to clone enough bytes to insert hook + #if 1 + string output = {"detour::insert(", moduleName, "::", functionName, ") failed: "}; + for(u32 n = 0; n < 16; n++) output.append(hex(sourceData[n], 2L), " "); + output.trimRight(" ", 1L); + MessageBoxA(0, output, "nall::detour", MB_OK); + #endif + return false; + } + + auto mirrorData = new u8[512](); + detour::mirror(mirrorData, sourceData); + + DWORD privileges; + VirtualProtect((void*)mirrorData, 512, PAGE_EXECUTE_READWRITE, &privileges); + VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges); + u64 address = (u64)target - ((u64)sourceData + 5); + sourceData[0] = 0xe9; //jmp target + sourceData[1] = address >> 0; + sourceData[2] = address >> 8; + sourceData[3] = address >> 16; + sourceData[4] = address >> 24; + VirtualProtect((void*)sourceData, 256, privileges, &privileges); + + source = (void*)mirrorData; + return true; + #else + return false; + #endif +} + +NALL_HEADER_INLINE auto detour::remove(const string& moduleName, const string& functionName, void*& source) -> bool { + HMODULE module = GetModuleHandleW(utf16_t(moduleName)); + if(!module) return false; + + auto sourceData = (u8*)GetProcAddress(module, functionName); + if(!sourceData) return false; + + auto mirrorData = (u8*)source; + if(mirrorData == sourceData) return false; //hook was never installed + + u32 length = detour::length(256 + mirrorData); + if(length < 5) return false; + + DWORD privileges; + VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges); + for(u32 n = 0; n < length; n++) sourceData[n] = mirrorData[256 + n]; + VirtualProtect((void*)sourceData, 256, privileges, &privileges); + + source = (void*)sourceData; + delete[] mirrorData; + return true; +} + +} diff --git a/nall/windows/detour.hpp b/nall/windows/detour.hpp index b80b8d9dc..ebc3113de 100644 --- a/nall/windows/detour.hpp +++ b/nall/windows/detour.hpp @@ -1,10 +1,9 @@ #pragma once -#include #include #include #include -#include +#include namespace nall { @@ -25,107 +24,46 @@ protected: u32 mode; u16 modify; }; - static opcode opcodes[]; + static constexpr opcode opcodes[] = { + //TODO: + //* fs:, gs: should force another opcode copy + //* conditional branches within +5-byte range should fail + + {0x50, 1}, //push eax + {0x51, 1}, //push ecx + {0x52, 1}, //push edx + {0x53, 1}, //push ebx + {0x54, 1}, //push esp + {0x55, 1}, //push ebp + {0x56, 1}, //push esi + {0x57, 1}, //push edi + {0x58, 1}, //pop eax + {0x59, 1}, //pop ecx + {0x5a, 1}, //pop edx + {0x5b, 1}, //pop ebx + {0x5c, 1}, //pop esp + {0x5d, 1}, //pop ebp + {0x5e, 1}, //pop esi + {0x5f, 1}, //pop edi + {0x64, 1}, //fs: + {0x65, 1}, //gs: + {0x68, 5}, //push dword + {0x6a, 2}, //push byte + {0x74, 2, RelNear, 0x0f84}, //je near -> je far + {0x75, 2, RelNear, 0x0f85}, //jne near -> jne far + {0x89, 2}, //mov reg,reg + {0x8b, 2}, //mov reg,reg + {0x90, 1}, //nop + {0xa1, 5}, //mov eax,[dword] + {0xeb, 2, RelNear, 0xe9}, //jmp near -> jmp far + }; }; -//TODO: -//* fs:, gs: should force another opcode copy -//* conditional branches within +5-byte range should fail -detour::opcode detour::opcodes[] = { - {0x50, 1}, //push eax - {0x51, 1}, //push ecx - {0x52, 1}, //push edx - {0x53, 1}, //push ebx - {0x54, 1}, //push esp - {0x55, 1}, //push ebp - {0x56, 1}, //push esi - {0x57, 1}, //push edi - {0x58, 1}, //pop eax - {0x59, 1}, //pop ecx - {0x5a, 1}, //pop edx - {0x5b, 1}, //pop ebx - {0x5c, 1}, //pop esp - {0x5d, 1}, //pop ebp - {0x5e, 1}, //pop esi - {0x5f, 1}, //pop edi - {0x64, 1}, //fs: - {0x65, 1}, //gs: - {0x68, 5}, //push dword - {0x6a, 2}, //push byte - {0x74, 2, RelNear, 0x0f84}, //je near -> je far - {0x75, 2, RelNear, 0x0f85}, //jne near -> jne far - {0x89, 2}, //mov reg,reg - {0x8b, 2}, //mov reg,reg - {0x90, 1}, //nop - {0xa1, 5}, //mov eax,[dword] - {0xeb, 2, RelNear, 0xe9}, //jmp near -> jmp far -}; - -inline auto detour::insert(const string& moduleName, const string& functionName, void*& source, void* target) -> bool { - HMODULE module = GetModuleHandleW(utf16_t(moduleName)); - if(!module) return false; - - u8* sourceData = (u8*)GetProcAddress(module, functionName); - if(!sourceData) return false; - - u32 sourceLength = detour::length(sourceData); - if(sourceLength < 5) { - //unable to clone enough bytes to insert hook - #if 1 - string output = {"detour::insert(", moduleName, "::", functionName, ") failed: "}; - for(u32 n = 0; n < 16; n++) output.append(hex<2>(sourceData[n]), " "); - output.trimRight(" ", 1L); - MessageBoxA(0, output, "nall::detour", MB_OK); - #endif - return false; - } - - auto mirrorData = new u8[512](); - detour::mirror(mirrorData, sourceData); - - DWORD privileges; - VirtualProtect((void*)mirrorData, 512, PAGE_EXECUTE_READWRITE, &privileges); - VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges); - u64 address = (u64)target - ((u64)sourceData + 5); - sourceData[0] = 0xe9; //jmp target - sourceData[1] = address >> 0; - sourceData[2] = address >> 8; - sourceData[3] = address >> 16; - sourceData[4] = address >> 24; - VirtualProtect((void*)sourceData, 256, privileges, &privileges); - - source = (void*)mirrorData; - return true; -} - -inline auto detour::remove(const string& moduleName, const string& functionName, void*& source) -> bool { - HMODULE module = GetModuleHandleW(utf16_t(moduleName)); - if(!module) return false; - - auto sourceData = (u8*)GetProcAddress(module, functionName); - if(!sourceData) return false; - - auto mirrorData = (u8*)source; - if(mirrorData == sourceData) return false; //hook was never installed - - u32 length = detour::length(256 + mirrorData); - if(length < 5) return false; - - DWORD privileges; - VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges); - for(u32 n = 0; n < length; n++) sourceData[n] = mirrorData[256 + n]; - VirtualProtect((void*)sourceData, 256, privileges, &privileges); - - source = (void*)sourceData; - delete[] mirrorData; - return true; -} - inline auto detour::length(const u8* function) -> u32 { u32 length = 0; while(length < 5) { - detour::opcode *opcode = 0; - foreach(op, detour::opcodes) { + const detour::opcode *opcode = 0; + for(auto& op : detour::opcodes) { if(function[length] == op.prefix) { opcode = &op; break; @@ -143,8 +81,8 @@ inline auto detour::mirror(u8* target, const u8* source) -> u32 { u32 size = detour::length(source); while(size) { - detour::opcode* opcode = nullptr; - foreach(op, detour::opcodes) { + const detour::opcode* opcode = nullptr; + for(auto& op : detour::opcodes) { if(*source == op.prefix) { opcode = &op; break; @@ -183,7 +121,11 @@ inline auto detour::mirror(u8* target, const u8* source) -> u32 { return source - entryPoint; } -#undef Implied +#undef Copy #undef RelNear } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/windows/guard.hpp b/nall/windows/guard.hpp deleted file mode 100644 index 147c56762..000000000 --- a/nall/windows/guard.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef NALL_WINDOWS_GUARD_HPP -#define NALL_WINDOWS_GUARD_HPP - -#define boolean WindowsBoolean -#define interface WindowsInterface - -#undef UNICODE -#undef WINVER -#undef WIN32_LEAN_AND_LEAN -#undef _WIN32_WINNT -#undef _WIN32_IE -#undef NOMINMAX -#undef PATH_MAX - -#define UNICODE -#define WINVER 0x0601 -#define WIN32_LEAN_AND_MEAN -#define _WIN32_WINNT WINVER -#define _WIN32_IE WINVER -#define NOMINMAX -#define PATH_MAX 260 - -#else -#undef NALL_WINDOWS_GUARD_HPP - -#undef boolean -#undef interface - -#undef far -#undef near - -#endif diff --git a/nall/windows/guid.cpp b/nall/windows/guid.cpp new file mode 100644 index 000000000..7f0f59390 --- /dev/null +++ b/nall/windows/guid.cpp @@ -0,0 +1,17 @@ +#include + +#include + +namespace nall { + +NALL_HEADER_INLINE auto guid() -> string { + GUID guidInstance; + CoCreateGuid(&guidInstance); + + wchar_t guidString[39]; + StringFromGUID2(guidInstance, guidString, 39); + + return (char*)utf8_t(guidString); +} + +} diff --git a/nall/windows/guid.hpp b/nall/windows/guid.hpp index 314683d49..63f9de6d5 100644 --- a/nall/windows/guid.hpp +++ b/nall/windows/guid.hpp @@ -4,14 +4,10 @@ namespace nall { -inline auto guid() -> string { - GUID guidInstance; - CoCreateGuid(&guidInstance); - - wchar_t guidString[39]; - StringFromGUID2(guidInstance, guidString, 39); - - return (char*)utf8_t(guidString); -} +auto guid() -> string; } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/windows/launcher.cpp b/nall/windows/launcher.cpp new file mode 100644 index 000000000..a25db095a --- /dev/null +++ b/nall/windows/launcher.cpp @@ -0,0 +1,93 @@ +#include + +namespace nall { + +NALL_HEADER_INLINE auto launch(const char* applicationName, const char* libraryName, u32 entryPoint) -> bool { + #if defined(ARCHITECTURE_X86) + //if a launcher does not send at least one message, a wait cursor will appear + PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0); + MSG msg; + GetMessage(&msg, 0, 0, 0); + + STARTUPINFOW si; + PROCESS_INFORMATION pi; + + memset(&si, 0, sizeof(STARTUPINFOW)); + BOOL result = CreateProcessW( + utf16_t(applicationName), GetCommandLineW(), NULL, NULL, TRUE, + DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, //do not break if application creates its own processes + NULL, NULL, &si, &pi + ); + if(result == false) return false; + + u8 entryData[1024], entryHook[1024] = { + 0x68, 0x00, 0x00, 0x00, 0x00, //push libraryName + 0xb8, 0x00, 0x00, 0x00, 0x00, //mov eax,LoadLibraryW + 0xff, 0xd0, //call eax + 0xcd, 0x03, //int 3 + }; + + entryHook[1] = (u8)((entryPoint + 14) >> 0); + entryHook[2] = (u8)((entryPoint + 14) >> 8); + entryHook[3] = (u8)((entryPoint + 14) >> 16); + entryHook[4] = (u8)((entryPoint + 14) >> 24); + + auto pLoadLibraryW = (u32)GetProcAddress(GetModuleHandleW(L"kernel32"), "LoadLibraryW"); + entryHook[6] = pLoadLibraryW >> 0; + entryHook[7] = pLoadLibraryW >> 8; + entryHook[8] = pLoadLibraryW >> 16; + entryHook[9] = pLoadLibraryW >> 24; + + utf16_t buffer = utf16_t(libraryName); + memcpy(entryHook + 14, buffer, 2 * wcslen(buffer) + 2); + + while(true) { + DEBUG_EVENT event; + WaitForDebugEvent(&event, INFINITE); + + if(event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; + + if(event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { + if(event.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) { + if(event.u.Exception.ExceptionRecord.ExceptionAddress == (void*)(entryPoint + 14 - 1)) { + HANDLE hProcess = OpenProcess(0, FALSE, event.dwProcessId); + HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, event.dwThreadId); + + CONTEXT context; + context.ContextFlags = CONTEXT_FULL; + GetThreadContext(hThread, &context); + + WriteProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryData, sizeof entryData, NULL); + context.Eip = entryPoint; + SetThreadContext(hThread, &context); + + CloseHandle(hThread); + CloseHandle(hProcess); + } + + ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE); + continue; + } + + ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); + continue; + } + + if(event.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) { + ReadProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryData, sizeof entryData, NULL); + WriteProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryHook, sizeof entryHook, NULL); + + ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE); + continue; + } + + ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE); + } + + return true; + #else + return false; + #endif +} + +} diff --git a/nall/windows/launcher.hpp b/nall/windows/launcher.hpp index 0d09faaf7..1415b2869 100644 --- a/nall/windows/launcher.hpp +++ b/nall/windows/launcher.hpp @@ -1,91 +1,15 @@ #pragma once +#include + namespace nall { //launch a new process and inject specified DLL into it -inline auto launch(const char* applicationName, const char* libraryName, u32 entryPoint) -> bool { - //if a launcher does not send at least one message, a wait cursor will appear - PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0); - MSG msg; - GetMessage(&msg, 0, 0, 0); - - STARTUPINFOW si; - PROCESS_INFORMATION pi; - - memset(&si, 0, sizeof(STARTUPINFOW)); - BOOL result = CreateProcessW( - utf16_t(applicationName), GetCommandLineW(), NULL, NULL, TRUE, - DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, //do not break if application creates its own processes - NULL, NULL, &si, &pi - ); - if(result == false) return false; - - u8 entryData[1024], entryHook[1024] = { - 0x68, 0x00, 0x00, 0x00, 0x00, //push libraryName - 0xb8, 0x00, 0x00, 0x00, 0x00, //mov eax,LoadLibraryW - 0xff, 0xd0, //call eax - 0xcd, 0x03, //int 3 - }; - - entryHook[1] = (u8)((entryPoint + 14) >> 0); - entryHook[2] = (u8)((entryPoint + 14) >> 8); - entryHook[3] = (u8)((entryPoint + 14) >> 16); - entryHook[4] = (u8)((entryPoint + 14) >> 24); - - auto pLoadLibraryW = (u32)GetProcAddress(GetModuleHandleW(L"kernel32"), "LoadLibraryW"); - entryHook[6] = pLoadLibraryW >> 0; - entryHook[7] = pLoadLibraryW >> 8; - entryHook[8] = pLoadLibraryW >> 16; - entryHook[9] = pLoadLibraryW >> 24; - - utf16_t buffer = utf16_t(libraryName); - memcpy(entryHook + 14, buffer, 2 * wcslen(buffer) + 2); - - while(true) { - DEBUG_EVENT event; - WaitForDebugEvent(&event, INFINITE); - - if(event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; - - if(event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) { - if(event.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) { - if(event.u.Exception.ExceptionRecord.ExceptionAddress == (void*)(entryPoint + 14 - 1)) { - HANDLE hProcess = OpenProcess(0, FALSE, event.dwProcessId); - HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, event.dwThreadId); - - CONTEXT context; - context.ContextFlags = CONTEXT_FULL; - GetThreadContext(hThread, &context); - - WriteProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryData, sizeof entryData, NULL); - context.Eip = entryPoint; - SetThreadContext(hThread, &context); - - CloseHandle(hThread); - CloseHandle(hProcess); - } - - ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE); - continue; - } - - ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); - continue; - } - - if(event.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) { - ReadProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryData, sizeof entryData, NULL); - WriteProcessMemory(pi.hProcess, (void*)entryPoint, (void*)&entryHook, sizeof entryHook, NULL); - - ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE); - continue; - } - - ContinueDebugEvent(event.dwProcessId, event.dwThreadId, DBG_CONTINUE); - } - - return true; -} +auto launch(const char* applicationName, const char* libraryName, u32 entryPoint) -> bool; } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/windows/registry.cpp b/nall/windows/registry.cpp new file mode 100644 index 000000000..7703ab202 --- /dev/null +++ b/nall/windows/registry.cpp @@ -0,0 +1,113 @@ +#include + +#include + +#ifndef KEY_WOW64_64KEY + #define KEY_WOW64_64KEY 0x0100 +#endif +#ifndef KEY_WOW64_32KEY + #define KEY_WOW64_32KEY 0x0200 +#endif + +#ifndef NWR_FLAGS + #define NWR_FLAGS KEY_WOW64_64KEY +#endif + +#ifndef NWR_SIZE + #define NWR_SIZE 4096 +#endif + +namespace nall { + +NALL_HEADER_INLINE auto registry::root(const string& name) { + if(name == "HKCR") return HKEY_CLASSES_ROOT; + if(name == "HKCC") return HKEY_CURRENT_CONFIG; + if(name == "HKCU") return HKEY_CURRENT_USER; + if(name == "HKLM") return HKEY_LOCAL_MACHINE; + if(name == "HKU" ) return HKEY_USERS; + return (HKEY)nullptr; +} + +NALL_HEADER_INLINE auto registry::exists(const string& name) -> bool { + auto part = name.split("\\"); + HKEY handle, rootKey = root(part.takeLeft()); + string node = part.takeRight(); + string path = part.merge("\\"); + if(RegOpenKeyExW(rootKey, utf16_t(path), 0, NWR_FLAGS | KEY_READ, &handle) == ERROR_SUCCESS) { + wchar_t data[NWR_SIZE] = L""; + DWORD size = NWR_SIZE * sizeof(wchar_t); + LONG result = RegQueryValueExW(handle, utf16_t(node), nullptr, nullptr, (LPBYTE)&data, (LPDWORD)&size); + RegCloseKey(handle); + if(result == ERROR_SUCCESS) return true; + } + return false; +} + +NALL_HEADER_INLINE auto registry::read(const string& name) -> string { + auto part = name.split("\\"); + HKEY handle, rootKey = root(part.takeLeft()); + string node = part.takeRight(); + string path = part.merge("\\"); + if(RegOpenKeyExW(rootKey, utf16_t(path), 0, NWR_FLAGS | KEY_READ, &handle) == ERROR_SUCCESS) { + wchar_t data[NWR_SIZE] = L""; + DWORD size = NWR_SIZE * sizeof(wchar_t); + LONG result = RegQueryValueExW(handle, utf16_t(node), nullptr, nullptr, (LPBYTE)&data, (LPDWORD)&size); + RegCloseKey(handle); + if(result == ERROR_SUCCESS) return (const char*)utf8_t(data); + } + return ""; +} + +NALL_HEADER_INLINE auto registry::write(const string& name, const string& data) -> void { + auto part = name.split("\\"); + HKEY handle, rootKey = root(part.takeLeft()); + string node = part.takeRight(), path; + DWORD disposition; + for(u32 n = 0; n < part.size(); n++) { + path.append(part[n]); + if(RegCreateKeyExW(rootKey, utf16_t(path), 0, nullptr, 0, NWR_FLAGS | KEY_ALL_ACCESS, nullptr, &handle, &disposition) == ERROR_SUCCESS) { + if(n == part.size() - 1) { + RegSetValueExW(handle, utf16_t(node), 0, REG_SZ, (BYTE*)(wchar_t*)utf16_t(data), (data.length() + 1) * sizeof(wchar_t)); + } + RegCloseKey(handle); + } + path.append("\\"); + } +} + +NALL_HEADER_INLINE auto registry::remove(const string& name) -> bool { + auto part = name.split("\\"); + HKEY rootKey = root(part.takeLeft()); + string node = part.takeRight(); + string path = part.merge("\\"); + if(!node) return SHDeleteKeyW(rootKey, utf16_t(path)) == ERROR_SUCCESS; + return SHDeleteValueW(rootKey, utf16_t(path), utf16_t(node)) == ERROR_SUCCESS; +} + +NALL_HEADER_INLINE auto registry::contents(const string& name) -> vector { + vector result; + auto part = name.split("\\"); + HKEY handle, rootKey = root(part.takeLeft()); + part.removeRight(); + string path = part.merge("\\"); + if(RegOpenKeyExW(rootKey, utf16_t(path), 0, NWR_FLAGS | KEY_READ, &handle) == ERROR_SUCCESS) { + DWORD folders, nodes; + RegQueryInfoKey(handle, nullptr, nullptr, nullptr, &folders, nullptr, nullptr, &nodes, nullptr, nullptr, nullptr, nullptr); + for(u32 n = 0; n < folders; n++) { + wchar_t name[NWR_SIZE] = L""; + DWORD size = NWR_SIZE * sizeof(wchar_t); + RegEnumKeyEx(handle, n, (wchar_t*)&name, &size, nullptr, nullptr, nullptr, nullptr); + result.append(string{(const char*)utf8_t(name), "\\"}); + } + for(u32 n = 0; n < nodes; n++) { + wchar_t name[NWR_SIZE] = L""; + DWORD size = NWR_SIZE * sizeof(wchar_t); + RegEnumValueW(handle, n, (wchar_t*)&name, &size, nullptr, nullptr, nullptr, nullptr); + result.append((const char*)utf8_t(name)); + } + RegCloseKey(handle); + } + return result; +} + +} diff --git a/nall/windows/registry.hpp b/nall/windows/registry.hpp index e63e037db..9c254c0d8 100644 --- a/nall/windows/registry.hpp +++ b/nall/windows/registry.hpp @@ -3,117 +3,25 @@ #include #include -#include -#undef interface -#ifndef KEY_WOW64_64KEY - #define KEY_WOW64_64KEY 0x0100 -#endif -#ifndef KEY_WOW64_32KEY - #define KEY_WOW64_32KEY 0x0200 -#endif - -#ifndef NWR_FLAGS - #define NWR_FLAGS KEY_WOW64_64KEY -#endif - -#ifndef NWR_SIZE - #define NWR_SIZE 4096 -#endif - namespace nall { struct registry { - static auto exists(const string& name) -> bool { - auto part = name.split("\\"); - HKEY handle, rootKey = root(part.takeLeft()); - string node = part.takeRight(); - string path = part.merge("\\"); - if(RegOpenKeyExW(rootKey, utf16_t(path), 0, NWR_FLAGS | KEY_READ, &handle) == ERROR_SUCCESS) { - wchar_t data[NWR_SIZE] = L""; - DWORD size = NWR_SIZE * sizeof(wchar_t); - LONG result = RegQueryValueExW(handle, utf16_t(node), nullptr, nullptr, (LPBYTE)&data, (LPDWORD)&size); - RegCloseKey(handle); - if(result == ERROR_SUCCESS) return true; - } - return false; - } + static auto exists(const string& name) -> bool; - static auto read(const string& name) -> string { - auto part = name.split("\\"); - HKEY handle, rootKey = root(part.takeLeft()); - string node = part.takeRight(); - string path = part.merge("\\"); - if(RegOpenKeyExW(rootKey, utf16_t(path), 0, NWR_FLAGS | KEY_READ, &handle) == ERROR_SUCCESS) { - wchar_t data[NWR_SIZE] = L""; - DWORD size = NWR_SIZE * sizeof(wchar_t); - LONG result = RegQueryValueExW(handle, utf16_t(node), nullptr, nullptr, (LPBYTE)&data, (LPDWORD)&size); - RegCloseKey(handle); - if(result == ERROR_SUCCESS) return (const char*)utf8_t(data); - } - return ""; - } + static auto read(const string& name) -> string; - static auto write(const string& name, const string& data = "") -> void { - auto part = name.split("\\"); - HKEY handle, rootKey = root(part.takeLeft()); - string node = part.takeRight(), path; - DWORD disposition; - for(u32 n = 0; n < part.size(); n++) { - path.append(part[n]); - if(RegCreateKeyExW(rootKey, utf16_t(path), 0, nullptr, 0, NWR_FLAGS | KEY_ALL_ACCESS, nullptr, &handle, &disposition) == ERROR_SUCCESS) { - if(n == part.size() - 1) { - RegSetValueExW(handle, utf16_t(node), 0, REG_SZ, (BYTE*)(wchar_t*)utf16_t(data), (data.length() + 1) * sizeof(wchar_t)); - } - RegCloseKey(handle); - } - path.append("\\"); - } - } + static auto write(const string& name, const string& data = "") -> void; - static auto remove(const string& name) -> bool { - auto part = name.split("\\"); - HKEY rootKey = root(part.takeLeft()); - string node = part.takeRight(); - string path = part.merge("\\"); - if(!node) return SHDeleteKeyW(rootKey, utf16_t(path)) == ERROR_SUCCESS; - return SHDeleteValueW(rootKey, utf16_t(path), utf16_t(node)) == ERROR_SUCCESS; - } + static auto remove(const string& name) -> bool; - static auto contents(const string& name) -> vector { - vector result; - auto part = name.split("\\"); - HKEY handle, rootKey = root(part.takeLeft()); - part.removeRight(); - string path = part.merge("\\"); - if(RegOpenKeyExW(rootKey, utf16_t(path), 0, NWR_FLAGS | KEY_READ, &handle) == ERROR_SUCCESS) { - DWORD folders, nodes; - RegQueryInfoKey(handle, nullptr, nullptr, nullptr, &folders, nullptr, nullptr, &nodes, nullptr, nullptr, nullptr, nullptr); - for(u32 n = 0; n < folders; n++) { - wchar_t name[NWR_SIZE] = L""; - DWORD size = NWR_SIZE * sizeof(wchar_t); - RegEnumKeyEx(handle, n, (wchar_t*)&name, &size, nullptr, nullptr, nullptr, nullptr); - result.append(string{(const char*)utf8_t(name), "\\"}); - } - for(u32 n = 0; n < nodes; n++) { - wchar_t name[NWR_SIZE] = L""; - DWORD size = NWR_SIZE * sizeof(wchar_t); - RegEnumValueW(handle, n, (wchar_t*)&name, &size, nullptr, nullptr, nullptr, nullptr); - result.append((const char*)utf8_t(name)); - } - RegCloseKey(handle); - } - return result; - } + static auto contents(const string& name) -> vector; private: - static auto root(const string& name) -> HKEY { - if(name == "HKCR") return HKEY_CLASSES_ROOT; - if(name == "HKCC") return HKEY_CURRENT_CONFIG; - if(name == "HKCU") return HKEY_CURRENT_USER; - if(name == "HKLM") return HKEY_LOCAL_MACHINE; - if(name == "HKU" ) return HKEY_USERS; - return nullptr; - } + static auto root(const string& name); }; } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/windows/utf8.cpp b/nall/windows/utf8.cpp new file mode 100644 index 000000000..bf6c13fc3 --- /dev/null +++ b/nall/windows/utf8.cpp @@ -0,0 +1,37 @@ +#include + +#include + +namespace nall { + +NALL_HEADER_INLINE auto utf16_t::operator=(const char* s) -> utf16_t& { + reset(); + if(!s) s = ""; + length = MultiByteToWideChar(CP_UTF8, 0, s, -1, nullptr, 0); + buffer = new wchar_t[length + 1]; + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + buffer[length] = 0; + return *this; +} + +NALL_HEADER_INLINE auto utf8_t::operator=(const wchar_t* s) -> utf8_t& { + reset(); + if(!s) s = L""; + length = WideCharToMultiByte(CP_UTF8, 0, s, -1, nullptr, 0, nullptr, nullptr); + buffer = new char[length + 1]; + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, nullptr, nullptr); + buffer[length] = 0; + return *this; +} + +NALL_HEADER_INLINE auto utf8_arguments(int& argc, char**& argv) -> void { + wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + argv = new char*[argc + 1](); + for(u32 i = 0; i < argc; i++) { + utf8_t arg(wargv[i]); + argv[i] = new char[arg.size() + 1]; + strcpy(argv[i], arg); + } +} + +} diff --git a/nall/windows/utf8.hpp b/nall/windows/utf8.hpp index b13d260ad..d97f20f45 100644 --- a/nall/windows/utf8.hpp +++ b/nall/windows/utf8.hpp @@ -1,5 +1,7 @@ #pragma once +#include + namespace nall { //UTF-8 to UTF-16 struct utf16_t { @@ -9,15 +11,7 @@ namespace nall { utf16_t(const utf16_t&) = delete; auto operator=(const utf16_t&) -> utf16_t& = delete; - auto operator=(const char* s) -> utf16_t& { - reset(); - if(!s) s = ""; - length = MultiByteToWideChar(CP_UTF8, 0, s, -1, nullptr, 0); - buffer = new wchar_t[length + 1]; - MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); - buffer[length] = 0; - return *this; - } + auto operator=(const char* s) -> utf16_t&; operator wchar_t*() { return buffer; } operator const wchar_t*() const { return buffer; } @@ -45,15 +39,7 @@ namespace nall { utf8_t(const utf8_t&) = delete; auto operator=(const utf8_t&) -> utf8_t& = delete; - auto operator=(const wchar_t* s) -> utf8_t& { - reset(); - if(!s) s = L""; - length = WideCharToMultiByte(CP_UTF8, 0, s, -1, nullptr, 0, nullptr, nullptr); - buffer = new char[length + 1]; - WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, nullptr, nullptr); - buffer[length] = 0; - return *this; - } + auto operator=(const wchar_t* s) -> utf8_t&; auto reset() -> void { delete[] buffer; @@ -73,12 +59,10 @@ namespace nall { u32 length = 0; }; - inline auto utf8_arguments(int& argc, char**& argv) -> void { - wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc); - argv = new char*[argc + 1](); - for(u32 i = 0; i < argc; i++) { - argv[i] = new char[PATH_MAX]; - strcpy(argv[i], nall::utf8_t(wargv[i])); - } - } + auto utf8_arguments(int& argc, char**& argv) -> void; + } + +#if defined(NALL_HEADER_ONLY) + #include +#endif diff --git a/nall/windows/windows.hpp b/nall/windows/windows.hpp new file mode 100644 index 000000000..e9118bf13 --- /dev/null +++ b/nall/windows/windows.hpp @@ -0,0 +1,17 @@ +#pragma once + +#undef NOMINMAX +#define NOMINMAX + +#undef UNICODE +#define UNICODE + +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN + +//target Windows 7 +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0601 +#include + +#include diff --git a/ruby/audio/xaudio2.cpp b/ruby/audio/xaudio2.cpp index 1fe5e2ea1..12906e12a 100644 --- a/ruby/audio/xaudio2.cpp +++ b/ruby/audio/xaudio2.cpp @@ -1,5 +1,4 @@ #include "xaudio2.hpp" -#undef interface struct AudioXAudio2 : AudioDriver, public IXAudio2VoiceCallback { enum : u32 { Buffers = 32 }; @@ -95,13 +94,13 @@ private: vector devices; auto construct() -> void { - XAudio2Create(&self.interface, 0 , XAUDIO2_DEFAULT_PROCESSOR); + XAudio2Create(&self.xa2Interface, 0 , XAUDIO2_DEFAULT_PROCESSOR); u32 deviceCount = 0; - self.interface->GetDeviceCount(&deviceCount); + self.xa2Interface->GetDeviceCount(&deviceCount); for(u32 deviceIndex : range(deviceCount)) { XAUDIO2_DEVICE_DETAILS deviceDetails{}; - self.interface->GetDeviceDetails(deviceIndex, &deviceDetails); + self.xa2Interface->GetDeviceDetails(deviceIndex, &deviceDetails); auto format = deviceDetails.OutputFormat.Format.wFormatTag; auto bits = deviceDetails.OutputFormat.Format.wBitsPerSample; @@ -129,15 +128,15 @@ private: auto destruct() -> void { terminate(); - if(self.interface) { - self.interface->Release(); - self.interface = nullptr; + if(self.xa2Interface) { + self.xa2Interface->Release(); + self.xa2Interface = nullptr; } } auto initialize() -> bool { terminate(); - if(!self.interface) return false; + if(!self.xa2Interface) return false; self.period = self.frequency * self.latency / Buffers / 1000.0 + 0.5; for(u32 n : range(Buffers)) buffers[n].resize(self.period); @@ -147,7 +146,7 @@ private: if(!hasDevices().find(self.device)) self.device = hasDevices().first(); u32 deviceID = devices[hasDevices().find(self.device)()].id; - if(FAILED(self.interface->CreateMasteringVoice(&self.masterVoice, self.channels, self.frequency, 0, deviceID, nullptr))) return terminate(), false; + if(FAILED(self.xa2Interface->CreateMasteringVoice(&self.masterVoice, self.channels, self.frequency, 0, deviceID, nullptr))) return terminate(), false; WAVEFORMATEX waveFormat{}; waveFormat.wFormatTag = WAVE_FORMAT_PCM; @@ -158,7 +157,7 @@ private: waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; waveFormat.cbSize = 0; - if(FAILED(self.interface->CreateSourceVoice(&self.sourceVoice, (WAVEFORMATEX*)&waveFormat, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))) return terminate(), false; + if(FAILED(self.xa2Interface->CreateSourceVoice(&self.sourceVoice, (WAVEFORMATEX*)&waveFormat, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, nullptr, nullptr))) return terminate(), false; clear(); return self.isReady = true; @@ -195,7 +194,7 @@ private: u32 index = 0; //current buffer for writing samples to volatile long queue = 0; //how many buffers are queued and ready for playback - IXAudio2* interface = nullptr; + IXAudio2* xa2Interface = nullptr; IXAudio2MasteringVoice* masterVoice = nullptr; IXAudio2SourceVoice* sourceVoice = nullptr; diff --git a/ruby/ruby.cpp b/ruby/ruby.cpp index b9afe7f02..e5e873be9 100644 --- a/ruby/ruby.cpp +++ b/ruby/ruby.cpp @@ -1,6 +1,4 @@ #include -using namespace nall; -using namespace ruby; #undef deprecated #undef mkdir @@ -21,10 +19,15 @@ using namespace ruby; #include #include #elif defined(DISPLAY_WINDOWS) - #define far + #include + #include + #include #include #endif +using namespace nall; +using namespace ruby; + #include #include #include diff --git a/ruby/video/direct3d.cpp b/ruby/video/direct3d.cpp index 9755a870b..85a97c5ad 100644 --- a/ruby/video/direct3d.cpp +++ b/ruby/video/direct3d.cpp @@ -1,7 +1,4 @@ -#undef interface -#define interface struct #include -#undef interface static LRESULT CALLBACK VideoDirect3D9_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false; diff --git a/sourcery/GNUmakefile b/sourcery/GNUmakefile index 34a2d4641..3882868e0 100644 --- a/sourcery/GNUmakefile +++ b/sourcery/GNUmakefile @@ -1,6 +1,6 @@ name := sourcery build := stable -flags += -I.. +flags += -I.. -DNALL_HEADER_ONLY nall.path := ../nall include $(nall.path)/GNUmakefile