Making string struct a bit more simple and support only utf8.
Still just ascii for now but a bit easier to manager. Use [..]A() functions from win api to not have to convert to wide char everywhere
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core/Common/CoreUtils.h>
|
||||||
#include <Core/Memory/Utils.h>
|
#include <Core/Memory/Utils.h>
|
||||||
|
|
||||||
namespace Juliet
|
namespace Juliet
|
||||||
@@ -6,21 +8,31 @@ namespace Juliet
|
|||||||
#define ConstString(str) { const_cast<char*>((str)), sizeof(str) - 1 }
|
#define ConstString(str) { const_cast<char*>((str)), sizeof(str) - 1 }
|
||||||
#define CStr(str) ((str).Data)
|
#define CStr(str) ((str).Data)
|
||||||
#define InplaceString(name, size) \
|
#define InplaceString(name, size) \
|
||||||
char name##_[size]; \
|
char name##_[size]; \
|
||||||
String name = { name##_, sizeof(name##_) }
|
MemSet(name##_, 0, sizeof(uint32)); \
|
||||||
|
String name = { name##_, 0 }
|
||||||
|
|
||||||
|
// Everything is Little Endian
|
||||||
|
enum class StringEncoding : uint8
|
||||||
|
{
|
||||||
|
Unknown = 0,
|
||||||
|
ASCII,
|
||||||
|
LATIN1,
|
||||||
|
UTF8,
|
||||||
|
UTF16,
|
||||||
|
UTF32,
|
||||||
|
UCS2,
|
||||||
|
UCS4,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Represents a UTF-8 String.
|
||||||
|
// Not null terminated.
|
||||||
struct String
|
struct String
|
||||||
{
|
{
|
||||||
char* Data;
|
char* Data;
|
||||||
size_t Size;
|
size_t Size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct String16
|
|
||||||
{
|
|
||||||
char16_t* Data;
|
|
||||||
size_t Size;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline size_t StringLength(String str)
|
inline size_t StringLength(String str)
|
||||||
{
|
{
|
||||||
return str.Size;
|
return str.Size;
|
||||||
@@ -28,16 +40,20 @@ namespace Juliet
|
|||||||
|
|
||||||
inline size_t StringLength(const char* str)
|
inline size_t StringLength(const char* str)
|
||||||
{
|
{
|
||||||
size_t counter = 0;
|
size_t length = 0;
|
||||||
if (str)
|
if (str)
|
||||||
{
|
{
|
||||||
while (*str++)
|
while (char ch = *str)
|
||||||
{
|
{
|
||||||
++counter;
|
if ((ch & 0xC0) != 0x80)
|
||||||
|
{
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
++str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return counter;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool StringIsValid(String str)
|
inline bool StringIsValid(String str)
|
||||||
@@ -101,14 +117,20 @@ namespace Juliet
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int8 StringCompareCaseInsensitive(String str1, String str2)
|
// Case insensitive compare. Supports ASCII only
|
||||||
{
|
// TODO: Support UNICODE
|
||||||
return 0;
|
extern JULIET_API int8 StringCompareCaseInsensitive(String str1, String str2);
|
||||||
}
|
|
||||||
|
|
||||||
// Do not allocate anything, you must allocate your out buffer yourself
|
// Do not allocate anything, you must allocate your out buffer yourself
|
||||||
// TODO: Version taking arena that can allocate
|
// TODO: Version taking arena that can allocate
|
||||||
extern JULIET_API bool ConvertString(String from, String to, String in, String& out);
|
// Do not take String type because we dont know the string encoding we are going from/to
|
||||||
|
// src and dst will be casted based on the encoding.
|
||||||
|
// size will correspond to the number of characters
|
||||||
|
// Will convert \0 character if present.
|
||||||
|
extern JULIET_API bool ConvertString(StringEncoding from, StringEncoding to, const char* src, size_t srcLen,
|
||||||
|
char*& dst, size_t& dstLen, size_t dstCapacity);
|
||||||
|
extern JULIET_API bool ConvertString(String from, String to, const char* src, size_t srcLen, char*& dst,
|
||||||
|
size_t& dstLen, size_t dstCapacity);
|
||||||
|
|
||||||
} // namespace Juliet
|
} // namespace Juliet
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <Core/Common/CoreTypes.h>
|
||||||
|
|
||||||
#define ArraySize(array) (sizeof(array) / sizeof(array[0]))
|
#define ArraySize(array) (sizeof(array) / sizeof(array[0]))
|
||||||
|
|
||||||
namespace Juliet
|
namespace Juliet
|
||||||
{
|
{
|
||||||
inline int32 MemCompare(const void* leftValue, const void* rightValue, size_t size)
|
inline int32 MemCompare(const void* leftValue, const void* rightValue, size_t size)
|
||||||
{
|
{
|
||||||
const unsigned char* left = static_cast<const unsigned char*>(leftValue);
|
auto left = static_cast<const unsigned char*>(leftValue);
|
||||||
const unsigned char* right = static_cast<const unsigned char*>(rightValue);
|
auto right = static_cast<const unsigned char*>(rightValue);
|
||||||
while (size && *left == *right)
|
while (size && *left == *right)
|
||||||
{
|
{
|
||||||
++left;
|
++left;
|
||||||
@@ -15,4 +17,6 @@ namespace Juliet
|
|||||||
}
|
}
|
||||||
return size ? *left - *right : 0;
|
return size ? *left - *right : 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
#define MemSet memset
|
||||||
|
} // namespace Juliet
|
||||||
|
|||||||
@@ -7,82 +7,345 @@ namespace Juliet
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
enum class Encoding : uint8
|
constexpr char kUnknown_ASCII = '?';
|
||||||
{
|
constexpr int32 kUnknown_UNICODE = 0xFFFD;
|
||||||
Unknown = 0,
|
|
||||||
ASCII,
|
|
||||||
LATIN1,
|
|
||||||
UTF8,
|
|
||||||
UTF16,
|
|
||||||
UTF32,
|
|
||||||
UCS2,
|
|
||||||
UCS4,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
const char* name;
|
String Name;
|
||||||
Encoding format;
|
StringEncoding Format;
|
||||||
} Encodings[] = {
|
} Encodings[] = {
|
||||||
/* *INDENT-OFF* */ // clang-format off
|
/* *INDENT-OFF* */ // clang-format off
|
||||||
{ "ASCII", Encoding::ASCII },
|
{ ConstString("ASCII"), StringEncoding::ASCII },
|
||||||
{ "US-ASCII", Encoding::ASCII },
|
{ ConstString("US-ASCII"), StringEncoding::ASCII },
|
||||||
{ "8859-1", Encoding::LATIN1 },
|
{ ConstString("8859-1"), StringEncoding::LATIN1 },
|
||||||
{ "ISO-8859-1", Encoding::LATIN1 },
|
{ ConstString("ISO-8859-1"), StringEncoding::LATIN1 },
|
||||||
#if defined(JULIET_WIN32)
|
#if defined(JULIET_WIN32)
|
||||||
{ "WCHAR_T", Encoding::UTF16 },
|
{ ConstString("WCHAR_T"), StringEncoding::UTF16 },
|
||||||
#else
|
#else
|
||||||
{ "WCHAR_T", Encoding::UCS4 },
|
{ ConstString("WCHAR_T"), StringEncoding::UCS4 },
|
||||||
#endif
|
#endif
|
||||||
{ "UTF8", Encoding::UTF8 },
|
{ ConstString("UTF8"), StringEncoding::UTF8 },
|
||||||
{ "UTF-8", Encoding::UTF8 },
|
{ ConstString("UTF-8"), StringEncoding::UTF8 },
|
||||||
{ "UTF16", Encoding::UTF16 },
|
{ ConstString("UTF16"), StringEncoding::UTF16 },
|
||||||
{ "UTF-16", Encoding::UTF16 },
|
{ ConstString("UTF-16"), StringEncoding::UTF16 },
|
||||||
{ "UTF32", Encoding::UTF32 },
|
{ ConstString("UTF32"), StringEncoding::UTF32 },
|
||||||
{ "UTF-32", Encoding::UTF32 },
|
{ ConstString("UTF-32"), StringEncoding::UTF32 },
|
||||||
{ "UCS2", Encoding::UCS2 },
|
{ ConstString("UCS2"), StringEncoding::UCS2 },
|
||||||
{ "UCS-2", Encoding::UCS2 },
|
{ ConstString("UCS-2"), StringEncoding::UCS2 },
|
||||||
{ "UCS-2-INTERNAL", Encoding::UCS2 },
|
{ ConstString("UCS-2-INTERNAL"), StringEncoding::UCS2 },
|
||||||
{ "UCS4", Encoding::UCS4 },
|
{ ConstString("UCS4"), StringEncoding::UCS4 },
|
||||||
{ "UCS-4", Encoding::UCS4 },
|
{ ConstString("UCS-4"), StringEncoding::UCS4 },
|
||||||
{ "UCS-4-INTERNAL", Encoding::UCS4 },
|
{ ConstString("UCS-4-INTERNAL"), StringEncoding::UCS4 },
|
||||||
/* *INDENT-ON* */ // clang-format on
|
/* *INDENT-ON* */ // clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Returns the number of codepoint case folded (lowercase equivalent in the language)
|
||||||
|
// Takes an UTF-8 codepoint (uint32) and codefold it to up to 3 uint32
|
||||||
|
// TODO Supports more than low ASCI :)
|
||||||
|
int8 CaseFoldUnicode(uint32 from, uint32* to)
|
||||||
|
{
|
||||||
|
if (from < 128)
|
||||||
|
{
|
||||||
|
// low-ASCII, easy!
|
||||||
|
if ((from >= 'A') && (from <= 'Z'))
|
||||||
|
{
|
||||||
|
*to = 'a' + (from - 'A');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*to = from;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Step(String& inStr, size_t byteToStep)
|
||||||
|
{
|
||||||
|
Assert(inStr.Size >= byteToStep);
|
||||||
|
|
||||||
|
inStr.Data += byteToStep;
|
||||||
|
inStr.Size -= byteToStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 StepUTF8(String& inStr, size_t byteToRead)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* From rfc3629, the UTF-8 spec:
|
||||||
|
* https://www.ietf.org/rfc/rfc3629.txt
|
||||||
|
*
|
||||||
|
* Char. number range | UTF-8 octet sequence
|
||||||
|
* (hexadecimal) | (binary)
|
||||||
|
* --------------------+---------------------------------------------
|
||||||
|
* 0000 0000-0000 007F | 0xxxxxxx
|
||||||
|
* 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
|
||||||
|
* 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
|
||||||
|
* 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||||
|
*/
|
||||||
|
|
||||||
|
// If string is empty then it's done.
|
||||||
|
if (inStr.Size == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto str = reinterpret_cast<const uint8*>(CStr(inStr));
|
||||||
|
const uint32 octet = byteToRead ? *str : 0;
|
||||||
|
|
||||||
|
if (octet == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((octet & 0x80) == 0x0) // One byte code point
|
||||||
|
{
|
||||||
|
Step(inStr, 1);
|
||||||
|
return octet;
|
||||||
|
}
|
||||||
|
Assert(false && "StepUTF8 only supports one byte codepoints for now");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool ConvertString(String from, String to, String in, String& out)
|
int8 StringCompareCaseInsensitive(String str1, String str2)
|
||||||
|
{
|
||||||
|
// TODO: Support UTF8. For now ASCII only.
|
||||||
|
uint32 left = 0;
|
||||||
|
uint32 right = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
uint32 leftFolded[3];
|
||||||
|
int8 num_folded = CaseFoldUnicode(StepUTF8(str1, 4), leftFolded);
|
||||||
|
Assert(num_folded == 1); // Only one uint32 codepoint supported for now (low ascii)
|
||||||
|
left = leftFolded[0];
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint32 rightFolded[3];
|
||||||
|
int8 num_folded = CaseFoldUnicode(StepUTF8(str2, 4), rightFolded);
|
||||||
|
Assert(num_folded == 1); // Only one uint32 codepoint supported for now (low ascii)
|
||||||
|
right = rightFolded[0];
|
||||||
|
}
|
||||||
|
if (left < right)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (left > right)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (left == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertString(StringEncoding from, StringEncoding to, const char* src, size_t srcLen, char*& dst, size_t& dstLen, size_t dstCapacity)
|
||||||
|
{
|
||||||
|
Assert(src && *src);
|
||||||
|
|
||||||
|
const char* srcStr = src;
|
||||||
|
char* dstStr = dst;
|
||||||
|
|
||||||
|
uint32 character = 0;
|
||||||
|
while (srcLen > 0)
|
||||||
|
{
|
||||||
|
// Decode in character
|
||||||
|
switch (from)
|
||||||
|
{
|
||||||
|
case StringEncoding::UTF8: // Uses RFC 3629
|
||||||
|
{
|
||||||
|
auto p = reinterpret_cast<const uint8*>(srcStr);
|
||||||
|
size_t left = 0;
|
||||||
|
bool overlong = false;
|
||||||
|
if (p[0] >= 0xF0)
|
||||||
|
{
|
||||||
|
if ((p[0] & 0xF8) != 0xF0)
|
||||||
|
{
|
||||||
|
character = kUnknown_UNICODE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (p[0] == 0xF0 && srcLen > 1 && (p[1] & 0xF0) == 0x80)
|
||||||
|
{
|
||||||
|
overlong = true;
|
||||||
|
}
|
||||||
|
character = static_cast<uint32>(p[0] & 0x07);
|
||||||
|
left = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (p[0] >= 0xE0)
|
||||||
|
{
|
||||||
|
if ((p[0] & 0xF0) != 0xE0)
|
||||||
|
{
|
||||||
|
character = kUnknown_UNICODE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (p[0] == 0xE0 && srcLen > 1 && (p[1] & 0xE0) == 0x80)
|
||||||
|
{
|
||||||
|
overlong = true;
|
||||||
|
}
|
||||||
|
character = static_cast<uint32>(p[0] & 0x0F);
|
||||||
|
left = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (p[0] >= 0xC0)
|
||||||
|
{
|
||||||
|
if ((p[0] & 0xE0) != 0xC0)
|
||||||
|
{
|
||||||
|
character = kUnknown_UNICODE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((p[0] & 0xDE) == 0xC0)
|
||||||
|
{
|
||||||
|
overlong = true;
|
||||||
|
}
|
||||||
|
character = static_cast<uint32>(p[0] & 0x1F);
|
||||||
|
left = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (p[0] & 0x80)
|
||||||
|
{
|
||||||
|
character = kUnknown_UNICODE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
character = static_cast<uint32>(p[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++srcStr;
|
||||||
|
--srcLen;
|
||||||
|
if (srcLen < left)
|
||||||
|
{
|
||||||
|
Log(LogLevel::Error, LogCategory::Core, "ConvertString: Failed to convert string. Incomplete input sequence");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (left--)
|
||||||
|
{
|
||||||
|
++p;
|
||||||
|
if ((p[0] & 0xC0) != 0x80)
|
||||||
|
{
|
||||||
|
character = kUnknown_UNICODE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
character <<= 6;
|
||||||
|
character |= (p[0] & 0x3F);
|
||||||
|
++srcStr;
|
||||||
|
--srcLen;
|
||||||
|
}
|
||||||
|
if (overlong)
|
||||||
|
{
|
||||||
|
character = kUnknown_UNICODE;
|
||||||
|
}
|
||||||
|
if ((character >= 0xD800 && character <= 0xDFFF) || (character == 0xFFFE || character == 0xFFFF) ||
|
||||||
|
character > 0x10FFFF)
|
||||||
|
{
|
||||||
|
character = kUnknown_UNICODE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case StringEncoding::Unknown: Assert(false && "ConvertString: Invalid Source Format: Unknown"); break;
|
||||||
|
case StringEncoding::ASCII:
|
||||||
|
case StringEncoding::LATIN1:
|
||||||
|
case StringEncoding::UTF16:
|
||||||
|
case StringEncoding::UTF32:
|
||||||
|
case StringEncoding::UCS2:
|
||||||
|
case StringEncoding::UCS4: Assert(false && "ConvertString: Unsupported Source Format"); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode out character
|
||||||
|
switch (to)
|
||||||
|
{
|
||||||
|
case StringEncoding::UTF16: // RFC 2781
|
||||||
|
{
|
||||||
|
auto p = reinterpret_cast<uint8*>(dstStr);
|
||||||
|
if (character > 0x10FFFF)
|
||||||
|
{
|
||||||
|
character = kUnknown_UNICODE;
|
||||||
|
}
|
||||||
|
if (character < 0x10000)
|
||||||
|
{
|
||||||
|
if (dstCapacity < 2)
|
||||||
|
{
|
||||||
|
Log(LogLevel::Error, LogCategory::Core, "ConvertString: Destination buffer too short to fit UTF16");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
p[1] = static_cast<uint8>(character >> 8);
|
||||||
|
p[0] = static_cast<uint8>(character);
|
||||||
|
|
||||||
|
dstStr += 2;
|
||||||
|
dstLen += 1;
|
||||||
|
dstCapacity -= 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dstCapacity < 4)
|
||||||
|
{
|
||||||
|
Log(LogLevel::Error, LogCategory::Core, "ConvertString: Destination buffer too short to fit UTF16");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
character = character - 0x10000;
|
||||||
|
uint16 word1 = 0xD800 | static_cast<uint16>((character >> 10) & 0x3FF);
|
||||||
|
uint16 word2 = 0xDC00 | static_cast<uint16>(character & 0x3FF);
|
||||||
|
p[1] = static_cast<uint8>(word1 >> 8);
|
||||||
|
p[0] = static_cast<uint8>(word1);
|
||||||
|
p[3] = static_cast<uint8>(word2 >> 8);
|
||||||
|
p[2] = static_cast<uint8>(word2);
|
||||||
|
|
||||||
|
dstStr += 4;
|
||||||
|
dstLen += 1;
|
||||||
|
dstCapacity -= 4;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case StringEncoding::Unknown: Assert(false && "ConvertString: Invalid Source Format: Unknown"); break;
|
||||||
|
case StringEncoding::ASCII:
|
||||||
|
case StringEncoding::LATIN1:
|
||||||
|
case StringEncoding::UTF8:
|
||||||
|
case StringEncoding::UTF32:
|
||||||
|
case StringEncoding::UCS2:
|
||||||
|
case StringEncoding::UCS4: Assert(false && "ConvertString: Unsupported Destination Format"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConvertString(String from, String to, const char* src, size_t srcSize, char*& dst, size_t& dstSize, size_t dstCapacity)
|
||||||
{
|
{
|
||||||
Assert(in.Size <= out.Size);
|
|
||||||
Assert(StringIsValid(from));
|
Assert(StringIsValid(from));
|
||||||
Assert(StringIsValid(to));
|
Assert(StringIsValid(to));
|
||||||
Assert(StringIsValid(in));
|
|
||||||
|
|
||||||
for (size_t idx = 0; idx < ArraySize(Encodings); ++idx)
|
// First find the encoding of the strings
|
||||||
{
|
auto sourceFormat = StringEncoding::Unknown;
|
||||||
|
auto destFormat = StringEncoding::Unknown;
|
||||||
|
for (auto& encoding : Encodings)
|
||||||
|
{
|
||||||
|
if (StringCompareCaseInsensitive(from, encoding.Name) == 0)
|
||||||
|
{
|
||||||
|
sourceFormat = encoding.Format;
|
||||||
|
if (destFormat != StringEncoding::Unknown)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StringCompareCaseInsensitive(to, encoding.Name) == 0)
|
||||||
|
{
|
||||||
|
destFormat = encoding.Format;
|
||||||
|
if (sourceFormat != StringEncoding::Unknown)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
if (sourceFormat == StringEncoding::Unknown || destFormat == StringEncoding::Unknown)
|
||||||
// for (i = 0; i < SDL_arraysize(encodings); ++i) {
|
{
|
||||||
// if (SDL_strcasecmp(fromcode, encodings[i].name) == 0) {
|
return false;
|
||||||
// src_fmt = encodings[i].format;
|
}
|
||||||
// if (dst_fmt != ENCODING_UNKNOWN) {
|
|
||||||
// break;
|
return ConvertString(sourceFormat, destFormat, src, srcSize, dst, dstSize, dstCapacity);
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (SDL_strcasecmp(tocode, encodings[i].name) == 0) {
|
|
||||||
// dst_fmt = encodings[i].format;
|
|
||||||
// if (src_fmt != ENCODING_UNKNOWN) {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if (src_fmt != ENCODING_UNKNOWN && dst_fmt != ENCODING_UNKNOWN) {
|
|
||||||
// SDL_iconv_t cd = (SDL_iconv_t)SDL_malloc(sizeof(*cd));
|
|
||||||
// if (cd) {
|
|
||||||
// cd->src_fmt = src_fmt;
|
|
||||||
// cd->dst_fmt = dst_fmt;
|
|
||||||
// return cd;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
} // namespace Juliet
|
} // namespace Juliet
|
||||||
|
|||||||
@@ -134,10 +134,10 @@ namespace Juliet::Win32
|
|||||||
{
|
{
|
||||||
uint8 peekedMessageCount = 0;
|
uint8 peekedMessageCount = 0;
|
||||||
MSG message = {};
|
MSG message = {};
|
||||||
while (PeekMessage(&message, nullptr, 0, 0, PM_REMOVE))
|
while (PeekMessageA(&message, nullptr, 0, 0, PM_REMOVE))
|
||||||
{
|
{
|
||||||
TranslateMessage(&message);
|
TranslateMessage(&message);
|
||||||
DispatchMessage(&message);
|
DispatchMessageA(&message);
|
||||||
|
|
||||||
// Since we peek at all messages of the program, it's possible that it stall here so we limit the number of peeked messages to an arbitrary limit
|
// Since we peek at all messages of the program, it's possible that it stall here so we limit the number of peeked messages to an arbitrary limit
|
||||||
if (++peekedMessageCount > kPeekMessageLimit)
|
if (++peekedMessageCount > kPeekMessageLimit)
|
||||||
@@ -155,7 +155,7 @@ namespace Juliet::Win32
|
|||||||
auto* windowState = GetWindowStateFromHandle(handle);
|
auto* windowState = GetWindowStateFromHandle(handle);
|
||||||
if (!windowState)
|
if (!windowState)
|
||||||
{
|
{
|
||||||
return CallWindowProc(DefWindowProc, handle, message, wParam, lParam);
|
return CallWindowProcA(DefWindowProcA, handle, message, wParam, lParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (message)
|
switch (message)
|
||||||
@@ -267,6 +267,6 @@ namespace Juliet::Win32
|
|||||||
return returnCode;
|
return returnCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return CallWindowProc(DefWindowProc, handle, message, wParam, lParam);
|
return CallWindowProcA(DefWindowProcA, handle, message, wParam, lParam);
|
||||||
}
|
}
|
||||||
} // namespace Juliet::Win32
|
} // namespace Juliet::Win32
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ namespace Juliet::Win32
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr auto WindowClassName = L"JulietWindowClass";
|
constexpr auto WindowClassName = "JulietWindowClass";
|
||||||
constexpr LPCWSTR WindowClassPtr = WindowClassName;
|
constexpr LPCSTR WindowClassPtr = WindowClassName;
|
||||||
|
|
||||||
bool SetupWindowState(NonNullPtr<DisplayDevice> self, NonNullPtr<Window> window, HWND handle)
|
bool SetupWindowState(NonNullPtr<DisplayDevice> self, NonNullPtr<Window> window, HWND handle)
|
||||||
{
|
{
|
||||||
@@ -46,7 +46,7 @@ namespace Juliet::Win32
|
|||||||
HINSTANCE instance = GetModuleHandle(nullptr);
|
HINSTANCE instance = GetModuleHandle(nullptr);
|
||||||
|
|
||||||
// TODO : Put outside, we should not create a new class for each new window
|
// TODO : Put outside, we should not create a new class for each new window
|
||||||
WNDCLASSEX WindowClass = {};
|
WNDCLASSEXA WindowClass = {};
|
||||||
WindowClass.cbSize = sizeof(WNDCLASSEX);
|
WindowClass.cbSize = sizeof(WNDCLASSEX);
|
||||||
WindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
WindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
||||||
WindowClass.lpfnWndProc = Win32MainWindowCallback;
|
WindowClass.lpfnWndProc = Win32MainWindowCallback;
|
||||||
@@ -54,7 +54,7 @@ namespace Juliet::Win32
|
|||||||
WindowClass.hCursor = LoadCursor(0, IDC_ARROW);
|
WindowClass.hCursor = LoadCursor(0, IDC_ARROW);
|
||||||
WindowClass.hbrBackground = static_cast<HBRUSH>(GetStockObject(LTGRAY_BRUSH));
|
WindowClass.hbrBackground = static_cast<HBRUSH>(GetStockObject(LTGRAY_BRUSH));
|
||||||
WindowClass.lpszClassName = WindowClassName;
|
WindowClass.lpszClassName = WindowClassName;
|
||||||
if (!RegisterClassEx(&WindowClass))
|
if (!RegisterClassExA(&WindowClass))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -64,8 +64,8 @@ namespace Juliet::Win32
|
|||||||
|
|
||||||
int x = CW_USEDEFAULT, y = CW_USEDEFAULT;
|
int x = CW_USEDEFAULT, y = CW_USEDEFAULT;
|
||||||
const int w = window->Width, h = window->Height;
|
const int w = window->Width, h = window->Height;
|
||||||
HWND handle = CreateWindowEx(styleEx, WindowClassPtr, L"JULIET TODO PASS TITLE", style, x, y, w, h, nullptr,
|
HWND handle = CreateWindowExA(styleEx, WindowClassPtr, "JULIET TODO PASS TITLE", style, x, y, w, h, nullptr,
|
||||||
nullptr, instance, nullptr);
|
nullptr, instance, nullptr);
|
||||||
|
|
||||||
PumpEvents(self);
|
PumpEvents(self);
|
||||||
|
|
||||||
@@ -97,69 +97,4 @@ namespace Juliet::Win32
|
|||||||
auto& win32State = reinterpret_cast<Window32State&>(*window->State);
|
auto& win32State = reinterpret_cast<Window32State&>(*window->State);
|
||||||
::ShowWindow(win32State.Handle, SW_HIDE);
|
::ShowWindow(win32State.Handle, SW_HIDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
LRESULT CALLBACK Win32MainWindowCallback(HWND Window, UINT Message, WPARAM WParam, LPARAM LParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateOSWindow(WindowState& state, uint16 width, uint16 height)
|
|
||||||
{
|
|
||||||
auto& win32State = reinterpret_cast<Window32State&>(state);
|
|
||||||
|
|
||||||
HINSTANCE Instance = GetModuleHandle(0);
|
|
||||||
|
|
||||||
WNDCLASSA WindowClass = {};
|
|
||||||
WindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
|
||||||
WindowClass.lpfnWndProc = Win32MainWindowCallback;
|
|
||||||
WindowClass.hInstance = Instance;
|
|
||||||
WindowClass.hCursor = LoadCursor(0, IDC_ARROW);
|
|
||||||
WindowClass.hbrBackground = static_cast<HBRUSH>(GetStockObject(LTGRAY_BRUSH));
|
|
||||||
WindowClass.lpszClassName = WindowClassName;
|
|
||||||
|
|
||||||
if (RegisterClassA(&WindowClass))
|
|
||||||
{
|
|
||||||
HWND handle = CreateWindowExA(0, WindowClass.lpszClassName, "Juliet", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
|
|
||||||
CW_USEDEFAULT, width, height, 0, 0, Instance, 0);
|
|
||||||
if (handle)
|
|
||||||
{
|
|
||||||
win32State.Handle = handle;
|
|
||||||
SetWindowLongPtr(win32State.Handle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(&win32State));
|
|
||||||
ShowWindow(handle, SW_SHOW);
|
|
||||||
win32State.IsOpen = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert(false);
|
|
||||||
// Win32ErrorMessage(PlatformError_Fatal,
|
|
||||||
// "Unable to open game window.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert(false);
|
|
||||||
// Win32ErrorMessage(PlatformError_Fatal,
|
|
||||||
// "Unable to register game window handle.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DestroyOSWindow(WindowState& state)
|
|
||||||
{
|
|
||||||
auto& win32State = reinterpret_cast<Window32State&>(state);
|
|
||||||
::DestroyWindow(win32State.Handle);
|
|
||||||
UnregisterClassA(WindowClassName, ::GetModuleHandle(nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateOSWindowState(WindowState& state)
|
|
||||||
{
|
|
||||||
auto& win32State = reinterpret_cast<Window32State&>(state);
|
|
||||||
MSG msg = {};
|
|
||||||
while (::PeekMessage(&msg, win32State.Handle, 0, 0, PM_REMOVE))
|
|
||||||
{
|
|
||||||
::TranslateMessage(&msg);
|
|
||||||
::DispatchMessage(&msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} // namespace Juliet::Win32
|
} // namespace Juliet::Win32
|
||||||
|
|||||||
@@ -5,38 +5,6 @@
|
|||||||
|
|
||||||
namespace Juliet
|
namespace Juliet
|
||||||
{
|
{
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// TODO : Move into string file
|
|
||||||
// Use portable code + pass the memory array into parameter and not use new
|
|
||||||
// This is from http://www.rohitab.com/discuss/topic/41257-char-to-lpcwstr/
|
|
||||||
static wchar_t* UTF8ToWideChar(const char* utf8)
|
|
||||||
{
|
|
||||||
wchar_t* w;
|
|
||||||
|
|
||||||
int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, 0, 0);
|
|
||||||
if (len <= 0)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
w = new wchar_t[len];
|
|
||||||
|
|
||||||
if (!w)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MultiByteToWideChar(CP_UTF8, 0, utf8, -1, w, len) <= 0)
|
|
||||||
{
|
|
||||||
delete[] w;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
DynamicLibrary* LoadDynamicLibrary(const char* filename)
|
DynamicLibrary* LoadDynamicLibrary(const char* filename)
|
||||||
{
|
{
|
||||||
if (!filename)
|
if (!filename)
|
||||||
@@ -45,9 +13,7 @@ namespace Juliet
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
LPWSTR wstr = UTF8ToWideChar(filename);
|
HMODULE handle = LoadLibraryA(filename);
|
||||||
HMODULE handle = LoadLibraryW(wstr);
|
|
||||||
delete[] wstr;
|
|
||||||
|
|
||||||
// Generate an error message if all loads failed
|
// Generate an error message if all loads failed
|
||||||
if (!handle)
|
if (!handle)
|
||||||
|
|||||||
@@ -7,43 +7,13 @@
|
|||||||
|
|
||||||
namespace Juliet::Platform
|
namespace Juliet::Platform
|
||||||
{
|
{
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// TODO : Move into string file
|
|
||||||
// Use portable code + pass the memory array into parameter and not use new
|
|
||||||
// From: https://stackoverflow.com/questions/215963/how-do-you-properly-use-widechartomultibyte
|
|
||||||
char* WideCharToUTF8(char16_t* wcharStr)
|
|
||||||
{
|
|
||||||
char* result = nullptr;
|
|
||||||
size_t length = WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPCWCH>(wcharStr), -1, nullptr, 0, nullptr, nullptr);
|
|
||||||
if (length <= 0)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = new char[length];
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPCWCH>(wcharStr), -1, result, length, nullptr, nullptr) <= 0)
|
|
||||||
{
|
|
||||||
delete[] result;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
String GetBasePath()
|
String GetBasePath()
|
||||||
{
|
{
|
||||||
// Allocate a buffer that could fit the module size.
|
// Allocate a buffer that could fit the module size.
|
||||||
// Max Path is a good start but could be bigger if the path include long path prefix
|
// Max Path is a good start but could be bigger if the path include long path prefix
|
||||||
String16 buffer{ .Data = nullptr, .Size = MAX_PATH };
|
size_t bufferSize=MAX_PATH;
|
||||||
buffer.Data = static_cast<char16_t*>(Calloc(MAX_PATH, sizeof(WCHAR)));
|
auto buffer = static_cast<char*>(Calloc(MAX_PATH, sizeof(char)));
|
||||||
if (buffer.Data == nullptr)
|
if (buffer == nullptr)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -51,14 +21,13 @@ namespace Juliet::Platform
|
|||||||
size_t moduleFilenameLength = 0;
|
size_t moduleFilenameLength = 0;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
moduleFilenameLength =
|
moduleFilenameLength = GetModuleFileNameA(nullptr, buffer, static_cast<uint32>(bufferSize));
|
||||||
GetModuleFileNameW(nullptr, reinterpret_cast<LPWSTR>(buffer.Data), static_cast<uint32>(buffer.Size));
|
|
||||||
|
|
||||||
// If the module filename length is bigger than the buffer size, we need to reallocate a bigger buffer
|
// If the module filename length is bigger than the buffer size, we need to reallocate a bigger buffer
|
||||||
if (moduleFilenameLength >= buffer.Size - 1)
|
if (moduleFilenameLength >= bufferSize - 1)
|
||||||
{
|
{
|
||||||
buffer.Size *= 2;
|
bufferSize *= 2;
|
||||||
buffer.Data = static_cast<char16_t*>(Realloc(buffer.Data, buffer.Size * sizeof(WCHAR)));
|
buffer = static_cast<char*>(Realloc(buffer, bufferSize * sizeof(char)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -68,26 +37,23 @@ namespace Juliet::Platform
|
|||||||
|
|
||||||
if (moduleFilenameLength == 0)
|
if (moduleFilenameLength == 0)
|
||||||
{
|
{
|
||||||
SafeFree(buffer.Data);
|
SafeFree(buffer);
|
||||||
Log(LogLevel::Error, LogCategory::Core, "Filesystem: Cannot locate executable path");
|
Log(LogLevel::Error, LogCategory::Core, "Filesystem: Cannot locate executable path");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
for (idx = moduleFilenameLength - 1; idx > 0; --idx)
|
for (idx = moduleFilenameLength - 1; idx > 0; --idx)
|
||||||
{
|
{
|
||||||
if (buffer.Data[idx] == '\\')
|
if (buffer[idx] == '\\')
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert(idx > 0 && "Path is not absolute!");
|
Assert(idx > 0 && "Path is not absolute!");
|
||||||
buffer.Data[idx + 1] = '\0'; // Chop chop
|
buffer[idx + 1] = '\0'; // Chop chop
|
||||||
|
|
||||||
// TODO: Add utils to Convert to/from UTF8W
|
return WrapString(buffer);
|
||||||
char* basePath = WideCharToUTF8(buffer.Data);
|
|
||||||
SafeFree(buffer.Data);
|
|
||||||
|
|
||||||
return WrapString(basePath);
|
|
||||||
}
|
}
|
||||||
} // namespace Juliet::Platform
|
} // namespace Juliet::Platform
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ namespace Juliet::Internal
|
|||||||
{
|
{
|
||||||
IOStream* IOFromFile(String filename, String mode)
|
IOStream* IOFromFile(String filename, String mode)
|
||||||
{
|
{
|
||||||
HANDLE hFile;
|
|
||||||
|
|
||||||
// "r" = reading, file must exist
|
// "r" = reading, file must exist
|
||||||
// "w" = writing, truncate existing, file may not exist
|
// "w" = writing, truncate existing, file may not exist
|
||||||
// "r+"= reading or writing, file must exist
|
// "r+"= reading or writing, file must exist
|
||||||
@@ -49,18 +47,11 @@ namespace Juliet::Internal
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent opening a dialog box when file doesnt exits (windows does that)
|
HANDLE hFile = CreateFileA(CStr(filename), (canWrite | canRead), (canWrite) ? 0 : FILE_SHARE_READ, nullptr,
|
||||||
// old_error_mode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
|
(openExisting | createAlways | openAlways), FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
if (FAILED(hFile))
|
||||||
{
|
{
|
||||||
// LPWSTR str = WIN_UTF8ToStringW(filename);
|
Log(LogLevel::Error, LogCategory::Core, "IOFromFile: CreateFileW failed");
|
||||||
// h = CreateFileW(str,
|
|
||||||
// (w_right | r_right),
|
|
||||||
// (w_right) ? 0 : FILE_SHARE_READ,
|
|
||||||
// NULL,
|
|
||||||
// (must_exist | truncate | a_mode),
|
|
||||||
// FILE_ATTRIBUTE_NORMAL,
|
|
||||||
// NULL);
|
|
||||||
// SDL_free(str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ void JulietApplication::Init()
|
|||||||
{
|
{
|
||||||
Log(LogLevel::Message, LogCategory::Editor, "Initializing Juliet Application...");
|
Log(LogLevel::Message, LogCategory::Editor, "Initializing Juliet Application...");
|
||||||
|
|
||||||
Log(LogLevel::Message, LogCategory::Editor, "%s", GetBasePath());
|
Log(LogLevel::Message, LogCategory::Editor, "%s", CStr(GetBasePath()));
|
||||||
|
|
||||||
GraphicsConfig config;
|
GraphicsConfig config;
|
||||||
GraphicsDevice = CreateGraphicsDevice(config);
|
GraphicsDevice = CreateGraphicsDevice(config);
|
||||||
|
|||||||
@@ -44,6 +44,6 @@ void Compile()
|
|||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
auto* stream = IOFromFile(ConstString("bleeblou"), ConstString("w"));
|
auto* stream = IOFromFile(ConstString("XF"), ConstString("w"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user