diff --git a/storm/CMakeLists.txt b/storm/CMakeLists.txt index 0237a10..e381544 100644 --- a/storm/CMakeLists.txt +++ b/storm/CMakeLists.txt @@ -7,6 +7,7 @@ file(GLOB STORM_SOURCES "crypto/*.cpp" "error/*.cpp" "hash/*.cpp" + "option*/*.cpp" "queue/*.cpp" "string/*.cpp" "thread/*.cpp" @@ -17,6 +18,7 @@ if(WHOA_SYSTEM_WIN) "win/*.cpp" "error/win/*.cpp" "thread/win/*.cpp" + "registry/win/*.cpp" ) list(APPEND STORM_SOURCES ${STORM_WIN_SOURCES}) endif() @@ -28,6 +30,7 @@ if(WHOA_SYSTEM_MAC) "error/unix/*.cpp" "thread/mac/*.cpp" "thread/mac/*.mm" + "registry/mac/*.mm" ) list(APPEND STORM_SOURCES ${STORM_MAC_SOURCES}) endif() @@ -37,6 +40,7 @@ if(WHOA_SYSTEM_LINUX) "linux/*.cpp" "error/unix/*.cpp" "thread/linux/*.cpp" + "registry/linux/*.cpp" ) list(APPEND STORM_SOURCES ${STORM_LINUX_SOURCES}) endif() diff --git a/storm/Crypto.cpp b/storm/Crypto.cpp index 0b8cae9..6eec554 100644 --- a/storm/Crypto.cpp +++ b/storm/Crypto.cpp @@ -3,8 +3,9 @@ #include void SARC4PrepareKey(const void* data, uint32_t len, SARC4Key* key) { - STORM_ASSERT(data); - STORM_VALIDATE(data, ERROR_INVALID_PARAMETER); + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(data); + STORM_VALIDATE_END; key->x = 0; key->y = 0; diff --git a/storm/Option.hpp b/storm/Option.hpp new file mode 100644 index 0000000..f3c8974 --- /dev/null +++ b/storm/Option.hpp @@ -0,0 +1,6 @@ +#ifndef STORM_OPTION_HPP +#define STORM_OPTION_HPP + +#include "storm/option/Option.hpp" + +#endif diff --git a/storm/Registry.hpp b/storm/Registry.hpp new file mode 100644 index 0000000..15b6948 --- /dev/null +++ b/storm/Registry.hpp @@ -0,0 +1,6 @@ +#ifndef STORM_REGISTRY_HPP +#define STORM_REGISTRY_HPP + +#include "storm/registry/Registry.hpp" + +#endif diff --git a/storm/String.hpp b/storm/String.hpp index 657ef9d..6fcd551 100644 --- a/storm/String.hpp +++ b/storm/String.hpp @@ -21,6 +21,8 @@ int32_t SStrCmpI(const char* string1, const char* string2, size_t maxchars); size_t SStrCopy(char* dest, const char* source, size_t destsize); +size_t SStrNCopy(char* dest, const char* source, size_t maxchars, size_t destsize); + char* SStrDupA(const char* string, const char* filename, uint32_t linenumber); uint32_t SStrHashHT(const char* string); diff --git a/storm/Unicode.cpp b/storm/Unicode.cpp index 9c64d13..3291b48 100644 --- a/storm/Unicode.cpp +++ b/storm/Unicode.cpp @@ -1,4 +1,61 @@ #include "storm/Unicode.hpp" +#include "storm/String.hpp" + +#include + +static const uint32_t offsetsFromUTF8[] = { + 0x0, + 0x3080, + 0xE2080, + 0x3C82080, + 0xFA082080, + 0x82082080 +}; + +static const uint32_t firstByteMark[] = { + 0x0, + 0x0, + 0xC0, + 0xE0, + 0xF0, + 0xF8, + 0xFC +}; + +static const uint8_t bytesFromUTF8[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05 +}; uint32_t SUniSGetUTF8(const uint8_t* strptr, int32_t* chars) { if (chars) { @@ -111,3 +168,172 @@ void SUniSPutUTF8(uint32_t c, char* strptr) { *curstr++ = v3; *curstr = '\0'; } + +int32_t SUniConvertUTF16to8(uint8_t* dst, uint32_t dstmaxchars, const uint16_t* src, uint32_t srcmaxchars, uint32_t* dstchars, uint32_t* srcchars) { + auto srcend = srcmaxchars == STORM_MAX_STR ? reinterpret_cast(UINTPTR_MAX) : &src[srcmaxchars]; + auto dstend = &dst[dstmaxchars]; + auto dststart = dst; + auto srcstart = src; + + int32_t result; + + while (src < srcend) { + uint32_t widechars = 1; + auto grapheme = static_cast(src[0]); + if (0xD7FF < grapheme && grapheme < 0xDC00) { + if (src + 1 >= srcend) { + goto fail; + } + auto char2 = static_cast(src[1]); + if (0xDBFF < char2 && char2 < 0xE000) { + grapheme = ((grapheme - 0xD7F7) * 1024) + char2; + widechars = 2; + } + } + + uint32_t chars; + + if (grapheme < 0x80) { + chars = 1; + if (grapheme == 0) { + if (dst < dstend) { + *dst++ = '\0'; + result = 0; + } + goto done; + } + } else if (grapheme < 0x800) { + chars = 2; + } else if (grapheme < 0x10000) { + chars = 3; + } else if (grapheme < 0x200000) { + chars = 4; + } else if (grapheme < 0x4000000) { + chars = 5; + } else if (grapheme > 0x7FFFFFFF) { + grapheme = 0xFFFD; + chars = 2; + } else { + chars = 6; + } + + result = chars; + + dst += chars; + + if (dst > dstend) { + goto done; + } + + switch (chars) { + case 6: + *--dst = (grapheme & 0x3F) | 0x80; + grapheme >>= 6; + case 5: + *--dst = (grapheme & 0x3F) | 0x80; + grapheme >>= 6; + case 4: + *--dst = (grapheme & 0x3F) | 0x80; + grapheme >>= 6; + case 3: + *--dst = (grapheme & 0x3F) | 0x80; + grapheme >>= 6; + case 2: + *--dst = (grapheme & 0x3F) | 0x80; + grapheme >>= 6; + case 1: + *--dst = grapheme | firstByteMark[chars]; + } + + src += widechars; + dst += chars; + } + +fail: + result = -1; + +done: + if (srcchars) { + *srcchars = src - srcstart; + } + + if (dstchars) { + *dstchars = dst - dststart; + } + return result; +} +#include + +int32_t SUniConvertUTF8to16(uint16_t* dst, uint32_t dstmaxchars, const uint8_t* src, uint32_t srcmaxchars, uint32_t* dstchars, uint32_t* srcchars) { + auto srcend = srcmaxchars == STORM_MAX_STR ? reinterpret_cast(UINTPTR_MAX) : src + srcmaxchars; + auto dstend = dst + dstmaxchars; + auto dststart = dst; + auto srcstart = src; + + int32_t result = 0; + + while (src < srcend) { + auto bytes = bytesFromUTF8[*src]; + if ((src + bytes) >= srcend) { + result = -1 - bytes; + goto done; + } + + uint32_t grapheme = 0; + + int32_t width = 0; + + switch (bytes) { + case 5: + grapheme = (grapheme + src[width++]) << 6; + case 4: + grapheme = (grapheme + src[width++]) << 6; + case 3: + grapheme = (grapheme + src[width++]) << 6; + case 2: + grapheme = (grapheme + src[width++]) << 6; + case 1: + grapheme = (grapheme + src[width++]) << 6; + case 0: + grapheme = (grapheme + src[width++]) - offsetsFromUTF8[bytes]; + } + + if (dst >= dstend) { + result = 1; + goto done; + } + + if (grapheme < 0x10000) { + *dst++ = static_cast(grapheme); + if (grapheme == 0x0) { + goto done; + } + } else if (grapheme < 0x110000) { + if (dst >= dstend) { + result = 0; + goto done; + } + auto v16 = grapheme - 0x10000; + *dst++ = static_cast(v16 >> 10) - 0x2800; + *dst++ = static_cast(v16 & 0x3FF) - 0x2400; + } else { + *dst++ = 0xFFFD; + } + + src += width; + } + + printf("fail yure %p %p\n", src, srcend); + + result = -1; + +done: + if (srcchars) { + *srcchars = src - srcstart; + } + + if (dstchars) { + *dstchars = dst - dststart; + } + return result; +} diff --git a/storm/Unicode.hpp b/storm/Unicode.hpp index 50f703d..bf6efc8 100644 --- a/storm/Unicode.hpp +++ b/storm/Unicode.hpp @@ -7,4 +7,8 @@ uint32_t SUniSGetUTF8(const uint8_t* strptr, int32_t* chars); void SUniSPutUTF8(uint32_t c, char* strptr); +int32_t SUniConvertUTF16to8(uint8_t* dst, uint32_t dstmaxchars, const uint16_t* src, uint32_t srcmaxchars, uint32_t* dstchars, uint32_t* srcchars); + +int32_t SUniConvertUTF8to16(uint16_t* dst, uint32_t dstmaxchars, const uint8_t* src, uint32_t srcmaxchars, uint32_t* dstchars, uint32_t* srcchars); + #endif diff --git a/storm/error/Codes.hpp b/storm/error/Codes.hpp deleted file mode 100644 index 5f2f578..0000000 --- a/storm/error/Codes.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef STORM_ERROR_CODES_HPP -#define STORM_ERROR_CODES_HPP - -#if defined(WHOA_SYSTEM_WIN) -#include "storm/error/win/Codes.hpp" -#endif - -#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) -#include "storm/error/unix/Codes.hpp" -#endif - -#include "storm/error/Macro.hpp" - -#define STORM_ERROR_APPLICATION_FATAL STORM_ERROR(0x84) - -#define STORM_COMMAND_ERROR_BAD_ARGUMENT STORM_ERROR(0x65) -#define STORM_COMMAND_ERROR_NOT_ENOUGH_ARGUMENTS STORM_ERROR(0x6D) -#define STORM_COMMAND_ERROR_OPEN_FAILED 0x6E - -#endif diff --git a/storm/error/Defines.hpp b/storm/error/Defines.hpp new file mode 100644 index 0000000..b1f12a4 --- /dev/null +++ b/storm/error/Defines.hpp @@ -0,0 +1,245 @@ +#ifndef STORM_ERROR_DEFINES_HPP +#define STORM_ERROR_DEFINES_HPP + +// error codes + +#if defined(WHOA_SYSTEM_WIN) + +#include + +#else + +#define ERROR_SUCCESS 0x0000 +#define ERROR_INVALID_FUNCTION 0x0001 +#define ERROR_FILE_NOT_FOUND 0x0002 +#define ERROR_PATH_NOT_FOUND 0x0003 +#define ERROR_TOO_MANY_OPEN_FILES 0x0004 +#define ERROR_ACCESS_DENIED 0x0005 +#define ERROR_INVALID_HANDLE 0x0006 +#define ERROR_ARENA_TRASHED 0x0007 +#define ERROR_NOT_ENOUGH_MEMORY 0x0008 +#define ERROR_INVALID_BLOCK 0x0009 +#define ERROR_BAD_ENVIRONMENT 0x000A +#define ERROR_BAD_FORMAT 0x000B +#define ERROR_INVALID_ACCESS 0x000C +#define ERROR_INVALID_DATA 0x000D +#define ERROR_INVALID_DRIVE 0x000F +#define ERROR_CURRENT_DIRECTORY 0x0010 +#define ERROR_NOT_SAME_DEVICE 0x0011 +#define ERROR_NO_MORE_FILES 0x0012 +#define ERROR_WRITE_PROTECT 0x0013 +#define ERROR_BAD_UNIT 0x0014 +#define ERROR_NOT_READY 0x0015 +#define ERROR_BAD_COMMAND 0x0016 +#define ERROR_CRC 0x0017 +#define ERROR_BAD_LENGTH 0x0018 +#define ERROR_SEEK 0x0019 +#define ERROR_NOT_DOS_DISK 0x001A +#define ERROR_SECTOR_NOT_FOUND 0x001B +#define ERROR_OUT_OF_PAPER 0x001C +#define ERROR_WRITE_FAULT 0x001D +#define ERROR_READ_FAULT 0x001E +#define ERROR_GEN_FAILURE 0x001F +#define ERROR_SHARING_VIOLATION 0x0020 +#define ERROR_LOCK_VIOLATION 0x0021 +#define ERROR_WRONG_DISK 0x0022 +#define ERROR_FCB_UNAVAILABLE 0x0023 +#define ERROR_SHARING_BUFFER_EXCEEDED 0x0024 +#define ERROR_NOT_SUPPORTED 0x0032 +#define ERROR_FILE_EXISTS 0x0050 +#define ERROR_DUP_FCB 0x0051 +#define ERROR_CANNOT_MAKE 0x0052 +#define ERROR_FAIL_I24 0x0053 +#define ERROR_OUT_OF_STRUCTURES 0x0054 +#define ERROR_ALREADY_ASSIGNED 0x0055 +#define ERROR_INVALID_PASSWORD 0x0056 +#define ERROR_INVALID_PARAMETER 0x0057 +#define ERROR_NET_WRITE_FAULT 0x0058 +#define ERROR_NO_PROC_SLOTS 0x0059 +#define ERROR_NOT_FROZEN 0x005A +#define ERR_TSTOVFL 0x005B +#define ERR_TSTDUP 0x005C +#define ERROR_NO_ITEMS 0x005D +#define ERROR_INTERRUPT 0x005F +#define ERROR_TOO_MANY_SEMAPHORES 0x0064 +#define ERROR_EXCL_SEM_ALREADY_OWNED 0x0065 +#define ERROR_SEM_IS_SET 0x0066 +#define ERROR_TOO_MANY_SEM_REQUESTS 0x0067 +#define ERROR_INVALID_AT_INTERRUPT_TIME 0x0068 +#define ERROR_SEM_OWNER_DIED 0x0069 +#define ERROR_SEM_USER_LIMIT 0x006A +#define ERROR_DISK_CHANGE 0x006B +#define ERROR_DRIVE_LOCKED 0x006C +#define ERROR_BROKEN_PIPE 0x006D +#define ERROR_OPEN_FAILED 0x006E +#define ERROR_BUFFER_OVERFLOW 0x006F +#define ERROR_DISK_FULL 0x0070 +#define ERROR_NO_MORE_SEARCH_HANDLES 0x0071 +#define ERROR_INVALID_TARGET_HANDLE 0x0072 +#define ERROR_PROTECTION_VIOLATION 0x0073 +#define ERROR_VIOKBD_REQUEST 0x0074 +#define ERROR_INVALID_CATEGORY 0x0075 +#define ERROR_INVALID_VERIFY_SWITCH 0x0076 +#define ERROR_BAD_DRIVER_LEVEL 0x0077 +#define ERROR_CALL_NOT_IMPLEMENTED 0x0078 +#define ERROR_SEM_TIMEOUT 0x0079 +#define ERROR_INSUFFICIENT_BUFFER 0x007A +#define ERROR_INVALID_NAME 0x007B +#define ERROR_INVALID_LEVEL 0x007C +#define ERROR_NO_VOLUME_LABEL 0x007D +#define ERROR_MOD_NOT_FOUND 0x007E +#define ERROR_PROC_NOT_FOUND 0x007F +#define ERROR_WAIT_NO_CHILDREN 0x0080 +#define ERROR_CHILD_NOT_COMPLETE 0x0081 +#define ERROR_DIRECT_ACCESS_HANDLE 0x0082 +#define ERROR_NEGATIVE_SEEK 0x0083 +#define ERROR_SEEK_ON_DEVICE 0x0084 +#define ERROR_IS_JOIN_TARGET 0x0085 +#define ERROR_IS_JOINED 0x0086 +#define ERROR_IS_SUBSTED 0x0087 +#define ERROR_NOT_JOINED 0x0088 +#define ERROR_NOT_SUBSTED 0x0089 +#define ERROR_JOIN_TO_JOIN 0x008A +#define ERROR_SUBST_TO_SUBST 0x008B +#define ERROR_JOIN_TO_SUBST 0x008C +#define ERROR_SUBST_TO_JOIN 0x008D +#define ERROR_BUSY_DRIVE 0x008E +#define ERROR_SAME_DRIVE 0x008F +#define ERROR_DIR_NOT_ROOT 0x0090 +#define ERROR_DIR_NOT_EMPTY 0x0091 +#define ERROR_IS_SUBST_PATH 0x0092 +#define ERROR_IS_JOIN_PATH 0x0093 +#define ERROR_PATH_BUSY 0x0094 +#define ERROR_IS_SUBST_TARGET 0x0095 +#define ERROR_SYSTEM_TRACE 0x0096 +#define ERROR_INVALID_EVENT_COUNT 0x0097 +#define ERROR_TOO_MANY_MUXWAITERS 0x0098 +#define ERROR_INVALID_LIST_FORMAT 0x0099 +#define ERROR_LABEL_TOO_LONG 0x009A +#define ERROR_TOO_MANY_TCBS 0x009B +#define ERROR_SIGNAL_REFUSED 0x009C +#define ERROR_DISCARDED 0x009D +#define ERROR_NOT_LOCKED 0x009E +#define ERROR_BAD_THREADID_ADDR 0x009F +#define ERROR_BAD_ARGUMENTS 0x00A0 +#define ERROR_BAD_PATHNAME 0x00A1 +#define ERROR_SIGNAL_PENDING 0x00A2 +#define ERROR_UNCERTAIN_MEDIA 0x00A3 +#define ERROR_MAX_THRDS_REACHED 0x00A4 +#define ERROR_MONITORS_NOT_SUPPORTED 0x00A5 +#define ERROR_INVALID_SEGMENT_NUMBER 0x00B4 +#define ERROR_INVALID_CALLGATE 0x00B5 +#define ERROR_INVALID_ORDINAL 0x00B6 +#define ERROR_ALREADY_EXISTS 0x00B7 +#define ERROR_NO_CHILD_PROCESS 0x00B8 +#define ERROR_CHILD_ALIVE_NOWAIT 0x00B9 +#define ERROR_INVALID_FLAG_NUMBER 0x00BA +#define ERROR_SEM_NOT_FOUND 0x00BB +#define ERROR_INVALID_STARTING_CODESEG 0x00BC +#define ERROR_INVALID_STACKSEG 0x00BD +#define ERROR_INVALID_MODULETYPE 0x00BE +#define ERROR_INVALID_EXE_SIGNATURE 0x00BF +#define ERROR_EXE_MARKED_INVALID 0x00C0 +#define ERROR_BAD_EXE_FORMAT 0x00C1 +#define ERROR_ITERATED_DATA_EXCEEDS_64k 0x00C2 +#define ERROR_INVALID_MINALLOCSIZE 0x00C3 +#define ERROR_DYNLINK_FROM_INVALID_RING 0x00C4 +#define ERROR_IOPL_NOT_ENABLED 0x00C5 +#define ERROR_INVALID_SEGDPL 0x00C6 +#define ERROR_AUTODATASEG_EXCEEDS_64k 0x00C7 +#define ERROR_RING2SEG_MUST_BE_MOVABLE 0x00C8 +#define ERROR_RELOC_CHAIN_XEEDS_SEGLIM 0x00C9 +#define ERROR_INFLOOP_IN_RELOC_CHAIN 0x00CA +#define ERROR_ENVVAR_NOT_FOUND 0x00CB +#define ERROR_NOT_CURRENT_CTRY 0x00CC +#define ERROR_NO_SIGNAL_SENT 0x00CD +#define ERROR_FILENAME_EXCED_RANGE 0x00CE +#define ERROR_RING2_STACK_IN_USE 0x00CF +#define ERROR_META_EXPANSION_TOO_LONG 0x00D0 +#define ERROR_INVALID_SIGNAL_NUMBER 0x00D1 +#define ERROR_THREAD_1_INACTIVE 0x00D2 +#define ERROR_INFO_NOT_AVAIL 0x00D3 +#define ERROR_LOCKED 0x00D4 +#define ERROR_BAD_DYNALINK 0x00D5 +#define ERROR_TOO_MANY_MODULES 0x00D6 +#define ERROR_NESTING_NOT_ALLOWED 0x00D7 +#define ERROR_INVALID_TOKEN 0x013B + +#endif + +#define STORM_NO_ERROR 0x85100000 + +#define STORM_ERROR(id) (STORM_NO_ERROR | (id & 0xFFFF)) + +#define STORM_ERROR_APPLICATION_FATAL STORM_ERROR(0x84) + +#define STORM_COMMAND_ERROR_BAD_ARGUMENT STORM_ERROR(0x65) +#define STORM_COMMAND_ERROR_NOT_ENOUGH_ARGUMENTS STORM_ERROR(0x6D) + +// assertions + +#if defined(WHOA_BUILD_ASSERTIONS) + +#define STORM_ASSERT(x) \ + if (!(x)) { \ + SErrPrepareAppFatal(__FILE__, __LINE__); \ + SErrDisplayAppFatal(#x); \ + } \ + (void)0 + +#else + +#define STORM_ASSERT(x) \ + (void)0 + +#endif + +// validation + +#if defined(NDEBUG) + +// (release) check all validation cases and return if any is false without displaying an error + +#define STORM_VALIDATE_BEGIN \ + { \ + bool intrn_valresult = true; \ + do { +#define STORM_VALIDATE(x) \ + if (!(x)) { \ + intrn_valresult &= !!(x); \ + break; \ + } +#define STORM_VALIDATE_END \ + } while(0); \ + if (!intrn_valresult) { \ + SErrSetLastError(ERROR_INVALID_PARAMETER); \ + return 0; \ + } \ + } +#define STORM_VALIDATE_END_VOID \ + } while(0); \ + if (!intrn_valresult) { \ + SErrSetLastError(ERROR_INVALID_PARAMETER); \ + return; \ + } \ + } +#else + +// (debug) display any failed validation as you would a failed assertion + +#define STORM_VALIDATE_BEGIN \ + do { +#define STORM_VALIDATE(x) \ + if (!(x)) { \ + SErrPrepareAppFatal(__FILE__, __LINE__); \ + SErrDisplayAppFatal(#x); \ + } \ + (void)0 +#define STORM_VALIDATE_END \ + } while (0) +#define STORM_VALIDATE_END_VOID \ + } while (0) + +#endif + +#endif diff --git a/storm/error/Error.cpp b/storm/error/Error.cpp index 70be320..107a709 100644 --- a/storm/error/Error.cpp +++ b/storm/error/Error.cpp @@ -1,5 +1,4 @@ #include "storm/error/Error.hpp" -#include "storm/error/Codes.hpp" #include "storm/error/Types.hpp" #include "storm/Thread.hpp" diff --git a/storm/error/Error.hpp b/storm/error/Error.hpp index f5cc848..aceecc0 100644 --- a/storm/error/Error.hpp +++ b/storm/error/Error.hpp @@ -3,8 +3,7 @@ #include -#include "storm/error/Macro.hpp" -#include "storm/error/Codes.hpp" +#include "storm/error/Defines.hpp" [[noreturn]] void SErrDisplayAppFatal(const char* format, ...); diff --git a/storm/error/Macro.hpp b/storm/error/Macro.hpp deleted file mode 100644 index c3cd704..0000000 --- a/storm/error/Macro.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef STORM_ERROR_MACRO_HPP -#define STORM_ERROR_MACRO_HPP - -#define STORM_NO_ERROR 0x85100000 - -#define STORM_ERROR(id) (STORM_NO_ERROR | (id & 0xFFFF)) - -// assertions -#if defined(NDEBUG) -#define STORM_ASSERT(x) \ - (void)0 -#else -#define STORM_ASSERT(x) \ - if (!(x)) { \ - SErrPrepareAppFatal(__FILE__, __LINE__); \ - SErrDisplayAppFatal(#x); \ - } \ - (void)0 -#endif - -#define STORM_VALIDATE(x, y, ...) \ - if (!(x)) { \ - SErrSetLastError(y); \ - return __VA_ARGS__; \ - } \ - (void)0 - -#define STORM_VALIDATE_STRING(x, y, ...) STORM_VALIDATE(x && *x, y, __VA_ARGS__) - -#define STORM_PANIC(...) \ - SErrPrepareAppFatal(__FILE__, __LINE__); \ - SErrDisplayAppFatal(__VA_ARGS__); \ - (void)0 - -#endif diff --git a/storm/error/unix/Codes.hpp b/storm/error/unix/Codes.hpp deleted file mode 100644 index 0a68df8..0000000 --- a/storm/error/unix/Codes.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef STORM_ERROR_UNIX_CODES_HPP -#define STORM_ERROR_UNIX_CODES_HPP - -#define ERROR_INVALID_PARAMETER 0x57 -#define ERROR_SUCCESS 0x0 - -#endif diff --git a/storm/error/win/Codes.hpp b/storm/error/win/Codes.hpp deleted file mode 100644 index 8ba26e3..0000000 --- a/storm/error/win/Codes.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef STORM_ERROR_WIN_CODES_HPP -#define STORM_ERROR_WIN_CODES_HPP - -#include - -#endif diff --git a/storm/registry/Registry.hpp b/storm/registry/Registry.hpp new file mode 100644 index 0000000..ec10a77 --- /dev/null +++ b/storm/registry/Registry.hpp @@ -0,0 +1,27 @@ +#ifndef STORM_REGISTRY_REGISTRY_HPP +#define STORM_REGISTRY_REGISTRY_HPP + +#include + +#define STORM_REGISTRY_MAX_PATH 260 +#define STORM_REGISTRY_MAX_VALUE 16384 + +#define STORM_REGISTRY_BATTLENET 0x02 +#define STORM_REGISTRY_CURRENT_USER_ONLY 0x04 +#define STORM_REGISTRY_FLUSH_KEY 0x08 +#define STORM_REGISTRY_NO_BASE_KEY 0x10 + +#define STORM_REGISTRY_TYPE_STRING 1 +#define STORM_REGISTRY_TYPE_DWORD 4 + +int32_t SRegLoadString(const char* keyname, const char* valuename, uint32_t flags, char* buffer, uint32_t buffersize); + +int32_t SRegLoadValue(const char* keyname, const char* valuename, uint32_t flags, uint32_t* value); + +int32_t SRegSaveString(const char* keyname, const char* valuename, uint32_t flags, const char* string); + +int32_t SRegSaveValue(const char* keyname, const char* valuename, uint32_t flags, uint32_t value); + +void SRegDestroy(); + +#endif diff --git a/storm/registry/linux/Registry.cpp b/storm/registry/linux/Registry.cpp new file mode 100644 index 0000000..ee558a1 --- /dev/null +++ b/storm/registry/linux/Registry.cpp @@ -0,0 +1,373 @@ +#include "storm/registry/Registry.hpp" +#include "bc/file/Close.hpp" +#include "bc/file/Defines.hpp" +#include "bc/file/GetFileInfo.hpp" +#include "bc/file/GetPos.hpp" +#include "bc/file/Types.hpp" +#include "bc/memory/Storm.hpp" +#include "storm/String.hpp" +#include "storm/Error.hpp" +#include "storm/List.hpp" +#include "bc/File.hpp" +#include "bc/Memory.hpp" +#include "storm/Thread.hpp" + +#include +#include +#include +#include +#include + +struct RegistryEntry : public TSLinkedNode { + char* name = nullptr; + char* value = nullptr; + + bool ParseLine(const char* line) { + char buffer[STORM_MAX_PATH]; + auto b = buffer; + auto end = buffer + sizeof(buffer); + while (b < end && *line && *line != '=') { + *b++ = *line++; + } + if (*line++ != '=') { + return false; + } + if (b >= end) { + return false; + } + *b = '\0'; + this->name = SStrDupA(buffer, __FILE__, __LINE__); + + b = buffer; + while (b < end && *line) { + auto c = *line++; + if (c == '\\') { + if (*line == 'n') { + *b++ = '\n'; + } else if (*line == '\\') { + *b++ = '\\'; + } else { + *b++ = *line; + } + line++; + } else { + *b++ = c; + } + } + if (b >= end) { + return false; + } + *b = '\0'; + + this->value = SStrDupA(buffer, __FILE__, __LINE__); + + return true; + } + + ~RegistryEntry() { + if (this->name) { + SMemFree(this->name, __FILE__, __LINE__, 0x0); + } + + if (this->value) { + SMemFree(this->value, __FILE__, __LINE__, 0x0); + } + } +}; + +static int32_t s_initregistry = 0; +static STORM_LIST(RegistryEntry) s_registry; +static SCritSect s_lockregistry; + +Blizzard::File::StreamRecord* RegistryFileCreate(int32_t mode) { + const char* basedir = getenv("XDG_CONFIG_HOME"); + if (!basedir) { + basedir = getenv("HOME"); + if (!basedir) { + basedir = "."; + } + } + + char name[PATH_MAX]; + *name = '\0'; + SStrPack(name, basedir, sizeof(name)); + SStrPack(name, "/.whoa", sizeof(name)); + + if (!Blizzard::File::IsDirectory(name)) { + if (!Blizzard::File::CreateDirectory(name, false)) { + return nullptr; + } + } + + SStrPack(name, "/registry.txt", sizeof(name)); + + Blizzard::File::StreamRecord* file; + if (!Blizzard::File::Open(name, mode, file)) { + return nullptr; + } + return file; +} + +int32_t RegistryFileWriteEntry(Blizzard::File::StreamRecord* file, RegistryEntry* entry) { + char valuebuffer[STORM_REGISTRY_MAX_VALUE]; + auto source = reinterpret_cast(entry->value); + auto dest = reinterpret_cast(valuebuffer); + auto sourceend = source + SStrLen(entry->value); + auto destend = dest + sizeof(valuebuffer); + + while (dest < destend && source < sourceend) { + auto c = *source++; + if (c == '\n' && (dest+1) < destend) { + *dest++ = '\\'; + *dest++ = 'n'; + } else { + *dest++ = c; + } + } + valuebuffer[std::min(sizeof(valuebuffer)-1, static_cast(dest-valuebuffer))] = '\0'; + + char linebuffer[STORM_REGISTRY_MAX_PATH + STORM_REGISTRY_MAX_VALUE + 2]; + SStrPrintf(linebuffer, sizeof(linebuffer), "%s=%s\n", entry->name, valuebuffer); + + auto length = SStrLen(linebuffer); + return Blizzard::File::Write(file, linebuffer, length); +} + +int32_t RegistryFileReadLine(Blizzard::File::StreamRecord* file, char* buffer, int32_t buffersize) { + int64_t start; + if (!Blizzard::File::GetPos(file, start)) { + return false; + } + + auto size = static_cast(Blizzard::File::GetFileInfo(file)->size); + if (start == size) { + return 0; + } + + auto count = static_cast(std::min(static_cast(buffersize), size-start)); + if (!Blizzard::File::Read(file, buffer, &count)) { + return 0; + } + + auto p = buffer; + auto end = buffer + std::min(buffersize-1, count); + while (p < end && *p && *p != '\n') { + p++; + } + *p = '\0'; + + + auto nextline = start + static_cast(p-buffer) + 1; + + Blizzard::File::SetPos(file, nextline, BC_FILE_SEEK_START); + + return 1; +} + +void RegistryInit() { + s_initregistry = true; + + auto file = RegistryFileCreate(Blizzard::File::Mode::mustexist|Blizzard::File::Mode::read); + if (!file) { + return; + } + + char buffer[STORM_REGISTRY_MAX_PATH + STORM_REGISTRY_MAX_VALUE + 2]; + while (RegistryFileReadLine(file, buffer, sizeof(buffer))) { + if (*buffer) { + auto entry = s_registry.NewNode(1, 0, 0); + + if (!entry->ParseLine(buffer)) { + s_registry.DeleteNode(entry); + } + } + } + + Blizzard::File::Close(file); + +} + +void RegistryFlush(bool remove) { + using Mode = Blizzard::File::Mode; + auto file = RegistryFileCreate(Mode::create|Mode::write|Mode::truncate); + if (!file) { + return; + } + + auto entry = s_registry.Head(); + while (entry) { + auto curentry = entry; + if (!RegistryFileWriteEntry(file, curentry)) { + break; + } + entry = entry->Next(); + if (remove) { + s_registry.DeleteNode(curentry); + } + } + + Blizzard::File::Close(file); +} + +void RegistryShutdown() { + s_initregistry = false; + RegistryFlush(true); +} + +void BuildFullKeyName(const char* keyname, const char* valuename, char* buffer, uint32_t buffersize) { + char key[STORM_MAX_PATH]; + auto k = key; + auto keyend = (key + sizeof(key)) - 1; + while (k < keyend && *keyname) { + *k = *keyname == '\\' ? ':' : *keyname; + k++; + keyname++; + } + *k = '\0'; + SStrPrintf(buffer, buffersize, "%s:%s", key, valuename); +} + +int32_t InternalLoadEntry(const char* keyname, const char* valuename, uint32_t flags, char* buffer, uint32_t bytes, uint32_t* bytesread) { + s_lockregistry.Enter(); + if (!s_initregistry) { + RegistryInit(); + } + + char fullkeyname[STORM_MAX_PATH]; + *fullkeyname = '\0'; + *bytesread = 0; + + BuildFullKeyName(keyname, valuename, fullkeyname, STORM_MAX_PATH); + + for (auto entry = s_registry.Head(); entry; entry = entry->Next()) { + if (!SStrCmpI(fullkeyname, entry->name, STORM_MAX_PATH)) { + auto valuelength = static_cast(SStrLen(entry->value)); + *bytesread = std::min(valuelength, bytes); + SStrCopy(buffer, entry->value, bytes); + s_lockregistry.Leave(); + return 1; + } + } + s_lockregistry.Leave(); + + return 0; +} + +int32_t InternalSaveEntry(const char* keyname, const char* valuename, uint32_t flags, const char* value) { + s_lockregistry.Enter(); + + if (!s_initregistry) { + RegistryInit(); + } + + char fullkeyname[STORM_MAX_PATH]; + *fullkeyname = '\0'; + BuildFullKeyName(keyname, valuename, fullkeyname, STORM_MAX_PATH); + + auto entry = s_registry.Head(); + while (entry) { + if (!SStrCmpI(fullkeyname, entry->name, STORM_MAX_PATH)) { + auto curentry = entry; + entry = entry->Next(); + s_registry.DeleteNode(curentry); + } else { + entry = entry->Next(); + } + } + + entry = s_registry.NewNode(1, 0, 0); + if (!entry) { + return 0; + + } + + entry->name = SStrDupA(fullkeyname, __FILE__, __LINE__); + entry->value = SStrDupA(value, __FILE__, __LINE__); + + if (flags & STORM_REGISTRY_FLUSH_KEY) { + RegistryFlush(false); + } + + s_lockregistry.Leave(); + + return 1; +} + +int32_t SRegLoadString(const char* keyname, const char* valuename, uint32_t flags, char* buffer, uint32_t buffersize) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE(buffer); + STORM_VALIDATE_END; + + uint32_t bytesread; + if (!InternalLoadEntry(keyname, valuename, flags, buffer, buffersize, &bytesread)) { + return 0; + } + + buffer[std::min(buffersize - 1, bytesread)] = '\0'; + return 1; +} + +int32_t SRegLoadValue(const char* keyname, const char* valuename, uint32_t flags, uint32_t* value) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE(value); + STORM_VALIDATE_END; + + char valuestring[11]; + + uint32_t datatype; + uint32_t bytesread; + if (!InternalLoadEntry(keyname, valuename, flags, valuestring, sizeof(valuestring), &bytesread)) { + return 0; + } + + *value = SStrToUnsigned(valuestring); + return 1; +} + +int32_t SRegSaveString(const char* keyname, const char* valuename, uint32_t flags, const char* string) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE(string); + STORM_VALIDATE_END; + + if (!InternalSaveEntry(keyname, valuename, flags, string)) { + return 0; + } + + return 1; +} + +int32_t SRegSaveValue(const char* keyname, const char* valuename, uint32_t flags, uint32_t value) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE_END; + + char valuestring[11]; + SStrPrintf(valuestring, sizeof(valuestring), "%u", value); + + if (!InternalSaveEntry(keyname, valuename, flags, valuestring)) { + return 0; + } + + return 1; +} + +void SRegDestroy() { + s_lockregistry.Enter(); + RegistryShutdown(); + s_lockregistry.Leave(); +} diff --git a/storm/registry/mac/Registry.mm b/storm/registry/mac/Registry.mm new file mode 100644 index 0000000..af14ee5 --- /dev/null +++ b/storm/registry/mac/Registry.mm @@ -0,0 +1,161 @@ +#include "storm/Error.hpp" +#include "storm/String.hpp" +#include "storm/registry/Registry.hpp" +#include "storm/registry/mac/Static.hpp" +#include + +const char* NextComponent(const char* path, char* component, size_t size) { + auto sep = SStrChrR(path, '\\'); + if (!sep) { + return path + SStrCopy(component, path, size); + } else { + SStrNCopy(component, path, sep - path, size); + return sep + 1; + } +} + +bool GetDefaultsAndKeyPath(const char* key, const char* name, uint32_t flags, NSUserDefaults** defaults, char* path, size_t size) { + STORM_ASSERT(key); + STORM_ASSERT(*key); + + id nextcomponent = NextComponent(key, path, size); + + id prefix = @"com.blizzard"; + if ((flags & STORM_REGISTRY_BATTLENET)) { + prefix = @"net.battle"; + } + NSString* domain; + if (!size || *path) { + domain = [NSString stringWithFormat: @"%@.%s", prefix, path]; + } else { + domain = prefix; + } + + id sregstatic = SRegStatic::Get(); + id hive = sregstatic->hives[domain]; + *defaults = hive; + + if (hive == nullptr) { + auto bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; + if ([bundleIdentifier isEqualToString: domain]) { + hive = [NSUserDefaults standardUserDefaults]; + } else { + hive = [[NSUserDefaults alloc] initWithSuiteName:domain]; + } + *defaults = hive; + sregstatic->hives[domain] = hive; + } + + int32_t length; + + if (name && *name) { + if (key && *key) { + length = SStrPrintf(path, size, "%s/%s", key, name); + } else { + length = SStrCopy(path, name, size); + } + } else { + length = SStrCopy(path, key, size); + } + + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(length < size); + STORM_VALIDATE_END; + + for (id sep = path; ; *sep = '/') { + sep = SStrChr(sep, '\\'); + if (!sep) { + break; + } + } + + return false; +} + +NSObject* GetObject(const char* keyname, const char* valuename, uint32_t flags) { + NSUserDefaults* defaults; + char path[STORM_MAX_PATH]; + if (!GetDefaultsAndKeyPath(keyname, valuename, flags, &defaults, path, STORM_MAX_PATH)) { + return nil; + } + + id string = [NSString stringWithUTF8String: path]; + return [defaults objectForKey: string]; +} + +bool SetObject(const char* key, const char* name, uint32_t flags, NSObject* object) { + NSUserDefaults* defaults + char path[STORM_MAX_PATH]; + if (!GetDefaultsAndKeyPath(keyname, valuename, flags, &defaults, path, STORM_MAX_PATH)) { + return false; + } + id string = [NSString stringWithUTF8String:path]; + [defaults setObject:object forKey:string]; + return true; +} + +int32_t SRegLoadString(const char* keyname, const char* valuename, uint32_t flags, char* buffer, uint32_t buffersize) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE(buffer); + STORM_VALIDATE_END; + + @autoreleasepool { + id string = GetObject(keyname, valuename, flags); + if ([string isKindOfClass:[NSString class]) { + return [string getCString buffer:buffer maxLength:buffersize encoding:NSUTF8StringEncoding]; + } + } + + return 0; +} + +int32_t SRegLoadValue(const char* keyname, const char* valuename, uint32_t flags, uint32_t* value) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE(value); + STORM_VALIDATE_END; + + @autoreleasepool { + id num = GetObject(keyname, valuename, flags); + if ([num isKindOfClass:[NSNumber class]) { + *value = [num unsignedIntValue]; + return 1; + } + } + + return 0; +} + +int32_t SRegSaveString(const char* keyname, const char* valuename, uint32_t flags, const char* string) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE(string); + STORM_VALIDATE_END; + + @autoreleasepool { + return SetObject(keyname, valuename, flags, [NSString stringWithUTF8String:value]); + } +} + +int32_t SRegSaveValue(const char* keyname, const char* valuename, uint32_t flags, uint32_t value) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE_END; + + @autoreleasepool { + return SetObject(keyname, valuename, flags, [NSNumber numberWithUnsignedInt:value]); + } +} diff --git a/storm/registry/mac/Static.hpp b/storm/registry/mac/Static.hpp new file mode 100644 index 0000000..74ea8c1 --- /dev/null +++ b/storm/registry/mac/Static.hpp @@ -0,0 +1,21 @@ +#ifndef STORM_REGISTRY_MAC_STATIC_HPP +#define STORM_REGISTRY_MAC_STATIC_HPP + +#import + +class SRegStatic { +public: + NSMutableDictionary* hives; + + static SRegStatic Get(); + + SRegStatic() { + this->hives = [NSMutableDictionary init]; + } + + ~SRegStatic() { + this->hives = nil; + } +}; + +#endif diff --git a/storm/registry/mac/Static.mm b/storm/registry/mac/Static.mm new file mode 100644 index 0000000..8e60e20 --- /dev/null +++ b/storm/registry/mac/Static.mm @@ -0,0 +1,14 @@ +#include "storm/registry/mac/Static.hpp" + +static SRegStatic* SRegStatic::Get() { + static SRegStatic sregstatic; + return &sregstatic; +} + +SRegStatic::SRegStatic() { + this->hives = [[NSMutableDictionary alloc] init]; +} + +SRegStatic::~SRegStatic() { + this->hives = nil; +} diff --git a/storm/registry/win/Registry.cpp b/storm/registry/win/Registry.cpp new file mode 100644 index 0000000..906de9c --- /dev/null +++ b/storm/registry/win/Registry.cpp @@ -0,0 +1,225 @@ + +#include "storm/registry/Registry.hpp" +#include "storm/Error.hpp" +#include "storm/String.hpp" +#include "storm/Unicode.hpp" +#include "storm/option/Options.hpp" +#include +#include +#include + +int32_t ILoadValue(HKEY parentKey, const char* subKeyName, const char* valuename, uint32_t* datatype, uint8_t* buffer, uint32_t bytes, uint32_t* bytesread) { + int32_t result; + uint16_t wname[STORM_MAX_PATH]; + HKEY key; + + if (g_opt.sregunicode) { + SUniConvertUTF8to16(wname, STORM_MAX_PATH, reinterpret_cast(subKeyName), STORM_MAX_STR, nullptr, nullptr); + result = RegOpenKeyExW(parentKey, reinterpret_cast(wname), 0, KEY_EXECUTE, &key); + if (result == ERROR_SUCCESS) { + *bytesread = bytes; + SUniConvertUTF8to16(wname, STORM_MAX_PATH, reinterpret_cast(valuename), STORM_MAX_STR, nullptr, nullptr); + auto value = RegQueryValueExW(key, reinterpret_cast(wname), nullptr, reinterpret_cast(datatype), reinterpret_cast(buffer), reinterpret_cast(bytesread)); + RegCloseKey(key); + return value; + } + } else { + result = RegOpenKeyExA(parentKey, reinterpret_cast(subKeyName), 0, KEY_EXECUTE, &key); + if (result == ERROR_SUCCESS) { + *bytesread = bytes; + auto value = RegQueryValueExA(key, reinterpret_cast(valuename), nullptr, reinterpret_cast(datatype), reinterpret_cast(buffer), reinterpret_cast(bytesread)); + RegCloseKey(key); + return value; + } + } + + return result; +} + +int32_t SRegGetBaseKey(uint8_t flags, char* buffer, uint32_t buffersize) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(buffer); + STORM_VALIDATE(buffersize); + STORM_VALIDATE_END; + + if (flags & STORM_REGISTRY_BATTLENET) { + SStrCopy(buffer, "Software\\Battle.net\\", STORM_MAX_PATH); + } else { + SStrCopy(buffer, "Software\\Blizzard Entertainment\\", STORM_MAX_PATH); + } + + return 1; +} + +void BuildFullKeyName(const char* keyname, uint8_t flags, char* buffer, uint32_t buffersize) { + *buffer = '\0'; + if (!(flags & STORM_REGISTRY_NO_BASE_KEY)) { + SRegGetBaseKey(flags, buffer, buffersize); + } + SStrPack(buffer, keyname, buffersize); +} + +int32_t InternalLoadEntry(const char* keyname, const char* valuename, uint32_t flags, uint32_t* datatype, uint8_t* buffer, uint32_t bytes, uint32_t* bytesread) { + char fullkeyname[STORM_MAX_PATH]; + *fullkeyname = '\0'; + *bytesread = 0; + *datatype = 0; + + BuildFullKeyName(keyname, flags, fullkeyname, STORM_MAX_PATH); + + auto result = ILoadValue(flags & STORM_REGISTRY_CURRENT_USER_ONLY ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, fullkeyname, valuename, datatype, buffer, bytes, bytesread); + if (result) { + SetLastError(result); + return 0; + } + + return 1; +} + +int32_t InternalSaveEntry(const char* keyname, const char* valuename, uint32_t flags, uint32_t datatype, const uint8_t* buffer, uint32_t bytes) { + char fullkeyname[STORM_MAX_PATH]; + *fullkeyname = '\0'; + BuildFullKeyName(keyname, flags, fullkeyname, STORM_MAX_PATH); + + HKEY key; + DWORD disposition; + int32_t result; + uint16_t wname[STORM_MAX_PATH] = {0}; + + if (g_opt.sregunicode) { + SUniConvertUTF8to16(wname, STORM_MAX_PATH, reinterpret_cast(fullkeyname), STORM_MAX_STR, nullptr, nullptr); + result = RegCreateKeyExW(flags & STORM_REGISTRY_CURRENT_USER_ONLY ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, reinterpret_cast(wname), 0, nullptr, 0, KEY_WRITE, nullptr, &key, &disposition); + } else { + result = RegCreateKeyExA(flags & STORM_REGISTRY_CURRENT_USER_ONLY ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, reinterpret_cast(fullkeyname), 0, nullptr, 0, KEY_WRITE, nullptr, &key, &disposition); + } + + if (result) { + SetLastError(result); + return 0; + } + + if (g_opt.sregunicode) { + SUniConvertUTF8to16(wname, STORM_MAX_PATH, reinterpret_cast(valuename), STORM_MAX_STR, nullptr, nullptr); + result = RegSetValueExW(key, reinterpret_cast(wname), 0, datatype, reinterpret_cast(buffer), bytes); + } else { + result = RegSetValueExA(key, reinterpret_cast(valuename), 0, datatype, reinterpret_cast(buffer), bytes); + } + if (!result && (flags & STORM_REGISTRY_FLUSH_KEY) != 0) { + result = RegFlushKey(key); + } + + RegCloseKey(key); + if (result) { + SetLastError(result); + return 0; + } + return 1; +} + +int32_t SRegLoadString(const char* keyname, const char* valuename, uint32_t flags, char* buffer, uint32_t buffersize) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE(buffer); + STORM_VALIDATE_END; + + uint32_t datatype; + uint16_t widecharstr[4096]; + uint32_t bytesread; + + if (g_opt.sregunicode) { + if (!InternalLoadEntry(keyname, valuename, flags, &datatype, reinterpret_cast(&widecharstr), sizeof(widecharstr), &bytesread)) { + return 0; + } + + if (datatype == REG_SZ) { + uint32_t dstchars; + SUniConvertUTF16to8(reinterpret_cast(buffer), buffersize, widecharstr, bytesread / 2, &dstchars, nullptr); + buffer[std::min(buffersize - 1, dstchars)] = '\0'; + return 1; + } else if (datatype == REG_DWORD) { + SStrPrintf(buffer, buffersize, "%u", (reinterpret_cast(widecharstr))[0]); + return 1; + } + } else { + if (!InternalLoadEntry(keyname, valuename, flags, &datatype, reinterpret_cast(buffer), buffersize, &bytesread)) { + return 0; + } + + if (datatype == REG_SZ) { + buffer[std::min(buffersize - 1, bytesread)] = '\0'; + return 1; + } else if (datatype == REG_DWORD) { + SStrPrintf(buffer, buffersize, "%u", (reinterpret_cast(buffer))[0]); + return 1; + } + } + + return 0; +} + +int32_t SRegLoadValue(const char* keyname, const char* valuename, uint32_t flags, uint32_t* value) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE(value); + STORM_VALIDATE_END; + + uint8_t buffer[256]; + uint32_t bytesread; + uint32_t datatype; + if (!InternalLoadEntry(keyname, valuename, flags, &datatype, buffer, sizeof(buffer), &bytesread)) { + return 0; + } + + if (datatype == REG_SZ) { + *value = strtoul(reinterpret_cast(buffer), 0, 0); + } else if (datatype == REG_DWORD) { + *value = *reinterpret_cast(buffer); + } + + return 1; +} + +int32_t SRegSaveString(const char* keyname, const char* valuename, uint32_t flags, const char* string) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE(string); + STORM_VALIDATE_END; + + if (g_opt.sregunicode) { + uint16_t widecharstr[STORM_MAX_PATH]; + SUniConvertUTF8to16(widecharstr, STORM_MAX_PATH, reinterpret_cast(string), STORM_MAX_STR, nullptr, nullptr); + // ersatz wcslen() + uint32_t length; + auto p = widecharstr; + while (*p) { + ++p; + } + length = p - widecharstr; + return InternalSaveEntry(keyname, valuename, flags, REG_SZ, reinterpret_cast(widecharstr), (length + 1) * 2); + } else { + return InternalSaveEntry(keyname, valuename, flags, REG_SZ, reinterpret_cast(string), SStrLen(string) + 1); + } +} + +int32_t SRegSaveValue(const char* keyname, const char* valuename, uint32_t flags, uint32_t value) { + STORM_VALIDATE_BEGIN; + STORM_VALIDATE(keyname); + STORM_VALIDATE(*keyname); + STORM_VALIDATE(valuename); + STORM_VALIDATE(*valuename); + STORM_VALIDATE_END; + + return InternalSaveEntry(keyname, valuename, flags, REG_DWORD, reinterpret_cast(&value), 4); +} + +void SRegDestroy() { +}