diff --git a/src/Client/Client.vcxproj b/src/Client/Client.vcxproj index 998ff917f..370023f3d 100644 --- a/src/Client/Client.vcxproj +++ b/src/Client/Client.vcxproj @@ -294,12 +294,11 @@ - + - @@ -309,7 +308,6 @@ - diff --git a/src/Client/Client.vcxproj.filters b/src/Client/Client.vcxproj.filters index 08fcdc428..da24920dc 100644 --- a/src/Client/Client.vcxproj.filters +++ b/src/Client/Client.vcxproj.filters @@ -347,9 +347,6 @@ - - Source Files\Platform - Source Files\Utils @@ -521,9 +518,6 @@ Source Files\Platform - - Source Files\Platform - Source Files\Rendering @@ -533,14 +527,14 @@ Source Files\Platform - - Source Files\Platform - Source Files\Audio Source Files\Audio + + Source Files\Platform + \ No newline at end of file diff --git a/src/Client/Lighting.c b/src/Client/Lighting.c index 29e23a2db..c25cfcbca 100644 --- a/src/Client/Lighting.c +++ b/src/Client/Lighting.c @@ -94,6 +94,9 @@ void Lighting_Refresh(void) { } +/*########################################################################################################################* +*----------------------------------------------------Lighting update------------------------------------------------------* +*#########################################################################################################################*/ static void Lighting_UpdateLighting(Int32 x, Int32 y, Int32 z, BlockID oldBlock, BlockID newBlock, Int32 index, Int32 lightH) { bool didBlock = Block_BlocksLight[oldBlock]; bool nowBlocks = Block_BlocksLight[newBlock]; @@ -128,7 +131,6 @@ static void Lighting_UpdateLighting(Int32 x, Int32 y, Int32 z, BlockID oldBlock, } } - static bool Lighting_Needs(BlockID block, BlockID other) { return Block_Draw[block] != DRAW_OPAQUE || Block_Draw[other] != DRAW_GAS; } @@ -217,6 +219,9 @@ void Lighting_OnBlockChanged(Int32 x, Int32 y, Int32 z, BlockID oldBlock, BlockI } +/*########################################################################################################################* +*---------------------------------------------------Lighting heightmap----------------------------------------------------* +*#########################################################################################################################*/ static Int32 Lighting_InitialHeightmapCoverage(Int32 x1, Int32 z1, Int32 xCount, Int32 zCount, Int32* skip) { Int32 elemsLeft = 0, index = 0, curRunCount = 0; Int32 x, z; @@ -301,7 +306,6 @@ static void Lighting_FinishHeightmapCoverage(Int32 x1, Int32 z1, Int32 xCount, I } } - void Lighting_LightHint(Int32 startX, Int32 startZ) { Int32 x1 = max(startX, 0), x2 = min(World_Width, startX + 18); Int32 z1 = max(startZ, 0), z2 = min(World_Length, startZ + 18); @@ -315,6 +319,9 @@ void Lighting_LightHint(Int32 startX, Int32 startZ) { } +/*########################################################################################################################* +*---------------------------------------------------Lighting component----------------------------------------------------* +*#########################################################################################################################*/ static void Lighting_Init(void) { Event_RegisterInt(&WorldEvents_EnvVarChanged, NULL, &Lighting_EnvVariableChanged); Lighting_SetSun(WorldEnv_DefaultSunCol); @@ -341,7 +348,6 @@ static void Lighting_Free(void) { Lighting_Reset(); } - void Lighting_MakeComponent(struct IGameComponent* comp) { comp->Init = Lighting_Init; comp->Free = Lighting_Free; diff --git a/src/Client/NixPlatform.c b/src/Client/NixPlatform.c deleted file mode 100644 index 2e0745285..000000000 --- a/src/Client/NixPlatform.c +++ /dev/null @@ -1,380 +0,0 @@ -#include "Platform.h" -#if CC_BUILD_NIX -#include "PackedCol.h" -#include "Drawer2D.h" -#include "Stream.h" -#include "DisplayDevice.h" -#include "Funcs.h" -#include "ErrorHandler.h" -#include "Constants.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define UNIX_EPOCH 62135596800 - -UChar* Platform_NewLine = "\n"; -UChar Directory_Separator = '/'; -ReturnCode ReturnCode_FileShareViolation = 1000000000; /* TODO: not used apparently */ -ReturnCode ReturnCode_FileNotFound = ENOENT; -ReturnCode ReturnCode_NotSupported = EPERM; -ReturnCode ReturnCode_InvalidArg = EINVAL; - -void Platform_UnicodeExpand(void* dstPtr, STRING_PURE String* src) { - if (src->length > FILENAME_SIZE) ErrorHandler_Fail("String too long to expand"); - UInt8* dst = dstPtr; - - Int32 i; - for (i = 0; i < src->length; i++) { - UInt16 codepoint = Convert_CP437ToUnicode(src->buffer[i]); - Int32 len = Stream_WriteUtf8(dst, codepoint); dst += len; - } - *dst = '\0'; -} - -static void Platform_InitDisplay(void) { - Display* display = XOpenDisplay(NULL); - if (!display) ErrorHandler_Fail("Failed to open display"); - - int screen = XDefaultScreen(display); - Window rootWin = XRootWindow(display, screen); - - /* TODO: Use Xinerama and XRandR for querying these */ - struct DisplayDevice device = { 0 }; - device.Bounds.Width = DisplayWidth(display, screen); - device.Bounds.Height = DisplayHeight(display, screen); - device.BitsPerPixel = DefaultDepth(display, screen); - DisplayDevice_Default = device; - - DisplayDevice_Meta[0] = display; - DisplayDevice_Meta[1] = screen; - DisplayDevice_Meta[2] = rootWin; -} - -pthread_mutex_t event_mutex; -void Platform_Init(void) { - Platform_InitDisplay(); - pthread_mutex_init(&event_mutex, NULL); -} - -void Platform_Free(void) { - pthread_mutex_destroy(&event_mutex); -} - -void Platform_Exit(ReturnCode code) { exit(code); } - -STRING_PURE String Platform_GetCommandLineArgs(void) { - /* TODO: Implement this */ - return String_MakeNull(); -} - -/* TODO: Not duplicate with windows*/ -static void Platform_AllocFailed(const UChar* place) { - UChar logBuffer[String_BufferSize(STRING_SIZE + 20)]; - String log = String_InitAndClearArray(logBuffer); - String_Format1(&log, "Failed allocating memory for: %c", place); - ErrorHandler_Fail(&log.buffer); -} - -void* Mem_Alloc(UInt32 numElems, UInt32 elemsSize, const UChar* place) { - void* ptr = malloc(numElems * elemsSize); /* TODO: avoid overflow here */ - if (!ptr) Platform_AllocFailed(place); - return ptr; -} - -void* Mem_AllocCleared(UInt32 numElems, UInt32 elemsSize, const UChar* place) { - void* ptr = calloc(numElems, elemsSize); /* TODO: avoid overflow here */ - if (!ptr) Platform_AllocFailed(place); - return ptr; -} - -void* Mem_Realloc(void* mem, UInt32 numElems, UInt32 elemsSize, const UChar* place) { - void* ptr = realloc(mem, numElems * elemsSize); /* TODO: avoid overflow here */ - if (!ptr) Platform_AllocFailed(place); - return ptr; -} - -void Mem_Free(void** mem) { - if (mem == NULL || *mem == NULL) return; - free(*mem); - *mem = NULL; -} - -void Mem_Set(void* dst, UInt8 value, UInt32 numBytes) { - memset(dst, value, numBytes); -} - -void Mem_Copy(void* dst, void* src, UInt32 numBytes) { - memcpy(dst, src, numBytes); -} - -void Platform_Log(STRING_PURE String* message) { puts(message->buffer); } -void Platform_LogConst(const UChar* message) { puts(message); } -void Platform_Log4(const UChar* format, const void* a1, const void* a2, const void* a3, const void* a4) { - UChar msgBuffer[String_BufferSize(512)]; - String msg = String_InitAndClearArray(msgBuffer); - String_Format4(&msg, format, a1, a2, a3, a4); - Platform_Log(&msg); -} - -void Platform_FromSysTime(DateTime* time, struct tm* sysTime) { - time->Year = sysTime->tm_year + 1900; - time->Month = sysTime->tm_mon + 1; - time->Day = sysTime->tm_mday; - time->Hour = sysTime->tm_hour; - time->Minute = sysTime->tm_min; - time->Second = sysTime->tm_sec; - time->Milli = 0; -} - -void DateTime_CurrentUTC(DateTime* time_) { - struct timeval cur; struct tm utc_time; - gettimeofday(&cur, NULL); - time_->Milli = cur.tv_usec / 1000; - - gmtime_r(&cur.tv_sec, &utc_time); - Platform_FromSysTime(time_, &utc_time); -} - -void DateTime_CurrentLocal(DateTime* time_) { - struct timeval cur; struct tm loc_time; - gettimeofday(&cur, NULL); - time_->Milli = cur.tv_usec / 1000; - - localtime_r(&cur.tv_sec, &loc_time); - Platform_FromSysTime(time_, &loc_time); -} - -bool Directory_Exists(STRING_PURE String* path) { - UInt8 data[1024]; Platform_UnicodeExpand(data, path); - struct stat sb; - return stat(data, &sb) == 0 && S_ISDIR(sb.st_mode); -} - -ReturnCode Directory_Create(STRING_PURE String* path) { - UInt8 data[1024]; Platform_UnicodeExpand(data, path); - /* read/write/search permissions for owner and group, and with read/search permissions for others. */ - /* TODO: Is the default mode in all cases */ - return mkdir(data, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1 ? errno : 0; -} - -bool File_Exists(STRING_PURE String* path) { - UInt8 data[1024]; Platform_UnicodeExpand(data, path); - struct stat sb; - return stat(data, &sb) == 0 && S_ISREG(sb.st_mode); -} - -ReturnCode Directory_Enum(STRING_PURE String* path, void* obj, Directory_EnumCallback callback) { - UInt8 data[1024]; Platform_UnicodeExpand(data, path); - DIR* dirPtr = opendir(data); - if (!dirPtr) return errno; - - UInt8 fileBuffer[String_BufferSize(FILENAME_SIZE)]; - String file = String_InitAndClearArray(fileBuffer); - struct dirent* entry; - - /* TODO: does this also include subdirectories */ - while (entry = readdir(dirPtr)) { - UInt16 len = String_CalcLen(entry->d_name, UInt16_MaxValue); - String_Clear(&file); - String_DecodeUtf8(&file, entry->d_name, len); - - Utils_UNSAFE_GetFilename(&file); - callback(&file, obj); - } - - int result = errno; /* return code from readdir */ - closedir(dirPtr); - return result; -} - -ReturnCode File_GetModifiedTime(STRING_PURE String* path, DateTime* time) { - UInt8 data[1024]; Platform_UnicodeExpand(data, path); - struct stat sb; - if (stat(data, &sb) == -1) return errno; - - DateTime_FromTotalMs(time, UNIX_EPOCH + sb.st_mtime); - return 0; -} - -ReturnCode File_Do(void** file, STRING_PURE String* path, int mode) { - UInt8 data[1024]; Platform_UnicodeExpand(data, path); - *file = open(data, mode); - return *file == -1 ? errno : 0; -} - -ReturnCode File_Open(void** file, STRING_PURE String* path) { - return File_Do(file, path, O_RDONLY); -} -ReturnCode File_Create(void** file, STRING_PURE String* path) { - return File_Do(file, path, O_WRONLY | O_CREAT | O_TRUNC); -} -ReturnCode File_Append(void** file, STRING_PURE String* path) { - ReturnCode code = File_Do(file, path, O_WRONLY | O_CREAT); - if (code != 0) return code; - return File_Seek(*file, 0, STREAM_SEEKFROM_END); -} - -ReturnCode File_Read(void* file, UInt8* buffer, UInt32 count, UInt32* bytesRead) { - ssize_t bytes = read((int)file, buffer, count); - if (bytes == -1) { *bytesRead = 0; return errno; } - *bytesRead = bytes; return 0; -} - -ReturnCode File_Write(void* file, UInt8* buffer, UInt32 count, UInt32* bytesWrote) { - ssize_t bytes = write((int)file, buffer, count); - if (bytes == -1) { *bytesWrote = 0; return errno; } - *bytesWrote = bytes; return 0; -} - -ReturnCode File_Close(void* file) { - return close((int)file) == -1 ? errno : 0; -} - -ReturnCode File_Seek(void* file, Int32 offset, Int32 seekType) { - int mode = -1; - switch (seekType) { - case STREAM_SEEKFROM_BEGIN: mode = SEEK_SET; break; - case STREAM_SEEKFROM_CURRENT: mode = SEEK_CUR; break; - case STREAM_SEEKFROM_END: mode = SEEK_END; break; - } - - return lseek((int)file, offset, mode) == -1 ? errno : 0; -} - -ReturnCode File_Position(void* file, UInt32* position) { - off_t pos = lseek((int)file, 0, SEEK_CUR); - if (pos == -1) { *position = -1; return errno; } - *position = pos; return 0; -} - -ReturnCode File_Length(void* file, UInt32* length) { - struct stat st; - if (fstat((int)file, &st) == -1) { *length = -1; return errno; } - *length = st.st_size; return 0; -} - -void Thread_Sleep(UInt32 milliseconds) { - usleep(milliseconds * 1000); -} - -void* Thread_StartCallback(void* lpParam) { - Thread_StartFunc* func = (Thread_StartFunc*)lpParam; - (*func)(); - return NULL; -} - -pthread_t threadList[3]; Int32 threadIndex; -void* Thread_Start(Thread_StartFunc* func) { - if (threadIndex == Array_Elems(threadList)) ErrorHandler_Fail("Cannot allocate thread"); - pthread_t* ptr = &threadList[threadIndex]; - int result = pthread_create(ptr, NULL, Thread_StartCallback, func); - - ErrorHandler_CheckOrFail(result, "Creating thread"); - threadIndex++; return ptr; -} - -void Thread_Join(void* handle) { - int result = pthread_join(*((pthread_t*)handle), NULL); - ErrorHandler_CheckOrFail(result, "Joining thread"); -} - -void Thread_FreeHandle(void* handle) { - int result = pthread_detach(*((pthread_t*)handle)); - ErrorHandler_CheckOrFail(result, "Detaching thread"); -} - -pthread_mutex_t mutexList[3]; Int32 mutexIndex; -void* Mutex_Create(void) { - if (mutexIndex == Array_Elems(mutexList)) ErrorHandler_Fail("Cannot allocate mutex"); - pthread_mutex_t* ptr = &mutexList[mutexIndex]; - int result = pthread_mutex_init(ptr, NULL); - - ErrorHandler_CheckOrFail(result, "Creating mutex"); - mutexIndex++; return ptr; -} - -void Mutex_Free(void* handle) { - int result = pthread_mutex_destroy((pthread_mutex_t*)handle); - ErrorHandler_CheckOrFail(result, "Destroying mutex"); -} - -void Mutex_Lock(void* handle) { - int result = pthread_mutex_lock((pthread_mutex_t*)handle); - ErrorHandler_CheckOrFail(result, "Locking mutex"); -} - -void Mutex_Unlock(void* handle) { - int result = pthread_mutex_unlock((pthread_mutex_t*)handle); - ErrorHandler_CheckOrFail(result, "Unlocking mutex"); -} - -pthread_cond_t condList[2]; Int32 condIndex; -void* Waitable_Create(void) { - if (condIndex == Array_Elems(condList)) ErrorHandler_Fail("Cannot allocate event"); - pthread_cond_t* ptr = &condList[condIndex]; - int result = pthread_cond_init(ptr, NULL); - - ErrorHandler_CheckOrFail(result, "Creating event"); - condIndex++; return ptr; -} - -void Waitable_Free(void* handle) { - int result = pthread_cond_destroy((pthread_cond_t*)handle); - ErrorHandler_CheckOrFail(result, "Destroying event"); -} - -void Waitable_Signal(void* handle) { - int result = pthread_cond_signal((pthread_cond_t*)handle); - ErrorHandler_CheckOrFail(result, "Signalling event"); -} - -void Waitable_Wait(void* handle) { - int result = pthread_cond_wait((pthread_cond_t*)handle, &event_mutex); - ErrorHandler_CheckOrFail(result, "Waiting event"); -} - -void Stopwatch_Measure(struct Stopwatch* timer) { - struct timespec value; - /* TODO: CLOCK_MONOTONIC_RAW ?? */ - clock_gettime(CLOCK_MONOTONIC, &value); - timer->Data[0] = value.tv_sec; - timer->Data[1] = value.tv_nsec; -} -void Stopwatch_Start(struct Stopwatch* timer) { Stopwatch_Measure(timer); } - -/* TODO: check this is actually accurate */ -Int32 Stopwatch_ElapsedMicroseconds(struct Stopwatch* timer) { - Int64 startS = timer->Data[0], startNS = timer->Data[1]; - Stopwatch_Measure(timer); - Int64 endS = timer->Data[0], endNS = timer->Data[1]; - - #define NS_PER_SEC 1000000000LL - Int64 elapsedNS = ((endS - startS) * NS_PER_SEC + endNS) - startNS; - return elapsedNS / 1000; -} - -/* TODO: Implement these stubs */ -void Font_Make(struct FontDesc* desc, STRING_PURE String* fontName, UInt16 size, UInt16 style) { desc->Size = size; } -void Font_Free(struct FontDesc* desc) { } -struct Size2D Platform_TextMeasure(struct DrawTextArgs* args) { } -void Platform_SetBitmap(struct Bitmap* bmp) { } -struct Size2D Platform_TextDraw(struct DrawTextArgs* args, Int32 x, Int32 y, PackedCol col) { } -void Platform_ReleaseBitmap(void) { } - -/* TODO: Implement these stubs */ -void Http_Init(void) { } -ReturnCode Http_MakeRequest(struct AsyncRequest* request, void** handle) { return 1; } -ReturnCode Http_GetRequestHeaders(struct AsyncRequest* request, void* handle, UInt32* size) { return 1; } -ReturnCode Http_GetRequestData(struct AsyncRequest* request, void* handle, void** data, UInt32 size, volatile Int32* progress) { return 1; } -ReturnCode Http_FreeRequest(void* handle) { return 1; } -ReturnCode Http_Free(void) { return 1; } -#endif diff --git a/src/Client/Platform.c b/src/Client/Platform.c new file mode 100644 index 000000000..0570c5523 --- /dev/null +++ b/src/Client/Platform.c @@ -0,0 +1,1176 @@ +#include "Platform.h" +#include "ErrorHandler.h" +#include "Stream.h" +#include "DisplayDevice.h" +#include "ExtMath.h" +#include "ErrorHandler.h" +#include "Drawer2D.h" +#include "Funcs.h" +#include "AsyncDownloader.h" + +#if CC_BUILD_WIN +#define WIN32_LEAN_AND_MEAN +#define NOSERVICE +#define NOMCX +#define NOIME +#define _WIN32_WINNT 0x0500 +#include +#include +#include +#include +#include + +#define HTTP_QUERY_ETAG 54 /* Missing from some old MingW32 headers */ +#define Socket__Error() WSAGetLastError() + +HDC hdc; +HANDLE heap; +UChar* Platform_NewLine = "\r\n"; +UChar Directory_Separator = '\\'; + +ReturnCode ReturnCode_FileShareViolation = ERROR_SHARING_VIOLATION; +ReturnCode ReturnCode_FileNotFound = ERROR_FILE_NOT_FOUND; +ReturnCode ReturnCode_NotSupported = ERROR_NOT_SUPPORTED; +ReturnCode ReturnCode_InvalidArg = ERROR_INVALID_PARAMETER; +ReturnCode ReturnCode_SocketInProgess = WSAEINPROGRESS; +ReturnCode ReturnCode_SocketWouldBlock = WSAEWOULDBLOCK; +#elif CC_BUILD_NIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define UNIX_EPOCH 62135596800 +#define Socket__Error() errno + +UChar* Platform_NewLine = "\n"; +UChar Directory_Separator = '/'; +ReturnCode ReturnCode_FileShareViolation = 1000000000; /* TODO: not used apparently */ +ReturnCode ReturnCode_FileNotFound = ENOENT; +ReturnCode ReturnCode_NotSupported = EPERM; +ReturnCode ReturnCode_InvalidArg = EINVAL; +ReturnCode ReturnCode_SocketInProgess = EINPROGRESS; +ReturnCode ReturnCode_SocketWouldBlock = EWOULDBLOCK; +#endif + + +/*########################################################################################################################* +*---------------------------------------------------------Memory----------------------------------------------------------* +*#########################################################################################################################*/ +static void Platform_AllocFailed(const UChar* place) { + UChar logBuffer[String_BufferSize(STRING_SIZE + 20)]; + String log = String_InitAndClearArray(logBuffer); + String_Format1(&log, "Failed allocating memory for: %c", place); + ErrorHandler_Fail(log.buffer); +} + +void Mem_Set(void* dst, UInt8 value, UInt32 numBytes) { memset(dst, value, numBytes); } +void Mem_Copy(void* dst, void* src, UInt32 numBytes) { memcpy(dst, src, numBytes); } + +#if CC_BUILD_WIN +void* Mem_Alloc(UInt32 numElems, UInt32 elemsSize, const UChar* place) { + UInt32 numBytes = numElems * elemsSize; /* TODO: avoid overflow here */ + void* ptr = HeapAlloc(heap, 0, numBytes); + if (!ptr) Platform_AllocFailed(place); + return ptr; +} + +void* Mem_AllocCleared(UInt32 numElems, UInt32 elemsSize, const UChar* place) { + UInt32 numBytes = numElems * elemsSize; /* TODO: avoid overflow here */ + void* ptr = HeapAlloc(heap, HEAP_ZERO_MEMORY, numBytes); + if (!ptr) Platform_AllocFailed(place); + return ptr; +} + +void* Mem_Realloc(void* mem, UInt32 numElems, UInt32 elemsSize, const UChar* place) { + UInt32 numBytes = numElems * elemsSize; /* TODO: avoid overflow here */ + void* ptr = HeapReAlloc(heap, 0, mem, numBytes); + if (!ptr) Platform_AllocFailed(place); + return ptr; +} + +void Mem_Free(void** mem) { + if (mem == NULL || *mem == NULL) return; + HeapFree(heap, 0, *mem); + *mem = NULL; +} +#elif CC_BUILD_NIX +void* Mem_Alloc(UInt32 numElems, UInt32 elemsSize, const UChar* place) { + void* ptr = malloc(numElems * elemsSize); /* TODO: avoid overflow here */ + if (!ptr) Platform_AllocFailed(place); + return ptr; +} + +void* Mem_AllocCleared(UInt32 numElems, UInt32 elemsSize, const UChar* place) { + void* ptr = calloc(numElems, elemsSize); /* TODO: avoid overflow here */ + if (!ptr) Platform_AllocFailed(place); + return ptr; +} + +void* Mem_Realloc(void* mem, UInt32 numElems, UInt32 elemsSize, const UChar* place) { + void* ptr = realloc(mem, numElems * elemsSize); /* TODO: avoid overflow here */ + if (!ptr) Platform_AllocFailed(place); + return ptr; +} + +void Mem_Free(void** mem) { + if (mem == NULL || *mem == NULL) return; + free(*mem); + *mem = NULL; +} +#endif + + +/*########################################################################################################################* +*------------------------------------------------------Logging/Time-------------------------------------------------------* +*#########################################################################################################################*/ +void Platform_Log1(const UChar* format, const void* a1) { + Platform_Log4(format, a1, NULL, NULL, NULL); +} +void Platform_Log2(const UChar* format, const void* a1, const void* a2) { + Platform_Log4(format, a1, a2, NULL, NULL); +} +void Platform_Log3(const UChar* format, const void* a1, const void* a2, const void* a3) { + Platform_Log4(format, a1, a2, a3, NULL); +} + +void Platform_Log4(const UChar* format, const void* a1, const void* a2, const void* a3, const void* a4) { + UChar msgBuffer[String_BufferSize(512)]; + String msg = String_InitAndClearArray(msgBuffer); + String_Format4(&msg, format, a1, a2, a3, a4); + Platform_Log(&msg); +} + +#if CC_BUILD_WIN +void Platform_Log(STRING_PURE String* message) { + /* TODO: log to console */ + OutputDebugStringA(message->buffer); + OutputDebugStringA("\n"); +} + +void Platform_LogConst(const UChar* message) { + /* TODO: log to console */ + OutputDebugStringA(message); + OutputDebugStringA("\n"); +} + +void Platform_FromSysTime(DateTime* time, SYSTEMTIME* sysTime) { + time->Year = sysTime->wYear; + time->Month = sysTime->wMonth; + time->Day = sysTime->wDay; + time->Hour = sysTime->wHour; + time->Minute = sysTime->wMinute; + time->Second = sysTime->wSecond; + time->Milli = sysTime->wMilliseconds; +} + +void DateTime_CurrentUTC(DateTime* time) { + SYSTEMTIME utcTime; + GetSystemTime(&utcTime); + Platform_FromSysTime(time, &utcTime); +} + +void DateTime_CurrentLocal(DateTime* time) { + SYSTEMTIME localTime; + GetLocalTime(&localTime); + Platform_FromSysTime(time, &localTime); +} + +bool stopwatch_highResolution; +LARGE_INTEGER stopwatch_freq; +void Stopwatch_Measure(struct Stopwatch* timer) { + if (stopwatch_highResolution) { + LARGE_INTEGER value; + QueryPerformanceCounter(&value); + timer->Data[0] = value.QuadPart; + } else { + FILETIME value; + GetSystemTimeAsFileTime(&value); + timer->Data[0] = (Int64)value.dwLowDateTime | ((Int64)value.dwHighDateTime << 32); + } +} +void Stopwatch_Start(struct Stopwatch* timer) { Stopwatch_Measure(timer); } + +/* TODO: check this is actually accurate */ +Int32 Stopwatch_ElapsedMicroseconds(struct Stopwatch* timer) { + Int64 start = timer->Data[0]; + Stopwatch_Measure(timer); + Int64 end = timer->Data[0]; + + if (stopwatch_highResolution) { + return (Int32)(((end - start) * 1000 * 1000) / stopwatch_freq.QuadPart); + } else { + return (Int32)((end - start) / 10); + } +} +#elif CC_BUILD_NIX +void Platform_Log(STRING_PURE String* message) { puts(message->buffer); } +void Platform_LogConst(const UChar* message) { puts(message); } + +void Platform_FromSysTime(DateTime* time, struct tm* sysTime) { + time->Year = sysTime->tm_year + 1900; + time->Month = sysTime->tm_mon + 1; + time->Day = sysTime->tm_mday; + time->Hour = sysTime->tm_hour; + time->Minute = sysTime->tm_min; + time->Second = sysTime->tm_sec; + time->Milli = 0; +} + +void DateTime_CurrentUTC(DateTime* time_) { + struct timeval cur; struct tm utc_time; + gettimeofday(&cur, NULL); + time_->Milli = cur.tv_usec / 1000; + + gmtime_r(&cur.tv_sec, &utc_time); + Platform_FromSysTime(time_, &utc_time); +} + +void DateTime_CurrentLocal(DateTime* time_) { + struct timeval cur; struct tm loc_time; + gettimeofday(&cur, NULL); + time_->Milli = cur.tv_usec / 1000; + + localtime_r(&cur.tv_sec, &loc_time); + Platform_FromSysTime(time_, &loc_time); +} + +void Stopwatch_Measure(struct Stopwatch* timer) { + struct timespec value; + /* TODO: CLOCK_MONOTONIC_RAW ?? */ + clock_gettime(CLOCK_MONOTONIC, &value); + timer->Data[0] = value.tv_sec; + timer->Data[1] = value.tv_nsec; +} +void Stopwatch_Start(struct Stopwatch* timer) { Stopwatch_Measure(timer); } + +/* TODO: check this is actually accurate */ +Int32 Stopwatch_ElapsedMicroseconds(struct Stopwatch* timer) { + Int64 startS = timer->Data[0], startNS = timer->Data[1]; + Stopwatch_Measure(timer); + Int64 endS = timer->Data[0], endNS = timer->Data[1]; + +#define NS_PER_SEC 1000000000LL + Int64 elapsedNS = ((endS - startS) * NS_PER_SEC + endNS) - startNS; + return elapsedNS / 1000; +} +#endif + + +/*########################################################################################################################* +*-----------------------------------------------------Directory/File------------------------------------------------------* +*#########################################################################################################################*/ +#if CC_BUILD_WIN +bool Directory_Exists(STRING_PURE String* path) { + WCHAR data[512]; Platform_ConvertString(data, path); + UInt32 attribs = GetFileAttributesW(data); + return attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY); +} + +ReturnCode Directory_Create(STRING_PURE String* path) { + WCHAR data[512]; Platform_ConvertString(data, path); + BOOL success = CreateDirectoryW(data, NULL); + return success ? 0 : GetLastError(); +} + +bool File_Exists(STRING_PURE String* path) { + WCHAR data[512]; Platform_ConvertString(data, path); + UInt32 attribs = GetFileAttributesW(data); + return attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY); +} + +ReturnCode Directory_Enum(STRING_PURE String* path, void* obj, Directory_EnumCallback callback) { + UChar fileBuffer[String_BufferSize(MAX_PATH + 10)]; + String file = String_InitAndClearArray(fileBuffer); + /* Need to append \* to search for files in directory */ + String_Format1(&file, "%s\\*", path); + WCHAR data[512]; Platform_ConvertString(data, &file); + + WIN32_FIND_DATAW entry; + HANDLE find = FindFirstFileW(data, &entry); + if (find == INVALID_HANDLE_VALUE) return GetLastError(); + + do { + if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; + String_Clear(&file); + Int32 i; + + for (i = 0; i < MAX_PATH && entry.cFileName[i] != '\0'; i++) { + String_Append(&file, Convert_UnicodeToCP437(entry.cFileName[i])); + } + + Utils_UNSAFE_GetFilename(&file); + callback(&file, obj); + } while (FindNextFileW(find, &entry)); + + ReturnCode result = GetLastError(); /* return code from FindNextFile */ + FindClose(find); + return result == ERROR_NO_MORE_FILES ? 0 : result; +} + +ReturnCode File_GetModifiedTime(STRING_PURE String* path, DateTime* time) { + void* file; ReturnCode result = File_Open(&file, path); + if (result) return result; + + FILETIME writeTime; + if (GetFileTime(file, NULL, NULL, &writeTime)) { + SYSTEMTIME sysTime; + FileTimeToSystemTime(&writeTime, &sysTime); + Platform_FromSysTime(time, &sysTime); + } else { + result = GetLastError(); + } + + File_Close(file); + return result; +} + +ReturnCode File_Do(void** file, STRING_PURE String* path, DWORD access, DWORD createMode) { + WCHAR data[512]; Platform_ConvertString(data, path); + *file = CreateFileW(data, access, FILE_SHARE_READ, NULL, createMode, 0, NULL); + return *file != INVALID_HANDLE_VALUE ? 0 : GetLastError(); +} + +ReturnCode File_Open(void** file, STRING_PURE String* path) { + return File_Do(file, path, GENERIC_READ, OPEN_EXISTING); +} +ReturnCode File_Create(void** file, STRING_PURE String* path) { + return File_Do(file, path, GENERIC_WRITE, CREATE_ALWAYS); +} +ReturnCode File_Append(void** file, STRING_PURE String* path) { + ReturnCode result = File_Do(file, path, GENERIC_WRITE, OPEN_ALWAYS); + if (result) return result; + return File_Seek(*file, 0, STREAM_SEEKFROM_END); +} + +ReturnCode File_Read(void* file, UInt8* buffer, UInt32 count, UInt32* bytesRead) { + BOOL success = ReadFile((HANDLE)file, buffer, count, bytesRead, NULL); + return success ? 0 : GetLastError(); +} + +ReturnCode File_Write(void* file, UInt8* buffer, UInt32 count, UInt32* bytesWrote) { + BOOL success = WriteFile((HANDLE)file, buffer, count, bytesWrote, NULL); + return success ? 0 : GetLastError(); +} + +ReturnCode File_Close(void* file) { + return CloseHandle((HANDLE)file) ? 0 : GetLastError(); +} + +ReturnCode File_Seek(void* file, Int32 offset, Int32 seekType) { + static UInt8 modes[3] = { FILE_BEGIN, FILE_CURRENT, FILE_END }; + DWORD pos = SetFilePointer(file, offset, NULL, modes[seekType]); + return pos == INVALID_SET_FILE_POINTER ? GetLastError() : 0; +} + +ReturnCode File_Position(void* file, UInt32* position) { + *position = SetFilePointer(file, 0, NULL, 1); /* SEEK_CUR */ + return *position == INVALID_SET_FILE_POINTER ? GetLastError() : 0; +} + +ReturnCode File_Length(void* file, UInt32* length) { + *length = GetFileSize(file, NULL); + return *length == INVALID_FILE_SIZE ? GetLastError() : 0; +} +#elif CC_BUILD_NIX +bool Directory_Exists(STRING_PURE String* path) { + UInt8 data[1024]; Platform_ConvertString(data, path); + struct stat sb; + return stat(data, &sb) == 0 && S_ISDIR(sb.st_mode); +} + +ReturnCode Directory_Create(STRING_PURE String* path) { + UInt8 data[1024]; Platform_ConvertString(data, path); + /* read/write/search permissions for owner and group, and with read/search permissions for others. */ + /* TODO: Is the default mode in all cases */ + return mkdir(data, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1 ? errno : 0; +} + +bool File_Exists(STRING_PURE String* path) { + UInt8 data[1024]; Platform_ConvertString(data, path); + struct stat sb; + return stat(data, &sb) == 0 && S_ISREG(sb.st_mode); +} + +ReturnCode Directory_Enum(STRING_PURE String* path, void* obj, Directory_EnumCallback callback) { + UInt8 data[1024]; Platform_ConvertString(data, path); + DIR* dirPtr = opendir(data); + if (!dirPtr) return errno; + + UInt8 fileBuffer[String_BufferSize(FILENAME_SIZE)]; + String file = String_InitAndClearArray(fileBuffer); + struct dirent* entry; + + /* TODO: does this also include subdirectories */ + while (entry = readdir(dirPtr)) { + UInt16 len = String_CalcLen(entry->d_name, UInt16_MaxValue); + String_Clear(&file); + String_DecodeUtf8(&file, entry->d_name, len); + + Utils_UNSAFE_GetFilename(&file); + callback(&file, obj); + } + + int result = errno; /* return code from readdir */ + closedir(dirPtr); + return result; +} + +ReturnCode File_GetModifiedTime(STRING_PURE String* path, DateTime* time) { + UInt8 data[1024]; Platform_ConvertString(data, path); + struct stat sb; + if (stat(data, &sb) == -1) return errno; + + DateTime_FromTotalMs(time, UNIX_EPOCH + sb.st_mtime); + return 0; +} + +ReturnCode File_Do(void** file, STRING_PURE String* path, int mode) { + UInt8 data[1024]; Platform_ConvertString(data, path); + *file = open(data, mode); + return *file == -1 ? errno : 0; +} + +ReturnCode File_Open(void** file, STRING_PURE String* path) { + return File_Do(file, path, O_RDONLY); +} +ReturnCode File_Create(void** file, STRING_PURE String* path) { + return File_Do(file, path, O_WRONLY | O_CREAT | O_TRUNC); +} +ReturnCode File_Append(void** file, STRING_PURE String* path) { + ReturnCode result = File_Do(file, path, O_WRONLY | O_CREAT); + if (result) return result; + return File_Seek(*file, 0, STREAM_SEEKFROM_END); +} + +ReturnCode File_Read(void* file, UInt8* buffer, UInt32 count, UInt32* bytesRead) { + ssize_t bytes = read((int)file, buffer, count); + if (bytes == -1) { *bytesRead = 0; return errno; } + *bytesRead = bytes; return 0; +} + +ReturnCode File_Write(void* file, UInt8* buffer, UInt32 count, UInt32* bytesWrote) { + ssize_t bytes = write((int)file, buffer, count); + if (bytes == -1) { *bytesWrote = 0; return errno; } + *bytesWrote = bytes; return 0; +} + +ReturnCode File_Close(void* file) { + return close((int)file) == -1 ? errno : 0; +} + +ReturnCode File_Seek(void* file, Int32 offset, Int32 seekType) { + return lseek((int)file, offset, modes[seektype]) == -1 ? errno : 0; +} + +ReturnCode File_Position(void* file, UInt32* position) { + off_t pos = lseek((int)file, 0, SEEK_CUR); + if (pos == -1) { *position = -1; return errno; } + *position = pos; return 0; +} + +ReturnCode File_Length(void* file, UInt32* length) { + struct stat st; + if (fstat((int)file, &st) == -1) { *length = -1; return errno; } + *length = st.st_size; return 0; +} +#endif + + +/*########################################################################################################################* +*--------------------------------------------------------Threading--------------------------------------------------------* +*#########################################################################################################################*/ +#if CC_BUILD_WIN +void Thread_Sleep(UInt32 milliseconds) { Sleep(milliseconds); } +DWORD WINAPI Thread_StartCallback(LPVOID lpParam) { + Thread_StartFunc* func = (Thread_StartFunc*)lpParam; + (*func)(); + return 0; +} + +void* Thread_Start(Thread_StartFunc* func) { + DWORD threadID; + void* handle = CreateThread(NULL, 0, Thread_StartCallback, func, 0, &threadID); + if (!handle) { + ErrorHandler_FailWithCode(GetLastError(), "Creating thread"); + } + return handle; +} + +void Thread_Join(void* handle) { + WaitForSingleObject((HANDLE)handle, INFINITE); +} + +void Thread_FreeHandle(void* handle) { + if (!CloseHandle((HANDLE)handle)) { + ErrorHandler_FailWithCode(GetLastError(), "Freeing thread handle"); + } +} + +CRITICAL_SECTION mutexList[3]; Int32 mutexIndex; +void* Mutex_Create(void) { + if (mutexIndex == Array_Elems(mutexList)) ErrorHandler_Fail("Cannot allocate mutex"); + CRITICAL_SECTION* ptr = &mutexList[mutexIndex]; + InitializeCriticalSection(ptr); mutexIndex++; + return ptr; +} + +void Mutex_Free(void* handle) { DeleteCriticalSection((CRITICAL_SECTION*)handle); } +void Mutex_Lock(void* handle) { EnterCriticalSection((CRITICAL_SECTION*)handle); } +void Mutex_Unlock(void* handle) { LeaveCriticalSection((CRITICAL_SECTION*)handle); } + +void* Waitable_Create(void) { + void* handle = CreateEventW(NULL, false, false, NULL); + if (!handle) { + ErrorHandler_FailWithCode(GetLastError(), "Creating waitable"); + } + return handle; +} + +void Waitable_Free(void* handle) { + if (!CloseHandle((HANDLE)handle)) { + ErrorHandler_FailWithCode(GetLastError(), "Freeing waitable"); + } +} + +void Waitable_Signal(void* handle) { SetEvent((HANDLE)handle); } +void Waitable_Wait(void* handle) { + WaitForSingleObject((HANDLE)handle, INFINITE); +} + +void Waitable_WaitFor(void* handle, UInt32 milliseconds) { + WaitForSingleObject((HANDLE)handle, milliseconds); +} +#elif CC_BUILD_NIX +void Thread_Sleep(UInt32 milliseconds) { usleep(milliseconds * 1000); } +void* Thread_StartCallback(void* lpParam) { + Thread_StartFunc* func = (Thread_StartFunc*)lpParam; + (*func)(); + return NULL; +} + +pthread_t threadList[3]; Int32 threadIndex; +void* Thread_Start(Thread_StartFunc* func) { + if (threadIndex == Array_Elems(threadList)) ErrorHandler_Fail("Cannot allocate thread"); + pthread_t* ptr = &threadList[threadIndex]; + int result = pthread_create(ptr, NULL, Thread_StartCallback, func); + + ErrorHandler_CheckOrFail(result, "Creating thread"); + threadIndex++; return ptr; +} + +void Thread_Join(void* handle) { + int result = pthread_join(*((pthread_t*)handle), NULL); + ErrorHandler_CheckOrFail(result, "Joining thread"); +} + +void Thread_FreeHandle(void* handle) { + int result = pthread_detach(*((pthread_t*)handle)); + ErrorHandler_CheckOrFail(result, "Detaching thread"); +} + +pthread_mutex_t mutexList[3]; Int32 mutexIndex; +void* Mutex_Create(void) { + if (mutexIndex == Array_Elems(mutexList)) ErrorHandler_Fail("Cannot allocate mutex"); + pthread_mutex_t* ptr = &mutexList[mutexIndex]; + int result = pthread_mutex_init(ptr, NULL); + + ErrorHandler_CheckOrFail(result, "Creating mutex"); + mutexIndex++; return ptr; +} + +void Mutex_Free(void* handle) { + int result = pthread_mutex_destroy((pthread_mutex_t*)handle); + ErrorHandler_CheckOrFail(result, "Destroying mutex"); +} + +void Mutex_Lock(void* handle) { + int result = pthread_mutex_lock((pthread_mutex_t*)handle); + ErrorHandler_CheckOrFail(result, "Locking mutex"); +} + +void Mutex_Unlock(void* handle) { + int result = pthread_mutex_unlock((pthread_mutex_t*)handle); + ErrorHandler_CheckOrFail(result, "Unlocking mutex"); +} + +pthread_cond_t condList[2]; Int32 condIndex; +void* Waitable_Create(void) { + if (condIndex == Array_Elems(condList)) ErrorHandler_Fail("Cannot allocate event"); + pthread_cond_t* ptr = &condList[condIndex]; + int result = pthread_cond_init(ptr, NULL); + + ErrorHandler_CheckOrFail(result, "Creating event"); + condIndex++; return ptr; +} + +void Waitable_Free(void* handle) { + int result = pthread_cond_destroy((pthread_cond_t*)handle); + ErrorHandler_CheckOrFail(result, "Destroying event"); +} + +void Waitable_Signal(void* handle) { + int result = pthread_cond_signal((pthread_cond_t*)handle); + ErrorHandler_CheckOrFail(result, "Signalling event"); +} + +void Waitable_Wait(void* handle) { + int result = pthread_cond_wait((pthread_cond_t*)handle, &event_mutex); + ErrorHandler_CheckOrFail(result, "Waiting event"); +} +#endif + + +/*########################################################################################################################* +*--------------------------------------------------------Font/Text--------------------------------------------------------* +*#########################################################################################################################*/ +#if CC_BUILD_WIN +void Font_Make(struct FontDesc* desc, STRING_PURE String* fontName, UInt16 size, UInt16 style) { + desc->Size = size; + desc->Style = style; + LOGFONTA font = { 0 }; + + font.lfHeight = -Math_CeilDiv(size * GetDeviceCaps(hdc, LOGPIXELSY), 72); + font.lfUnderline = style == FONT_STYLE_UNDERLINE; + font.lfWeight = style == FONT_STYLE_BOLD ? FW_BOLD : FW_NORMAL; + font.lfQuality = ANTIALIASED_QUALITY; /* TODO: CLEARTYPE_QUALITY looks slightly better */ + + String dstName = String_Init(font.lfFaceName, 0, LF_FACESIZE); + String_AppendString(&dstName, fontName); + desc->Handle = CreateFontIndirectA(&font); + if (!desc->Handle) ErrorHandler_Fail("Creating font handle failed"); +} + +void Font_Free(struct FontDesc* desc) { + if (!DeleteObject(desc->Handle)) ErrorHandler_Fail("Deleting font handle failed"); + desc->Handle = NULL; +} + +/* TODO: not associate font with device so much */ +struct Size2D Platform_TextMeasure(struct DrawTextArgs* args) { + WCHAR data[512]; Platform_ConvertString(data, &args->Text); + HGDIOBJ oldFont = SelectObject(hdc, args->Font.Handle); + SIZE area; GetTextExtentPointW(hdc, data, args->Text.length, &area); + + SelectObject(hdc, oldFont); + return Size2D_Make(area.cx, area.cy); +} + +HBITMAP platform_dib; +HBITMAP platform_oldBmp; +struct Bitmap* platform_bmp; +void* platform_bits; + +void Platform_SetBitmap(struct Bitmap* bmp) { + platform_bmp = bmp; + platform_bits = NULL; + + BITMAPINFO bmi = { 0 }; + bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = bmp->Width; + bmi.bmiHeader.biHeight = -bmp->Height; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + platform_dib = CreateDIBSection(hdc, &bmi, 0, &platform_bits, NULL, 0); + if (!platform_dib) ErrorHandler_Fail("Failed to allocate DIB for text"); + platform_oldBmp = SelectObject(hdc, platform_dib); +} + +/* TODO: check return codes and stuff */ +/* TODO: make text prettier.. somehow? */ +/* TODO: Do we need to / 255 instead of >> 8 ? */ +struct Size2D Platform_TextDraw(struct DrawTextArgs* args, Int32 x, Int32 y, PackedCol col) { + WCHAR strUnicode[String_BufferSize(FILENAME_SIZE)]; + Platform_ConvertString(strUnicode, &args->Text); + + HGDIOBJ oldFont = (HFONT)SelectObject(hdc, (HFONT)args->Font.Handle); + SIZE area; GetTextExtentPointW(hdc, strUnicode, args->Text.length, &area); + TextOutW(hdc, 0, 0, strUnicode, args->Text.length); + + Int32 xx, yy; + struct Bitmap* bmp = platform_bmp; + for (yy = 0; yy < area.cy; yy++) { + UInt8* src = (UInt8*)platform_bits + (yy * (bmp->Width << 2)); + UInt8* dst = (UInt8*)Bitmap_GetRow(bmp, y + yy); dst += x * BITMAP_SIZEOF_PIXEL; + + for (xx = 0; xx < area.cx; xx++) { + UInt8 intensity = *src, invIntensity = UInt8_MaxValue - intensity; + dst[0] = ((col.B * intensity) >> 8) + ((dst[0] * invIntensity) >> 8); + dst[1] = ((col.G * intensity) >> 8) + ((dst[1] * invIntensity) >> 8); + dst[2] = ((col.R * intensity) >> 8) + ((dst[2] * invIntensity) >> 8); + //dst[3] = ((col.A * intensity) >> 8) + ((dst[3] * invIntensity) >> 8); + dst[3] = intensity + ((dst[3] * invIntensity) >> 8); + src += BITMAP_SIZEOF_PIXEL; dst += BITMAP_SIZEOF_PIXEL; + } + } + + SelectObject(hdc, oldFont); + //DrawTextA(hdc, args->Text.buffer, args->Text.length, + // &r, DT_NOPREFIX | DT_SINGLELINE | DT_NOCLIP); + return Size2D_Make(area.cx, area.cy); +} + +void Platform_ReleaseBitmap(void) { + /* TODO: Check return values */ + SelectObject(hdc, platform_oldBmp); + DeleteObject(platform_dib); + + platform_oldBmp = NULL; + platform_dib = NULL; + platform_bmp = NULL; +} +#elif CC_BUILD_NIX +/* TODO: Implement these stubs */ +void Font_Make(struct FontDesc* desc, STRING_PURE String* fontName, UInt16 size, UInt16 style) { desc->Size = size; } +void Font_Free(struct FontDesc* desc) { } +struct Size2D Platform_TextMeasure(struct DrawTextArgs* args) { } +void Platform_SetBitmap(struct Bitmap* bmp) { } +struct Size2D Platform_TextDraw(struct DrawTextArgs* args, Int32 x, Int32 y, PackedCol col) { } +void Platform_ReleaseBitmap(void) { } +#endif + + +/*########################################################################################################################* +*---------------------------------------------------------Socket----------------------------------------------------------* +*#########################################################################################################################*/ +void Socket_Create(SocketPtr* socketResult) { + *socketResult = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (*socketResult == -1) { + ErrorHandler_FailWithCode(Socket__Error(), "Failed to create socket"); + } +} + +static ReturnCode Socket_ioctl(SocketPtr socket, UInt32 cmd, Int32* data) { +#if CC_BUILD_WIN + return ioctlsocket(socket, cmd, data); +#else + return ioctl(socket, cmd, data); +#endif +} + +ReturnCode Socket_Available(SocketPtr socket, UInt32* available) { + return Socket_ioctl(socket, FIONREAD, available); +} +ReturnCode Socket_SetBlocking(SocketPtr socket, bool blocking) { + Int32 blocking_raw = blocking ? 0 : -1; + return Socket_ioctl(socket, FIONBIO, &blocking_raw); +} + +ReturnCode Socket_GetError(SocketPtr socket, ReturnCode* result) { + Int32 resultSize = sizeof(ReturnCode); + return getsockopt(socket, SOL_SOCKET, SO_ERROR, result, &resultSize); +} + +ReturnCode Socket_Connect(SocketPtr socket, STRING_PURE String* ip, Int32 port) { + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(ip->buffer); + addr.sin_port = htons((UInt16)port); + + ReturnCode result = connect(socket, (struct sockaddr*)(&addr), sizeof(addr)); + return result == -1 ? Socket__Error() : 0; +} + +ReturnCode Socket_Read(SocketPtr socket, UInt8* buffer, UInt32 count, UInt32* modified) { + Int32 recvCount = recv(socket, buffer, count, 0); + if (recvCount != -1) { *modified = recvCount; return 0; } + *modified = 0; return Socket__Error(); +} + +ReturnCode Socket_Write(SocketPtr socket, UInt8* buffer, UInt32 count, UInt32* modified) { + Int32 sentCount = send(socket, buffer, count, 0); + if (sentCount != -1) { *modified = sentCount; return 0; } + *modified = 0; return Socket__Error(); +} + +ReturnCode Socket_Close(SocketPtr socket) { + ReturnCode result = 0; +#if CC_BUILD_WIN + ReturnCode result1 = shutdown(socket, SD_BOTH); +#else + ReturnCode result1 = shutdown(socket, SHUT_RDWR); +#endif + if (result1 == -1) result = Socket__Error(); + +#if CC_BUILD_WIN + ReturnCode result2 = closesocket(socket); +#else + ReturnCode result2 = close(socket); +#endif + if (result2 == -1) result = Socket__Error(); + return result; +} + +ReturnCode Socket_Select(SocketPtr socket, Int32 selectMode, bool* success) { + fd_set set; + FD_ZERO(&set); + FD_SET(socket, &set); + + struct timeval time = { 0 }; + Int32 selectCount = -1; + + if (selectMode == SOCKET_SELECT_READ) { + selectCount = select(1, &set, NULL, NULL, &time); + } else if (selectMode == SOCKET_SELECT_WRITE) { + selectCount = select(1, NULL, &set, NULL, &time); + } else if (selectMode == SOCKET_SELECT_ERROR) { + selectCount = select(1, NULL, NULL, &set, &time); + } + + if (selectCount == -1) { *success = false; return Socket__Error(); } +#if CC_BUILD_WIN + *success = set.fd_count != 0; return 0; +#else + *success = FD_ISSET(socket, &set); return 0; +#endif +} + + +/*########################################################################################################################* +*----------------------------------------------------------Http-----------------------------------------------------------* +*#########################################################################################################################*/ +#if CC_BUILD_WIN +HINTERNET hInternet; +void Http_Init(void) { + /* TODO: Should we use INTERNET_OPEN_TYPE_PRECONFIG instead? */ + hInternet = InternetOpenA(PROGRAM_APP_NAME, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + if (!hInternet) ErrorHandler_FailWithCode(GetLastError(), "Failed to init WinINet"); +} + +ReturnCode Http_MakeRequest(struct AsyncRequest* request, void** handle) { + String url = String_FromRawArray(request->URL); + UChar headersBuffer[String_BufferSize(STRING_SIZE * 2)]; + String headers = String_MakeNull(); + + /* https://stackoverflow.com/questions/25308488/c-wininet-custom-http-headers */ + if (request->Etag[0] || request->LastModified.Year) { + headers = String_InitAndClearArray(headersBuffer); + if (request->LastModified.Year > 0) { + String_AppendConst(&headers, "If-Modified-Since: "); + DateTime_HttpDate(&request->LastModified, &headers); + String_AppendConst(&headers, "\r\n"); + } + + if (request->Etag[0]) { + String etag = String_FromRawArray(request->Etag); + String_AppendConst(&headers, "If-None-Match: "); + String_AppendString(&headers, &etag); + String_AppendConst(&headers, "\r\n"); + } + String_AppendConst(&headers, "\r\n\r\n"); + } + + *handle = InternetOpenUrlA(hInternet, url.buffer, headers.buffer, headers.length, + INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD, NULL); + return *handle == NULL ? GetLastError() : 0; +} + +/* TODO: Test last modified and etag even work */ +#define Http_Query(flags, result) HttpQueryInfoA(handle, flags, result, &bufferLen, NULL) +ReturnCode Http_GetRequestHeaders(struct AsyncRequest* request, void* handle, UInt32* size) { + DWORD bufferLen; + + UInt32 status; + bufferLen = sizeof(DWORD); + if (!Http_Query(HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status)) return GetLastError(); + request->StatusCode = status; + + bufferLen = sizeof(DWORD); + if (!Http_Query(HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, size)) return GetLastError(); + + SYSTEMTIME lastModified; + bufferLen = sizeof(SYSTEMTIME); + if (Http_Query(HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &lastModified)) { + Platform_FromSysTime(&request->LastModified, &lastModified); + } + + String etag = String_InitAndClearArray(request->Etag); + bufferLen = etag.capacity; + Http_Query(HTTP_QUERY_ETAG, etag.buffer); + + return 0; +} + +ReturnCode Http_GetRequestData(struct AsyncRequest* request, void* handle, void** data, UInt32 size, volatile Int32* progress) { + if (size == 0) return ERROR_NOT_SUPPORTED; + *data = Mem_Alloc(size, sizeof(UInt8), "http get data"); + + *progress = 0; + UInt8* buffer = *data; + UInt32 left = size, read, totalRead = 0; + + while (left > 0) { + UInt32 toRead = left, avail = 0; + /* only read as much data that is pending */ + if (InternetQueryDataAvailable(handle, &avail, 0, NULL)) { + toRead = min(toRead, avail); + } + + bool success = InternetReadFile(handle, buffer, toRead, &read); + if (!success) { Mem_Free(data); return GetLastError(); } + + if (!read) break; + buffer += read; totalRead += read; left -= read; + *progress = (Int32)(100.0f * totalRead / size); + } + + *progress = 100; + return 0; +} + +ReturnCode Http_FreeRequest(void* handle) { + return InternetCloseHandle(handle) ? 0 : GetLastError(); +} + +ReturnCode Http_Free(void) { + return InternetCloseHandle(hInternet) ? 0 : GetLastError(); +} +#elif CC_BUILD_NIX +void Http_Init(void) { } +ReturnCode Http_MakeRequest(struct AsyncRequest* request, void** handle) { return 1; } +ReturnCode Http_GetRequestHeaders(struct AsyncRequest* request, void* handle, UInt32* size) { return 1; } +ReturnCode Http_GetRequestData(struct AsyncRequest* request, void* handle, void** data, UInt32 size, volatile Int32* progress) { return 1; } +ReturnCode Http_FreeRequest(void* handle) { return 1; } +ReturnCode Http_Free(void) { return 1; } +#endif + + +/*########################################################################################################################* +*----------------------------------------------------------Audio----------------------------------------------------------* +*#########################################################################################################################*/ +#if CC_BUILD_WIN +struct AudioContext { + HWAVEOUT Handle; + WAVEHDR Headers[AUDIO_MAX_CHUNKS]; + struct AudioFormat Format; + Int32 NumBuffers; +}; +struct AudioContext Audio_Contexts[20]; + +void Audio_Init(AudioHandle* handle, Int32 buffers) { + Int32 i, j; + for (i = 0; i < Array_Elems(Audio_Contexts); i++) { + struct AudioContext* ctx = &Audio_Contexts[i]; + if (ctx->NumBuffers) continue; + ctx->NumBuffers = buffers; + + *handle = i; + for (j = 0; j < buffers; j++) { + ctx->Headers[j].dwFlags = WHDR_DONE; + } + return; + } + ErrorHandler_Fail("No free audio contexts"); +} + +void Audio_Free(AudioHandle handle) { + struct AudioContext* ctx = &Audio_Contexts[handle]; + if (!ctx->Handle) return; + + ReturnCode result = waveOutClose(ctx->Handle); + ErrorHandler_CheckOrFail(result, "Audio - closing device"); + Mem_Set(ctx, 0, sizeof(struct AudioContext)); +} + +struct AudioFormat* Audio_GetFormat(AudioHandle handle) { + struct AudioContext* ctx = &Audio_Contexts[handle]; + return &ctx->Format; +} + +void Audio_SetFormat(AudioHandle handle, struct AudioFormat* format) { + struct AudioContext* ctx = &Audio_Contexts[handle]; + struct AudioFormat* cur = &ctx->Format; + + /* only recreate handle if we need to */ + if (AudioFormat_Eq(cur, format)) return; + if (ctx->Handle) { + ReturnCode result = waveOutClose(ctx->Handle); + ErrorHandler_CheckOrFail(result, "Audio - closing device"); + } + + WAVEFORMATEX fmt = { 0 }; + fmt.nChannels = format->Channels; + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.wBitsPerSample = format->BitsPerSample; + fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; + fmt.nSamplesPerSec = format->SampleRate; + fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; + + if (waveOutGetNumDevs() == 0u) ErrorHandler_Fail("No audio devices found"); + ReturnCode result = waveOutOpen(&ctx->Handle, WAVE_MAPPER, &fmt, NULL, NULL, CALLBACK_NULL); + ErrorHandler_CheckOrFail(result, "Audio - opening device"); + ctx->Format = *format; +} + +void Audio_PlayData(AudioHandle handle, Int32 idx, void* data, UInt32 dataSize) { + struct AudioContext* ctx = &Audio_Contexts[handle]; + WAVEHDR* hdr = &ctx->Headers[idx]; + Mem_Set(hdr, 0, sizeof(WAVEHDR)); + + hdr->lpData = data; + hdr->dwBufferLength = dataSize; + hdr->dwLoops = 1; + + ReturnCode result = waveOutPrepareHeader(ctx->Handle, hdr, sizeof(WAVEHDR)); + ErrorHandler_CheckOrFail(result, "Audio - prepare header"); + result = waveOutWrite(ctx->Handle, hdr, sizeof(WAVEHDR)); + ErrorHandler_CheckOrFail(result, "Audio - write header"); +} + +bool Audio_IsCompleted(AudioHandle handle, Int32 idx) { + struct AudioContext* ctx = &Audio_Contexts[handle]; + WAVEHDR* hdr = &ctx->Headers[idx]; + if (!(hdr->dwFlags & WHDR_DONE)) return false; + + if (hdr->dwFlags & WHDR_PREPARED) { + ReturnCode result = waveOutUnprepareHeader(ctx->Handle, hdr, sizeof(WAVEHDR)); + ErrorHandler_CheckOrFail(result, "Audio - unprepare header"); + } + return true; +} + +bool Audio_IsFinished(AudioHandle handle) { + struct AudioContext* ctx = &Audio_Contexts[handle]; + Int32 i; + for (i = 0; i < ctx->NumBuffers; i++) { + if (!Audio_IsCompleted(handle, i)) return false; + } + return true; +} +#elif CC_BUILD_NIX +#endif + + +/*########################################################################################################################* +*--------------------------------------------------------Platform---------------------------------------------------------* +*#########################################################################################################################*/ +#if CC_BUILD_WIN +void Platform_ConvertString(void* dstPtr, STRING_PURE String* src) { + if (src->length > FILENAME_SIZE) ErrorHandler_Fail("String too long to expand"); + WCHAR* dst = dstPtr; + + Int32 i; + for (i = 0; i < src->length; i++) { + *dst = Convert_CP437ToUnicode(src->buffer[i]); dst++; + } + *dst = '\0'; +} + +static void Platform_InitDisplay(void) { + HDC hdc = GetDC(NULL); + struct DisplayDevice device = { 0 }; + + device.Bounds.Width = GetSystemMetrics(SM_CXSCREEN); + device.Bounds.Height = GetSystemMetrics(SM_CYSCREEN); + device.BitsPerPixel = GetDeviceCaps(hdc, BITSPIXEL); + DisplayDevice_Default = device; + + ReleaseDC(NULL, hdc); +} + +void Platform_Init(void) { + Platform_InitDisplay(); + heap = GetProcessHeap(); /* TODO: HeapCreate instead? probably not */ + hdc = CreateCompatibleDC(NULL); + if (!hdc) ErrorHandler_Fail("Failed to get screen DC"); + + SetTextColor(hdc, 0x00FFFFFF); + SetBkColor(hdc, 0x00000000); + SetBkMode(hdc, OPAQUE); + + stopwatch_highResolution = QueryPerformanceFrequency(&stopwatch_freq); + WSADATA wsaData; + ReturnCode wsaResult = WSAStartup(MAKEWORD(2, 2), &wsaData); + ErrorHandler_CheckOrFail(wsaResult, "WSAStartup failed"); +} + +void Platform_Free(void) { + DeleteDC(hdc); + WSACleanup(); + HeapDestroy(heap); +} + +void Platform_Exit(ReturnCode code) { ExitProcess(code); } + +STRING_PURE String Platform_GetCommandLineArgs(void) { + String args = String_FromReadonly(GetCommandLineA()); + + Int32 argsStart; + if (args.buffer[0] == '"') { + /* Handle path argument in full "path" form, which can include spaces */ + argsStart = String_IndexOf(&args, '"', 1) + 1; + } else { + argsStart = String_IndexOf(&args, ' ', 0) + 1; + } + + if (argsStart == 0) argsStart = args.length; + args = String_UNSAFE_SubstringAt(&args, argsStart); + + /* get rid of duplicate leading spaces before first arg */ + while (args.length && args.buffer[0] == ' ') { + args = String_UNSAFE_SubstringAt(&args, 1); + } + return args; +} +#elif CC_BUILD_NIX +void Platform_ConvertString(void* dstPtr, STRING_PURE String* src) { + if (src->length > FILENAME_SIZE) ErrorHandler_Fail("String too long to expand"); + UInt8* dst = dstPtr; + + Int32 i; + for (i = 0; i < src->length; i++) { + UInt16 codepoint = Convert_CP437ToUnicode(src->buffer[i]); + Int32 len = Stream_WriteUtf8(dst, codepoint); dst += len; + } + *dst = '\0'; +} + +static void Platform_InitDisplay(void) { + Display* display = XOpenDisplay(NULL); + if (!display) ErrorHandler_Fail("Failed to open display"); + + int screen = XDefaultScreen(display); + Window rootWin = XRootWindow(display, screen); + + /* TODO: Use Xinerama and XRandR for querying these */ + struct DisplayDevice device = { 0 }; + device.Bounds.Width = DisplayWidth(display, screen); + device.Bounds.Height = DisplayHeight(display, screen); + device.BitsPerPixel = DefaultDepth(display, screen); + DisplayDevice_Default = device; + + DisplayDevice_Meta[0] = display; + DisplayDevice_Meta[1] = screen; + DisplayDevice_Meta[2] = rootWin; +} + +pthread_mutex_t event_mutex; +void Platform_Init(void) { + Platform_InitDisplay(); + pthread_mutex_init(&event_mutex, NULL); +} + +void Platform_Free(void) { + pthread_mutex_destroy(&event_mutex); +} + +void Platform_Exit(ReturnCode code) { exit(code); } + +STRING_PURE String Platform_GetCommandLineArgs(void) { + /* TODO: Implement this */ + return String_MakeNull(); +} +#endif diff --git a/src/Client/Platform.h b/src/Client/Platform.h index 14d94af27..e5e985da8 100644 --- a/src/Client/Platform.h +++ b/src/Client/Platform.h @@ -27,7 +27,7 @@ extern ReturnCode ReturnCode_SocketInProgess; extern ReturnCode ReturnCode_SocketWouldBlock; extern ReturnCode ReturnCode_InvalidArg; -void Platform_UnicodeExpand(void* dstPtr, STRING_PURE String* src); +void Platform_ConvertString(void* dstPtr, STRING_PURE String* src); void Platform_Init(void); void Platform_Free(void); void Platform_Exit(ReturnCode code); @@ -43,12 +43,16 @@ void Mem_Copy(void* dst, void* src, UInt32 numBytes); void Platform_Log(STRING_PURE String* message); void Platform_LogConst(const UChar* message); -#define Platform_Log1(format, a1) Platform_Log4(format, a1, NULL, NULL, NULL) -#define Platform_Log2(format, a1, a2) Platform_Log4(format, a1, a2, NULL, NULL) -#define Platform_Log3(format, a1, a2, a3) Platform_Log4(format, a1, a2, a3, NULL) +void Platform_Log1(const UChar* format, const void* a1); +void Platform_Log2(const UChar* format, const void* a1, const void* a2); +void Platform_Log3(const UChar* format, const void* a1, const void* a2, const void* a3); void Platform_Log4(const UChar* format, const void* a1, const void* a2, const void* a3, const void* a4); + void DateTime_CurrentUTC(DateTime* time); void DateTime_CurrentLocal(DateTime* time); +struct Stopwatch { Int64 Data[2]; }; +void Stopwatch_Start(struct Stopwatch* timer); +Int32 Stopwatch_ElapsedMicroseconds(struct Stopwatch* timer); bool Directory_Exists(STRING_PURE String* path); ReturnCode Directory_Create(STRING_PURE String* path); @@ -85,10 +89,6 @@ void Waitable_Signal(void* handle); void Waitable_Wait(void* handle); void Waitable_WaitFor(void* handle, UInt32 milliseconds); -struct Stopwatch { Int64 Data[2]; }; -void Stopwatch_Start(struct Stopwatch* timer); -Int32 Stopwatch_ElapsedMicroseconds(struct Stopwatch* timer); - void Font_Make(struct FontDesc* desc, STRING_PURE String* fontName, UInt16 size, UInt16 style); void Font_Free(struct FontDesc* desc); struct Size2D Platform_TextMeasure(struct DrawTextArgs* args); diff --git a/src/Client/Program.c b/src/Client/Program.c index f1891c4ca..9709f99af 100644 --- a/src/Client/Program.c +++ b/src/Client/Program.c @@ -14,7 +14,7 @@ #include "Audio.h" #include "ExtMath.h" -#define CC_TEST_VORBIS +//#define CC_TEST_VORBIS #ifdef CC_TEST_VORBIS #include "Vorbis.h" diff --git a/src/Client/Socket.c b/src/Client/Socket.c deleted file mode 100644 index 800f0a994..000000000 --- a/src/Client/Socket.c +++ /dev/null @@ -1,118 +0,0 @@ -#include "Platform.h" -#include "ErrorHandler.h" - -#if CC_BUILD_WIN -#define WIN32_LEAN_AND_MEAN -#define _WIN32_WINNT 0x0500 -#include - -ReturnCode ReturnCode_SocketInProgess = WSAEINPROGRESS; -ReturnCode ReturnCode_SocketWouldBlock = WSAEWOULDBLOCK; -#define Socket__Error() WSAGetLastError() -#elif CC_BUILD_NIX -#include -#include -#include -#include -#include - -ReturnCode ReturnCode_SocketInProgess = EINPROGRESS; -ReturnCode ReturnCode_SocketWouldBlock = EWOULDBLOCK; -#define Socket__Error() errno -#else -#error "You're not using BSD sockets, define the interface in Socket.c" -#endif - -void Socket_Create(SocketPtr* socketResult) { - *socketResult = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (*socketResult == -1) { - ErrorHandler_FailWithCode(Socket__Error(), "Failed to create socket"); - } -} - -ReturnCode Platform_ioctl(SocketPtr socket, UInt32 cmd, Int32* data) { -#if CC_BUILD_WIN - return ioctlsocket(socket, cmd, data); -#else - return ioctl(socket, cmd, data); -#endif -} - -ReturnCode Socket_Available(SocketPtr socket, UInt32* available) { - return Platform_ioctl(socket, FIONREAD, available); -} -ReturnCode Socket_SetBlocking(SocketPtr socket, bool blocking) { - Int32 blocking_raw = blocking ? 0 : -1; - return Platform_ioctl(socket, FIONBIO, &blocking_raw); -} - -ReturnCode Socket_GetError(SocketPtr socket, ReturnCode* result) { - Int32 resultSize = sizeof(ReturnCode); - return getsockopt(socket, SOL_SOCKET, SO_ERROR, result, &resultSize); -} - -ReturnCode Socket_Connect(SocketPtr socket, STRING_PURE String* ip, Int32 port) { - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = inet_addr(ip->buffer); - addr.sin_port = htons((UInt16)port); - - ReturnCode result = connect(socket, (struct sockaddr*)(&addr), sizeof(addr)); - return result == -1 ? Socket__Error() : 0; -} - -ReturnCode Socket_Read(SocketPtr socket, UInt8* buffer, UInt32 count, UInt32* modified) { - Int32 recvCount = recv(socket, buffer, count, 0); - if (recvCount != -1) { *modified = recvCount; return 0; } - *modified = 0; return Socket__Error(); -} - -ReturnCode Socket_Write(SocketPtr socket, UInt8* buffer, UInt32 count, UInt32* modified) { - Int32 sentCount = send(socket, buffer, count, 0); - if (sentCount != -1) { *modified = sentCount; return 0; } - *modified = 0; return Socket__Error(); -} - -ReturnCode Socket_Close(SocketPtr socket) { - ReturnCode result = 0; -#if CC_BUILD_WIN - ReturnCode result1 = shutdown(socket, SD_BOTH); -#else - ReturnCode result1 = shutdown(socket, SHUT_RDWR); -#endif - if (result1 == -1) result = Socket__Error(); - -#if CC_BUILD_WIN - ReturnCode result2 = closesocket(socket); -#else - ReturnCode result2 = close(socket); -#endif - if (result2 == -1) result = Socket__Error(); - return result; -} - -ReturnCode Socket_Select(SocketPtr socket, Int32 selectMode, bool* success) { - fd_set set; - FD_ZERO(&set); - FD_SET(socket, &set); - - struct timeval time = { 0 }; - Int32 selectCount; - - if (selectMode == SOCKET_SELECT_READ) { - selectCount = select(1, &set, NULL, NULL, &time); - } else if (selectMode == SOCKET_SELECT_WRITE) { - selectCount = select(1, NULL, &set, NULL, &time); - } else if (selectMode == SOCKET_SELECT_ERROR) { - selectCount = select(1, NULL, NULL, &set, &time); - } else { - selectCount = -1; - } - - if (selectCount == -1) { *success = false; return Socket__Error(); } -#if CC_BUILD_WIN - *success = set.fd_count != 0; return 0; -#else - *success = FD_ISSET(socket, &set); return 0; -#endif -} \ No newline at end of file diff --git a/src/Client/Vorbis.c b/src/Client/Vorbis.c index 0bf578e14..9347f1b72 100644 --- a/src/Client/Vorbis.c +++ b/src/Client/Vorbis.c @@ -110,9 +110,6 @@ static ReturnCode Vorbis_TryReadBits(struct VorbisState* ctx, UInt32 bitsCount, } -#define VORBIS_MAX_CHANS 8 -#define Vorbis_ChanData(ctx, ch) (ctx->Values + (ch) * ctx->CurBlockSize) - static Int32 iLog(Int32 x) { Int32 bits = 0; while (x > 0) { bits++; x >>= 1; } @@ -524,7 +521,7 @@ static void Floor_Synthesis(struct VorbisState* ctx, struct Floor* f, Int32 ch) Int32 YFinal[FLOOR_MAX_VALUES]; bool Step2[FLOOR_MAX_VALUES]; - Real32* data = Vorbis_ChanData(ctx, ch); + Real32* data = ctx->CurOutput[ch]; Int32* yList = f->YList[ch]; Step2[0] = true; @@ -886,6 +883,8 @@ void imdct_calc(Real32* in, Real32* out, struct imdct_state* state) { } } + /* TODO: eliminate this, do w/u in-place */ + /* TODO: dynamically allocate mem for imdct */ if (l+1 <= log2_n - 4) { Mem_Copy(w, u, sizeof(u)); } @@ -1158,7 +1157,7 @@ ReturnCode Vorbis_DecodeFrame(struct VorbisState* ctx) { ctx->Values = Mem_AllocCleared(ctx->Channels * ctx->CurBlockSize, sizeof(Real32), "audio values"); for (i = 0; i < ctx->Channels; i++) { - ctx->CurOutput[i] = Vorbis_ChanData(ctx, i); + ctx->CurOutput[i] = ctx->Values + i * ctx->CurBlockSize; } /* decode floor */ @@ -1189,7 +1188,7 @@ ReturnCode Vorbis_DecodeFrame(struct VorbisState* ctx) { if (mapping->Mux[j] != i) continue; doNotDecode[ch] = !hasResidue[j]; - data[ch] = Vorbis_ChanData(ctx, j); + data[ch] = ctx->CurOutput[j]; ch++; } @@ -1199,8 +1198,8 @@ ReturnCode Vorbis_DecodeFrame(struct VorbisState* ctx) { /* inverse coupling */ for (i = mapping->CouplingSteps - 1; i >= 0; i--) { - Real32* magValues = Vorbis_ChanData(ctx, mapping->Magnitude[i]); - Real32* angValues = Vorbis_ChanData(ctx, mapping->Angle[i]); + Real32* magValues = ctx->CurOutput[mapping->Magnitude[i]]; + Real32* angValues = ctx->CurOutput[mapping->Angle[i]]; for (j = 0; j < ctx->DataSize; j++) { Real32 m = magValues[j], a = angValues[j]; @@ -1239,7 +1238,7 @@ ReturnCode Vorbis_DecodeFrame(struct VorbisState* ctx) { /* inverse monolithic transform of audio spectrum vector */ for (i = 0; i < ctx->Channels; i++) { - Real32* data = Vorbis_ChanData(ctx, i); + Real32* data = ctx->CurOutput[i]; if (!hasFloor[i]) { /* TODO: Do we actually need to zero data here (residue type 2 maybe) */ Mem_Set(data, 0, ctx->CurBlockSize * sizeof(Real32)); @@ -1260,6 +1259,7 @@ Int32 Vorbis_OutputFrame(struct VorbisState* ctx, Int16* data) { size = (ctx->PrevBlockSize / 4) + (ctx->CurBlockSize / 4); /* TODO: There's probably a nicer way of doing this.. */ + /* TODO: Do this in-place */ Real32* combined[VORBIS_MAX_CHANS]; for (i = 0; i < ctx->Channels; i++) { combined[i] = Mem_AllocCleared(size, sizeof(Real32), "temp combined"); diff --git a/src/Client/WinPlatform.c b/src/Client/WinPlatform.c deleted file mode 100644 index 2bc880b44..000000000 --- a/src/Client/WinPlatform.c +++ /dev/null @@ -1,706 +0,0 @@ -#include "Platform.h" -#if CC_BUILD_WIN -#include "Stream.h" -#include "DisplayDevice.h" -#include "ExtMath.h" -#include "ErrorHandler.h" -#include "Drawer2D.h" -#include "Funcs.h" -#include "AsyncDownloader.h" -#define WIN32_LEAN_AND_MEAN -#define NOSERVICE -#define NOMCX -#define NOIME -#define _WIN32_WINNT 0x0500 -#include -#include -#include -#include -#include - -/* Missing from some old MingW32 headers */ -#ifndef HTTP_QUERY_ETAG -#define HTTP_QUERY_ETAG 54 -#endif - -HDC hdc; -HANDLE heap; -bool stopwatch_highResolution; -LARGE_INTEGER stopwatch_freq; - -UChar* Platform_NewLine = "\r\n"; -UChar Directory_Separator = '\\'; -ReturnCode ReturnCode_FileShareViolation = ERROR_SHARING_VIOLATION; -ReturnCode ReturnCode_FileNotFound = ERROR_FILE_NOT_FOUND; -ReturnCode ReturnCode_NotSupported = ERROR_NOT_SUPPORTED; -ReturnCode ReturnCode_InvalidArg = ERROR_INVALID_PARAMETER; - -void Platform_UnicodeExpand(void* dstPtr, STRING_PURE String* src) { - if (src->length > FILENAME_SIZE) ErrorHandler_Fail("String too long to expand"); - WCHAR* dst = dstPtr; - - Int32 i; - for (i = 0; i < src->length; i++) { - *dst = Convert_CP437ToUnicode(src->buffer[i]); dst++; - } - *dst = '\0'; -} - -static void Platform_InitDisplay(void) { - HDC hdc = GetDC(NULL); - struct DisplayDevice device = { 0 }; - - device.Bounds.Width = GetSystemMetrics(SM_CXSCREEN); - device.Bounds.Height = GetSystemMetrics(SM_CYSCREEN); - device.BitsPerPixel = GetDeviceCaps(hdc, BITSPIXEL); - DisplayDevice_Default = device; - - ReleaseDC(NULL, hdc); -} - -void Platform_Init(void) { - Platform_InitDisplay(); - heap = GetProcessHeap(); /* TODO: HeapCreate instead? probably not */ - hdc = CreateCompatibleDC(NULL); - if (!hdc) ErrorHandler_Fail("Failed to get screen DC"); - - SetTextColor(hdc, 0x00FFFFFF); - SetBkColor(hdc, 0x00000000); - SetBkMode(hdc, OPAQUE); - - stopwatch_highResolution = QueryPerformanceFrequency(&stopwatch_freq); - WSADATA wsaData; - ReturnCode wsaResult = WSAStartup(MAKEWORD(2, 2), &wsaData); - ErrorHandler_CheckOrFail(wsaResult, "WSAStartup failed"); -} - -void Platform_Free(void) { - DeleteDC(hdc); - WSACleanup(); - HeapDestroy(heap); -} - -void Platform_Exit(ReturnCode code) { - ExitProcess(code); -} - -STRING_PURE String Platform_GetCommandLineArgs(void) { - String args = String_FromReadonly(GetCommandLineA()); - - Int32 argsStart; - if (args.buffer[0] == '"') { - /* Handle path argument in full "path" form, which can include spaces */ - argsStart = String_IndexOf(&args, '"', 1) + 1; - } else { - argsStart = String_IndexOf(&args, ' ', 0) + 1; - } - - if (argsStart == 0) argsStart = args.length; - args = String_UNSAFE_SubstringAt(&args, argsStart); - - /* get rid of duplicate leading spaces before first arg */ - while (args.length && args.buffer[0] == ' ') { - args = String_UNSAFE_SubstringAt(&args, 1); - } - return args; -} - -static void Platform_AllocFailed(const UChar* place) { - UChar logBuffer[String_BufferSize(STRING_SIZE + 20)]; - String log = String_InitAndClearArray(logBuffer); - String_Format1(&log, "Failed allocating memory for: %c", place); - ErrorHandler_Fail(log.buffer); -} - -void* Mem_Alloc(UInt32 numElems, UInt32 elemsSize, const UChar* place) { - UInt32 numBytes = numElems * elemsSize; /* TODO: avoid overflow here */ - void* ptr = HeapAlloc(heap, 0, numBytes); - if (!ptr) Platform_AllocFailed(place); - return ptr; -} - -void* Mem_AllocCleared(UInt32 numElems, UInt32 elemsSize, const UChar* place) { - UInt32 numBytes = numElems * elemsSize; /* TODO: avoid overflow here */ - void* ptr = HeapAlloc(heap, HEAP_ZERO_MEMORY, numBytes); - if (!ptr) Platform_AllocFailed(place); - return ptr; -} - -void* Mem_Realloc(void* mem, UInt32 numElems, UInt32 elemsSize, const UChar* place) { - UInt32 numBytes = numElems * elemsSize; /* TODO: avoid overflow here */ - void* ptr = HeapReAlloc(heap, 0, mem, numBytes); - if (!ptr) Platform_AllocFailed(place); - return ptr; -} - -void Mem_Free(void** mem) { - if (mem == NULL || *mem == NULL) return; - HeapFree(heap, 0, *mem); - *mem = NULL; -} - -void Mem_Set(void* dst, UInt8 value, UInt32 numBytes) { - memset(dst, value, numBytes); -} - -void Mem_Copy(void* dst, void* src, UInt32 numBytes) { - memcpy(dst, src, numBytes); -} - - -void Platform_Log(STRING_PURE String* message) { - /* TODO: log to console */ - OutputDebugStringA(message->buffer); - OutputDebugStringA("\n"); -} - -void Platform_LogConst(const UChar* message) { - /* TODO: log to console */ - OutputDebugStringA(message); - OutputDebugStringA("\n"); -} - -void Platform_Log4(const UChar* format, const void* a1, const void* a2, const void* a3, const void* a4) { - UChar msgBuffer[String_BufferSize(512)]; - String msg = String_InitAndClearArray(msgBuffer); - String_Format4(&msg, format, a1, a2, a3, a4); - Platform_Log(&msg); -} - -void Platform_FromSysTime(DateTime* time, SYSTEMTIME* sysTime) { - time->Year = sysTime->wYear; - time->Month = sysTime->wMonth; - time->Day = sysTime->wDay; - time->Hour = sysTime->wHour; - time->Minute = sysTime->wMinute; - time->Second = sysTime->wSecond; - time->Milli = sysTime->wMilliseconds; -} - -void DateTime_CurrentUTC(DateTime* time) { - SYSTEMTIME utcTime; - GetSystemTime(&utcTime); - Platform_FromSysTime(time, &utcTime); -} - -void DateTime_CurrentLocal(DateTime* time) { - SYSTEMTIME localTime; - GetLocalTime(&localTime); - Platform_FromSysTime(time, &localTime); -} - - -bool File_Exists(STRING_PURE String* path) { - WCHAR data[512]; Platform_UnicodeExpand(data, path); - UInt32 attribs = GetFileAttributesW(data); - return attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY); -} - -bool Directory_Exists(STRING_PURE String* path) { - WCHAR data[512]; Platform_UnicodeExpand(data, path); - UInt32 attribs = GetFileAttributesW(data); - return attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY); -} - -ReturnCode Directory_Create(STRING_PURE String* path) { - WCHAR data[512]; Platform_UnicodeExpand(data, path); - BOOL success = CreateDirectoryW(data, NULL); - return success ? 0 : GetLastError(); -} - -ReturnCode Directory_Enum(STRING_PURE String* path, void* obj, Directory_EnumCallback callback) { - UChar fileBuffer[String_BufferSize(MAX_PATH + 10)]; - String file = String_InitAndClearArray(fileBuffer); - /* Need to append \* to search for files in directory */ - String_Format1(&file, "%s\\*", path); - WCHAR data[512]; Platform_UnicodeExpand(data, &file); - - WIN32_FIND_DATAW entry; - HANDLE find = FindFirstFileW(data, &entry); - if (find == INVALID_HANDLE_VALUE) return GetLastError(); - - do { - if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; - String_Clear(&file); - Int32 i; - - for (i = 0; i < MAX_PATH && entry.cFileName[i] != '\0'; i++) { - String_Append(&file, Convert_UnicodeToCP437(entry.cFileName[i])); - } - - Utils_UNSAFE_GetFilename(&file); - callback(&file, obj); - } while (FindNextFileW(find, &entry)); - - ReturnCode result = GetLastError(); /* return code from FindNextFile */ - FindClose(find); - return result == ERROR_NO_MORE_FILES ? 0 : result; -} - -ReturnCode File_GetModifiedTime(STRING_PURE String* path, DateTime* time) { - void* file; ReturnCode result = File_Open(&file, path); - if (result) return result; - - FILETIME writeTime; - if (GetFileTime(file, NULL, NULL, &writeTime)) { - SYSTEMTIME sysTime; - FileTimeToSystemTime(&writeTime, &sysTime); - Platform_FromSysTime(time, &sysTime); - } else { - result = GetLastError(); - } - - File_Close(file); - return result; -} - - -ReturnCode File_Do(void** file, STRING_PURE String* path, DWORD access, DWORD createMode) { - WCHAR data[512]; Platform_UnicodeExpand(data, path); - *file = CreateFileW(data, access, FILE_SHARE_READ, NULL, createMode, 0, NULL); - return *file != INVALID_HANDLE_VALUE ? 0 : GetLastError(); -} - -ReturnCode File_Open(void** file, STRING_PURE String* path) { - return File_Do(file, path, GENERIC_READ, OPEN_EXISTING); -} -ReturnCode File_Create(void** file, STRING_PURE String* path) { - return File_Do(file, path, GENERIC_WRITE, CREATE_ALWAYS); -} -ReturnCode File_Append(void** file, STRING_PURE String* path) { - ReturnCode code = File_Do(file, path, GENERIC_WRITE, OPEN_ALWAYS); - if (code != 0) return code; - return File_Seek(*file, 0, STREAM_SEEKFROM_END); -} - -ReturnCode File_Read(void* file, UInt8* buffer, UInt32 count, UInt32* bytesRead) { - BOOL success = ReadFile((HANDLE)file, buffer, count, bytesRead, NULL); - return success ? 0 : GetLastError(); -} - -ReturnCode File_Write(void* file, UInt8* buffer, UInt32 count, UInt32* bytesWrote) { - BOOL success = WriteFile((HANDLE)file, buffer, count, bytesWrote, NULL); - return success ? 0 : GetLastError(); -} - -ReturnCode File_Close(void* file) { - return CloseHandle((HANDLE)file) ? 0 : GetLastError(); -} - -ReturnCode File_Seek(void* file, Int32 offset, Int32 seekType) { - DWORD mode = -1; - switch (seekType) { - case STREAM_SEEKFROM_BEGIN: mode = FILE_BEGIN; break; - case STREAM_SEEKFROM_CURRENT: mode = FILE_CURRENT; break; - case STREAM_SEEKFROM_END: mode = FILE_END; break; - } - - DWORD pos = SetFilePointer(file, offset, NULL, mode); - return pos == INVALID_SET_FILE_POINTER ? GetLastError() : 0; -} - -ReturnCode File_Position(void* file, UInt32* position) { - *position = SetFilePointer(file, 0, NULL, 1); /* SEEK_CUR */ - return *position == INVALID_SET_FILE_POINTER ? GetLastError() : 0; -} - -ReturnCode File_Length(void* file, UInt32* length) { - *length = GetFileSize(file, NULL); - return *length == INVALID_FILE_SIZE ? GetLastError() : 0; -} - - -void Thread_Sleep(UInt32 milliseconds) { - Sleep(milliseconds); -} - -DWORD WINAPI Thread_StartCallback(LPVOID lpParam) { - Thread_StartFunc* func = (Thread_StartFunc*)lpParam; - (*func)(); - return 0; -} - -void* Thread_Start(Thread_StartFunc* func) { - DWORD threadID; - void* handle = CreateThread(NULL, 0, Thread_StartCallback, func, 0, &threadID); - if (!handle) { - ErrorHandler_FailWithCode(GetLastError(), "Creating thread"); - } - return handle; -} - -void Thread_Join(void* handle) { - WaitForSingleObject((HANDLE)handle, INFINITE); -} - -void Thread_FreeHandle(void* handle) { - if (!CloseHandle((HANDLE)handle)) { - ErrorHandler_FailWithCode(GetLastError(), "Freeing thread handle"); - } -} - -CRITICAL_SECTION mutexList[3]; Int32 mutexIndex; -void* Mutex_Create(void) { - if (mutexIndex == Array_Elems(mutexList)) ErrorHandler_Fail("Cannot allocate mutex"); - CRITICAL_SECTION* ptr = &mutexList[mutexIndex]; - InitializeCriticalSection(ptr); mutexIndex++; - return ptr; -} - -void Mutex_Free(void* handle) { - DeleteCriticalSection((CRITICAL_SECTION*)handle); -} - -void Mutex_Lock(void* handle) { - EnterCriticalSection((CRITICAL_SECTION*)handle); -} - -void Mutex_Unlock(void* handle) { - LeaveCriticalSection((CRITICAL_SECTION*)handle); -} - -void* Waitable_Create(void) { - void* handle = CreateEventW(NULL, false, false, NULL); - if (!handle) { - ErrorHandler_FailWithCode(GetLastError(), "Creating waitable"); - } - return handle; -} - -void Waitable_Free(void* handle) { - if (!CloseHandle((HANDLE)handle)) { - ErrorHandler_FailWithCode(GetLastError(), "Freeing waitable"); - } -} - -void Waitable_Signal(void* handle) { - SetEvent((HANDLE)handle); -} - -void Waitable_Wait(void* handle) { - WaitForSingleObject((HANDLE)handle, INFINITE); -} - -void Waitable_WaitFor(void* handle, UInt32 milliseconds) { - WaitForSingleObject((HANDLE)handle, milliseconds); -} - -void Stopwatch_Measure(struct Stopwatch* timer) { - if (stopwatch_highResolution) { - LARGE_INTEGER value; - QueryPerformanceCounter(&value); - timer->Data[0] = value.QuadPart; - } else { - FILETIME value; - GetSystemTimeAsFileTime(&value); - timer->Data[0] = (Int64)value.dwLowDateTime | ((Int64)value.dwHighDateTime << 32); - } -} -void Stopwatch_Start(struct Stopwatch* timer) { Stopwatch_Measure(timer); } - -/* TODO: check this is actually accurate */ -Int32 Stopwatch_ElapsedMicroseconds(struct Stopwatch* timer) { - Int64 start = timer->Data[0]; - Stopwatch_Measure(timer); - Int64 end = timer->Data[0]; - - if (stopwatch_highResolution) { - return (Int32)(((end - start) * 1000 * 1000) / stopwatch_freq.QuadPart); - } else { - return (Int32)((end - start) / 10); - } -} - -void Font_Make(struct FontDesc* desc, STRING_PURE String* fontName, UInt16 size, UInt16 style) { - desc->Size = size; - desc->Style = style; - LOGFONTA font = { 0 }; - - font.lfHeight = -Math_CeilDiv(size * GetDeviceCaps(hdc, LOGPIXELSY), 72); - font.lfUnderline = style == FONT_STYLE_UNDERLINE; - font.lfWeight = style == FONT_STYLE_BOLD ? FW_BOLD : FW_NORMAL; - font.lfQuality = ANTIALIASED_QUALITY; /* TODO: CLEARTYPE_QUALITY looks slightly better */ - - String dstName = String_Init(font.lfFaceName, 0, LF_FACESIZE); - String_AppendString(&dstName, fontName); - desc->Handle = CreateFontIndirectA(&font); - if (!desc->Handle) ErrorHandler_Fail("Creating font handle failed"); -} - -void Font_Free(struct FontDesc* desc) { - if (!DeleteObject(desc->Handle)) ErrorHandler_Fail("Deleting font handle failed"); - desc->Handle = NULL; -} - -/* TODO: not associate font with device so much */ -struct Size2D Platform_TextMeasure(struct DrawTextArgs* args) { - WCHAR data[512]; Platform_UnicodeExpand(data, &args->Text); - HGDIOBJ oldFont = SelectObject(hdc, args->Font.Handle); - SIZE area; GetTextExtentPointW(hdc, data, args->Text.length, &area); - - SelectObject(hdc, oldFont); - return Size2D_Make(area.cx, area.cy); -} - -HBITMAP platform_dib; -HBITMAP platform_oldBmp; -struct Bitmap* platform_bmp; -void* platform_bits; - -void Platform_SetBitmap(struct Bitmap* bmp) { - platform_bmp = bmp; - platform_bits = NULL; - - BITMAPINFO bmi = { 0 }; - bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = bmp->Width; - bmi.bmiHeader.biHeight = -bmp->Height; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - - platform_dib = CreateDIBSection(hdc, &bmi, 0, &platform_bits, NULL, 0); - if (!platform_dib) ErrorHandler_Fail("Failed to allocate DIB for text"); - platform_oldBmp = SelectObject(hdc, platform_dib); -} - -/* TODO: check return codes and stuff */ -/* TODO: make text prettier.. somehow? */ -/* TODO: Do we need to / 255 instead of >> 8 ? */ -struct Size2D Platform_TextDraw(struct DrawTextArgs* args, Int32 x, Int32 y, PackedCol col) { - WCHAR strUnicode[String_BufferSize(FILENAME_SIZE)]; - Platform_UnicodeExpand(strUnicode, &args->Text); - - HGDIOBJ oldFont = (HFONT)SelectObject(hdc, (HFONT)args->Font.Handle); - SIZE area; GetTextExtentPointW(hdc, strUnicode, args->Text.length, &area); - TextOutW(hdc, 0, 0, strUnicode, args->Text.length); - - Int32 xx, yy; - struct Bitmap* bmp = platform_bmp; - for (yy = 0; yy < area.cy; yy++) { - UInt8* src = (UInt8*)platform_bits + (yy * (bmp->Width << 2)); - UInt8* dst = (UInt8*)Bitmap_GetRow(bmp, y + yy); dst += x * BITMAP_SIZEOF_PIXEL; - - for (xx = 0; xx < area.cx; xx++) { - UInt8 intensity = *src, invIntensity = UInt8_MaxValue - intensity; - dst[0] = ((col.B * intensity) >> 8) + ((dst[0] * invIntensity) >> 8); - dst[1] = ((col.G * intensity) >> 8) + ((dst[1] * invIntensity) >> 8); - dst[2] = ((col.R * intensity) >> 8) + ((dst[2] * invIntensity) >> 8); - //dst[3] = ((col.A * intensity) >> 8) + ((dst[3] * invIntensity) >> 8); - dst[3] = intensity + ((dst[3] * invIntensity) >> 8); - src += BITMAP_SIZEOF_PIXEL; dst += BITMAP_SIZEOF_PIXEL; - } - } - - SelectObject(hdc, oldFont); - //DrawTextA(hdc, args->Text.buffer, args->Text.length, - // &r, DT_NOPREFIX | DT_SINGLELINE | DT_NOCLIP); - return Size2D_Make(area.cx, area.cy); -} - -void Platform_ReleaseBitmap(void) { - /* TODO: Check return values */ - SelectObject(hdc, platform_oldBmp); - DeleteObject(platform_dib); - - platform_oldBmp = NULL; - platform_dib = NULL; - platform_bmp = NULL; -} - -HINTERNET hInternet; -void Http_Init(void) { - /* TODO: Should we use INTERNET_OPEN_TYPE_PRECONFIG instead? */ - hInternet = InternetOpenA(PROGRAM_APP_NAME, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); - if (!hInternet) ErrorHandler_FailWithCode(GetLastError(), "Failed to init WinINet"); -} - -ReturnCode Http_MakeRequest(struct AsyncRequest* request, void** handle) { - String url = String_FromRawArray(request->URL); - UChar headersBuffer[String_BufferSize(STRING_SIZE * 2)]; - String headers = String_MakeNull(); - - /* https://stackoverflow.com/questions/25308488/c-wininet-custom-http-headers */ - if (request->Etag[0] || request->LastModified.Year) { - headers = String_InitAndClearArray(headersBuffer); - if (request->LastModified.Year > 0) { - String_AppendConst(&headers, "If-Modified-Since: "); - DateTime_HttpDate(&request->LastModified, &headers); - String_AppendConst(&headers, "\r\n"); - } - - if (request->Etag[0]) { - String etag = String_FromRawArray(request->Etag); - String_AppendConst(&headers, "If-None-Match: "); - String_AppendString(&headers, &etag); - String_AppendConst(&headers, "\r\n"); - } - String_AppendConst(&headers, "\r\n\r\n"); - } - - *handle = InternetOpenUrlA(hInternet, url.buffer, headers.buffer, headers.length, - INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD, NULL); - return *handle == NULL ? GetLastError() : 0; -} - -/* TODO: Test last modified and etag even work */ -#define Http_Query(flags, result) HttpQueryInfoA(handle, flags, result, &bufferLen, NULL) -ReturnCode Http_GetRequestHeaders(struct AsyncRequest* request, void* handle, UInt32* size) { - DWORD bufferLen; - - UInt32 status; - bufferLen = sizeof(DWORD); - if (!Http_Query(HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status)) return GetLastError(); - request->StatusCode = status; - - bufferLen = sizeof(DWORD); - if (!Http_Query(HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, size)) return GetLastError(); - - SYSTEMTIME lastModified; - bufferLen = sizeof(SYSTEMTIME); - if (Http_Query(HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &lastModified)) { - Platform_FromSysTime(&request->LastModified, &lastModified); - } - - String etag = String_InitAndClearArray(request->Etag); - bufferLen = etag.capacity; - Http_Query(HTTP_QUERY_ETAG, etag.buffer); - - return 0; -} - -ReturnCode Http_GetRequestData(struct AsyncRequest* request, void* handle, void** data, UInt32 size, volatile Int32* progress) { - if (size == 0) return ERROR_NOT_SUPPORTED; - *data = Mem_Alloc(size, sizeof(UInt8), "http get data"); - - *progress = 0; - UInt8* buffer = *data; - UInt32 left = size, read, totalRead = 0; - - while (left > 0) { - UInt32 toRead = left, avail = 0; - /* only read as much data that is pending */ - if (InternetQueryDataAvailable(handle, &avail, 0, NULL)) { - toRead = min(toRead, avail); - } - - bool success = InternetReadFile(handle, buffer, toRead, &read); - if (!success) { Mem_Free(data); return GetLastError(); } - - if (!read) break; - buffer += read; totalRead += read; left -= read; - *progress = (Int32)(100.0f * totalRead / size); - } - - *progress = 100; - return 0; -} - -ReturnCode Http_FreeRequest(void* handle) { - return InternetCloseHandle(handle) ? 0 : GetLastError(); -} - -ReturnCode Http_Free(void) { - return InternetCloseHandle(hInternet) ? 0 : GetLastError(); -} - - -struct AudioContext { - HWAVEOUT Handle; - WAVEHDR Headers[AUDIO_MAX_CHUNKS]; - struct AudioFormat Format; - Int32 NumBuffers; -}; -struct AudioContext Audio_Contexts[20]; - -void Audio_Init(AudioHandle* handle, Int32 buffers) { - Int32 i, j; - for (i = 0; i < Array_Elems(Audio_Contexts); i++) { - struct AudioContext* ctx = &Audio_Contexts[i]; - if (ctx->NumBuffers) continue; - ctx->NumBuffers = buffers; - - *handle = i; - for (j = 0; j < buffers; j++) { - ctx->Headers[j].dwFlags = WHDR_DONE; - } - return; - } - ErrorHandler_Fail("No free audio contexts"); -} - -void Audio_Free(AudioHandle handle) { - struct AudioContext* ctx = &Audio_Contexts[handle]; - if (!ctx->Handle) return; - - ReturnCode result = waveOutClose(ctx->Handle); - ErrorHandler_CheckOrFail(result, "Audio - closing device"); - Mem_Set(ctx, 0, sizeof(struct AudioContext)); -} - -struct AudioFormat* Audio_GetFormat(AudioHandle handle) { - struct AudioContext* ctx = &Audio_Contexts[handle]; - return &ctx->Format; -} - -void Audio_SetFormat(AudioHandle handle, struct AudioFormat* format) { - struct AudioContext* ctx = &Audio_Contexts[handle]; - struct AudioFormat* cur = &ctx->Format; - - /* only recreate handle if we need to */ - if (AudioFormat_Eq(cur, format)) return; - if (ctx->Handle) { - ReturnCode result = waveOutClose(ctx->Handle); - ErrorHandler_CheckOrFail(result, "Audio - closing device"); - } - - WAVEFORMATEX fmt = { 0 }; - fmt.nChannels = format->Channels; - fmt.wFormatTag = WAVE_FORMAT_PCM; - fmt.wBitsPerSample = format->BitsPerSample; - fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8; - fmt.nSamplesPerSec = format->SampleRate; - fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; - - if (waveOutGetNumDevs() == 0u) ErrorHandler_Fail("No audio devices found"); - ReturnCode result = waveOutOpen(&ctx->Handle, WAVE_MAPPER, &fmt, NULL, NULL, CALLBACK_NULL); - ErrorHandler_CheckOrFail(result, "Audio - opening device"); - ctx->Format = *format; -} - -void Audio_PlayData(AudioHandle handle, Int32 idx, void* data, UInt32 dataSize) { - struct AudioContext* ctx = &Audio_Contexts[handle]; - WAVEHDR* hdr = &ctx->Headers[idx]; - Mem_Set(hdr, 0, sizeof(WAVEHDR)); - - hdr->lpData = data; - hdr->dwBufferLength = dataSize; - hdr->dwLoops = 1; - - ReturnCode result = waveOutPrepareHeader(ctx->Handle, hdr, sizeof(WAVEHDR)); - ErrorHandler_CheckOrFail(result, "Audio - prepare header"); - result = waveOutWrite(ctx->Handle, hdr, sizeof(WAVEHDR)); - ErrorHandler_CheckOrFail(result, "Audio - write header"); -} - -bool Audio_IsCompleted(AudioHandle handle, Int32 idx) { - struct AudioContext* ctx = &Audio_Contexts[handle]; - WAVEHDR* hdr = &ctx->Headers[idx]; - if (!(hdr->dwFlags & WHDR_DONE)) return false; - - if (hdr->dwFlags & WHDR_PREPARED) { - ReturnCode result = waveOutUnprepareHeader(ctx->Handle, hdr, sizeof(WAVEHDR)); - ErrorHandler_CheckOrFail(result, "Audio - unprepare header"); - } - return true; -} - -bool Audio_IsFinished(AudioHandle handle) { - struct AudioContext* ctx = &Audio_Contexts[handle]; - Int32 i; - for (i = 0; i < ctx->NumBuffers; i++) { - if (!Audio_IsCompleted(handle, i)) return false; - } - return true; -} -#endif diff --git a/src/Client/WinWindow.c b/src/Client/WinWindow.c index 5daa91a8f..671d51d32 100644 --- a/src/Client/WinWindow.c +++ b/src/Client/WinWindow.c @@ -411,7 +411,7 @@ void Window_Create(Int32 x, Int32 y, Int32 width, Int32 height, STRING_REF Strin if (atom == 0) { ErrorHandler_FailWithCode(GetLastError(), "Failed to register window class"); } - WCHAR data[512]; Platform_UnicodeExpand(data, title); + WCHAR data[512]; Platform_ConvertString(data, title); win_Handle = CreateWindowExW(0, atom, data, win_Style, rect.left, rect.top, RECT_WIDTH(rect), RECT_HEIGHT(rect),