diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f5aecec6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +Build_CompilerWorkingDirectory/* +Build_Developers/* +Build_Ship/* +Build_Internal/* +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +*.licenseheader +*.dll +*.exe +*.obj +*.so +*.dynlib +*.lib +*.d +*.o +*.out +.vs \ No newline at end of file diff --git a/Aurora.json b/Aurora.json new file mode 100644 index 00000000..2ce19cc2 --- /dev/null +++ b/Aurora.json @@ -0,0 +1,20 @@ +{ + "type": "aurora", + "name": "AuroraRuntime", + "dllexport": ["AURORA_ENGINE_KERNEL_EXPORT"], + "dllimport": [], + "impDefines": ["AURORA_ENGINE_KERNEL"], + "staticImpDefines": "AURORA_ENGINE_KERNEL_STATIC", + "defines": [], + "soft-depends": ["wxwidgets"], + "depends": ["glm", "asio", "mimalloc", "uuid", "fmt", "json", "bzip2", "ltc", "o1heap", "zstd", "zlib", "lz4", "mbedtls"], + "features": ["remove-platform-code", "guess-platform-code"], + "actions": [ + { + "if": "win32", + "then": { + "links": "Bcrypt.lib" + } + } + ] +} \ No newline at end of file diff --git a/Include/Aurora/Async/Async.hpp b/Include/Aurora/Async/Async.hpp new file mode 100644 index 00000000..406c8930 --- /dev/null +++ b/Include/Aurora/Async/Async.hpp @@ -0,0 +1,319 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Async.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Async +{ + class IWorkItem; + class IAsyncApp; + + using AVoid = AuUInt8; + + AUKN_SYM IAsyncApp *GetAsyncApp(); + + using ThreadGroup_t = AuUInt8; + using ThreadId_t = AuUInt16; + + using WorkerId_t = std::pair; + using DispatchTarget_t = std::pair>; + + struct IWorkItemHandler + { + enum class EProcessNext + { + eInvalid = -1, + eFinished = 0, + eRerun, + eSchedule, + eFailed + }; + + struct ProcessInfo + { + ProcessInfo(bool finished) : type(finished ? EProcessNext::eFinished : EProcessNext::eFailed) {} + ProcessInfo(EProcessNext type) : type(type) {} + ProcessInfo(const AuList> &blockedBy) : type(EProcessNext::eSchedule), waitFor(blockedBy) {} + // ... + + EProcessNext type; + AuList> waitFor; + AuUInt32 reschedMs; + //AuUInt64 reschedNs; + }; + + virtual void DispatchFrame(ProcessInfo &info) = 0; + virtual void Shutdown() = 0; + }; + + + template + struct FJob + { + std::function onSuccess = 0; + std::function onFailure = 0; + }; + + template + struct CJob + { + void(* onSuccess)(const Info_t &, const Result_t &); // + void(* onFailure)(const Info_t &, bool taskNeverDispatched); // called from caller thread if taskNeverDispatched + }; + + template + struct FTask + { + std::function(const Info_t &)> onFrame = 0; + }; + + template + struct CTask + { + std::optional(* onFrame)(const Info_t &); + }; + + + class IWorkItem + { + public: + virtual void WaitFor(const AuSPtr &workItem) = 0; + virtual void WaitFor(const AuList> &workItem) = 0; + virtual void SetSchedTime(AuUInt32 ms) = 0; + + virtual void Dispatch() = 0; + + virtual bool BlockUntilComplete() = 0; + virtual bool HasFinished() = 0; + virtual bool HasFailed() = 0; + }; + + + AUKN_SYM AuSPtr NewWorkItem(const DispatchTarget_t &worker, const AuSPtr &task, bool supportsBlocking = false); + + struct BasicWorkStdFunc : IWorkItemHandler + { + std::function callback; + std::function error; + + BasicWorkStdFunc(std::function &&callback, std::function &&error) : callback(std::move(callback)), error(std::move(error)) + {} + + BasicWorkStdFunc(std::function &&callback) : callback(std::move(callback)) + {} + + BasicWorkStdFunc(const std::function &callback) : callback(callback) + {} + + BasicWorkStdFunc(const std::function &callback, const std::function &error) : callback(callback), error(error) + {} + + private: + + void DispatchFrame(ProcessInfo &info) override + { + try + { + callback(); + } + catch (...) + { + Debug::PrintError(); + } + } + + void Shutdown() override + { + try + { + error(); + } + catch (...) + { + Debug::PrintError(); + } + } + }; + + template, typename Job_t = FJob> + struct BasicWorkCallback : IWorkItemHandler, std::enable_shared_from_this + { + BasicWorkCallback() + { + caller = GetAsyncApp()->GetCurrentThread(); + } + + Task_t task; + Job_t callback; + + Info_t input; + + private: + + static constexpr bool IsCallbackPtr = std::is_pointer_v || is_base_of_template; + static constexpr bool IsTaskPtr = std::is_pointer_v || is_base_of_template; + + WorkerId_t caller; + + void DispatchFrame(ProcessInfo &info) override + { + std::optional ret; + + if constexpr (IsTaskPtr) + { + ret = task->onFrame(input); + } + else + { + ret = task.onFrame(input); + } + + auto pin = this->shared_from_this(); + + std::function func = [ret, &callback, &input, pin]() + { + try + { + if (ret.has_value()) + { + if constexpr (IsCallbackPtr) + { + callback->onSuccess(input, *ret); + } + else + { + callback.onSuccess(input, *ret); + } + } + else + { + if constexpr (IsCallbackPtr) + { + callback->onFailure(input, false); + } + else + { + callback.onFailure(input, false); + } + } + } + catch (...) + { + Debug::PrintError(); + } + }; + + try + { + if (caller == GetAsyncApp()->GetCurrentThread()) + { + func(); + } + else + { + std::function err = [&callback, &input, pin]() + { + // TODO: we shold probably pass: is caller thread: false, has finished: true + if constexpr (IsCallbackPtr) + { + callback->onFailure(input, false); + } + else + { + callback.onFailure(input, false); + } + }; + + // TODO: this is somewhat evil. double alloc when we could reuse this + WorkItemShared(caller, std::make_shared(func, err))->Dispatch(); + } + } + catch (...) + { + Debug::PrintError(); + Shutdown(); + } + } + + void Shutdown() override + { + try + { + if constexpr (IsCallbackPtr) + { + callback->onFailure(true); + } + else + { + callback.onFailure(true); + } + } + catch (...) + { + Debug::PrintError(); + } + } + }; + + template, typename Cleanup_t = std::function> + struct WorkItemCallabale : IWorkItemHandler + { + Frame_t frame; + Cleanup_t cleanup; + + private: + void DispatchFrame(ProcessInfo &info) override + { + frame(); + info.type = IWorkItemHandler::EProcessNext::eFinished; + } + + void Shutdown() override + { + cleanup(); + } + }; + + template + static void TranslateAsyncFunctionToDispatcher() + { + + } + + class IAsyncApp + { + public: + // Main thread logic + virtual void Main() = 0; + virtual void Shutdown() = 0; + virtual bool Exiting() = 0; + + // Spawning + virtual bool Spawn(WorkerId_t) = 0; + virtual Threading::Threads::ThreadShared_t ResolveHandle(WorkerId_t) = 0; + virtual AuBST> GetThreads() = 0; + virtual WorkerId_t GetCurrentThread() = 0; + + // Synchronization + virtual bool Sync(ThreadGroup_t group, bool requireSignal = false, AuUInt32 timeout = 0) = 0; + virtual void Signal(ThreadGroup_t group) = 0; + + virtual bool WaitFor(DispatchTarget_t unlocker, Threading::IWaitable *primitive, int ms) = 0; // when unlocker = this, pump event loop + + virtual bool SyncTimeout(ThreadGroup_t group, AuUInt32 ms) = 0; + + virtual void SyncAllSafe() = 0; + + // Features + virtual void AddFeature(WorkerId_t id, AuSPtr feature, bool async = false) = 0; + + // Debug + virtual void AssertInThreadGroup(ThreadGroup_t thread) = 0; + virtual void AssertWorker(WorkerId_t id) = 0; + }; + +} \ No newline at end of file diff --git a/Include/Aurora/Compression/BasicCompression.hpp b/Include/Aurora/Compression/BasicCompression.hpp new file mode 100644 index 00000000..c133a695 --- /dev/null +++ b/Include/Aurora/Compression/BasicCompression.hpp @@ -0,0 +1,31 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BasicCompression.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Compression +{ + /** + Compresses an in memory blob with zstandard + */ + AUKN_SYM bool Compress(const void *buffer, AuUInt32 length, AuList &out, int compressionLevel = 3); + + /** + Compresses an in memory blob with zstandard + */ + AUKN_SYM bool Compress(const AuList &in, AuList &out, int compressionLevel = 3); + + /** + Decompresses an in memory blob with zstandard + */ + AUKN_SYM bool Decompress(const void *buffer, AuUInt32 length, AuList &out); + + /** + Decompresses an in memory blob with zstandard + */ + AUKN_SYM bool Decompress(const AuList &in, AuList &out); +} \ No newline at end of file diff --git a/Include/Aurora/Compression/Compression.hpp b/Include/Aurora/Compression/Compression.hpp new file mode 100644 index 00000000..2bb6ab2d --- /dev/null +++ b/Include/Aurora/Compression/Compression.hpp @@ -0,0 +1,36 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Compression.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +/* + Ballpark figures of real world performance: + https://www.gaia-gis.it/fossil/librasterlite2/wiki?name=benchmarks+(2019+update) + RELATIVE + Useful for compression ratio variation and realistic igors lzma decompression perf values + + 9th gen intel benchmark figures in MB/s: + https://github.com/lz4/lz4 + + Similar to above, i7 bin: + https://facebook.github.io/zstd/ + + LZMA -> Large packages. Sony conures. (Reliable 2.3 - 2.5. Scales all the way up to 20+. Extremely slow but scalable) + LZ4 -> Network compression and other large data streams (~4.5GB/s, 2.884 compression ratio) + ZSTD -> Standard use (~1.5GB/s to 2.5GB/s, respective compression ratios 2.4 and 2.1. Can be pushed to ~10 at around 750MB/s. Great general use. ) + ZIP -> Zlib has a disgusting decompression upper limit of around 450MB/s for 2.7 +*/ + +#include "BasicCompression.hpp" + +#include "ECompresionType.hpp" +#include "CompressionInfo.hpp" + +#include "StreamPipeProcessor.hpp" + +#include "ICompressionStream.hpp" +#include "StreamProcessor.hpp" \ No newline at end of file diff --git a/Include/Aurora/Compression/CompressionInfo.hpp b/Include/Aurora/Compression/CompressionInfo.hpp new file mode 100644 index 00000000..ca71512d --- /dev/null +++ b/Include/Aurora/Compression/CompressionInfo.hpp @@ -0,0 +1,34 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CompressionInfo.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Compression +{ + struct CompressionInfo + { + ECompresionType type; + + /// ZSTD: -5 <= level <= 22 + /// recommended: ZSTD_CLEVEL_DEFAULT + /// LZMA: 0 <= level <= 9 + /// LZ4 : N/A + /// ZLIB: 0 <= x >= 9 + /// recommended: 6 + /// BZIP: 0 <= x >= 9 + AuInt8 compressionLevel{}; + + /// LZMA: 5 <= fb <= 273, default = 32 + AuUInt32 fbWordSize{}; + + /// LZMA only + /// (1 << 12) <= dictSize <= (1 << 27) for 32-bit version + /// (1 << 12) <= dictSize <= (3 << 29) for 64-bit version + /// default = (1 << 24) + AuUInt32 dictSize{}; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Compression/ECompresionType.hpp b/Include/Aurora/Compression/ECompresionType.hpp new file mode 100644 index 00000000..df3fc124 --- /dev/null +++ b/Include/Aurora/Compression/ECompresionType.hpp @@ -0,0 +1,20 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ECompresionType.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Compression +{ + enum class ECompresionType + { + eLZMA, + eZSTD, + eDeflate, + eLZ4, + eBZIP2 + }; +} \ No newline at end of file diff --git a/Include/Aurora/Compression/ICompressionStream.hpp b/Include/Aurora/Compression/ICompressionStream.hpp new file mode 100644 index 00000000..05b1d496 --- /dev/null +++ b/Include/Aurora/Compression/ICompressionStream.hpp @@ -0,0 +1,24 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ICompressionStream.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Compression +{ + class ICompressionStream + { + public: + virtual bool Ingest(AuUInt32 minimum, AuUInt32 request) = 0; + virtual bool Read(void * /*opt*/, AuUInt32 &len, bool ingestUntilEOS = true) = 0; + }; + + class ICompressionStreamEx : public ICompressionStream + { + public: + virtual void Flush() = 0; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Compression/StreamPipeProcessor.hpp b/Include/Aurora/Compression/StreamPipeProcessor.hpp new file mode 100644 index 00000000..91625630 --- /dev/null +++ b/Include/Aurora/Compression/StreamPipeProcessor.hpp @@ -0,0 +1,32 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: StreamPipeProcessor.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Compression +{ + struct CompressionPipe + { + /// algorithm + ECompresionType type; + + /// LZMA decompression + compression, and ZSTD compression only + AuUInt32 threads; + + /// consume from stream callback + std::function inPipe; + + /// write to stream callback + std::function writePipe; + + /// preemption and reporting + std::function reportProgress; + }; + + AUKN_SYM bool Decompress(const CompressionPipe &stream); + AUKN_SYM bool Compress(const CompressionPipe &stream, const CompressionInfo &info); +} \ No newline at end of file diff --git a/Include/Aurora/Compression/StreamProcessor.hpp b/Include/Aurora/Compression/StreamProcessor.hpp new file mode 100644 index 00000000..8a247cba --- /dev/null +++ b/Include/Aurora/Compression/StreamProcessor.hpp @@ -0,0 +1,16 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: StreamProcessor.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include + +namespace Aurora::Compression +{ + AUKN_SHARED_API(Decompressor, ICompressionStream, Aurora::IO::IStreamReader *reader, ECompresionType type); + AUKN_SHARED_API(Compressor, ICompressionStreamEx, Aurora::IO::IStreamReader *reader, const CompressionInfo &info); +} \ No newline at end of file diff --git a/Include/Aurora/Console/Commands/Commands.hpp b/Include/Aurora/Console/Commands/Commands.hpp new file mode 100644 index 00000000..f234ac2a --- /dev/null +++ b/Include/Aurora/Console/Commands/Commands.hpp @@ -0,0 +1,18 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Commands.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +#include + +namespace Aurora::Console::Commands +{ + using CommandCallback_cb = std::function; + + AUKN_SYM void AddCommand(const AuString &tag, const Parse::ParseObject &commandStructure, const CommandCallback_cb &callback); + AUKN_SYM bool DispatchCommand(const AuString &string); +} \ No newline at end of file diff --git a/Include/Aurora/Console/Console.hpp b/Include/Aurora/Console/Console.hpp new file mode 100644 index 00000000..5aad6bcd --- /dev/null +++ b/Include/Aurora/Console/Console.hpp @@ -0,0 +1,20 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Console.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "EAnsiColor.hpp" +#include "ConsoleMessage.hpp" + +namespace Aurora::Console +{ + AUKN_SYM void WriteLine(const ConsoleMessage &msg); +} + +#include "Commands/Commands.hpp" +#include "Hooks/Hooks.hpp" +#include "Logging/Logging.hpp" \ No newline at end of file diff --git a/Include/Aurora/Console/ConsoleMessage.hpp b/Include/Aurora/Console/ConsoleMessage.hpp new file mode 100644 index 00000000..7efb5305 --- /dev/null +++ b/Include/Aurora/Console/ConsoleMessage.hpp @@ -0,0 +1,55 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConsoleMessage.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +//#include + +namespace Aurora::Threading::Threads +{ + AUKN_SYM AuThreadId_t GetThreadId(); +} + +namespace Aurora::Time +{ + AUKN_SYM AuUInt64 CurrentClockMS(); +} + +namespace Aurora::Console +{ + class AUKN_SYM ConsoleMessage + { + public: + EAnsiColor color; + AuString prefix; + AuString line; + AuUInt64 time; + AuThreadId_t tid; + + ConsoleMessage() + { + color = EAnsiColor::eReset; + } + + ConsoleMessage(const AuString &prefix, const AuString &line) : prefix(prefix), line(line), color(EAnsiColor::eReset) + { + time = Time::CurrentClockMS(); + tid = Threading::Threads::GetThreadId(); + } + + ConsoleMessage(const EAnsiColor color, const AuString &prefix, const AuString &line) : prefix(prefix), line(line), color(color) + { + time = Time::CurrentClockMS(); + tid = Threading::Threads::GetThreadId(); + } + + AuString StringifyTime(bool simple = false) const; + AuString GetWrappedTag() const; + AuString ToConsole() const; + AuString ToSimplified() const; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Console/EAnsiColor.hpp b/Include/Aurora/Console/EAnsiColor.hpp new file mode 100644 index 00000000..43f8c0bd --- /dev/null +++ b/Include/Aurora/Console/EAnsiColor.hpp @@ -0,0 +1,29 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EAnsiColor.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Console +{ + enum class EAnsiColor + { + eRed, + eBoldRed, + eGreen, + eBoldGreen, + eYellow, + eBoldYellow, + eBlue, + eBoldBlue, + eMagenta, + eBoldMagenta, + eCyan, + eBoldCyan, + eReset, + eCount + }; +} \ No newline at end of file diff --git a/Include/Aurora/Console/Hooks/Hooks.hpp b/Include/Aurora/Console/Hooks/Hooks.hpp new file mode 100644 index 00000000..9664d9f2 --- /dev/null +++ b/Include/Aurora/Console/Hooks/Hooks.hpp @@ -0,0 +1,15 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Hooks.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Console::Hooks +{ + using LineHook_cb = std::function; + + AUKN_SYM void AddHook(LineHook_cb hook); +} \ No newline at end of file diff --git a/Include/Aurora/Console/Logging/Logging.hpp b/Include/Aurora/Console/Logging/Logging.hpp new file mode 100644 index 00000000..d4097632 --- /dev/null +++ b/Include/Aurora/Console/Logging/Logging.hpp @@ -0,0 +1,47 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Logging.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Console::Logging +{ + template + static inline void WriteLinef(const AuString &tag, const AuString &msg, T... args) + { + Aurora::Console::WriteLine(ConsoleMessage(EAnsiColor::eReset, tag, fmt::format(msg, args...))); + } + + template + static inline void WriteLinef(EAnsiColor color, const AuString &tag, const AuString &msg, T... args) + { + Aurora::Console::WriteLine(ConsoleMessage(color, tag, fmt::format(msg, args...))); + } + + template + static inline void LogInfo(const AuString &line, T... args) + { + Aurora::Console::Logging::WriteLinef(EAnsiColor::eGreen, "Info", line, args...); + } + + template + static inline void LogDbg(const AuString &line, T... args) + { + Aurora::Console::Logging::WriteLinef(EAnsiColor::eYellow, "Debug", line, args...); + } + + template + static inline void LogWarn(const AuString &line, T... args) + { + Aurora::Console::Logging::WriteLinef(EAnsiColor::eRed, "Warn", line, args...); + } + + template + static inline void LogGame(const AuString &line, T... args) + { + Aurora::Console::Logging::WriteLinef(EAnsiColor::eBlue, "Game", line, args...); + } +} \ No newline at end of file diff --git a/Include/Aurora/Crypto/AES/AES.hpp b/Include/Aurora/Crypto/AES/AES.hpp new file mode 100644 index 00000000..89b3b1bd --- /dev/null +++ b/Include/Aurora/Crypto/AES/AES.hpp @@ -0,0 +1,35 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AES.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Crypto::AES +{ + AUKN_SYM AuUInt GetSafeCipherPadding(const void* plainText, + AuUInt plainTextLength); + + // Remember: AES works in chunks of 128 bits + // IVS are 16 bytes long + // Chunks are 16 bytes long + // Keys are 16, 24, or 32 bytes long + // Initialization vectors must never be constant + // Initialization vectors should be determined by a handshaake + // It is not the end of the world if an IV is made public by design + // Keys must be random + // Initialization vectors could be derived from SHA1, Tiger, or SHA2 digests + AUKN_SYM bool Encrypt(const void* plainText, AuUInt plainTextLength, + const void* iv, void* outIv, AuUInt ivLength, + const void* key, AuUInt keyLength, + AuList& out, + bool safe); + + AUKN_SYM bool Decrypt(const void* cipherText, AuUInt cipherTextLength, + const void* iv, void* outIv, AuUInt ivLength, + const void* key, AuUInt keyLength, + AuList& plainText, + bool safe); +} \ No newline at end of file diff --git a/Include/Aurora/Crypto/CA/CA.hpp b/Include/Aurora/Crypto/CA/CA.hpp new file mode 100644 index 00000000..76b2963a --- /dev/null +++ b/Include/Aurora/Crypto/CA/CA.hpp @@ -0,0 +1,28 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CA.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Crypto::CA +{ + class ICertificateStore + { + public: + virtual void AddSignature(const PublicRSAKey& CA, const AuList& sig, + EHashType method, EPaddingType type) = 0; + virtual void AddPublicCert(const X509::Certificate& cert) = 0; + + /// For future support of http gets of the CA list + virtual bool& AllowHTTPTree() = 0; + + virtual bool CheckKey(const PublicKey& pub) = 0; + virtual bool CheckCert(const X509::Certificate& cert) = 0; + }; + + + AUKN_SHARED_API(NewCA, ICertificateStore); +} \ No newline at end of file diff --git a/Include/Aurora/Crypto/Crypto.hpp b/Include/Aurora/Crypto/Crypto.hpp new file mode 100644 index 00000000..ede3932f --- /dev/null +++ b/Include/Aurora/Crypto/Crypto.hpp @@ -0,0 +1,40 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Crypto.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Crypto +{ + using DerBuffer = AuList; + using PrivateRSAKey = DerBuffer; // a/k/a "RSA", OpenSSL, private key in mbedtls, PKCS1 + using PublicRSAKey = DerBuffer; + using PrivateECCKey = DerBuffer; + using PublicECCKey = DerBuffer; + using PrivateKey = DerBuffer; // PKCS8 + using PublicKey = DerBuffer; + + namespace X509 + { + using Certificate = AuList; + } + + struct RSAPair + { + X509::Certificate pub; + PrivateRSAKey priv; + }; +} + +#include "EHashType.hpp" +#include "EPaddingType.hpp" +#include "AES/AES.hpp" +#include "X509/X509.hpp" +#include "CA/CA.hpp" +#include "ECC25519/ECC25519.hpp" +#include "ECCNIST/ECCNIST.hpp" +#include "PEM/PEM.hpp" +#include "RSA/RSA.hpp" diff --git a/Include/Aurora/Crypto/ECC25519/ECC25519.hpp b/Include/Aurora/Crypto/ECC25519/ECC25519.hpp new file mode 100644 index 00000000..aedf31e2 --- /dev/null +++ b/Include/Aurora/Crypto/ECC25519/ECC25519.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ECC25519.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Crypto::ECC25519 +{ + +} \ No newline at end of file diff --git a/Include/Aurora/Crypto/ECCNIST/ECCNIST.hpp b/Include/Aurora/Crypto/ECCNIST/ECCNIST.hpp new file mode 100644 index 00000000..5d420c30 --- /dev/null +++ b/Include/Aurora/Crypto/ECCNIST/ECCNIST.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ECCNIST.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Crypto::ECCNIST +{ + +} \ No newline at end of file diff --git a/Include/Aurora/Crypto/EHashType.hpp b/Include/Aurora/Crypto/EHashType.hpp new file mode 100644 index 00000000..d040e018 --- /dev/null +++ b/Include/Aurora/Crypto/EHashType.hpp @@ -0,0 +1,19 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EHashType.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Crypto +{ + enum class EHashType + { + eSHA1_20_160, + eTiger_24_192, + eSHA2_32_256, + eSHA2_64_512 + }; +} \ No newline at end of file diff --git a/Include/Aurora/Crypto/EPaddingType.hpp b/Include/Aurora/Crypto/EPaddingType.hpp new file mode 100644 index 00000000..855032a4 --- /dev/null +++ b/Include/Aurora/Crypto/EPaddingType.hpp @@ -0,0 +1,24 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EPaddingType.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Crypto +{ + enum class EPaddingType + { + ePaddingNone, + // For use with encryption + ePKCS_OAEP, + // For use with signing and encryption + ePKCS_1_5, + // For use with signing + ePKCS_1_PSS, + // For use with signing + ePKCS_1_5_NA1 + }; +} \ No newline at end of file diff --git a/Include/Aurora/Crypto/PEM/PEM.hpp b/Include/Aurora/Crypto/PEM/PEM.hpp new file mode 100644 index 00000000..b128202d --- /dev/null +++ b/Include/Aurora/Crypto/PEM/PEM.hpp @@ -0,0 +1,23 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: PEM.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Crypto::PEM +{ + AUKN_SYM AuString ToString(const Aurora::Crypto::X509::Certificate &in); + AUKN_SYM AuString PublicToString(const PublicKey &in); + AUKN_SYM AuString PrivateToString(const PrivateKey &in); + AUKN_SYM AuString PublicRSAToString(const PrivateRSAKey &in); + AUKN_SYM AuString PrivateRSAToString(const PublicRSAKey &in); + + AUKN_SYM bool FromString(const AuString &in, Aurora::Crypto::X509::Certificate &out); + AUKN_SYM bool PublicFromString(const AuString &in, PublicKey &out); + AUKN_SYM bool PrivateFromString(const AuString &in, PrivateKey &out); + AUKN_SYM bool PublicRSAFromString(const AuString &in, PrivateRSAKey &out); + AUKN_SYM bool PrivateRSAFromString(const AuString &in, PublicRSAKey &out); +} \ No newline at end of file diff --git a/Include/Aurora/Crypto/RSA/RSA.hpp b/Include/Aurora/Crypto/RSA/RSA.hpp new file mode 100644 index 00000000..9796fd0a --- /dev/null +++ b/Include/Aurora/Crypto/RSA/RSA.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RSA.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Crypto::RSA +{ + +} \ No newline at end of file diff --git a/Include/Aurora/Crypto/X509/ESignatureAlgorithm.hpp b/Include/Aurora/Crypto/X509/ESignatureAlgorithm.hpp new file mode 100644 index 00000000..fe683d90 --- /dev/null +++ b/Include/Aurora/Crypto/X509/ESignatureAlgorithm.hpp @@ -0,0 +1,20 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ESignatureAlgorithm.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Crypto::X509 +{ + enum class ESignatureAlgorithm + { + eInvalidAlgorithm, + eMD5WithRSA, + eSHA1WithRSA, + eSHA256WithRSA, + eECDSAWithRSA + }; +} \ No newline at end of file diff --git a/Include/Aurora/Crypto/X509/X509.hpp b/Include/Aurora/Crypto/X509/X509.hpp new file mode 100644 index 00000000..e3c7a3fb --- /dev/null +++ b/Include/Aurora/Crypto/X509/X509.hpp @@ -0,0 +1,62 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: X509.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +#include "ESignatureAlgorithm.hpp" + +namespace Aurora::Crypto::X509 +{ + // yes, these structure completely disregards the principles of the x509 structure and the rationales behind certain extensions + // however, this is not intended to be a grandiose TLS stack + // + // PKSC#1 and #8, and x509 extensions (ie: rfc5280 key-ids, v3 exts) are not supported in our deps + // we had to reimplement them ourselves >:( + // lets worry about the more important issues + + struct CertName + { + AuString commonName; // Tbs + + AuString department; // Tbs + AuString organization; // Tbs + AuString state; // Tbs + AuString countryCode; // Tbs + + AuString name; // Tbs + AuString email; // Tbs + AuString title; // Tbs + }; + + struct DecodedCertificate + { + // TODO: + //SignatureAlgorithm signature; + struct Issuer : CertName + { + AuList id; + } issuer; + struct Subject : CertName + { + AuList id; + } subject; + struct Vaildity // Tbs + { // Tbs + AuUInt issued; // Tbs + AuUInt expire; // Tbs + } validity; // Tbs + AuList serialNumber; // Tbs + AuList algorithmOid; // Tbs + // TODO: usage // extension + AuList AIAs; + // TODO: AuString CRL; + // TODO: AuList subjectNames; + }; + + AUKN_SYM bool Decode(const Certificate &der, DecodedCertificate &out); + AUKN_SYM bool Validate(const Certificate &der, const Certificate &parentDer); +} \ No newline at end of file diff --git a/Include/Aurora/Data/Data.hpp b/Include/Aurora/Data/Data.hpp new file mode 100644 index 00000000..9b376c9a --- /dev/null +++ b/Include/Aurora/Data/Data.hpp @@ -0,0 +1,137 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Data.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Data +{ + enum class DataType + { + kTypeUInt, // Human friendly types + kTypeSInt, + kTypeNumber, + kTypeString, + kTypeBoolean, + kTypeUUID, + kTypeVec3, + kTypeVec4, + + kTypeGenericMax, // Binary serialization + + kTypeStructFloat, + kTypeStructUInt8, + kTypeStructInt8, + kTypeStructUInt16, + kTypeStructInt16, + kTypeStructUInt32, + kTypeStructInt32, + + kTypeSpecialComponent, // Special QST + kTypeSpecialArray, // + kTypeSpecialObject, // + + kTypeEND, + + kTypeStructDouble = kTypeNumber, // overlap bin serial with human friendly aliases + kTypeStructUInt64 = kTypeUInt, // + kTypeStructInt64 = kTypeSInt, // + }; + + union PrimitiveValue + { + AuUInt64 uint; + AuInt64 sint; + double number; + bool boolean; + glm::vec3 vec3; + glm::vec4 vec4; + }; + + struct Value + { + #define CONSTRUCTOR(type, member)\ + Value(type val)\ + {\ + primitive.member = val;\ + } + CONSTRUCTOR(const glm::vec3 &, vec3); + CONSTRUCTOR(const glm::vec4 &, vec4); + CONSTRUCTOR(bool, boolean); + CONSTRUCTOR(double, number); + CONSTRUCTOR(AuInt64, sint); + CONSTRUCTOR(AuUInt64, uint); + #undef CONSTRUCTOR + + Value(uuids::uuid val) + { + UUID = val; + } + + Value(const AuString &val) + { + string = val; + } + + Value() + { + + } + + PrimitiveValue primitive; + uuids::uuid UUID; + AuString string; + }; + + struct TypedValue + { + DataType type; + Value value; + }; + + template + static constexpr AuUInt GetDatatypeLength() + { + switch /*constexpr*/ (type) + { + case DataType::kTypeBoolean: return 1; + case DataType::kTypeStructFloat: return 4; + case DataType::kTypeStructDouble: return 8; + case DataType::kTypeVec3: return 3 * 4; + case DataType::kTypeVec4: return 4 * 4; + case DataType::kTypeStructUInt8: return 1; + case DataType::kTypeStructInt8: return 1; + case DataType::kTypeStructUInt16: return 2; + case DataType::kTypeStructInt16: return 2; + case DataType::kTypeStructUInt32: return 4; + case DataType::kTypeStructInt32: return 4; + case DataType::kTypeStructUInt64: return 8; + case DataType::kTypeStructInt64: return 8; + default: static_assert(false, "invalid datatype"); + } + } + + static AuUInt GetDatatypeLength(Data::DataType type) + { + switch /*constexpr*/ (type) + { + case DataType::kTypeBoolean: return 1; + case DataType::kTypeStructFloat: return 4; + case DataType::kTypeStructDouble: return 8; + case DataType::kTypeVec3: return 3 * 4; + case DataType::kTypeVec4: return 4 * 4; + case DataType::kTypeStructUInt8: return 1; + case DataType::kTypeStructInt8: return 1; + case DataType::kTypeStructUInt16: return 2; + case DataType::kTypeStructInt16: return 2; + case DataType::kTypeStructUInt32: return 4; + case DataType::kTypeStructInt32: return 4; + case DataType::kTypeStructUInt64: return 8; + case DataType::kTypeStructInt64: return 8; + default: return 0; + } + } +} \ No newline at end of file diff --git a/Include/Aurora/Debug/Debug.hpp b/Include/Aurora/Debug/Debug.hpp new file mode 100644 index 00000000..61e7da19 --- /dev/null +++ b/Include/Aurora/Debug/Debug.hpp @@ -0,0 +1,58 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Debug.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include + +#include "StackTrace.hpp" +#include "FailureCategory.hpp" + +namespace Aurora::Debug +{ + /** + Retrieves a print-friendly callstack of the last trap (either innocent exception or fatal mem access)
+ On Win32, this information is always available
+ On other platforms, this function will likely yield no valuable information + */ + AUKN_SYM AuString GetLastErrorStack(); + + AUKN_SYM StackTrace GetLastStackTrace(); + + + /** + Retrieve information about the last exception.
+ On Win32, this information is always available
+ On other platforms, this information is only available within C++14 catch blocks + */ + AUKN_SYM AuString GetLastException(); + + /** + Retrieve the last system error (IE: Win32, GetLastError()) + */ + AUKN_SYM AuString GetLastSystemMessage(); + + /** + Prints the current error state of the thread including:
+ Current System Error,
+ Current Runtime Error,
+ Last Exception Call Stack,
+ Last major ring notification + + */ + AUKN_SYM void PrintError(); + + /** + Immediately terminates the process. + May attempt some hardened telemetry debug ops + */ + AUKN_SYM AU_NORETURN void Panic(); +} + +#include "SysPanic.hpp" +#include "SysAssertions.hpp" +#include "SysErrors.hpp" \ No newline at end of file diff --git a/Include/Aurora/Debug/FailureCategory.hpp b/Include/Aurora/Debug/FailureCategory.hpp new file mode 100644 index 00000000..0bec2676 --- /dev/null +++ b/Include/Aurora/Debug/FailureCategory.hpp @@ -0,0 +1,39 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: FailureCategory.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Debug +{ + enum FailureCategory + { + kFailureGeneric, + kFailureNested, + kFailureMemory, + kFailureIO, + kFailureFIO, + kFailureNet, + kFailureAudio, + kFailureHAL, + kFailureHALContext, + kFailureCrypto, + kFailureParam, + kFailureLogicError, + kFailureMathError, + kFailureUnavailableError, + kFailureTimeoutError, + kFailureWatchdogError, + kFailureServiceError, + kFailurePermissionError, + kFailureOutOfRange, + kFailureSyntaxError, + kFailureDisconnected, + kFailureUninitialized, + + kFailureUserBegin = 256 + }; +} \ No newline at end of file diff --git a/Include/Aurora/Debug/StackTrace.hpp b/Include/Aurora/Debug/StackTrace.hpp new file mode 100644 index 00000000..acbaf0c2 --- /dev/null +++ b/Include/Aurora/Debug/StackTrace.hpp @@ -0,0 +1,24 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: StackTrace.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Debug +{ + struct StackTraceEntry + { + std::optional label; + AuUInt64 address; + std::optional module; + std::optional> file; // file, line, offset + + AUKN_SYM AuString Stringify() const; + }; + + using StackTrace = AuList; + AUKN_SYM AuString StringifyStackTrace(const StackTrace &backtrace); +} \ No newline at end of file diff --git a/Include/Aurora/Debug/SysAssertions.hpp b/Include/Aurora/Debug/SysAssertions.hpp new file mode 100644 index 00000000..312c4879 --- /dev/null +++ b/Include/Aurora/Debug/SysAssertions.hpp @@ -0,0 +1,193 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: SysAssertions.hpp + Date: 2021-6-10 + Author: Reece +***/ + +//#pragma once <- AURORA_NO_ASSERTIONS may be redefined. do not cache + +#if !defined(AURORA_NO_ASSERTIONS) + +// defines SysAssert and SysAssertDbg +#if defined(DEBUG) + /// @private + template + static inline void SysAssertFileEx(const char *file, int fileno, const char *func, bool tru, T... args) + { + if (tru) + { + return; + } + + Aurora::Console::Logging::WriteLinef( + Aurora::Console::EAnsiColor::eBoldRed, + "Fatal", + "Expression address: {} {}:{}", func, file, fileno); + + if constexpr (sizeof...(T) == 0) + { + SysPanic("That's all folks"); + } + else + { + SysPanic(args...); + } + } + + + + /** + A simple assertion with an optional message + */ + #define SysAssert(tru, ...) \ + do \ + { \ + SysAssertFileEx(__FILE__, __LINE__, __FUNCTION__, tru, ## __VA_ARGS__); \ + } while (0) + + /** + A simple assertion with an optional message under debug targets + */ + #define SysAssertDbg SysAssert + + + +#else + + /// @private + template + static inline void SysAssertEx(bool tru, T... args) + { + if (tru) + { + return; + } + + if constexpr (sizeof...(T) == 0) + { + SysPanic("That's all folks"); + } + else + { + SysPanic(args...); + } + } + + + + /** + A simple assertion with an optional message + */ + #define SysAssert(tru, ...) \ + do \ + { \ + SysAssertEx(tru, ## __VA_ARGS__); \ + } while (0) + + /** + A simple assertion with an optional message under debug targets + */ + #define SysAssertDbg(tru, ...) do {} while (0) + + + +#endif + +// defines SysAssertExp and SysAssertDbgExp +#if defined(DEBUG) + /// @private + template + static inline void SysAssertFileExpEx(const char *file, int fileno, const char *func, const char *exp, bool tru, T... args) + { + if (tru) + { + return; + } + + Aurora::Console::Logging::WriteLinef( + Aurora::Console::EAnsiColor::eBoldRed, + "Fatal", + "Expression address: {} {}:{}", func, file, fileno); + + Aurora::Console::Logging::WriteLinef( + Aurora::Console::EAnsiColor::eBoldRed, + "Fatal", + "Expression failed: {}", exp); + + if constexpr (sizeof...(T) == 0) + { + SysPanic("That's all folks"); + } + else + { + SysPanic(args...); + } + } + + + + /** + A simple assertion with a debug expresison and optional message debugging + */ + #define SysAssertExp(tru, ...) \ + do \ + { \ + SysAssertFileExpEx(__FILE__, __LINE__, __FUNCTION__, _AUKCON_STRINGIFY_X(tru), tru, ## __VA_ARGS__); \ + } while (0) + + + /** + A simple assertion with a debug expresison and optional message debugging under debug targets only + */ + #define SysAssertDbgExp SysAssertExp + + + +#else + /// @private + template + static inline void SysAssertExpEx(const char *exp, bool tru, T... args) + { + if (tru) + { + return; + } + + Aurora::Console::Logging::WriteLinef( + Aurora::Console::EAnsiColor::eBoldRed, + "Fatal", + "Expression failed: {}", exp); + + if constexpr (sizeof...(T) == 0) + { + SysPanic("That's all folks"); + } + else + { + SysPanic(args...); + } + } + + + + /** + A simple assertion with a debug expresison and optional message debugging + */ + #define SysAssertExp(tru, ...) \ + do \ + { \ + SysAssertExpEx(_AUKCON_STRINGIFY_X(tru), tru, ## __VA_ARGS__); \ + } while (0) + + /** + A simple assertion with a debug expresison and optional message debugging under debug targets only + */ + #define SysAssertDbgExp(tru, ...) do {} while (0) + + + +#endif + +#endif \ No newline at end of file diff --git a/Include/Aurora/Debug/SysErrors.hpp b/Include/Aurora/Debug/SysErrors.hpp new file mode 100644 index 00000000..af21cf13 --- /dev/null +++ b/Include/Aurora/Debug/SysErrors.hpp @@ -0,0 +1,161 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: SysErrors.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Debug +{ + AUKN_SYM void _PushError(AuUInt address, FailureCategory category, const char *msg); + + #if defined(AURORA_COMPILER_MSVC) + #define _DBG_RET_ADDR (AuUInt)_ReturnAddress() + #elif defined(AURORA_COMPILER_CLANG) + #define _DBG_RET_ADDR (AuUInt)__builtin_return_address(0) + #else + #define _DBG_RET_ADDR AuUInt(0) + #endif + + #if defined(AURORA_COMPILER_MSVC) + #pragma optimize("", off) + #define _FREECOMPILER_OPTIMIZE_OFF + #else + #define _FREECOMPILER_OPTIMIZE_OFF [[gnu::optimize(0)]] + #endif + + static AU_NOINLINE void ErrorMakeNested() _FREECOMPILER_OPTIMIZE_OFF + { + _PushError(_DBG_RET_ADDR, FailureCategory::kFailureNested, nullptr); + } + + template + static AU_NOINLINE void SysPushError(FailureCategory category, const AuString &msg, T... args) _FREECOMPILER_OPTIMIZE_OFF + { + if constexpr (sizeof...(T) == 0) + { + _PushError(_DBG_RET_ADDR, category, msg.c_str()); + } + else + { + _PushError(_DBG_RET_ADDR, category, fmt::format(msg, args...).c_str()); + } + } + + static AU_NOINLINE void SysPushError(FailureCategory category) _FREECOMPILER_OPTIMIZE_OFF + { + _PushError(_DBG_RET_ADDR, category, nullptr); + } + + #if defined(AURORA_COMPILER_MSVC) + #pragma optimize("", on) + #endif +} + +#define SysCheckReturn(x, ...) if (!(x)) { Aurora::Debug::ErrorMakeNested(); return ##__VA_ARGS__; } + +#define SysPushErrorError(error, ...) Aurora::Debug::SysPushError(Aurora::Debug::error, ## __VA_ARGS__) + +// legacy +#define SysPushErrorArg(...) SysPushErrorError(kFailureParam, ## __VA_ARGS__) +#define SysPushErrorGen(...) SysPushErrorError(kFailureGeneric, ## __VA_ARGS__) +#define SysPushErrorCrypt(...) SysPushErrorError(kFailureCrypto, ## __VA_ARGS__) +#define SysPushErrorNet(...) SysPushErrorError(kFailureNet, ## __VA_ARGS__) +#define SysPushErrorMem(...) SysPushErrorError(kFailureMemory, ## __VA_ARGS__) + +// edge case +#define SysPushErrorNested() Aurora::Debug::ErrorMakeNested(); + +// enums +#define SysPushErrorGeneric(...) SysPushErrorError(kFailureGeneric, ## __VA_ARGS__) +#define SysPushErrorMemory(...) SysPushErrorError(kFailureMemory, ## __VA_ARGS__) +#define SysPushErrorIO(...) SysPushErrorError(kFailureIO, ## __VA_ARGS__) +#define SysPushErrorFIO(...) SysPushErrorError(kFailureFIO, ## __VA_ARGS__) +#define SysPushErrorNet(...) SysPushErrorError(kFailureNet, ## __VA_ARGS__) +#define SysPushErrorAudio(...) SysPushErrorError(kFailureAudio, ## __VA_ARGS__) +#define SysPushErrorHAL(...) SysPushErrorError(kFailureHAL, ## __VA_ARGS__) +#define SysPushErrorHALContext(...) SysPushErrorError(kFailureHALContext, ## __VA_ARGS__) +#define SysPushErrorCrypto(...) SysPushErrorError(kFailureCrypto, ## __VA_ARGS__) +#define SysPushErrorParam(...) SysPushErrorError(kFailureParam, ## __VA_ARGS__) +#define SysPushErrorLogicError(...) SysPushErrorError(kFailureLogicError, ## __VA_ARGS__) +#define SysPushErrorMathError(...) SysPushErrorError(kFailureMathError, ## __VA_ARGS__) +#define SysPushErrorUnavailableError(...) SysPushErrorError(kFailureUnavailableError, ## __VA_ARGS__) +#define SysPushErrorTimeoutError(...) SysPushErrorError(kFailureTimeoutError, ## __VA_ARGS__) +#define SysPushErrorWatchdogError(...) SysPushErrorError(kFailureWatchdogError, ## __VA_ARGS__) +#define SysPushErrorServiceError(...) SysPushErrorError(kFailureServiceError, ## __VA_ARGS__) +#define SysPushErrorPermissionError(...) SysPushErrorError(kFailurePermissionError, ## __VA_ARGS__) +#define SysPushErrorOutOfRange(...) SysPushErrorError(kFailureOutOfRange, ## __VA_ARGS__) +#define SysPushErrorSyntaxError(...) SysPushErrorError(kFailureSyntaxError, ## __VA_ARGS__) +#define SysPushErrorDisconnected(...) SysPushErrorError(kFailureDisconnected, ## __VA_ARGS__) +#define SysPushErrorUninitialized(...) SysPushErrorError(kFailureUninitialized, ## __VA_ARGS__) + + + + + +#if defined(DEBUG) || defined(INTERNAL) + + #define SysPushErrorArgDbg SysPushErrorArg + #define SysPushErrorGenDbg SysPushErrorGen + #define SysPushErrorCryptDbg SysPushErrorCrypt + #define SysPushErrorNetDbg SysPushErrorNet + #define SysPushErrorMemDbg SysPushErrorMem + + #define SysPushErrorGenericDbg SysPushErrorGeneric + #define SysPushErrorNestedDbg SysPushErrorNested + #define SysPushErrorMemoryDbg SysPushErrorMemory + #define SysPushErrorIODbg SysPushErrorIO + #define SysPushErrorFIODbg SysPushErrorFIO + #define SysPushErrorNetDbg SysPushErrorNet + #define SysPushErrorAudioDbg SysPushErrorAudio + #define SysPushErrorHALDbg SysPushErrorHAL + #define SysPushErrorHALContextDbg SysPushErrorHALContext + #define SysPushErrorCryptoDbg SysPushErrorCrypto + #define SysPushErrorParamDbg SysPushErrorParam + #define SysPushErrorLogicErrorDbg SysPushErrorLogicError + #define SysPushErrorMathErrorDbg SysPushErrorMathError + #define SysPushErrorUnavailableErrorDbg SysPushErrorUnavailableError + #define SysPushErrorTimeoutErrorDbg SysPushErrorTimeoutError + #define SysPushErrorWatchdogErrorDbg SysPushErrorWatchdogError + #define SysPushErrorServiceErrorDbg SysPushErrorServiceError + #define SysPushErrorPermissionErrorDbg SysPushErrorPermissionError + #define SysPushErrorOutOfRangeDbg SysPushErrorOutOfRange + #define SysPushErrorSyntaxErrorDbg SysPushErrorSyntaxError + #define SysPushErrorDisconnectedDbg SysPushErrorDisconnected + #define SysPushErrorUninitializedDbg SysPushErrorUninitialized + +#else + + #define SysPushErrorArgDbg(...) + #define SysPushErrorGenDbg(...) + #define SysPushErrorCryptDbg(...) + #define SysPushErrorNetDbg(...) + #define SysPushErrorMemDbg(...) + + #define SysPushErrorGenericDbg(...) + #define SysPushErrorNestedDbg(...) + #define SysPushErrorMemoryDbg(...) + #define SysPushErrorIODbg(...) + #define SysPushErrorFIODbg(...) + #define SysPushErrorNetDbg(...) + #define SysPushErrorAudioDbg(...) + #define SysPushErrorHALDbg(...) + #define SysPushErrorHALContextDbg(...) + #define SysPushErrorCryptoDbg(...) + #define SysPushErrorParamDbg(...) + #define SysPushErrorLogicErrorDbg(...) + #define SysPushErrorMathErrorDbg(...) + #define SysPushErrorUnavailableErrorDbg(...) + #define SysPushErrorTimeoutErrorDbg(...) + #define SysPushErrorWatchdogErrorDbg(...) + #define SysPushErrorServiceErrorDbg(...) + #define SysPushErrorPermissionErrorDbg(...) + #define SysPushErrorOutOfRangeDbg(...) + #define SysPushErrorSyntaxErrorDbg(...) + #define SysPushErrorDisconnectedDbg(...) + #define SysPushErrorUninitializedDbg(...) + + +#endif \ No newline at end of file diff --git a/Include/Aurora/Debug/SysPanic.hpp b/Include/Aurora/Debug/SysPanic.hpp new file mode 100644 index 00000000..c2f742ad --- /dev/null +++ b/Include/Aurora/Debug/SysPanic.hpp @@ -0,0 +1,15 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: SysPanic.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +template +static inline void __declspec(noreturn) SysPanic(T... args) +{ + Aurora::Console::Logging::WriteLinef(Aurora::Console::EAnsiColor::eBoldRed, "Fatal", args...); + Aurora::Debug::Panic(); +} \ No newline at end of file diff --git a/Include/Aurora/HWInfo/CpuInfo.hpp b/Include/Aurora/HWInfo/CpuInfo.hpp new file mode 100644 index 00000000..8d1c7c25 --- /dev/null +++ b/Include/Aurora/HWInfo/CpuInfo.hpp @@ -0,0 +1,26 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CpuInfo.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::HWInfo +{ + struct CpuInfo + { + Aurora::Build::EArchitecture cpuArch; + + AuUInt8 socket; + AuUInt8 cores; + AuUInt8 threads; + + AuUInt32 cpuId[4]; + char vendor[12]; + unsigned features; + }; + + AUKN_SYM const CpuInfo &GetCPUInfo(); +} \ No newline at end of file diff --git a/Include/Aurora/HWInfo/HWInfo.hpp b/Include/Aurora/HWInfo/HWInfo.hpp new file mode 100644 index 00000000..3f97e872 --- /dev/null +++ b/Include/Aurora/HWInfo/HWInfo.hpp @@ -0,0 +1,11 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: HWInfo.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "CpuInfo.hpp" +#include "RamInfo.hpp" \ No newline at end of file diff --git a/Include/Aurora/HWInfo/RamInfo.hpp b/Include/Aurora/HWInfo/RamInfo.hpp new file mode 100644 index 00000000..7e2ba3a1 --- /dev/null +++ b/Include/Aurora/HWInfo/RamInfo.hpp @@ -0,0 +1,15 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RamInfo.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::HWInfo +{ + AUKN_SYM AuUInt32 GetRamAvailableMb(); + AUKN_SYM AuUInt32 GetRamStartupAvailableMb(); + AUKN_SYM AuUInt32 GetRamTotalMb(); +} \ No newline at end of file diff --git a/Include/Aurora/Hashing/CE/CE.hpp b/Include/Aurora/Hashing/CE/CE.hpp new file mode 100644 index 00000000..8e2aa751 --- /dev/null +++ b/Include/Aurora/Hashing/CE/CE.hpp @@ -0,0 +1,10 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CE.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +#include "fnv1.hpp" \ No newline at end of file diff --git a/Include/Aurora/Hashing/CE/fnv1.hpp b/Include/Aurora/Hashing/CE/fnv1.hpp new file mode 100644 index 00000000..4547c8a2 --- /dev/null +++ b/Include/Aurora/Hashing/CE/fnv1.hpp @@ -0,0 +1,35 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: fnv1.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Hashing +{ + namespace CE + { + constexpr uint64_t val_64_const = 0xcbf29ce484222325; + constexpr uint64_t prime_64_const = 0x100000001b3; + + inline constexpr uint64_t fnv1a(const char *const str, const uint64_t value = val_64_const) noexcept + { + return (str[0] == '\0') ? value : fnv1a(&str[1], (value ^ uint64_t(str[0])) * prime_64_const); + } + + inline constexpr uint32_t fnv1a_trunc(const char *const str) noexcept + { + return static_cast(fnv1a(str)); + } + + constexpr uint64_t val_32_const = 0x811c9dc5; + constexpr uint64_t prime_32_const = 0x01000193; + + inline constexpr uint32_t fnv1a_32(const char *const str, const uint32_t value = val_32_const) noexcept + { + return (str[0] == '\0') ? value : fnv1a_32(&str[1], (value ^ uint32_t(str[0])) * prime_32_const); + } + } +} \ No newline at end of file diff --git a/Include/Aurora/Hashing/CommonDigests.hpp b/Include/Aurora/Hashing/CommonDigests.hpp new file mode 100644 index 00000000..5511fc3d --- /dev/null +++ b/Include/Aurora/Hashing/CommonDigests.hpp @@ -0,0 +1,61 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CommonDigests.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Hashing +{ + AUKN_SYM void MD5(const void *buffer, AuMach length, std::array &md5); + static void MD5(const AuList &bytebuffer, std::array &md5) + { + return MD5(bytebuffer.data(), bytebuffer.size(), md5); + } + static void MD5(const AuString &bytebuffer, std::array &md5) + { + return MD5(bytebuffer.data(), bytebuffer.size(), md5); + } + + AUKN_SYM void SHA1(const void *buffer, AuMach length, std::array &sha1); + static void SHA1(const AuList &bytebuffer, std::array &sha1) + { + return SHA1(bytebuffer.data(), bytebuffer.size(), sha1); + } + static void SHA1(const AuString &bytebuffer, std::array &sha1) + { + return SHA1(bytebuffer.data(), bytebuffer.size(), sha1); + } + + AUKN_SYM void Tiger(const void *buffer, AuMach length, std::array &tiger); + static void Tiger(const AuList &bytebuffer, std::array &tiger) + { + return Tiger(bytebuffer.data(), bytebuffer.size(), tiger); + } + static void Tiger(const AuString &bytebuffer, std::array &tiger) + { + return Tiger(bytebuffer.data(), bytebuffer.size(), tiger); + } + + AUKN_SYM void SHA2(const void *buffer, AuMach length, std::array &sha2); + static void SHA2(const AuList &bytebuffer, std::array &sha2) + { + return SHA2(bytebuffer.data(), bytebuffer.size(), sha2); + } + static void SHA2(const AuString &bytebuffer, std::array &sha2) + { + return SHA2(bytebuffer.data(), bytebuffer.size(), sha2); + } + + AUKN_SYM void SHA2_64(const void *buffer, AuMach length, std::array &sha2); + static void SHA2_64(const AuList &bytebuffer, std::array &sha2) + { + return SHA2_64(bytebuffer.data(), bytebuffer.size(), sha2); + } + static void SHA2_64(const AuString &bytebuffer, std::array &sha2) + { + return SHA2_64(bytebuffer.data(), bytebuffer.size(), sha2); + } +} \ No newline at end of file diff --git a/Include/Aurora/Hashing/EHashType.hpp b/Include/Aurora/Hashing/EHashType.hpp new file mode 100644 index 00000000..01e761a1 --- /dev/null +++ b/Include/Aurora/Hashing/EHashType.hpp @@ -0,0 +1,20 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EHashType.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Hashing +{ + enum class EHashType + { + eMD5, + eSHA1, + eSHA2_32, + eSHA2_64, + eTiger + }; +} \ No newline at end of file diff --git a/Include/Aurora/Hashing/HashStream.hpp b/Include/Aurora/Hashing/HashStream.hpp new file mode 100644 index 00000000..d2911a16 --- /dev/null +++ b/Include/Aurora/Hashing/HashStream.hpp @@ -0,0 +1,20 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: HashStream.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Hashing +{ + class IHashStream + { + public: + virtual void Ingest(const void *, AuUInt32) = 0; + virtual AuUInt8 *GetBytes(AuUInt32 &length) = 0; + }; + + AUKN_SHARED_API(HashStream, IHashStream, EHashType type); +} \ No newline at end of file diff --git a/Include/Aurora/Hashing/Hashing.hpp b/Include/Aurora/Hashing/Hashing.hpp new file mode 100644 index 00000000..0fbdefda --- /dev/null +++ b/Include/Aurora/Hashing/Hashing.hpp @@ -0,0 +1,14 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Hashing.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +#include "EHashType.hpp" +#include "HashStream.hpp" +#include "CommonDigests.hpp" + +#include "CE/fnv1.hpp" \ No newline at end of file diff --git a/Include/Aurora/IO/Buffered/BlobArbitraryReader.hpp b/Include/Aurora/IO/Buffered/BlobArbitraryReader.hpp new file mode 100644 index 00000000..3badd81b --- /dev/null +++ b/Include/Aurora/IO/Buffered/BlobArbitraryReader.hpp @@ -0,0 +1,51 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BlobArbitraryReader.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::Buffered +{ + class BlobArbitraryReader : public IArbitraryStreamer + { + public: + AU_NO_COPY_NO_MOVE(BlobArbitraryReader) + + BlobArbitraryReader(const AuList &buffer) : buffer_(buffer) {} + BlobArbitraryReader() {} + ~BlobArbitraryReader(){} + + virtual EStreamError Open() override + { + return EStreamError::eErrorNone; + } + + virtual EStreamError ArbitraryRead(AuUInt32 offset, void *buffer, AuUInt32 &len) override + { + auto oldLen = std::exchange(len, 0); + if (buffer_.empty()) return EStreamError::eErrorEndOfStream; + + auto endOffset = offset + oldLen; + auto realEndOffset = std::min(AuUInt32(buffer_.size()), endOffset); + auto actualLength = realEndOffset - offset; + + if (actualLength < 0) return EStreamError::eErrorEndOfStream; + + len = actualLength; + std::memcpy(buffer, buffer_.data() + offset, len); + + return EStreamError::eErrorNone; + } + + virtual void Close() override + { + buffer_.clear(); + } + + private: + AuList buffer_; + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/Buffered/BlobReader.hpp b/Include/Aurora/IO/Buffered/BlobReader.hpp new file mode 100644 index 00000000..83314868 --- /dev/null +++ b/Include/Aurora/IO/Buffered/BlobReader.hpp @@ -0,0 +1,48 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BlobReader.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::Buffered +{ + class BlobReader : public IStreamReader + { + public: + AU_NO_COPY_NO_MOVE(BlobReader) + + BlobReader(const AuList &buffer) : buffer_(buffer) {} + BlobReader() {} + ~BlobReader() {} + + virtual EStreamError Open() override + { + return EStreamError::eErrorNone; + } + + virtual EStreamError Read(void *buffer, AuUInt32 &len) override + { + auto oldLen = std::exchange(len, 0); + + if (buffer_.empty()) return EStreamError::eErrorEndOfStream; + + auto realEndOffset = std::min(AuUInt32(buffer_.size()), oldLen); + + len = realEndOffset; + std::memcpy(buffer, buffer_.data(), len); + + return EStreamError::eErrorNone; + } + + virtual void Close() override + { + buffer_.clear(); + } + + private: + AuList buffer_; + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/Buffered/BlobWriter.hpp b/Include/Aurora/IO/Buffered/BlobWriter.hpp new file mode 100644 index 00000000..5be0d3cd --- /dev/null +++ b/Include/Aurora/IO/Buffered/BlobWriter.hpp @@ -0,0 +1,49 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BlobWriter.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::Buffered +{ + class BlobWriter : public IStreamWriter + { + public: + AU_NO_COPY_NO_MOVE(BlobWriter) + + BlobWriter() {} + ~BlobWriter() {} + + virtual EStreamError Open() override + { + return EStreamError::eErrorNone; + } + + virtual EStreamError Write(const void *buffer, AuUInt32 len) override + { + auto idx = buffer_.size(); + buffer_.resize(idx + len); + std::memcpy(buffer_.data() + idx, buffer, len); + return EStreamError::eErrorNone; + } + + virtual void Flush() override + { + } + + virtual void Close() override + { + } + + const AuList &GetBuffer() + { + return this->buffer_; + } + + private: + AuList buffer_; + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/Buffered/Buffered.hpp b/Include/Aurora/IO/Buffered/Buffered.hpp new file mode 100644 index 00000000..9c9ad190 --- /dev/null +++ b/Include/Aurora/IO/Buffered/Buffered.hpp @@ -0,0 +1,14 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Buffered.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +// Most internal transactions will be buffered. +// For sake of being able to mix network, file, serial, and other code, its nice to use the same reader interface for buffered and streams where read/write until EOS is required +#include "BlobArbitraryReader.hpp" +#include "BlobReader.hpp" +#include "BlobWriter.hpp" \ No newline at end of file diff --git a/Include/Aurora/IO/EStreamError.hpp b/Include/Aurora/IO/EStreamError.hpp new file mode 100644 index 00000000..47f471b6 --- /dev/null +++ b/Include/Aurora/IO/EStreamError.hpp @@ -0,0 +1,20 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EStreamError.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO +{ + enum class EStreamError + { + eErrorNone, + eErrorEndOfStream, + eErrorStreamNotOpen, + eErrorStreamInterrupted, + eErrorHandleClosed + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/FS/FS.hpp b/Include/Aurora/IO/FS/FS.hpp new file mode 100644 index 00000000..0e6a8e20 --- /dev/null +++ b/Include/Aurora/IO/FS/FS.hpp @@ -0,0 +1,47 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: FS.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::FS +{ + /** + Lists files with respect to a given partial or full path of a directory + @param files relative address of an entry + */ + AUKN_SYM bool FilesInDirectory(const AuString &string, AuList &files); + + /** + Lists directories with respect to a given partial or full path of a directory + @param files relative address of an entry + */ + AUKN_SYM bool DirsInDirectory(const AuString &string, AuList &dirs); + + AUKN_SYM bool WriteFile(const AuString &path, const void *data, size_t length); + AUKN_SYM bool WriteString(const AuString &path, const AuString &str); + + AUKN_SYM bool ReadFile(const AuString &path, AuList &buffer); + AUKN_SYM bool ReadString(const AuString &path, AuString &buffer); + + AUKN_SYM bool FileExists(const AuString &path); + AUKN_SYM bool DirExists(const AuString &path); + + AUKN_SYM bool DirMk(const AuString &path); + + AUKN_SYM bool Remove(const AuString &path); + + AUKN_SYM bool Relink(const AuString &src, const AuString &dest); + + AUKN_SYM bool Copy(const AuString &src, const AuString &dest); +} + +#include "FileStream.hpp" +#include "FileArbitraryReader.hpp" +#include "FileReader.hpp" +#include "FileWriter.hpp" +#include "Resources.hpp" +#include "Stat.hpp" \ No newline at end of file diff --git a/Include/Aurora/IO/FS/FileArbitraryReader.hpp b/Include/Aurora/IO/FS/FileArbitraryReader.hpp new file mode 100644 index 00000000..aa329ea2 --- /dev/null +++ b/Include/Aurora/IO/FS/FileArbitraryReader.hpp @@ -0,0 +1,50 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: FileArbitraryReader.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::FS +{ + class FileArbitraryReader : public IArbitraryStreamer + { + public: + AU_NO_COPY_NO_MOVE(FileArbitraryReader) + + FileArbitraryReader() {} + ~FileArbitraryReader() {} + + template + bool OpenFile(T... args) + { + stream_ = OpenReadUnique(args...); + return stream_ != nullptr; + } + + virtual EStreamError Open() override + { + return stream_ ? EStreamError::eErrorNone : EStreamError::eErrorStreamInterrupted; + } + + virtual EStreamError ArbitraryRead(AuUInt32 offset, void *buffer, AuUInt32 &len) override + { + auto oldLen = std::exchange(len, 0); + if (!stream_) return EStreamError::eErrorStreamNotOpen; + stream_->SetOffset(offset); + len = stream_->Read(buffer, oldLen); + if (len == 0) return EStreamError::eErrorEndOfStream; + return EStreamError::eErrorNone; + } + + virtual void Close() override + { + stream_.reset(); + } + + private: + OpenReadUnique_t stream_{}; + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/FS/FileReader.hpp b/Include/Aurora/IO/FS/FileReader.hpp new file mode 100644 index 00000000..f4d85f42 --- /dev/null +++ b/Include/Aurora/IO/FS/FileReader.hpp @@ -0,0 +1,49 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: FileReader.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::FS +{ + class FileReader : public IStreamReader + { + public: + AU_NO_COPY_NO_MOVE(FileReader) + + FileReader() {} + ~FileReader() {} + + template + bool OpenFile(T... args) + { + stream_ = OpenReadUnique(args...); + return stream_ != nullptr; + } + + virtual EStreamError Open() override + { + return stream_ ? EStreamError::eErrorNone : EStreamError::eErrorStreamInterrupted; + } + + virtual EStreamError Read(void *buffer, AuUInt32 &len) override + { + auto oldLen = std::exchange(len, 0); + if (!stream_) return EStreamError::eErrorStreamNotOpen; + len = stream_->Read(buffer, oldLen); + if (len == 0) return EStreamError::eErrorEndOfStream; + return EStreamError::eErrorNone; + } + + virtual void Close() override + { + stream_.reset(); + } + + private: + OpenReadUnique_t stream_{}; + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/FS/FileStream.hpp b/Include/Aurora/IO/FS/FileStream.hpp new file mode 100644 index 00000000..70217d7f --- /dev/null +++ b/Include/Aurora/IO/FS/FileStream.hpp @@ -0,0 +1,26 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: FileStream.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::FS +{ + class IFileStream + { + public: + virtual AuUInt64 GetOffset() = 0; + virtual bool SetOffset(AuUInt64 offset) = 0; + virtual AuUInt64 GetLength() = 0; + virtual AuUInt64 Read(void *in, AuUInt64 length) = 0; + virtual AuUInt64 Write(const void *out, AuUInt64 length) = 0; + virtual void Close() = 0; + virtual void Flush() = 0; + }; + + AUKN_SHARED_API(OpenRead, IFileStream, const AuString &path); + AUKN_SHARED_API(OpenWrite, IFileStream, const AuString &path); +} \ No newline at end of file diff --git a/Include/Aurora/IO/FS/FileWriter.hpp b/Include/Aurora/IO/FS/FileWriter.hpp new file mode 100644 index 00000000..d811af44 --- /dev/null +++ b/Include/Aurora/IO/FS/FileWriter.hpp @@ -0,0 +1,54 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: FileWriter.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::FS +{ + class FileWriter : public IStreamWriter + { + public: + AU_NO_COPY_NO_MOVE(FileWriter) + + FileWriter() {} + ~FileWriter() {} + + template + bool OpenFile(T... args) + { + stream_ = OpenWriteUnique(args...); + return stream_ != nullptr; + } + + virtual EStreamError Open() override + { + return stream_ ? EStreamError::eErrorNone : EStreamError::eErrorStreamInterrupted; + } + + virtual EStreamError Write(const void *buffer, AuUInt32 len) override + { + if (!stream_) return EStreamError::eErrorStreamNotOpen; + len = stream_->Write(buffer, len); + if (len == 0) return EStreamError::eErrorEndOfStream; + if (len != len) return EStreamError::eErrorStreamInterrupted; + return EStreamError::eErrorNone; + } + + virtual void Flush() override + { + if (stream_) stream_->Flush(); + } + + virtual void Close() override + { + stream_.reset(); + } + + private: + OpenWriteUnique_t stream_{}; + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/FS/Resources.hpp b/Include/Aurora/IO/FS/Resources.hpp new file mode 100644 index 00000000..da28af7c --- /dev/null +++ b/Include/Aurora/IO/FS/Resources.hpp @@ -0,0 +1,25 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Resources.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::FS +{ + /** + Suffixes a global read/writable path with nameSpace and a splitter + */ + AUKN_SYM bool GetSystemDomain(AuString& path); + + /** + Suffixes a global read/writable path with within a home/sandbox directory + */ + AUKN_SYM bool GetProfileDomain(AuString& path); + + + AUKN_SYM bool GetSystemResourcePath(const AuString& fileName, AuString& path); + +} \ No newline at end of file diff --git a/Include/Aurora/IO/FS/Stat.hpp b/Include/Aurora/IO/FS/Stat.hpp new file mode 100644 index 00000000..8f736801 --- /dev/null +++ b/Include/Aurora/IO/FS/Stat.hpp @@ -0,0 +1,27 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Stat.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::FS +{ + struct Stat + { + bool exists; + bool existsFile, existsDirectory, existsSystemResource; + bool symLink; + AuInt64 created; + AuInt64 modified; + AuInt64 accessed; + AuUInt64 size; + }; + + /** + Classic file stat function + */ + AUKN_SYM bool StatFile(const AuString &path, Stat &stat); +} \ No newline at end of file diff --git a/Include/Aurora/IO/IArbitraryStreamer.hpp b/Include/Aurora/IO/IArbitraryStreamer.hpp new file mode 100644 index 00000000..ece8f95e --- /dev/null +++ b/Include/Aurora/IO/IArbitraryStreamer.hpp @@ -0,0 +1,19 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: IArbitraryStreamer.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO +{ + class IArbitraryStreamer + { + public: + virtual EStreamError Open() = 0; + virtual EStreamError ArbitraryRead(AuUInt32 offset, void *buffer, AuUInt32 &len) = 0; + virtual void Close() = 0; + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/IO.hpp b/Include/Aurora/IO/IO.hpp new file mode 100644 index 00000000..a4acbbec --- /dev/null +++ b/Include/Aurora/IO/IO.hpp @@ -0,0 +1,19 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: IO.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "EStreamError.hpp" + +#include "IStreamReader.hpp" +#include "IStreamWriter.hpp" +#include "IArbitraryStreamer.hpp" // arbitrary read stream, dunno what you would want to call thousands_sep + +#include "Buffered/Buffered.hpp" + +#include "FS/FS.hpp" +#include "Net/Net.hpp" \ No newline at end of file diff --git a/Include/Aurora/IO/IStreamReader.hpp b/Include/Aurora/IO/IStreamReader.hpp new file mode 100644 index 00000000..bb5fe1fc --- /dev/null +++ b/Include/Aurora/IO/IStreamReader.hpp @@ -0,0 +1,39 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: IStreamReader.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO +{ + class IStreamReader + { + public: + virtual EStreamError Open() = 0; + virtual EStreamError Read(void *buffer, AuUInt32 &len) = 0; + virtual void Close() = 0; + + EStreamError ReadAll(AuList &buffer) + { + static const int kBufferSize = 2048; + AuUInt8 temp[kBufferSize]; + + AuUInt32 len = kBufferSize; + EStreamError ret = EStreamError::eErrorEndOfStream; + + while ((ret = Read(temp, len)) == EStreamError::eErrorNone) + { + if (len == 0) break; + + buffer.insert(buffer.end(), temp, temp + len); + + if (len != kBufferSize) break; + } + + return ret; + } + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/IStreamWriter.hpp b/Include/Aurora/IO/IStreamWriter.hpp new file mode 100644 index 00000000..e5942c2b --- /dev/null +++ b/Include/Aurora/IO/IStreamWriter.hpp @@ -0,0 +1,20 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: IStreamWriter.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO +{ + class IStreamWriter + { + public: + virtual EStreamError Open() = 0; + virtual EStreamError Write(const void *buffer, AuUInt32 len) = 0; + virtual void Flush() = 0; + virtual void Close() = 0; + }; +} \ No newline at end of file diff --git a/Include/Aurora/IO/Net/Net.hpp b/Include/Aurora/IO/Net/Net.hpp new file mode 100644 index 00000000..c6b60d0e --- /dev/null +++ b/Include/Aurora/IO/Net/Net.hpp @@ -0,0 +1,183 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Net.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::IO::Net +{ + enum TransportProtocol + { + kProtocolUDP, + kProtocolTCP + }; + + enum IPProtocol + { + kIPProtocolV4, + kIPProtocolV6 + }; + + struct IPAddress + { + IPProtocol ip; + bool hasIP; + AuString address; + union + { + AuUInt8 v4[4]; + AuUInt16 v6[8]; + }; + + AUKN_SYM AuString toString(); + }; + + struct Endpoint + { + TransportProtocol protocol; + IPAddress address; + AuUInt16 port; + + // 0 - destination is a stateless datagram server + // 1 - destination is a psuedo-stateful server + AuUInt UDPTimeout; + + AUKN_SYM AuString toString(); + }; + + class INetworkStream; + class IBasicSocket; + class IClientSocket; + + using StreamHasData_cb = std::function socket)>; // DTLS/UDP/TCP/TLS -> TRUE = expects another frame or stream buffer, FALSE = end of socket life + using InitializeSocket_cb = std::function socket)>; + using ShutdownSocket_cb = std::function socket)>; + + using Error_t = std::error_code; + + class IBasicSocket + { + public: + virtual bool IsActive() = 0; + virtual Error_t GetLastError() = 0; + virtual void Shutdown() = 0; + virtual bool GetLocalEndpoint(Endpoint &out) = 0; + }; + + class IClientSocket : public IBasicSocket + { + public: + // called during loop pump + struct ClientCallbacks + { + StreamHasData_cb hasData; // on data available + ShutdownSocket_cb onShutdown; // Socket shutdown or otherwise dead + ShutdownSocket_cb onStreamOverSaturation; // DoS? + ShutdownSocket_cb onTLSHandshakeAbort; + }; + + virtual bool Initialize(const ClientCallbacks &callbacks) = 0; + virtual bool Initialize() = 0; + + virtual bool GetRemoteEndpoint(Endpoint &out) = 0; + + virtual bool ReadAsync(AuUInt8 *buffer, bool all, AuUInt32 &length) = 0; + virtual bool WriteAsync(const AuUInt8 *buffer, AuUInt32 length) = 0; + virtual bool WriteAsync(const AuUInt8 *buffer, AuUInt32 length, std::function) = 0; + + virtual AuUInt GetInternalRecvBuffer() = 0; + virtual bool SetInternalRecvBuffer(AuUInt bytes) = 0; + }; + + class ILocalClientSocket : public IClientSocket + { + public: + using ConnectCallback_cb = std::function; + virtual bool Connect() = 0; + virtual void ConnectAsync(ConnectCallback_cb cb) = 0; + }; + + struct TlsConnect + { + Aurora::Crypto::X509::Certificate serverCertificate; + AuSPtr socket; + }; + + using TlsConnect_cb = std::function; + + struct ServerInfo + { + Endpoint listen; + }; + + struct TLSServerInfo : ServerInfo + { + Aurora::Crypto::RSAPair cert; + }; + + struct ClientInfo + { + Endpoint socket; + AuString service; + }; + + struct TLSClientInfo : ClientInfo + { + TlsConnect_cb pinning; + }; + + class IServer : public IBasicSocket + { + public: + virtual bool AddAcceptCallback(const InitializeSocket_cb &accept) = 0; // on accept* + virtual bool AddExitCallback(const ShutdownSocket_cb &accept) = 0; // server shutdown* + + virtual void GetClients(AuList> &soccies) = 0; + virtual bool Listen() = 0; + }; + + class INetworkingPool + { + public: + /** + Supported thread models: + while (true) + { + // read from sockets pre-frame + PumpSOL(); + + // process other async network logic here + + // process writes and a few early reads before we sleep + PumpEOL(); + + // yield + Yield(); + } + + while (true) + { + // process other async network logic here + + // run + Run() + } + + */ + virtual void PumpSOL() = 0; + virtual void PumpEOL() = 0; + + virtual void Run() = 0; + + virtual bool NewServer(const ServerInfo &listen, IServer *&out) = 0; + virtual bool NewTlsServer(const TLSServerInfo &keys, IServer *&out) = 0; + virtual void DeleteServer(IServer *socket) = 0; + + virtual bool NewClient(const ClientInfo &info, ILocalClientSocket *&out) = 0; + virtual bool NewTlsClient(const TLSClientInfo &info, ILocalClientSocket *&out) = 0; + virtual void DeleteClient(ILocalClientSocket *socket) = 0; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Legacy/BIMap.hpp b/Include/Aurora/Legacy/BIMap.hpp new file mode 100644 index 00000000..8f5fdee9 --- /dev/null +++ b/Include/Aurora/Legacy/BIMap.hpp @@ -0,0 +1,9 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BIMap.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + diff --git a/Include/Aurora/Legacy/IDMap.hpp b/Include/Aurora/Legacy/IDMap.hpp new file mode 100644 index 00000000..3288fe80 --- /dev/null +++ b/Include/Aurora/Legacy/IDMap.hpp @@ -0,0 +1,9 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: IDMap.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + diff --git a/Include/Aurora/Legacy/Legacy.hpp b/Include/Aurora/Legacy/Legacy.hpp new file mode 100644 index 00000000..ddbaaad9 --- /dev/null +++ b/Include/Aurora/Legacy/Legacy.hpp @@ -0,0 +1,11 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Legacy.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "BIMap.hpp" +#include "IDMap.hpp" \ No newline at end of file diff --git a/Include/Aurora/Locale/Locale.hpp b/Include/Aurora/Locale/Locale.hpp new file mode 100644 index 00000000..47ba9bfb --- /dev/null +++ b/Include/Aurora/Locale/Locale.hpp @@ -0,0 +1,34 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Locale.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Locale +{ + struct LocalizationInfo + { + LocalizationInfo(const AuString& language, const AuString& country, const AuString& codeset) : Language(language), Country(country), Codeset(codeset) {} + + const AuString& Language; + const AuString& Country; + const AuString& Codeset; + }; + + /* + Retrieves information about the current system
+ This function will never fail
+ This function will never release memory + */ + AUKN_SYM LocalizationInfo GetLocale(); + + +#if defined(AURORA_PLATFORM_WIN32) || defined(I_REALLY_NEED_WIDECHAR_PUBAPI) + AUKN_SYM std::wstring ConvertFromUTF8(const AuString &in); + AUKN_SYM AuString ConvertFromWChar(const wchar_t *in, AuMach length); + AUKN_SYM AuString ConvertFromWChar(const wchar_t *in); +#endif +} \ No newline at end of file diff --git a/Include/Aurora/Memory/Handles.hpp b/Include/Aurora/Memory/Handles.hpp new file mode 100644 index 00000000..a4850b35 --- /dev/null +++ b/Include/Aurora/Memory/Handles.hpp @@ -0,0 +1,8 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Handles.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once diff --git a/Include/Aurora/Memory/Heap.hpp b/Include/Aurora/Memory/Heap.hpp new file mode 100644 index 00000000..6e99fd16 --- /dev/null +++ b/Include/Aurora/Memory/Heap.hpp @@ -0,0 +1,139 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Heap.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Memory +{ + class Heap + { + public: + virtual Types::size_t GetChunkSize(const void *head) = 0; + + template + T ZAlloc(Types::size_t length) + { + return reinterpret_cast(_ZAlloc(length)); + } + + template + T ZAlloc(Types::size_t length, Types::size_t align) + { + return reinterpret_cast(_ZAlloc(length, align)); + } + + template + T *ZAlloc() + { + return reinterpret_cast(_ZAlloc(sizeof(T))); + } + + template + T *NewArray(Types::size_t count) + { + return ZAlloc(count * sizeof(T)); + } + + template + T *NewArray(Types::size_t count, Types::size_t align) + { + return ZAlloc(count * sizeof(T), align); + } + + /// Fast, unsafe alloc + template + T FAlloc(Types::size_t length) + { + return reinterpret_cast(_FAlloc(length)); + } + + template + T FAlloc(Types::size_t length, Types::size_t align) + { + return reinterpret_cast(_FAlloc(length, align)); + } + + template + T ZRealloc(T in, Types::size_t length) + { + return reinterpret_cast(_ZRealloc(reinterpret_cast(in), length)); + } + + template + T ZRealloc(T in, Types::size_t length, Types::size_t alloc) + { + return reinterpret_cast(_ZRealloc(reinterpret_cast(in), length), alloc); + } + + template + T FRealloc(T in, Types::size_t length) + { + return reinterpret_cast(_SRealloc(reinterpret_cast(in), length)); + } + + template + T FRealloc(T in, Types::size_t length, Types::size_t alloc) + { + return reinterpret_cast(_SRealloc(reinterpret_cast(in), length), alloc); + } + + template + void Free(T in) + { + _Free(reinterpret_cast(in)); + } + + template + AuSPtr ToSmartPointer(T *in) + { + if (in == nullptr) return {}; + return + std::shared_ptr(in, + [=](T *delt) + { + if constexpr (std::is_class_v) + { + delt->~T(); + } + this->Free(delt); + }); + } + + template + static AuSPtr ToSmartPointer(AuSPtr heap, T *in) + { + return + std::shared_ptr(in, + [=](T *delt) + { + heap->Free(delt); + }); + } + + private: + virtual AU_ALLOC void *_ZAlloc(Types::size_t length) = 0; + virtual AU_ALLOC void *_ZAlloc(Types::size_t length, Types::size_t align) = 0; + virtual AU_ALLOC void *_FAlloc(Types::size_t length) = 0; + virtual AU_ALLOC void *_FAlloc(Types::size_t length, Types::size_t align) = 0; + virtual AU_ALLOC void *_ZRealloc(void *buffer, Types::size_t length, Types::size_t align) = 0; + virtual AU_ALLOC void *_ZRealloc(void *buffer, Types::size_t length) = 0; + virtual AU_ALLOC void *_FRealloc(void *buffer, Types::size_t length, Types::size_t align) = 0; + virtual AU_ALLOC void *_FRealloc(void *buffer, Types::size_t length) = 0; + virtual void _Free(void* buffer) = 0; + }; + + /** + Returns a heap interface backed by the default allocator + */ + AUKN_SHARED_API(GetDefaultDiscontiguousHeap, Heap); + + /** + Allocates Fize amount of memory + @return a heap backed by allocated memory + */ + AUKN_SHARED_API(AllocHeap, Heap, AuUInt size); +} \ No newline at end of file diff --git a/Include/Aurora/Memory/MemRef.hpp b/Include/Aurora/Memory/MemRef.hpp new file mode 100644 index 00000000..f878790f --- /dev/null +++ b/Include/Aurora/Memory/MemRef.hpp @@ -0,0 +1,32 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: MemRef.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Memory +{ + template + struct MemRef + { + MemRef(T *val) + { + Pointer = val; + } + + MemRef(T &val) + { + Pointer = &val; + } + + T *operator->() const + { + return Pointer; + } + + T *Pointer; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Memory/Memory.hpp b/Include/Aurora/Memory/Memory.hpp new file mode 100644 index 00000000..5dd79b3a --- /dev/null +++ b/Include/Aurora/Memory/Memory.hpp @@ -0,0 +1,143 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Memory.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include + +#include "MemRef.hpp" +#include "Heap.hpp" +#include "Handles.hpp" + +namespace Aurora::Memory +{ + AUKN_SYM AU_ALLOC void *_ZAlloc(Types::size_t length); + AUKN_SYM AU_ALLOC void *_ZAlloc(Types::size_t length, Types::size_t align); + AUKN_SYM AU_ALLOC void *_FAlloc(Types::size_t length); + AUKN_SYM AU_ALLOC void *_FAlloc(Types::size_t length, Types::size_t align); + AUKN_SYM AU_ALLOC void *_ZRealloc(void *buffer, Types::size_t length, Types::size_t align); + AUKN_SYM AU_ALLOC void *_ZRealloc(void *buffer, Types::size_t length); + AUKN_SYM AU_ALLOC void *_FRealloc(void *buffer, Types::size_t length, Types::size_t align); + AUKN_SYM AU_ALLOC void *_FRealloc(void *buffer, Types::size_t length); + AUKN_SYM void _Free(void *buffer); + + AUKN_SYM Types::size_t GetChunkSize(const void *head); + + // These memory management APIs do not support class types, and will likely *never* support them + // QST already handles dynamic allocation of structs in a given heap properly (afaik) + // _new, new, et al are backed by operator overloads directed towards these functions + // -> do not double init + // TODO: ensure typeof(T) is not a pointer of a class + + template + T ZAlloc(Types::size_t length) + { + static_assert(!std::is_class::type>::value, "Do not use heap/kmem apis with classes"); + return reinterpret_cast(_ZAlloc(length)); + } + + template + T ZAlloc(Types::size_t length, Types::size_t align) + { + static_assert(!std::is_class::type>::value, "Do not use heap/kmem apis with classes"); + return reinterpret_cast(_ZAlloc(length, align)); + } + + template + T *NewArray(Types::size_t count) + { + static_assert(!std::is_class::value, "Do not use heap/kmem apis with classes"); + return FAlloc(count * sizeof(T)); + } + + template + T *NewArray(Types::size_t count, Types::size_t align) + { + static_assert(!std::is_class::value, "Do not use heap/kmem apis with classes"); + return FAlloc(count * sizeof(T), align); + } + + template + AuSPtr AllocateFastArray(Types::size_t length, Types::size_t align = sizeof(T)) + { + static_assert(!std::is_class::type>::value, "Do not use heap/kmem apis with classes"); + return AuSPtr(reinterpret_cast(_FAlloc(length)), [](T *ptr) + { + _Free(ptr); + }); + } + + template + struct SpeedyArrayAllocator + { + typedef T value_type; + + SpeedyArrayAllocator() = default; + template constexpr SpeedyArrayAllocator(const SpeedyArrayAllocator &) noexcept + { + } + + [[nodiscard]] T *allocate(std::size_t n) + { + if (auto p = (NewArray(n))) + { + return p; + } + + throw std::bad_alloc(); + } + + void deallocate(T *p, std::size_t n) noexcept + { + _Free(p); + } + }; + + template + T FAlloc(Types::size_t length) + { + static_assert(!std::is_class::type>::value, "Do not use heap/kmem apis with classes"); + return reinterpret_cast(_FAlloc(length)); + } + + template + T FAlloc(Types::size_t length, Types::size_t align) + { + static_assert(!std::is_class::type>::value, "Do not use heap/kmem apis with classes"); + return reinterpret_cast(_FAlloc(length, align)); + } + + template + T ZRealloc(T in, Types::size_t length) + { + return reinterpret_cast(_ZRealloc(reinterpret_cast(in), length)); + } + + template + T ZRealloc(T in, Types::size_t length, Types::size_t alloc) + { + return reinterpret_cast(_ZRealloc(reinterpret_cast(in), length), alloc); + } + + template + T SRealloc(T in, Types::size_t length) + { + return reinterpret_cast(_SRealloc(reinterpret_cast(in), length)); + } + + template + T SRealloc(T in, Types::size_t length, Types::size_t alloc) + { + return reinterpret_cast(_SRealloc(reinterpret_cast(in), length), alloc); + } + + template + void Free(T in) + { + _Free(reinterpret_cast(in)); + } +} \ No newline at end of file diff --git a/Include/Aurora/Parse/Base32.hpp b/Include/Aurora/Parse/Base32.hpp new file mode 100644 index 00000000..97dc6f15 --- /dev/null +++ b/Include/Aurora/Parse/Base32.hpp @@ -0,0 +1,23 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Base32.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Parse +{ + AUKN_SYM bool Base32Decode(const AuString &in, AuList &decoded); + + AUKN_SYM bool Base32Encode(const void *buffer, AuMach length, AuString &encoded); + static bool Base32Encode(const AuString &in, AuString &encoded) + { + return Base32Encode(in.data(), in.size(), encoded); + } + static bool Base32Encode(const AuList &in, AuString &encoded) + { + return Base32Encode(in.data(), in.size(), encoded); + } +} \ No newline at end of file diff --git a/Include/Aurora/Parse/Base64.hpp b/Include/Aurora/Parse/Base64.hpp new file mode 100644 index 00000000..9461f1a1 --- /dev/null +++ b/Include/Aurora/Parse/Base64.hpp @@ -0,0 +1,23 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Base64.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Parse +{ + AUKN_SYM bool Base64Decode(const AuString &in, AuList &decoded, bool url = false); + + AUKN_SYM bool Base64Encode(const void *buffer, AuMach length, AuString &encoded, bool url = false); + static bool Base64Encode(const AuString &in, AuString &encoded, bool url = false) + { + return Base64Encode(in.data(), in.size(), encoded, url); + } + static bool Base64Encode(const AuList &in, AuString &encoded, bool url = false) + { + return Base64Encode(in.data(), in.size(), encoded, url); + } +} \ No newline at end of file diff --git a/Include/Aurora/Parse/LineParser.hpp b/Include/Aurora/Parse/LineParser.hpp new file mode 100644 index 00000000..d6bee763 --- /dev/null +++ b/Include/Aurora/Parse/LineParser.hpp @@ -0,0 +1,32 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LineParser.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Parse +{ + static void SplitNewlines(const AuString &in, std::function lineCallback) + { + AuMach index = 0; + AuString cpy = in; + + while ((index = cpy.find("\n")) != AuString::npos) + { + auto line = cpy.substr(0, index); + cpy = cpy.substr(index + 1); + + if (line[line.size() - 1] == '\r') + { + line.pop_back(); + } + + lineCallback(line); + } + + lineCallback(cpy); + } +} \ No newline at end of file diff --git a/Include/Aurora/Parse/Parse.hpp b/Include/Aurora/Parse/Parse.hpp new file mode 100644 index 00000000..e4e71667 --- /dev/null +++ b/Include/Aurora/Parse/Parse.hpp @@ -0,0 +1,14 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Parse.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "Base32.hpp" +#include "Base64.hpp" + +#include "LineParser.hpp" +#include "Parser.hpp" \ No newline at end of file diff --git a/Include/Aurora/Parse/Parser.hpp b/Include/Aurora/Parse/Parser.hpp new file mode 100644 index 00000000..cf04a4b5 --- /dev/null +++ b/Include/Aurora/Parse/Parser.hpp @@ -0,0 +1,163 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Parser.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include + +namespace Aurora::Parse +{ + enum class ParsableTag + { + kParseUInt = (const int)Aurora::Data::DataType::kTypeUInt, + kParseSInt = (const int)Aurora::Data::DataType::kTypeSInt, + kParseNumber = (const int)Aurora::Data::DataType::kTypeNumber, + kParseString = (const int)Aurora::Data::DataType::kTypeString, + kParseBoolean = (const int)Aurora::Data::DataType::kTypeBoolean, + kParseUUID = (const int)Aurora::Data::DataType::kTypeUUID, + kParseVec3 = (const int)Aurora::Data::DataType::kTypeVec3, + kParseVec4 = (const int)Aurora::Data::DataType::kTypeVec4, + kParseObject, + kParseStringVararg + }; + + struct ParseBit; + struct ParsedBit; + using ParseObject = AuList; + using ParsedObject = AuList; + + using ParsePrimativeValue = Aurora::Data::PrimitiveValue; + using ParseValue = Aurora::Data::Value; + + struct ParseValueEx : ParseValue + { + ParsedObject Object; + }; + + struct ParseBit + { + ParseBit(ParsableTag _Tag, const AuString &_Label) : tag(_Tag), label(_Label) + { + } + ParseBit(ParsableTag _Tag, const AuString &_Label, bool _Array, bool _Optional, bool _vararg) : tag(_Tag), label(_Label), array(_Array), optional(_Optional), vararg(_vararg) + { + } + ParseBit(ParsableTag _Tag) : tag(_Tag) + { + } + ParseBit(ParsableTag _Tag, bool _Array, bool _Optional, bool _vararg) : tag(_Tag), array(_Array), optional(_Optional), vararg(_vararg) + { + } + ParseBit(ParseObject _ObjectParse) : tag(ParsableTag::kParseObject), objectParse(_ObjectParse) + { + } + ParseBit(ParseObject _ObjectParse, const AuString &_Label) : tag(ParsableTag::kParseObject), objectParse(_ObjectParse), label(_Label) + { + } + + // marks this "parse bit" and all following part bits optional + bool optional = false; + + // if true, the lexer yoinks a length prefix token before iterating over array length + bool array = false; + + // if true, the lexer parses tag or object parse until EOS + bool vararg = false; + + // parse defintion + ParsableTag tag; + ParseObject objectParse; + + // optional: + AuString label = ""; + + // parse object + // {...} + // single parse bit: + // + // optional: + // () + // var arg: + // ( ...) + // arrays + // [ ] + }; + + struct ParsedBit + { + ParsedBit() + { + } + ParsedBit(ParsableTag _tag) : tag(_tag) + { + } + + AuMach count; + ParsableTag tag; + bool isArray; + + struct + { + ParseValueEx single; + AuList array; + } value; + }; + + struct ParseResult + { + AuString SyntaxError; + AuString DebugTree; + ParsedObject result; + }; + + using ConsumeStream_cb = std::function; + + static ConsumeStream_cb StringToConsumable(const AuString &str, AuMach &index) + { + auto getc = [&](AuUInt8 &byt) mutable + { + if (index == str.length()) + { + return false; + } + byt = str[index]; + index++; + return true; + }; + return getc; + } + + AUKN_SYM void VaildateStructure(const ParseObject &object); + AUKN_SYM bool ConsumeToken(ParsableTag type, ConsumeStream_cb getc, ParseValue &out); + + static bool ConsumeToken(ParsableTag type, const AuString &str, AuMach &index, ParseValueEx &out) + { + return ConsumeToken(type, StringToConsumable(str, index), out); + } + + static bool ConsumeToken(ParsableTag type, const AuString &str, ParseValueEx &out) + { + AuMach index{}; + return ConsumeToken(type, StringToConsumable(str, index), out); + } + + AUKN_SYM bool Parse(ParseResult &result, const ParseObject &structure, ConsumeStream_cb getc); + + static bool Parse(ParseResult &result, const ParseObject &structure, const AuString &str, AuMach &index) + { + return Parse(result, structure, StringToConsumable(str, index)); + } + + static bool Parse(ParseResult &result, const ParseObject &structure, const AuString &str) + { + AuMach index{}; + return Parse(result, structure, StringToConsumable(str, index)); + } + + AUKN_SYM void SerializeToken(ParsableTag type, const ParseValue &value, AuString &str); + AUKN_SYM void Serialize(const ParsedObject &structure, AuString &ret); +} \ No newline at end of file diff --git a/Include/Aurora/Process/Paths.hpp b/Include/Aurora/Process/Paths.hpp new file mode 100644 index 00000000..f3d54cab --- /dev/null +++ b/Include/Aurora/Process/Paths.hpp @@ -0,0 +1,20 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Paths.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Process +{ + /** + Retrieves the applications current working directory + */ + AUKN_SYM bool GetWorkingDirectory(AuString &path); + + AUKN_SYM bool GetProcName(AuString &executable); + AUKN_SYM bool GetProcPath(AuString &path); + AUKN_SYM bool GetProcFullPath(AuString &path); +} \ No newline at end of file diff --git a/Include/Aurora/Process/Process.hpp b/Include/Aurora/Process/Process.hpp new file mode 100644 index 00000000..6b8b54af --- /dev/null +++ b/Include/Aurora/Process/Process.hpp @@ -0,0 +1,11 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Process.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +#include "Paths.hpp" +#include "ProcessMap.hpp" \ No newline at end of file diff --git a/Include/Aurora/Process/ProcessMap.hpp b/Include/Aurora/Process/ProcessMap.hpp new file mode 100644 index 00000000..df9bc5ef --- /dev/null +++ b/Include/Aurora/Process/ProcessMap.hpp @@ -0,0 +1,24 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ModuleInfo.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Process +{ + struct Segment + { + AuUInt base; + AuUInt size; + char perms[6]; + AuString module; + }; + using Segments = AuList; + + AUKN_SYM std::optional GetSegment(AuUInt pointer); + AUKN_SYM Segments DumpExecutableRoot(); + AUKN_SYM Segments DumpExecutableAll(); +} \ No newline at end of file diff --git a/Include/Aurora/Processes/ESpawnType.hpp b/Include/Aurora/Processes/ESpawnType.hpp new file mode 100644 index 00000000..b4a5da35 --- /dev/null +++ b/Include/Aurora/Processes/ESpawnType.hpp @@ -0,0 +1,18 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ESpawnType.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Processes +{ + enum class ESpawnType + { + eSpawnAtomicOvermap, // posix: exec, win32: i dunno + eSpawnChildProcessWorker, // posix: thread, win32: job process + eSpawnThreadLeader // posix: thread leader / new sid / pid != 0 daemon, win32: process + }; +} \ No newline at end of file diff --git a/Include/Aurora/Processes/IProcess.hpp b/Include/Aurora/Processes/IProcess.hpp new file mode 100644 index 00000000..b003b520 --- /dev/null +++ b/Include/Aurora/Processes/IProcess.hpp @@ -0,0 +1,41 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: IProcess.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading +{ + class IWaitable; +} + +namespace Aurora::Processes +{ + class IProcess + { + public: + /// + /// @return the waitable of a waitable child process or thread leader + virtual Threading::IWaitable *AsWaitable() = 0; + + /// Assuming AsWaitable()->TryLock() && Spawn != SpawnType::kSpawnAtomicOvermap, + /// returns the true return value of the process + /// otherwise returns zero + virtual AuSInt GetExitCode() = 0; + + /// + /// @param exitOnSpawn atomically destroys the current process
+ /// some platforms only support this + /// + virtual bool Start(enum ESpawnType, bool fwdOut, bool fwdErr, bool fwdIn) = 0; + + virtual bool Terminate() = 0; + virtual bool TryKill() = 0; + + virtual bool Read(bool error, void *buffer, AuUInt32 &len) = 0; + virtual bool Write(const void *buffer, AuUInt32 len) = 0; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Processes/Open.hpp b/Include/Aurora/Processes/Open.hpp new file mode 100644 index 00000000..0099c004 --- /dev/null +++ b/Include/Aurora/Processes/Open.hpp @@ -0,0 +1,17 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Open.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Processes +{ + /// opens a standard uri formatted string (eg, protocol://endpoint/path) + AUKN_SYM void OpenUri(const AuString &uri); + + /// opens a file given a relative path or abs path + AUKN_SYM void OpenFile(const AuString &file); +} \ No newline at end of file diff --git a/Include/Aurora/Processes/Processes.hpp b/Include/Aurora/Processes/Processes.hpp new file mode 100644 index 00000000..550ced6f --- /dev/null +++ b/Include/Aurora/Processes/Processes.hpp @@ -0,0 +1,14 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Processes.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "ESpawnType.hpp" +#include "IProcess.hpp" +#include "Spawn.hpp" + +#include "Open.hpp" \ No newline at end of file diff --git a/Include/Aurora/Processes/Spawn.hpp b/Include/Aurora/Processes/Spawn.hpp new file mode 100644 index 00000000..1b11bbe2 --- /dev/null +++ b/Include/Aurora/Processes/Spawn.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Spawn.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Processes +{ + AUKN_SHARED_API(Spawn, IProcess, const AuString &app, const AuList &args); +} \ No newline at end of file diff --git a/Include/Aurora/RNG/RNG.hpp b/Include/Aurora/RNG/RNG.hpp new file mode 100644 index 00000000..fff1ea31 --- /dev/null +++ b/Include/Aurora/RNG/RNG.hpp @@ -0,0 +1,53 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RNG.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::RNG +{ + AUKN_SYM void ReadSecureRNG(void *in, AuUInt length); + + template + void Read(T(&array)[N]) + { + ReadSecureRNG(array, N * sizeof(T)); + } + + template + void Read(T *array, AuUInt length) + { + ReadSecureRNG(array, length * sizeof(T)); + } + + enum class RngStringCharacters + { + eAlphaCharacters, + eAlphaNumericCharacters, + eExtendedEntropy, + MAX + }; + + static void RngString(char *string, AuUInt length, RngStringCharacters type = RngStringCharacters::eAlphaCharacters) + { + static std::pair rngSequence[static_cast(RngStringCharacters::MAX)] = + { + {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 52}, + {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", 62}, + {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890=-+!$%^*.[];:", 75} + }; + + ReadSecureRNG(string, length); + + const auto &pair = rngSequence[static_cast(type)]; + + for (auto i = 0; i < length; i++) + { + auto idx = static_cast(reinterpret_cast(string)[i]) / 255.f * static_cast(pair.second); + string[i] = pair.first[static_cast(idx)]; + } + } +} \ No newline at end of file diff --git a/Include/Aurora/Registry/ERegistrySource.hpp b/Include/Aurora/Registry/ERegistrySource.hpp new file mode 100644 index 00000000..74ab296e --- /dev/null +++ b/Include/Aurora/Registry/ERegistrySource.hpp @@ -0,0 +1,18 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ERegistrySource.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Registry +{ + enum ERegistrySource + { + eFSGlobal, + eFSProfile, + eGlobal + }; +} \ No newline at end of file diff --git a/Include/Aurora/Registry/IRegistry.hpp b/Include/Aurora/Registry/IRegistry.hpp new file mode 100644 index 00000000..cdcb180d --- /dev/null +++ b/Include/Aurora/Registry/IRegistry.hpp @@ -0,0 +1,23 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: IRegistry.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Registry +{ + using RegistryType = Aurora::Data::DataType; + using RegistryValue = Aurora::Data::Value; + + class IRegistry + { + public: + virtual bool KeyExists(const AuString &key, RegistryType &type) = 0; + virtual bool GetOrCreateKey(const AuString &key, const RegistryValue &def, RegistryValue &value) = 0; + virtual bool SetKey(const AuString &key, const RegistryValue &value) = 0; + virtual bool GetKey(const AuString &key, RegistryValue &value) = 0; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Registry/Registry.hpp b/Include/Aurora/Registry/Registry.hpp new file mode 100644 index 00000000..01bbf148 --- /dev/null +++ b/Include/Aurora/Registry/Registry.hpp @@ -0,0 +1,18 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Registry.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include + +#include "ERegistrySource.hpp" +#include "IRegistry.hpp" + +namespace Aurora::Registry +{ + AUKN_SHARED_API(Registry, IRegistry, ERegistrySource source, const AuString &name); +} \ No newline at end of file diff --git a/Include/Aurora/Runtime.hpp b/Include/Aurora/Runtime.hpp new file mode 100644 index 00000000..9dc010ca --- /dev/null +++ b/Include/Aurora/Runtime.hpp @@ -0,0 +1,138 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Runtime.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#if defined(AURORA_ENGINE_KERNEL_STATIC) + #define AUKN_SYM +#else + #if defined(AURORA_ENGINE_KERNEL_EXPORT) + #define AUKN_SYM AURORA_SYMBOL_EXPORT + #else + #define AUKN_SYM AURORA_SYMBOL_IMPORT + #endif +#endif + +#include "../AuroraMacros.hpp" + +#if defined(_AUHAS_ASIO) + #include +#endif + +#include +#include + +#include "../AuroraTypedefs.hpp" + +#include +#include +#include +#include +#include + +#define AUKN_SHARED_API(name, type, ...) AU_SHARED_API_EX(AUKN_SYM, name, type, ## __VA_ARGS__) + +#include "Memory/Memory.hpp" + +#include "Console/Console.hpp" +#include "Crypto/Crypto.hpp" +#include "Compression/Compression.hpp" +#include "Data/Data.hpp" +#include "Debug/Debug.hpp" +#include "Hashing/Hashing.hpp" +#include "HWInfo/HWInfo.hpp" +#include "IO/IO.hpp" +#include "Legacy/Legacy.hpp" +#include "Locale/Locale.hpp" +#include "Parse/Parse.hpp" +#include "Process/Process.hpp" +#include "Processes/Processes.hpp" +#include "Registry/Registry.hpp" +#include "RNG/RNG.hpp" +#include "Telemetry/Telemetery.hpp" +#include "Threading/Threading.hpp" +#include "Async/Async.hpp" +#include "Time/Time.hpp" + +#include "../AuroraUtils.hpp" + +namespace Aurora +{ + struct LocalLogInfo + { + bool enableLogging {true}; + bool autoCompressOldLogs {false}; + AuUInt32 maxSizeMB {128 * 1024 * 1024}; // these numbers feel insane, but at least we have a max threshold + int maxLogs {1024}; // by default, we neither leak disk space or waste opportunities of being able to dig through old data + }; + + struct TelemetryConfigDoc + { + LocalLogInfo localLogging; + bool enabled {false}; + + AuString address; + AuUInt16 port {45069}; + AuString serviceIdnt {"7b5f7a54-7122-4489-ac1a-3d75884b307e"}; + + bool wantsActiveMonitoring {false}; + + bool privacyNoData {false}; + bool privacyNoMod {false}; + bool privacyNoLog {false}; + bool privacyNoWarn {false}; + + bool alwaysCrashdump {true}; + bool alwaysDisableCrashdump {false}; + + }; + + struct TelemetryConfig + { + bool readModNameJsonConfig {}; + TelemetryConfigDoc defaultConfig; + }; + + struct ConsoleConfig + { + LocalLogInfo logging; + + bool disableAll {}; + + bool forceConsoleWindow {}; + bool forceToolKitWindow {}; + + bool attemptDarkTheme {true}; + + AuString supportPublic {"https://jira.reece.sx"}; + AuString supportInternal {"https://jira.reece.sx"}; + }; + + struct CryptoConfig + { + bool allowChineseCerts {false}; + + bool allowRussianCerts {true}; + + bool allowHTTPRetrievalOfCerts {true}; + bool enablePinning {true}; + AuList blacklistedCerts{}; + AuList whitelistedCerts{}; + }; + + struct RuntimeStartInfo + { + ConsoleConfig console; + CryptoConfig crypto; + TelemetryConfig telemetry; + }; + + AUKN_SYM void RuntimeStart(const RuntimeStartInfo &info); + AUKN_SYM void RuntimeShutdown(); + + AUKN_SYM void RuntimeSysPump(); +} \ No newline at end of file diff --git a/Include/Aurora/Telemetry/BlackBox.hpp b/Include/Aurora/Telemetry/BlackBox.hpp new file mode 100644 index 00000000..25f8f298 --- /dev/null +++ b/Include/Aurora/Telemetry/BlackBox.hpp @@ -0,0 +1,168 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BlackBox.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Telemetry +{ + enum class ENewBlackBoxEntry + { + eSpecsMetadata, + eMessage, + eStackWarning, + eWinCxxException, + eAccessViolation, + eAssertion, + ePanic, + eLog, + eScreenshot, + eMinidump, + eAnnounceMap, + eAnnounceThreadName, + eAnnounceApplication, + eAnnounceSymbols + }; + + struct NewBlockboxEntryMessage + { + AuUInt address; + AuUInt32 category; + AuString message; + AuUInt32 threadId; + }; + + struct NewBlockBoxEntryStackReport + { + Debug::StackTrace backtrace; + }; + + struct NewBlackboxEntryUpdateMapEntry + { + AuUInt base; + AuUInt size; + char perms[6]; + AuString module; + }; + + struct NewBlackboxEntryUpdateMap + { + Aurora::Process::Segments map; + }; + + enum class ENewBlackBoxAccessViolation + { + eUnknown, + eNXViolation, + eWriteViolation, + eMissingPage + }; + + struct NewBlockBoxEntryMemoryViolation + { + AuUInt address; + AuUInt code; + ENewBlackBoxAccessViolation type; + NewBlockBoxEntryStackReport stack; + }; + + struct NewBlockBoxEntryWin32CxxException + { + NewBlockBoxEntryStackReport stack; + AuString str; + }; + + struct NewBlockboxEntryBasicMessage + { + Console::ConsoleMessage message; + }; + + enum class ENewBlackBoxResourceType + { + eLocal, + eResource + }; + + struct NewBlackBoxResource + { + ENewBlackBoxResourceType type; + + AuString path; + int resourceId; + }; + + struct NewBlackBoxEntryScreenshot + { + NewBlackBoxResource resource; + }; + + enum class MinidumpType + { + eMSVC, + eBreakpadMSVC, + eBrokenElfRamDump, + eMagicalMinidump, + eExternalEcosystemTransaction + }; + + struct NewBlackBoxEntryMinidump + { + MinidumpType type; + bool includesRx; + Aurora::Build::EPlatform platform; + NewBlackBoxResource resource; + }; + + struct NewBlockboxEntryReportSpecs + { + Aurora::Build::EPlatform platform; + Aurora::Build::ECompiler compiler; + Aurora::Build::EABI abi; + + Aurora::HWInfo::CpuInfo cpuInfo; + + AuUInt16 ramMb; + AuUInt16 remainingMb; + AuUInt16 pageSize; + }; + + struct NewBlackBoxEntryReportApplication + { + AuUInt16 applicationServiceNumber; + AuString applicationExecutableName; + AuString applicationPath; + + AuString locality; + }; + + struct NewBlackBoxEntryMapThread + { + AuUInt64 id; + AuUInt64 name; + }; + + struct NewBlockboxEntry + { + ENewBlackBoxEntry type; + NewBlackBoxEntryReportApplication process; + NewBlockboxEntryReportSpecs specs; + NewBlockboxEntryMessage message; + NewBlockboxEntryMessage assertion; + NewBlockBoxEntryStackReport stack; + NewBlockBoxEntryWin32CxxException wincxx; + NewBlockboxEntryBasicMessage log; + NewBlockboxEntryBasicMessage panic; + NewBlockBoxEntryMemoryViolation violation; + NewBlackBoxEntryMinidump minidump; + NewBlackboxEntryUpdateMap map; + }; + + using StreamWriter = std::function; + using StreamReader = std::function; + + AUKN_SYM AuList ReadBlackboxStream(const StreamReader &reader); + AUKN_SYM void WriteBlackboxStream(const AuList &, StreamWriter writer); +} \ No newline at end of file diff --git a/Include/Aurora/Telemetry/Telemetery.hpp b/Include/Aurora/Telemetry/Telemetery.hpp new file mode 100644 index 00000000..d4c0629a --- /dev/null +++ b/Include/Aurora/Telemetry/Telemetery.hpp @@ -0,0 +1,15 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Telemetery.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "BlackBox.hpp" + +namespace Aurora::Telemetry +{ + AUKN_SYM void Mayday(); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/IWaitable.hpp b/Include/Aurora/Threading/IWaitable.hpp new file mode 100644 index 00000000..991539ed --- /dev/null +++ b/Include/Aurora/Threading/IWaitable.hpp @@ -0,0 +1,29 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: IWaitable.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading +{ + /** + IWaitable represents a generic waitable primitive
+ There is no guarantee of any specific underlying primitive or backing by the operating system directly
+ *All* methods **must** be supported on all platforms. If they don't, think harder!
+ Implementable on: NX, Win32 via CVs, and others via pthreads
+ Zero timeout = infinity
+ */ + class IWaitable + { + public: + virtual bool HasOSHandle(AuMach &mach) = 0; + virtual bool HasLockImplementation() = 0; + virtual void Lock() = 0; + virtual bool Lock(AuUInt64 timeout /*=0*/) = 0; + virtual bool TryLock() = 0; + virtual void Unlock() = 0; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/LockGuard.hpp b/Include/Aurora/Threading/LockGuard.hpp new file mode 100644 index 00000000..49f19e1b --- /dev/null +++ b/Include/Aurora/Threading/LockGuard.hpp @@ -0,0 +1,31 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LockGuard.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading +{ + template + class LockGuard + { + public: + LockGuard(Aurora::Memory::MemRef lock) : lockReference_(lock) + { + lockReference_->Lock(); + } + + ~LockGuard() + { + lockReference_->Unlock(); + } + + private: + Aurora::Memory::MemRef lockReference_; + }; + + using WaitableLockGuard = LockGuard; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/LockGuardPtr.hpp b/Include/Aurora/Threading/LockGuardPtr.hpp new file mode 100644 index 00000000..2e0e2e15 --- /dev/null +++ b/Include/Aurora/Threading/LockGuardPtr.hpp @@ -0,0 +1,29 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LockGuardPtr.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading +{ + template + class LockGuardPtr + { + public: + LockGuardPtr(T *lock) : lockReference_(lock) + { + lockReference_->Lock(); + } + + ~LockGuardPtr() + { + lockReference_->Unlock(); + } + + private: + T *lockReference_; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Primitives/ConditionEx.hpp b/Include/Aurora/Threading/Primitives/ConditionEx.hpp new file mode 100644 index 00000000..3a2f9338 --- /dev/null +++ b/Include/Aurora/Threading/Primitives/ConditionEx.hpp @@ -0,0 +1,28 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConditionalEx.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Primitives +{ + /** + A comprehensive CV that does not strictly abide by typical assumptions.
+ This object is not optimal; however, it will work with any or no IWaitable
+ On systems where context switches are expensive, this object should be avoided at all costs
+ This object depends on the synchronization of primitives within our abstraction layer rather than the OS's implementation of condition variables
+ */ + class ConditionEx + { + public: + virtual void WaitForSignal() = 0; + virtual void WaitForSignal(Aurora::Threading::IWaitable *waitable) = 0; + virtual void Broadcast() = 0; + virtual void Signal() = 0; + }; + + AUKN_SHARED_API(FeaturefulCondition, ConditionEx); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Primitives/ConditionMutex.hpp b/Include/Aurora/Threading/Primitives/ConditionMutex.hpp new file mode 100644 index 00000000..922bea46 --- /dev/null +++ b/Include/Aurora/Threading/Primitives/ConditionMutex.hpp @@ -0,0 +1,27 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConditionMutex.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Primitives +{ + /** + An extremely basic mutex primitive.
+ This should be used with the traditional CV idiom of lock(), while(cond) { cv.wait() }, unlock() .
+ For other use cases, you should use the alternative mutex.
+ Note the usage of *should*; you could use this as a replacement to the standard mutex should there be no
+ desire of the IWaitable interface. + */ + class IConditionMutex + { + public: + virtual void Lock() = 0; + virtual void Unlock() = 0; + }; + + AUKN_SHARED_API(ConditionMutex, IConditionMutex); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Primitives/ConditionVariable.hpp b/Include/Aurora/Threading/Primitives/ConditionVariable.hpp new file mode 100644 index 00000000..f3004dc6 --- /dev/null +++ b/Include/Aurora/Threading/Primitives/ConditionVariable.hpp @@ -0,0 +1,29 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConditionVariable.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Primitives +{ + /** + A standard conditional variable tied to IConditionalMutex
+ Multiple conditionals may use a common IConditionalMutex.
+ Unlike the POSIX standard,
+ 1) we define no waitable as illegal
+ 2) conditional variables may not rebind their mutex + */ + class IConditionVariable + { + public: + virtual IConditionMutex* GetMutex() = 0; + virtual bool WaitForSignal(AuUInt32 timeout = 0) = 0; + virtual void Broadcast() = 0; + virtual void Signal() = 0; + }; + + AUKN_SHARED_API(ConditionVariable, IConditionVariable, IConditionMutex *); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Primitives/CriticalSection.hpp b/Include/Aurora/Threading/Primitives/CriticalSection.hpp new file mode 100644 index 00000000..3c5f00ad --- /dev/null +++ b/Include/Aurora/Threading/Primitives/CriticalSection.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CriticalSection.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Primitives +{ + AUKN_SHARED_API(CriticalSection, IWaitable); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Primitives/Event.hpp b/Include/Aurora/Threading/Primitives/Event.hpp new file mode 100644 index 00000000..927b8881 --- /dev/null +++ b/Include/Aurora/Threading/Primitives/Event.hpp @@ -0,0 +1,38 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Event.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Primitives +{ + /** + Event synchronization primitive + */ + class IEvent : public IWaitable + { + public: + // Unlike the other types, unlock is always a nop. It makes sense for the schedular or any other caller to not automatically reset an event, contrary to `::Lock(); work; ::Unlock();` + // Ordinarily, we would expect the thread responsible for dispatching work to Unlock, and the caller Reset, at least in principle. This, however, does not account for reusability. + // It makes sense to implement reset-on-worker-acknowledge, permit-multiple-triggers (a kind of countless semaphores), and any combination thereof + // Besides from atomicRelease, Reset and Set is on your watch. + + inline void Wait() + { + Lock(); + } + + inline void Wait(AuUInt32 ms) + { + Lock(ms); + } + + virtual void Reset() = 0; + virtual void Set() = 0; + }; + + AUKN_SHARED_API(Event, IEvent, bool triggerd, bool atomicRelease, bool permitMultipleTriggers = false); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Primitives/Mutex.hpp b/Include/Aurora/Threading/Primitives/Mutex.hpp new file mode 100644 index 00000000..36576893 --- /dev/null +++ b/Include/Aurora/Threading/Primitives/Mutex.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Mutex.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Primitives +{ + AUKN_SHARED_API(Mutex, IWaitable); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Primitives/Primitives.hpp b/Include/Aurora/Threading/Primitives/Primitives.hpp new file mode 100644 index 00000000..a303f817 --- /dev/null +++ b/Include/Aurora/Threading/Primitives/Primitives.hpp @@ -0,0 +1,19 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Primitives.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "ConditionMutex.hpp" +#include "ConditionVariable.hpp" + +#include "Mutex.hpp" +#include "Event.hpp" +#include "Semaphore.hpp" +#include "RWLock.hpp" +#include "SpinLock.hpp" +#include "CriticalSection.hpp" +#include "ConditionEx.hpp" \ No newline at end of file diff --git a/Include/Aurora/Threading/Primitives/RWLock.hpp b/Include/Aurora/Threading/Primitives/RWLock.hpp new file mode 100644 index 00000000..5dd34a47 --- /dev/null +++ b/Include/Aurora/Threading/Primitives/RWLock.hpp @@ -0,0 +1,20 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RWLock.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Primitives +{ + class RWLock + { + public: + virtual IWaitable *AsReadable() = 0; + virtual IWaitable *AsWritable() = 0; + }; + + AUKN_SHARED_API(RWLock, RWLock); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Primitives/Semaphore.hpp b/Include/Aurora/Threading/Primitives/Semaphore.hpp new file mode 100644 index 00000000..106d41fa --- /dev/null +++ b/Include/Aurora/Threading/Primitives/Semaphore.hpp @@ -0,0 +1,19 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Semaphore.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Primitives +{ + class ISemaphore : public IWaitable + { + public: + virtual void Unlock(long count) = 0; + }; + + AUKN_SHARED_API(Semaphore, ISemaphore, int initialCount = 0); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Primitives/SpinLock.hpp b/Include/Aurora/Threading/Primitives/SpinLock.hpp new file mode 100644 index 00000000..2d2b24bc --- /dev/null +++ b/Include/Aurora/Threading/Primitives/SpinLock.hpp @@ -0,0 +1,37 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: SpinLock.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Primitives +{ + // NOTE: A SPINLOCK CAN NOT BE AN IWAITABLE BY DEFINITION + // Partial interface support exists for completeness alone! + class AUKN_SYM SpinLock : public IWaitable + { + public: + SpinLock(); + + bool HasOSHandle(AuMach &mach) override; + bool HasLockImplementation() override; + + bool Lock(AuUInt64 timeout) override; + bool TryLock() override; + + void Lock() override; + void Unlock() override; + + private: + #if defined(_AU_HAS_ATOMIC_INTRINS) + volatile AuAtomicInt value_; + #else + std::atomic_long value_; + #endif + }; + + AUKN_SHARED_API(SpinLock, SpinLock); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Sleep.hpp b/Include/Aurora/Threading/Sleep.hpp new file mode 100644 index 00000000..c5af890b --- /dev/null +++ b/Include/Aurora/Threading/Sleep.hpp @@ -0,0 +1,14 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Sleep.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading +{ + AUKN_SYM void Sleep(AuUInt64 timeout = 0); + AUKN_SYM void SleepNs(AuUInt64 timeout = 0); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threading.hpp b/Include/Aurora/Threading/Threading.hpp new file mode 100644 index 00000000..0756c388 --- /dev/null +++ b/Include/Aurora/Threading/Threading.hpp @@ -0,0 +1,29 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Threading.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "IWaitable.hpp" + +#include "Waitables/Waitables.hpp" +#include "Primitives/Primitives.hpp" + +#include "WaitFor.hpp" +#include "Sleep.hpp" + +#include "LockGuard.hpp" +#include "LockGuardPtr.hpp" + +#include "Threads/Threads.hpp" + +namespace Aurora::Threading +{ + /** + @deprecated + */ + AUKN_SYM void ContextYield(); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threads/AbstractThreadVectors.hpp b/Include/Aurora/Threading/Threads/AbstractThreadVectors.hpp new file mode 100644 index 00000000..9f6cdc4f --- /dev/null +++ b/Include/Aurora/Threading/Threads/AbstractThreadVectors.hpp @@ -0,0 +1,23 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AbstractThreadVectors.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Threads +{ + class IAuroraThread; + + /*! + User-provided interface-style callback structure of thread related events
+ DoRun/DoExit are called within the threads context + */ + struct AbstractThreadVectors + { + std::function DoRun; + std::function DoExit; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threads/EThreadPrio.hpp b/Include/Aurora/Threading/Threads/EThreadPrio.hpp new file mode 100644 index 00000000..676fe960 --- /dev/null +++ b/Include/Aurora/Threading/Threads/EThreadPrio.hpp @@ -0,0 +1,21 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EThreadPrio.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Threads +{ + enum class EThreadPrio + { + eInvalid, + ePrioAboveHigh, + ePrioHigh, + ePrioNormal, + ePrioSub, + ePrioRT + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threads/IThreadFeature.hpp b/Include/Aurora/Threading/Threads/IThreadFeature.hpp new file mode 100644 index 00000000..38a08e6e --- /dev/null +++ b/Include/Aurora/Threading/Threads/IThreadFeature.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace Aurora::Threading::Threads +{ + class IThreadFeature + { + public: + virtual void Init() = 0; + virtual void Cleanup() = 0; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threads/TLSStaticVariable.hpp b/Include/Aurora/Threading/Threads/TLSStaticVariable.hpp new file mode 100644 index 00000000..fd2650d9 --- /dev/null +++ b/Include/Aurora/Threading/Threads/TLSStaticVariable.hpp @@ -0,0 +1,17 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: TLSStaticVariable.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Threads +{ + template + class TLSStaticVariable + { + // TODO: implement templates for a thread feature based tls implementation + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threads/TLSVariable.hpp b/Include/Aurora/Threading/Threads/TLSVariable.hpp new file mode 100644 index 00000000..7f470295 --- /dev/null +++ b/Include/Aurora/Threading/Threads/TLSVariable.hpp @@ -0,0 +1,66 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: TLSVariable.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Threads +{ + template + class TLSVariable + { + private: + int _; + + public: + TLSVariable() {} + ~TLSVariable() + { + GetThread()->GetTlsView()->Remove(GetHandle()); + } + + AuUInt64 GetHandle() + { + return (AuUInt64(reinterpret_cast(&_)) & (~kTlsKeyMask)) | kTlsKeyFollowsConvention | kTlsKeyResettablePointerHandle; + } + + T &Get() + { + auto view = GetThread()->GetTlsView(); + auto ptr = view->GetOrSetup(GetHandle(), + [](void *buffer) -> void + { + if constexpr (std::is_class_v) + { + new (buffer) T(); + } + else + { + //std::memset(buffer, 0, sizeof(T)); + } + }, + [](void *buffer) -> void + { + if constexpr (std::is_class_v) + { + reinterpret_cast(buffer)->~T(); + } + }); + return *reinterpret_cast(ptr); + } + + T& operator ->() + { + return Get(); + } + + TLSVariable& operator =(const T & val) + { + Get() = val; + return *this; + } + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threads/TLSView.hpp b/Include/Aurora/Threading/Threads/TLSView.hpp new file mode 100644 index 00000000..26dd05b7 --- /dev/null +++ b/Include/Aurora/Threading/Threads/TLSView.hpp @@ -0,0 +1,42 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: TLSView.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Threads +{ + static const AuUInt64 kTlsKeyFollowsConvention = AuUInt64(1) << AuUInt64(64 - 1); + static const AuUInt64 kTlsKeyStaticPointerHandle = AuUInt64(1) << AuUInt64(63 - 1); + static const AuUInt64 kTlsKeyResettablePointerHandle = AuUInt64(1) << AuUInt64(62 - 1); + static const AuUInt64 kTlsKeyMask = kTlsKeyResettablePointerHandle | kTlsKeyStaticPointerHandle | kTlsKeyFollowsConvention; + + class TLSView + { + public: + using TlsCb = std::function; + + virtual void Remove(AuUInt64 key) = 0; + virtual void *GetTLS(AuUInt64 key, AuMach length, bool noAutoCreate = false) = 0; + virtual void *InitTLS(AuUInt64 key, AuMach length, const TlsCb &init, const TlsCb &deinit) = 0; + + void *GetOrSetup(AuUInt64 key, AuMach length, const TlsCb &init, const TlsCb &deinit) + { + void *tls; + #if 0 + if (!(tls = GetTLS(key, length, true))) + #endif + { + tls = InitTLS(key, length, init, deinit); + if (!tls) + { + return nullptr; + } + } + return tls; + } + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threads/Thread.hpp b/Include/Aurora/Threading/Threads/Thread.hpp new file mode 100644 index 00000000..bc2514a1 --- /dev/null +++ b/Include/Aurora/Threading/Threads/Thread.hpp @@ -0,0 +1,47 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Thread.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "AbstractThreadVectors.hpp" + +namespace Aurora::Threading::Threads +{ + class TLSView; + + + /// Threads may be reimplemented as fibers or under some other userland context switcher + class IAuroraThread + { + public: + virtual void Run() = 0; + virtual void Exit() = 0; + virtual bool Exiting() = 0; + virtual void SendExitSignal() = 0; + + virtual void SetPrio(EThreadPrio prio) = 0; + virtual void SetAffinity(AuUInt32 mask) = 0; + virtual void SetName(const AuString &name) = 0; + + virtual EThreadPrio GetPrio() = 0; + virtual AuUInt32 GetMask() = 0; + virtual AuString GetName() = 0; + + virtual AuSPtr GetTlsView() = 0; + + virtual void ExecuteInDeadThread(std::function callback) = 0; + virtual IWaitable * AsWaitable() = 0; + }; + + AUKN_SYM IAuroraThread *GetThread(); + AUKN_SYM AuThreadId_t GetThreadId(); + + /** + Constructs a thread instance given a set of callbacks + */ + AUKN_SHARED_API(Thread, IAuroraThread, const AbstractThreadVectors &vectors); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threads/Threads.hpp b/Include/Aurora/Threading/Threads/Threads.hpp new file mode 100644 index 00000000..596e3ee7 --- /dev/null +++ b/Include/Aurora/Threading/Threads/Threads.hpp @@ -0,0 +1,16 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Threads.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +#include "IThreadFeature.hpp" +#include "EThreadPrio.hpp" +#include "AbstractThreadVectors.hpp" +#include "Thread.hpp" +#include "TLSView.hpp" +#include "TLSVariable.hpp" +#include "TLSStaticVariable.hpp" \ No newline at end of file diff --git a/Include/Aurora/Threading/WaitFor.hpp b/Include/Aurora/Threading/WaitFor.hpp new file mode 100644 index 00000000..90b037c3 --- /dev/null +++ b/Include/Aurora/Threading/WaitFor.hpp @@ -0,0 +1,36 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: WaitFor.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading +{ + using PollCallback_cb = std::function; + + AUKN_SYM bool YieldPollNs(bool permitMultipleContextSwitches, AuUInt64 timeoutNs, PollCallback_cb cb); + AUKN_SYM bool YieldPoll(bool permitMultipleContextSwitches, AuUInt64 timeoutMs, PollCallback_cb cb); + + /*! + Waits for a list of IWaitable objects.
+ See: Mutex, CriticalSection, Semaphore, Event, Thread, Async, and others + */ + AUKN_SYM bool WaitFor(IWaitable *waitable, AuUInt64 timeout = 0); + + static bool WaitFor(std::atomic &value, AuUInt64 timeout = 0) + { + Waitables::BooleanWaitable waitable(value); + return WaitFor(&waitable, timeout); + } + + /*! + Waits on a list of IWaitable objects.
+ See: Mutex, CriticalSection, Semaphore, Event, Thread, Async, and others
+ On timeout, returns false
+ On error, waitables are restored to their state at the point of WaitFors call + */ + AUKN_SYM bool WaitFor(const AuList &waitables, AuUInt64 timeout = 0); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Waitables/BooleanWaitable.hpp b/Include/Aurora/Threading/Waitables/BooleanWaitable.hpp new file mode 100644 index 00000000..b67f390c --- /dev/null +++ b/Include/Aurora/Threading/Waitables/BooleanWaitable.hpp @@ -0,0 +1,53 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BooleanWaitable.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Waitables +{ + class BooleanWaitable : public IWaitable + { + public: + BooleanWaitable(std::atomic &value) : value_(value) + { + } + + bool TryLock() override + { + return value_; + } + + bool HasOSHandle(AuMach &mach) override + { + return false; + } + + bool HasLockImplementation() override + { + return false; + } + + void Unlock() override + { + SysPanic("BooleanWaitable is incompatible with WaitFor given an array of IWaitables"); + } + + void Lock() override + { + SysPanic("BooleanWaitable is not lockable"); + } + + bool Lock(AuUInt64 timeout) override + { + SysPanic("BooleanWaitable is not lockable"); + return false; + } + + private: + std::atomic &value_; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Waitables/CBWaitable.hpp b/Include/Aurora/Threading/Waitables/CBWaitable.hpp new file mode 100644 index 00000000..7726e7cf --- /dev/null +++ b/Include/Aurora/Threading/Waitables/CBWaitable.hpp @@ -0,0 +1,53 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CBWaitable.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading::Waitables +{ + class CBWaitable : public IWaitable + { + public: + CBWaitable(const std::function &value) : callback_(value) + { + } + + bool TryLock() override + { + return callback_(); + } + + bool HasOSHandle(AuMach &mach) override + { + return false; + } + + bool HasLockImplementation() override + { + return false; + } + + void Unlock() override + { + SysPanic("CBWaitable is incompatible with WaitFor given an array of IWaitables"); + } + + void Lock() override + { + SysPanic("CBWaitable is not lockable"); + } + + bool Lock(AuUInt64 timeout) override + { + SysPanic("CBWaitable is not lockable"); + return false; + } + + private: + std::function callback_; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Waitables/Waitables.hpp b/Include/Aurora/Threading/Waitables/Waitables.hpp new file mode 100644 index 00000000..e9f7674a --- /dev/null +++ b/Include/Aurora/Threading/Waitables/Waitables.hpp @@ -0,0 +1,11 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Waitables.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +#include "CBWaitable.hpp" +#include "BooleanWaitable.hpp" \ No newline at end of file diff --git a/Include/Aurora/Time/Clock.hpp b/Include/Aurora/Time/Clock.hpp new file mode 100644 index 00000000..91cd3af3 --- /dev/null +++ b/Include/Aurora/Time/Clock.hpp @@ -0,0 +1,114 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Clock.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Time +{ + // aurora epoch to time_t + + /** + Converts seconds from the Aurora epoch to time_t + @deprecated + */ + AUKN_SYM time_t SToCTime(AuInt64 time); + + /** + Converts nanoseconds from the Aurora epoch to time_t + @deprecated + */ + AUKN_SYM time_t NSToCTime(AuInt64 time); + + /** + Converts milliseconds from the Aurora epoch to time_t + @deprecated + */ + AUKN_SYM time_t MSToCTime(AuInt64 time); + + /** + Converts milliseconds from the Aurora epoch to a civil timestamp structure + similar to or of std::tm + + Range: 1900 -> 2001 +- 2^63-1 ms + */ + AUKN_SYM std::tm ToCivilTime(AuInt64 time, bool UTC = true); + + /** + Converts civil time to milliseconds from the Aurora epoch + + Range: 1900 -> 2001 +- 2^63-1 ms + */ + AUKN_SYM AuInt64 FromCivilTime(const std::tm &time, bool UTC = true); + + /** + Retrieves system clock in jiffies + */ + AUKN_SYM AuUInt64 CurrentClock(); + + /** + Retrieves system clock in milliseconds from the Aurora epoch + */ + AUKN_SYM AuUInt64 CurrentClockMS(); + + /** + Retrieves system clock in nanoseconds from the Aurora epoch + */ + AUKN_SYM AuUInt64 CurrentClockNS(); + + /** + Returns a high resolution count of jiffies with an undefined epoch from a + high resolution clock. + + These values should not nor can be accurately converted meaningfully + */ + AUKN_SYM AuUInt64 CurrentInternalClock(); + AUKN_SYM AuUInt64 CurrentInternalClockMS(); + AUKN_SYM AuUInt64 CurrentInternalClockNS(); + + /** + Let's say you're fucked and you need a ball park figure. + Enjoy... + */ + AUKN_SYM AuUInt64 ConvertInternalToAuroraEpochMS(AuUInt64 in); + AUKN_SYM AuUInt64 ConvertInternalToAuroraEpochNS(AuUInt64 in); + + /** + Translates the Aurora epoch to the standard unix epoch + */ + AUKN_SYM AuInt64 ConvertAuroraToUnixMS(AuInt64 in); + + /** + Translates the Aurora epoch to the standard unix epoch + */ + AUKN_SYM AuInt64 ConvertAuroraToUnixNS(AuInt64 in); + + + /** + Translates a standard unix epoch to the Aurora epoch + */ + AUKN_SYM AuInt64 ConvertUnixToAuroraMS(AuInt64 in); + + /** + Translates a standard unix epoch to the Aurora epoch + */ + AUKN_SYM AuInt64 ConvertUnixToAuroraNS(AuInt64 in); + + /** + Retrieves the freqency as a fraction of: jiffies per second / 1 * nanoseconds in a second + */ + AUKN_SYM double CPUFrequencyDeltaNS(); + + /** + Retrieves the freqency as a fraction of: jiffies per second / 1 * milliseconds in a second + */ + AUKN_SYM double CPUFrequencyDeltaMS(); + + /** + Retrieves the freqency of jiffies per second + */ + AUKN_SYM AuUInt64 ClockJiffies(); +} \ No newline at end of file diff --git a/Include/Aurora/Time/DebugBenchmark.hpp b/Include/Aurora/Time/DebugBenchmark.hpp new file mode 100644 index 00000000..ff30c2a2 --- /dev/null +++ b/Include/Aurora/Time/DebugBenchmark.hpp @@ -0,0 +1,52 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: DebugBenchmark.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +#include + +namespace Aurora::Time +{ +#if ((defined(AU_FORCE_BENCHMARK) || defined(DEBUG) || defined(INTERNAL)) && (!defined(AU_FORCE_NOBENCHMARK))) + + class DebugBenchmark + { + public: + DebugBenchmark(AuString message) : message_(message) {} + + ~DebugBenchmark() + { + Finish(); + } + + void Finish() + { + auto time = timer_.End(); + Aurora::Console::Logging::LogDbg("[Benchmark] {} took {}", message_, ConvertMSToTimescaleEN(time)); + } + + private: + Timer timer_; + AuString message_; + }; + +#else + + class DebugBenchmark + { + public: + template + DebugBenchmark(T... args) {} + + void Finish() {} + }; + +#endif + +} + +#define SysBenchmark(...) Aurora::Time::DebugBenchmark _(__VA_ARGS__); \ No newline at end of file diff --git a/Include/Aurora/Time/Time.hpp b/Include/Aurora/Time/Time.hpp new file mode 100644 index 00000000..82385224 --- /dev/null +++ b/Include/Aurora/Time/Time.hpp @@ -0,0 +1,51 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Time.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +namespace Aurora::Time +{ + static inline AuString ConvertMSToTimescaleEN(AuUInt32 ms) + { + const auto msDiv1000 = ms / 1000; // seconds + const auto msDiv1000Mod60 = msDiv1000 % 60; // remaining seconds relative to next whole minute + const auto msDiv1000Div60 = msDiv1000 / 60; // total minutes + + if (ms < 1000) + { + return std::to_string(ms) + "ms"; + } + else if (ms >= 1000) + { + auto s = msDiv1000; + auto remMs = ms % 1000; + return std::to_string(s) + "." + std::to_string(remMs) + "s"; + } + else if (ms > (1000 * 60)) + { + auto m = msDiv1000Div60; + auto remS = msDiv1000Mod60; + return std::to_string(m) + "m " + std::to_string(remS) + "s"; + } + else if (ms > (1000 * 60 * 60)) + { + auto h = msDiv1000Div60 / 60; + auto remM = msDiv1000Div60 % 60; + auto remS = msDiv1000Mod60; + return std::to_string(h) + "h " + std::to_string(remM) + "m" + std::to_string(remS) + "s"; + } + else + { + return std::to_string(ms); // ? + } + } +} + +#include "Clock.hpp" +#include "Timer.hpp" +#include "TimerHighRes.hpp" +#include "DebugBenchmark.hpp" \ No newline at end of file diff --git a/Include/Aurora/Time/Timer.hpp b/Include/Aurora/Time/Timer.hpp new file mode 100644 index 00000000..b0cd14f4 --- /dev/null +++ b/Include/Aurora/Time/Timer.hpp @@ -0,0 +1,33 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Timer.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Time +{ + class Timer + { + public: + Timer() + { + Start(); + } + + void Start() + { + start_ = CurrentClockMS(); + } + + AuUInt64 End() + { + return CurrentClockMS() - start_; + } + + private: + AuUInt64 start_; + }; +} \ No newline at end of file diff --git a/Include/Aurora/Time/TimerHighRes.hpp b/Include/Aurora/Time/TimerHighRes.hpp new file mode 100644 index 00000000..c537b4a9 --- /dev/null +++ b/Include/Aurora/Time/TimerHighRes.hpp @@ -0,0 +1,35 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: TimerHighRes.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +namespace Aurora::Time +{ + class TimerHighRes + { + public: + TimerHighRes() + { + Start(); + } + + void Start() + { + start_ = CurrentInternalClockNS(); + } + + AuUInt64 End() + { + auto re = std::clamp(CurrentInternalClockNS() - start_, AuInt64(0), std::numeric_limits::max()); + if (re == std::numeric_limits::max()) return 0; // ez overflow in subtract. get out of here in 2-3 branches + return re; + } + + private: + AuUInt64 start_; + }; +} \ No newline at end of file diff --git a/Include/AuroraMacros.hpp b/Include/AuroraMacros.hpp new file mode 100644 index 00000000..12a5e27f --- /dev/null +++ b/Include/AuroraMacros.hpp @@ -0,0 +1,76 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuroraMacros.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once +#define AU_NO_COPY(type) type(type&) = delete; +#define AU_NO_MOVE(type) type(type&&) = delete; +#define AU_NO_COPY_NO_MOVE(type) AU_NO_COPY(type) AU_NO_MOVE(type) + +#define AU_STRINGIFY_(in) #in +#define AU_STRINGIFY(in) AU_STRINGIFY_(in) + +/// @hideinitializer +#define _AUKCON_STRINGIFY_X(in) AU_STRINGIFY(in) + +#define AU_SHARED_API_EX(vis, name, type, ...) \ + \ +vis type *name ## New(__VA_ARGS__); \ +vis void name ## Release(type *); \ +static inline void name ## Destroy(type *val) \ +{ \ + name ## Release(val); \ +} \ + \ +struct CppDeleter ## name \ +{ \ + void operator()(type *t) \ + { \ + name ## Release(t); \ + } \ +}; \ + \ +using name ## Unique_t = std::unique_ptr; \ +template \ +name ## Unique_t name ## Unique(T... args) \ +{ \ + return name ## Unique_t(name ## New(args...)); \ +} \ + \ +using name ## Shared_t = std::shared_ptr; \ +template \ +name ## Shared_t name ## Shared(T... args) \ +{ \ + return name ## Shared_t(name ## New(args...), name ## Release); \ +} + +#define AU_SHARED_API(name, type, ...) AU_SHARED_API_EX(, name, type, AU_VA_ALL_OPT) + +#if defined(AURORA_COMPILER_MSVC) + #define AU_NOINLINE __declspec(noinline) +#else + #define AU_NOINLINE __attribute__((noinline)) +#endif + +#if defined(AURORA_COMPILER_MSVC) + #define AU_NORETURN __declspec(noreturn) +#else + #define AU_NORETURN [[noreturn]] +#endif + +#if defined(AURORA_PLATFORM_WIN32) + #define AU_ALLOC __declspec(allocator) +#elif defined(AURORA_COMPILER_CLANG) + #define AU_ALLOC __declspec(allocator) +#elif defined(AURORA_COMPILER_COMMUNISM) + #define AU_ALLOC __attribute__((malloc)) +#else + #define AU_ALLOC +#endif + +#if !defined(NO__NEW) + #define _new new (std::nothrow) +#endif \ No newline at end of file diff --git a/Include/AuroraRuntime.hpp b/Include/AuroraRuntime.hpp new file mode 100644 index 00000000..fad2011b --- /dev/null +++ b/Include/AuroraRuntime.hpp @@ -0,0 +1,8 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuroraRuntime.hpp + Date: 2021-6-9 + Author: Reece +***/ +#include "Aurora/Runtime.hpp" \ No newline at end of file diff --git a/Include/AuroraTypedefs.hpp b/Include/AuroraTypedefs.hpp new file mode 100644 index 00000000..5813a31d --- /dev/null +++ b/Include/AuroraTypedefs.hpp @@ -0,0 +1,71 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuroraTypedefs.hpp + Date: 2021-6-10 + Author: Reece +***/ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Aurora::Memory +{ + template + struct SpeedyArrayAllocator; +} + + +template +using AuList = typename std::conditional::value, std::vector, std::vector>>::type; + +template +using AuHashMap = std::unordered_map; + +template +using AuHashMapEx = std::unordered_map; + +template +using AuBST = std::map; + +template +using AuBSTEx = std::map; + +template +using AuSPtr = std::shared_ptr; + +template +using AuWPtr = std::weak_ptr; + +#if defined(AURORA_COMPILER_MSVC) +using AuAtomicInt = long; +#else +using AuAtomicInt = int; +#endif + +//#include "tinyutf8.h" + +using AuString = std::string;// tiny_utf8::utf8_string; + +using AuUInt64 = Aurora::Types::uint64_t; +using AuUInt32 = Aurora::Types::uint32_t; +using AuUInt16 = Aurora::Types::uint16_t; +using AuUInt8 = Aurora::Types::uint8_t; +using AuInt64 = Aurora::Types::int64_t; +using AuInt32 = Aurora::Types::int32_t; +using AuInt16 = Aurora::Types::int16_t; +using AuInt8 = Aurora::Types::int8_t; +using AuMach = Aurora::Types::size_t; +using AuSMach = Aurora::Types::ssize_t; +using AuSInt = AuSMach; +using AuUInt = AuMach; + +using AuThreadId_t = AuUInt64; +static const AuThreadId_t kThreadIdSpecialMask = AuThreadId_t(1) << AuThreadId_t(63); \ No newline at end of file diff --git a/Include/AuroraUtils.hpp b/Include/AuroraUtils.hpp new file mode 100644 index 00000000..a430f967 --- /dev/null +++ b/Include/AuroraUtils.hpp @@ -0,0 +1,376 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: AuroraUtils.hpp + Date: 2021-6-9 + Author: Reece +***/ +#pragma once + +template +static constexpr int ArraySize(const T(&array)[Z]) +{ + return Z; +} + +#if defined(DEBUG) || defined(INTERNAL) + +template +static void inline SafeDelete(T *in) +{ + static_assert(std::is_base_of::type>::value); + auto cast = dynamic_cast(in); + if (cast == nullptr) + { + Z re; + SysPanic("Tried to free: 0x{:x}, type \"{}\" was not inherited from \"{}\"", AuUInt(in), typeid(in).name(), typeid(re).name()); + } + delete cast; +} + +#else + +template +static void inline SafeDelete(T *in) +{ + static_assert(std::is_base_of::type>::value); + delete static_cast(in); +} + +#endif + +static inline AuUInt64 ConvertMagicTag64(const char buffer[8]) +{ + return *reinterpret_cast(buffer); +} + +static inline AuUInt32 ConvertMagicTag32(const char buffer[4]) +{ + return *reinterpret_cast(buffer); +} + +template +static inline std::optional> OptionalSharedDynamicCast(std::optional> &in) +{ + if (!in.has_value()) return {}; + return std::dynamic_pointer_cast(in.value()); +} + +template +static inline std::optional> OptionalSharedStaticCast(std::optional> &in) +{ + if (!in.has_value()) return {}; + return std::static_pointer_cast(in.value()); +} + +static inline bool EndsWith(std::string const &value, std::string const &ending) +{ + if (ending.size() > value.size()) return false; + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); +} + +static inline bool StartsWith(std::string const &value, std::string const &starting) +{ + return value.rfind(starting, 0) == 0; +} + +template +static inline AuString ToStringASCIIOp(T op, const AuString &in) +{ + AuString ret; + ret.resize(in.size()); + std::transform(in.begin(), in.end(), ret.begin(), [=](const char &c) + { + return op(c); + }); + return ret; +} + +static inline AuString ToLower(const AuString &in) +{ + return ToStringASCIIOp(std::tolower, in); +} + +static inline AuString ToUpper(const AuString &in) +{ + return ToStringASCIIOp(std::toupper, in); +} + +template +static inline bool TryFind(Map &map, const Key &key, Value *&ptr) +{ + auto itr = map.find(key); + if (itr != map.end()) + { + ptr = &itr->second; + return true; + } + else + { + ptr = nullptr; + return false; + } +} + +template +static inline bool TryFind(Map *map, const Key &key, Value *&ptr) +{ + auto itr = map->find(key); + if (itr != map->end()) + { + ptr = &itr->second; + return true; + } + else + { + ptr = nullptr; + return false; + } +} + +template +static inline bool TryFind(Map &map, const Key &key) +{ + auto itr = map.find(key); + if (itr != map.end()) + { + return true; + } + else + { + return false; + } +} + +template +static inline bool TryFind(Map *map, const Key &key) +{ + auto itr = map->find(key); + if (itr != map->end()) + { + return true; + } + else + { + return false; + } +} +template +static inline bool TryFindGeneric(Map &map, const Key &key, Value *&ptr) +{ + auto itr = map.find(key); + if (itr != map.end()) + { + ptr = &*itr; + return true; + } + else + { + ptr = nullptr; + return false; + } +} + +template +static inline bool TryDelete(Map &map, const Key &key) +{ + auto itr = map.find(key); + if (itr != map.end()) + { + map.erase(itr); + return true; + } + else + { + return false; + } +} + +template +static inline bool TryDeleteList(List &list, const Key &key) +{ + auto itr = std::find(list.begin(), list.end(), key); + if (itr != list.end()) + { + list.erase(itr); + return true; + } + else + { + return false; + } +} + +template +static inline bool TryInsert(Container &container, const Type &value) +{ + try + { + container.insert(container.end(), value); + + return true; + } + catch (...) + { + return false; + } +} +template +static inline bool TryInsert(Container &container, Type &&value) +{ + try + { + container.insert(container.end(), value); + + return true; + } + catch (...) + { + return false; + } +} + +template +static inline bool TryInsert(Container *container, const Type &value) +{ + try + { + container->insert(container->end(), value); + + return true; + } + catch (...) + { + return false; + } +} + +template +static inline bool TryInsert(Container *container, Type &&value) +{ + try + { + container->insert(container->end(), value); + + return true; + } + catch (...) + { + return false; + } +} + +template +static inline bool TryInsertNoEnd(Container &container, Type &&value) // move +{ + try + { + container.insert(value); + + return true; + } + catch (...) + { + return false; + } +} + +template +static inline bool TryInsertNoEnd(Container &container, const Type &value) // copy +{ + try + { + container.insert(value); + + return true; + } + catch (...) + { + return false; + } +} + +template +static inline bool TryInsertNoEnd(Container *container, Type &&value) // move +{ + try + { + container->insert(value); + + return true; + } + catch (...) + { + return false; + } +} + + +template +static inline bool TryInsertNoEnd(Container *container, const Type &value) // copy +{ + try + { + container->insert(value); + + return true; + } + catch (...) + { + return false; + } +} + +template +static inline bool TryResize(T &list, AuUInt length) +{ + try + { + list.resize(length); + return true; + } + catch (...) + { + return false; + } +} + +static inline std::string ReplaceAll(std::string &str, const std::string &from, const std::string &to) +{ + size_t start_pos = 0; + while ((start_pos = str.find(from, start_pos)) != std::string::npos) + { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); // Handles case where 'to' is a substring of 'from' + } + return str; +} + +// i told myself not to copy this, required a split function twice, now here we are :D +static inline AuList SplitString(const AuString& str, const AuString& delim) +{ + AuList tokens; + AuUInt prev = 0, pos = 0; + do + { + pos = str.find(delim, prev); + if (pos == AuString::npos) pos = str.length(); + auto token = str.substr(prev, pos-prev); + if (!token.empty()) tokens.push_back(token); + prev = pos + delim.length(); + } + while (pos < str.length() && prev < str.length()); + return tokens; +} + +template < template class base,typename derived> +struct is_base_of_template_impl +{ + template + static constexpr std::true_type test(const base *); + static constexpr std::false_type test(...); + using type = decltype(test(std::declval())); +}; + +template < template class base,typename derived> +using is_base_of_template = typename is_base_of_template_impl::type; diff --git a/Source/Async/Async.cpp b/Source/Async/Async.cpp new file mode 100644 index 00000000..86a2cb7e --- /dev/null +++ b/Source/Async/Async.cpp @@ -0,0 +1,10 @@ +#include +#include "Async.hpp" + +namespace Aurora::Async +{ + void InitAsync() + { + + } +} \ No newline at end of file diff --git a/Source/Async/Async.hpp b/Source/Async/Async.hpp new file mode 100644 index 00000000..36d8cd3d --- /dev/null +++ b/Source/Async/Async.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace Aurora::Async +{ + void InitAsync(); +} \ No newline at end of file diff --git a/Source/Async/AsyncApp.cpp b/Source/Async/AsyncApp.cpp new file mode 100644 index 00000000..7ae95ca9 --- /dev/null +++ b/Source/Async/AsyncApp.cpp @@ -0,0 +1,7 @@ +#include +#include "AsyncApp.hpp" + +namespace Aurora::Async +{ + +} \ No newline at end of file diff --git a/Source/Async/AsyncApp.hpp b/Source/Async/AsyncApp.hpp new file mode 100644 index 00000000..07578cd2 --- /dev/null +++ b/Source/Async/AsyncApp.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace Aurora::Async +{ + +} \ No newline at end of file diff --git a/Source/Async/Schedular.cpp b/Source/Async/Schedular.cpp new file mode 100644 index 00000000..2282739f --- /dev/null +++ b/Source/Async/Schedular.cpp @@ -0,0 +1,7 @@ +#include +#include "Schedular.hpp" + +namespace Aurora::Async +{ + +} \ No newline at end of file diff --git a/Source/Async/Schedular.hpp b/Source/Async/Schedular.hpp new file mode 100644 index 00000000..07578cd2 --- /dev/null +++ b/Source/Async/Schedular.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace Aurora::Async +{ + +} \ No newline at end of file diff --git a/Source/Async/WorkItem.cpp b/Source/Async/WorkItem.cpp new file mode 100644 index 00000000..f23aff4e --- /dev/null +++ b/Source/Async/WorkItem.cpp @@ -0,0 +1,198 @@ +#include +#include "WorkItem.hpp" + +namespace Aurora::Async +{ + WorkItem::WorkItem(const DispatchTarget_t &worker, const AuSPtr &task, bool supportsBlocking) : worker_(worker), task_(task) + { + if (supportsBlocking) + { + finishedEvent_ = Threading::Primitives::EventUnique(false, true, true); + SysAssert(finishedEvent_ ? true : false); + } + } + + WorkItem::~WorkItem() + { + Fail(); + } + + void WorkItem::WaitFor(const AuSPtr &workItem) + { + auto dependency = std::reinterpret_pointer_cast(workItem); + + Threading::LockGuard l(lock); + Threading::LockGuard l2(dependency->lock); + + dependency->waiters_.push_back(shared_from_this()); + waitOn_.push_back(workItem); + } + + void WorkItem::WaitFor(const AuList> &workItems) + { + Threading::LockGuard l(lock); + + for (auto &workItem : workItems) + { + auto dependency = std::reinterpret_pointer_cast(workItem); + Threading::LockGuard l2(dependency->lock); + dependency->waiters_.push_back(shared_from_this()); + waitOn_.push_back(workItem); + } + } + + void WorkItem::SetSchedTime(AuUInt32 ms) + { + Threading::LockGuard l(lock); + dispatchTime_ = Time::CurrentClockMS() + ms; + } + + void WorkItem::Dispatch() + { + DispatchEx(false); + } + + void WorkItem::DispatchEx(bool check) + { + Threading::LockGuard l(lock); + + if (check) + { + if (!dispatchPending_) + { + return; + } + } + else + { + dispatchPending_ = true; + } + + for (auto itr = waitOn_.begin(); itr != waitOn_.end(); ) + { + auto &waitable = *itr; + + if (!waitable->HasFinished()) + { + return; + } + + itr = waitOn_.erase(itr); + } + + if (Time::CurrentClockMS() >= dispatchTime_) + { + Schedule(); + return; + } + + SendOff(); + } + + void WorkItem::Handle() + { + Threading::LockGuard l(lock); + + IWorkItemHandler::ProcessInfo info(true); + task_->DispatchFrame(info); + + switch (info.type) + { + case IWorkItemHandler::EProcessNext::eFinished: + { + // do nothing + break; + } + case IWorkItemHandler::EProcessNext::eInvalid: + { + SysPanic("Handle Invalid"); + break; + } + case IWorkItemHandler::EProcessNext::eSchedule: + { + if (info.reschedMs) + { + SetSchedTime(info.reschedMs); + } + WaitFor(info.waitFor); + // fall through + } + case IWorkItemHandler::EProcessNext::eRerun: + { + DispatchEx(false); + return; + } + case IWorkItemHandler::EProcessNext::eFailed: + { + Fail(); + return; + } + } + + for (auto &waiter : waiters_) + { + std::reinterpret_pointer_cast(waiter)->DispatchEx(true); + } + + finished = true; + if (finishedEvent_) + { + finishedEvent_->Set(); + } + } + + void WorkItem::Fail() + { + failed = true; + + if (auto task_ = std::exchange(this->task_, {})) + { + task_->Shutdown(); + } + + for (auto &waiter : waiters_) + { + std::reinterpret_pointer_cast(waiter)->Fail(); + } + + waiters_.clear(); + waitOn_.clear(); + + if (finishedEvent_) + { + finishedEvent_->Set(); + } + } + + bool WorkItem::BlockUntilComplete() + { + if (!finishedEvent_) return false; + finishedEvent_->Lock(); + return true; + } + + bool WorkItem::HasFinished() + { + return finished; + } + + bool WorkItem::HasFailed() + { + return failed; + } + + void WorkItem::Schedule() + { + // TODO: + } + + void WorkItem::SendOff() + { + // TODO: + } + + AUKN_SYM AuSPtr NewWorkItem(const WorkerId_t &worker, const AuSPtr &task, bool annoying) + { + return std::make_shared(worker, task, annoying); + } +} \ No newline at end of file diff --git a/Source/Async/WorkItem.hpp b/Source/Async/WorkItem.hpp new file mode 100644 index 00000000..d2db4ebd --- /dev/null +++ b/Source/Async/WorkItem.hpp @@ -0,0 +1,42 @@ +#pragma once + +namespace Aurora::Async +{ + class WorkItem : public IWorkItem, public std::enable_shared_from_this + { + public: + WorkItem(const DispatchTarget_t &worker_, const AuSPtr &task_, bool supportsBlocking); + ~WorkItem(); + + void WaitFor(const AuSPtr &workItem) override; + void WaitFor(const AuList> &workItem) override; + void SetSchedTime(AuUInt32 ms) override; + + void Dispatch() override; + + bool BlockUntilComplete() override; + bool HasFinished() override; + bool HasFailed() override; + + void Handle(); + + private: + void DispatchEx(bool check); + AuSPtr task_; + DispatchTarget_t worker_; + AuList> waitOn_; + AuList> waiters_; + Threading::Primitives::SpinLock lock; + Threading::Primitives::EventUnique_t finishedEvent_; + + bool finished {}; + bool failed {}; + bool dispatchPending_ {}; + AuUInt32 dispatchTime_ {}; + + void Fail(); + void Schedule(); + void SendOff(); + }; + +} \ No newline at end of file diff --git a/Source/Compression/BlockCompressor.cpp b/Source/Compression/BlockCompressor.cpp new file mode 100644 index 00000000..ced0df4d --- /dev/null +++ b/Source/Compression/BlockCompressor.cpp @@ -0,0 +1,44 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BlockCompressor.cpp + Date: 2021-6-17 + Author: Reece +***/ +#include +#include "Compression.hpp" +#include "BlockCompressor.hpp" + +#include "bzlib.h" +#include "zstd.h" +#include "zlib.h" +#include "lz4.h" + +namespace Aurora::Compression +{ + bool BaseStreamDeflate::Read(void * /*opt*/ buffer, AuUInt32 &len, bool ingestUntilError) + { + if (ingestUntilError) + { + while (this->_outbuffer.size() < len) + { + if (!Ingest(0, len)) + { + return false; + } + } + } + + return StreamRead(buffer, len, this->_outbuffer); + } + + AUKN_SYM ICompressionStreamEx *CompressorNew(IO::IStreamReader *reader, const CompressionInfo &info) + { + return nullptr; + } + + AUKN_SYM void CompressorRelease(ICompressionStreamEx * stream) + { + SafeDelete(stream); + } +} \ No newline at end of file diff --git a/Source/Compression/BlockCompressor.hpp b/Source/Compression/BlockCompressor.hpp new file mode 100644 index 00000000..506a773d --- /dev/null +++ b/Source/Compression/BlockCompressor.hpp @@ -0,0 +1,23 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BlockCompressor.hpp + Date: 2021-6-17 + Author: Reece +***/ +#pragma once + +namespace Aurora::Compression +{ + class BaseStreamDeflate : public ICompressionStreamEx + { + public: + virtual ~BaseStreamDeflate() { } + + virtual bool Init(Aurora::IO::IStreamReader *reader, const CompressionInfo &info) = 0; + bool Read(void * /*opt*/ buffer, AuUInt32 &len, bool ingestUntilError) override; + + protected: + AuList _outbuffer; + }; +} \ No newline at end of file diff --git a/Source/Compression/BlockDecompressor.cpp b/Source/Compression/BlockDecompressor.cpp new file mode 100644 index 00000000..66c6fd43 --- /dev/null +++ b/Source/Compression/BlockDecompressor.cpp @@ -0,0 +1,396 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BlockDecompressor.cpp + Date: 2021-6-17 + Author: Reece +***/ +#include +#include "Compression.hpp" +#include "BlockDecompressor.hpp" + +#include "bzlib.h" +#include "zstd.h" +#include "zlib.h" +#include "lz4.h" + +namespace Aurora::Compression +{ + bool BaseStream::Read(void * /*opt*/ buffer, AuUInt32 &len, bool ingestUntilError) + { + if (ingestUntilError) + { + while (this->_outbuffer.size() < len) + { + if (!Ingest(0, len)) + { + return false; + } + } + } + + return StreamRead(buffer, len, this->_outbuffer); + } + + class ZSTDInflate : public BaseStream + { + public: + ~ZSTDInflate() + { + if (auto dctx = std::exchange(_dctx, {})) + { + ZSTD_freeDCtx(dctx); + } + } + + bool Init(Aurora::IO::IStreamReader *reader) + { + this->_reader = reader; + this->_dctx = ZSTD_createDCtx(); + + if (!this->_dctx) + { + SysPushErrorGen("Couldn't create decompressor"); + return false; + } + + this->_outbuffer.reserve(10 * 1024); + + return true; + } + + bool Ingest(AuUInt32 minimum, AuUInt32 requestBuffer) override + { + AuUInt32 length = ZSTD_DStreamInSize(); + void *din = alloca(length); + + AuUInt32 readLength = requestBuffer; + AuUInt32 requested = std::min(readLength, length); + AuUInt32 request = requested; + AuUInt32 done{}; + + auto outFrameLength = ZSTD_DStreamOutSize(); + void *dout = alloca(outFrameLength); + + while (done != readLength) + { + if (this->_reader->Read(din, request) != IO::EStreamError::eErrorNone) + { + return minimum <= done; + } + + done += request; + + ZSTD_inBuffer input = { din, request, 0 }; + while (input.pos < input.size) + { + ZSTD_outBuffer output = { dout, outFrameLength, 0 }; + + auto ret = ZSTD_decompressStream(this->_dctx, &output, &input); + if (ZSTD_isError(ret)) + { + SysPushErrorIO("Compression error: {}", ret); + return false; + } + + this->_outbuffer.insert(this->_outbuffer.end(), reinterpret_cast(output.dst), reinterpret_cast(output.dst) + output.pos); + } + } + + return true; + } + + private: + Aurora::IO::IStreamReader *_reader; + ZSTD_DCtx *_dctx; + + }; + + class ZIPInflate : public BaseStream + { + public: + ~ZIPInflate() + { + if (auto ctx = std::exchange(this->_init, {})) + { + inflateEnd(&this->_ctx); + } + } + + bool Init(Aurora::IO::IStreamReader *reader) + { + this->_reader = reader; + + auto ret = inflateInit(&this->_ctx); + if (ret != Z_OK) + { + SysPushErrorMem("Error: {}", ret); + return false; + } + + this->_outbuffer.reserve(10 * 1024); + this->_init = true; + return true; + } + + bool Ingest(AuUInt32 minimum, AuUInt32 requestBuffer) override + { + int ret; + AuUInt32 readLength = requestBuffer; + unsigned char dout[4096]; + unsigned char din[4096]; + AuUInt32 request = std::min(requestBuffer, AuUInt32(ArraySize(din))); + + AuUInt32 done{}; + while (done != readLength) + { + if (this->_reader->Read(din, request) != IO::EStreamError::eErrorNone) + { + return minimum <= done; + } + + done += request; + + this->_ctx.avail_in = request; + this->_ctx.next_in = reinterpret_cast(din); + + do + { + this->_ctx.avail_out = ArraySize(dout); + this->_ctx.next_out = dout; + + ret = inflate(&this->_ctx, Z_NO_FLUSH); + if (ret != Z_OK) + { + SysPushErrorIO("Error: {}", ret); + return false; + } + + auto have = ArraySize(dout) - this->_ctx.avail_out; + + this->_outbuffer.insert(this->_outbuffer.end(), reinterpret_cast(dout), reinterpret_cast(dout) + have); + + + } while (this->_ctx.avail_out == 0); + SysAssert(this->_ctx.avail_in == 0); + } + + return true; + } + + private: + AuList _outbuffer; + Aurora::IO::IStreamReader *_reader; + z_stream _ctx {}; + bool _init {}; + + }; + + class BZIPInflate : public BaseStream + { + public: + ~BZIPInflate() + { + if (auto ctx = std::exchange(this->_init, {})) + { + BZ2_bzDecompressEnd(&this->_ctx); + } + } + + bool Init(Aurora::IO::IStreamReader *reader) + { + this->_reader = reader; + + auto ret = BZ2_bzDecompressInit(&this->_ctx, 0, 0); + if (ret != Z_OK) + { + SysPushErrorMem("Error: {}", ret); + return false; + } + + this->_outbuffer.reserve(10 * 1024); + this->_init = true; + return true; + } + + bool Ingest(AuUInt32 minimum, AuUInt32 requestBuffer) override + { + int ret; + char dout[4096]; + char din[4096]; + + AuUInt32 readLength = requestBuffer; + AuUInt32 request = std::min(requestBuffer, AuUInt32(ArraySize(din))); + + AuUInt32 done{}; + while (done != readLength) + { + if (this->_reader->Read(din, request) != IO::EStreamError::eErrorNone) + { + return minimum <= done; + } + + done += request; + + this->_ctx.avail_in = request; + this->_ctx.next_in = reinterpret_cast(din); + + do + { + this->_ctx.avail_out = ArraySize(dout); + this->_ctx.next_out = dout; + + ret = BZ2_bzDecompress(&this->_ctx); + if (ret != Z_OK) + { + SysPushErrorIO("Error: {}", ret); + return false; + } + + auto have = ArraySize(dout) - this->_ctx.avail_out; + + this->_outbuffer.insert(this->_outbuffer.end(), reinterpret_cast(dout), reinterpret_cast(dout) + have); + + + } while (this->_ctx.avail_out == 0); + SysAssert(this->_ctx.avail_in == 0); + } + + return true; + } + + private: + AuList _outbuffer; + Aurora::IO::IStreamReader *_reader; + bz_stream _ctx {}; + bool _init {}; + + }; + + class LZ4Inflate : public BaseStream + { + public: + ~LZ4Inflate() + { + if (auto ctx = std::exchange(this->_lz4Stream, {})) + { + LZ4_freeStreamDecode(this->_lz4Stream); + } + } + + bool Init(Aurora::IO::IStreamReader *reader) + { + this->_reader = reader; + + this->_lz4Stream = LZ4_createStreamDecode(); + if (!this->_lz4Stream) + { + SysPushErrorMem(); + return false; + } + + this->_outbuffer.reserve(10 * 1024); + return true; + } + + bool Ingest(AuUInt32 minimum, AuUInt32 requestBuffer) override + { + int ret; + char dout[4096]; + char din[4096]; + + AuUInt32 readLength = requestBuffer; + AuUInt32 request = std::min(requestBuffer, AuUInt32(ArraySize(din))); + AuUInt32 done{}; + + while (done != readLength) + { + if (this->_reader->Read(din, request) != IO::EStreamError::eErrorNone) + { + return minimum <= done; + } + + done += request; + + + int framesLength = done; + char *framesPointer = (char *)din; + while (true) + { + if (framesLength < 2) + { + return false; + } + framesLength -= 2; + + auto frameLength = *reinterpret_cast(framesPointer); + if (frameLength > framesLength) + { + return false; + } + framesPointer += 2; + + auto err = LZ4_decompress_safe_continue(_lz4Stream, framesPointer, dout, frameLength, ArraySize(dout)); + if (err < 0) + { + ret = false; + return false; + } + + this->_outbuffer.insert(this->_outbuffer.end(), reinterpret_cast(dout), reinterpret_cast(dout) + err); + + framesPointer += framesLength; + framesLength -= framesLength; + } + } + + return true; + } + + private: + AuList _outbuffer; + Aurora::IO::IStreamReader *_reader; + LZ4_streamDecode_t* _lz4Stream {}; + + }; + + AUKN_SYM ICompressionStream *DecompressorNew(IO::IStreamReader *reader, ECompresionType type) + { + BaseStream * ret{}; + + switch (type) + { + case ECompresionType::eZSTD: + ret = new ZSTDInflate(); + break; + case ECompresionType::eBZIP2: + ret = new BZIPInflate(); + break; + case ECompresionType::eLZ4: + ret = new LZ4Inflate(); + break; + case ECompresionType::eDeflate: + ret = new ZIPInflate(); + break; + default: + ret = nullptr; + break; + } + + if (ret) + { + if (!ret->Init(reader)) + { + ret = nullptr; + delete ret; + } + } + + return ret; + } + + AUKN_SYM void DecompressorRelease(ICompressionStream * stream) + { + SafeDelete(stream); + } +} \ No newline at end of file diff --git a/Source/Compression/BlockDecompressor.hpp b/Source/Compression/BlockDecompressor.hpp new file mode 100644 index 00000000..4047dc27 --- /dev/null +++ b/Source/Compression/BlockDecompressor.hpp @@ -0,0 +1,23 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: BlockDecompressor.hpp + Date: 2021-6-17 + Author: Reece +***/ +#pragma once + +namespace Aurora::Compression +{ + class BaseStream : public ICompressionStream + { + public: + virtual ~BaseStream() {} + + virtual bool Init(Aurora::IO::IStreamReader *reader) = 0; + bool Read(void * /*opt*/ buffer, AuUInt32 &len, bool ingestUntilError) override; + + protected: + AuList _outbuffer; + }; +} \ No newline at end of file diff --git a/Source/Compression/Compression.cpp b/Source/Compression/Compression.cpp new file mode 100644 index 00000000..1eaac232 --- /dev/null +++ b/Source/Compression/Compression.cpp @@ -0,0 +1,75 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Compression.cpp + Date: 2021-6-17 + Author: Reece +***/ +#include +#include "Compression.hpp" + +#include "zstd.h" + +namespace Aurora::Compression +{ + AUKN_SYM bool Compress(const void *buffer, AuUInt32 length, AuList &out, int compressionLevel ) + { + if (!TryResize(out, length)) + { + return false; + } + + auto ret = ZSTD_compress(&out[0], out.size(), buffer, length, compressionLevel); + if (ZSTD_isError(ret)) + { + return false; + } + + out.resize(ret); + return true; + } + + AUKN_SYM bool Compress(const AuList &in, AuList &out, int compressionLevel) + { + return Compress(in.data(), in.size(), out, compressionLevel); + } + + AUKN_SYM bool Decompress(const void *buffer, AuUInt32 length, AuList &out) + { + auto inflatedLength = ZSTD_getFrameContentSize(buffer, length); + + if (inflatedLength == ZSTD_CONTENTSIZE_ERROR) + { + return false; + } + + if (inflatedLength == ZSTD_CONTENTSIZE_UNKNOWN) + { + return false; + } + + if (ZSTD_isError(inflatedLength)) + { + return false; + } + + if (!TryResize(out, inflatedLength)) + { + return false; + } + + auto ret = ZSTD_decompress(&out[0], out.size(), buffer, length); + if (ZSTD_isError(ret)) + { + return false; + } + + out.resize(ret); + return true; + } + + AUKN_SYM bool Decompress(const AuList &in, AuList &out) + { + return Decompress(in.data(), in.size(), out); + } +} \ No newline at end of file diff --git a/Source/Compression/Compression.hpp b/Source/Compression/Compression.hpp new file mode 100644 index 00000000..6cf8b1cd --- /dev/null +++ b/Source/Compression/Compression.hpp @@ -0,0 +1,25 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Compression.hpp + Date: 2021-6-17 + Author: Reece +***/ +#pragma once + +namespace Aurora::Compression +{ + static const AuUInt64 kChunkSize = 4096; + + // This is acommon prolog required by BlockCompressors and BlockDecompressors. dw about it. + static inline bool StreamRead(void * /*opt*/ buffer, AuUInt32 &len, AuList &vec) + { + len = std::min(AuUInt32(vec.size()), len); + if (len == 0) return false; + std::memcpy(buffer, vec.data(), len); + auto rem = vec.size() - len; + std::memmove(vec.data(), vec.data() + len, rem); + vec.resize(rem); + return true; + } +} \ No newline at end of file diff --git a/Source/Compression/StreamCompression.cpp b/Source/Compression/StreamCompression.cpp new file mode 100644 index 00000000..706255ce --- /dev/null +++ b/Source/Compression/StreamCompression.cpp @@ -0,0 +1,561 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: StreamCompression.cpp + Date: 2021-6-17 + Author: Reece +***/ +#include +#include "Compression.hpp" +#include "StreamCompression.hpp" + +#include "bzlib.h" +#include "zstd.h" +#include "zlib.h" +#include "lz4.h" + +namespace Aurora::Compression +{ + static bool DecompressZSTD(const CompressionPipe &info) + { + auto length = ZSTD_DStreamInSize(); + + AuList buffer; + auto ok = TryResize(buffer, length); + if (!ok) + { + SysPushErrorMem("Couldn't reserve inflation buffers"); + return false; + } + + auto outFrameLength = ZSTD_DStreamOutSize(); + AuList inflatedBuffer; + ok = TryResize(inflatedBuffer, outFrameLength); + if (!ok) + { + SysPushErrorMem("Couldn't reserve inflation buffers"); + return false; + } + + auto dctx = ZSTD_createDCtx(); + if (!dctx) + { + + SysPushErrorGeneric("Couldn't create decompressor"); + return false; + } + + AuUInt read = buffer.size(); + while (read = (info.inPipe(buffer.data(), length))) + { + ZSTD_inBuffer input = { buffer.data(), read, 0 }; + while (input.pos < input.size) + { + ZSTD_outBuffer output = { inflatedBuffer.data(), outFrameLength, 0 }; + auto ret = ZSTD_decompressStream(dctx, &output, &input); + if (ZSTD_isError(ret)) + { + SysPushErrorIO("Compression error: {}", ret); + ZSTD_freeDCtx(dctx); + return false; + } + + info.writePipe(output.dst, output.pos); + if (info.reportProgress) + { + if (!info.reportProgress(input.pos, output.pos)) + { + ZSTD_freeDCtx(dctx); + return false; + } + } + } + } + + ZSTD_freeDCtx(dctx); + return true; + } + + static bool CompressZSTD(const CompressionPipe &stream, const CompressionInfo &info) + { + size_t ret; + const auto buffInSize = ZSTD_CStreamInSize(); + const auto buffOutSize = ZSTD_CStreamOutSize(); + + AuList inflatedBuffer; + auto ok = TryResize(inflatedBuffer, buffInSize); + if (!ok) + { + SysPushErrorMem("Couldn't reserve deflation buffers"); + return false; + } + + AuList deflatedBuffer; + ok = TryResize(deflatedBuffer, buffOutSize); + if (!ok) + { + SysPushErrorMem("Couldn't reserve deflation buffers. Out of memory"); + return false; + } + + auto cctx = ZSTD_createCCtx(); + if (!cctx) + { + SysPushErrorGeneric("Couldn't create decompressor"); + return false; + } + + ret = ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, info.compressionLevel); + if (ZSTD_isError(ret)) + { + SysPushErrorGen("Invalid compression level"); + ZSTD_freeCCtx(cctx); + return false; + } + + ret = ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1); + if (ZSTD_isError(ret)) + { + SysPushErrorGen("Invalid option"); + ZSTD_freeCCtx(cctx); + return false; + } + + ret = ZSTD_CCtx_setParameter(cctx, ZSTD_c_nbWorkers, std::max(stream.threads, 1u)); + if (ZSTD_isError(ret)) + { + SysPushErrorGen(); + ZSTD_freeCCtx(cctx); + return false; + } + + bool status = true; + + size_t const toRead = buffInSize; + + while (true) + { + size_t read = stream.inPipe(inflatedBuffer.data(), buffInSize); + /* Select the flush mode. + * If the read may not be finished (read == toRead) we use + * ZSTD_e_continue. If this is the last chunk, we use ZSTD_e_end. + * Zstd optimizes the case where the first flush mode is ZSTD_e_end, + * since it knows it is compressing the entire source in one pass. + */ + int const lastChunk = (read < toRead); + ZSTD_EndDirective const mode = lastChunk ? ZSTD_e_end : ZSTD_e_continue; + /* Set the input buffer to what we just read. + * We compress until the input buffer is empty, each time flushing the + * output. + */ + ZSTD_inBuffer input = { inflatedBuffer.data(), read, 0 }; + int finished; + do + { + /* Compress into the output buffer and write all of the output to + * the file so we can reuse the buffer next iteration. + */ + ZSTD_outBuffer output = { deflatedBuffer.data(), buffOutSize, 0 }; + size_t const remaining = ZSTD_compressStream2(cctx, &output, &input, mode); + if (ZSTD_isError(remaining)) + { + SysPushErrorGen(": {}", ZSTD_getErrorName(remaining)); + ZSTD_freeCCtx(cctx); + return false; + } + + stream.writePipe(output.dst, output.pos); + + if (stream.reportProgress) + { + if (!stream.reportProgress(input.pos, output.pos)) + { + ZSTD_freeCCtx(cctx); + return false; + } + } + + /* If we're on the last chunk we're finished when zstd returns 0, + * which means its consumed all the input AND finished the frame. + * Otherwise, we're finished when we've consumed all the input. + */ + finished = lastChunk ? (remaining == 0) : (input.pos == input.size); + } while (!finished); + + SysAssertExp(input.pos == input.size); + + if (lastChunk) break; + } + + ZSTD_freeCCtx(cctx); + return true; + } + + static bool CompressZLib(const CompressionPipe &stream, const CompressionInfo &info) + { + int ret, flush; + z_stream strm {}; + unsigned char in[kChunkSize]; + unsigned char out[kChunkSize]; + + ret = deflateInit(&strm, info.compressionLevel); + if (ret != Z_OK) + { + SysPushErrorIO("Error: {}", ret); + return false; + } + + AuUInt32 inputStat = 0, outputStat = 0; + + do + { + auto read = stream.inPipe(in, ArraySize(in)); + strm.avail_in = read; + + inputStat += read; + + flush = !strm.avail_in ? Z_FINISH : Z_NO_FLUSH; + strm.next_in = in; + + do + { + strm.avail_out = ArraySize(out); + strm.next_out = out; + + ret = deflate(&strm, flush); + if (ret != Z_OK) + { + SysPushErrorIO("Error: {}", ret); + return false; + } + + auto have = ArraySize(out) - strm.avail_out; + + stream.writePipe(out, have); + + outputStat += have; + if (!stream.reportProgress(inputStat, outputStat)) + { + deflateEnd(&strm); + return false; + } + + } while (strm.avail_out == 0); + SysAssert(strm.avail_in == 0); + + } while (flush != Z_FINISH); + + deflateEnd(&strm); + return true; + } + + static bool DecompressZLib(const CompressionPipe &stream) + { + int ret; + z_stream strm {}; + unsigned char in[kChunkSize]; + unsigned char out[kChunkSize]; + + ret = inflateInit(&strm); + if (ret != Z_OK) + { + SysPushErrorIO("Error: {}", ret); + return false; + } + + AuUInt32 inputStat = 0, outputStat = 0; + + do + { + auto read = stream.inPipe(in, ArraySize(in)); + inputStat += read; + if (!read) + { + break; + } + + strm.avail_in = read; + strm.next_in = in; + + do + { + strm.avail_out = ArraySize(out); + strm.next_out = out; + + ret = inflate(&strm, Z_NO_FLUSH); + if (ret != Z_OK) + { + SysPushErrorIO("Error: {}", ret); + return false; + } + + auto have = ArraySize(out) - strm.avail_out; + + stream.writePipe(out, have); + + outputStat += have; + if (!stream.reportProgress(inputStat, outputStat)) + { + inflateEnd(&strm); + return false; + } + + } while (strm.avail_out == 0); + SysAssert(strm.avail_in == 0); + + } while (ret != Z_STREAM_END); + + inflateEnd(&strm); + return true; + } + + static bool CompressBZip2(const CompressionPipe &stream, const CompressionInfo &info) + { + int ret, flush; + bz_stream strm {}; + char in[kChunkSize]; + char out[kChunkSize]; + + ret = BZ2_bzCompressInit(&strm, info.compressionLevel, 0, 0); + if (ret != BZ_OK) + { + SysPushErrorIO("Error: {}", ret); + return false; + } + + AuUInt32 inputStat = 0, outputStat = 0; + + do + { + auto read = stream.inPipe(in, ArraySize(in)); + strm.avail_in = read; + + inputStat += read; + + flush = !strm.avail_in ? BZ_FINISH : BZ_RUN; + strm.next_in = in; + + do + { + strm.avail_out = ArraySize(out); + strm.next_out = out; + + ret = BZ2_bzCompress(&strm, flush); + if (ret != BZ_OK) + { + SysPushErrorIO("Error: {}", ret); + return false; + } + + auto have = ArraySize(out) - strm.avail_out; + + stream.writePipe(out, have); + + outputStat += have; + if (!stream.reportProgress(inputStat, outputStat)) + { + BZ2_bzCompressEnd(&strm); + return false; + } + + } while (strm.avail_out == 0); + SysAssert(strm.avail_in == 0); + + } while (flush != BZ_FINISH); + + BZ2_bzCompressEnd(&strm); + return true; + } + + static bool DecompressBZip2(const CompressionPipe &stream) + { + int ret; + bz_stream strm {}; + char in[kChunkSize]; + char out[kChunkSize]; + + ret = BZ2_bzDecompressInit(&strm, 0, 0); + if (ret != Z_OK) + { + SysPushErrorIO("Error: {}", ret); + return false; + } + + AuUInt32 inputStat = 0, outputStat = 0; + + do + { + auto read = stream.inPipe(in, ArraySize(in)); + inputStat += read; + if (!read) + { + break; + } + + strm.avail_in = read; + strm.next_in = in; + + do + { + strm.avail_out = ArraySize(out); + strm.next_out = out; + + ret = BZ2_bzDecompress(&strm); + if (ret != Z_OK) + { + SysPushErrorIO("Error: {}", ret); + return false; + } + + auto have = ArraySize(out) - strm.avail_out; + + stream.writePipe(out, have); + + outputStat += have; + if (!stream.reportProgress(inputStat, outputStat)) + { + BZ2_bzDecompressEnd(&strm); + return false; + } + + } while (strm.avail_out == 0); + SysAssert(strm.avail_in == 0); + + } while (ret != BZ_FINISH); + + BZ2_bzDecompressEnd(&strm); + return true; + } + + static bool CompressLZ4(const CompressionPipe &stream, const CompressionInfo &info) + { + bool ret = true; + LZ4_stream_t* const lz4Stream = LZ4_createStream(); + char inBuf[kChunkSize]; + char outBuf[kChunkSize]; + AuUInt32 inputStat = 0, outputStat = 0; + + while (true) + { + auto read = stream.inPipe(inBuf, ArraySize(inBuf)); + if (!read) break; + + AuUInt16 bufferedBytes = LZ4_compress_fast_continue(lz4Stream, inBuf, outBuf, read, ArraySize(outBuf), 1); + + if (bufferedBytes <= 0) + { + ret = false; + break; + } + + stream.writePipe(&bufferedBytes, sizeof(bufferedBytes)); + stream.writePipe(outBuf, bufferedBytes); + + inputStat += read; + outputStat += bufferedBytes; + + if (!stream.reportProgress(inputStat, outputStat)) break; + } + + LZ4_freeStream(lz4Stream); + return ret; + } + + static bool DecompressLZ4(const CompressionPipe &pipe) + { + bool ret = true; + LZ4_streamDecode_t* lz4Stream = LZ4_createStreamDecode(); + char inBuf[kChunkSize]; + char outBuf[kChunkSize]; + AuUInt32 inputStat = 0, outputStat = 0; + + while (true) + { + AuUInt16 bufferedBytes; + auto read = pipe.inPipe(&bufferedBytes, sizeof(bufferedBytes)); + if (read == 0) + { + break; + } + + if (read != 2) + { + ret = false; + goto out; + } + + //while (bufferedBytes) TODO: + { + auto toRead = std::min(AuUInt16(ArraySize(inBuf)), bufferedBytes); + + read = pipe.inPipe(inBuf, toRead); + if (read != toRead) + { + ret = false; + goto out; + } + bufferedBytes -= toRead; + + auto err = LZ4_decompress_safe_continue(lz4Stream, inBuf, outBuf, read, ArraySize(outBuf)); + if (err < 0) + { + ret = false; + goto out; + } + + pipe.writePipe(outBuf, err); + + inputStat += bufferedBytes; + outputStat += read; + + if (!pipe.reportProgress(inputStat, outputStat)) + { + break; + } + } + } + + out: + LZ4_freeStreamDecode(lz4Stream); + return ret; + } + + AUKN_SYM bool Compress(const CompressionPipe &stream, const CompressionInfo &info) + { + switch (info.type) + { + case ECompresionType::eZSTD: + return CompressZSTD(stream, info); + case ECompresionType::eDeflate: + return CompressZLib(stream, info); + case ECompresionType::eBZIP2: + return CompressBZip2(stream, info); + case ECompresionType::eLZ4: + return CompressLZ4(stream, info); + //case ECompresionType::eLZMA: + // return CompressLZMA(stream, info); + default: + return false; + } + } + + + AUKN_SYM bool Decompress(const CompressionPipe &pipe) + { + switch (pipe.type) + { + case ECompresionType::eZSTD: + return DecompressZSTD(pipe); + case ECompresionType::eDeflate: + return DecompressZLib(pipe); + case ECompresionType::eBZIP2: + return DecompressBZip2(pipe); + case ECompresionType::eLZ4: + return DecompressLZ4(pipe); + //case ECompresionType::eLZMA: + // return DecompressLZMA(pipe); + default: + return false; + } + } +} \ No newline at end of file diff --git a/Source/Compression/StreamCompression.hpp b/Source/Compression/StreamCompression.hpp new file mode 100644 index 00000000..0b33ccdf --- /dev/null +++ b/Source/Compression/StreamCompression.hpp @@ -0,0 +1,8 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: StreamCompression.hpp + Date: 2021-6-17 + Author: Reece +***/ +#pragma once diff --git a/Source/Console/Commands/Commands.cpp b/Source/Console/Commands/Commands.cpp new file mode 100644 index 00000000..62875f1f --- /dev/null +++ b/Source/Console/Commands/Commands.cpp @@ -0,0 +1,102 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Commands.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "Commands.hpp" + +namespace Aurora::Console::Commands +{ + struct Command; + struct CommandDispatch; + + static AuHashMap gCommands; + static AuList gLineCallbacks; + static AuList gPendingCommands; + static auto gMutex = Threading::Primitives::MutexUnique(); + + struct Command + { + AuString tag; + Parse::ParseObject commandStructure; + const CommandCallback_cb &callback; + + Command(AuString tag, Parse::ParseObject commandStructure, const CommandCallback_cb &callback) : tag(tag), commandStructure(commandStructure), callback(callback) {} + }; + + struct CommandDispatch + { + Parse::ParsedObject arguments; + const CommandCallback_cb &callback; + + CommandDispatch(const Parse::ParsedObject &arguments, const CommandCallback_cb &callback) : arguments(arguments), callback(callback) {} + }; + + static bool Dispatch(const AuString &string) + { + Threading::WaitableLockGuard guard(gMutex.get()); + AuString tag; + AuString cmdParse; + AuMach offset; + Parse::ParseResult res; + + auto commandSplit = string.find(" "); + if (commandSplit != AuString::npos) + { + tag = string.substr(0, commandSplit); + cmdParse = string.substr(commandSplit + 1); + } + else + { + tag = string; + } + + auto cmdItr = gCommands.find(tag); + if (cmdItr == gCommands.end()) + { + LogWarn("Command {} does not exist", tag); + return false; + } + + auto const &cmdEntry = cmdItr->second; + + offset = 0; + auto consumable = Parse::StringToConsumable(cmdParse, offset); + auto status = Parse::Parse(res, cmdEntry.commandStructure, consumable); + + if (!status) + { + LogWarn("Couldn't parse command {}", string); + return false; + } + + gPendingCommands.push_back(CommandDispatch(res.result, cmdEntry.callback)); + return true; + } + + void AddCommand(const AuString &tag, const Parse::ParseObject &commandStructure, const CommandCallback_cb &callback) + { + Threading::WaitableLockGuard guard(gMutex.get()); + gCommands.insert(std::make_pair(tag, Command(tag, commandStructure, callback))); + } + + bool DispatchCommand(const AuString &string) + { + return Dispatch(string); + } + + void PumpCommands() + { + gMutex->Lock(); + auto commands = std::exchange(gPendingCommands, {}); + gMutex->Unlock(); + + for (const auto &command : commands) + { + command.callback(command.arguments); + } + } +} \ No newline at end of file diff --git a/Source/Console/Commands/Commands.hpp b/Source/Console/Commands/Commands.hpp new file mode 100644 index 00000000..a1c220f9 --- /dev/null +++ b/Source/Console/Commands/Commands.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Commands.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + +namespace Aurora::Console::Commands +{ + void PumpCommands(); +} \ No newline at end of file diff --git a/Source/Console/Console.cpp b/Source/Console/Console.cpp new file mode 100644 index 00000000..6482cdb3 --- /dev/null +++ b/Source/Console/Console.cpp @@ -0,0 +1,48 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Console.cpp + Date: 2021-6-8 + Author: Reece +***/ +#include +#include "Console.hpp" +#include "Commands/Commands.hpp" +#include "Hooks/Hooks.hpp" +#include "ConsoleAsioStd/ConsoleAsioStd.hpp" +#include "ConsoleWxWidgets/ConsoleWxWidgets.hpp" +#include "ConsoleLogger/ConsoleLogger.hpp" + +namespace Aurora::Console +{ + void WriteLine(const ConsoleMessage &msg) + { + Hooks::WriteLine(msg); + } + + void Init() + { + ConsoleAsioStd::Init(); + ConsoleWxWidgets::Init(); + } + + void Init2() + { + ConsoleLogger::Init(); + } + + void Pump() + { + Commands::PumpCommands(); + ConsoleAsioStd::Pump(); + ConsoleWxWidgets::Pump(); + ConsoleLogger::Pump(); + } + + void Exit() + { + ConsoleAsioStd::Exit(); + ConsoleWxWidgets::Exit(); + ConsoleLogger::Exit(); + } +} \ No newline at end of file diff --git a/Source/Console/Console.hpp b/Source/Console/Console.hpp new file mode 100644 index 00000000..ec3ad417 --- /dev/null +++ b/Source/Console/Console.hpp @@ -0,0 +1,16 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Console.hpp + Date: 2021-6-8 + Author: Reece +***/ +#pragma once + +namespace Aurora::Console +{ + void Init(); + void Init2(); + void Pump(); + void Exit(); +} \ No newline at end of file diff --git a/Source/Console/ConsoleAsioStd/ConsoleAsioStd.cpp b/Source/Console/ConsoleAsioStd/ConsoleAsioStd.cpp new file mode 100644 index 00000000..021825e7 --- /dev/null +++ b/Source/Console/ConsoleAsioStd/ConsoleAsioStd.cpp @@ -0,0 +1,196 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConsoleAsioStd.cpp + Date: 2021-6-8 + Author: Reece +***/ +#include +#include "ConsoleAsioStd.hpp" + +namespace Aurora::Console::ConsoleAsioStd +{ +#if defined(AURORA_PLATFORM_WIN32) || defined(AURORA_PLATFORM_LINUX) || defined(AURORA_PLATFORM_APPLE) + +#if defined(AURORA_PLATFORM_WIN32) + using StreamDescriptor_t = asio::windows::stream_handle; +#else + using StreamDescriptor_t = asio::posix::stream_descriptor; +#endif + + static const AuMach kLineBufferMax = 2048; + + static AuUInt8 gLineBuffer[kLineBufferMax] = { 0 }; + static AuString gLineString = ""; + + static StreamDescriptor_t *gInputStream = {}; + static StreamDescriptor_t *gOutputStream = {}; + + static asio::io_context gIOC; + + void Init() + { + #if defined(AURORA_PLATFORM_WIN32) + + if (GetConsoleWindow() == NULL) + { + if (!gRuntimeConfig.console.forceConsoleWindow) + { + return; + } + + if (gRuntimeConfig.console.disableAll) + { + return; + } + + auto ok = AllocConsole(); + SysAssert(ok, "Request of a Win32 console yielded nada, forceConsole wasn't respected"); + } + else + { + // no always means no under UNIX targets; but on windows, if you're compiling a console app, dont be surprised to find console output + // instead of attempting to detach from conhost, link as a WindowedApp and do not setup any windows, if you're looking for windowless + // ignore gRuntimeConfig.console.disableAll + } + + DWORD dwMode; + bool ok; + auto hOutput = GetStdHandle(STD_OUTPUT_HANDLE); + SysAssert(hOutput != INVALID_HANDLE_VALUE, "Couldn't open STDOUT"); + + ok = GetConsoleMode(hOutput, &dwMode); + SysAssert(ok, "Couldn't get console mode"); + + dwMode |= ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING; + ok = SetConsoleMode(hOutput, dwMode); + SysAssert(ok, "Couldn't set console mode"); + + auto fileHandle = CreateFileA("CONIN$", + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, + NULL); + + SysAssert(fileHandle != INVALID_HANDLE_VALUE, "Couldn't open CONIN"); + gInputStream = _new StreamDescriptor_t(gIOC, fileHandle); + + fileHandle = CreateFileA("CONOUT$", + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING, + NULL); + + SysAssert(fileHandle != INVALID_HANDLE_VALUE, "Couldn't open CONOUT"); + gOutputStream = _new StreamDescriptor_t(gIOC, fileHandle); + + #else + + // no always means no under UNIX targets + // we *know* stdin/out should be available under these standards + if (gRuntimeConfig.console.disableAll) + { + return; + } + + gInputStream = _new StreamDescriptor_t(gIOC, STDIN_FILENO); + gOutputStream = _new StreamDescriptor_t(gIOC, STDOUT_FILENO); + + #endif + + SysAssert(gInputStream, "Couldn't allocate input stream handler"); + SysAssert(gOutputStream, "Couldn't allocate output stream handler"); + + Aurora::Console::Hooks::AddHook([](const Aurora::Console::ConsoleMessage &string) -> void + { + #if defined(DEBUG) && defined(AURORA_PLATFORM_WIN32) + auto debugLine = string.ToSimplified() + "\r\n"; + OutputDebugStringA(debugLine.c_str()); + #endif + + auto writeLine = string.ToConsole(); + + #if defined(AURORA_PLATFORM_WIN32) + writeLine += '\r'; + #endif + writeLine += '\n'; + + asio::write(*gOutputStream, asio::buffer(writeLine)); + }); + } + + static void ProcessLines() + { + AuMach index = 0; + + while ((index = gLineString.find("\n")) != AuString::npos) + { + auto line = gLineString.substr(0, index); + gLineString = gLineString.substr(index + 1); + + if (line[line.size() - 1] == '\r') + { + line.pop_back(); + } + + if (line.size()) + { + Aurora::Console::Commands::DispatchCommand(line); + } + } + } + + static void StdInHandler(const asio::error_code &code, std::size_t bytesCopied) + { + if (bytesCopied == 0) + { + return; + } + + gLineString.append(reinterpret_cast(gLineBuffer), bytesCopied); + + ProcessLines(); + } + + void Pump() + { + if (!gInputStream) return; + + gInputStream->async_read_some(asio::buffer(gLineBuffer, kLineBufferMax), StdInHandler); + + gIOC.poll_one(); + } + + void Exit() + { + if (gInputStream) + { + delete gInputStream; + } + + if (gOutputStream) + { + delete gOutputStream; + } + } + +#else + + void Pump() + { + } + + void Init() + { + } + + void Exit() + { + } + +#endif +} \ No newline at end of file diff --git a/Source/Console/ConsoleAsioStd/ConsoleAsioStd.hpp b/Source/Console/ConsoleAsioStd/ConsoleAsioStd.hpp new file mode 100644 index 00000000..01f27e15 --- /dev/null +++ b/Source/Console/ConsoleAsioStd/ConsoleAsioStd.hpp @@ -0,0 +1,15 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConsoleAsioStd.hpp + Date: 2021-6-8 + Author: Reece +***/ +#pragma once + +namespace Aurora::Console::ConsoleAsioStd +{ + void Init(); + void Pump(); + void Exit(); +} \ No newline at end of file diff --git a/Source/Console/ConsoleLogger/ConsoleLogger.cpp b/Source/Console/ConsoleLogger/ConsoleLogger.cpp new file mode 100644 index 00000000..42b16287 --- /dev/null +++ b/Source/Console/ConsoleLogger/ConsoleLogger.cpp @@ -0,0 +1,201 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConsoleLogger.cpp + Date: 2021-6-22 + Author: Reece +***/ +#include +#include "ConsoleLogger.hpp" + +namespace Aurora::Console::ConsoleLogger +{ + static Threading::Threads::ThreadUnique_t gFileWriterThread; + static AuList gLogBuffer; + static Threading::Primitives::RWLockUnique_t gLogMutex; + static IO::FS::OpenWriteUnique_t gFileHandle; + + static const auto & gLogConfig = gRuntimeConfig.console.logging; + + static void EraseFilesByTimestamp(const AuString &path, /*const its not worth reallocation*/ AuList &files) + { + if (files.size() <= gLogConfig.maxLogs) + { + return; + } + + // our filenames are usually prefixed by an ISO 8601 timestamp where the most significant bits are last (YYYY-MM-DD?HH-MM-SS) + // a quick ghetto sort should be all we need. no need to waste time parsing timestamps + std::sort(files.begin(), files.end()); + + auto amount = files.size() - gLogConfig.maxLogs; + for (auto x = 0, i = 0; ((x < amount) && (i > files.size())); i++) + { + try + { + if (IO::FS::Remove(path + "/" + files[i])) + { + x++; + } + } + catch (...) + { + + } + } + } + + static void CleanupOldLogs() + { + AuString path; + AuString procName; + + if (!Process::GetProcName(procName)) + { + return; + } + + if (!IO::FS::GetProfileDomain(path)) + { + return; + } + + path += "/Logs/"; + path += procName; + + AuList files; + AuBST fileMeta; + AuUInt32 size {}; + IO::FS::FilesInDirectory(path, files); + + for (const auto &file : files) + { + IO::FS::Stat stat; + IO::FS::StatFile(path + "/" + file, stat); + fileMeta[file] = stat; + size += stat.size; + } + + EraseFilesByTimestamp(path, files); + // TODO: erase when size >= gLogConfig.maxSizeMb * 1024 + } + + static void CompressLogs() + { + + } + + void Flush() + { + Threading::WaitableLockGuard a(gLogMutex->AsReadable()); + + if (gFileHandle) + { + gFileHandle->Write(gLogBuffer.data(), gLogBuffer.size()); + } + + gLogBuffer.clear(); + } + + static bool OpenLogFile() + { + AuString path; + AuString procName; + + if (!Process::GetProcName(procName)) + { + return false; + } + + if (!IO::FS::GetProfileDomain(path)) + { + return false; + } + + auto tm = Time::ToCivilTime(Time::CurrentClockMS()); + + path += fmt::format("/Logs/{}/{:04}-{:02}-{:02}T{:02}-{:02}-{:02}Z.txt", + procName, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + + gFileHandle = IO::FS::OpenWriteUnique(path); + return gFileHandle ? true : false; + } + + static void LogThreadInit() + { + CleanupOldLogs(); + + auto thread = Threading::Threads::GetThread(); + while (!thread->Exiting()) + { + Sleep(500); + Flush(); + } + } + + void Init() + { + if (!gLogConfig.enableLogging) + { + return; + } + + if (!OpenLogFile()) + { + return; + } + + gLogMutex = Threading::Primitives::RWLockUnique(); + if (!gLogMutex) + { + return; + } + + Threading::Threads::AbstractThreadVectors handler; + handler.DoRun = [](Threading::Threads::IAuroraThread *) + { + LogThreadInit(); + }; + + gFileWriterThread = Threading::Threads::ThreadUnique(handler); + if (!gFileWriterThread) + { + return; + } + + gFileWriterThread->SetName("ConsoleFIO"); + gFileWriterThread->Run(); + + Console::Hooks::AddHook([&](const Console::ConsoleMessage &string) -> void + { + Threading::WaitableLockGuard a(gLogMutex->AsWritable()); + auto str = string.ToSimplified(); + + gLogBuffer.reserve(gLogBuffer.size() + str.size() + 2); + + gLogBuffer.insert(gLogBuffer.end(), reinterpret_cast(str.data()), reinterpret_cast(str.data()) + str.size()); + + #if defined(AURORA_PLATFORM_WIN32) + gLogBuffer.insert(gLogBuffer.end(), AuUInt8('\r')); + #endif + gLogBuffer.insert(gLogBuffer.end(), AuUInt8('\n')); + }); + } + + void Pump() + { + + } + + void Exit() + { + Flush(); + gFileWriterThread.reset(); + gLogMutex.reset(); + gFileHandle.reset(); + + CompressLogs(); + } +} \ No newline at end of file diff --git a/Source/Console/ConsoleLogger/ConsoleLogger.hpp b/Source/Console/ConsoleLogger/ConsoleLogger.hpp new file mode 100644 index 00000000..19cc724f --- /dev/null +++ b/Source/Console/ConsoleLogger/ConsoleLogger.hpp @@ -0,0 +1,16 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConsoleLogger.hpp + Date: 2021-6-22 + Author: Reece +***/ +#pragma once + +namespace Aurora::Console::ConsoleLogger +{ + void Init(); + void Pump(); + void Exit(); + void Flush(); +} \ No newline at end of file diff --git a/Source/Console/ConsoleMessage.cpp b/Source/Console/ConsoleMessage.cpp new file mode 100644 index 00000000..352d1fbf --- /dev/null +++ b/Source/Console/ConsoleMessage.cpp @@ -0,0 +1,60 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConsoleMessage.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "ConsoleMessage.hpp" + +namespace Aurora::Console +{ + static std::array(EAnsiColor::eCount)> AnsiCheats{ + "\033[0;31m", + "\033[1;31m", + "\033[0;32m", + "\033[1;32m", + "\033[0;33m", + "\033[1;33m" + "\033[0;34m", + "\033[1;34m", + "\033[0;35m", + "\033[1;35m", + "\033[0;36m", + "\033[1;36m", + "\033[0m", + "\033[0m" + }; + + AuString ConsoleMessage::StringifyTime(bool simple) const + { + tm localized; + + localized = Aurora::Time::ToCivilTime(time, false); + + if (simple) + { + return fmt::format("{:%H:%M:%S}", localized); + } + else + { + return fmt::format("{:%Y-%m-%d %H:%M:%S}", localized); + } + } + + AuString ConsoleMessage::GetWrappedTag() const + { + return "[" + prefix + "]"; + } + + AuString ConsoleMessage::ToConsole() const + { + return fmt::format("{}[{}] {:<7} | {}{}", AnsiCheats[static_cast(color)], StringifyTime(), GetWrappedTag(), line, AnsiCheats[static_cast(EAnsiColor::eReset)]); + } + + AuString ConsoleMessage::ToSimplified() const + { + return fmt::format("{:<9} {:<7} | {}", StringifyTime(true), GetWrappedTag(), line); + } +} \ No newline at end of file diff --git a/Source/Console/ConsoleMessage.hpp b/Source/Console/ConsoleMessage.hpp new file mode 100644 index 00000000..37f76318 --- /dev/null +++ b/Source/Console/ConsoleMessage.hpp @@ -0,0 +1,8 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConsoleMessage.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once diff --git a/Source/Console/ConsoleWxWidgets/ConsoleWxWidgets.cpp b/Source/Console/ConsoleWxWidgets/ConsoleWxWidgets.cpp new file mode 100644 index 00000000..d51e25cd --- /dev/null +++ b/Source/Console/ConsoleWxWidgets/ConsoleWxWidgets.cpp @@ -0,0 +1,581 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConsoleWxWidgets.cpp + Date: 2021-6-8 + Author: Reece +***/ +#include +#include "ConsoleWxWidgets.hpp" + +#if defined(_AUHAS_WXWIDGETS) +#include +#include +#include + +#include +#include +#include + +static bool DarkModeEnabled(); + +class ConsoleFrame; + +static AuList gPendingLines; +static Aurora::Threading::Primitives::MutexUnique_t gMutex; +static bool gWxConsoleReady; +static ConsoleFrame *gWxFrame; + + +class WxSplitterLine : public wxWindow +{ +public: + WxSplitterLine() {} + WxSplitterLine(wxSize splitter, wxColor color, wxWindow *parent, wxWindowID winid) : wxWindow(parent, winid), _color(color) + { + SetMinSize(splitter); + } + + void OnPaint(wxPaintEvent &paint) + { + wxPaintDC re(this); + re.SetBrush(_color); + re.SetBackground(_color); + re.Clear(); + } + +private: + DECLARE_EVENT_TABLE() + + wxColor _color; +}; +wxBEGIN_EVENT_TABLE(WxSplitterLine, wxPanel) + EVT_PAINT(WxSplitterLine::OnPaint) +wxEND_EVENT_TABLE() + +class ConsoleApp : public wxApp +{ +public: + ~ConsoleApp(); + virtual bool OnInit(); +}; +IMPLEMENT_APP_NO_MAIN(ConsoleApp); + +ConsoleApp::~ConsoleApp() +{ + +} + + +class ConsoleFrame : public wxFrame +{ +public: + ConsoleFrame(const wxString &title, const wxPoint &pos, const wxSize &size); + + ~ConsoleFrame() + { + Aurora::Threading::Primitives::MutexUnique_t re(gMutex.get()); + gWxConsoleReady = false; + } + + void WriteLine(const Aurora::Console::ConsoleMessage &msg); + +private: + + void OnHello(wxCommandEvent &event); + void OnExit(wxCommandEvent &event); + void OnAbout(wxCommandEvent &event); + void OnBugWrite(wxCommandEvent &event); + void OnInit(wxWindowCreateEvent &event); + void OnCmd(wxCommandEvent &event); + + WxSplitterLine *NewSplitter(wxSize splitter, wxColor color); + + wxDECLARE_EVENT_TABLE(); + wxRichTextCtrl *richtextbox_; + wxTextCtrl *commandbox_; + wxMenuBar *menuBar_; + wxBoxSizer *sizer_; + AuList _splitters; +}; + +enum +{ + ID_Hello = 1, + ID_BLACKBOX, + ID_WRITE_REPORT, + ID_OPENTXT, + ID_OPENBIN +}; +wxBEGIN_EVENT_TABLE(ConsoleFrame, wxFrame) + EVT_MENU(ID_Hello, ConsoleFrame::OnHello) + EVT_MENU(wxID_EXIT, ConsoleFrame::OnExit) + EVT_MENU(wxID_ABOUT, ConsoleFrame::OnAbout) + EVT_MENU(ID_WRITE_REPORT, ConsoleFrame::OnBugWrite) + EVT_WINDOW_CREATE(ConsoleFrame::OnInit) +wxEND_EVENT_TABLE() + + +bool ConsoleApp::OnInit() +{ + ConsoleFrame *frame = _new ConsoleFrame("Aurora Engine Console", wxPoint(50, 50), wxSize(1080, 550)); + frame->Show(true); + gWxFrame = frame; + return true; +} + +void ConsoleFrame::OnCmd(wxCommandEvent &event) +{ + // im lazy, i know, bite me + auto textbox = reinterpret_cast(this); + + AuString line = textbox->GetLineText(0); + if (line.empty()) + { + return; + } + + textbox->Clear(); + + Aurora::Console::Commands::DispatchCommand(line); +} + +WxSplitterLine *ConsoleFrame::NewSplitter(wxSize splitter, wxColor color) +{ + auto re = new WxSplitterLine(splitter, color, this, -1); + _splitters.push_back(re); + return re; +} + +void ConsoleFrame::WriteLine(const Aurora::Console::ConsoleMessage &string) +{ + static std::array(Aurora::Console::EAnsiColor::eCount)> ColorMap + { + *wxRED, // kRed, + *wxRED, // kBoldRed, + *wxGREEN, // kGreen, + *wxGREEN, // kBoldGreen, + *wxYELLOW, // kYellow, + *wxYELLOW, // kBoldYellow, + *wxBLUE, // kBlue, + *wxBLUE, // kBoldBlue, + *wxRED, // kMagenta, + *wxRED, // kBoldMagenta, + *wxBLUE, // kCyan, + *wxBLUE, // kBoldCyan, + *wxWHITE, // kReset + }; + + static std::array(Aurora::Console::EAnsiColor::eCount)> HahaBald + { + false, // kRed, + true, // kBoldRed, + false, // kGreen, + true, // kBoldGreen, + false, // kYellow, + true, // kBoldYellow, + false, // kBlue, + true, // kBoldBlue, + false, // kMagenta, + true, // kBoldMagenta, + false, // kCyan, + true, // kBoldCyan, + false, // kReset + }; + + auto writeLine = string.ToSimplified(); + + bool useColor = DarkModeEnabled(); + + auto color = useColor ? ColorMap[static_cast(string.color)] : wxColor(0, 0, 0); + auto bold = useColor ? HahaBald[static_cast(string.color)] : false; + + + if (richtextbox_->GetNumberOfLines() > 1000) + richtextbox_->Clear(); + + //richtextbox_->BeginParagraphSpacing(0, 20); + + auto pos = richtextbox_->GetCaretPosition(); + auto selPos = richtextbox_->GetLastPosition() - 1; + + richtextbox_->SetCaretPosition(selPos); + + richtextbox_->BeginTextColour(color); + + if (bold) + richtextbox_->BeginBold(); + + richtextbox_->WriteText(writeLine); + richtextbox_->Newline(); + + if (bold) + richtextbox_->EndBold(); + + richtextbox_->EndTextColour(); + + + if ((pos == -1) || // true once + (pos == selPos) || // most likely + (abs(pos - selPos) < 5)) // ux + { + auto newPos = richtextbox_->GetLastPosition() - 1; + richtextbox_->ScrollIntoView(newPos, WXK_DOWN); + richtextbox_->SetCaretPosition(newPos); + } + else + { + richtextbox_->SetCaretPosition(pos); + } + + //richtextbox_->Disable + //richtextbox_->EndParagraphSpacing(); +} + +ConsoleFrame::ConsoleFrame(const wxString &title, const wxPoint &pos, const wxSize &size) + : wxFrame(NULL, wxID_ANY, title, pos, size) +{ +#if defined(AURORA_PLATFORM_WIN32) + bool useWin32DarkHack = DarkModeEnabled(); +#else + bool useWin32DarkHack = false; +#endif + + // 1080, 550 + this->SetClientSize(this->FromDIP(wxSize(903, 434))); + + menuBar_ = _new wxMenuBar; + if (!menuBar_) return; + + { + wxMenu *menuFile = _new wxMenu; + if (!menuFile) return; + + menuFile->Append(wxID_SAVE, "Export text log"); + menuFile->AppendSeparator(); + menuFile->Append(ID_OPENTXT, "Open text log"); + menuFile->Append(ID_OPENBIN, "Open binary log"); + + menuBar_->Append(menuFile, "&Logs"); + } + + { + wxMenu *menuHelp = _new wxMenu; + if (!menuHelp) return; + + menuHelp->Append(ID_BLACKBOX, "&Report application state to the black box"); + menuHelp->Append(ID_WRITE_REPORT, "&[Internal] Write a bug report"); + menuHelp->Append(ID_WRITE_REPORT, "&[Public] Write a bug report"); + menuBar_->Append(menuHelp, "&Bugs"); + } + + richtextbox_ = _new wxRichTextCtrl(this, -1, wxEmptyString, + wxDefaultPosition, wxDefaultSize, + (useWin32DarkHack ? wxBORDER_NONE : 0) | wxTE_MULTILINE | wxRE_READONLY | wxRE_CENTER_CARET); + if (!richtextbox_) return; + + commandbox_ = _new wxTextCtrl(this, -1, wxEmptyString, + wxDefaultPosition, wxDefaultSize, + (useWin32DarkHack ? wxBORDER_NONE : 0) | wxTE_PROCESS_ENTER); + if (!commandbox_) return; + + if (useWin32DarkHack) + { + this->SetBackgroundColour(wxColour(0, 0, 0)); + + richtextbox_->SetBackgroundColour(wxColour(0, 0, 0)); + richtextbox_->SetForegroundColour(wxColour(255, 255, 255)); + + commandbox_->SetBackgroundColour(wxColour(0, 0, 0)); + commandbox_->SetForegroundColour(wxColour(255, 255, 255)); + + this->Connect(wxEVT_SHOW, wxWindowCreateEventHandler(ConsoleFrame::OnInit)); + } + + commandbox_->SetHint("Type a command here"); + commandbox_->Connect(wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler(ConsoleFrame::OnCmd)); + + sizer_ = _new wxBoxSizer(wxVERTICAL); + if (!sizer_) return; + + sizer_->Add(richtextbox_, 3, wxEXPAND); + if (useWin32DarkHack) + { + sizer_->Add(NewSplitter(wxSize(4, 2), wxColour(0x38, 0x38, 0x38)), 0, wxEXPAND); + sizer_->Add(NewSplitter(wxSize(4, 1), wxColour(0, 0, 0)), 0, wxEXPAND); + } + sizer_->Add(commandbox_, 0, wxEXPAND | wxBOTTOM); + if (useWin32DarkHack) + { + sizer_->Add(NewSplitter(wxSize(4, 1), wxColour(0, 0, 0)), 0, wxEXPAND); + sizer_->Add(NewSplitter(wxSize(4, 2), wxColour(0x38, 0x38, 0x38)), 0, wxEXPAND); + } + + SetSizer(sizer_); + SetMenuBar(menuBar_); + + gWxConsoleReady = true; +} + +#if defined(AURORA_PLATFORM_WIN32) +#include +#include + +static bool DarkModeEnabled() +{ + return Aurora::Extensions::Win32::g_darkModeSupported; +} + +void ConsoleFrame::OnInit(wxWindowCreateEvent &event) +{ + if (!DarkModeEnabled()) return; + + auto handle = event.GetWindow()->GetHWND(); + Aurora::Extensions::Win32::AllowDarkModeForWindow(handle, true); + + if (Aurora::Extensions::Win32::_FlushMenuThemes) + Aurora::Extensions::Win32::_FlushMenuThemes(); + + SetWindowTheme(handle, L"DarkMode_Explorer", NULL); + SendMessageW(handle, WM_THEMECHANGED, 0, 0); + + Aurora::Extensions::Win32::RefreshTitleBarThemeColor(handle); + UpdateWindow(handle); +} + +#else +static bool DarkModeEnabled() +{ + return Aurora::Build::EPlatform == Aurora::Build::EPlatform::kPlatformLinux; +} +#endif + +void ConsoleFrame::OnExit(wxCommandEvent &event) +{ + Close(true); +} + +void ConsoleFrame::OnAbout(wxCommandEvent &event) +{ + +} + +void ConsoleFrame::OnHello(wxCommandEvent &event) +{ + LogGame("nani?!"); +} + +void ConsoleFrame::OnBugWrite(wxCommandEvent &event) +{ + Aurora::Processes::OpenUri(gRuntimeConfig.console.supportInternal); +} + + +namespace Aurora::Console::ConsoleWxWidgets +{ + static wxInitializer *gInitializer; + + static bool UseWxConsole() + { + if (gRuntimeConfig.console.disableAll) + { + return false; + } + + if (GetConsoleWindow()) + { + if (!gRuntimeConfig.console.forceToolKitWindow) + { + return false; + } + + return true; + } + + return true; + } + + static bool WxWidgetsInit() + { + #if defined(AURORA_PLATFORM_WIN32) + Aurora::Extensions::Win32::InitDarkMode(); + #endif + + wxApp::SetInstance(_new ConsoleApp()); + + #if defined(AURORA_PLATFORM_WIN32) || true + std::wstring auroraw(L"Aurora"); + static wxChar *s = auroraw.data(); + static int count = 1; + #endif + + gInitializer = new wxInitializer(count, &s); + + if (!gInitializer->IsOk()) + { + #if wxUSE_LOG + delete wxLog::SetActiveTarget(NULL); + #endif + } + + try + { + if (!wxTheApp->CallOnInit()) + { + return false; + } + } + catch (...) + { + wxTheApp->OnUnhandledException(); + return false; + } + + return true; + } + + + void WxWidgetsRequestExit() + { + if (!gMutex) + { + return; + } + + Threading::LockGuardPtr re(gMutex.get()); // pupose two of the mutex: locking deconstruction/gWxConsoleReady + // its a horible hack, but - + // 1) our dtor comes before the final apps class, and + // 2) the app will deregister the top window before the dtor + // which means + // `if (!gWxConsoleReady) return;` under lock + // -> gWxConsoleReady must be true, and `wxTheApp->GetTopWindow`'s dtor is locked, + // even as a remote thread, we should be able to do ipc given a dtor pinned object safely + // the shitty wiki doesn't describe how we're supposed to dispatch cmds to the os loop (w/o a window) + + if (!gWxConsoleReady) return; + + auto window = wxTheApp->GetTopWindow(); + if (!window) return; + + window->GetEventHandler()->CallAfter([]() + { + wxTheApp->Exit(); + }); + } + + static Aurora::Threading::Threads::ThreadUnique_t gWxWidgetsThread; + + static void WxWidgetsThreadMain() + { + if (!WxWidgetsInit()) + { + return; + } + + try + { + wxTheApp->OnRun(); + } + catch (...) + { + wxTheApp->OnUnhandledException(); + } + } + + void Init() + { + if (!UseWxConsole()) + { + return; + } + + gMutex = Aurora::Threading::Primitives::MutexUnique(); + + Aurora::Console::Hooks::AddHook([&](const Aurora::Console::ConsoleMessage &string) -> void + { + gMutex->Lock(); + gPendingLines.push_back(string); + gMutex->Unlock(); + }); + + Aurora::Threading::Threads::AbstractThreadVectors handler; + handler.DoRun = [](Aurora::Threading::Threads::IAuroraThread *) + { + WxWidgetsThreadMain(); + }; + + gWxWidgetsThread = Aurora::Threading::Threads::ThreadUnique(handler); + if (!gWxWidgetsThread) + { + return; + } + + gWxWidgetsThread->SetName("WxWidgets"); + gWxWidgetsThread->Run(); + } + + static void WxWidgetsPump() + { + // called from within WxWidget main loop, do not worry about locking is ready + // we _know_ the wxwidget app is referenceable + + if (!wxTheApp) return; + if (!gWxConsoleReady) return; + + AuList lines; + { + Aurora::Threading::LockGuardPtr re(gMutex.get()); + lines = std::exchange(gPendingLines, {}); + } + + for (const auto& line : lines) + { + gWxFrame->WriteLine(line); + } + } + + void Pump() + { + Aurora::Threading::LockGuardPtr re(gMutex.get()); + + if (!gWxConsoleReady) return; + + wxTheApp->GetTopWindow()->GetEventHandler()->CallAfter([]() + { + WxWidgetsPump(); + }); + } + + void Exit() + { + WxWidgetsRequestExit(); + + gWxWidgetsThread.reset(); + + auto wxHandle = wxTheApp; + if (wxHandle) + { + delete wxHandle; + } + + gMutex.reset(); + gPendingLines.clear(); + } +} + +#else + +namespace Aurora::Console::ConsoleWxWidgets +{ + void Init() + { + } + void Pump() + { + } + void Exit() + { + } +} + +#endif \ No newline at end of file diff --git a/Source/Console/ConsoleWxWidgets/ConsoleWxWidgets.hpp b/Source/Console/ConsoleWxWidgets/ConsoleWxWidgets.hpp new file mode 100644 index 00000000..8c3e7b33 --- /dev/null +++ b/Source/Console/ConsoleWxWidgets/ConsoleWxWidgets.hpp @@ -0,0 +1,15 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ConsoleWxWidgets.hpp + Date: 2021-6-8 + Author: Reece +***/ +#pragma once + +namespace Aurora::Console::ConsoleWxWidgets +{ + void Init(); + void Pump(); + void Exit(); +} \ No newline at end of file diff --git a/Source/Console/Hooks/Hooks.cpp b/Source/Console/Hooks/Hooks.cpp new file mode 100644 index 00000000..f0f35511 --- /dev/null +++ b/Source/Console/Hooks/Hooks.cpp @@ -0,0 +1,58 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Hooks.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "Hooks.hpp" + +namespace Aurora::Console::Hooks +{ + static auto gMutex = Threading::Primitives::MutexUnique(); + static AuList gLineCallbacks; + + void AddHook(LineHook_cb hook) + { + Aurora::Threading::WaitableLockGuard guard(gMutex.get()); + gLineCallbacks.push_back(hook); + } + + void WriteLine(const ConsoleMessage &msg) + { + // Note: I believe waiting for the second pump to deposit each line + // would be a bad idea for warnings/debug logs. + // console messages must be written to the handler as soon as + // they are produced to alleviate loss of buffered data on termination. + gMutex->Lock(); + auto callbacks = gLineCallbacks; + gMutex->Unlock(); + + if (msg.line.find('\n') == std::string::npos) [[likely]] + { + for (const auto &callback : callbacks) + { + callback(msg); + } + } + else [[unlikely]] // Note: yes, this is a write line function. + // yes, .find('\n') is rather complex, but i dont care. + // logging FIO has and always will be the bottleneck. + // given the assumption this is slow, we may as well + // unfuck writelines with \n's. fmt's, unwinded stacks, + // etc all benefit from this + { + Aurora::Parse::SplitNewlines(msg.line, + [&](const AuString &line) + { + ConsoleMessage dup = msg; + dup.line = line; + for (const auto &callback : callbacks) + { + callback(dup); + } + }); + } + } +} \ No newline at end of file diff --git a/Source/Console/Hooks/Hooks.hpp b/Source/Console/Hooks/Hooks.hpp new file mode 100644 index 00000000..0278612e --- /dev/null +++ b/Source/Console/Hooks/Hooks.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Hooks.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + +namespace Aurora::Console::Hooks +{ + void WriteLine(const ConsoleMessage &msg); +} \ No newline at end of file diff --git a/Source/Crypto.cpp b/Source/Crypto.cpp new file mode 100644 index 00000000..64b474fa --- /dev/null +++ b/Source/Crypto.cpp @@ -0,0 +1,43 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Crypto.cpp + Date: 2021-6-17 + Author: Reece +***/ +#include +#include "Crypto.hpp" +#include + +namespace Crypto +{ + static prng_state gPrng; + + static void TomCryptInit() + { + crypt_mp_init("ltm"); + + yarrow_start(&gPrng); + + gHashTiger = register_hash(&tiger_desc); + register_hash(&md5_desc); + gHashSha1 = register_hash(&sha1_desc); + gHashSha256 = register_hash(&sha256_desc); + gHashSha512 = register_hash(&sha512_desc); + register_hash(&rmd128_desc); + register_hash(&rmd160_desc); + gPrngYarrow = register_prng(&yarrow_desc); + gAesCipher = register_cipher(&aes_desc); + } + + static void MBedTlsInit() + { + + } + + void InitCrypto() + { + TomCryptInit(); + MBedTlsInit(); + } +} \ No newline at end of file diff --git a/Source/Crypto.hpp b/Source/Crypto.hpp new file mode 100644 index 00000000..17c22f7c --- /dev/null +++ b/Source/Crypto.hpp @@ -0,0 +1,20 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Crypto.hpp + Date: 2021-6-17 + Author: Reece +***/ +#pragma once + +namespace Crypto +{ + inline int gAesCipher; + inline int gHashTiger; + inline int gHashSha1; + inline int gHashSha256; + inline int gHashSha512; + inline int gPrngYarrow; + + void InitCrypto(); +} \ No newline at end of file diff --git a/Source/Crypto/AES/Aes.cpp b/Source/Crypto/AES/Aes.cpp new file mode 100644 index 00000000..44d38952 --- /dev/null +++ b/Source/Crypto/AES/Aes.cpp @@ -0,0 +1,292 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Aes.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "../Crypto.hpp" +#include "Aes.hpp" +#include +#include + +namespace Aurora::Crypto::AES +{ + AUKN_SYM AuUInt GetSafeCipherPadding(const void *plainText, AuUInt plainTextLength) + { + auto tptr = reinterpret_cast(plainText); + + auto overhang = plainTextLength & (128 - 1); + auto primaryLength = plainTextLength - overhang; + + if (overhang == 0) + { + auto lastbyte = tptr[primaryLength - 1]; + if (lastbyte <= 128) + { + return plainTextLength + 128; + } + else + { + return plainTextLength; + } + } + else + { + return 128 - overhang; + } + } + + static bool EncryptCoolCodePadding(const void *plainText, AuUInt plainTextLength, + const void *iv, void *outIv, AuUInt ivLength, + const void *key, AuUInt keyLength, + AuList &out, + symmetric_CBC &cbc) + { + + auto overhang = plainTextLength & (128 - 1) /* || fast_rng_bool */; + auto padding = 128 - overhang; + auto primaryLength = plainTextLength - overhang; + + int ret; + auto tptr = reinterpret_cast(plainText); + + unsigned char pad[128] = + { + 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0xDE, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0xDE, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0xDE, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0xDE, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0xDE, + 0x00, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, + 0xC0, 0x01, 0xC0, 0xDE, 0x00, 0x00, 0x00, 0x80 + }; + + if (overhang == 0) + { + auto lastbyte = tptr[primaryLength - 1]; + if (lastbyte <= 128) + { + if (!TryResize(out, primaryLength + 128)) + { + SysPushErrorMem(); + return false; + } + + // encrypt plain text + ret = cbc_encrypt(tptr, out.data(), primaryLength, &cbc); + if (ret != CRYPT_OK) + { + SysPushErrorCrypto("{}", ret); + return false; + } + + // encrypt another 128 bytes of trash :( 50/50 chance + ret = cbc_encrypt(pad, out.data() + primaryLength, 128, &cbc); + if (ret != CRYPT_OK) + { + SysPushErrorCrypto("{}", ret); + return false; + } + } + else + { + if (!TryResize(out, primaryLength)) + { + SysPushErrorMem(); + return false; + } + + ret = cbc_encrypt(tptr, out.data(), primaryLength, &cbc); + if (ret != CRYPT_OK) + { + SysPushErrorCrypto("{}", ret); + return false; + } + } + } + else + { + if (!TryResize(out, primaryLength + 128)) + { + SysPushErrorMem(); + return false; + } + + ret = cbc_encrypt(tptr, out.data(), primaryLength, &cbc); + if (ret != CRYPT_OK) + { + SysPushErrorCrypto("{}", ret); + return false; + } + + std::memcpy(pad, tptr + primaryLength, overhang); + pad[127] = padding; + + ret = cbc_encrypt(pad, out.data() + primaryLength, 128, &cbc); + if (ret != CRYPT_OK) + { + SysPushErrorCrypto("{}", ret); + return false; + } + } + + return true; + } + + + AUKN_SYM bool Encrypt(const void *plainText, AuUInt plainTextLength, + const void *iv, void *outIv, AuUInt ivLength, + const void *key, AuUInt keyLength, + AuList &out, + bool safe) + { + symmetric_CBC cbc; + + if (ivLength != 16) + { + SysPushErrorCrypt("{}", ivLength); + return false; + } + + if ((keyLength != 16) && + (keyLength != 24) && + (keyLength != 32)) + { + SysPushErrorCrypt("{}", keyLength); + return false; + } + + auto ret = cbc_start(::Crypto::gAesCipher, + reinterpret_cast(iv), + reinterpret_cast(key), + keyLength, + 0, + &cbc); + if (ret != CRYPT_OK) + { + SysPushErrorCrypt("{}", ret); + return false; + } + + auto tptr = reinterpret_cast(plainText); + + if (safe) + { + if (!EncryptCoolCodePadding(plainText, plainTextLength, iv, outIv, ivLength, key, keyLength, out, cbc)) + { + return false; + } + } + else + { + if ((plainTextLength & (16 - 1)) != 0) + { + SysPushErrorArg("{}", keyLength); + return false; + } + + ret = cbc_encrypt(tptr, out.data(), plainTextLength, &cbc); + if (ret != CRYPT_OK) + { + SysPushErrorCrypt("{}", ret); + return false; + } + } + + if (outIv) + { + unsigned long ivLength = 16; + ret = cbc_getiv(reinterpret_cast(outIv), &ivLength, &cbc); + + if (ret != CRYPT_OK) + { + SysPushErrorCrypt("{}", ret); + return false; + } + } + + return true; + } + + + AUKN_SYM bool Decrypt(const void *cipherText, AuUInt cipherTextLength, + const void *iv, void *outIv, AuUInt ivLength, + const void *key, AuUInt keyLength, + AuList &plainText, + bool safe) + { + + + symmetric_CBC cbc; + + if (ivLength != 16) + { + SysPushErrorArg("{}", ivLength); + return false; + } + + if ((keyLength != 16) && + (keyLength != 24) && + (keyLength != 32)) + { + SysPushErrorArg("{}", keyLength); + return false; + } + + auto ret = cbc_start(::Crypto::gAesCipher, + reinterpret_cast(iv), + reinterpret_cast(key), + keyLength, + 0, + &cbc); + if (ret != CRYPT_OK) + { + SysPushErrorCrypt("{}", ret); + return false; + } + + auto tptr = reinterpret_cast(cipherText); + + if (!TryResize(plainText, cipherTextLength)) + { + SysPushErrorMem(); + return false; + } + + ret = cbc_decrypt(tptr, plainText.data(), cipherTextLength, &cbc); + if (ret != CRYPT_OK) + { + SysPushErrorCrypt("{}", ret); + return false; + } + + if (safe) + { + auto magic = plainText[cipherTextLength - 1]; + if (magic <= 128) + { + plainText.resize(cipherTextLength - magic); + } + } + + if (outIv) + { + unsigned long ivLength = 16; + ret = cbc_getiv(reinterpret_cast(outIv), &ivLength, &cbc); + if (ret != CRYPT_OK) + { + + SysPushErrorCrypt("{}", ret); + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/Source/Crypto/AES/Aes.hpp b/Source/Crypto/AES/Aes.hpp new file mode 100644 index 00000000..ba6cdab0 --- /dev/null +++ b/Source/Crypto/AES/Aes.hpp @@ -0,0 +1,8 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Aes.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once diff --git a/Source/Crypto/CA/CA.cpp b/Source/Crypto/CA/CA.cpp new file mode 100644 index 00000000..b3c3d228 --- /dev/null +++ b/Source/Crypto/CA/CA.cpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CA.cpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Crypto/CA/CA.hpp b/Source/Crypto/CA/CA.hpp new file mode 100644 index 00000000..b74e833e --- /dev/null +++ b/Source/Crypto/CA/CA.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CA.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Crypto/Crypto.cpp b/Source/Crypto/Crypto.cpp new file mode 100644 index 00000000..68145517 --- /dev/null +++ b/Source/Crypto/Crypto.cpp @@ -0,0 +1,8 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Crypto.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include "Crypto.hpp" \ No newline at end of file diff --git a/Source/Crypto/Crypto.hpp b/Source/Crypto/Crypto.hpp new file mode 100644 index 00000000..1ec53445 --- /dev/null +++ b/Source/Crypto/Crypto.hpp @@ -0,0 +1,9 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Crypto.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + diff --git a/Source/Crypto/ECC25519/EccX25519Private.cpp b/Source/Crypto/ECC25519/EccX25519Private.cpp new file mode 100644 index 00000000..ddc0307f --- /dev/null +++ b/Source/Crypto/ECC25519/EccX25519Private.cpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EccX25519Private.cpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Crypto/ECC25519/EccX25519Private.hpp b/Source/Crypto/ECC25519/EccX25519Private.hpp new file mode 100644 index 00000000..d30fd959 --- /dev/null +++ b/Source/Crypto/ECC25519/EccX25519Private.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EccX25519Private.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Crypto/ECC25519/EccX25519Public.cpp b/Source/Crypto/ECC25519/EccX25519Public.cpp new file mode 100644 index 00000000..09e601ea --- /dev/null +++ b/Source/Crypto/ECC25519/EccX25519Public.cpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EccX25519Public.cpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Crypto/ECC25519/EccX25519Public.hpp b/Source/Crypto/ECC25519/EccX25519Public.hpp new file mode 100644 index 00000000..a06511ab --- /dev/null +++ b/Source/Crypto/ECC25519/EccX25519Public.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EccX25519Public.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Crypto/ECCNIST/EccNISTPrivate.cpp b/Source/Crypto/ECCNIST/EccNISTPrivate.cpp new file mode 100644 index 00000000..7029cf86 --- /dev/null +++ b/Source/Crypto/ECCNIST/EccNISTPrivate.cpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EccNISTPrivate.cpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Crypto/ECCNIST/EccNISTPrivate.hpp b/Source/Crypto/ECCNIST/EccNISTPrivate.hpp new file mode 100644 index 00000000..9cb3bb43 --- /dev/null +++ b/Source/Crypto/ECCNIST/EccNISTPrivate.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EccNISTPrivate.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Crypto/ECCNIST/EccNISTPublic.cpp b/Source/Crypto/ECCNIST/EccNISTPublic.cpp new file mode 100644 index 00000000..87913e98 --- /dev/null +++ b/Source/Crypto/ECCNIST/EccNISTPublic.cpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EccNISTPublic.cpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Crypto/ECCNIST/EccNISTPublic.hpp b/Source/Crypto/ECCNIST/EccNISTPublic.hpp new file mode 100644 index 00000000..da3cc4c1 --- /dev/null +++ b/Source/Crypto/ECCNIST/EccNISTPublic.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: EccNISTPublic.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Crypto/PEM/PEM.cpp b/Source/Crypto/PEM/PEM.cpp new file mode 100644 index 00000000..9a223f71 --- /dev/null +++ b/Source/Crypto/PEM/PEM.cpp @@ -0,0 +1,109 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: PEM.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "../Crypto.hpp" +#include "PEM.hpp" + +namespace Aurora::Crypto::PEM +{ + static bool ParsePEM(const AuString &begin, const AuString &end, const AuString &src, AuList &buf) + { + AuUInt lines = 0; + AuString str; + int fail = 0; + bool finished = 0; + + str.reserve(src.size()); + + Parse::SplitNewlines(src, + [&](const AuString &src) + { + if (lines == 0) + { + fail |= (int)(src != begin); + lines++; + return; + } + + if (src == end) + { + fail |= (int)!Parse::Base64Decode(str, buf); + finished = true; + return; + } + + str += src; + }); + + return fail ? false : finished; + } + + static AuString SerializePEM(const AuString &begin, const AuString &end, const AuList &buf) + { + AuString ret; + ret += begin; + ret += '\n'; + AuString b64; + if (!Parse::Base64Encode(buf, b64)) + return ""; + ret += b64; + ret += '\n'; + ret += end; + return ret; + } + + AUKN_SYM AuString ToString(const Aurora::Crypto::X509::Certificate &in) + { + return SerializePEM("-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----", in); + } + + AUKN_SYM AuString PrivateToString(const PrivateKey &in) + { + return SerializePEM("-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----", in); + } + + AUKN_SYM AuString PublicToString(const PublicKey &in) + { + return SerializePEM("-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----", in); + } + + AUKN_SYM AuString PublicRSAToString(const PrivateRSAKey &in) + { + return SerializePEM("-----BEGIN RSA PUBLIC KEY-----", "-----END RSA PUBLIC KEY-----", in); + } + + AUKN_SYM AuString PrivateRSAToString(const PublicRSAKey &in) + { + return SerializePEM("-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----", in); + } + + AUKN_SYM bool FromString(const AuString &in, Aurora::Crypto::X509::Certificate &out) + { + return ParsePEM("-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----", in, out); + } + + AUKN_SYM bool PrivateFromString(const AuString &in, PrivateKey &out) + { + return ParsePEM("-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----", in, out); + } + + AUKN_SYM bool PublicRSAFromString(const AuString &in, PrivateRSAKey &out) + { + return ParsePEM("-----BEGIN RSA PUBLIC KEY-----", "-----END RSA PUBLIC KEY-----", in, out); + } + + AUKN_SYM bool PrivateRSAFromString(const AuString &in, PublicRSAKey &out) + { + return ParsePEM("-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----", in, out); + } + + AUKN_SYM bool PublicFromString(const AuString &in, PublicKey &out) + { + return ParsePEM("-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----", in, out); + } +} \ No newline at end of file diff --git a/Source/Crypto/PEM/PEM.hpp b/Source/Crypto/PEM/PEM.hpp new file mode 100644 index 00000000..ae4c88fe --- /dev/null +++ b/Source/Crypto/PEM/PEM.hpp @@ -0,0 +1,9 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: PEM.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + diff --git a/Source/Crypto/RSA/RSAPrivate.cpp b/Source/Crypto/RSA/RSAPrivate.cpp new file mode 100644 index 00000000..3b51c9e3 --- /dev/null +++ b/Source/Crypto/RSA/RSAPrivate.cpp @@ -0,0 +1,10 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RSAPrivate.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "../Crypto.hpp" +#include "RSAPrivate.hpp" diff --git a/Source/Crypto/RSA/RSAPrivate.hpp b/Source/Crypto/RSA/RSAPrivate.hpp new file mode 100644 index 00000000..9df37cc7 --- /dev/null +++ b/Source/Crypto/RSA/RSAPrivate.hpp @@ -0,0 +1,8 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RSAPrivate.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once diff --git a/Source/Crypto/RSA/RSAPublic.cpp b/Source/Crypto/RSA/RSAPublic.cpp new file mode 100644 index 00000000..10366707 --- /dev/null +++ b/Source/Crypto/RSA/RSAPublic.cpp @@ -0,0 +1,10 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RSAPublic.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "../Crypto.hpp" +#include "RSAPublic.hpp" diff --git a/Source/Crypto/RSA/RSAPublic.hpp b/Source/Crypto/RSA/RSAPublic.hpp new file mode 100644 index 00000000..a2a62e2b --- /dev/null +++ b/Source/Crypto/RSA/RSAPublic.hpp @@ -0,0 +1,9 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RSAPublic.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + diff --git a/Source/Crypto/X509/x509.cpp b/Source/Crypto/X509/x509.cpp new file mode 100644 index 00000000..8b4c96ea --- /dev/null +++ b/Source/Crypto/X509/x509.cpp @@ -0,0 +1,478 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: x509.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "../Crypto.hpp" +#include "x509.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +#define AURORA_OID_ID_AD MBEDTLS_OID_PKIX "\x30" +#define AURORA_OID_ID_PE_ONE MBEDTLS_OID_PKIX "\x01" +#define AURORA_OID_ID_PE_ONE_AIA AURORA_OID_ID_PE_ONE "\x01" + +#define AURORA_OID_ID_AD_CA_ISSUERS AURORA_OID_ID_AD "\x02" +#define AURORA_OID_ID_AD_OCSP AURORA_OID_ID_AD "\x01" + +namespace Aurora::Crypto::X509 +{ +#pragma region functions copied from mbedtls, modified to do extract the asn fields we care about + static int x509_get_crt_ext(mbedtls_x509_crt *crt, const char *oid, int oidLength, std::function cb); + + static int x509_get_ca_id(mbedtls_x509_crt *crt, AuList &key) + { + bool ok = false; + return x509_get_crt_ext(crt, MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER, sizeof(MBEDTLS_OID_AUTHORITY_KEY_IDENTIFIER) - 1, [&](mbedtls_x509_buf &ex, unsigned char **p, unsigned char *end) + { + int ret = 0; + size_t len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) + { + ok = false; + return; + } + + auto tag = **p; + (*p)++; + if ((ret = mbedtls_asn1_get_len(p, end, &len)) != 0) + { + ok = false; + return; + } + + + if (!TryResize(key, len)) + { + ok = false; + return; + } + + memcpy(key.data(), *p, key.size()); + ok = true; + }) == 0 && ok; + } + + static int x509_get_subject_id(mbedtls_x509_crt *crt, AuList &key) + { + bool ok = false; + return x509_get_crt_ext(crt, MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER, sizeof(MBEDTLS_OID_SUBJECT_KEY_IDENTIFIER) - 1, [&](mbedtls_x509_buf &ex, unsigned char **p, unsigned char *end) + { + int ret = 0; + size_t len; + + + auto tag = **p; + (*p)++; + if ((ret = mbedtls_asn1_get_len(p, end, &len)) != 0) + { + ok = false; + return; + } + + if (!TryResize(key, len)) + { + ok = false; + return; + } + + memcpy(key.data(), *p, key.size()); + ok = true; + }) == 0 && ok; + } + + static int x509_get_aia(mbedtls_x509_crt *crt, AuList &ocsp, AuList &caIssers) + { + bool ok = false; + return x509_get_crt_ext(crt, AURORA_OID_ID_PE_ONE_AIA, sizeof(AURORA_OID_ID_PE_ONE_AIA) - 1, [&](mbedtls_x509_buf &ex, unsigned char **p, unsigned char *end) + { + int ret = 0; + size_t len, sublen; + + + /* + AuthorityInfoAccessSyntax ::= + SEQUENCE SIZE (1..MAX) OF AccessDescription + */ + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) + { + ok = false; + return; + } + + while (*p != end) + { + /* + AccessDescription ::= SEQUENCE { + accessMethod OBJECT IDENTIFIER, + accessLocation GeneralName } + */ + + auto endOfResource = *p + len; + if ((ret = mbedtls_asn1_get_tag(p, endOfResource, &sublen, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) + { + ok = false; + return; + } + + // access method id + mbedtls_x509_buf oid = { 0, 0, NULL }; + if ((ret = mbedtls_asn1_get_tag(p, *p + sublen, &oid.len, + MBEDTLS_ASN1_OID)) != 0) + { + ok = false; + return; + } + + oid.tag = MBEDTLS_ASN1_OID; + oid.p = *p; + *p += oid.len; + + // general name + mbedtls_x509_buf name = { 0, 0, NULL }; + + auto tag = **p; + (*p)++; + if ((ret = mbedtls_asn1_get_len(p, endOfResource, &name.len)) != 0) + { + ok = false; + return; + } + + if ((tag & MBEDTLS_ASN1_TAG_CLASS_MASK) != + MBEDTLS_ASN1_CONTEXT_SPECIFIC) + { + ok = false; + return; + } + + name.tag = tag; + name.p = *p; + + if (oid.len == sizeof(AURORA_OID_ID_AD_OCSP) - 1) + { + if (memcmp(oid.p, AURORA_OID_ID_AD_OCSP, oid.len) == 0) + { + ocsp.push_back(AuString(reinterpret_cast(name.p), reinterpret_cast(name.p) + name.len)); + } + } + + if (oid.len == sizeof(AURORA_OID_ID_AD_CA_ISSUERS) - 1) + { + if (memcmp(oid.p, AURORA_OID_ID_AD_CA_ISSUERS, oid.len) == 0) + { + caIssers.push_back(AuString(reinterpret_cast(name.p), reinterpret_cast(name.p) + name.len)); + } + } + + *p += name.len; + } + }) == 0 && ok; + } + + static int x509_get_crt_ext(mbedtls_x509_crt *crt, const char *oid, int oidLength, std::function cb) + { + int ret = 0; + size_t len; + unsigned char *end_ext_data, *start_ext_octet, *end_ext_octet; + + + unsigned char *scre = crt->v3_ext.p; + unsigned char **p = &scre; + auto end = crt->v3_ext.p + crt->v3_ext.len; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) + return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret); + + if (end != *p + len) + return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + + while (*p < end) + { + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + mbedtls_x509_buf extn_oid = { 0, 0, NULL }; + int is_critical = 0; /* DEFAULT FALSE */ + int ext_type = 0; + + if ((ret = mbedtls_asn1_get_tag(p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) + return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret); + + end_ext_data = *p + len; + + /* Get extension ID */ + if ((ret = mbedtls_asn1_get_tag(p, end_ext_data, &extn_oid.len, + MBEDTLS_ASN1_OID)) != 0) + return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret); + + extn_oid.tag = MBEDTLS_ASN1_OID; + extn_oid.p = *p; + *p += extn_oid.len; + + /* Get optional critical */ + if ((ret = mbedtls_asn1_get_bool(p, end_ext_data, &is_critical)) != 0 && + (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) + return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret); + + /* Data should be octet string type */ + if ((ret = mbedtls_asn1_get_tag(p, end_ext_data, &len, + MBEDTLS_ASN1_OCTET_STRING)) != 0) + return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret); + + start_ext_octet = *p; + end_ext_octet = *p + len; + + if (end_ext_octet != end_ext_data) + return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + + + if (extn_oid.len != oidLength) + { + *p = end_ext_octet; + continue; + } + + if (memcmp(extn_oid.p, oid, oidLength)) + { + *p = end_ext_octet; + continue; + } + + cb(extn_oid, p, end_ext_octet); + return 0; + } + + if (*p != end) + return(MBEDTLS_ERR_X509_INVALID_EXTENSIONS + + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH); + + return MBEDTLS_ERR_X509_INVALID_EXTENSIONS; + } + + + template + static bool find_oid_value_in_name(const mbedtls_x509_name *name, const char(&oid)[C], AuString &value) + { + const char *short_name = NULL; + size_t retval = 0; + + while (name != NULL) + { + if (!name->oid.p) + { + name = name->next; + continue; + } + + if (name->oid.len != (C - 1)) + { + name = name->next; + continue; + } + + if (memcmp(oid, name->oid.p, (C - 1))) + { + name = name->next; + continue; + } + + value = AuString(reinterpret_cast(name->val.p), reinterpret_cast(name->val.p) + name->val.len); + return true; + } + + return false; + } +#pragma endregion + + static void FindCommonNames(const mbedtls_x509_name &name, CertName &out) + { + find_oid_value_in_name(&name, MBEDTLS_OID_AT_CN, out.commonName); + find_oid_value_in_name(&name, MBEDTLS_OID_AT_COUNTRY, out.countryCode); + find_oid_value_in_name(&name, MBEDTLS_OID_AT_ORGANIZATION, out.organization); + find_oid_value_in_name(&name, MBEDTLS_OID_AT_ORG_UNIT, out.department); + find_oid_value_in_name(&name, MBEDTLS_OID_AT_STATE, out.state); + find_oid_value_in_name(&name, MBEDTLS_OID_PKCS9_EMAIL, out.email); + find_oid_value_in_name(&name, MBEDTLS_OID_AT_TITLE, out.title); + find_oid_value_in_name(&name, MBEDTLS_OID_AT_GIVEN_NAME, out.name); + } + + static bool ParseCert(const Certificate &der, std::function cb) + { + bool ret = false; + mbedtls_x509_crt crt {}; + + mbedtls_x509_crt_init(&crt); + + auto status = mbedtls_x509_crt_parse(&crt, + reinterpret_cast(der.data()), + der.size()); + + if (status < 0) goto out; + + cb(crt); + + ret = true; + out: + mbedtls_x509_crt_free(&crt); + return ret; + } + + static AuInt64 ConvertTime(const mbedtls_x509_time &time) + { + std::tm tm = {}; + tm.tm_year = time.year - 1900; + tm.tm_mon = time.mon - 1; + tm.tm_mday = time.day; + tm.tm_sec = time.sec; + tm.tm_min = time.min; + tm.tm_hour = time.hour; + return Aurora::Time::FromCivilTime(tm, true); + } + + AUKN_SYM bool Decode(const Certificate &der, DecodedCertificate &out) + { + bool failed = false; + + return ParseCert(der, + [&](mbedtls_x509_crt &crt) + { + auto &issuer = crt.issuer; + auto &subject = crt.subject; + + FindCommonNames(issuer, out.issuer); + FindCommonNames(subject, out.subject); + + out.validity.issued = ConvertTime(crt.valid_from); + out.validity.expire = ConvertTime(crt.valid_to); + + x509_get_ca_id(&crt, out.issuer.id); + x509_get_subject_id(&crt, out.subject.id); + + out.serialNumber.resize(crt.serial.len); + memcpy(out.serialNumber.data(), crt.serial.p, out.serialNumber.size()); + + out.algorithmOid.resize(crt.sig_oid.len); + memcpy(out.algorithmOid.data(), crt.sig_oid.p, out.algorithmOid.size()); + + AuList oscp; + x509_get_aia(&crt, oscp, out.AIAs); + }) && !failed; + + } + + // inb4 you're racist. dont care virgin + static bool IsHighRiskStateIssuer(const mbedtls_x509_crt &ca) + { + AuString issuer; + + if (!find_oid_value_in_name(&ca.issuer, MBEDTLS_OID_AT_COUNTRY, issuer)) + { + // FAIL! + return true; + } + + if (issuer.empty()) + { + // FAIL! + return true; + } + + // Winnie the Pooh has been caught with his paws in the honey jar one too many times + // At least other nation state attacks have been covert enough for us to not know about any *TLS* MITM attacks originating from them + // Pro-china cuckolds at mozilla suggested we merely block certs issued issued by compromised cn CAs using their issue date. lol no + // https://arstechnica.com/information-technology/2015/03/google-warns-of-unauthorized-tls-certificates-trusted-by-almost-all-oses/ + // https://www.zdnet.com/article/china-is-now-blocking-all-encrypted-https-traffic-using-tls-1-3-and-esni/ + // https://www.popularmechanics.com/military/news/a28510/china-secret-plan-invade-taiwan/ + // https://www.theguardian.com/world/2021/mar/10/china-could-invade-taiwan-in-next-six-years-top-us-admiral-warns + if ((!gRuntimeConfig.crypto.allowChineseCerts) && + (stricmp(issuer.c_str(), "cn") == 0) || // mainland and countries the communists decided are theirs + (stricmp(issuer.c_str(), "mo") == 0) || // macau, china special admin region + (stricmp(issuer.c_str(), "hk") == 0) || // hong kong + /*(stricmp(issuer.c_str(), "tw") == 0)*/ false) // tehe yes we will be ready to invade by 2020. i suppose taiwan is safe enough for.now + { + SysPushErrorCrypt("The funny western yellow bear in the east is behind you"); + return true; + } + + // The intersection between technical threats and countries we can't do business with legally + // https://en.wikipedia.org/wiki/United_States_sanctions + // https://en.wikipedia.org/wiki/Communications_in_Iran + // https://www.gov.uk/government/collections/financial-sanctions-regime-specific-consolidated-lists-and-releases + // https://en.wikipedia.org/wiki/Lazarus_Group + // https://www.bbc.co.uk/news/stories-57520169 + if ((stricmp(issuer.c_str(), "ir") == 0) || // iran - has own intranet. IXPs are owned by the state + (stricmp(issuer.c_str(), "iq") == 0) || // iraq + (stricmp(issuer.c_str(), "kp") == 0)) // north korea - has own intranet. IXPs are owned by the state + { + SysPushErrorCrypt("Service is unavailable in your country for legal and/or technical safety concerns"); + return true; + } + + // Russia is somewhat misunderstood and have their hands tied in a lot of cases, not to excuse blatent corruption + // Moscow wants MSK-IX IXP to be independent and have ran tests to validate their intranet could work without the rest of the world + // I wonder if Pavel Durov would trust russian certificates, probably not, right? Major states, especially Russia, shouldn't be trusted with foreign services + // allowRussianCerts is true by default; however, services targeting a market of close political enemies of russia should consider disallowing russian certs + // Further, private instrastructure around Russian territories should consider a heightened global pinning security policy + if ((!gRuntimeConfig.crypto.allowRussianCerts) && + (stricmp(issuer.c_str(), "ru") == 0)) + { + SysPushErrorCrypt("Service is unavailable in your country for technical safety concerns"); + return true; + } + + // The 5 eyes are smart enough to know MITM attacks would send alarms ringing at major services providers + // They are known to MITM plain text traffic and hoard vulns; however, we can design around these issues + // Do not use NIST curves, do not use plain text across public IXPs, don't save data in plain text in EU/US datacenters, etc + // There is nothing we can do about western powers. They seem to be playing... not fair... but smart + { + // obama bin listening + } + + return false; + } + + AUKN_SYM bool Validate(const Certificate &der, const Certificate &parentDer) + { + bool failed = false; + + // gross + return ParseCert(der, + [&](mbedtls_x509_crt &crt) + { + ParseCert(parentDer, + [&](mbedtls_x509_crt &ca) + { + + if (failed = IsHighRiskStateIssuer(ca)) + { + return; + } + + failed = 0 > mbedtls_x509_crt_verify_restartable(&crt, &ca, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + }); + }) && !failed; + } +} \ No newline at end of file diff --git a/Source/Crypto/X509/x509.hpp b/Source/Crypto/X509/x509.hpp new file mode 100644 index 00000000..a523854b --- /dev/null +++ b/Source/Crypto/X509/x509.hpp @@ -0,0 +1,9 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: x509.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + diff --git a/Source/Debug/Debug.cpp b/Source/Debug/Debug.cpp new file mode 100644 index 00000000..f899b692 --- /dev/null +++ b/Source/Debug/Debug.cpp @@ -0,0 +1,289 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Debug.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "Debug.hpp" +#include + +#if defined(AURORA_PLATFORM_WIN32) +#include "ExceptionWatcher.Win32.hpp" +#endif + +namespace Aurora::Debug +{ + static AuUInt32 gFenceOSError = -1; + AuUInt32 GetOSErrorFence() + { + return gFenceOSError; + } + +#if defined(AURORA_PLATFORM_WIN32) + + AuString GetOSErrorStringWin32(DWORD error) + { + AuString ret; + + char *err = nullptr; + if (!FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&err, + 0, + NULL)) + { + return nullptr; + } + + ret = err; + LocalFree(err); + + return ret; + } + + std::optional TryFetchOSError() + { + static OSError_t lastError{}; + + OSError_t ret{}; + + ret.first = GetLastError(); + if ((ret.first == ERROR_SUCCESS) || + (ret.first == WSAEWOULDBLOCK)) + { + return {}; + } + + if (lastError.first == ret.first) + { + return lastError; + } + + ret.second = GetOSErrorStringWin32(lastError.first); + + gFenceOSError++; + return lastError = ret; + } + + + std::optional TryGetOrFetchOSError() + { + static std::optional lastErrorError; + static std::optional lastErrorFence; + + std::optional tempError = TryFetchOSError(); + + if (!tempError) + { + return lastErrorError; + } + + if (lastErrorFence) + { + if (lastErrorFence.value() != GetOSErrorFence()) + { + SetLastError(ERROR_SUCCESS); + Telemetry::InsertOSError(tempError.value()); + } + } + + lastErrorFence = GetOSErrorFence(); + lastErrorError = tempError; + return tempError; + } + +#else + + std::optional TryFetchOSError() + { + return {}; + } + + std::optional TryGetOrFetchOSError() + { + return {}; + } + +#endif + + static AuUInt32 gFenceCError = -1; + AuUInt32 GetCErrorFence() + { + return gFenceCError; + } + + std::optional TryFetchCError() + { + static int lastError = 0; + + auto errorNumber = errno; + if (!errorNumber) + { + return {}; + } + + if (lastError == errorNumber) + { + return errorNumber; + } + + gFenceCError++; + return lastError = errorNumber; + } + + std::optional TryGetOrFetchCError() + { + static std::optional lastErrorError; + static std::optional lastErrorFence; + + std::optional tempError = TryFetchCError(); + + if (!tempError) + { + return lastErrorError; + } + + if (lastErrorFence) + { + if (lastErrorFence.value() != GetCErrorFence()) + { + errno = 0; + Telemetry::InsertCError(tempError.value()); + } + } + + lastErrorFence = GetCErrorFence(); + lastErrorError = tempError; + return tempError; + } + + AuUInt32 GetFenceId() + { + return GetCErrorFence() << 8 ^ GetOSErrorFence(); // preserve lowest 8 bits, or in new additional bits, overlapping bits can get fucked + } + + AUKN_SYM void PrintError() + { + AuUInt32 rng = GetFenceId(); + + Telemetry::InsertManualFence(rng); + + static AuUInt32 cLastFence = 0; + auto cFence = GetCErrorFence(); + auto cError = TryGetOrFetchCError(); + if ((cError) && (cFence != cLastFence)) + { + LogWarn("Language Error: {} ({})", strerror(*cError), *cError); + cLastFence = cFence; + } + + static AuUInt32 osLastFence = 0; + auto osFence = GetOSErrorFence(); + auto osError = TryGetOrFetchOSError(); + if ((osError) && (osFence != osLastFence)) + { + LogWarn("Operating System Error: {} (0x{:x})", osError->second, osError->first); + osLastFence = osFence; + } + + Telemetry::InsertManualFence(rng); + } + + void CheckErrors() + { + AuUInt32 rng = GetFenceId(); + Telemetry::InsertManualFence(rng); + TryGetOrFetchCError(); + TryGetOrFetchOSError(); + Telemetry::InsertManualFence(rng); + } + + AUKN_SYM void _PushError(AuUInt address, FailureCategory category, const char *msg) + { + LastError error{ address, category, msg }; + + AuUInt32 rng = GetFenceId(); + Telemetry::InsertManualFence(rng); + Telemetry::InsertMsgError(error); + TryGetOrFetchCError(); + TryGetOrFetchOSError(); + Telemetry::InsertManualFence(rng); + } + + AUKN_SYM AuString StackTraceEntry::Stringify() const + { + const auto frame = *this; + AuString backTraceBuffer; + + backTraceBuffer.reserve(512 - 32); // 512 seems like a nice length minus some overhead for a bucket allocator + + backTraceBuffer += fmt::format("\tAddress: {:x}", frame.address); + + if (frame.module) + { + auto modName = frame.module.value(); + if (modName.size()) + { + backTraceBuffer += fmt::format(" within {}", modName); + } + else + { + backTraceBuffer += ", invalid module"; + } + } + else + { + backTraceBuffer += ", unknown module"; + } + + if (frame.label) + { + backTraceBuffer += fmt::format(" ({}) \n", frame.label.value()); + } + else + { + backTraceBuffer += ", unknown function\n"; + } + + if (frame.file) + { + const auto &re = frame.file.value(); + backTraceBuffer += fmt::format("\t\t{}:{} ({}) \n", std::get<0>(re), std::get<1>(re), std::get<2>(re)); + } + else + { + backTraceBuffer += "\t\t[proprietary]\n"; + } + + return backTraceBuffer; + } + + AUKN_SYM AuString StringifyStackTrace(const StackTrace &backtrace) + { + AuString backTraceBuffer; + + backTraceBuffer.reserve(2048); + + backTraceBuffer += "Unwinding call frame:"; + + for (const auto &frame : backtrace) + { + backTraceBuffer += "\n"; + backTraceBuffer += frame.Stringify(); + } + + return backTraceBuffer; + } + + + void InitDebug() + { + #if defined(AURORA_PLATFORM_WIN32) + InitWin32(); + #endif + } +} \ No newline at end of file diff --git a/Source/Debug/Debug.hpp b/Source/Debug/Debug.hpp new file mode 100644 index 00000000..4bf066f7 --- /dev/null +++ b/Source/Debug/Debug.hpp @@ -0,0 +1,36 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Debug.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + +namespace Aurora::Debug +{ + struct LastError + { + AuUInt address; + FailureCategory category; + AuString dbg; + }; + + using OSError_t = std::pair; + + AuUInt32 GetOSErrorFence(); + std::optional TryGetOrFetchOSError(); + std::optional TryFetchOSError(); + + AuUInt32 GetCErrorFence(); + std::optional TryFetchCError(); + std::optional TryGetOrFetchCError(); + + AuUInt32 GetStackTraceFence(); + std::optional TryFetchStackTrace(); + std::optional TryGetOrFetchStackTrace(); + + void CheckErrors(); + + void InitDebug(); +} diff --git a/Source/Debug/ExceptionWatcher.Linux.cpp b/Source/Debug/ExceptionWatcher.Linux.cpp new file mode 100644 index 00000000..54a85993 --- /dev/null +++ b/Source/Debug/ExceptionWatcher.Linux.cpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ExceptionWatcher.Linux.cpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Debug/ExceptionWatcher.Linux.hpp b/Source/Debug/ExceptionWatcher.Linux.hpp new file mode 100644 index 00000000..575a55ed --- /dev/null +++ b/Source/Debug/ExceptionWatcher.Linux.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ExceptionWatcher.Linux.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Debug/ExceptionWatcher.Win32.cpp b/Source/Debug/ExceptionWatcher.Win32.cpp new file mode 100644 index 00000000..04392729 --- /dev/null +++ b/Source/Debug/ExceptionWatcher.Win32.cpp @@ -0,0 +1,327 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ExceptionWatcher.Win32.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "Debug.hpp" +#include "ExceptionWatcher.Win32.hpp" + +#include +#include + +#include +#include +#include + +#pragma comment(lib,"Dbghelp.lib") + +#include + +static thread_local int gDebugLocked = 0; + +namespace Aurora::Debug +{ + static void ParseStack(CONTEXT *ctx, StackTrace &backTrace) + { + char buffer[sizeof(SYMBOL_INFO) + (MAX_SYM_NAME + 1) * sizeof(char)] = { 0 }; + AuString backTraceBuffer; + HMODULE hModule; + DWORD disp; + DWORD64 displacement = 0; + HANDLE process = GetCurrentProcess(); + STACKFRAME64 stack = { 0 }; + CONTEXT cpy = *ctx; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + + #if defined(AURORA_ARCH_X64) + stack.AddrPC.Offset = ctx->Rip; + stack.AddrPC.Mode = AddrModeFlat; + stack.AddrStack.Offset = ctx->Rsp; + stack.AddrStack.Mode = AddrModeFlat; + stack.AddrFrame.Offset = ctx->Rbp; + stack.AddrFrame.Mode = AddrModeFlat; + #elif defined(AURORA_ARCH_XX86) + stack.AddrPC.Offset = ctx->Eip; + stack.AddrPC.Mode = AddrModeFlat; + stack.AddrStack.Offset = ctx->Esp; + stack.AddrStack.Mode = AddrModeFlat; + stack.AddrFrame.Offset = ctx->Ebp; + stack.AddrFrame.Mode = AddrModeFlat; + #endif + + for (ULONG frame = 0; ; frame++) + { + StackTraceEntry frameCurrent; + + auto result = StackWalk64 + ( + #if defined(AURORA_ARCH_X64) + IMAGE_FILE_MACHINE_AMD64, + #else + IMAGE_FILE_MACHINE_I386, + #endif + INVALID_HANDLE_VALUE, + INVALID_HANDLE_VALUE, + &stack, + &cpy, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL + ); + + if (!result) + { + break; + } + + pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); + pSymbol->MaxNameLen = MAX_SYM_NAME; + + frameCurrent.address = stack.AddrPC.Offset; + + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)(stack.AddrPC.Offset), &hModule)) + { + if (hModule != NULL) + { + frameCurrent.module = Process::ModuleToPath(hModule); + } + } + + if (SymFromAddr(process, (ULONG64)stack.AddrPC.Offset, &displacement, pSymbol)) + { + frameCurrent.label = pSymbol->Name; + } + + #if defined(DEBUG) + IMAGEHLP_LINE64 line; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + if (SymGetLineFromAddr64(process, stack.AddrPC.Offset, &disp, &line)) + { + frameCurrent.file = std::make_tuple(line.FileName, line.LineNumber, 0); + } + #endif + + backTrace.push_back(frameCurrent); + } + } + + #define EXCEPTION_ENTRY(n) {n, #n} + static const AuHashMap kExceptionTable + { + EXCEPTION_ENTRY(STILL_ACTIVE), + EXCEPTION_ENTRY(EXCEPTION_ACCESS_VIOLATION), + EXCEPTION_ENTRY(EXCEPTION_DATATYPE_MISALIGNMENT), + EXCEPTION_ENTRY(EXCEPTION_BREAKPOINT), + EXCEPTION_ENTRY(EXCEPTION_SINGLE_STEP), + EXCEPTION_ENTRY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), + EXCEPTION_ENTRY(EXCEPTION_FLT_DENORMAL_OPERAND), + EXCEPTION_ENTRY(EXCEPTION_FLT_DIVIDE_BY_ZERO), + EXCEPTION_ENTRY(EXCEPTION_FLT_INEXACT_RESULT), + EXCEPTION_ENTRY(EXCEPTION_FLT_INVALID_OPERATION), + EXCEPTION_ENTRY(EXCEPTION_FLT_OVERFLOW), + EXCEPTION_ENTRY(EXCEPTION_FLT_STACK_CHECK), + EXCEPTION_ENTRY(EXCEPTION_FLT_UNDERFLOW), + EXCEPTION_ENTRY(EXCEPTION_INT_DIVIDE_BY_ZERO), + EXCEPTION_ENTRY(EXCEPTION_INT_OVERFLOW), + EXCEPTION_ENTRY(EXCEPTION_PRIV_INSTRUCTION), + EXCEPTION_ENTRY(EXCEPTION_IN_PAGE_ERROR), + EXCEPTION_ENTRY(EXCEPTION_ILLEGAL_INSTRUCTION), + EXCEPTION_ENTRY(EXCEPTION_NONCONTINUABLE_EXCEPTION), + EXCEPTION_ENTRY(EXCEPTION_STACK_OVERFLOW), + EXCEPTION_ENTRY(EXCEPTION_INVALID_DISPOSITION), + EXCEPTION_ENTRY(EXCEPTION_GUARD_PAGE), + EXCEPTION_ENTRY(EXCEPTION_INVALID_HANDLE), + //EXCEPTION_ENTRY(EXCEPTION_POSSIBLE_DEADLOCK), + EXCEPTION_ENTRY(CONTROL_C_EXIT) + }; +#undef EXCEPTION_ENTRY +#define EXCEPTION_ENTRY(n) {n, true} + static const AuHashMap kExceptionFatalTable + { + EXCEPTION_ENTRY(EXCEPTION_ACCESS_VIOLATION), + EXCEPTION_ENTRY(EXCEPTION_DATATYPE_MISALIGNMENT), + EXCEPTION_ENTRY(EXCEPTION_ARRAY_BOUNDS_EXCEEDED), + EXCEPTION_ENTRY(EXCEPTION_FLT_DENORMAL_OPERAND), + EXCEPTION_ENTRY(EXCEPTION_FLT_DIVIDE_BY_ZERO), + EXCEPTION_ENTRY(EXCEPTION_FLT_INEXACT_RESULT), + EXCEPTION_ENTRY(EXCEPTION_FLT_INVALID_OPERATION), + EXCEPTION_ENTRY(EXCEPTION_FLT_OVERFLOW), + EXCEPTION_ENTRY(EXCEPTION_FLT_STACK_CHECK), + EXCEPTION_ENTRY(EXCEPTION_FLT_UNDERFLOW), + EXCEPTION_ENTRY(EXCEPTION_INT_DIVIDE_BY_ZERO), + EXCEPTION_ENTRY(EXCEPTION_INT_OVERFLOW), + EXCEPTION_ENTRY(EXCEPTION_PRIV_INSTRUCTION), + EXCEPTION_ENTRY(EXCEPTION_IN_PAGE_ERROR), + EXCEPTION_ENTRY(EXCEPTION_ILLEGAL_INSTRUCTION), + EXCEPTION_ENTRY(EXCEPTION_NONCONTINUABLE_EXCEPTION), + EXCEPTION_ENTRY(EXCEPTION_STACK_OVERFLOW), + EXCEPTION_ENTRY(EXCEPTION_INVALID_DISPOSITION), + EXCEPTION_ENTRY(EXCEPTION_GUARD_PAGE) + }; +#undef EXCEPTION_ENTRY + + static bool IsReadable(const void *address) + { + MEMORY_BASIC_INFORMATION info; + + if (!VirtualQuery(address, &info, sizeof(info))) + { + return false; + } + + if (!info.BaseAddress) + { + return false; + } + + return (info.Protect & (PAGE_READONLY | PAGE_READWRITE)) != 0; + } + + void InitWin32() + { + #if defined(DEBUG) || defined(INTERNAL) + SymInitialize(GetCurrentProcess(), NULL, TRUE); + #endif + + AddVectoredExceptionHandler(1, + [](_EXCEPTION_POINTERS *ExceptionInfo) -> LONG + { + Telemetry::NewBlockboxEntry entry; + entry.type = Telemetry::ENewBlackBoxEntry::eWinCxxException; + std::exception *exception = nullptr; + + if (ExceptionInfo->ExceptionRecord->ExceptionCode < STATUS_GUARD_PAGE_VIOLATION) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) + { + return EXCEPTION_CONTINUE_SEARCH; + } + + auto minimal = gDebugLocked++; + + if (minimal >= 3) + { + SysPanic("Nested Exception"); + } + + bool cxxThrow = ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == EH_PURE_MAGIC_NUMBER1; + bool cxxThrowPure = ExceptionInfo->ExceptionRecord->ExceptionInformation[0] == EH_MAGIC_NUMBER1; + + if ((ExceptionInfo->ExceptionRecord->ExceptionCode == EH_EXCEPTION_NUMBER) && + (ExceptionInfo->ExceptionRecord->NumberParameters >= 4) && + (cxxThrow) || + (cxxThrowPure) + ) + { + HMODULE handle {}; + auto *throwInfo = reinterpret_cast(ExceptionInfo->ExceptionRecord->ExceptionInformation[2]); + + if (throwInfo) + { + auto attribs = throwInfo->attributes; + + if (_EH_RELATIVE_TYPEINFO) + { + handle = reinterpret_cast(ExceptionInfo->ExceptionRecord->ExceptionInformation[3]); + } + + #if defined(_EH_RELATIVE_TYPEINFO) + const auto catchableTypeArray = reinterpret_cast(static_cast(throwInfo->pCatchableTypeArray) + reinterpret_cast(handle)); + const auto type = reinterpret_cast(static_cast(catchableTypeArray->arrayOfCatchableTypes[0]) + reinterpret_cast(handle)); + const auto descriptor = reinterpret_cast(static_cast(type->pType) + reinterpret_cast(handle)); + #else + const auto catchableTypeArray = reinterpret_cast(static_cast(throwInfo->pCatchableTypeArray)); + const auto type = reinterpret_cast(static_cast(catchableTypeArray->arrayOfCatchableTypes[0])); + const auto descriptor = reinterpret_cast(static_cast(type->pType)); + #endif + + if (strncmp(descriptor->name, ".?AVException@", ArraySize(".?AVException@") - 1) == 0) + { + exception = reinterpret_cast(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]); + if (IsReadable(exception)) + { + entry.wincxx.str = exception->what(); + } + } + else if (((type->properties & CT_IsSimpleType) != 0) && + ((type->properties & CT_IsWinRTHandle) == 0)) + { + // assert descriptor->name == ".PEAD"? `DEAP.`? + auto possibleStringPointer = reinterpret_cast(ExceptionInfo->ExceptionRecord->ExceptionInformation[1]); + if (IsReadable(possibleStringPointer)) + { + auto string = *possibleStringPointer; + if (IsReadable(string)) + { + entry.wincxx.str = string; + } + } + } + } + } + + bool isCritical = TryFind(kExceptionFatalTable, ExceptionInfo->ExceptionRecord->ExceptionCode); + if (entry.wincxx.str.empty()) + { + const AuString *msg; + if (TryFind(kExceptionTable, ExceptionInfo->ExceptionRecord->ExceptionCode, msg)) + { + entry.wincxx.str = *msg; + } + else + { + entry.wincxx.str = std::to_string(ExceptionInfo->ExceptionRecord->ExceptionCode); + } + } + + try + { + if (minimal < 3) + { + ParseStack(ExceptionInfo->ContextRecord, entry.wincxx.stack.backtrace); + } + } + catch (...) + { + + } + + #if defined(INTERNAL) || defined(DEBUG) + bool isInternal = true; + #else + bool isInternal = false; + #endif + + if ((isCritical || isInternal) && (minimal == 0)) + { + LogWarn("NT Exception: 0x{:x}, {}", ExceptionInfo->ExceptionRecord->ExceptionCode, entry.wincxx.str); + LogWarn("{}", StringifyStackTrace(entry.wincxx.stack.backtrace)); + } + + try + { + Telemetry::Report(entry); + + if (isCritical) + { + Telemetry::Mayday(); + } + } + catch (...) + { + + } + + gDebugLocked = 0; + return EXCEPTION_CONTINUE_SEARCH; + }); + } +} \ No newline at end of file diff --git a/Source/Debug/ExceptionWatcher.Win32.hpp b/Source/Debug/ExceptionWatcher.Win32.hpp new file mode 100644 index 00000000..6e1903ff --- /dev/null +++ b/Source/Debug/ExceptionWatcher.Win32.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: ExceptionWatcher.Win32.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + +namespace Aurora::Debug +{ + void InitWin32(); +} \ No newline at end of file diff --git a/Source/Debug/Panic.cpp b/Source/Debug/Panic.cpp new file mode 100644 index 00000000..7ade6826 --- /dev/null +++ b/Source/Debug/Panic.cpp @@ -0,0 +1,66 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Panic.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "Debug.hpp" +#include "Panic.hpp" + +#include + +namespace Aurora::Debug +{ + AUKN_SYM void Panic() + { + try + { + CheckErrors(); + } + catch (...) + { + + } + + try + { + Console::ConsoleLogger::Flush(); + } + catch (...) + { + + } + + static bool panicSingleshot = false; + if (std::exchange(panicSingleshot, true)) + { + try + { + Telemetry::Mayday(); + } + catch (...) + { + + } + } + + // + static bool handlingFatal = false; + if (std::exchange(handlingFatal, true)) + { + std::terminate(); + } + + #if defined(AURORA_PLATFORM_WIN32) + // there is a syscall you can use to terminate in instances like this + // anticheats and drm love it + // i just dont remember what it is + // amendment: i believe its some software interrupt that raises an unhandleable exception + TerminateProcess(GetCurrentProcess(), 0xDEAD); + #else + std::exit(0xDEAD); + #endif + } +} \ No newline at end of file diff --git a/Source/Debug/Panic.hpp b/Source/Debug/Panic.hpp new file mode 100644 index 00000000..982a7f73 --- /dev/null +++ b/Source/Debug/Panic.hpp @@ -0,0 +1,9 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Panic.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + diff --git a/Source/Entrypoint.cpp b/Source/Entrypoint.cpp new file mode 100644 index 00000000..2b741af3 --- /dev/null +++ b/Source/Entrypoint.cpp @@ -0,0 +1,78 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Entrypoint.cpp + Date: 2021-5-13 + Author: Reece +***/ +#include +#include +#include "RuntimeInternal.hpp" +#include "Crypto.hpp" +#include "Processes/Processes.hpp" +#include "RNG/RNG.hpp" +#include "Locale/Locale.hpp" +#include "Console/Console.hpp" +#include "IO/FS/FS.hpp" +#include "Hashing/Hashing.hpp" +#include "Debug/Debug.hpp" + +static void Init() +{ + Crypto::InitCrypto(); + Aurora::Console::Init(); + Aurora::IO::FS::InitResources(); + Aurora::Console::Init2(); + Aurora::Debug::InitDebug(); + Aurora::Locale::Init(); + Aurora::Processes::Init(); + Aurora::RNG::Init(); + Aurora::Hashing::InitHashing(); +} + +static void Pump() +{ + Aurora::Console::Pump(); +} + +static void Deinit() +{ + Aurora::RNG::Release(); + Aurora::Console::Exit(); +} + +namespace Aurora +{ + AUKN_SYM void RuntimeStart(const RuntimeStartInfo &info) + { + gRuntimeConfig = info; + Init(); + } + + AUKN_SYM void RuntimeShutdown() + { + Deinit(); + } + + AUKN_SYM void RuntimeSysPump() + { + Pump(); + } +} + +#if defined(AURORA_PLATFORM_WIN32) + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpReserved) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hinstDLL); + } + + return TRUE; +} + +#endif \ No newline at end of file diff --git a/Source/Extensions/CURL/LibCurl.cpp b/Source/Extensions/CURL/LibCurl.cpp new file mode 100644 index 00000000..4c0929fb --- /dev/null +++ b/Source/Extensions/CURL/LibCurl.cpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LibCurl.cpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Extensions/CURL/LibCurl.hpp b/Source/Extensions/CURL/LibCurl.hpp new file mode 100644 index 00000000..c0f5f25f --- /dev/null +++ b/Source/Extensions/CURL/LibCurl.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LibCurl.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Extensions/LTC/LTC.cpp b/Source/Extensions/LTC/LTC.cpp new file mode 100644 index 00000000..53573e99 --- /dev/null +++ b/Source/Extensions/LTC/LTC.cpp @@ -0,0 +1,18 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LTC.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "LTC.hpp" + +extern "C" +{ + AUKN_SYM unsigned long rng_get_bytes(unsigned char *out, unsigned long outlen, void (*callback)(void)) + { + Aurora::RNG::ReadSecureRNG(out, outlen); + return outlen; + } +} \ No newline at end of file diff --git a/Source/Extensions/LTC/LTC.hpp b/Source/Extensions/LTC/LTC.hpp new file mode 100644 index 00000000..938334b6 --- /dev/null +++ b/Source/Extensions/LTC/LTC.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LTC.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Extensions/LTC/LTCEccEx.c b/Source/Extensions/LTC/LTCEccEx.c new file mode 100644 index 00000000..1fc36bf3 --- /dev/null +++ b/Source/Extensions/LTC/LTCEccEx.c @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LTCEccEx.c + Date: 2021-6-24 + Author: Reece +***/ diff --git a/Source/Extensions/LTC/LTCEccEx.h b/Source/Extensions/LTC/LTCEccEx.h new file mode 100644 index 00000000..03217e7f --- /dev/null +++ b/Source/Extensions/LTC/LTCEccEx.h @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LTCEccEx.h + Date: 2021-6-24 + Author: Reece +***/ diff --git a/Source/Extensions/LTC/LTCExport.c b/Source/Extensions/LTC/LTCExport.c new file mode 100644 index 00000000..00874284 --- /dev/null +++ b/Source/Extensions/LTC/LTCExport.c @@ -0,0 +1,143 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LTCExport.c + Date: 2021-6-24 + Author: Reece +***/ +#include +#include "tomcrypt_private.h" +#include "LTCExtensions.h" + +int rsa_basic_export(unsigned char *out, unsigned long *outlen, const rsa_key *key, int flags) +{ + unsigned long zero = 0; + int err; + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen != NULL); + LTC_ARGCHK(key != NULL); + + + if ((flags & kRsaFlagPublic) == 0) + { + if (key->type == PK_PUBLIC) + { + return CRYPT_INVALID_ARG; + }; + + /* private key */ + /* output is + Version, n, e, d, p, q, d mod (p-1), d mod (q - 1), 1/q mod p + */ + return der_encode_sequence_multi(out, outlen, + LTC_ASN1_SHORT_INTEGER, 1UL, &zero, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_INTEGER, 1UL, key->e, + LTC_ASN1_INTEGER, 1UL, key->d, + LTC_ASN1_INTEGER, 1UL, key->p, + LTC_ASN1_INTEGER, 1UL, key->q, + LTC_ASN1_INTEGER, 1UL, key->dP, + LTC_ASN1_INTEGER, 1UL, key->dQ, + LTC_ASN1_INTEGER, 1UL, key->qP, + LTC_ASN1_EOL, 0UL, NULL); + } + else + { + /* public key */ + unsigned long tmplen, *ptmplen; + unsigned char *tmp = NULL; + + if (key->type != PK_PUBLIC) + { + return CRYPT_INVALID_ARG; + } + + return der_encode_sequence_multi(out, outlen, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_INTEGER, 1UL, key->e, + LTC_ASN1_EOL, 0UL, NULL); + } +} + +int rsa_pkcs8_export(unsigned char *out, unsigned long *outlen, const rsa_key *key, int flags) +{ + int ret; + char temp[4096]; + unsigned long length = sizeof(temp); + + if ((ret = rsa_basic_export(temp, &length, key, flags)) != CRYPT_OK) + { + return ret; + } + + if ((flags & kRsaFlagPKCS1) != 0) + { + if (*outlen < length) + { + *outlen = length; + return CRYPT_BUFFER_OVERFLOW; + } + + memcpy(out, temp, length); + + return CRYPT_OK; + } + else + { + unsigned long oid[16]; + unsigned long *oidReference; + + ltc_asn1_list alg_seq[2]; + + ret = pk_get_oid(PKA_RSA, &oidReference); + if (ret != CRYPT_OK) + { + return ret; + } + + unsigned long oidArraySize = 16; + ret = pk_oid_str_to_num(oidReference, oid, &oidArraySize); + if (ret != CRYPT_OK) + { + return ret; + } + + LTC_SET_ASN1(alg_seq, 0, LTC_ASN1_OBJECT_IDENTIFIER, oid, oidArraySize); + LTC_SET_ASN1(alg_seq, 1, LTC_ASN1_NULL, NULL, 0UL); + + if ((flags & kRsaFlagPublic) == 0) + { + if (key->type == PK_PUBLIC) + { + return CRYPT_INVALID_ARG; + }; + + void *keyType; + + mp_init(&keyType); + mp_set_int(keyType, 0); + + ret = der_encode_sequence_multi(out, outlen, + LTC_ASN1_INTEGER, 1, keyType, + LTC_ASN1_SEQUENCE, 2, alg_seq, + LTC_ASN1_OCTET_STRING, length, temp, + LTC_ASN1_EOL, 0UL, NULL); + + mp_clear(keyType); + } + else + { + if (key->type != PK_PUBLIC) + { + return CRYPT_INVALID_ARG; + } + + ret = der_encode_sequence_multi(out, outlen, + LTC_ASN1_SEQUENCE, 1, alg_seq, + LTC_ASN1_BIT_STRING, length, temp, + LTC_ASN1_EOL, 0UL, NULL); + } + + return ret; + } +} diff --git a/Source/Extensions/LTC/LTCExtensions.h b/Source/Extensions/LTC/LTCExtensions.h new file mode 100644 index 00000000..608906a2 --- /dev/null +++ b/Source/Extensions/LTC/LTCExtensions.h @@ -0,0 +1,15 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LTCExtensions.h + Date: 2021-6-24 + Author: Reece +***/ +#pragma once + +static const int kRsaFlagPublic = 1 << 0; +static const int kRsaFlagPKCS1 = 1 << 1; + +int rsa_pkcs8_export(unsigned char *out, unsigned long *outlen, const rsa_key *key, int flags); +int rsa_basic_export(unsigned char *out, unsigned long *outlen, const rsa_key *key, int flags); +int rsa_import_ex(const unsigned char *in, unsigned long inlen, rsa_key *key, int flags); \ No newline at end of file diff --git a/Source/Extensions/LTC/LTCExtensions.hpp b/Source/Extensions/LTC/LTCExtensions.hpp new file mode 100644 index 00000000..5540c693 --- /dev/null +++ b/Source/Extensions/LTC/LTCExtensions.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LTCExtensions.hpp + Date: 2021-6-24 + Author: Reece +***/ +#pragma once + +extern "C" +{ +#include "LTCExtensions.h" +} \ No newline at end of file diff --git a/Source/Extensions/LTC/LTCImportEx.c b/Source/Extensions/LTC/LTCImportEx.c new file mode 100644 index 00000000..c1beb984 --- /dev/null +++ b/Source/Extensions/LTC/LTCImportEx.c @@ -0,0 +1,159 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LTCImportEx.c + Date: 2021-6-24 + Author: Reece +***/ +#include +#include "tomcrypt_private.h" +#include "LTCExtensions.h" + +int rsa_import_ex(const unsigned char *in, unsigned long inlen, rsa_key *key, int flags) +{ + int err; + void *zero; + char scratch[4096]; + const unsigned char *rsa = scratch; + unsigned long rsa_len = sizeof(scratch); + unsigned long len; + + LTC_ARGCHK(in != NULL); + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(ltc_mp.name != NULL); + + /* init key */ + if ((err = rsa_init(key)) != CRYPT_OK) + { + return err; + } + + len = 0; + + if ((flags & kRsaFlagPKCS1) != 0) + { + rsa = in; + rsa_len = inlen; + } + else + { + unsigned long oid[16]; + const char *rsaoid; + ltc_asn1_list alg_seq[2], top_seq[3]; + ltc_asn1_list *l = NULL; + + /* get RSA alg oid */ + err = pk_get_oid(PKA_RSA, &rsaoid); + if (err != CRYPT_OK) + { + goto LBL_ERR; + } + + if ((err = mp_init(&zero)) != CRYPT_OK) + { + goto LBL_ERR; + } + + LTC_SET_ASN1(alg_seq, 0, LTC_ASN1_OBJECT_IDENTIFIER, oid, 16UL); + LTC_SET_ASN1(alg_seq, 1, LTC_ASN1_NULL, NULL, 0UL);// << key + if ((flags & kRsaFlagPublic) == 0) + { + LTC_SET_ASN1(top_seq, 0, LTC_ASN1_INTEGER, zero, 1UL); + LTC_SET_ASN1(top_seq, 1, LTC_ASN1_SEQUENCE, alg_seq, 2UL); + LTC_SET_ASN1(top_seq, 2, LTC_ASN1_OCTET_STRING, rsa, rsa_len); + + err = der_decode_sequence(in, inlen, top_seq, 3UL); + } + else + { + LTC_SET_ASN1(top_seq, 0, LTC_ASN1_SEQUENCE, alg_seq, 1UL); + LTC_SET_ASN1(top_seq, 1, LTC_ASN1_BIT_STRING, rsa, rsa_len); + + err = der_decode_sequence(in, inlen, top_seq, 2UL); + } + + mp_clear(zero); + + rsa_len = top_seq[2].size; + + if (err != CRYPT_OK) + { + goto LBL_ERR; + } + + /* check alg oid */ + if ((err = pk_oid_cmp_with_asn1(rsaoid, &alg_seq[0])) != CRYPT_OK) + { + goto LBL_ERR; + } + } + + if ((flags & kRsaFlagPublic) != 0) + { + /* it's a public key and we lack e */ + if ((err = der_decode_sequence_multi(rsa, rsa_len, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_INTEGER, 1UL, key->e, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) + { + goto LBL_ERR; + } + key->type = PK_PUBLIC; + } + else + { + err = der_decode_sequence_multi(rsa, rsa_len, LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_EOL, 0UL, NULL); + + if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) + { + goto LBL_ERR; + } + + if (mp_cmp_d(key->N, 0) == LTC_MP_EQ) + { + if ((err = mp_init(&zero)) != CRYPT_OK) + { + goto LBL_ERR; + } + + /* it's a private key */ + if ((err = der_decode_sequence_multi(rsa, rsa_len, + LTC_ASN1_INTEGER, 1UL, zero, + LTC_ASN1_INTEGER, 1UL, key->N, + LTC_ASN1_INTEGER, 1UL, key->e, + LTC_ASN1_INTEGER, 1UL, key->d, + LTC_ASN1_INTEGER, 1UL, key->p, + LTC_ASN1_INTEGER, 1UL, key->q, + LTC_ASN1_INTEGER, 1UL, key->dP, + LTC_ASN1_INTEGER, 1UL, key->dQ, + LTC_ASN1_INTEGER, 1UL, key->qP, + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) + { + mp_clear(zero); + goto LBL_ERR; + } + + mp_clear(zero); + + key->type = PK_PRIVATE; + } + else if (mp_cmp_d(key->N, 1) == LTC_MP_EQ) + { + /* we don't support multi-prime RSA */ + err = CRYPT_PK_INVALID_TYPE; + goto LBL_ERR; + } + else + { + err = CRYPT_PK_INVALID_TYPE; + goto LBL_ERR; + } + } + + return CRYPT_OK; + +LBL_ERR: + rsa_free(key); + return err; +} \ No newline at end of file diff --git a/Source/Extensions/LTM/LTM.cpp b/Source/Extensions/LTM/LTM.cpp new file mode 100644 index 00000000..21efafa7 --- /dev/null +++ b/Source/Extensions/LTM/LTM.cpp @@ -0,0 +1,29 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LTM.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "LTM.hpp" + +extern "C" +{ + typedef enum + { + MP_OKAY = 0, /* no error */ + MP_ERR = -1, /* unknown error */ + MP_MEM = -2, /* out of mem */ + MP_VAL = -3, /* invalid input */ + MP_ITER = -4, /* maximum iterations reached */ + MP_BUF = -5, /* buffer overflow, supplied buffer too small */ + MP_OVF = -6 /* mp_int overflow, too many digits */ + } mp_err; + + AUKN_SYM mp_err s_mp_rand_platform(void *p, size_t n) + { + Aurora::RNG::ReadSecureRNG(p, n); + return MP_OKAY; + } +} \ No newline at end of file diff --git a/Source/Extensions/LTM/LTM.hpp b/Source/Extensions/LTM/LTM.hpp new file mode 100644 index 00000000..82b02a94 --- /dev/null +++ b/Source/Extensions/LTM/LTM.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LTM.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Extensions/LZMA/LZMA.cpp b/Source/Extensions/LZMA/LZMA.cpp new file mode 100644 index 00000000..109e1b59 --- /dev/null +++ b/Source/Extensions/LZMA/LZMA.cpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LZMA.cpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Extensions/LZMA/LZMA.hpp b/Source/Extensions/LZMA/LZMA.hpp new file mode 100644 index 00000000..07f2784a --- /dev/null +++ b/Source/Extensions/LZMA/LZMA.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: LZMA.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Extensions/MBedTls/MBedTLS.cpp b/Source/Extensions/MBedTls/MBedTLS.cpp new file mode 100644 index 00000000..238a9348 --- /dev/null +++ b/Source/Extensions/MBedTls/MBedTLS.cpp @@ -0,0 +1,19 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: MBedTLS.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "MBedTLS.hpp" + +extern "C" +{ + AUKN_SYM int mbedtls_platform_entropy_poll(void *data, unsigned char *output, size_t len, size_t *olen) + { + Aurora::RNG::ReadSecureRNG(output, len); + *olen = len; + return 0; + } +} \ No newline at end of file diff --git a/Source/Extensions/MBedTls/MBedTLS.hpp b/Source/Extensions/MBedTls/MBedTLS.hpp new file mode 100644 index 00000000..3ec1e4ce --- /dev/null +++ b/Source/Extensions/MBedTls/MBedTLS.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: MBedTLS.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Extensions/Win32/DarkTheme.cpp b/Source/Extensions/Win32/DarkTheme.cpp new file mode 100644 index 00000000..32486379 --- /dev/null +++ b/Source/Extensions/Win32/DarkTheme.cpp @@ -0,0 +1,171 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: DarkTheme.cpp + Date: 2021-6-11 + Author: Reece +***/ +#include +#include "DarkTheme.hpp" +#include + +namespace Aurora::Extensions::Win32 +{ + enum IMMERSIVE_HC_CACHE_MODE + { + IHCM_USE_CACHED_VALUE, + IHCM_REFRESH + }; + + enum PreferredAppMode + { + Default, + AllowDark, + ForceDark, + ForceLight, + Max + }; + + enum WINDOWCOMPOSITIONATTRIB + { + WCA_UNDEFINED = 0, + WCA_USEDARKMODECOLORS = 26, + WCA_LAST = 27 + }; + + struct WINDOWCOMPOSITIONATTRIBDATA + { + WINDOWCOMPOSITIONATTRIB Attrib; + PVOID pvData; + SIZE_T cbData; + }; + + using fnRtlGetNtVersionNumbers = void (WINAPI *)(LPDWORD major, LPDWORD minor, LPDWORD build); + using fnSetWindowCompositionAttribute = BOOL(WINAPI *)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA *); + + // 1809 17763 + using fnShouldAppsUseDarkMode = bool (WINAPI *)(); // ordinal 132 + using fnAllowDarkModeForWindow = bool (WINAPI *)(HWND hWnd, bool allow); // ordinal 133 + using fnAllowDarkModeForApp = bool (WINAPI *)(bool allow); // ordinal 135, in 1809 + + using fnSetPreferredAppMode = PreferredAppMode(WINAPI *)(PreferredAppMode appMode); // ordinal 135, in 1903 + using fnIsDarkModeAllowedForApp = bool (WINAPI *)(); // ordinal 139 + + static fnSetWindowCompositionAttribute _SetWindowCompositionAttribute = nullptr; + static fnAllowDarkModeForWindow _AllowDarkModeForWindow = nullptr; + static fnAllowDarkModeForApp _AllowDarkModeForApp = nullptr; + // 1903 18362 + static fnSetPreferredAppMode _SetPreferredAppMode = nullptr; + + static DWORD g_buildNumber = 0; + + bool AllowDarkModeForWindow(HWND hWnd, bool allow) + { + if (g_darkModeSupported) + { + return _AllowDarkModeForWindow(hWnd, allow); + } + return false; + } + + void RefreshTitleBarThemeColor(HWND hWnd) + { + BOOL dark = TRUE; + + if (g_buildNumber < 18362) + { + SetPropW(hWnd, L"UseImmersiveDarkModeColors", reinterpret_cast(static_cast(dark))); + } + else if (_SetWindowCompositionAttribute) + { + WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &dark, sizeof(dark) }; + _SetWindowCompositionAttribute(hWnd, &data); + } + } + + void MakeWindowBordersDark(HWND window) + { + if (!g_darkModeSupported) return; + SetWindowTheme(window, L"DarkMode_Explorer", NULL); + AllowDarkModeForWindow(window, true); + RefreshTitleBarThemeColor(window); + } + + void AllowDarkModeForApp() + { + if (_AllowDarkModeForApp) + { + _AllowDarkModeForApp(true); + } + else if (_SetPreferredAppMode) + { + _SetPreferredAppMode(ForceDark); + } + } + + static constexpr bool CheckBuildNumber(DWORD buildNumber) + { + return (buildNumber == 17763 || // 1809 + buildNumber == 18362 || // 1903 + buildNumber == 18363 || // 1909 + buildNumber == 19041); // 2004 + } + + using fnRefreshImmersiveColorPolicyState = void (WINAPI *)(); // ordinal 104 + fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState = nullptr; + + void InitDarkMode() + { + auto RtlGetNtVersionNumbers = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetNtVersionNumbers")); + if (!RtlGetNtVersionNumbers) + { + return; + } + + DWORD major, minor; + RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber); + g_buildNumber &= ~0xF0000000; + if (major != 10 || minor != 0 || !CheckBuildNumber(g_buildNumber)) + { + return; + } + + HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (!hUxtheme) + { + return; + } + + _AllowDarkModeForWindow = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133))); + _RefreshImmersiveColorPolicyState = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(104))); + auto ord135 = GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135)); + + if (g_buildNumber < 18362) + { + _AllowDarkModeForApp = reinterpret_cast(ord135); + } + else + { + _SetPreferredAppMode = reinterpret_cast(ord135); + } + + _FlushMenuThemes = reinterpret_cast(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136))); + + _SetWindowCompositionAttribute = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetWindowCompositionAttribute")); + + if (_AllowDarkModeForWindow && + (_AllowDarkModeForApp || _SetPreferredAppMode)) + { + g_darkModeSupported = true; + + AllowDarkModeForApp(); + if (_RefreshImmersiveColorPolicyState) + { + if (g_buildNumber < 18362) + { + _RefreshImmersiveColorPolicyState(); + } + } + } + } +} \ No newline at end of file diff --git a/Source/Extensions/Win32/DarkTheme.hpp b/Source/Extensions/Win32/DarkTheme.hpp new file mode 100644 index 00000000..f55fd819 --- /dev/null +++ b/Source/Extensions/Win32/DarkTheme.hpp @@ -0,0 +1,24 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: DarkTheme.hpp + Date: 2021-6-11 + Author: Reece +***/ +#pragma once + +namespace Aurora::Extensions::Win32 +{ + inline bool g_darkModeSupported = false; + + using fnFlushMenuThemes = void (WINAPI *)(); + inline fnFlushMenuThemes _FlushMenuThemes = nullptr; + + void AllowDarkModeForApp(); + void RefreshTitleBarThemeColor(HWND hWnd); + bool AllowDarkModeForWindow(HWND hWnd, bool allow); + + AUKN_SYM void MakeWindowBordersDark(HWND window); + + void InitDarkMode(); +} \ No newline at end of file diff --git a/Source/HWInfo/CpuInfo.cpp b/Source/HWInfo/CpuInfo.cpp new file mode 100644 index 00000000..11b2d97c --- /dev/null +++ b/Source/HWInfo/CpuInfo.cpp @@ -0,0 +1,11 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CpuInfo.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "HWInfo.hpp" +#include "CpuInfo.hpp" + diff --git a/Source/HWInfo/CpuInfo.hpp b/Source/HWInfo/CpuInfo.hpp new file mode 100644 index 00000000..45519d54 --- /dev/null +++ b/Source/HWInfo/CpuInfo.hpp @@ -0,0 +1,9 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CpuInfo.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + diff --git a/Source/HWInfo/HWInfo.hpp b/Source/HWInfo/HWInfo.hpp new file mode 100644 index 00000000..24ec8d6a --- /dev/null +++ b/Source/HWInfo/HWInfo.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: HWInfo.hpp + Date: 2021-6-19 + Author: Reece +***/ diff --git a/Source/HWInfo/RamInfo.Linux.cpp b/Source/HWInfo/RamInfo.Linux.cpp new file mode 100644 index 00000000..4c65e425 --- /dev/null +++ b/Source/HWInfo/RamInfo.Linux.cpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RamInfo.Linux.cpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/HWInfo/RamInfo.Linux.hpp b/Source/HWInfo/RamInfo.Linux.hpp new file mode 100644 index 00000000..b1203f32 --- /dev/null +++ b/Source/HWInfo/RamInfo.Linux.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RamInfo.Linux.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/HWInfo/RamInfo.Win32.cpp b/Source/HWInfo/RamInfo.Win32.cpp new file mode 100644 index 00000000..bdb62b98 --- /dev/null +++ b/Source/HWInfo/RamInfo.Win32.cpp @@ -0,0 +1,12 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RamInfo.Win32.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "HWInfo.hpp" +#include "RamInfo.hpp" +#include "RamInfo.Win32.hpp" + diff --git a/Source/HWInfo/RamInfo.Win32.hpp b/Source/HWInfo/RamInfo.Win32.hpp new file mode 100644 index 00000000..a8d2587f --- /dev/null +++ b/Source/HWInfo/RamInfo.Win32.hpp @@ -0,0 +1,9 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RamInfo.Win32.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + diff --git a/Source/HWInfo/RamInfo.hpp b/Source/HWInfo/RamInfo.hpp new file mode 100644 index 00000000..7c11112b --- /dev/null +++ b/Source/HWInfo/RamInfo.hpp @@ -0,0 +1,7 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: RamInfo.hpp + Date: 2021-6-12 + Author: Reece +***/ diff --git a/Source/Hashing/CommonDigests.cpp b/Source/Hashing/CommonDigests.cpp new file mode 100644 index 00000000..650a217c --- /dev/null +++ b/Source/Hashing/CommonDigests.cpp @@ -0,0 +1,53 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CommonDigests.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "CommonDigests.hpp" +#include + +namespace Aurora::Hashing +{ + AUKN_SYM void MD5(const void *buffer, AuMach length, std::array &md5) + { + hash_state md; + md5_init(&md); + md5_process(&md, reinterpret_cast(buffer), length); + md5_done(&md, md5.data()); + } + + AUKN_SYM void SHA1(const void *buffer, AuMach length, std::array &sha1) + { + hash_state md; + sha1_init(&md); + sha1_process(&md, reinterpret_cast(buffer), length); + sha1_done(&md, sha1.data()); + } + + AUKN_SYM void Tiger(const void *buffer, AuMach length, std::array &tiger) + { + hash_state md; + tiger_init(&md); + tiger_process(&md, reinterpret_cast(buffer), length); + tiger_done(&md, tiger.data()); + } + + AUKN_SYM void SHA2(const void *buffer, AuMach length, std::array &sha2) + { + hash_state md; + sha256_init(&md); + sha256_process(&md, reinterpret_cast(buffer), length); + sha256_done(&md, sha2.data()); + } + + AUKN_SYM void SHA2_64(const void *buffer, AuMach length, std::array &sha2) + { + hash_state md; + sha512_init(&md); + sha512_process(&md, reinterpret_cast(buffer), length); + sha512_done(&md, sha2.data()); + } +} \ No newline at end of file diff --git a/Source/Hashing/CommonDigests.hpp b/Source/Hashing/CommonDigests.hpp new file mode 100644 index 00000000..3dc0b661 --- /dev/null +++ b/Source/Hashing/CommonDigests.hpp @@ -0,0 +1,9 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: CommonDigests.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + diff --git a/Source/Hashing/HashStream.cpp b/Source/Hashing/HashStream.cpp new file mode 100644 index 00000000..305a5ea9 --- /dev/null +++ b/Source/Hashing/HashStream.cpp @@ -0,0 +1,109 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: HashStream.cpp + Date: 2021-6-12 + Author: Reece +***/ +#include +#include "HashStream.hpp" +#include + +namespace Aurora::Hashing +{ + class HashStream : public IHashStream + { + public: + HashStream(EHashType type); + + void Ingest(const void *buffer, AuUInt32 len) override; + AuUInt8 *GetBytes(AuUInt32 &length) override; + + private: + AuUInt8 buffer_[64]{}; + hash_state state_{}; + EHashType type_{}; + }; + + HashStream::HashStream(EHashType type) : type_(type) + { + switch (type) + { + case EHashType::eMD5: + md5_init(&state_); + break; + case EHashType::eSHA1: + sha1_init(&state_); + break; + case EHashType::eSHA2_32: + sha256_init(&state_); + break; + case EHashType::eSHA2_64: + sha512_init(&state_); + break; + case EHashType::eTiger: + tiger_init(&state_); + break; + } + } + + void HashStream::Ingest(const void *buffer, AuUInt32 len) + { + switch (type_) + { + case EHashType::eMD5: + md5_process(&state_, reinterpret_cast(buffer), len); + break; + case EHashType::eSHA1: + sha1_process(&state_, reinterpret_cast(buffer), len); + break; + case EHashType::eSHA2_32: + sha256_process(&state_, reinterpret_cast(buffer), len); + break; + case EHashType::eSHA2_64: + sha512_process(&state_, reinterpret_cast(buffer), len); + break; + case EHashType::eTiger: + tiger_process(&state_, reinterpret_cast(buffer), len); + break; + } + } + + AuUInt8 *HashStream::GetBytes(AuUInt32 &length) + { + switch (type_) + { + case EHashType::eMD5: + length = 16; + md5_done(&state_, reinterpret_cast(buffer_)); + return buffer_; + case EHashType::eSHA1: + length = 20; + sha1_done(&state_, reinterpret_cast(buffer_)); + return buffer_; + case EHashType::eSHA2_32: + length = 32; + sha256_done(&state_, reinterpret_cast(buffer_)); + return buffer_; + case EHashType::eSHA2_64: + length = 64; + sha512_done(&state_, reinterpret_cast(buffer_)); + return buffer_; + case EHashType::eTiger: + length = 24; + tiger_done(&state_, reinterpret_cast(buffer_)); + return buffer_; + } + return nullptr; + } + + AUKN_SYM IHashStream *HashStreamNew(EHashType type) + { + return _new HashStream(type); + } + + AUKN_SYM void HashStreamRelease(IHashStream *stream) + { + SafeDelete(stream); + } +} \ No newline at end of file diff --git a/Source/Hashing/HashStream.hpp b/Source/Hashing/HashStream.hpp new file mode 100644 index 00000000..15e5a770 --- /dev/null +++ b/Source/Hashing/HashStream.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: HashStream.hpp + Date: 2021-6-12 + Author: Reece +***/ +#pragma once + +namespace Aurora::Hashing +{ + +} \ No newline at end of file diff --git a/Source/Hashing/Hashing.cpp b/Source/Hashing/Hashing.cpp new file mode 100644 index 00000000..b7e75001 --- /dev/null +++ b/Source/Hashing/Hashing.cpp @@ -0,0 +1,17 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Hashing.cpp + Date: 2021-6-17 + Author: Reece +***/ +#include +#include "Hashing.hpp" + +namespace Aurora::Hashing +{ + void InitHashing() + { + + } +} \ No newline at end of file diff --git a/Source/Hashing/Hashing.hpp b/Source/Hashing/Hashing.hpp new file mode 100644 index 00000000..6fd8516f --- /dev/null +++ b/Source/Hashing/Hashing.hpp @@ -0,0 +1,13 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: Hashing.hpp + Date: 2021-6-17 + Author: Reece +***/ +#pragma once + +namespace Aurora::Hashing +{ + void InitHashing(); +} \ No newline at end of file diff --git a/Source/IO/FS/FS.Apple.cpp b/Source/IO/FS/FS.Apple.cpp new file mode 100644 index 00000000..be4a8b29 --- /dev/null +++ b/Source/IO/FS/FS.Apple.cpp @@ -0,0 +1,236 @@ +/*** + Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: FS.Unix.cpp + Date: 2021-6-12 + Author: Reece +***/ + +#include +#include "FS.hpp" +#include "FS.Generic.hpp" +#include