Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
#Find the correct architecture to build for
|
||||
ifndef ARCH
|
||||
ARCH = $(shell getconf LONG_BIT)
|
||||
endif
|
||||
|
||||
#Set correct flags for architecture
|
||||
ifeq ($(ARCH), 32)
|
||||
#Global
|
||||
CXXFLAGS += -m32 -march=pentium4 -mtune=generic
|
||||
|
||||
#Arch name
|
||||
ARCHNAME = x86
|
||||
else
|
||||
#Global
|
||||
CXXFLAGS += -m64 -march=athlon64 -mtune=generic
|
||||
|
||||
#Arch name
|
||||
ARCHNAME = x64
|
||||
endif
|
||||
|
||||
#Global flags
|
||||
ifeq ($(origin AR), default)
|
||||
AR = gcc-ar
|
||||
endif
|
||||
BIN = libos.a
|
||||
SRCDIR = source/os/source
|
||||
|
||||
UNAME = $(shell uname)
|
||||
ifeq ($(UNAME), Darwin)
|
||||
OSNAME = osx
|
||||
else
|
||||
OSNAME = lin
|
||||
endif
|
||||
|
||||
ifdef DEBUG
|
||||
OBJDIR = obj_d/$(OSNAME)$(ARCH)/os
|
||||
BINDIR = obj_d/$(OSNAME)$(ARCH)
|
||||
|
||||
CXXFLAGS += -O0 -g
|
||||
CXXFLAGS += -D_DEBUG
|
||||
else
|
||||
OBJDIR = obj/$(OSNAME)$(ARCH)/os
|
||||
BINDIR = obj/$(OSNAME)$(ARCH)
|
||||
|
||||
CXXFLAGS += -Ofast
|
||||
CXXFLAGS += -DNDEBUG
|
||||
ifndef NLTO
|
||||
CXXFLAGS += -flto
|
||||
else
|
||||
CXXFLAGS += -fno-lto
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef CC
|
||||
CC = g++
|
||||
endif
|
||||
|
||||
CXXFLAGS += -std=c++11
|
||||
CXXFLAGS += -Wall -Wno-invalid-offsetof -Wno-switch -Wno-reorder
|
||||
CXXFLAGS += -I./source/os/include -I./source/os/source -I./source/util/include
|
||||
|
||||
#Source code files
|
||||
SOURCES = \
|
||||
threads.cpp \
|
||||
threads_gcc.cpp \
|
||||
files.cpp \
|
||||
files_linux.cpp \
|
||||
virtual_asm_linux.cpp \
|
||||
virtual_asm_$(ARCHNAME).cpp
|
||||
|
||||
CPP_FILES = $(addprefix $(SRCDIR)/, $(SOURCES))
|
||||
OBJ_FILES = $(addprefix $(OBJDIR)/, $(SOURCES:.cpp=.o))
|
||||
DEP_FILES = $(addprefix $(OBJDIR)/, $(SOURCES:.cpp=.d))
|
||||
|
||||
-include $(DEP_FILES)
|
||||
|
||||
compile: $(DEP_FILES) $(BINDIR)/$(BIN)
|
||||
|
||||
#Dependency files to take care of header changes
|
||||
$(OBJDIR)/%.d: $(SRCDIR)/%.cpp
|
||||
@mkdir -p $(dir $@)
|
||||
@$(CC) $(CXXFLAGS) -MM -MT "$(@:.d=.o)" $< >> $@
|
||||
|
||||
#Object files are compiled separately
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
|
||||
@mkdir -p $(dir $@)
|
||||
@echo $<
|
||||
@$(CC) $(CXXFLAGS) $< -c -o $@
|
||||
|
||||
#Complete binary compile
|
||||
$(BINDIR)/$(BIN): $(OBJ_FILES)
|
||||
@mkdir -p $(BINDIR)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
clean:
|
||||
@rm -rf $(OBJDIR)
|
||||
@rm -f ./.depend
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
//Returns the current working directory
|
||||
std::string getWorkingDirectory();
|
||||
|
||||
//Attempts to change the current working directory to <dir>, returns true if successful
|
||||
bool setWorkingDirectory(const std::string& dir);
|
||||
|
||||
//Lists all files and folders in <dir> to <out>
|
||||
// If a filter is specified, it applies only to files
|
||||
bool listDirectory(const std::string& dir, std::vector<std::string>& out, const char* filter = "*");
|
||||
|
||||
//Get the contents of a file in a string
|
||||
std::string getFileContents(const std::string& filename);
|
||||
|
||||
//Get the absolute real path to a file
|
||||
std::string getAbsolutePath(const std::string& relpath);
|
||||
|
||||
//Check if a file exists
|
||||
bool fileExists(const std::string& path);
|
||||
|
||||
//Check if a file is writable
|
||||
bool fileWritable(const std::string& path);
|
||||
|
||||
//Check if a file exists and is a directory
|
||||
bool isDirectory(const std::string& path);
|
||||
|
||||
//Create a directory
|
||||
void makeDirectory(const std::string& path);
|
||||
|
||||
//Join path elements
|
||||
std::string path_join(const std::string& one, const std::string& two);
|
||||
|
||||
//Go up a directory
|
||||
std::string path_up(const std::string& path);
|
||||
|
||||
//Split path into elements
|
||||
void path_split(const std::string& path, std::vector<std::string>& out);
|
||||
|
||||
//Check whether a path is inside another path
|
||||
bool path_inside(const std::string& folder, const std::string& subpath);
|
||||
|
||||
//Get the dirname and basename of a file
|
||||
std::string getBasename(const std::string& filename, bool includeExtension = true);
|
||||
std::string getDirname(const std::string& filename);
|
||||
|
||||
//Get the root directory for storing profile data
|
||||
std::string getProfileRoot();
|
||||
|
||||
//Get the name of a temporary file
|
||||
std::string getTemporaryFile();
|
||||
|
||||
void watchDirectory(const std::string& path, std::function<void(std::string&)> callback);
|
||||
void watchFile(const std::string& path, std::function<bool()> callback);
|
||||
void clearWatches();
|
||||
|
||||
//Get file mtimes
|
||||
time_t getModifiedTime(const std::string& filename);
|
||||
@@ -0,0 +1,265 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define Threaded(type) __declspec(thread) type
|
||||
|
||||
#define threadcall __stdcall
|
||||
|
||||
#else
|
||||
#include <atomic>
|
||||
#include <pthread.h>
|
||||
|
||||
#define Threaded(type) __thread type
|
||||
|
||||
#define threadcall
|
||||
#endif
|
||||
|
||||
//#define PROFILE_LOCKS
|
||||
#ifdef PROFILE_LOCKS
|
||||
#include <string>
|
||||
#include <set>
|
||||
#endif
|
||||
#include <functional>
|
||||
|
||||
namespace threads {
|
||||
|
||||
unsigned getNumberOfProcessors();
|
||||
void async(std::function<int()> f);
|
||||
|
||||
void sleep(unsigned int milliseconds);
|
||||
//Sleep intended for use as a wait in a non-critical busy loop
|
||||
void idle();
|
||||
|
||||
#ifndef _MSC_VER
|
||||
typedef unsigned int threadreturn;
|
||||
typedef threadreturn (*threadfunc)(void*);
|
||||
#else
|
||||
typedef unsigned long threadreturn;
|
||||
typedef threadreturn threadcall threadfunc(void*);
|
||||
#endif
|
||||
|
||||
class atomic_int {
|
||||
#ifdef _MSC_VER
|
||||
volatile long value;
|
||||
#else
|
||||
volatile int value;
|
||||
#endif
|
||||
public:
|
||||
int get_basic();
|
||||
void set_basic(int val);
|
||||
|
||||
int operator++();
|
||||
int operator++(int);
|
||||
int operator--();
|
||||
int operator--(int);
|
||||
|
||||
int operator+=(int value);
|
||||
int operator-=(int value);
|
||||
|
||||
int operator|=(int value);
|
||||
int operator&=(int value);
|
||||
|
||||
void operator=(int value);
|
||||
|
||||
int exchange(int value);
|
||||
int compare_exchange_strong(int value, int compareTo);
|
||||
|
||||
//Like compare_exchange_strong, but will not return until the value is exchanged
|
||||
void wait_compare_exchange(int xchg, int compareTo, const int spinCount);
|
||||
|
||||
int get() const { return value; }
|
||||
operator int() const { return value; }
|
||||
|
||||
atomic_int() : value(0) {}
|
||||
atomic_int(int v) : value(v) {}
|
||||
};
|
||||
|
||||
//Swap a value atomically
|
||||
int swap(int* ptr, int newval);
|
||||
int compare_and_swap(int* ptr, int oldval, int newval);
|
||||
//long long swap(long long* ptr, long long newval);
|
||||
long long compare_and_swap(long long* ptr, long long oldval, long long newval);
|
||||
void* swap(void** ptr, void* newval);
|
||||
void* compare_and_swap(void** ptr, void* oldval, void* newval);
|
||||
|
||||
class _threadlocalPointer {
|
||||
#ifdef _MSC_VER
|
||||
long index;
|
||||
#elif defined(__GNUC__)
|
||||
pthread_key_t key;
|
||||
#endif
|
||||
public:
|
||||
_threadlocalPointer();
|
||||
~_threadlocalPointer();
|
||||
|
||||
void set(void* ptr);
|
||||
void* get();
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class threadlocalPointer : public _threadlocalPointer {
|
||||
public:
|
||||
inline operator T*() {
|
||||
return (T*)get();
|
||||
}
|
||||
|
||||
inline void operator=(T* ptr) {
|
||||
set((void*)ptr);
|
||||
}
|
||||
|
||||
inline T* operator->() {
|
||||
return (T*)get();
|
||||
}
|
||||
};
|
||||
|
||||
enum ThreadPriority {
|
||||
TP_High,
|
||||
TP_Normal,
|
||||
TP_Low
|
||||
};
|
||||
|
||||
extern const int invalidThreadID;
|
||||
void createThread(threadfunc func, void* arg);
|
||||
int getThreadID();
|
||||
void setThreadPriority(ThreadPriority priority);
|
||||
|
||||
struct Mutex {
|
||||
private:
|
||||
atomic_int owningThread;
|
||||
unsigned lockCount;
|
||||
static const unsigned spinCount;
|
||||
public:
|
||||
#ifdef PROFILE_LOCKS
|
||||
atomic_int profileCount;
|
||||
std::string name;
|
||||
bool observed;
|
||||
Mutex();
|
||||
Mutex(const char* name);
|
||||
~Mutex();
|
||||
#endif
|
||||
|
||||
void lock();
|
||||
bool try_lock();
|
||||
void release();
|
||||
|
||||
bool hasLock();
|
||||
};
|
||||
|
||||
struct Lock {
|
||||
private:
|
||||
Mutex* mutex;
|
||||
public:
|
||||
|
||||
Lock(Mutex& mtx) : mutex(&mtx) { mtx.lock(); }
|
||||
~Lock() { mutex->release(); }
|
||||
};
|
||||
|
||||
struct ReadWriteMutex {
|
||||
private:
|
||||
atomic_int owningThread;
|
||||
atomic_int readCount;
|
||||
static const unsigned spinCount;
|
||||
public:
|
||||
#ifdef PROFILE_LOCKS
|
||||
atomic_int profileReadCount;
|
||||
atomic_int profileWriteCount;
|
||||
std::string name;
|
||||
bool observed;
|
||||
ReadWriteMutex();
|
||||
~ReadWriteMutex();
|
||||
#endif
|
||||
|
||||
void writeLock();
|
||||
void readLock();
|
||||
void release();
|
||||
|
||||
bool hasLock();
|
||||
bool hasWriteLock();
|
||||
};
|
||||
|
||||
struct ReadLock {
|
||||
private:
|
||||
ReadWriteMutex* mutex;
|
||||
public:
|
||||
ReadLock(ReadWriteMutex& mtx) : mutex(&mtx) { mtx.readLock(); }
|
||||
~ReadLock() { mutex->release(); }
|
||||
};
|
||||
|
||||
struct WriteLock {
|
||||
private:
|
||||
ReadWriteMutex* mutex;
|
||||
public:
|
||||
WriteLock(ReadWriteMutex& mtx) : mutex(&mtx) { mtx.writeLock(); }
|
||||
~WriteLock() { mutex->release(); }
|
||||
};
|
||||
|
||||
struct Signal {
|
||||
private:
|
||||
atomic_int flag;
|
||||
static const unsigned spinCount;
|
||||
public:
|
||||
Signal(int start = 0);
|
||||
|
||||
//Sets the signal to the specified value
|
||||
void signal(int value);
|
||||
//Reduces the signal's value by one
|
||||
void signalDown();
|
||||
//Increases the signal's value by one
|
||||
void signalUp();
|
||||
//Reduces the signal's value by a value
|
||||
void signalDown(int value);
|
||||
//Increases the signal's value by a value
|
||||
void signalUp(int value);
|
||||
|
||||
//Returns true if the flag is at the value
|
||||
bool check(int checkFor) const;
|
||||
//Checks if the flag is at value, and sets it if it is
|
||||
bool checkAndSignal(int checkFor, int newSignal);
|
||||
|
||||
//Waits until the value is the specified value
|
||||
void wait(int waitFor) const;
|
||||
//Waits until the value is not the specified value
|
||||
void waitNot(int waitForNot) const;
|
||||
//Waits until the value is the specified value, then sets it to a new value
|
||||
void waitAndSignal(int waitFor, int newSignal);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct SharedData {
|
||||
mutable atomic_int count;
|
||||
T data;
|
||||
|
||||
SharedData(int startCount = 1) : count(startCount), data() {}
|
||||
|
||||
void grab() const {
|
||||
++count;
|
||||
}
|
||||
|
||||
void drop() const {
|
||||
if(--count == 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
return data;
|
||||
}
|
||||
|
||||
const T& operator*() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return &data;
|
||||
}
|
||||
|
||||
const T* operator->() const {
|
||||
return &data;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef PROFILE_LOCKS
|
||||
void profileMutexCycle(std::function<void(Mutex*)> cb);
|
||||
void profileReadWriteMutexCycle(std::function<void(ReadWriteMutex*)> cb);
|
||||
#endif
|
||||
|
||||
};
|
||||
@@ -0,0 +1,574 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace assembler {
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
struct Register;
|
||||
struct MemAddress;
|
||||
|
||||
enum RegCode : byte {
|
||||
EAX = 0,
|
||||
ECX = 1,
|
||||
EDX = 2,
|
||||
EBX = 3,
|
||||
ESP = 4, SIB = 4,
|
||||
EBP = 5, ADDR = 5,
|
||||
ESI = 6,
|
||||
EDI = 7,
|
||||
|
||||
R8 = 8,
|
||||
R9 = 9,
|
||||
R10 = 10,
|
||||
R12 = 12,
|
||||
R13 = 13,
|
||||
R14 = 14,
|
||||
R15 = 15,
|
||||
|
||||
//R11 is supervolatile. Can change in between
|
||||
//virtual asm ops (used as a temporary), so be careful
|
||||
//with using it.
|
||||
R11 = 11,
|
||||
|
||||
//XMM registers have unique numbers so we can recognize them
|
||||
XMM0 = 16,
|
||||
XMM1 = 17,
|
||||
XMM2 = 18,
|
||||
XMM3 = 19,
|
||||
XMM4 = 20,
|
||||
XMM5 = 21,
|
||||
XMM6 = 22,
|
||||
XMM7 = 23,
|
||||
|
||||
XMM8 = 24,
|
||||
XMM9 = 25,
|
||||
XMM10 = 26,
|
||||
XMM11 = 27,
|
||||
XMM12 = 28,
|
||||
XMM13 = 29,
|
||||
XMM14 = 30,
|
||||
XMM15 = 31,
|
||||
|
||||
NONE = 0,
|
||||
};
|
||||
|
||||
//Floating Point Register codes representing the stack registers on the FPU
|
||||
// The top of the stack is always FPU_0
|
||||
enum FloatReg : byte {
|
||||
FPU_0 = 0,
|
||||
FPU_1 = 1,
|
||||
FPU_2 = 2,
|
||||
FPU_3 = 3,
|
||||
FPU_4 = 4,
|
||||
FPU_5 = 5,
|
||||
FPU_6 = 6,
|
||||
FPU_7 = 7,
|
||||
};
|
||||
|
||||
enum JumpType {
|
||||
Overflow,
|
||||
NotOverflow,
|
||||
Below, Carry = Below,
|
||||
NotBelow, NotCarry = NotBelow,
|
||||
Equal, Zero = Equal,
|
||||
NotEqual, NotZero = NotEqual,
|
||||
NotAbove,
|
||||
Above,
|
||||
Sign,
|
||||
NotSign,
|
||||
Parity,
|
||||
NotParity,
|
||||
Less,
|
||||
GreaterOrEqual,
|
||||
LessOrEqual,
|
||||
Greater,
|
||||
Jump,
|
||||
|
||||
JumpTypeCount
|
||||
};
|
||||
|
||||
|
||||
//Handles thread safety for the JIT
|
||||
struct CriticalSection {
|
||||
void* pLock;
|
||||
|
||||
void enter();
|
||||
void leave();
|
||||
|
||||
CriticalSection();
|
||||
~CriticalSection();
|
||||
};
|
||||
|
||||
struct AddrPrefix {
|
||||
MemAddress& adr;
|
||||
bool defLong;
|
||||
unsigned char further;
|
||||
|
||||
AddrPrefix(MemAddress& Adr, bool DefLong, unsigned char Further)
|
||||
: adr(Adr), defLong(DefLong), further(Further) {
|
||||
}
|
||||
};
|
||||
|
||||
struct RegPrefix {
|
||||
Register& reg;
|
||||
unsigned short other;
|
||||
bool defLong;
|
||||
|
||||
RegPrefix(Register& Reg, unsigned short Other, bool DefLong)
|
||||
: reg(Reg), other(Other), defLong(DefLong) {
|
||||
}
|
||||
};
|
||||
|
||||
//Stores information about the code page
|
||||
// Generates an executable page in memory when created
|
||||
// Deletes the asssociated page when deleted
|
||||
//Implementation in virtual_asm_<operating system>.cpp (e.g. virtual_asm_windows.cpp)
|
||||
struct CodePage {
|
||||
void* page;
|
||||
unsigned int size, used, references;
|
||||
bool final;
|
||||
|
||||
CodePage(unsigned int Size, void* requestedStart = 0);
|
||||
~CodePage();
|
||||
|
||||
void grab();
|
||||
void drop();
|
||||
|
||||
//Call finalize when done writing to the code page to guarantee that it can be executed
|
||||
//No more writing may be done to the allocated pages
|
||||
void finalize();
|
||||
|
||||
//Returns the pointer to the first currently unused chunk of the page
|
||||
template<class T>
|
||||
T getFunctionPointer() {
|
||||
return reinterpret_cast<T>((byte*)page+used);
|
||||
}
|
||||
|
||||
byte* getActivePage() const {
|
||||
return (byte*)page+used;
|
||||
}
|
||||
|
||||
//Marks bytes as used;
|
||||
//future calls to getFunctionPointer() will not reference the location that is being marked as used
|
||||
void markBytesUsed(unsigned int count) {
|
||||
used += count;
|
||||
}
|
||||
|
||||
//Marks bytes up to <address> as used
|
||||
void markUsedAddress(void* address) {
|
||||
unsigned newUsed = (unsigned)((byte*)address - (byte*)page);
|
||||
if(newUsed > used && newUsed <= size)
|
||||
used = newUsed;
|
||||
}
|
||||
|
||||
//Returns the number of bytes not yet allocated to a function
|
||||
unsigned int getFreeSize() const {
|
||||
return size-used;
|
||||
}
|
||||
|
||||
//Returns the smallest page (in bytes) that can be allocated by a code page (Sizes other than multiples of this size allocate an extra page)
|
||||
static unsigned int getMinimumPageSize();
|
||||
|
||||
private:
|
||||
CodePage() {}
|
||||
};
|
||||
|
||||
//Stores the code pointer and provides access to various processor-level operations
|
||||
// To work with the processor, create a set of 'Register' instances, each taking the RegCode of the associated register (e.g. Register eax(cpu, EAX))
|
||||
//Implementation in virtual_asm_<processor instruction set>.cpp (e.g. virtual_asm_x86.cpp)
|
||||
struct Processor {
|
||||
//Pointer to the location for the next opcode
|
||||
byte* op;
|
||||
byte* pageStart;
|
||||
//The current mode of operation, in bits
|
||||
// e.g. 32 bits for x86, indicating that operations should treat addresses as if they were unsigned integers
|
||||
unsigned bitMode, lastBitMode;
|
||||
//The number of bytes currently on the stack that we are responsible for
|
||||
unsigned stackDepth;
|
||||
//Reserved jump space
|
||||
unsigned jumpSpace;
|
||||
byte* jumpPtr;
|
||||
|
||||
//Initializes the processor to point to the active page of the code page
|
||||
//Optionally takes a bitMode override (defaults to the same bitMode as the exe)
|
||||
Processor(CodePage& codePage, unsigned defaultBitMode = sizeof(void*)*8 );
|
||||
|
||||
//Creates a jump to the new code page, and marks the current address as used on the old code page
|
||||
//Updates output pointer to the new code page's active page
|
||||
void migrate(CodePage& prevPage, CodePage& newPage);
|
||||
|
||||
//Changes the current bitMode, and stores the previous bitMode
|
||||
void setBitMode(unsigned bits) {
|
||||
lastBitMode = bitMode;
|
||||
bitMode = bits;
|
||||
}
|
||||
|
||||
//Restores the previous bitMode
|
||||
void resetBitMode() {
|
||||
bitMode = lastBitMode;
|
||||
}
|
||||
|
||||
//Returns the alignment of the stack (number of bytes a push increments esp)
|
||||
static unsigned pushSize();
|
||||
|
||||
//Pushes data to the opcode output
|
||||
template<class T>
|
||||
Processor& operator<<(T b) {
|
||||
*(T*)op = b; op += sizeof(T);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//Pushes bytes representing a memory address to the opcode output
|
||||
template<class T>
|
||||
Processor& operator<<(MemAddress addr);
|
||||
|
||||
//Pushes bytes representing a prefix
|
||||
template<class T>
|
||||
Processor& operator<<(AddrPrefix pr);
|
||||
|
||||
template<class T>
|
||||
Processor& operator<<(RegPrefix pr);
|
||||
|
||||
//Calls the function, passing the arguments specified by 'args'
|
||||
//args is a string like "rrcmrm" which specifies arguments as sourced by a Register*, MemAddres*, or a constant
|
||||
//EBP is invalid during the call
|
||||
void call_cdecl(void* func, const char* args, va_list ap);
|
||||
void call_cdecl(void* func, const char* args, ...);
|
||||
|
||||
//Use call() in between these to set up a call with an arbitrary function
|
||||
unsigned call_cdecl_args(const char* args, ...);
|
||||
unsigned call_cdecl_args(const char* args, va_list ap);
|
||||
unsigned call_thiscall_args(Register* obj, const char* args, ...);
|
||||
unsigned call_thiscall_args(Register* obj, const char* args, va_list ap);
|
||||
|
||||
//Prepares for a call to manual call to a cdecl function (Do not use with call_cdecl)
|
||||
// Use before pushing arguments
|
||||
// Invalidates EBP until call_cdecl_end()
|
||||
void call_cdecl_prep(unsigned argBytes);
|
||||
//Ends a manual call to a cdecl function (Do not use with call_cdecl)
|
||||
// Use after returning from the function
|
||||
void call_cdecl_end(unsigned argBytes, bool returnPointer = false);
|
||||
|
||||
//Note: stdcall is like cdecl, but does not use cdecl_end
|
||||
|
||||
//Calls the function, passing the arguments specified by 'args'
|
||||
//args is a string like "rrcmrm" which specifies arguments as sourced by a Register*, MemAddres*, or a constant
|
||||
//EBP is invalid during the call
|
||||
void call_stdcall(void* func, const char* args, ...);
|
||||
|
||||
//To call a thiscall:
|
||||
// cpu.call_thiscall_prep(total argument size)
|
||||
// cpu.push(arguments)
|
||||
// cpu.call_thiscall_this(source of 'this' pointer)
|
||||
// cpu.call(function)
|
||||
// cpu.call_thiscall_end(total argument size)
|
||||
void call_thiscall_prep(unsigned argBytes);
|
||||
void call_thiscall_this(MemAddress address);
|
||||
void call_thiscall_this(Register& reg);
|
||||
void call_thiscall_this_mem(MemAddress address, Register& memreg);
|
||||
void call_thiscall_this_mem(Register& reg, Register& memreg);
|
||||
void call_thiscall_end(unsigned argBytes, bool returnPointer = false);
|
||||
|
||||
//Calls a function (push code pointer, jump to function)
|
||||
void call(Register& reg);
|
||||
void call(void* func);
|
||||
|
||||
//Pushes a constant value onto the stack (Pushes are always pushSize() large, values beyond this size are an error)
|
||||
void push(size_t value);
|
||||
//Pops <count> times (Pops are always pushSize() large)
|
||||
void pop(unsigned int count);
|
||||
|
||||
//Pushes the value of <reg> onto the stack
|
||||
void push(Register& reg);
|
||||
//Pops the alue of <reg> from the stack
|
||||
void pop(Register& reg);
|
||||
|
||||
//Get a register corresponding to an argument on 64-bit calling convention
|
||||
unsigned maxIntArgs64();
|
||||
unsigned maxFloatArgs64();
|
||||
bool isIntArg64Register(unsigned char number, unsigned char arg);
|
||||
bool isFloatArg64Register(unsigned char number, unsigned char arg);
|
||||
Register intArg64(unsigned char number, unsigned char arg);
|
||||
Register floatArg64(unsigned char number, unsigned char arg);
|
||||
Register intArg64(unsigned char number, unsigned char arg, Register defaultReg);
|
||||
Register floatArg64(unsigned char number, unsigned char arg, Register defaultReg);
|
||||
Register floatReturn64();
|
||||
Register intReturn64();
|
||||
|
||||
//Pushes the memory at <address> onto the stack (Pushes are always pushSize() large, pushing larger values invokes multiple pushes)
|
||||
void push(MemAddress address);
|
||||
//Pops the value on the stack to the memory at <address> (Pops are always pushSize() large, popping larger values invokes multiple pops)
|
||||
void pop(MemAddress address);
|
||||
|
||||
//Prepares a short jump (fewer than approx. 120 bytes in either direction)
|
||||
// Pass the return to a matching end_short_jump
|
||||
void* prep_short_jump(JumpType type);
|
||||
//Ends a short jump
|
||||
void end_short_jump(void* p);
|
||||
|
||||
//Prepares a large jump (can jump to any location)
|
||||
// Pass the return to a matching end_long_jump
|
||||
void* prep_long_jump(JumpType type);
|
||||
//Ends a large jump
|
||||
void end_long_jump(void* p);
|
||||
|
||||
//Jumps to <dest>
|
||||
void jump(JumpType type, volatile byte* dest);
|
||||
//Jumps to the address in <reg>
|
||||
void jump(Register& reg);
|
||||
//Decrements ecx and jumps if it becomes 0; Optionally conditionally jumps based on a Zero/NotZero test
|
||||
void loop(volatile byte* dest, JumpType type = Jump);
|
||||
|
||||
//Copies from *esi to *edi, and adjusts them both by the data size according to the direction flag
|
||||
void string_copy(unsigned size);
|
||||
//Sets direction flag for string copy
|
||||
void setDirFlag(bool forward);
|
||||
|
||||
//Returns from a function (pop code pointer, jump there)
|
||||
void ret();
|
||||
|
||||
//Triggers a debug break
|
||||
void debug_interrupt();
|
||||
|
||||
private:
|
||||
Processor() {}
|
||||
};
|
||||
|
||||
//Provides access to the floating point unit's state
|
||||
//Implementation in virtual_asm_<processor instruction set>.cpp (e.g. virtual_asm_x86.cpp)
|
||||
struct FloatingPointUnit {
|
||||
Processor& cpu;
|
||||
|
||||
FloatingPointUnit(Processor& CPU);
|
||||
|
||||
//Clears the FPU's state and registers
|
||||
void init();
|
||||
|
||||
//Negates FPU_0
|
||||
void negate();
|
||||
|
||||
//Pushes
|
||||
void load_const_0();
|
||||
void load_const_1();
|
||||
|
||||
//FPU_1 becomes FPU_0 (Pops the fpu stack)
|
||||
void pop();
|
||||
|
||||
//Exchanges contents of FPU_n and FPU_0
|
||||
void exchange(FloatReg floatReg);
|
||||
|
||||
//Compares FPU_0 to floatReg, setting the CPU's flags according to the values' relation
|
||||
// Optionally pops the fpu stack
|
||||
void compare_toCPU(FloatReg floatReg, bool pop = true);
|
||||
|
||||
//Pushes the specified data type stored at <address> onto the FPU stack (becomes FPU_0)
|
||||
void load_float(MemAddress address);
|
||||
void load_dword(MemAddress address);
|
||||
void load_qword(MemAddress address);
|
||||
void load_double(MemAddress address);
|
||||
|
||||
//Stores the value on FPU_0 to <address> according to the data type
|
||||
// Optionally pops the fpu stack
|
||||
void store_float(MemAddress address, bool pop = true);
|
||||
void store_dword(MemAddress address, bool pop = true);
|
||||
void store_double(MemAddress address, bool pop = true);
|
||||
|
||||
//Control words
|
||||
void store_control_word(MemAddress address);
|
||||
void load_control_word(MemAddress address);
|
||||
|
||||
//Effect: FPU_0 -= <reg>
|
||||
void operator-=(FloatReg reg);
|
||||
|
||||
//Effect: FPU_0 += *(float*)address
|
||||
void add_float(MemAddress address);
|
||||
//Effect: FPU_0 -= *(float*)address
|
||||
void sub_float(MemAddress address);
|
||||
//Effect: FPU_0 *= *(float*)address
|
||||
void mult_float(MemAddress address);
|
||||
//Effect: FPU_0 /= *(float*)address
|
||||
void div_float(MemAddress address);
|
||||
|
||||
//Effect: FPU_0 += *(double*)address
|
||||
void add_double(MemAddress address);
|
||||
void add_double(FloatReg reg, bool pop = true);
|
||||
//Effect: FPU_0 -= *(double*)address
|
||||
// If Reversed: FPU_0 = *(double*)address - FPU_0
|
||||
void sub_double(MemAddress address, bool reversed = false);
|
||||
void sub_double(FloatReg reg, bool reversed = false, bool pop = true);
|
||||
//Effect: FPU_0 *= *(double*)address
|
||||
void mult_double(MemAddress address);
|
||||
void mult_double(FloatReg reg, bool pop = true);
|
||||
//Effect: FPU_0 /= *(double*)address
|
||||
// If Reversed: FPU_0 = *(double*)address / FPU_0
|
||||
void div_double(MemAddress address, bool reversed = false);
|
||||
void div_double(FloatReg reg, bool reversed = false, bool pop = true);
|
||||
};
|
||||
|
||||
//Temporary struct that represents an addition to a memory address, with optional scaling
|
||||
struct ScaledIndex {
|
||||
RegCode reg;
|
||||
unsigned char scaleFactor;
|
||||
|
||||
ScaledIndex(RegCode Reg, unsigned char Scale) : reg(Reg), scaleFactor(Scale) {}
|
||||
};
|
||||
|
||||
//Temporary struct that stores data necessary for memory access
|
||||
// Provides operations that can be performed on a memory address
|
||||
//Implementation in virtual_asm_<processor instruction set>.cpp (e.g. virtual_asm_x86.cpp)
|
||||
struct MemAddress {
|
||||
Processor& cpu;
|
||||
void* absolute_address;
|
||||
int offset;
|
||||
unsigned bitMode;
|
||||
RegCode code;
|
||||
RegCode scaleReg;
|
||||
unsigned char other;
|
||||
unsigned char scaleFactor;
|
||||
bool Float;
|
||||
bool Signed;
|
||||
|
||||
MemAddress(Processor& CPU, void* address);
|
||||
MemAddress(Processor& CPU, RegCode Code);
|
||||
MemAddress(Processor& CPU, RegCode Code, int Offset);
|
||||
MemAddress operator+(ScaledIndex scale);
|
||||
MemAddress operator+(int Offset);
|
||||
MemAddress operator-(int Offset);
|
||||
|
||||
void operator++();
|
||||
void operator--();
|
||||
|
||||
void operator-();
|
||||
void operator~();
|
||||
|
||||
void operator+=(unsigned int amount);
|
||||
void operator-=(unsigned int amount);
|
||||
|
||||
void operator=(unsigned int value);
|
||||
void operator=(void* pointer);
|
||||
void operator=(Register fromReg);
|
||||
|
||||
void operator&=(unsigned int value);
|
||||
void operator|=(unsigned int value);
|
||||
|
||||
//Copies memory using an intermediate register
|
||||
void direct_copy(MemAddress address, Register& intermediate);
|
||||
AddrPrefix prefix(unsigned char further = 0, bool defLong = false);
|
||||
};
|
||||
|
||||
//Converts a MemAddress from the default unsigned <cpu bit mode> to match the passed type
|
||||
template<class T>
|
||||
MemAddress as(MemAddress addr) {
|
||||
addr.bitMode = sizeof(T) * 8;
|
||||
addr.Signed = (T)-1 < (T)0;
|
||||
return addr;
|
||||
}
|
||||
|
||||
template<>
|
||||
MemAddress as<float>(MemAddress addr);
|
||||
|
||||
template<>
|
||||
MemAddress as<double>(MemAddress addr);
|
||||
|
||||
//Structure that provides operations that can be performed on a register
|
||||
// Also provides the means to generate MemAddresses relative to a register via dereference (e.g. *eax+8)
|
||||
//Implementation in virtual_asm_<processor instruction set>.cpp (e.g. virtual_asm_x86.cpp)
|
||||
struct Register {
|
||||
Processor& cpu;
|
||||
RegCode code;
|
||||
unsigned bitMode;
|
||||
|
||||
Register(Processor& CPU, RegCode Code);
|
||||
Register(Processor& CPU, RegCode Code, unsigned BitModeOverride);
|
||||
|
||||
void set_regCode(Register& other) {
|
||||
code = other.code;
|
||||
bitMode = other.bitMode;
|
||||
}
|
||||
|
||||
unsigned getBitMode() const;
|
||||
unsigned getBitMode(const MemAddress& addr) const;
|
||||
|
||||
MemAddress operator*() const;
|
||||
ScaledIndex operator*(unsigned char scale) const;
|
||||
|
||||
//Loads the address pointed to by <address> into this register
|
||||
void copy_address(MemAddress address);
|
||||
|
||||
void swap(MemAddress address);
|
||||
void swap(Register& other);
|
||||
|
||||
void operator<<=(Register& other);
|
||||
void operator>>=(Register& other);
|
||||
void rightshift_logical(Register& other);
|
||||
|
||||
void operator+=(unsigned int amount);
|
||||
void operator+=(MemAddress address);
|
||||
void operator+=(Register& other);
|
||||
|
||||
void operator-=(unsigned int amount);
|
||||
void operator-=(Register& other);
|
||||
void operator-=(MemAddress address);
|
||||
|
||||
void operator*=(MemAddress address);
|
||||
|
||||
void operator-();
|
||||
void operator~();
|
||||
|
||||
void operator--();
|
||||
void operator++();
|
||||
|
||||
void operator&=(unsigned long long mask);
|
||||
void operator&=(MemAddress address);
|
||||
void operator&=(Register other);
|
||||
|
||||
void operator^=(MemAddress address);
|
||||
void operator^=(Register& other);
|
||||
|
||||
void operator|=(MemAddress address);
|
||||
void operator|=(unsigned long long mask);
|
||||
|
||||
//Copies a smaller data type, retaining the sign
|
||||
void copy_expanding(MemAddress address);
|
||||
//Copies an 8 bit register, leaving 0s in higher bytes
|
||||
void copy_zeroing(Register& other);
|
||||
|
||||
void operator=(unsigned long long value);
|
||||
void operator=(void* pointer);
|
||||
void operator=(Register other);
|
||||
void operator=(MemAddress addr);
|
||||
|
||||
void operator==(Register other);
|
||||
void operator==(MemAddress addr);
|
||||
void operator==(unsigned int test);
|
||||
|
||||
void setIf(JumpType condition);
|
||||
void* setDeferred(unsigned long long def = 0);
|
||||
|
||||
bool xmm();
|
||||
bool extended();
|
||||
RegCode index();
|
||||
|
||||
RegPrefix prefix(unsigned short other = 0, bool defaultLong = false);
|
||||
RegPrefix prefix(Register& other, bool defaultLong = false);
|
||||
unsigned char modrm(unsigned short other);
|
||||
unsigned char modrm(Register& other);
|
||||
|
||||
//Multiplies *address with value, stores the result in this register
|
||||
void multiply_signed(MemAddress address, int value);
|
||||
|
||||
//Divides {eax,edx} by this register; result in eax, remainder in edx
|
||||
void divide();
|
||||
void divide_signed();
|
||||
};
|
||||
|
||||
//Converts a MemAddress from the default unsigned <cpu bit mode> to match the passed type
|
||||
template<class T>
|
||||
Register as(Register reg) {
|
||||
reg.bitMode = sizeof(T) * 8;
|
||||
return reg;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{3667F0B1-3E26-407C-841B-9FA2E554CB43}</ProjectGuid>
|
||||
<RootNamespace>os</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>MultiByte</CharacterSet>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(SolutionDir)\..\..\lib\win32\</OutDir>
|
||||
<TargetName>$(ProjectName)d</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(SolutionDir)\..\..\lib\win64\</OutDir>
|
||||
<TargetName>$(ProjectName)64d</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(SolutionDir)\..\..\lib\win32\</OutDir>
|
||||
<TargetName>$(ProjectName)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<TargetExt>.lib</TargetExt>
|
||||
<OutDir>$(SolutionDir)\..\..\lib\win64\</OutDir>
|
||||
<TargetName>$(ProjectName)64</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\include\;..\..\util\include\</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>utild.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
<Lib>
|
||||
<AdditionalLibraryDirectories>..\..\lib\win32\</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\include\;..\..\util\include\</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>util64d.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
<Lib>
|
||||
<AdditionalLibraryDirectories>..\..\lib\win64\</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>..\include\;..\..\util\include\</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>util.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
<Lib>
|
||||
<AdditionalLibraryDirectories>..\..\lib\win32\</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>..\include\;..\..\util\include\</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>util64.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
<Lib>
|
||||
<AdditionalLibraryDirectories>..\..\lib\win64\</AdditionalLibraryDirectories>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\files.h" />
|
||||
<ClInclude Include="..\include\threads.h" />
|
||||
<ClInclude Include="..\include\virtual_asm.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\source\files.cpp" />
|
||||
<ClCompile Include="..\source\files_win.cpp" />
|
||||
<ClCompile Include="..\source\threads.cpp" />
|
||||
<ClCompile Include="..\source\threads_windows.cpp" />
|
||||
<ClCompile Include="..\source\virtual_asm_windows.cpp" />
|
||||
<ClCompile Include="..\source\virtual_asm_x64.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\source\virtual_asm_x86.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\threads.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\files.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\virtual_asm.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\source\threads.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\source\threads_windows.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\source\files.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\source\files_win.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\source\virtual_asm_windows.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\source\virtual_asm_x86.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\source\virtual_asm_x64.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "files.h"
|
||||
#include "str_util.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
std::string getFileContents(const std::string& filename) {
|
||||
std::ifstream str(filename);
|
||||
return std::string((std::istreambuf_iterator<char>(str)),
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
std::string path_up(const std::string& path) {
|
||||
char last = path[path.size() - 1];
|
||||
size_t pos;
|
||||
if(last == '/' || last == '\\')
|
||||
pos = path.substr(0, path.size() - 1).find_last_of("/\\");
|
||||
else
|
||||
pos = path.find_last_of("/\\");
|
||||
if(pos == std::string::npos)
|
||||
return "";
|
||||
return path.substr(0, pos);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
#ifndef WIN_MODE
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/stat.h>
|
||||
#include "limits.h"
|
||||
#include "files.h"
|
||||
#include "str_util.h"
|
||||
#include "threads.h"
|
||||
#include <unordered_map>
|
||||
#ifndef __APPLE__
|
||||
#include <sys/inotify.h>
|
||||
#endif
|
||||
|
||||
std::string getWorkingDirectory() {
|
||||
char buffer[1024];
|
||||
getcwd(buffer, 1024);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
bool setWorkingDirectory(const std::string& dir) {
|
||||
return chdir(dir.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool listDirectory(const std::string& dir, std::vector<std::string>& out, const char* filter) {
|
||||
DIR* dp;
|
||||
struct dirent* de;
|
||||
|
||||
if(!(dp = opendir(dir.c_str())))
|
||||
return false;
|
||||
|
||||
CompiledPattern filterReqs;
|
||||
compile_pattern(filter, filterReqs);
|
||||
|
||||
while((de = readdir(dp))) {
|
||||
if(de->d_name[0] == '.' && de->d_name[1] == '\0')
|
||||
continue;
|
||||
if(de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')
|
||||
continue;
|
||||
if(match(de->d_name, filterReqs))
|
||||
out.push_back(de->d_name);
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string getTemporaryFile() {
|
||||
std::string tmp = getProfileRoot();
|
||||
tmp = path_join(tmp, ".savetmp.XXXXXX");
|
||||
int fd = mkstemp((char*)tmp.c_str());
|
||||
close(fd);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
std::string getAbsolutePath(const std::string& relpath) {
|
||||
char* pth = realpath(relpath.c_str(), 0);
|
||||
if(!pth)
|
||||
return relpath;
|
||||
std::string path(pth);
|
||||
free(pth);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string getDirname(const std::string& filename) {
|
||||
std::string fl(filename);
|
||||
char* dname = dirname(&fl[0]);
|
||||
|
||||
return std::string(dname);
|
||||
}
|
||||
|
||||
std::string getBasename(const std::string& filename, bool includeExtension) {
|
||||
std::string fl(filename);
|
||||
char* bname = basename(&fl[0]);
|
||||
std::string name(bname);
|
||||
|
||||
if(!includeExtension) {
|
||||
auto ext = name.find_last_of('.');
|
||||
if(ext != name.npos)
|
||||
name = name.substr(0,ext);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
bool fileExists(const std::string& filename) {
|
||||
struct stat st;
|
||||
return stat(filename.c_str(), &st) == 0;
|
||||
}
|
||||
|
||||
bool fileWritable(const std::string& filename) {
|
||||
return access(filename.c_str(), W_OK) == 0;
|
||||
}
|
||||
|
||||
bool isDirectory(const std::string& filename) {
|
||||
struct stat st;
|
||||
if(stat(filename.c_str(), &st) != 0)
|
||||
return false;
|
||||
return S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
void makeDirectory(const std::string& path) {
|
||||
mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
}
|
||||
|
||||
std::string getProfileRoot() {
|
||||
std::string path = getenv("HOME");
|
||||
path = path_join(path, ".starruler2");
|
||||
return path;
|
||||
}
|
||||
|
||||
time_t getModifiedTime(const std::string& filename) {
|
||||
struct stat st;
|
||||
stat(filename.c_str(), &st);
|
||||
return st.st_mtime;
|
||||
}
|
||||
|
||||
//Check whether a path is inside another path
|
||||
bool path_inside(const std::string& folder, const std::string& subpath) {
|
||||
std::vector<std::string> subpath_parts;
|
||||
std::string suffix;
|
||||
std::string prefix;
|
||||
if(!subpath.empty() && subpath[0] != '/')
|
||||
prefix = ".";
|
||||
else
|
||||
prefix = "/";
|
||||
|
||||
path_split(subpath, subpath_parts);
|
||||
unsigned cnt = subpath_parts.size();
|
||||
for(unsigned i = 0; i < cnt; ++i) {
|
||||
if(subpath_parts[i].empty())
|
||||
continue;
|
||||
if(fileExists(path_join(prefix, subpath_parts[i])))
|
||||
prefix = path_join(prefix, subpath_parts[i]);
|
||||
else
|
||||
suffix = path_join(suffix, subpath_parts[i]);
|
||||
}
|
||||
|
||||
std::string check = path_join(getAbsolutePath(folder), "");
|
||||
std::string inside = path_join(getAbsolutePath(prefix), suffix);
|
||||
return inside.compare(0, check.size(), check) == 0;
|
||||
}
|
||||
|
||||
//Split path into elements
|
||||
void path_split(const std::string& path, std::vector<std::string>& out) {
|
||||
split(path, out, '/', false, true);
|
||||
}
|
||||
|
||||
//Join path elements
|
||||
std::string path_join(const std::string& one, const std::string& two) {
|
||||
if(one.empty())
|
||||
return two;
|
||||
|
||||
auto endpos = one.find_last_not_of('/');
|
||||
if(endpos != std::string::npos)
|
||||
endpos += 1;
|
||||
auto startpos = two.find_first_not_of('/');
|
||||
if(startpos == std::string::npos)
|
||||
startpos = 0;
|
||||
|
||||
return one.substr(0, endpos) + '/' + two.substr(startpos, std::string::npos);
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
int inotify_fd = -1;
|
||||
threads::Mutex inotify_mtx;
|
||||
|
||||
struct DirectoryMonitor {
|
||||
std::function<void(std::string&)> dircb;
|
||||
std::unordered_map<std::string, std::function<bool()>> callbacks;
|
||||
};
|
||||
std::unordered_map<std::string, DirectoryMonitor*> directory_names;
|
||||
std::unordered_map<int, DirectoryMonitor*> watched_directories;
|
||||
|
||||
const int inotify_buff_size = 16000;
|
||||
const size_t inotify_name_off = (size_t)&((inotify_event*)0)->name;
|
||||
|
||||
threads::threadreturn threadcall inotify_thread(void* arg) {
|
||||
char buffer[inotify_buff_size];
|
||||
|
||||
while(inotify_fd != -1) {
|
||||
size_t ind = 0;
|
||||
int r = read(inotify_fd, buffer, inotify_buff_size);
|
||||
if(r < 0) {
|
||||
//Just read again if interrupted
|
||||
if(errno != EINTR)
|
||||
perror("Error reading from inotify buffer");
|
||||
continue;
|
||||
}
|
||||
|
||||
threads::Lock lock(inotify_mtx);
|
||||
while(ind < (size_t)r) {
|
||||
inotify_event* pevt = (inotify_event*)&buffer[ind];
|
||||
size_t size = inotify_name_off + pevt->len;
|
||||
|
||||
int wd = pevt->wd;
|
||||
auto it = watched_directories.find(wd);
|
||||
if(it == watched_directories.end()) {
|
||||
inotify_rm_watch(inotify_fd, wd);
|
||||
}
|
||||
else {
|
||||
std::string filename(pevt->name);
|
||||
DirectoryMonitor* d = it->second;
|
||||
|
||||
if(d->dircb)
|
||||
d->dircb(filename);
|
||||
auto f = d->callbacks.find(filename);
|
||||
if(f != d->callbacks.end()) {
|
||||
if(!f->second())
|
||||
d->callbacks.erase(f);
|
||||
}
|
||||
}
|
||||
|
||||
ind += size;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void watchDirectory(const std::string& path, std::function<void(std::string&)> callback) {
|
||||
threads::Lock lock(inotify_mtx);
|
||||
if(inotify_fd == -1) {
|
||||
inotify_fd = inotify_init();
|
||||
threads::createThread(inotify_thread, 0);
|
||||
}
|
||||
|
||||
std::string dir = getAbsolutePath(path);
|
||||
|
||||
auto it = directory_names.find(dir);
|
||||
if(it == directory_names.end()) {
|
||||
int wd = inotify_add_watch(inotify_fd, dir.c_str(), IN_CLOSE_WRITE);
|
||||
|
||||
DirectoryMonitor* d = new DirectoryMonitor();
|
||||
watched_directories[wd] = d;
|
||||
directory_names[dir] = d;
|
||||
|
||||
d->dircb = callback;
|
||||
}
|
||||
else {
|
||||
it->second->dircb = callback;
|
||||
}
|
||||
}
|
||||
|
||||
void watchFile(const std::string& path, std::function<bool()> callback) {
|
||||
if(!callback)
|
||||
return;
|
||||
|
||||
threads::Lock lock(inotify_mtx);
|
||||
if(inotify_fd == -1) {
|
||||
inotify_fd = inotify_init();
|
||||
threads::createThread(inotify_thread, 0);
|
||||
}
|
||||
|
||||
std::string dir = getAbsolutePath(getDirname(path));
|
||||
std::string file = getBasename(path);
|
||||
|
||||
auto it = directory_names.find(dir);
|
||||
if(it == directory_names.end()) {
|
||||
int wd = inotify_add_watch(inotify_fd, dir.c_str(), IN_CLOSE_WRITE);
|
||||
|
||||
DirectoryMonitor* d = new DirectoryMonitor();
|
||||
watched_directories[wd] = d;
|
||||
directory_names[dir] = d;
|
||||
|
||||
d->callbacks[file] = callback;
|
||||
}
|
||||
else {
|
||||
it->second->callbacks[file] = callback;
|
||||
}
|
||||
}
|
||||
|
||||
void clearWatches() {
|
||||
threads::Lock lock(inotify_mtx);
|
||||
if(inotify_fd == -1)
|
||||
return;
|
||||
|
||||
for(auto d = watched_directories.begin(); d != watched_directories.end(); ++d) {
|
||||
delete d->second;
|
||||
inotify_rm_watch(inotify_fd, d->first);
|
||||
}
|
||||
directory_names.clear();
|
||||
watched_directories.clear();
|
||||
|
||||
inotify_fd = -1;
|
||||
close(inotify_fd);
|
||||
}
|
||||
|
||||
#else
|
||||
void watchDirectory(const std::string& path, bool (*callback)(void), bool recursive) {
|
||||
//Not supported on Mac yet
|
||||
}
|
||||
|
||||
void watchFile(const std::string& path, std::function<bool()> callback) {
|
||||
//Not supported on Mac yet
|
||||
}
|
||||
|
||||
void clearWatches() {
|
||||
//Not supported on Mac yet
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,283 @@
|
||||
#ifdef _MSC_VER
|
||||
#include <Windows.h>
|
||||
#include <ShlObj.h>
|
||||
#include <Shlwapi.h>
|
||||
|
||||
#include "files.h"
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "str_util.h"
|
||||
|
||||
#include "threads.h"
|
||||
|
||||
bool fileExists(const std::string& path) {
|
||||
return _access(path.c_str(),0) == 0;
|
||||
}
|
||||
|
||||
bool fileWritable(const std::string& path) {
|
||||
return _access(path.c_str(),2) == 0;
|
||||
}
|
||||
|
||||
#include <sys/stat.h>
|
||||
bool isDirectory(const std::string& path) {
|
||||
struct stat info;
|
||||
if(stat(path.c_str(),&info) == 0)
|
||||
return (info.st_mode & _S_IFDIR) != 0;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
//Join path elements
|
||||
std::string path_join(const std::string& one, const std::string& two) {
|
||||
if(one.empty())
|
||||
return two;
|
||||
|
||||
auto endOfPath = one.find_last_not_of("\\/");
|
||||
if(endOfPath != std::string::npos)
|
||||
endOfPath += 1;
|
||||
auto startOfPath = two.find_first_not_of("\\/");
|
||||
if(startOfPath == std::string::npos)
|
||||
startOfPath = 0;
|
||||
|
||||
return one.substr(0,endOfPath) + "/" + two.substr(startOfPath, std::string::npos);
|
||||
}
|
||||
|
||||
//Split path into elements
|
||||
void path_split(const std::string& path, std::vector<std::string>& out) {
|
||||
size_t prev = path.find_first_not_of("\\/");
|
||||
|
||||
while(prev != std::string::npos) {
|
||||
size_t next = path.find_first_of("\\/", prev);
|
||||
out.push_back(path.substr(prev, next - prev));
|
||||
prev = path.find_first_not_of("\\/", next);
|
||||
}
|
||||
}
|
||||
|
||||
//Check whether a path is inside another path
|
||||
bool path_inside(const std::string& folder, const std::string& subpath) {
|
||||
//NOTE: getAbsolutePath normalizes to / to \ on Windows
|
||||
std::string prefix = getAbsolutePath(folder), toPath = getAbsolutePath(subpath);
|
||||
if(toPath.size() < prefix.size())
|
||||
return false;
|
||||
for(unsigned i = 0; i < prefix.size(); ++i)
|
||||
if(prefix[i] != toPath[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//Create a directory
|
||||
void makeDirectory(const std::string& path) {
|
||||
CreateDirectory(path.c_str(),NULL);
|
||||
}
|
||||
|
||||
std::string getProfileRoot() {
|
||||
TCHAR path[MAX_PATH], *profile_folder = "\\My Games\\Star Ruler 2\\";
|
||||
SHGetFolderPath( NULL, CSIDL_MYDOCUMENTS, NULL, 0, path);
|
||||
PathAppend(path, profile_folder);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string getWorkingDirectory() {
|
||||
char buffer[MAX_PATH];
|
||||
_getcwd(buffer, MAX_PATH);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
bool setWorkingDirectory(const std::string& dir) {
|
||||
return _chdir(dir.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool listDirectory(const std::string& dir, std::vector<std::string>& out, const char* filter) {
|
||||
auto full_dir = dir + "\\*";
|
||||
|
||||
CompiledPattern filterReqs;
|
||||
compile_pattern(filter, filterReqs);
|
||||
|
||||
WIN32_FIND_DATA result;
|
||||
auto file = FindFirstFile(full_dir.c_str(), &result);
|
||||
if(file == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
do {
|
||||
if(result.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
if(strcmp(result.cFileName,".") == 0 || strcmp(result.cFileName,"..") == 0)
|
||||
continue;
|
||||
if(match(result.cFileName, filterReqs))
|
||||
out.push_back(result.cFileName);
|
||||
} while(FindNextFile(file, &result) != FALSE);
|
||||
|
||||
FindClose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
void clearTemps() {
|
||||
std::string tempPath = getProfileRoot() + "/temp/";
|
||||
std::vector<std::string> files;
|
||||
|
||||
if(listDirectory(tempPath, files))
|
||||
for(auto i = files.begin(), end = files.end(); i != end; ++i)
|
||||
remove(i->c_str());
|
||||
|
||||
RemoveDirectory(tempPath.c_str());
|
||||
|
||||
auto backup = getProfileRoot() + "temp.TMP";
|
||||
remove(backup.c_str());
|
||||
}
|
||||
|
||||
std::string getTemporaryFile() {
|
||||
static bool first = true;
|
||||
if(first) {
|
||||
atexit(clearTemps);
|
||||
first = false;
|
||||
}
|
||||
|
||||
std::string tempPath = getProfileRoot() + "/temp/";
|
||||
CreateDirectory(tempPath.c_str(), NULL);
|
||||
|
||||
char buffer[MAX_PATH];
|
||||
auto result = GetTempFileName(tempPath.c_str(), "sr2", 0, buffer);
|
||||
if(result != 0)
|
||||
return buffer;
|
||||
else
|
||||
return getProfileRoot() + "temp.TMP";
|
||||
}
|
||||
|
||||
std::string getAbsolutePath(const std::string& relpath) {
|
||||
char buffer[MAX_PATH];
|
||||
auto len = GetFullPathName(relpath.c_str(), MAX_PATH, buffer, 0);
|
||||
return std::string(buffer, len);
|
||||
}
|
||||
|
||||
std::string getBasename(const std::string& filename, bool includeExtension) {
|
||||
auto dir_end = filename.find_last_of("\\/");
|
||||
std::string name = (dir_end == filename.npos) ? filename : filename.substr(dir_end+1);
|
||||
if(includeExtension == false) {
|
||||
auto ext = name.find_last_of('.');
|
||||
if(ext != name.npos)
|
||||
name = name.substr(0,ext);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::string getDirname(const std::string& filename) {
|
||||
auto dir_end = filename.find_last_of("\\/");
|
||||
if(dir_end == filename.npos)
|
||||
return "";
|
||||
else
|
||||
return filename.substr(0,dir_end);
|
||||
}
|
||||
|
||||
threads::Mutex watch_mtx;
|
||||
struct DirectoryMonitor {
|
||||
std::string path;
|
||||
std::function<void(std::string&)> dircb;
|
||||
std::unordered_map<std::string, std::function<bool()>> callbacks;
|
||||
};
|
||||
std::unordered_map<std::string, DirectoryMonitor*> directory_names;
|
||||
const int watch_buff_size = 16000;
|
||||
|
||||
threads::threadreturn threadcall WatchDir(void* arg) {
|
||||
DirectoryMonitor* d = (DirectoryMonitor*)arg;
|
||||
|
||||
HANDLE dir = CreateFile(
|
||||
d->path.c_str(),
|
||||
FILE_LIST_DIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||||
|
||||
DWORD r = 0;
|
||||
char buffer[watch_buff_size];
|
||||
|
||||
while(true) {
|
||||
ReadDirectoryChangesW(
|
||||
dir, &buffer, sizeof(buffer),
|
||||
false, FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||
&r, NULL, NULL);
|
||||
|
||||
threads::Lock lock(watch_mtx);
|
||||
size_t ind = 0;
|
||||
while(ind < r) {
|
||||
FILE_NOTIFY_INFORMATION* pevt = (FILE_NOTIFY_INFORMATION*)&buffer[ind];
|
||||
std::wstring filename_w((wchar_t*)pevt->FileName, pevt->FileNameLength / 2);
|
||||
|
||||
std::string filename; filename.reserve(filename_w.size());
|
||||
|
||||
//Strip UTF16 to ASCII
|
||||
for(auto c = filename_w.cbegin(), end = filename_w.cend(); c != end; ++c)
|
||||
filename.push_back((char)*c & 0x7f);
|
||||
|
||||
if(d->dircb)
|
||||
d->dircb(filename);
|
||||
auto f = d->callbacks.find(filename);
|
||||
if(f != d->callbacks.end()) {
|
||||
if(!f->second())
|
||||
d->callbacks.erase(f);
|
||||
}
|
||||
|
||||
if(pevt->NextEntryOffset == 0)
|
||||
break;
|
||||
ind += pevt->NextEntryOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void watchDirectory(const std::string& path, std::function<void(std::string&)> callback) {
|
||||
threads::Lock lock(watch_mtx);
|
||||
std::string dir = getAbsolutePath(path);
|
||||
|
||||
auto it = directory_names.find(dir);
|
||||
if(it == directory_names.end()) {
|
||||
DirectoryMonitor* d = new DirectoryMonitor();
|
||||
d->path = dir;
|
||||
d->dircb = callback;
|
||||
|
||||
directory_names[dir] = d;
|
||||
|
||||
threads::createThread(WatchDir, d);
|
||||
}
|
||||
else {
|
||||
it->second->dircb = callback;
|
||||
}
|
||||
}
|
||||
|
||||
void watchFile(const std::string& path, std::function<bool()> callback) {
|
||||
if(!callback)
|
||||
return;
|
||||
|
||||
threads::Lock lock(watch_mtx);
|
||||
std::string dir = getAbsolutePath(getDirname(path));
|
||||
std::string file = getBasename(path);
|
||||
|
||||
auto it = directory_names.find(dir);
|
||||
if(it == directory_names.end()) {
|
||||
DirectoryMonitor* d = new DirectoryMonitor();
|
||||
d->path = dir;
|
||||
d->callbacks[file] = callback;
|
||||
|
||||
directory_names[dir] = d;
|
||||
|
||||
threads::createThread(WatchDir, d);
|
||||
}
|
||||
else {
|
||||
it->second->callbacks[file] = callback;
|
||||
}
|
||||
}
|
||||
|
||||
void clearWatches() {
|
||||
threads::Lock lock(watch_mtx);
|
||||
for(auto d = directory_names.begin(); d != directory_names.end(); ++d)
|
||||
delete d->second;
|
||||
directory_names.clear();
|
||||
//TODO: Stop threads
|
||||
}
|
||||
|
||||
|
||||
time_t getModifiedTime(const std::string& filename) {
|
||||
struct stat st;
|
||||
stat(filename.c_str(), &st);
|
||||
return st.st_mtime;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,311 @@
|
||||
#include "threads.h"
|
||||
#include <assert.h>
|
||||
|
||||
namespace threads {
|
||||
|
||||
threadreturn threadcall asyncWrapper(void* pData) {
|
||||
std::function<int()>* pFunc = (std::function<int()>*)pData;
|
||||
|
||||
int r = (*pFunc)();
|
||||
|
||||
delete pFunc;
|
||||
return r;
|
||||
}
|
||||
|
||||
void async(std::function<int()> f) {
|
||||
createThread(asyncWrapper, new std::function<int()>(f));
|
||||
}
|
||||
|
||||
const unsigned Mutex::spinCount = 5;
|
||||
const unsigned ReadWriteMutex::spinCount = 8;
|
||||
const unsigned Signal::spinCount = 3;
|
||||
|
||||
void Mutex::lock() {
|
||||
int id = getThreadID();
|
||||
if(owningThread != id) {
|
||||
owningThread.wait_compare_exchange(id, invalidThreadID, spinCount);
|
||||
lockCount = 1;
|
||||
}
|
||||
else {
|
||||
++lockCount;
|
||||
}
|
||||
|
||||
#ifdef PROFILE_LOCKS
|
||||
++profileCount;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Mutex::try_lock() {
|
||||
int id = getThreadID();
|
||||
if(owningThread != id) {
|
||||
for(unsigned i = 0; i < spinCount; ++i) {
|
||||
if(owningThread.compare_exchange_strong(id, invalidThreadID) == invalidThreadID) {
|
||||
lockCount = 1;
|
||||
#ifdef PROFILE_LOCKS
|
||||
++profileCount;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
++lockCount;
|
||||
#ifdef PROFILE_LOCKS
|
||||
++profileCount;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void Mutex::release() {
|
||||
assert(lockCount > 0);
|
||||
if(--lockCount == 0)
|
||||
owningThread = invalidThreadID;
|
||||
}
|
||||
|
||||
bool Mutex::hasLock() {
|
||||
return owningThread == getThreadID();
|
||||
}
|
||||
|
||||
void atomic_int::wait_compare_exchange(int xchg, int compareTo, const int spinCount) {
|
||||
int spins = 0;
|
||||
while(compare_exchange_strong(xchg, compareTo) != compareTo) {
|
||||
++spins;
|
||||
if(spins == spinCount) {
|
||||
sleep(0);
|
||||
spins = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Threaded(ReadWriteMutex*) rw_locks[8];
|
||||
Threaded(unsigned) rw_lockCounts[8];
|
||||
Threaded(int) rw_head = -1;
|
||||
|
||||
unsigned& get_rw_thread_lockCount(ReadWriteMutex* lock) {
|
||||
for(int i = rw_head; i >= 0; --i)
|
||||
if(rw_locks[i] == lock)
|
||||
return rw_lockCounts[i];
|
||||
++rw_head;
|
||||
rw_locks[rw_head] = lock;
|
||||
rw_lockCounts[rw_head] = 0;
|
||||
assert(rw_head < 8);
|
||||
return rw_lockCounts[rw_head];
|
||||
}
|
||||
|
||||
void remove_rw_thread_lockCount(ReadWriteMutex* lock) {
|
||||
for(int i = rw_head; i >= 0; --i) {
|
||||
if(rw_locks[i] == lock) {
|
||||
for(int j = i; j < rw_head; ++j) {
|
||||
rw_locks[j] = rw_locks[j+1];
|
||||
rw_lockCounts[j] = rw_lockCounts[j+1];
|
||||
}
|
||||
--rw_head;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReadWriteMutex::writeLock() {
|
||||
auto id = getThreadID();
|
||||
unsigned& threadLockCount = get_rw_thread_lockCount(this);
|
||||
if(owningThread != id) {
|
||||
//Acquire right to increase readCount
|
||||
owningThread.wait_compare_exchange(id, invalidThreadID, spinCount);
|
||||
|
||||
//Cannot upgrade locks to write locks, this creates deadlocks
|
||||
if(threadLockCount > 0)
|
||||
throw "Upgrading a read lock is invalid.";
|
||||
|
||||
//Wait until reading threads are done
|
||||
unsigned spins = 0;
|
||||
while(readCount > (int)threadLockCount) {
|
||||
++spins;
|
||||
if(spins == spinCount) {
|
||||
sleep(0);
|
||||
spins = 0;
|
||||
}
|
||||
}
|
||||
|
||||
++readCount;
|
||||
}
|
||||
else {
|
||||
++readCount;
|
||||
}
|
||||
|
||||
++threadLockCount;
|
||||
|
||||
#ifdef PROFILE_LOCKS
|
||||
++profileWriteCount;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ReadWriteMutex::hasLock() {
|
||||
for(int i = rw_head; i >= 0; --i)
|
||||
if(rw_locks[i] == this)
|
||||
return rw_lockCounts[i] != 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReadWriteMutex::hasWriteLock() {
|
||||
return owningThread == getThreadID();
|
||||
}
|
||||
|
||||
void ReadWriteMutex::readLock() {
|
||||
auto id = getThreadID();
|
||||
unsigned& threadLockCount = get_rw_thread_lockCount(this);
|
||||
|
||||
if(owningThread != id && threadLockCount == 0) {
|
||||
owningThread.wait_compare_exchange(id, invalidThreadID, spinCount);
|
||||
++readCount;
|
||||
owningThread = invalidThreadID;
|
||||
}
|
||||
else {
|
||||
++readCount;
|
||||
}
|
||||
|
||||
++threadLockCount;
|
||||
|
||||
#ifdef PROFILE_LOCKS
|
||||
++profileReadCount;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ReadWriteMutex::release() {
|
||||
if(--readCount == 0 && owningThread == getThreadID())
|
||||
owningThread = invalidThreadID;
|
||||
auto& threadLockCount = get_rw_thread_lockCount(this);
|
||||
if(--threadLockCount == 0)
|
||||
remove_rw_thread_lockCount(this);
|
||||
}
|
||||
|
||||
Signal::Signal(int start) : flag(start) {
|
||||
}
|
||||
|
||||
void Signal::signal(int value) {
|
||||
flag.set_basic(value);
|
||||
}
|
||||
|
||||
void Signal::signalDown() {
|
||||
--flag;
|
||||
}
|
||||
|
||||
void Signal::signalUp() {
|
||||
++flag;
|
||||
}
|
||||
|
||||
void Signal::signalDown(int value) {
|
||||
flag -= value;
|
||||
}
|
||||
|
||||
void Signal::signalUp(int value) {
|
||||
flag += value;
|
||||
}
|
||||
|
||||
bool Signal::check(int checkFor) const {
|
||||
return flag == checkFor;
|
||||
}
|
||||
|
||||
bool Signal::checkAndSignal(int waitFor, int newSignal) {
|
||||
return flag.compare_exchange_strong(newSignal, waitFor) == waitFor;
|
||||
}
|
||||
|
||||
void Signal::wait(int waitFor) const {
|
||||
unsigned spins = 0;
|
||||
while(flag != waitFor) {
|
||||
if(spins++ == spinCount) {
|
||||
sleep(1);
|
||||
spins = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Signal::waitNot(int waitForNot) const {
|
||||
unsigned spins = 0;
|
||||
while(flag == waitForNot) {
|
||||
if(spins++ == spinCount) {
|
||||
sleep(1);
|
||||
spins = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Signal::waitAndSignal(int waitFor, int newSignal) {
|
||||
flag.wait_compare_exchange(newSignal, waitFor, spinCount);
|
||||
}
|
||||
|
||||
#ifdef PROFILE_LOCKS
|
||||
std::set<Mutex*>* mutexes = 0;
|
||||
std::set<ReadWriteMutex*>* rwMutexes = 0;
|
||||
Mutex listMutex;
|
||||
|
||||
Mutex::Mutex() : observed(false) {
|
||||
if(this != &listMutex) {
|
||||
threads::Lock lock(listMutex);
|
||||
if(!mutexes)
|
||||
mutexes = new std::set<Mutex*>();
|
||||
mutexes->insert(this);
|
||||
}
|
||||
|
||||
char buff[256];
|
||||
sprintf(buff, "Mutex %p", this);
|
||||
name = buff;
|
||||
}
|
||||
|
||||
Mutex::Mutex(const char* mtxName) : observed(false) {
|
||||
if(this != &listMutex) {
|
||||
threads::Lock lock(listMutex);
|
||||
if(!mutexes)
|
||||
mutexes = new std::set<Mutex*>();
|
||||
mutexes->insert(this);
|
||||
}
|
||||
|
||||
name = mtxName;
|
||||
}
|
||||
|
||||
Mutex::~Mutex() {
|
||||
if(this != &listMutex) {
|
||||
threads::Lock lock(listMutex);
|
||||
mutexes->erase(this);
|
||||
}
|
||||
}
|
||||
|
||||
ReadWriteMutex::ReadWriteMutex() : observed(false) {
|
||||
{
|
||||
threads::Lock lock(listMutex);
|
||||
if(!rwMutexes)
|
||||
rwMutexes = new std::set<ReadWriteMutex*>();
|
||||
rwMutexes->insert(this);
|
||||
}
|
||||
|
||||
char buff[256];
|
||||
sprintf(buff, "ReadWriteMutex %p", this);
|
||||
name = buff;
|
||||
}
|
||||
|
||||
ReadWriteMutex::~ReadWriteMutex() {
|
||||
threads::Lock lock(listMutex);
|
||||
rwMutexes->erase(this);
|
||||
}
|
||||
|
||||
void profileMutexCycle(std::function<void(Mutex*)> cb) {
|
||||
threads::Lock lock(listMutex);
|
||||
for(auto it = mutexes->begin(), end = mutexes->end(); it != end; ++it) {
|
||||
if(cb)
|
||||
cb(*it);
|
||||
(*it)->profileCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void profileReadWriteMutexCycle(std::function<void(ReadWriteMutex*)> cb) {
|
||||
threads::Lock lock(listMutex);
|
||||
for(auto it = rwMutexes->begin(), end = rwMutexes->end(); it != end; ++it) {
|
||||
if(cb)
|
||||
cb(*it);
|
||||
(*it)->profileReadCount = 0;
|
||||
(*it)->profileWriteCount = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
@@ -0,0 +1,159 @@
|
||||
#include "threads.h"
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace threads {
|
||||
|
||||
atomic_int nextThreadID(2);
|
||||
Threaded(int) threadID = 1;
|
||||
|
||||
const int invalidThreadID = 0;
|
||||
|
||||
int getThreadID() {
|
||||
return threadID;
|
||||
}
|
||||
|
||||
struct threadInfo {
|
||||
pthread_t thread;
|
||||
threadfunc func;
|
||||
void* arg;
|
||||
};
|
||||
|
||||
void* startThread(void* arg) {
|
||||
threadInfo* info = (threadInfo*)arg;
|
||||
threadID = nextThreadID++;
|
||||
|
||||
info->func(info->arg);
|
||||
|
||||
delete info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setThreadPriority(ThreadPriority priority) {
|
||||
//NOT IMPLEMENTED
|
||||
}
|
||||
|
||||
void createThread(threadfunc func, void* arg) {
|
||||
threadInfo* info = new threadInfo();
|
||||
info->func = func;
|
||||
info->arg = arg;
|
||||
|
||||
pthread_create(&info->thread, 0, &startThread, info);
|
||||
}
|
||||
|
||||
pthread_mutex_t sleepMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_cond_t sleepCond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
unsigned getNumberOfProcessors() {
|
||||
return sysconf(_SC_NPROCESSORS_ONLN);
|
||||
}
|
||||
|
||||
void sleep(unsigned int milliseconds) {
|
||||
struct timespec wait;
|
||||
wait.tv_sec = (milliseconds / 1000);
|
||||
wait.tv_nsec = (milliseconds % 1000) * 1000 * 1000;
|
||||
|
||||
nanosleep(&wait, 0);
|
||||
}
|
||||
|
||||
void idle() {
|
||||
#ifdef __APPLE__
|
||||
sleep(1);
|
||||
#else
|
||||
sleep(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int atomic_int::operator++() {
|
||||
return __sync_add_and_fetch(&value, 1);
|
||||
}
|
||||
|
||||
int atomic_int::operator++(int) {
|
||||
return __sync_fetch_and_add(&value, 1);
|
||||
}
|
||||
|
||||
int atomic_int::operator--() {
|
||||
return __sync_add_and_fetch(&value, -1);
|
||||
}
|
||||
|
||||
int atomic_int::operator--(int) {
|
||||
return __sync_fetch_and_add(&value, -1);
|
||||
}
|
||||
|
||||
int atomic_int::operator+=(int _add) {
|
||||
return __sync_fetch_and_add(&value, _add);
|
||||
}
|
||||
|
||||
int atomic_int::operator-=(int _sub) {
|
||||
return __sync_fetch_and_sub(&value, _sub);
|
||||
}
|
||||
|
||||
void atomic_int::operator=(int _value) {
|
||||
value = _value;
|
||||
}
|
||||
|
||||
int atomic_int::operator&=(int _value) {
|
||||
return __sync_and_and_fetch(&value, _value);
|
||||
}
|
||||
|
||||
int atomic_int::operator|=(int _value) {
|
||||
return __sync_or_and_fetch(&value, _value);
|
||||
}
|
||||
|
||||
int atomic_int::exchange(int _xchg) {
|
||||
return __sync_lock_test_and_set(&value, _xchg);
|
||||
}
|
||||
|
||||
int atomic_int::compare_exchange_strong(int _xchg, int compareTo) {
|
||||
return __sync_val_compare_and_swap(&value, compareTo, _xchg);
|
||||
}
|
||||
|
||||
int atomic_int::get_basic() {
|
||||
return value;
|
||||
}
|
||||
|
||||
void atomic_int::set_basic(int val) {
|
||||
value = val;
|
||||
}
|
||||
|
||||
_threadlocalPointer::_threadlocalPointer() {
|
||||
pthread_key_create(&key, 0);
|
||||
}
|
||||
|
||||
void _threadlocalPointer::set(void* ptr) {
|
||||
pthread_setspecific(key, ptr);
|
||||
}
|
||||
|
||||
void* _threadlocalPointer::get() {
|
||||
return pthread_getspecific(key);
|
||||
}
|
||||
|
||||
_threadlocalPointer::~_threadlocalPointer() {
|
||||
pthread_key_delete(key);
|
||||
}
|
||||
|
||||
int swap(int* ptr, int newval) {
|
||||
return __sync_lock_test_and_set(ptr, newval);
|
||||
}
|
||||
|
||||
int compare_and_swap(int* ptr, int oldval, int newval) {
|
||||
return __sync_val_compare_and_swap(ptr, oldval, newval);
|
||||
}
|
||||
|
||||
long long swap(long long* ptr, long long newval) {
|
||||
return __sync_lock_test_and_set(ptr, newval);
|
||||
}
|
||||
|
||||
long long compare_and_swap(long long* ptr, long long oldval, long long newval) {
|
||||
return __sync_val_compare_and_swap(ptr, oldval, newval);
|
||||
}
|
||||
|
||||
void* swap(void** ptr, void* newval) {
|
||||
return __sync_lock_test_and_set(ptr, newval);
|
||||
}
|
||||
|
||||
void* compare_and_swap(void** ptr, void* oldval, void* newval) {
|
||||
return __sync_val_compare_and_swap(ptr, oldval, newval);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,154 @@
|
||||
#include "threads.h"
|
||||
#include <Windows.h>
|
||||
#include <intrin.h>
|
||||
|
||||
namespace threads {
|
||||
|
||||
atomic_int nextThreadID(1);
|
||||
const int invalidThreadID = 0;
|
||||
Threaded(int) threadID = invalidThreadID;
|
||||
|
||||
int getThreadID() {
|
||||
if(threadID == invalidThreadID) {
|
||||
threadID = nextThreadID++;
|
||||
return threadID;
|
||||
}
|
||||
else {
|
||||
return threadID;
|
||||
}
|
||||
}
|
||||
|
||||
void setThreadPriority(ThreadPriority priority) {
|
||||
int priority_val;
|
||||
switch(priority) {
|
||||
case TP_High:
|
||||
priority_val = THREAD_PRIORITY_ABOVE_NORMAL; break;
|
||||
case TP_Normal: default:
|
||||
priority_val = THREAD_PRIORITY_NORMAL; break;
|
||||
case TP_Low:
|
||||
priority_val = THREAD_PRIORITY_BELOW_NORMAL; break;
|
||||
}
|
||||
|
||||
SetThreadPriority(GetCurrentThread(), priority_val);
|
||||
}
|
||||
|
||||
void createThread(unsigned long threadcall entry(void*), void* arg) {
|
||||
CreateThread(NULL, 0, entry, arg, 0, NULL);
|
||||
}
|
||||
|
||||
int atomic_int::operator++() {
|
||||
return _InterlockedIncrement(&value);
|
||||
}
|
||||
|
||||
int atomic_int::operator++(int) {
|
||||
return (int)_InterlockedIncrement(&value) - 1;
|
||||
}
|
||||
|
||||
int atomic_int::operator--() {
|
||||
return (int)_InterlockedDecrement(&value);
|
||||
}
|
||||
|
||||
int atomic_int::operator--(int) {
|
||||
return (int)_InterlockedDecrement(&value) + 1;
|
||||
}
|
||||
|
||||
int atomic_int::operator+=(int _add) {
|
||||
return (int)_InterlockedExchangeAdd(&value, (long)_add);
|
||||
}
|
||||
|
||||
int atomic_int::operator-=(int _sub) {
|
||||
return (int)_InterlockedExchangeAdd(&value, -(long)_sub);
|
||||
}
|
||||
|
||||
int atomic_int::operator&=(int mask) {
|
||||
return (int)_InterlockedAnd(&value, (long)mask);
|
||||
}
|
||||
|
||||
int atomic_int::operator|=(int mask) {
|
||||
return (int)_InterlockedOr(&value, (long)mask);
|
||||
}
|
||||
|
||||
void atomic_int::operator=(int _value) {
|
||||
value = _value;
|
||||
}
|
||||
|
||||
int atomic_int::exchange(int _xchg) {
|
||||
return (int)_InterlockedExchange(&value, (long)_xchg);
|
||||
}
|
||||
|
||||
int atomic_int::compare_exchange_strong(int _xchg, int compareTo) {
|
||||
return (int)_InterlockedCompareExchange(&value, (long)_xchg, (long)compareTo);
|
||||
}
|
||||
|
||||
int atomic_int::get_basic() {
|
||||
return value;
|
||||
}
|
||||
|
||||
void atomic_int::set_basic(int val) {
|
||||
value = val;
|
||||
}
|
||||
|
||||
_threadlocalPointer::_threadlocalPointer() : index(TlsAlloc()) {
|
||||
}
|
||||
|
||||
_threadlocalPointer::~_threadlocalPointer() {
|
||||
TlsFree(index);
|
||||
}
|
||||
|
||||
void _threadlocalPointer::set(void* ptr) {
|
||||
TlsSetValue(index, ptr);
|
||||
}
|
||||
|
||||
void* _threadlocalPointer::get() {
|
||||
return TlsGetValue(index);
|
||||
}
|
||||
|
||||
unsigned getNumberOfProcessors() {
|
||||
SYSTEM_INFO info;
|
||||
GetSystemInfo(&info);
|
||||
return info.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
void sleep(unsigned int milliseconds) {
|
||||
Sleep(milliseconds);
|
||||
}
|
||||
|
||||
void idle() {
|
||||
Sleep(1);
|
||||
}
|
||||
|
||||
int swap(int* ptr, int newval) {
|
||||
return (int)_InterlockedExchange((long*)ptr, (long)newval);
|
||||
}
|
||||
|
||||
int compare_and_swap(int* ptr, int oldval, int newval) {
|
||||
return (int)_InterlockedCompareExchange((long*)ptr, (long)newval, (long)oldval);
|
||||
}
|
||||
|
||||
//long long swap(long long* ptr, long long newval) {
|
||||
// return _InterlockedExchange64(ptr, newval);
|
||||
//}
|
||||
|
||||
long long compare_and_swap(long long* ptr, long long oldval, long long newval) {
|
||||
return _InterlockedCompareExchange64(ptr, newval, oldval);
|
||||
}
|
||||
|
||||
#ifdef _M_AMD64
|
||||
void* swap(void** ptr, void* newval) {
|
||||
return (void*)_InterlockedExchange64((long long*)ptr, (long long)newval);
|
||||
}
|
||||
|
||||
void* compare_and_swap(void** ptr, void* oldval, void* newval) {
|
||||
return (void*)_InterlockedCompareExchange64((long long*)ptr, (long long)newval, (long long)oldval);
|
||||
}
|
||||
#else
|
||||
void* swap(void** ptr, void* newval) {
|
||||
return (void*)_InterlockedExchange((long*)ptr, (long)newval);
|
||||
}
|
||||
|
||||
void* compare_and_swap(void** ptr, void* oldval, void* newval) {
|
||||
return (void*)_InterlockedCompareExchange((long*)ptr, (long)newval, (long)oldval);
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
@@ -0,0 +1,155 @@
|
||||
#include "virtual_asm.h"
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
//OSX has MAP_ANON
|
||||
#ifndef MAP_ANONYMOUS
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#endif
|
||||
|
||||
namespace assembler {
|
||||
|
||||
unsigned Processor::maxIntArgs64() {
|
||||
return 6;
|
||||
}
|
||||
|
||||
unsigned Processor::maxFloatArgs64() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
bool Processor::isIntArg64Register(unsigned char number, unsigned char arg) {
|
||||
return number < 6;
|
||||
}
|
||||
|
||||
bool Processor::isFloatArg64Register(unsigned char number, unsigned char arg) {
|
||||
return number < 8;
|
||||
}
|
||||
|
||||
Register Processor::intArg64(unsigned char number, unsigned char arg) {
|
||||
switch(number) {
|
||||
case 0:
|
||||
return Register(*this, EDI);
|
||||
case 1:
|
||||
return Register(*this, ESI);
|
||||
case 2:
|
||||
return Register(*this, EDX);
|
||||
case 3:
|
||||
return Register(*this, ECX);
|
||||
case 4:
|
||||
return Register(*this, R8);
|
||||
case 5:
|
||||
return Register(*this, R9);
|
||||
default:
|
||||
throw "Integer64 argument index out of bounds";
|
||||
}
|
||||
}
|
||||
|
||||
Register Processor::floatArg64(unsigned char number, unsigned char arg) {
|
||||
switch(number) {
|
||||
case 0:
|
||||
return Register(*this, XMM0);
|
||||
case 1:
|
||||
return Register(*this, XMM1);
|
||||
case 2:
|
||||
return Register(*this, XMM2);
|
||||
case 3:
|
||||
return Register(*this, XMM3);
|
||||
case 4:
|
||||
return Register(*this, XMM4);
|
||||
case 5:
|
||||
return Register(*this, XMM5);
|
||||
case 6:
|
||||
return Register(*this, XMM6);
|
||||
case 7:
|
||||
return Register(*this, XMM7);
|
||||
default:
|
||||
throw "Float64 argument index out of bounds";
|
||||
}
|
||||
}
|
||||
|
||||
Register Processor::intArg64(unsigned char number, unsigned char arg, Register defaultReg) {
|
||||
if(isIntArg64Register(number, arg))
|
||||
return intArg64(number, arg);
|
||||
return defaultReg;
|
||||
}
|
||||
|
||||
Register Processor::floatArg64(unsigned char number, unsigned char arg, Register defaultReg) {
|
||||
if(isFloatArg64Register(number, arg))
|
||||
return floatArg64(number, arg);
|
||||
return defaultReg;
|
||||
}
|
||||
|
||||
Register Processor::intReturn64() {
|
||||
return Register(*this, EAX);
|
||||
}
|
||||
|
||||
Register Processor::floatReturn64() {
|
||||
return Register(*this, XMM0);
|
||||
}
|
||||
|
||||
CodePage::CodePage(unsigned int Size, void* requestedStart) : used(0), final(false), references(1) {
|
||||
unsigned minPageSize = getMinimumPageSize();
|
||||
unsigned pages = Size / minPageSize;
|
||||
|
||||
if(Size % minPageSize != 0)
|
||||
pages += 1;
|
||||
|
||||
size_t reqptr = (size_t)requestedStart;
|
||||
if(reqptr % minPageSize != 0)
|
||||
reqptr -= (reqptr % minPageSize);
|
||||
|
||||
page = mmap(
|
||||
(void*)reqptr,
|
||||
Size,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE,
|
||||
0,
|
||||
0);
|
||||
|
||||
size = pages * minPageSize;
|
||||
}
|
||||
|
||||
void CodePage::grab() {
|
||||
++references;
|
||||
}
|
||||
|
||||
void CodePage::drop() {
|
||||
if(--references == 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
CodePage::~CodePage() {
|
||||
munmap(page, size);
|
||||
}
|
||||
|
||||
void CodePage::finalize() {
|
||||
mprotect(page, size, PROT_READ | PROT_EXEC);
|
||||
final = true;
|
||||
}
|
||||
|
||||
unsigned int CodePage::getMinimumPageSize() {
|
||||
return getpagesize();
|
||||
}
|
||||
|
||||
void CriticalSection::enter() {
|
||||
pthread_mutex_lock((pthread_mutex_t*)pLock);
|
||||
}
|
||||
|
||||
void CriticalSection::leave() {
|
||||
pthread_mutex_unlock((pthread_mutex_t*)pLock);
|
||||
}
|
||||
|
||||
CriticalSection::CriticalSection() {
|
||||
pthread_mutex_t* mutex = new pthread_mutex_t();
|
||||
pthread_mutex_init(mutex, 0);
|
||||
|
||||
pLock = mutex;
|
||||
}
|
||||
CriticalSection::~CriticalSection() {
|
||||
pthread_mutex_t* mutex = (pthread_mutex_t*)pLock;
|
||||
pthread_mutex_destroy(mutex);
|
||||
delete mutex;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,143 @@
|
||||
#include "virtual_asm.h"
|
||||
#include <Windows.h>
|
||||
|
||||
namespace assembler {
|
||||
|
||||
unsigned Processor::maxIntArgs64() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
unsigned Processor::maxFloatArgs64() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
bool Processor::isIntArg64Register(unsigned char number, unsigned char arg) {
|
||||
return arg < 4;
|
||||
}
|
||||
|
||||
bool Processor::isFloatArg64Register(unsigned char number, unsigned char arg) {
|
||||
return arg < 4;
|
||||
}
|
||||
|
||||
Register Processor::intArg64(unsigned char number, unsigned char arg) {
|
||||
switch(arg) {
|
||||
case 0:
|
||||
return Register(*this, ECX);
|
||||
case 1:
|
||||
return Register(*this, EDX);
|
||||
case 2:
|
||||
return Register(*this, R8);
|
||||
case 3:
|
||||
return Register(*this, R9);
|
||||
default:
|
||||
throw "Integer64 argument index out of bounds";
|
||||
}
|
||||
}
|
||||
|
||||
Register Processor::floatArg64(unsigned char number, unsigned char arg) {
|
||||
switch(arg) {
|
||||
case 0:
|
||||
return Register(*this, XMM0);
|
||||
case 1:
|
||||
return Register(*this, XMM1);
|
||||
case 2:
|
||||
return Register(*this, XMM2);
|
||||
case 3:
|
||||
return Register(*this, XMM3);
|
||||
default:
|
||||
throw "Float64 argument index out of bounds";
|
||||
}
|
||||
}
|
||||
|
||||
Register Processor::intArg64(unsigned char number, unsigned char arg, Register defaultReg) {
|
||||
if(isIntArg64Register(number, arg))
|
||||
return intArg64(number, arg);
|
||||
return defaultReg;
|
||||
}
|
||||
|
||||
Register Processor::floatArg64(unsigned char number, unsigned char arg, Register defaultReg) {
|
||||
if(isFloatArg64Register(number, arg))
|
||||
return floatArg64(number, arg);
|
||||
return defaultReg;
|
||||
}
|
||||
|
||||
Register Processor::intReturn64() {
|
||||
return Register(*this, EAX);
|
||||
}
|
||||
|
||||
Register Processor::floatReturn64() {
|
||||
return Register(*this, XMM0);
|
||||
}
|
||||
|
||||
CodePage::CodePage(unsigned int Size, void* requestedStart) : used(0), final(false), references(1) {
|
||||
SYSTEM_INFO info;
|
||||
GetSystemInfo(&info);
|
||||
|
||||
unsigned minPageSize = info.dwPageSize;
|
||||
size_t pageStep = (size_t)info.dwAllocationGranularity * 2;
|
||||
if((size_t)Size > pageStep)
|
||||
pageStep = (size_t)Size;
|
||||
|
||||
unsigned pages = Size / minPageSize;
|
||||
if(Size % minPageSize != 0)
|
||||
pages += 1;
|
||||
|
||||
size = (pages * minPageSize) - 2;
|
||||
|
||||
//Search for progressively more distant possible page locations, then just get any available one
|
||||
for(int i = 1; i < 256; ++i) {
|
||||
void* request = (char*)requestedStart + i*pageStep;
|
||||
page = VirtualAlloc(request, size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||
if(page != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
page = VirtualAlloc(0, size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
|
||||
}
|
||||
|
||||
void CodePage::grab() {
|
||||
++references;
|
||||
}
|
||||
|
||||
void CodePage::drop() {
|
||||
if(--references == 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
CodePage::~CodePage() {
|
||||
VirtualFree(page,0,MEM_RELEASE);
|
||||
}
|
||||
|
||||
void CodePage::finalize() {
|
||||
FlushInstructionCache(GetCurrentProcess(),page,size);
|
||||
DWORD oldProtect = PAGE_EXECUTE_READWRITE;
|
||||
VirtualProtect(page,size,PAGE_EXECUTE_READ,&oldProtect);
|
||||
final = true;
|
||||
}
|
||||
|
||||
unsigned int CodePage::getMinimumPageSize() {
|
||||
SYSTEM_INFO info;
|
||||
GetSystemInfo(&info);
|
||||
return info.dwPageSize;
|
||||
}
|
||||
|
||||
|
||||
void CriticalSection::enter() {
|
||||
EnterCriticalSection((CRITICAL_SECTION*)pLock);
|
||||
}
|
||||
|
||||
void CriticalSection::leave() {
|
||||
LeaveCriticalSection((CRITICAL_SECTION*)pLock);
|
||||
}
|
||||
|
||||
CriticalSection::CriticalSection() {
|
||||
auto* section = new CRITICAL_SECTION;
|
||||
InitializeCriticalSection(section);
|
||||
pLock = section;
|
||||
}
|
||||
CriticalSection::~CriticalSection() {
|
||||
DeleteCriticalSection((CRITICAL_SECTION*)pLock);
|
||||
delete (CRITICAL_SECTION*)pLock;
|
||||
}
|
||||
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user