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.
This commit is contained in:
invertego
2022-12-18 09:18:52 +00:00
committed by Luke Usher
parent 8f60efcb20
commit e1e11cf75e
56 changed files with 1665 additions and 1304 deletions
+2 -2
View File
@@ -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)
+6 -3
View File
@@ -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/
+1 -3
View File
@@ -1,9 +1,8 @@
#if defined(DISPLAY_WINDOWS)
#define TBS_TRANSPARENTBKGND 0x1000
#include <nall/windows/guard.hpp>
#include <nall/windows/windows.hpp>
#include <winsock2.h>
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <uxtheme.h>
@@ -23,7 +22,6 @@
#include <gtksourceview/gtksource.h>
#endif
#endif
#include <nall/windows/guard.hpp>
#include <nall/windows/registry.hpp>
#include <nall/windows/utf8.hpp>
#endif
+2 -3
View File
@@ -1,14 +1,13 @@
#include <nall/windows/guard.hpp>
#include <nall/windows/windows.hpp>
#include <winsock2.h>
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <commdlg.h>
#include <uxtheme.h>
#include <io.h>
#include <shlobj.h>
#include <shellapi.h>
#include <dwmapi.h>
#include <nall/windows/guard.hpp>
#include <nall/windows/registry.hpp>
#include <nall/windows/utf8.hpp>
+10 -3
View File
@@ -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/
+5
View File
@@ -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
+3 -1
View File
@@ -1,8 +1,10 @@
#pragma once
#include <nall/string.hpp>
#include <nall/directory.hpp>
#include <nall/file.hpp>
#include <nall/location.hpp>
#include <nall/path.hpp>
#include <nall/string.hpp>
#include <nall/vector.hpp>
namespace nall {
+2
View File
@@ -1,5 +1,7 @@
#pragma once
#include <nall/string.hpp>
namespace nall::Decode {
//returns empty string on malformed content
+86
View File
@@ -0,0 +1,86 @@
#include <nall/directory.hpp>
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<string> {
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<string> 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<string> {
if(!pathname) return {};
vector<string> 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
}
+4 -77
View File
@@ -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<string> {
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<string> 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<string> {
if(!pathname) return {};
vector<string> 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 <nall/directory.cpp>
#endif
+39
View File
@@ -0,0 +1,39 @@
#include <nall/dl.hpp>
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
}
+5 -29
View File
@@ -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 <nall/dl.cpp>
#endif
+80
View File
@@ -0,0 +1,80 @@
#include <nall/file-map.hpp>
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
}
+6 -69
View File
@@ -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 <nall/file-map.cpp>
#endif
+36
View File
@@ -0,0 +1,36 @@
#include <nall/http/client.hpp>
#if defined(PLATFORM_WINDOWS)
#include <ws2tcpip.h>
#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;
}
}
}
+5 -26
View File
@@ -2,6 +2,8 @@
#include <nall/http/role.hpp>
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 <nall/http/client.cpp>
#endif
+2
View File
@@ -1,5 +1,7 @@
#pragma once
#include <nall/chrono.hpp>
#include <nall/file-map.hpp>
#include <nall/http/message.hpp>
namespace nall::HTTP {
+185
View File
@@ -0,0 +1,185 @@
#include <nall/http/server.hpp>
#include <nall/thread.hpp>
#if defined(PLATFORM_WINDOWS)
#include <ws2tcpip.h>
#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;
}
}
+11 -177
View File
@@ -3,6 +3,9 @@
#include <nall/service.hpp>
#include <nall/http/role.hpp>
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<Response (Request&)>& 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 <nall/http/server.cpp>
#endif
+15
View File
@@ -0,0 +1,15 @@
#include <nall/inode.hpp>
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
}
}
+5 -9
View File
@@ -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 <nall/inode.cpp>
#endif
+6
View File
@@ -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 <machine/endian.h>
#elif defined(linux) || defined(__linux__)
+44
View File
@@ -0,0 +1,44 @@
#include <nall/main.hpp>
#if defined(PLATFORM_WINDOWS)
#include <objbase.h>
#include <shellapi.h>
#include <winsock2.h>
#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);
}
+4 -31
View File
@@ -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 <nall/main.cpp>
#endif
+47
View File
@@ -0,0 +1,47 @@
#include <nall/memory.hpp>
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
}
}
+7 -39
View File
@@ -195,47 +195,11 @@ template<u32 size, typename T> 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 <nall/memory.cpp>
#endif
+32
View File
@@ -0,0 +1,32 @@
#if !defined(NALL_HEADER_ONLY)
#include <nall/intrinsics.hpp>
#if defined(PLATFORM_WINDOWS)
#include <nall/windows/windows.hpp>
#endif
#include <nall/directory.cpp>
#include <nall/dl.cpp>
#include <nall/file-map.cpp>
#include <nall/inode.cpp>
#include <nall/main.cpp>
#include <nall/memory.cpp>
#include <nall/path.cpp>
#include <nall/platform.cpp>
#include <nall/random.cpp>
#include <nall/run.cpp>
#include <nall/thread.cpp>
//currently unused by ares
//#include <nall/smtp.cpp>
//#include <nall/http/client.cpp>
//#include <nall/http/server.cpp>
#if defined(PLATFORM_WINDOWS)
//#include <nall/windows/detour.cpp>
//#include <nall/windows/guid.cpp>
//#include <nall/windows/launcher.cpp>
#include <nall/windows/registry.cpp>
#include <nall/windows/utf8.cpp>
#endif
#endif
+144
View File
@@ -0,0 +1,144 @@
#include <nall/path.hpp>
#if defined(PLATFORM_WINDOWS)
#include <shlobj.h>
#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;
}
}
+12 -127
View File
@@ -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 <nall/path.cpp>
#endif
+25
View File
@@ -0,0 +1,25 @@
#include <nall/platform.hpp>
#if defined(PLATFORM_WINDOWS)
#include <winsock2.h>
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
+17 -27
View File
@@ -9,18 +9,12 @@ namespace Math {
}
#if defined(PLATFORM_WINDOWS)
#include <nall/windows/guard.hpp>
#include <initguid.h>
#include <cguid.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#if defined(NALL_HEADER_ONLY)
#include <nall/windows/windows.hpp>
#endif
#include <direct.h>
#include <io.h>
#include <wchar.h>
#include <shlobj.h>
#include <shellapi.h>
#include <nall/windows/guard.hpp>
#include <nall/windows/utf8.hpp>
#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 <nall/platform.cpp>
#endif
+40
View File
@@ -0,0 +1,40 @@
#include <nall/random.hpp>
#if defined(PLATFORM_LINUX) && __has_include(<sys/random.h>)
#include <sys/random.h>
#elif defined(PLATFORM_ANDROID) && __has_include(<sys/syscall.h>)
#include <sys/syscall.h>
#elif defined(PLATFORM_WINDOWS) && __has_include(<wincrypt.h>)
#include <wincrypt.h>
#else
#include <stdio.h>
#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(<sys/random.h>)
getrandom(&seed, 32, GRND_NONBLOCK);
#elif defined(PLATFORM_ANDROID) && __has_include(<sys/syscall.h>)
syscall(__NR_getrandom, &seed, 32, 0x0001); //GRND_NONBLOCK
#elif defined(PLATFORM_WINDOWS) && __has_include(<wincrypt.h>)
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;
}
}
+10 -37
View File
@@ -9,19 +9,14 @@
#include <nall/cipher/chacha20.hpp>
#endif
#if defined(PLATFORM_LINUX) && __has_include(<sys/random.h>)
#include <sys/random.h>
#elif defined(PLATFORM_ANDROID) && __has_include(<sys/syscall.h>)
#include <sys/syscall.h>
#elif defined(PLATFORM_WINDOWS) && __has_include(<wincrypt.h>)
#include <wincrypt.h>
#else
#include <stdio.h>
#endif
namespace nall {
template<typename Base> struct RNG {
struct RNGBase {
protected:
auto randomSeed() -> u256;
};
template<typename Base> struct RNG : RNGBase {
template<typename T = u64> auto random() -> T {
u64 value = 0;
for(u32 n : range((sizeof(T) + 3) / 4)) {
@@ -37,32 +32,6 @@ template<typename Base> 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(<sys/random.h>)
getrandom(&seed, 32, GRND_NONBLOCK);
#elif defined(PLATFORM_ANDROID) && __has_include(<sys/syscall.h>)
syscall(__NR_getrandom, &seed, 32, 0x0001); //GRND_NONBLOCK
#elif defined(PLATFORM_WINDOWS) && __has_include(<wincrypt.h>)
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<typename T = u64> inline auto random() -> T {
}
}
#if defined(NALL_HEADER_ONLY)
#include <nall/random.cpp>
#endif
+102
View File
@@ -0,0 +1,102 @@
#include <nall/run.hpp>
#include <nall/path.hpp>
#if defined(PLATFORM_WINDOWS)
#include <shellapi.h>
#endif
namespace nall {
#if defined(PLATFORM_WINDOWS)
NALL_HEADER_INLINE auto execute(const string& name, vector<string> 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<string> 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
}
+10 -82
View File
@@ -110,94 +110,18 @@ template<typename... P> inline auto invoke(const string& name, P&&... p) -> void
#elif defined(PLATFORM_WINDOWS)
auto execute(const string& name, vector<string> argl) -> execute_result_t;
template<typename... P> inline auto execute(const string& name, P&&... p) -> execute_result_t {
vector<string> argl(name, std::forward<P>(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<string> argl) -> void;
template<typename... P> inline auto invoke(const string& name, P&&... p) -> void {
vector<string> argl(std::forward<P>(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<typename... P> inline auto invoke(const string& name, P&&... p) -> void
#endif
}
#if defined(NALL_HEADER_ONLY)
#include <nall/run.cpp>
#endif
+149
View File
@@ -0,0 +1,149 @@
#include <nall/smtp.hpp>
#if defined(PLATFORM_WINDOWS)
#include <ws2tcpip.h>
#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<u8> 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
}
+10 -144
View File
@@ -1,17 +1,17 @@
#pragma once
#include <nall/base64.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/file.hpp>
#include <nall/location.hpp>
#include <nall/random.hpp>
#include <nall/encode/base64.hpp>
#if !defined(PLATFORM_WINDOWS)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
#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<u8> 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 <nall/smtp.cpp>
#endif
}
+53
View File
@@ -0,0 +1,53 @@
#include <nall/thread.hpp>
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<void (uintptr)>& 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
}
+4 -44
View File
@@ -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<void (uintptr)>& 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 <nall/thread.cpp>
#endif
+1
View File
@@ -1,5 +1,6 @@
#pragma once
#include <initializer_list>
#include <type_traits>
#include <nall/stdint.hpp>
+69
View File
@@ -0,0 +1,69 @@
#include <nall/windows/detour.hpp>
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;
}
}
+43 -101
View File
@@ -1,10 +1,9 @@
#pragma once
#include <nall/foreach.hpp>
#include <nall/platform.hpp>
#include <nall/stdint.hpp>
#include <nall/string.hpp>
#include <nall/utf8.hpp>
#include <nall/windows/utf8.hpp>
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 <nall/windows/detour.cpp>
#endif
-32
View File
@@ -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
+17
View File
@@ -0,0 +1,17 @@
#include <nall/windows/guid.hpp>
#include <combaseapi.h>
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);
}
}
+5 -9
View File
@@ -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 <nall/windows/guid.cpp>
#endif
+93
View File
@@ -0,0 +1,93 @@
#include <nall/windows/launcher.hpp>
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
}
}
+7 -83
View File
@@ -1,91 +1,15 @@
#pragma once
#include <nall/stdint.hpp>
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 <nall/windows/launcher.cpp>
#endif
+113
View File
@@ -0,0 +1,113 @@
#include <nall/windows/registry.hpp>
#include <shlwapi.h>
#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<string> {
vector<string> 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;
}
}
+10 -102
View File
@@ -3,117 +3,25 @@
#include <nall/platform.hpp>
#include <nall/string.hpp>
#include <shlwapi.h>
#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<string> {
vector<string> 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<string>;
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 <nall/windows/registry.cpp>
#endif
+37
View File
@@ -0,0 +1,37 @@
#include <nall/windows/utf8.hpp>
#include <shellapi.h>
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);
}
}
}
+10 -26
View File
@@ -1,5 +1,7 @@
#pragma once
#include <nall/stdint.hpp>
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 <nall/windows/utf8.cpp>
#endif
+17
View File
@@ -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 <sdkddkver.h>
#include <windows.h>
+10 -11
View File
@@ -1,5 +1,4 @@
#include "xaudio2.hpp"
#undef interface
struct AudioXAudio2 : AudioDriver, public IXAudio2VoiceCallback {
enum : u32 { Buffers = 32 };
@@ -95,13 +94,13 @@ private:
vector<Device> 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;
+6 -3
View File
@@ -1,6 +1,4 @@
#include <ruby/ruby.hpp>
using namespace nall;
using namespace ruby;
#undef deprecated
#undef mkdir
@@ -21,10 +19,15 @@ using namespace ruby;
#include <IOKit/graphics/IOGraphicsLib.h>
#include <nall/macos/guard.hpp>
#elif defined(DISPLAY_WINDOWS)
#define far
#include <nall/windows/windows.hpp>
#include <initguid.h>
#include <cguid.h>
#include <mmsystem.h>
#endif
using namespace nall;
using namespace ruby;
#include <ruby/video/video.cpp>
#include <ruby/audio/audio.cpp>
#include <ruby/input/input.cpp>
-3
View File
@@ -1,7 +1,4 @@
#undef interface
#define interface struct
#include <d3d9.h>
#undef interface
static LRESULT CALLBACK VideoDirect3D9_WindowProcedure(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if(msg == WM_SYSKEYDOWN && wparam == VK_F4) return false;
+1 -1
View File
@@ -1,6 +1,6 @@
name := sourcery
build := stable
flags += -I..
flags += -I.. -DNALL_HEADER_ONLY
nall.path := ../nall
include $(nall.path)/GNUmakefile