From 71c1e243fe933ce248a3e2fe64ce31095bd3ecba Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Fri, 10 Jan 2020 13:31:12 +1000 Subject: [PATCH] Remove YBaseLib dependency --- .gitmodules | 3 - dep/CMakeLists.txt | 87 -- dep/YBaseLib | 1 - duckstation.sln | 19 - src/common/CMakeLists.txt | 29 +- src/common/align.h | 39 + src/common/assert.cpp | 135 ++ src/common/assert.h | 48 + src/common/audio_stream.cpp | 4 +- src/common/byte_stream.cpp | 1325 +++++++++++++++++ src/common/byte_stream.h | 226 +++ src/common/cd_image.cpp | 4 +- src/common/cd_image_bin.cpp | 5 +- src/common/cd_image_cue.cpp | 9 +- src/common/cd_subchannel_replacement.cpp | 6 +- src/common/common.vcxproj | 39 +- src/common/common.vcxproj.filters | 20 + src/common/cpu_detect.h | 31 + src/common/d3d11/shader_compiler.cpp | 19 +- src/common/d3d11/shader_compiler.h | 2 +- src/common/d3d11/staging_texture.cpp | 3 +- src/common/d3d11/staging_texture.h | 2 +- src/common/d3d11/stream_buffer.cpp | 4 +- src/common/d3d11/stream_buffer.h | 2 +- src/common/d3d11/texture.cpp | 2 +- src/common/d3d11/texture.h | 2 +- src/common/fifo_queue.h | 2 +- src/common/file_system.cpp | 1275 ++++++++++++++++ src/common/file_system.h | 176 +++ src/common/gl/program.cpp | 7 +- src/common/gl/stream_buffer.cpp | 3 +- src/common/gl/texture.cpp | 4 +- src/common/iso_reader.cpp | 2 +- src/common/jit_code_buffer.cpp | 27 +- src/common/log.cpp | 343 +++++ src/common/log.h | 92 ++ src/common/md5_digest.cpp | 226 +++ src/common/md5_digest.h | 20 + src/common/rectangle.h | 1 + src/common/state_wrapper.cpp | 4 +- src/common/state_wrapper.h | 2 +- src/common/string.cpp | 1039 +++++++++++++ src/common/string.h | 373 +++++ src/common/string_util.cpp | 140 ++ src/common/string_util.h | 29 + src/common/timer.cpp | 127 ++ src/common/timer.h | 29 + src/common/timestamp.cpp | 518 +++++++ src/common/timestamp.h | 75 + src/common/types.h | 52 +- src/common/windows_headers.h | 39 + src/core/CMakeLists.txt | 2 +- src/core/analog_controller.cpp | 4 +- src/core/bios.cpp | 5 +- src/core/bus.cpp | 17 +- src/core/bus.h | 5 +- src/core/cdrom.cpp | 4 +- src/core/core.vcxproj | 19 +- src/core/cpu_code_cache.cpp | 2 +- src/core/cpu_core.cpp | 3 +- src/core/cpu_core.inl | 2 +- src/core/cpu_disasm.h | 2 +- src/core/cpu_recompiler_code_generator.cpp | 8 +- src/core/cpu_recompiler_code_generator.h | 11 - .../cpu_recompiler_code_generator_aarch64.cpp | 2 +- src/core/cpu_recompiler_register_cache.cpp | 2 +- src/core/cpu_recompiler_register_cache.h | 2 +- src/core/cpu_recompiler_types.h | 29 +- src/core/cpu_types.cpp | 2 +- src/core/digital_controller.cpp | 2 +- src/core/dma.cpp | 2 +- src/core/game_list.cpp | 149 +- src/core/game_list.h | 7 +- src/core/gpu.cpp | 2 +- src/core/gpu.h | 1 + src/core/gpu_commands.cpp | 13 +- src/core/gpu_hw.cpp | 4 +- src/core/gpu_hw_d3d11.cpp | 5 +- src/core/gpu_hw_opengl.cpp | 5 +- src/core/gpu_hw_opengl_es.cpp | 5 +- src/core/gpu_hw_shadergen.cpp | 4 +- src/core/gpu_sw.cpp | 4 +- src/core/host_interface.cpp | 81 +- src/core/host_interface.h | 14 +- src/core/interrupt_controller.cpp | 2 +- src/core/mdec.cpp | 2 +- src/core/memory_card.cpp | 11 +- src/core/pad.cpp | 22 +- src/core/settings.cpp | 16 +- src/core/sio.cpp | 2 +- src/core/spu.cpp | 2 +- src/core/system.cpp | 28 +- src/core/system.h | 3 - src/core/timers.cpp | 2 +- src/duckstation-qt/CMakeLists.txt | 7 +- src/duckstation-qt/d3d11displaywindow.cpp | 3 +- src/duckstation-qt/d3d11displaywindow.h | 2 +- src/duckstation-qt/duckstation-qt.vcxproj | 19 +- src/duckstation-qt/main.cpp | 10 +- src/duckstation-qt/opengldisplaywindow.cpp | 6 +- src/duckstation-qt/qthostinterface.cpp | 24 +- src/duckstation-qt/qtutils.cpp | 10 +- src/duckstation/CMakeLists.txt | 2 +- src/duckstation/d3d11_host_display.cpp | 3 +- src/duckstation/d3d11_host_display.h | 2 +- src/duckstation/duckstation.vcxproj | 19 +- src/duckstation/main.cpp | 40 +- src/duckstation/opengl_host_display.cpp | 3 +- src/duckstation/sdl_audio_stream.cpp | 4 +- src/duckstation/sdl_host_interface.cpp | 69 +- src/duckstation/sdl_host_interface.h | 5 +- src/duckstation/sdl_settings_interface.cpp | 2 +- 112 files changed, 6888 insertions(+), 522 deletions(-) delete mode 160000 dep/YBaseLib create mode 100644 src/common/align.h create mode 100644 src/common/assert.cpp create mode 100644 src/common/assert.h create mode 100644 src/common/byte_stream.cpp create mode 100644 src/common/byte_stream.h create mode 100644 src/common/cpu_detect.h create mode 100644 src/common/file_system.cpp create mode 100644 src/common/file_system.h create mode 100644 src/common/log.cpp create mode 100644 src/common/log.h create mode 100644 src/common/md5_digest.cpp create mode 100644 src/common/md5_digest.h create mode 100644 src/common/string.cpp create mode 100644 src/common/string.h create mode 100644 src/common/string_util.cpp create mode 100644 src/common/string_util.h create mode 100644 src/common/timer.cpp create mode 100644 src/common/timer.h create mode 100644 src/common/timestamp.cpp create mode 100644 src/common/timestamp.h create mode 100644 src/common/windows_headers.h diff --git a/.gitmodules b/.gitmodules index fce060378..e69de29bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "dep/YBaseLib"] - path = dep/YBaseLib - url = https://github.com/stenzek/YBaseLib.git diff --git a/dep/CMakeLists.txt b/dep/CMakeLists.txt index e3d76fc68..f8f96b471 100644 --- a/dep/CMakeLists.txt +++ b/dep/CMakeLists.txt @@ -13,90 +13,3 @@ if(${CPU_ARCH} STREQUAL "aarch64") add_subdirectory(vixl) endif() -###################### YBaseLib ############################ - -set(YBASELIB_SRC_BASE ${CMAKE_SOURCE_DIR}/dep/YBaseLib/Source) -set(YBASELIB_INCLUDES ${CMAKE_SOURCE_DIR}/dep/YBaseLib/Include) -set(YBASELIB_SRC_FILES - ${YBASELIB_SRC_BASE}/YBaseLib/Android - ${YBASELIB_SRC_BASE}/YBaseLib/Android/AndroidBarrier.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Android/AndroidConditionVariable.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Android/AndroidEvent.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Android/AndroidFileSystem.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Android/AndroidPlatform.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Android/AndroidReadWriteLock.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Android/AndroidThread.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Assert.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Atomic.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/BinaryBlob.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/BinaryReadBuffer.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/BinaryReader.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/BinaryWriteBuffer.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/BinaryWriter.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/ByteStream.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/CallbackQueue.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/CircularBuffer.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/CPUID.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/CRC32.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/CString.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Endian.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Error.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Exception.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/FileSystem.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/HashTrait.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/HTML5 - ${YBASELIB_SRC_BASE}/YBaseLib/HTML5/HTML5Barrier.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/HTML5/HTML5ConditionVariable.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/HTML5/HTML5FileSystem.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/HTML5/HTML5Platform.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/HTML5/HTML5ReadWriteLock.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/HTML5/HTML5Thread.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Log.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Math.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/MD5Digest.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Memory.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/NameTable.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/NumericLimits.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/POSIX - ${YBASELIB_SRC_BASE}/YBaseLib/POSIX/POSIXConditionVariable.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/POSIX/POSIXFileSystem.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/POSIX/POSIXPlatform.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/POSIX/POSIXReadWriteLock.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/POSIX/POSIXSubprocess.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/POSIX/POSIXThread.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/ProgressCallbacks.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/ReferenceCounted.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Sockets - ${YBASELIB_SRC_BASE}/YBaseLib/Sockets/Generic - ${YBASELIB_SRC_BASE}/YBaseLib/Sockets/Generic/BufferedStreamSocket.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Sockets/Generic/ListenSocket.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Sockets/Generic/SocketMultiplexer.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Sockets/Generic/StreamSocket.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Sockets/SocketAddress.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/StringConverter.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/String.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/StringParser.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/TaskQueue.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/TextReader.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/TextWriter.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/ThreadPool.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Timer.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Timestamp.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Windows - ${YBASELIB_SRC_BASE}/YBaseLib/Windows/WindowsBarrier.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Windows/WindowsConditionVariable.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Windows/WindowsFileSystem.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Windows/WindowsPlatform.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Windows/WindowsReadWriteLock.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Windows/WindowsSubprocess.cpp - ${YBASELIB_SRC_BASE}/YBaseLib/Windows/WindowsThread.cpp -) - -add_library(YBaseLib STATIC ${YBASELIB_SRC_FILES}) -target_include_directories(YBaseLib PRIVATE ${YBASELIB_INCLUDES} ${YBASELIB_SRC_BASE}) -target_include_directories(YBaseLib PUBLIC ${YBASELIB_INCLUDES}) -target_link_libraries(YBaseLib PUBLIC Threads::Threads) - -if(ANDROID) - target_link_libraries(YBaseLib PRIVATE log) -endif() diff --git a/dep/YBaseLib b/dep/YBaseLib deleted file mode 160000 index 7004a8b56..000000000 --- a/dep/YBaseLib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7004a8b562373915bb56ea73171c4f3b43fafdea diff --git a/duckstation.sln b/duckstation.sln index 887207f59..9681c0464 100644 --- a/duckstation.sln +++ b/duckstation.sln @@ -15,8 +15,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "src\core\core.vcxpr EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation", "src\duckstation\duckstation.vcxproj", "{DAA8F93D-9C17-4DE2-BD0B-57891E0FF0D9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "YBaseLib", "dep\YBaseLib\Source\YBaseLib.vcxproj", "{B56CE698-7300-4FA5-9609-942F1D05C5A2}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stb", "dep\stb\stb.vcxproj", "{ED601289-AC1A-46B8-A8ED-17DB9EB73423}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcue", "dep\libcue\libcue.vcxproj", "{6A4208ED-E3DC-41E1-81CD-F61025FC285A}" @@ -121,22 +119,6 @@ Global {DAA8F93D-9C17-4DE2-BD0B-57891E0FF0D9}.ReleaseLTCG|x64.Build.0 = ReleaseLTCG|x64 {DAA8F93D-9C17-4DE2-BD0B-57891E0FF0D9}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32 {DAA8F93D-9C17-4DE2-BD0B-57891E0FF0D9}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.Debug|x64.ActiveCfg = Debug|x64 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.Debug|x64.Build.0 = Debug|x64 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.Debug|x86.ActiveCfg = Debug|Win32 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.Debug|x86.Build.0 = Debug|Win32 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.DebugFast|x64.ActiveCfg = DebugFast|x64 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.DebugFast|x64.Build.0 = DebugFast|x64 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.DebugFast|x86.ActiveCfg = DebugFast|Win32 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.DebugFast|x86.Build.0 = DebugFast|Win32 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.Release|x64.ActiveCfg = Release|x64 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.Release|x64.Build.0 = Release|x64 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.Release|x86.ActiveCfg = Release|Win32 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.Release|x86.Build.0 = Release|Win32 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.ReleaseLTCG|x64.Build.0 = ReleaseLTCG|x64 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32 - {B56CE698-7300-4FA5-9609-942F1D05C5A2}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32 {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug|x64.ActiveCfg = Debug|x64 {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug|x64.Build.0 = Debug|x64 {ED601289-AC1A-46B8-A8ED-17DB9EB73423}.Debug|x86.ActiveCfg = Debug|Win32 @@ -236,7 +218,6 @@ Global GlobalSection(NestedProjects) = preSolution {43540154-9E1E-409C-834F-B84BE5621388} = {BA490C0E-497D-4634-A21E-E65012006385} {BB08260F-6FBC-46AF-8924-090EE71360C6} = {BA490C0E-497D-4634-A21E-E65012006385} - {B56CE698-7300-4FA5-9609-942F1D05C5A2} = {BA490C0E-497D-4634-A21E-E65012006385} {ED601289-AC1A-46B8-A8ED-17DB9EB73423} = {BA490C0E-497D-4634-A21E-E65012006385} {6A4208ED-E3DC-41E1-81CD-F61025FC285A} = {BA490C0E-497D-4634-A21E-E65012006385} {ACE32F47-2960-4FB3-9F77-2C375625BF61} = {BA490C0E-497D-4634-A21E-E65012006385} diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 45b7b16ba..e2a5afc50 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,7 +1,12 @@ add_library(common + align.h + assert.cpp + assert.h audio_stream.cpp audio_stream.h bitfield.h + byte_stream.cpp + byte_stream.h cd_image.cpp cd_image.h cd_image_bin.cpp @@ -10,27 +15,44 @@ add_library(common cd_subchannel_replacement.h cd_xa.cpp cd_xa.h + cpu_detect.h + fifo_queue.h + file_system.cpp + file_system.h gl/program.cpp gl/program.h gl/stream_buffer.cpp gl/stream_buffer.h gl/texture.cpp gl/texture.h + heap_array.h iso_reader.cpp iso_reader.h jit_code_buffer.cpp jit_code_buffer.h + log.cpp + log.h + md5_digest.cpp + md5_digest.h null_audio_stream.cpp null_audio_stream.h rectangle.h state_wrapper.cpp state_wrapper.h + string.cpp + string.h + string_util.cpp + string_util.h + timer.cpp + timer.h + timestamp.cpp + timestamp.h types.h ) target_include_directories(common PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(common PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") -target_link_libraries(common PRIVATE YBaseLib glad libcue Threads::Threads) +target_link_libraries(common PRIVATE glad libcue Threads::Threads) if(WIN32) target_sources(common PRIVATE @@ -42,6 +64,11 @@ if(WIN32) d3d11/stream_buffer.h d3d11/texture.cpp d3d11/texture.h + windows_headers.h ) target_link_libraries(common PRIVATE d3dcompiler.lib) endif() + +if(ANDROID) + target_link_libraries(common PRIVATE log) +endif() diff --git a/src/common/align.h b/src/common/align.h new file mode 100644 index 000000000..18783dcb2 --- /dev/null +++ b/src/common/align.h @@ -0,0 +1,39 @@ +#pragma once + +namespace Common { +template +bool IsAligned(T value, unsigned int alignment) +{ + return (value % static_cast(alignment)) == 0; +} +template +T AlignUp(T value, unsigned int alignment) +{ + return (value + static_cast(alignment - 1)) / static_cast(alignment) * static_cast(alignment); +} +template +T AlignDown(T value, unsigned int alignment) +{ + return value / static_cast(alignment) * static_cast(alignment); +} +template +bool IsAlignedPow2(T value, unsigned int alignment) +{ + return (value & static_cast(alignment - 1)) == 0; +} +template +T AlignUpPow2(T value, unsigned int alignment) +{ + return (value + static_cast(alignment - 1)) & static_cast(~static_cast(alignment - 1)); +} +template +T AlignDownPow2(T value, unsigned int alignment) +{ + return value & static_cast(~static_cast(alignment - 1)); +} +template +bool IsPow2(T value) +{ + return (value & (value - 1)) == 0; +} +} // namespace Common diff --git a/src/common/assert.cpp b/src/common/assert.cpp new file mode 100644 index 000000000..838d52201 --- /dev/null +++ b/src/common/assert.cpp @@ -0,0 +1,135 @@ +#include "assert.h" +#include +#include +#include + +#ifdef Y_PLATFORM_WINDOWS +#define WIN32_LEAN_AND_MEAN 1 +#include +#include +#include +#endif + +static std::mutex s_AssertFailedMutex; + +static inline void FreezeThreads(void** ppHandle) +{ +#ifdef Y_PLATFORM_WINDOWS + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hSnapshot != INVALID_HANDLE_VALUE) + { + THREADENTRY32 threadEntry; + if (Thread32First(hSnapshot, &threadEntry)) + { + do + { + if (threadEntry.th32ThreadID == GetCurrentThreadId()) + continue; + + HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadEntry.th32ThreadID); + if (hThread != NULL) + { + SuspendThread(hThread); + CloseHandle(hThread); + } + } while (Thread32Next(hSnapshot, &threadEntry)); + } + } + + *ppHandle = (void*)hSnapshot; +#else + *ppHandle = nullptr; +#endif +} + +static inline void ResumeThreads(void* pHandle) +{ +#ifdef Y_PLATFORM_WINDOWS + HANDLE hSnapshot = (HANDLE)pHandle; + if (pHandle != INVALID_HANDLE_VALUE) + { + THREADENTRY32 threadEntry; + if (Thread32First(hSnapshot, &threadEntry)) + { + do + { + if (threadEntry.th32ThreadID == GetCurrentThreadId()) + continue; + + HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, threadEntry.th32ThreadID); + if (hThread != NULL) + { + ResumeThread(hThread); + CloseHandle(hThread); + } + } while (Thread32Next(hSnapshot, &threadEntry)); + } + CloseHandle(hSnapshot); + } +#else +#endif +} + +void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine) +{ + std::lock_guard guard(s_AssertFailedMutex); + + void* pHandle; + FreezeThreads(&pHandle); + + char szMsg[512]; + std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)", szMessage, szFunction, szFile, uLine); + +#ifdef Y_PLATFORM_WINDOWS + SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, Y_strlen(szMsg), NULL, NULL); + OutputDebugStringA(szMsg); + + Y_strncat(szMsg, sizeof(szMsg), + "\n\nPress Abort to exit, Retry to break to debugger, or Ignore to attempt to continue."); + int result = MessageBoxA(NULL, szMsg, NULL, MB_ABORTRETRYIGNORE | MB_ICONERROR); + if (result == IDRETRY) + __debugbreak(); + else if (result != IDIGNORE) + TerminateProcess(GetCurrentProcess(), 0xBAADC0DE); +#else + fputs(szMsg, stderr); + fputs("\nAborting application.\n", stderr); + fflush(stderr); + abort(); +#endif + + ResumeThreads(pHandle); +} + +void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine) +{ + std::lock_guard guard(s_AssertFailedMutex); + + void* pHandle; + FreezeThreads(&pHandle); + + char szMsg[512]; + std::snprintf(szMsg, sizeof(szMsg), "%s in function %s (%s:%u)", szMessage, szFunction, szFile, uLine); + +#ifdef Y_PLATFORM_WINDOWS + SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY); + WriteConsoleA(GetStdHandle(STD_ERROR_HANDLE), szMsg, Y_strlen(szMsg), NULL, NULL); + OutputDebugStringA(szMsg); + + Y_strncat(szMsg, sizeof(szMsg), + "\n\nDo you want to attempt to break into a debugger? Pressing Cancel will abort the application."); + int result = MessageBoxA(NULL, szMsg, NULL, MB_OKCANCEL | MB_ICONERROR); + if (result == IDOK) + __debugbreak(); + + TerminateProcess(GetCurrentProcess(), 0xBAADC0DE); +#else + fputs(szMsg, stderr); + fputs("\nAborting application.\n", stderr); + fflush(stderr); + abort(); +#endif + + ResumeThreads(pHandle); +} diff --git a/src/common/assert.h b/src/common/assert.h new file mode 100644 index 000000000..733d8248f --- /dev/null +++ b/src/common/assert.h @@ -0,0 +1,48 @@ +#pragma once + +void Y_OnAssertFailed(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine); +void Y_OnPanicReached(const char* szMessage, const char* szFunction, const char* szFile, unsigned uLine); + +#define Assert(expr) \ + if (!(expr)) \ + { \ + Y_OnAssertFailed("Assertion failed: '" #expr "'", __FUNCTION__, __FILE__, __LINE__); \ + } +#define AssertMsg(expr, msg) \ + if (!(expr)) \ + { \ + Y_OnAssertFailed("Assertion failed: '" msg "'", __FUNCTION__, __FILE__, __LINE__); \ + } + +#if Y_BUILD_CONFIG_DEBUG +#define DebugAssert(expr) \ + if (!(expr)) \ + { \ + Y_OnAssertFailed("Debug assertion failed: '" #expr "'", __FUNCTION__, __FILE__, __LINE__); \ + } +#define DebugAssertMsg(expr, msg) \ + if (!(expr)) \ + { \ + Y_OnAssertFailed("Debug assertion failed: '" msg "'", __FUNCTION__, __FILE__, __LINE__); \ + } +#define DebugUnreachableCode() Y_OnPanicReached("Unreachable code reached", __FUNCTION__, __FILE__, __LINE__) +#else +#define DebugAssert(expr) +#define DebugAssertMsg(expr, msg) +#define DebugUnreachableCode() +#endif + +// Panics the application, displaying an error message. +#define Panic(Message) Y_OnPanicReached("Panic triggered: '" Message "'", __FUNCTION__, __FILE__, __LINE__) + +// Kills the application, indicating a pure function call that should not have happened. +#define PureCall() Y_OnPanicReached("PureCall encountered", __FUNCTION__, __FILE__, __LINE__) + +// Kills the application, indicating that code that was never supposed to be reached has been executed. +#define UnreachableCode() Y_OnPanicReached("Unreachable code reached", __FUNCTION__, __FILE__, __LINE__) + +// Helper for switch cases. +#define DefaultCaseIsUnreachable() \ + default: \ + UnreachableCode(); \ + break; diff --git a/src/common/audio_stream.cpp b/src/common/audio_stream.cpp index 996d2fd5f..9bf936f0b 100644 --- a/src/common/audio_stream.cpp +++ b/src/common/audio_stream.cpp @@ -1,5 +1,7 @@ #include "audio_stream.h" -#include "YBaseLib/Assert.h" +#include "assert.h" +#include +#include AudioStream::AudioStream() = default; diff --git a/src/common/byte_stream.cpp b/src/common/byte_stream.cpp new file mode 100644 index 000000000..1ac17aa75 --- /dev/null +++ b/src/common/byte_stream.cpp @@ -0,0 +1,1325 @@ +#include "byte_stream.h" +#include "assert.h" +#include "log.h" +#include +#include +#include +#include +#include +#include +#if defined(WIN32) +#include "windows_headers.h" +#include +#include +#include +#else +#include +#include +#endif + +Log_SetChannel(ByteStream); + +class FileByteStream : public ByteStream +{ +public: + FileByteStream(FILE* pFile) : m_pFile(pFile) { DebugAssert(m_pFile != nullptr); } + + virtual ~FileByteStream() { fclose(m_pFile); } + + virtual bool ReadByte(u8* pDestByte) override + { + if (m_errorState) + return false; + + if (fread(pDestByte, 1, 1, m_pFile) != 1) + { + m_errorState = true; + return false; + } + + return true; + } + + virtual u32 Read(void* pDestination, u32 ByteCount) override + { + if (m_errorState) + return 0; + + u32 readCount = (u32)fread(pDestination, 1, ByteCount, m_pFile); + if (readCount != ByteCount && ferror(m_pFile) != 0) + m_errorState = true; + + return readCount; + } + + virtual bool Read2(void* pDestination, u32 ByteCount, u32* pNumberOfBytesRead /* = nullptr */) override + { + if (m_errorState) + return false; + + u32 bytesRead = Read(pDestination, ByteCount); + + if (pNumberOfBytesRead != nullptr) + *pNumberOfBytesRead = bytesRead; + + if (bytesRead != ByteCount) + { + m_errorState = true; + return false; + } + + return true; + } + + virtual bool WriteByte(u8 SourceByte) override + { + if (m_errorState) + return false; + + if (fwrite(&SourceByte, 1, 1, m_pFile) != 1) + { + m_errorState = true; + return false; + } + + return true; + } + + virtual u32 Write(const void* pSource, u32 ByteCount) override + { + if (m_errorState) + return 0; + + u32 writeCount = (u32)fwrite(pSource, 1, ByteCount, m_pFile); + if (writeCount != ByteCount) + m_errorState = true; + + return writeCount; + } + + virtual bool Write2(const void* pSource, u32 ByteCount, u32* pNumberOfBytesWritten /* = nullptr */) override + { + if (m_errorState) + return false; + + u32 bytesWritten = Write(pSource, ByteCount); + + if (pNumberOfBytesWritten != nullptr) + *pNumberOfBytesWritten = bytesWritten; + + if (bytesWritten != ByteCount) + { + m_errorState = true; + return false; + } + + return true; + } + +#if defined(WIN32) + + virtual bool SeekAbsolute(u64 Offset) override + { + if (m_errorState) + return false; + + if (_fseeki64(m_pFile, Offset, SEEK_SET) != 0) + { + m_errorState = true; + return false; + } + + return true; + } + + virtual bool SeekRelative(s64 Offset) override + { + if (m_errorState) + return false; + + if (_fseeki64(m_pFile, Offset, SEEK_CUR) != 0) + { + m_errorState = true; + return true; + } + + return true; + } + + virtual bool SeekToEnd() override + { + if (m_errorState) + return false; + + if (_fseeki64(m_pFile, 0, SEEK_END) != 0) + { + m_errorState = true; + return false; + } + + return true; + } + + virtual u64 GetPosition() const override { return _ftelli64(m_pFile); } + + virtual u64 GetSize() const override + { + s64 OldPos = _ftelli64(m_pFile); + _fseeki64(m_pFile, 0, SEEK_END); + s64 Size = _ftelli64(m_pFile); + _fseeki64(m_pFile, OldPos, SEEK_SET); + return (u64)Size; + } + +#else + + virtual bool SeekAbsolute(u64 Offset) override + { + if (m_errorState) + return false; + + if (fseeko(m_pFile, static_cast(Offset), SEEK_SET) != 0) + { + m_errorState = true; + return false; + } + + return true; + } + + virtual bool SeekRelative(s64 Offset) override + { + if (m_errorState) + return false; + + if (fseeko(m_pFile, static_cast(Offset), SEEK_CUR) != 0) + { + m_errorState = true; + return false; + } + + return true; + } + + virtual bool SeekToEnd() override + { + if (m_errorState) + return false; + + if (fseeko(m_pFile, 0, SEEK_END) != 0) + { + m_errorState = true; + return false; + } + + return true; + } + + virtual u64 GetPosition() const override { return static_cast(ftello(m_pFile)); } + + virtual u64 GetSize() const override + { + off_t OldPos = ftello(m_pFile); + fseeko(m_pFile, 0, SEEK_END); + off_t Size = ftello(m_pFile); + fseeko(m_pFile, OldPos, SEEK_SET); + return (u64)Size; + } + +#endif + + virtual bool Flush() override + { + if (m_errorState) + return false; + + if (fflush(m_pFile) != 0) + { + m_errorState = true; + return false; + } + + return true; + } + + virtual bool Commit() override { return true; } + + virtual bool Discard() override { return false; } + +protected: + FILE* m_pFile; +}; + +class AtomicUpdatedFileByteStream : public FileByteStream +{ +public: + AtomicUpdatedFileByteStream(FILE* pFile, const char* originalFileName, const char* temporaryFileName) + : FileByteStream(pFile), m_committed(false), m_discarded(false), m_originalFileName(originalFileName), + m_temporaryFileName(temporaryFileName) + { + } + + virtual ~AtomicUpdatedFileByteStream() + { + if (m_discarded) + { +#if WIN32 + // delete the temporary file + if (!DeleteFileA(m_temporaryFileName.c_str())) + Log_WarningPrintf( + "AtomicUpdatedFileByteStream::~AtomicUpdatedFileByteStream(): Failed to delete temporary file '%s'", + m_temporaryFileName.c_str()); +#else + // delete the temporary file + if (remove(m_temporaryFileName.c_str()) < 0) + Log_WarningPrintf( + "AtomicUpdatedFileByteStream::~AtomicUpdatedFileByteStream(): Failed to delete temporary file '%s'", + m_temporaryFileName.c_str()); +#endif + } + else if (!m_committed) + { + Commit(); + } + + // fclose called by FileByteStream destructor + } + + virtual bool Flush() override + { + if (fflush(m_pFile) != 0) + { + m_errorState = true; + return false; + } + + return true; + } + + virtual bool Commit() override + { + Assert(!m_discarded); + if (m_committed) + return Flush(); + + fflush(m_pFile); + +#ifdef WIN32 + // move the atomic file name to the original file name + if (!MoveFileExA(m_temporaryFileName.c_str(), m_originalFileName.c_str(), MOVEFILE_REPLACE_EXISTING)) + { + Log_WarningPrintf("AtomicUpdatedFileByteStream::Commit(): Failed to rename temporary file '%s' to '%s'", + m_temporaryFileName.c_str(), m_originalFileName.c_str()); + m_discarded = true; + } + else + { + m_committed = true; + } +#else + // move the atomic file name to the original file name + if (rename(m_temporaryFileName.c_str(), m_originalFileName.c_str()) < 0) + { + Log_WarningPrintf("AtomicUpdatedFileByteStream::Commit(): Failed to rename temporary file '%s' to '%s'", + m_temporaryFileName.c_str(), m_originalFileName.c_str()); + m_discarded = true; + } + else + { + m_committed = true; + } +#endif + + return (!m_discarded); + } + + virtual bool Discard() override + { + Assert(!m_committed); + m_discarded = true; + return true; + } + +private: + bool m_committed; + bool m_discarded; + std::string m_originalFileName; + std::string m_temporaryFileName; +}; + +NullByteStream::NullByteStream() {} + +NullByteStream::~NullByteStream() {} + +bool NullByteStream::ReadByte(u8* pDestByte) +{ + *pDestByte = 0; + return true; +} + +u32 NullByteStream::Read(void* pDestination, u32 ByteCount) +{ + if (ByteCount > 0) + std::memset(pDestination, 0, ByteCount); + + return ByteCount; +} + +bool NullByteStream::Read2(void* pDestination, u32 ByteCount, u32* pNumberOfBytesRead /* = nullptr */) +{ + if (ByteCount > 0) + std::memset(pDestination, 0, ByteCount); + + if (pNumberOfBytesRead) + *pNumberOfBytesRead = ByteCount; + + return true; +} + +bool NullByteStream::WriteByte(u8 SourceByte) +{ + return true; +} + +u32 NullByteStream::Write(const void* pSource, u32 ByteCount) +{ + return ByteCount; +} + +bool NullByteStream::Write2(const void* pSource, u32 ByteCount, u32* pNumberOfBytesWritten /* = nullptr */) +{ + return true; +} + +bool NullByteStream::SeekAbsolute(u64 Offset) +{ + return true; +} + +bool NullByteStream::SeekRelative(s64 Offset) +{ + return true; +} + +bool NullByteStream::SeekToEnd() +{ + return true; +} + +u64 NullByteStream::GetSize() const +{ + return 0; +} + +u64 NullByteStream::GetPosition() const +{ + return 0; +} + +bool NullByteStream::Flush() +{ + return true; +} + +bool NullByteStream::Commit() +{ + return true; +} + +bool NullByteStream::Discard() +{ + return true; +} + +MemoryByteStream::MemoryByteStream(void* pMemory, u32 MemSize) +{ + m_iPosition = 0; + m_iSize = MemSize; + m_pMemory = (u8*)pMemory; +} + +MemoryByteStream::~MemoryByteStream() {} + +bool MemoryByteStream::ReadByte(u8* pDestByte) +{ + if (m_iPosition < m_iSize) + { + *pDestByte = m_pMemory[m_iPosition++]; + return true; + } + + return false; +} + +u32 MemoryByteStream::Read(void* pDestination, u32 ByteCount) +{ + u32 sz = ByteCount; + if ((m_iPosition + ByteCount) > m_iSize) + sz = m_iSize - m_iPosition; + + if (sz > 0) + { + std::memcpy(pDestination, m_pMemory + m_iPosition, sz); + m_iPosition += sz; + } + + return sz; +} + +bool MemoryByteStream::Read2(void* pDestination, u32 ByteCount, u32* pNumberOfBytesRead /* = nullptr */) +{ + u32 r = Read(pDestination, ByteCount); + if (pNumberOfBytesRead != NULL) + *pNumberOfBytesRead = r; + + return (r == ByteCount); +} + +bool MemoryByteStream::WriteByte(u8 SourceByte) +{ + if (m_iPosition < m_iSize) + { + m_pMemory[m_iSize++] = SourceByte; + return true; + } + + return false; +} + +u32 MemoryByteStream::Write(const void* pSource, u32 ByteCount) +{ + u32 sz = ByteCount; + if ((m_iPosition + ByteCount) > m_iSize) + sz = m_iSize - m_iPosition; + + if (sz > 0) + { + std::memcpy(m_pMemory + m_iPosition, pSource, sz); + m_iPosition += sz; + } + + return sz; +} + +bool MemoryByteStream::Write2(const void* pSource, u32 ByteCount, u32* pNumberOfBytesWritten /* = nullptr */) +{ + u32 r = Write(pSource, ByteCount); + if (pNumberOfBytesWritten != nullptr) + *pNumberOfBytesWritten = r; + + return (r == ByteCount); +} + +bool MemoryByteStream::SeekAbsolute(u64 Offset) +{ + u32 Offset32 = (u32)Offset; + if (Offset32 > m_iSize) + return false; + + m_iPosition = Offset32; + return true; +} + +bool MemoryByteStream::SeekRelative(s64 Offset) +{ + s32 Offset32 = (s32)Offset; + if ((Offset32 < 0 && -Offset32 > (s32)m_iPosition) || (u32)((s32)m_iPosition + Offset32) > m_iSize) + return false; + + m_iPosition += Offset32; + return true; +} + +bool MemoryByteStream::SeekToEnd() +{ + m_iPosition = m_iSize; + return true; +} + +u64 MemoryByteStream::GetSize() const +{ + return (u64)m_iSize; +} + +u64 MemoryByteStream::GetPosition() const +{ + return (u64)m_iPosition; +} + +bool MemoryByteStream::Flush() +{ + return true; +} + +bool MemoryByteStream::Commit() +{ + return true; +} + +bool MemoryByteStream::Discard() +{ + return false; +} + +ReadOnlyMemoryByteStream::ReadOnlyMemoryByteStream(const void* pMemory, u32 MemSize) +{ + m_iPosition = 0; + m_iSize = MemSize; + m_pMemory = reinterpret_cast(pMemory); +} + +ReadOnlyMemoryByteStream::~ReadOnlyMemoryByteStream() {} + +bool ReadOnlyMemoryByteStream::ReadByte(u8* pDestByte) +{ + if (m_iPosition < m_iSize) + { + *pDestByte = m_pMemory[m_iPosition++]; + return true; + } + + return false; +} + +u32 ReadOnlyMemoryByteStream::Read(void* pDestination, u32 ByteCount) +{ + u32 sz = ByteCount; + if ((m_iPosition + ByteCount) > m_iSize) + sz = m_iSize - m_iPosition; + + if (sz > 0) + { + std::memcpy(pDestination, m_pMemory + m_iPosition, sz); + m_iPosition += sz; + } + + return sz; +} + +bool ReadOnlyMemoryByteStream::Read2(void* pDestination, u32 ByteCount, u32* pNumberOfBytesRead /* = nullptr */) +{ + u32 r = Read(pDestination, ByteCount); + if (pNumberOfBytesRead != nullptr) + *pNumberOfBytesRead = r; + + return (r == ByteCount); +} + +bool ReadOnlyMemoryByteStream::WriteByte(u8 SourceByte) +{ + return false; +} + +u32 ReadOnlyMemoryByteStream::Write(const void* pSource, u32 ByteCount) +{ + return 0; +} + +bool ReadOnlyMemoryByteStream::Write2(const void* pSource, u32 ByteCount, u32* pNumberOfBytesWritten /* = nullptr */) +{ + return false; +} + +bool ReadOnlyMemoryByteStream::SeekAbsolute(u64 Offset) +{ + u32 Offset32 = (u32)Offset; + if (Offset32 > m_iSize) + return false; + + m_iPosition = Offset32; + return true; +} + +bool ReadOnlyMemoryByteStream::SeekRelative(s64 Offset) +{ + s32 Offset32 = (s32)Offset; + if ((Offset32 < 0 && -Offset32 > (s32)m_iPosition) || (u32)((s32)m_iPosition + Offset32) > m_iSize) + return false; + + m_iPosition += Offset32; + return true; +} + +bool ReadOnlyMemoryByteStream::SeekToEnd() +{ + m_iPosition = m_iSize; + return true; +} + +u64 ReadOnlyMemoryByteStream::GetSize() const +{ + return (u64)m_iSize; +} + +u64 ReadOnlyMemoryByteStream::GetPosition() const +{ + return (u64)m_iPosition; +} + +bool ReadOnlyMemoryByteStream::Flush() +{ + return false; +} + +bool ReadOnlyMemoryByteStream::Commit() +{ + return false; +} + +bool ReadOnlyMemoryByteStream::Discard() +{ + return false; +} + +GrowableMemoryByteStream::GrowableMemoryByteStream(void* pInitialMem, u32 InitialMemSize) +{ + m_iPosition = 0; + m_iSize = 0; + + if (pInitialMem != nullptr) + { + m_iMemorySize = InitialMemSize; + m_pPrivateMemory = nullptr; + m_pMemory = (u8*)pInitialMem; + } + else + { + m_iMemorySize = std::max(InitialMemSize, (u32)64); + m_pPrivateMemory = m_pMemory = (u8*)std::malloc(m_iMemorySize); + } +} + +GrowableMemoryByteStream::~GrowableMemoryByteStream() +{ + if (m_pPrivateMemory != nullptr) + std::free(m_pPrivateMemory); +} + +bool GrowableMemoryByteStream::ReadByte(u8* pDestByte) +{ + if (m_iPosition < m_iSize) + { + *pDestByte = m_pMemory[m_iPosition++]; + return true; + } + + return false; +} + +u32 GrowableMemoryByteStream::Read(void* pDestination, u32 ByteCount) +{ + u32 sz = ByteCount; + if ((m_iPosition + ByteCount) > m_iSize) + sz = m_iSize - m_iPosition; + + if (sz > 0) + { + std::memcpy(pDestination, m_pMemory + m_iPosition, sz); + m_iPosition += sz; + } + + return sz; +} + +bool GrowableMemoryByteStream::Read2(void* pDestination, u32 ByteCount, u32* pNumberOfBytesRead /* = nullptr */) +{ + u32 r = Read(pDestination, ByteCount); + if (pNumberOfBytesRead != NULL) + *pNumberOfBytesRead = r; + + return (r == ByteCount); +} + +bool GrowableMemoryByteStream::WriteByte(u8 SourceByte) +{ + if (m_iPosition == m_iMemorySize) + Grow(1); + + m_pMemory[m_iPosition++] = SourceByte; + m_iSize = std::max(m_iSize, m_iPosition); + return true; +} + +u32 GrowableMemoryByteStream::Write(const void* pSource, u32 ByteCount) +{ + if ((m_iPosition + ByteCount) > m_iMemorySize) + Grow(ByteCount); + + std::memcpy(m_pMemory + m_iPosition, pSource, ByteCount); + m_iPosition += ByteCount; + m_iSize = std::max(m_iSize, m_iPosition); + return ByteCount; +} + +bool GrowableMemoryByteStream::Write2(const void* pSource, u32 ByteCount, u32* pNumberOfBytesWritten /* = nullptr */) +{ + u32 r = Write(pSource, ByteCount); + if (pNumberOfBytesWritten != nullptr) + *pNumberOfBytesWritten = r; + + return (r == ByteCount); +} + +bool GrowableMemoryByteStream::SeekAbsolute(u64 Offset) +{ + u32 Offset32 = (u32)Offset; + if (Offset32 > m_iSize) + return false; + + m_iPosition = Offset32; + return true; +} + +bool GrowableMemoryByteStream::SeekRelative(s64 Offset) +{ + s32 Offset32 = (s32)Offset; + if ((Offset32 < 0 && -Offset32 > (s32)m_iPosition) || (u32)((s32)m_iPosition + Offset32) > m_iSize) + return false; + + m_iPosition += Offset32; + return true; +} + +bool GrowableMemoryByteStream::SeekToEnd() +{ + m_iPosition = m_iSize; + return true; +} + +u64 GrowableMemoryByteStream::GetSize() const +{ + return (u64)m_iSize; +} + +u64 GrowableMemoryByteStream::GetPosition() const +{ + return (u64)m_iPosition; +} + +bool GrowableMemoryByteStream::Flush() +{ + return true; +} + +bool GrowableMemoryByteStream::Commit() +{ + return true; +} + +bool GrowableMemoryByteStream::Discard() +{ + return false; +} + +void GrowableMemoryByteStream::Grow(u32 MinimumGrowth) +{ + u32 NewSize = std::max(m_iMemorySize + MinimumGrowth, m_iMemorySize * 2); + if (m_pPrivateMemory == nullptr) + { + m_pPrivateMemory = (u8*)std::malloc(NewSize); + std::memcpy(m_pPrivateMemory, m_pMemory, m_iSize); + m_pMemory = m_pPrivateMemory; + m_iMemorySize = NewSize; + } + else + { + m_pPrivateMemory = m_pMemory = (u8*)std::realloc(m_pPrivateMemory, NewSize); + m_iMemorySize = NewSize; + } +} + +#if defined(_MSC_VER) + +std::unique_ptr ByteStream_OpenFileStream(const char* fileName, u32 openMode) +{ + if ((openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE)) == BYTESTREAM_OPEN_WRITE) + { + // if opening with write but not create, the path must exist. + if (GetFileAttributes(fileName) == INVALID_FILE_ATTRIBUTES) + return nullptr; + } + + char modeString[16]; + u32 modeStringLength = 0; + + if (openMode & BYTESTREAM_OPEN_WRITE) + { + // if the file exists, use r+, otherwise w+ + // HACK: if we're not truncating, and the file exists (we want to only update it), we still have to use r+ + if ((openMode & BYTESTREAM_OPEN_TRUNCATE) || GetFileAttributes(fileName) == INVALID_FILE_ATTRIBUTES) + { + modeString[modeStringLength++] = 'w'; + if (openMode & BYTESTREAM_OPEN_READ) + modeString[modeStringLength++] = '+'; + } + else + { + modeString[modeStringLength++] = 'r'; + modeString[modeStringLength++] = '+'; + } + + modeString[modeStringLength++] = 'b'; + } + else if (openMode & BYTESTREAM_OPEN_READ) + { + modeString[modeStringLength++] = 'r'; + modeString[modeStringLength++] = 'b'; + } + + // doesn't work with _fdopen + if (!(openMode & BYTESTREAM_OPEN_ATOMIC_UPDATE)) + { + if (openMode & BYTESTREAM_OPEN_STREAMED) + modeString[modeStringLength++] = 'S'; + else if (openMode & BYTESTREAM_OPEN_SEEKABLE) + modeString[modeStringLength++] = 'R'; + } + + modeString[modeStringLength] = 0; + + if (openMode & BYTESTREAM_OPEN_CREATE_PATH) + { + u32 i; + u32 fileNameLength = static_cast(std::strlen(fileName)); + char* tempStr = (char*)alloca(fileNameLength + 1); + + // check if it starts with a drive letter. if so, skip ahead + if (fileNameLength >= 2 && fileName[1] == ':') + { + if (fileNameLength <= 3) + { + // create a file called driveletter: or driveletter:\ ? you must be crazy + i = fileNameLength; + } + else + { + std::memcpy(tempStr, fileName, 3); + i = 3; + } + } + else + { + // start at beginning + i = 0; + } + + // step through each path component, create folders as necessary + for (; i < fileNameLength; i++) + { + if (i > 0 && (fileName[i] == '\\' || fileName[i] == '/')) + { + // terminate the string + tempStr[i] = '\0'; + + // check if it exists + struct stat s; + if (stat(tempStr, &s) < 0) + { + if (errno == ENOENT) + { + // try creating it + if (_mkdir(tempStr) < 0) + { + // no point trying any further down the chain + break; + } + } + else // if (errno == ENOTDIR) + { + // well.. someone's trying to open a fucking weird path that is comprised of both directories and files... + // I aint sticking around here to find out what disaster awaits... let fopen deal with it + break; + } + } + +// append platform path seperator +#if defined(WIN32) + tempStr[i] = '\\'; +#else + tempStr[i] = '/'; +#endif + } + else + { + // append character to temp string + tempStr[i] = fileName[i]; + } + } + } + + if (openMode & BYTESTREAM_OPEN_ATOMIC_UPDATE) + { + DebugAssert(openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE)); + + // generate the temporary file name + u32 fileNameLength = static_cast(std::strlen(fileName)); + char* temporaryFileName = (char*)alloca(fileNameLength + 8); + std::snprintf(temporaryFileName, fileNameLength + 8, "%s.XXXXXX", fileName); + + // fill in random characters + _mktemp_s(temporaryFileName, fileNameLength + 8); + + // open the file + errno_t err; + FILE* pTemporaryFile; + + // massive hack here + DWORD desiredAccess = GENERIC_WRITE; + if (openMode & BYTESTREAM_OPEN_READ) + desiredAccess |= GENERIC_READ; + HANDLE hFile = CreateFileA(temporaryFileName, desiredAccess, FILE_SHARE_DELETE, NULL, CREATE_NEW, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return nullptr; + + // get fd from this + int fd = _open_osfhandle(reinterpret_cast(hFile), 0); + if (fd < 0) + { + CloseHandle(hFile); + DeleteFileA(temporaryFileName); + return nullptr; + } + + // convert to a stream + pTemporaryFile = _fdopen(fd, modeString); + if (pTemporaryFile == nullptr) + { + _close(fd); + DeleteFileA(temporaryFileName); + return nullptr; + } + + // create the stream pointer + std::unique_ptr pStream = + std::make_unique(pTemporaryFile, fileName, temporaryFileName); + + // do we need to copy the existing file into this one? + if (!(openMode & BYTESTREAM_OPEN_TRUNCATE)) + { + FILE* pOriginalFile; + err = fopen_s(&pOriginalFile, fileName, "rb"); + if (err != 0 || pOriginalFile == nullptr) + { + // this will delete the temporary file + pStream->Discard(); + return nullptr; + } + + static const size_t BUFFERSIZE = 4096; + u8 buffer[BUFFERSIZE]; + while (!feof(pOriginalFile)) + { + size_t nBytes = fread(buffer, BUFFERSIZE, sizeof(u8), pOriginalFile); + if (nBytes == 0) + break; + + if (pStream->Write(buffer, (u32)nBytes) != (u32)nBytes) + { + pStream->Discard(); + fclose(pOriginalFile); + return nullptr; + } + } + + // close original file + fclose(pOriginalFile); + } + + // return pointer + return pStream; + } + else + { + // forward through + FILE* pFile; + errno_t err = fopen_s(&pFile, fileName, modeString); + if (err != 0 || pFile == NULL) + return nullptr; + + return std::make_unique(pFile); + } +} + +#else + +std::unique_ptr ByteStream_OpenFileStream(const char* fileName, u32 openMode) +{ + if ((openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE)) == BYTESTREAM_OPEN_WRITE) + { + // if opening with write but not create, the path must exist. + struct stat s; + if (stat(fileName, &s) < 0) + return nullptr; + } + + char modeString[16]; + u32 modeStringLength = 0; + + if (openMode & BYTESTREAM_OPEN_WRITE) + { + if (openMode & BYTESTREAM_OPEN_TRUNCATE) + modeString[modeStringLength++] = 'w'; + else + modeString[modeStringLength++] = 'a'; + + modeString[modeStringLength++] = 'b'; + + if (openMode & BYTESTREAM_OPEN_READ) + modeString[modeStringLength++] = '+'; + } + else if (openMode & BYTESTREAM_OPEN_READ) + { + modeString[modeStringLength++] = 'r'; + modeString[modeStringLength++] = 'b'; + } + + modeString[modeStringLength] = 0; + + if (openMode & BYTESTREAM_OPEN_CREATE_PATH) + { + u32 i; + const u32 fileNameLength = static_cast(std::strlen(fileName)); + char* tempStr = (char*)alloca(fileNameLength + 1); + +#if defined(WIN32) + // check if it starts with a drive letter. if so, skip ahead + if (fileNameLength >= 2 && fileName[1] == ':') + { + if (fileNameLength <= 3) + { + // create a file called driveletter: or driveletter:\ ? you must be crazy + i = fileNameLength; + } + else + { + std::memcpy(tempStr, fileName, 3); + i = 3; + } + } + else + { + // start at beginning + i = 0; + } +#endif + + // step through each path component, create folders as necessary + for (i = 0; i < fileNameLength; i++) + { + if (i > 0 && (fileName[i] == '\\' || fileName[i] == '/') && fileName[i] != ':') + { + // terminate the string + tempStr[i] = '\0'; + + // check if it exists + struct stat s; + if (stat(tempStr, &s) < 0) + { + if (errno == ENOENT) + { + // try creating it +#if defined(WIN32) + if (mkdir(tempStr) < 0) +#else + if (mkdir(tempStr, 0777) < 0) +#endif + { + // no point trying any further down the chain + break; + } + } + else // if (errno == ENOTDIR) + { + // well.. someone's trying to open a fucking weird path that is comprised of both directories and files... + // I aint sticking around here to find out what disaster awaits... let fopen deal with it + break; + } + } + +// append platform path seperator +#if defined(WIN32) + tempStr[i] = '\\'; +#else + tempStr[i] = '/'; +#endif + } + else + { + // append character to temp string + tempStr[i] = fileName[i]; + } + } + } + + if (openMode & BYTESTREAM_OPEN_ATOMIC_UPDATE) + { + DebugAssert(openMode & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE)); + + // generate the temporary file name + const u32 fileNameLength = static_cast(std::strlen(fileName)); + char* temporaryFileName = (char*)alloca(fileNameLength + 8); + std::snprintf(temporaryFileName, fileNameLength + 8, "%s.XXXXXX", fileName); + + // fill in random characters +#if defined(__linux__) || defined(__ANDROID__) + mkstemp(temporaryFileName); +#else + mktemp(temporaryFileName); +#endif + + // open the file + std::FILE* pTemporaryFile = std::fopen(temporaryFileName, modeString); + if (pTemporaryFile == nullptr) + return nullptr; + + // create the stream pointer + std::unique_ptr pStream = + std::make_unique(pTemporaryFile, fileName, temporaryFileName); + + // do we need to copy the existing file into this one? + if (!(openMode & BYTESTREAM_OPEN_TRUNCATE)) + { + std::FILE* pOriginalFile = std::fopen(fileName, "rb"); + if (!pOriginalFile) + { + // this will delete the temporary file + pStream->SetErrorState(); + return nullptr; + } + + static const size_t BUFFERSIZE = 4096; + u8 buffer[BUFFERSIZE]; + while (!std::feof(pOriginalFile)) + { + size_t nBytes = std::fread(buffer, BUFFERSIZE, sizeof(u8), pOriginalFile); + if (nBytes == 0) + break; + + if (pStream->Write(buffer, (u32)nBytes) != (u32)nBytes) + { + pStream->SetErrorState(); + std::fclose(pOriginalFile); + return nullptr; + } + } + + // close original file + std::fclose(pOriginalFile); + } + + // return pointer + return pStream; + } + else + { + std::FILE* pFile = std::fopen(fileName, modeString); + if (!pFile) + return nullptr; + + return std::make_unique(pFile); + } +} + +#endif + +std::unique_ptr ByteStream_CreateMemoryStream(void* pMemory, u32 Size) +{ + DebugAssert(pMemory != nullptr && Size > 0); + return std::make_unique(pMemory, Size); +} + +std::unique_ptr ByteStream_CreateReadOnlyMemoryStream(const void* pMemory, u32 Size) +{ + DebugAssert(pMemory != nullptr && Size > 0); + return std::make_unique(pMemory, Size); +} + +std::unique_ptr ByteStream_CreateNullStream() +{ + return std::make_unique(); +} + +std::unique_ptr ByteStream_CreateGrowableMemoryStream(void* pInitialMemory, u32 InitialSize) +{ + return std::make_unique(pInitialMemory, InitialSize); +} + +std::unique_ptr ByteStream_CreateGrowableMemoryStream() +{ + return std::make_unique(nullptr, 0); +} + +bool ByteStream_CopyStream(ByteStream* pDestinationStream, ByteStream* pSourceStream) +{ + const u32 chunkSize = 4096; + u8 chunkData[chunkSize]; + + u64 oldSourcePosition = pSourceStream->GetPosition(); + if (!pSourceStream->SeekAbsolute(0) || !pDestinationStream->SeekAbsolute(0)) + return false; + + bool success = false; + for (;;) + { + u32 nBytes = pSourceStream->Read(chunkData, chunkSize); + if (nBytes == 0) + { + success = true; + break; + } + + if (pDestinationStream->Write(chunkData, nBytes) != nBytes) + break; + } + + return (pSourceStream->SeekAbsolute(oldSourcePosition) && success); +} + +bool ByteStream_AppendStream(ByteStream* pSourceStream, ByteStream* pDestinationStream) +{ + const u32 chunkSize = 4096; + u8 chunkData[chunkSize]; + + u64 oldSourcePosition = pSourceStream->GetPosition(); + if (!pSourceStream->SeekAbsolute(0)) + return false; + + bool success = false; + for (;;) + { + u32 nBytes = pSourceStream->Read(chunkData, chunkSize); + if (nBytes == 0) + { + success = true; + break; + } + + if (pDestinationStream->Write(chunkData, nBytes) != nBytes) + break; + } + + return (pSourceStream->SeekAbsolute(oldSourcePosition) && success); +} + +u32 ByteStream_CopyBytes(ByteStream* pSourceStream, u32 byteCount, ByteStream* pDestinationStream) +{ + const u32 chunkSize = 4096; + u8 chunkData[chunkSize]; + + u32 remaining = byteCount; + while (remaining > 0) + { + u32 toCopy = std::min(remaining, chunkSize); + u32 bytesRead = pSourceStream->Read(chunkData, toCopy); + if (bytesRead == 0) + break; + + u32 bytesWritten = pDestinationStream->Write(chunkData, bytesRead); + if (bytesWritten == 0) + break; + + remaining -= bytesWritten; + } + + return byteCount - remaining; +} diff --git a/src/common/byte_stream.h b/src/common/byte_stream.h new file mode 100644 index 000000000..14ce021e6 --- /dev/null +++ b/src/common/byte_stream.h @@ -0,0 +1,226 @@ +#pragma once +#include "types.h" +#include + +// base byte stream creation functions +enum BYTESTREAM_OPEN_MODE +{ + BYTESTREAM_OPEN_READ = 1, // open stream for writing + BYTESTREAM_OPEN_WRITE = 2, // open stream for writing + BYTESTREAM_OPEN_APPEND = 4, // seek to the end + BYTESTREAM_OPEN_TRUNCATE = 8, // truncate the file, seek to start + BYTESTREAM_OPEN_CREATE = 16, // if the file does not exist, create it + BYTESTREAM_OPEN_CREATE_PATH = 32, // if the file parent directories don't exist, create them + BYTESTREAM_OPEN_ATOMIC_UPDATE = 64, // + BYTESTREAM_OPEN_SEEKABLE = 128, + BYTESTREAM_OPEN_STREAMED = 256, +}; + +// interface class used by readers, writers, etc. +class ByteStream +{ +public: + virtual ~ByteStream() {} + + // reads a single byte from the stream. + virtual bool ReadByte(u8* pDestByte) = 0; + + // read bytes from this stream. returns the number of bytes read, if this isn't equal to the requested size, an error + // or EOF occurred. + virtual u32 Read(void* pDestination, u32 ByteCount) = 0; + + // read bytes from this stream, optionally returning the number of bytes read. + virtual bool Read2(void* pDestination, u32 ByteCount, u32* pNumberOfBytesRead = nullptr) = 0; + + // writes a single byte to the stream. + virtual bool WriteByte(u8 SourceByte) = 0; + + // write bytes to this stream, returns the number of bytes written. if this isn't equal to the requested size, a + // buffer overflow, or write error occurred. + virtual u32 Write(const void* pSource, u32 ByteCount) = 0; + + // write bytes to this stream, optionally returning the number of bytes written. + virtual bool Write2(const void* pSource, u32 ByteCount, u32* pNumberOfBytesWritten = nullptr) = 0; + + // seeks to the specified position in the stream + // if seek failed, returns false. + virtual bool SeekAbsolute(u64 Offset) = 0; + virtual bool SeekRelative(s64 Offset) = 0; + virtual bool SeekToEnd() = 0; + + // gets the current offset in the stream + virtual u64 GetPosition() const = 0; + + // gets the size of the stream + virtual u64 GetSize() const = 0; + + // flush any changes to the stream to disk + virtual bool Flush() = 0; + + // if the file was opened in atomic update mode, discards any changes made to the file + virtual bool Discard() = 0; + + // if the file was opened in atomic update mode, commits the file and replaces the temporary file + virtual bool Commit() = 0; + + // state accessors + inline bool InErrorState() const { return m_errorState; } + inline void SetErrorState() { m_errorState = true; } + inline void ClearErrorState() { m_errorState = false; } + +protected: + ByteStream() : m_errorState(false) {} + + // state bits + bool m_errorState; + + // make it noncopyable + ByteStream(const ByteStream&) = delete; + ByteStream& operator=(const ByteStream&) = delete; +}; + +class NullByteStream : public ByteStream +{ +public: + NullByteStream(); + ~NullByteStream(); + + virtual bool ReadByte(u8* pDestByte) override final; + virtual u32 Read(void* pDestination, u32 ByteCount) override final; + virtual bool Read2(void* pDestination, u32 ByteCount, u32* pNumberOfBytesRead /* = nullptr */) override final; + virtual bool WriteByte(u8 SourceByte) override final; + virtual u32 Write(const void* pSource, u32 ByteCount) override final; + virtual bool Write2(const void* pSource, u32 ByteCount, u32* pNumberOfBytesWritten /* = nullptr */) override final; + virtual bool SeekAbsolute(u64 Offset) override final; + virtual bool SeekRelative(s64 Offset) override final; + virtual bool SeekToEnd() override final; + virtual u64 GetSize() const override final; + virtual u64 GetPosition() const override final; + virtual bool Flush() override final; + virtual bool Commit() override final; + virtual bool Discard() override final; +}; + +class MemoryByteStream : public ByteStream +{ +public: + MemoryByteStream(void* pMemory, u32 MemSize); + virtual ~MemoryByteStream(); + + u8* GetMemoryPointer() const { return m_pMemory; } + u32 GetMemorySize() const { return m_iSize; } + + virtual bool ReadByte(u8* pDestByte) override; + virtual u32 Read(void* pDestination, u32 ByteCount) override; + virtual bool Read2(void* pDestination, u32 ByteCount, u32* pNumberOfBytesRead /* = nullptr */) override; + virtual bool WriteByte(u8 SourceByte) override; + virtual u32 Write(const void* pSource, u32 ByteCount) override; + virtual bool Write2(const void* pSource, u32 ByteCount, u32* pNumberOfBytesWritten /* = nullptr */) override; + virtual bool SeekAbsolute(u64 Offset) override; + virtual bool SeekRelative(s64 Offset) override; + virtual bool SeekToEnd() override; + virtual u64 GetSize() const override; + virtual u64 GetPosition() const override; + virtual bool Flush() override; + virtual bool Commit() override; + virtual bool Discard() override; + +private: + u8* m_pMemory; + u32 m_iPosition; + u32 m_iSize; +}; + +class ReadOnlyMemoryByteStream : public ByteStream +{ +public: + ReadOnlyMemoryByteStream(const void* pMemory, u32 MemSize); + virtual ~ReadOnlyMemoryByteStream(); + + const u8* GetMemoryPointer() const { return m_pMemory; } + u32 GetMemorySize() const { return m_iSize; } + + virtual bool ReadByte(u8* pDestByte) override; + virtual u32 Read(void* pDestination, u32 ByteCount) override; + virtual bool Read2(void* pDestination, u32 ByteCount, u32* pNumberOfBytesRead /* = nullptr */) override; + virtual bool WriteByte(u8 SourceByte) override; + virtual u32 Write(const void* pSource, u32 ByteCount) override; + virtual bool Write2(const void* pSource, u32 ByteCount, u32* pNumberOfBytesWritten /* = nullptr */) override; + virtual bool SeekAbsolute(u64 Offset) override; + virtual bool SeekRelative(s64 Offset) override; + virtual bool SeekToEnd() override; + virtual u64 GetSize() const override; + virtual u64 GetPosition() const override; + virtual bool Flush() override; + virtual bool Commit() override; + virtual bool Discard() override; + +private: + const u8* m_pMemory; + u32 m_iPosition; + u32 m_iSize; +}; + +class GrowableMemoryByteStream : public ByteStream +{ +public: + GrowableMemoryByteStream(void* pInitialMem, u32 InitialMemSize); + virtual ~GrowableMemoryByteStream(); + + u8* GetMemoryPointer() const { return m_pMemory; } + u32 GetMemorySize() const { return m_iSize; } + + virtual bool ReadByte(u8* pDestByte) override; + virtual u32 Read(void* pDestination, u32 ByteCount) override; + virtual bool Read2(void* pDestination, u32 ByteCount, u32* pNumberOfBytesRead /* = nullptr */) override; + virtual bool WriteByte(u8 SourceByte) override; + virtual u32 Write(const void* pSource, u32 ByteCount) override; + virtual bool Write2(const void* pSource, u32 ByteCount, u32* pNumberOfBytesWritten /* = nullptr */) override; + virtual bool SeekAbsolute(u64 Offset) override; + virtual bool SeekRelative(s64 Offset) override; + virtual bool SeekToEnd() override; + virtual u64 GetSize() const override; + virtual u64 GetPosition() const override; + virtual bool Flush() override; + virtual bool Commit() override; + virtual bool Discard() override; + +private: + void Grow(u32 MinimumGrowth); + + u8* m_pPrivateMemory; + u8* m_pMemory; + u32 m_iPosition; + u32 m_iSize; + u32 m_iMemorySize; +}; + +// base byte stream creation functions +// opens a local file-based stream. fills in error if passed, and returns false if the file cannot be opened. +std::unique_ptr ByteStream_OpenFileStream(const char* FileName, u32 OpenMode); + +// memory byte stream, caller is responsible for management, therefore it can be located on either the stack or on the +// heap. +std::unique_ptr ByteStream_CreateMemoryStream(void* pMemory, u32 Size); + +// a growable memory byte stream will automatically allocate its own memory if the provided memory is overflowed. +// a "pure heap" buffer, i.e. a buffer completely managed by this implementation, can be created by supplying a NULL +// pointer and initialSize of zero. +std::unique_ptr ByteStream_CreateGrowableMemoryStream(void* pInitialMemory, u32 InitialSize); +std::unique_ptr ByteStream_CreateGrowableMemoryStream(); + +// readable memory stream +std::unique_ptr ByteStream_CreateReadOnlyMemoryStream(const void* pMemory, u32 Size); + +// null memory stream +std::unique_ptr ByteStream_CreateNullStream(); + +// copies one stream's contents to another. rewinds source streams automatically, and returns it back to its old +// position. +bool ByteStream_CopyStream(ByteStream* pDestinationStream, ByteStream* pSourceStream); + +// appends one stream's contents to another. +bool ByteStream_AppendStream(ByteStream* pSourceStream, ByteStream* pDestinationStream); + +// copies a number of bytes from one to another +u32 ByteStream_CopyBytes(ByteStream* pSourceStream, u32 byteCount, ByteStream* pDestinationStream); diff --git a/src/common/cd_image.cpp b/src/common/cd_image.cpp index 5eb5cf256..8dd3e702a 100644 --- a/src/common/cd_image.cpp +++ b/src/common/cd_image.cpp @@ -1,6 +1,6 @@ #include "cd_image.h" -#include "YBaseLib/ByteStream.h" -#include "YBaseLib/Log.h" +#include "assert.h" +#include "log.h" #include Log_SetChannel(CDImage); diff --git a/src/common/cd_image_bin.cpp b/src/common/cd_image_bin.cpp index 7930ad354..2be68ab4f 100644 --- a/src/common/cd_image_bin.cpp +++ b/src/common/cd_image_bin.cpp @@ -1,6 +1,7 @@ -#include "YBaseLib/Log.h" #include "cd_image.h" #include "cd_subchannel_replacement.h" +#include "file_system.h" +#include "log.h" Log_SetChannel(CDImageBin); class CDImageBin : public CDImage @@ -41,7 +42,7 @@ CDImageBin::~CDImageBin() bool CDImageBin::Open(const char* filename) { m_filename = filename; - m_fp = std::fopen(filename, "rb"); + m_fp = FileSystem::OpenCFile(filename, "rb"); if (!m_fp) { Log_ErrorPrintf("Failed to open binfile '%s'", filename); diff --git a/src/common/cd_image_cue.cpp b/src/common/cd_image_cue.cpp index 1c536a412..357ac793d 100644 --- a/src/common/cd_image_cue.cpp +++ b/src/common/cd_image_cue.cpp @@ -1,7 +1,10 @@ -#include "YBaseLib/Log.h" +#include "assert.h" #include "cd_image.h" #include "cd_subchannel_replacement.h" +#include "file_system.h" +#include "log.h" #include +#include #include Log_SetChannel(CDImageCueSheet); @@ -61,7 +64,7 @@ static std::string ReplaceExtension(std::string_view path, std::string_view new_ bool CDImageCueSheet::OpenAndParse(const char* filename) { - std::FILE* cue_fp = std::fopen(filename, "rb"); + std::FILE* cue_fp = FileSystem::OpenCFile(filename, "rb"); if (!cue_fp) { Log_ErrorPrintf("Failed to open cuesheet '%s'", filename); @@ -98,7 +101,7 @@ bool CDImageCueSheet::OpenAndParse(const char* filename) if (it == m_files.end()) { std::string track_full_filename = basepath + track_filename; - std::FILE* track_fp = std::fopen(track_full_filename.c_str(), "rb"); + std::FILE* track_fp = FileSystem::OpenCFile(track_full_filename.c_str(), "rb"); if (!track_fp) { Log_ErrorPrintf("Failed to open track filename '%s' (from '%s' and '%s')", track_full_filename.c_str(), diff --git a/src/common/cd_subchannel_replacement.cpp b/src/common/cd_subchannel_replacement.cpp index cced30f33..776137481 100644 --- a/src/common/cd_subchannel_replacement.cpp +++ b/src/common/cd_subchannel_replacement.cpp @@ -1,5 +1,7 @@ #include "cd_subchannel_replacement.h" -#include "YBaseLib/Log.h" +#include "log.h" +#include "file_system.h" +#include #include Log_SetChannel(CDSubChannelReplacement); @@ -29,7 +31,7 @@ static constexpr u32 MSFToLBA(u8 minute_bcd, u8 second_bcd, u8 frame_bcd) bool CDSubChannelReplacement::LoadSBI(const char* path) { - std::unique_ptr fp(std::fopen(path, "rb"), [](std::FILE* fp) { std::fclose(fp); }); + auto fp = FileSystem::OpenManagedCFile(path, "rb"); if (!fp) return false; diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index f5fc643de..9dc19f2f8 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -35,29 +35,42 @@ + + + + + + + + + + + + + @@ -65,15 +78,22 @@ + + + + + + + @@ -85,9 +105,6 @@ {6a4208ed-e3dc-41e1-81cd-f61025fc285a} - - {b56ce698-7300-4fa5-9609-942f1d05c5a2} - {EE054E08-3799-4A59-A422-18259C105FFD} @@ -226,7 +243,7 @@ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\libsamplerate\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 true @@ -252,7 +269,7 @@ _ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\libsamplerate\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default false true @@ -281,7 +298,7 @@ WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\libsamplerate\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 true @@ -307,7 +324,7 @@ _ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\libsamplerate\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default false true @@ -337,7 +354,7 @@ true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\libsamplerate\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 false @@ -367,7 +384,7 @@ true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\libsamplerate\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 @@ -397,7 +414,7 @@ true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\libsamplerate\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true stdcpp17 false @@ -427,7 +444,7 @@ true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true - $(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\libsamplerate\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 67e8a518e..bb293b537 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -35,6 +35,17 @@ + + + + + + + + + + + @@ -68,6 +79,15 @@ + + + + + + + + + diff --git a/src/common/cpu_detect.h b/src/common/cpu_detect.h new file mode 100644 index 000000000..dd700b4ce --- /dev/null +++ b/src/common/cpu_detect.h @@ -0,0 +1,31 @@ +#pragma once + +#if defined(_MSC_VER) + +#if defined(_M_X64) +#define CPU_X64 1 +#elif defined(_M_IX86) +#define CPU_X86 1 +#else +#error Unknown architecture. +#endif + +#elif defined(__GNUC__) || defined(__clang__) + +#if defined(__x86_64__) +#define CPU_X64 1 +#elif defined(__i386__) +#define CPU_X86 1 +#elif defined(__aarch64__) +#define CPU_AARCH64 1 +#elif defined(__arm__) +#define CPU_ARM 1 +#else +#error Unknown architecture. +#endif + +#else + +#error Unknown compiler. + +#endif diff --git a/src/common/d3d11/shader_compiler.cpp b/src/common/d3d11/shader_compiler.cpp index 8184161b1..84a4153d7 100644 --- a/src/common/d3d11/shader_compiler.cpp +++ b/src/common/d3d11/shader_compiler.cpp @@ -1,6 +1,6 @@ #include "shader_compiler.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/String.h" +#include "../log.h" +#include "../string_util.h" #include #include #include @@ -54,33 +54,32 @@ ComPtr CompileShader(Type type, D3D_FEATURE_LEVEL feature_level, std:: D3DCompile(code.data(), code.size(), "0", nullptr, nullptr, "main", target, debug ? flags_debug : flags_non_debug, 0, blob.GetAddressOf(), error_blob.GetAddressOf()); - String error_string; + std::string error_string; if (error_blob) { - error_string.AppendString(static_cast(error_blob->GetBufferPointer()), - static_cast(error_blob->GetBufferSize())); + error_string.append(static_cast(error_blob->GetBufferPointer()), error_blob->GetBufferSize()); error_blob.Reset(); } if (FAILED(hr)) { - Log_ErrorPrintf("Failed to compile '%s':\n%s", target, error_string.GetCharArray()); + Log_ErrorPrintf("Failed to compile '%s':\n%s", target, error_string.c_str()); - std::ofstream ofs(SmallString::FromFormat("bad_shader_%u.txt", s_next_bad_shader_id++), + std::ofstream ofs(StringUtil::StdStringFromFormat("bad_shader_%u.txt", s_next_bad_shader_id++).c_str(), std::ofstream::out | std::ofstream::binary); if (ofs.is_open()) { ofs << code; ofs << "\n\nCompile as " << target << " failed: " << hr << "\n"; - ofs.write(error_string.GetCharArray(), error_string.GetLength()); + ofs.write(error_string.c_str(), error_string.size()); ofs.close(); } return {}; } - if (!error_string.IsEmpty()) - Log_WarningPrintf("'%s' compiled with warnings:\n%s", target, error_string.GetCharArray()); + if (!error_string.empty()) + Log_WarningPrintf("'%s' compiled with warnings:\n%s", target, error_string.c_str()); return blob; } diff --git a/src/common/d3d11/shader_compiler.h b/src/common/d3d11/shader_compiler.h index 496778b2e..162b0c584 100644 --- a/src/common/d3d11/shader_compiler.h +++ b/src/common/d3d11/shader_compiler.h @@ -1,5 +1,5 @@ #pragma once -#include "YBaseLib/Windows/WindowsHeaders.h" +#include "../windows_headers.h" #include #include #include diff --git a/src/common/d3d11/staging_texture.cpp b/src/common/d3d11/staging_texture.cpp index 73f2b9be2..79a4d701d 100644 --- a/src/common/d3d11/staging_texture.cpp +++ b/src/common/d3d11/staging_texture.cpp @@ -1,5 +1,6 @@ #include "staging_texture.h" -#include "YBaseLib/Log.h" +#include "../log.h" +#include "../assert.h" Log_SetChannel(D3D11); namespace D3D11 { diff --git a/src/common/d3d11/staging_texture.h b/src/common/d3d11/staging_texture.h index d344945bd..53456a6c9 100644 --- a/src/common/d3d11/staging_texture.h +++ b/src/common/d3d11/staging_texture.h @@ -1,6 +1,6 @@ #pragma once #include "../types.h" -#include "YBaseLib/Windows/WindowsHeaders.h" +#include "../windows_headers.h" #include #include #include diff --git a/src/common/d3d11/stream_buffer.cpp b/src/common/d3d11/stream_buffer.cpp index b14ed95b4..2b68915f9 100644 --- a/src/common/d3d11/stream_buffer.cpp +++ b/src/common/d3d11/stream_buffer.cpp @@ -1,5 +1,7 @@ #include "stream_buffer.h" -#include "YBaseLib/Log.h" +#include "../align.h" +#include "../assert.h" +#include "../log.h" Log_SetChannel(D3D11); namespace D3D11 { diff --git a/src/common/d3d11/stream_buffer.h b/src/common/d3d11/stream_buffer.h index c4c7f93be..cdc62931f 100644 --- a/src/common/d3d11/stream_buffer.h +++ b/src/common/d3d11/stream_buffer.h @@ -1,6 +1,6 @@ #pragma once #include "../types.h" -#include "YBaseLib/Windows/WindowsHeaders.h" +#include "../windows_headers.h" #include #include diff --git a/src/common/d3d11/texture.cpp b/src/common/d3d11/texture.cpp index 1e8eace58..ced98eda3 100644 --- a/src/common/d3d11/texture.cpp +++ b/src/common/d3d11/texture.cpp @@ -1,5 +1,5 @@ #include "texture.h" -#include "YBaseLib/Log.h" +#include "../log.h" Log_SetChannel(D3D11); namespace D3D11 { diff --git a/src/common/d3d11/texture.h b/src/common/d3d11/texture.h index 2a1303985..e39b7bd3c 100644 --- a/src/common/d3d11/texture.h +++ b/src/common/d3d11/texture.h @@ -1,6 +1,6 @@ #pragma once #include "../types.h" -#include "YBaseLib/Windows/WindowsHeaders.h" +#include "../windows_headers.h" #include #include diff --git a/src/common/fifo_queue.h b/src/common/fifo_queue.h index 48fb86ec8..1206ee66e 100644 --- a/src/common/fifo_queue.h +++ b/src/common/fifo_queue.h @@ -1,5 +1,5 @@ #pragma once -#include "YBaseLib/Assert.h" +#include "assert.h" #include "types.h" #include #include diff --git a/src/common/file_system.cpp b/src/common/file_system.cpp new file mode 100644 index 000000000..38508b0f2 --- /dev/null +++ b/src/common/file_system.cpp @@ -0,0 +1,1275 @@ +#include "file_system.h" +#include "assert.h" +#include "byte_stream.h" +#include "log.h" +#include "string_util.h" +#include +#include +#include +Log_SetChannel(FileSystem); + +#if defined(WIN32) +#include +#else +#include +#include +#include +#include +#include +#endif + +namespace FileSystem { + +ChangeNotifier::ChangeNotifier(const String& directoryPath, bool recursiveWatch) + : m_directoryPath(directoryPath), m_recursiveWatch(recursiveWatch) +{ +} + +ChangeNotifier::~ChangeNotifier() {} + +void CanonicalizePath(char* Destination, u32 cbDestination, const char* Path, bool OSPath /*= true*/) +{ + u32 i, j; + DebugAssert(Destination && cbDestination > 0 && Path); + + // get length + u32 pathLength = static_cast(std::strlen(Path)); + + // clone to a local buffer if the same pointer + if (Destination == Path) + { + char* pathClone = (char*)alloca(pathLength + 1); + StringUtil::Strlcpy(pathClone, Path, pathLength + 1); + Path = pathClone; + } + + // zero destination + std::memset(Destination, 0, cbDestination); + + // iterate path + u32 destinationLength = 0; + for (i = 0; i < pathLength;) + { + char prevCh = (i > 0) ? Path[i - 1] : '\0'; + char currentCh = Path[i]; + char nextCh = (i < pathLength) ? Path[i + 1] : '\0'; + + if (currentCh == '.') + { + if (prevCh == '\\' || prevCh == '/' || prevCh == '\0') + { + // handle '.' + if (nextCh == '\\' || nextCh == '/' || nextCh == '\0') + { + // skip '.\' + i++; + + // remove the previous \, if we have one trailing the dot it'll append it anyway + if (destinationLength > 0) + Destination[--destinationLength] = '\0'; + + continue; + } + // handle '..' + else if (nextCh == '.') + { + char afterNext = ((i + 1) < pathLength) ? Path[i + 2] : '\0'; + if (afterNext == '\\' || afterNext == '/' || afterNext == '\0') + { + // remove one directory of the path, including the /. + if (destinationLength > 1) + { + for (j = destinationLength - 2; j > 0; j--) + { + if (Destination[j] == '\\' || Destination[j] == '/') + break; + } + + destinationLength = j; +#ifdef _DEBUG + Destination[destinationLength] = '\0'; +#endif + } + + // skip the dot segment + i += 2; + continue; + } + } + } + } + + // fix ospath + if (OSPath && (currentCh == '\\' || currentCh == '/')) + currentCh = FS_OSPATH_SEPERATOR_CHARACTER; + + // copy character + if (destinationLength < cbDestination) + { + Destination[destinationLength++] = currentCh; +#ifdef _DEBUG + Destination[destinationLength] = '\0'; +#endif + } + else + break; + + // increment position by one + i++; + } + + // ensure nullptr termination + if (destinationLength < cbDestination) + Destination[destinationLength] = '\0'; + else + Destination[destinationLength - 1] = '\0'; +} + +void CanonicalizePath(String& Destination, const char* Path, bool OSPath /* = true */) +{ + // the function won't actually write any more characters than are present to the buffer, + // so we can get away with simply passing both pointers if they are the same. + if (Destination.GetWriteableCharArray() != Path) + { + // otherwise, resize the destination to at least the source's size, and then pass as-is + Destination.Reserve(static_cast(std::strlen(Path)) + 1); + } + + CanonicalizePath(Destination.GetWriteableCharArray(), Destination.GetBufferSize(), Path, OSPath); + Destination.UpdateSize(); +} + +void CanonicalizePath(String& Destination, bool OSPath /* = true */) +{ + CanonicalizePath(Destination, Destination); +} + +static inline bool FileSystemCharacterIsSane(char c, bool StripSlashes) +{ + if (!(c >= 'a' && c <= 'z') && !(c >= 'A' && c <= 'Z') && !(c >= '0' && c <= '9') && c != ' ' && c != ' ' && + c != '_' && c != '-') + { + if (!StripSlashes && (c == '/' || c == '\\')) + return true; + + return false; + } + + return true; +} + +void SanitizeFileName(char* Destination, u32 cbDestination, const char* FileName, bool StripSlashes /* = true */) +{ + u32 i; + u32 fileNameLength = static_cast(std::strlen(FileName)); + + if (FileName == Destination) + { + for (i = 0; i < fileNameLength; i++) + { + if (!FileSystemCharacterIsSane(FileName[i], StripSlashes)) + Destination[i] = '_'; + } + } + else + { + for (i = 0; i < fileNameLength && i < cbDestination; i++) + { + if (FileSystemCharacterIsSane(FileName[i], StripSlashes)) + Destination[i] = FileName[i]; + else + Destination[i] = '_'; + } + } +} + +void SanitizeFileName(String& Destination, const char* FileName, bool StripSlashes /* = true */) +{ + u32 i; + u32 fileNameLength; + + // if same buffer, use fastpath + if (Destination.GetWriteableCharArray() == FileName) + { + fileNameLength = Destination.GetLength(); + for (i = 0; i < fileNameLength; i++) + { + if (!FileSystemCharacterIsSane(FileName[i], StripSlashes)) + Destination[i] = '_'; + } + } + else + { + fileNameLength = static_cast(std::strlen(FileName)); + Destination.Resize(fileNameLength); + for (i = 0; i < fileNameLength; i++) + { + if (FileSystemCharacterIsSane(FileName[i], StripSlashes)) + Destination[i] = FileName[i]; + else + Destination[i] = '_'; + } + } +} + +void SanitizeFileName(String& Destination, bool StripSlashes /* = true */) +{ + return SanitizeFileName(Destination, Destination, StripSlashes); +} + +void BuildPathRelativeToFile(char* Destination, u32 cbDestination, const char* CurrentFileName, const char* NewFileName, + bool OSPath /* = true */, bool Canonicalize /* = true */) +{ + s32 i; + u32 currentPos = 0; + DebugAssert(Destination != nullptr && cbDestination > 0 && CurrentFileName != nullptr && NewFileName != nullptr); + + // clone to a local buffer if the same pointer + std::string pathClone; + if (Destination == CurrentFileName) + { + pathClone = CurrentFileName; + CurrentFileName = pathClone.c_str(); + } + + // search for a / or \, copy everything up to and including it to the destination + i = (s32)std::strlen(CurrentFileName); + for (; i >= 0; i--) + { + if (CurrentFileName[i] == '/' || CurrentFileName[i] == '\\') + { + // cap to destination length + u32 copyLen; + if (NewFileName[0] != '\0') + copyLen = std::min((u32)(i + 1), cbDestination); + else + copyLen = std::min((u32)i, cbDestination); + + if (copyLen > 0) + { + std::memcpy(Destination, CurrentFileName, copyLen); + if (copyLen == cbDestination) + Destination[cbDestination - 1] = '\0'; + + currentPos = copyLen; + } + + break; + } + } + + // copy the new parts in + if (currentPos < cbDestination && NewFileName[0] != '\0') + StringUtil::Strlcpy(Destination + currentPos, NewFileName, cbDestination - currentPos); + + // canonicalize it + if (Canonicalize) + CanonicalizePath(Destination, cbDestination, Destination, OSPath); + else if (OSPath) + BuildOSPath(Destination, cbDestination, Destination); +} + +void BuildPathRelativeToFile(String& Destination, const char* CurrentFileName, const char* NewFileName, + bool OSPath /* = true */, bool Canonicalize /* = true */) +{ + s32 i; + DebugAssert(CurrentFileName != nullptr && NewFileName != nullptr); + + // get curfile length + u32 curFileLength = static_cast(std::strlen(CurrentFileName)); + + // clone to a local buffer if the same pointer + if (Destination.GetWriteableCharArray() == CurrentFileName) + { + char* pathClone = (char*)alloca(curFileLength + 1); + StringUtil::Strlcpy(pathClone, CurrentFileName, curFileLength + 1); + CurrentFileName = pathClone; + } + + // search for a / or \\, copy everything up to and including it to the destination + Destination.Clear(); + i = (s32)curFileLength; + for (; i >= 0; i--) + { + if (CurrentFileName[i] == '/' || CurrentFileName[i] == '\\') + { + if (NewFileName[0] != '\0') + Destination.AppendSubString(CurrentFileName, 0, i + 1); + else + Destination.AppendSubString(CurrentFileName, 0, i); + + break; + } + } + + // copy the new parts in + if (NewFileName[0] != '\0') + Destination.AppendString(NewFileName); + + // canonicalize it + if (Canonicalize) + CanonicalizePath(Destination, Destination.GetCharArray(), OSPath); + else if (OSPath) + BuildOSPath(Destination, Destination.GetCharArray()); +} + +std::unique_ptr OpenFile(const char* FileName, u32 Flags) +{ + // has a path + if (FileName[0] == '\0') + return nullptr; + + // forward to local file wrapper + return ByteStream_OpenFileStream(FileName, Flags); +} + +FileSystem::ManagedCFilePtr OpenManagedCFile(const char* filename, const char* mode) +{ + return ManagedCFilePtr(OpenCFile(filename, mode), [](std::FILE* fp) { std::fclose(fp); }); +} + +std::FILE* OpenCFile(const char* filename, const char* mode) +{ +#ifdef WIN32 + std::FILE* fp; + if (fopen_s(&fp, filename, mode) != 0) + return nullptr; + + return fp; +#else + return std::fopen(filename, mode); +#endif +} + +void BuildOSPath(char* Destination, u32 cbDestination, const char* Path) +{ + u32 i; + u32 pathLength = static_cast(std::strlen(Path)); + + if (Destination == Path) + { + // fast path + for (i = 0; i < pathLength; i++) + { + if (Destination[i] == '/') + Destination[i] = FS_OSPATH_SEPERATOR_CHARACTER; + } + } + else + { + // slow path + pathLength = std::max(pathLength, cbDestination - 1); + for (i = 0; i < pathLength; i++) + { + Destination[i] = (Path[i] == '/') ? FS_OSPATH_SEPERATOR_CHARACTER : Path[i]; + } + + Destination[pathLength] = '\0'; + } +} + +void BuildOSPath(String& Destination, const char* Path) +{ + u32 i; + u32 pathLength; + + if (Destination.GetWriteableCharArray() == Path) + { + // fast path + pathLength = Destination.GetLength(); + ; + for (i = 0; i < pathLength; i++) + { + if (Destination[i] == '/') + Destination[i] = FS_OSPATH_SEPERATOR_CHARACTER; + } + } + else + { + // slow path + pathLength = static_cast(std::strlen(Path)); + Destination.Resize(pathLength); + for (i = 0; i < pathLength; i++) + { + Destination[i] = (Path[i] == '/') ? FS_OSPATH_SEPERATOR_CHARACTER : Path[i]; + } + } +} + +void BuildOSPath(String& Destination) +{ + BuildOSPath(Destination, Destination); +} + +#ifdef _WIN32 + +static u32 TranslateWin32Attributes(u32 Win32Attributes) +{ + u32 r = 0; + + if (Win32Attributes & FILE_ATTRIBUTE_DIRECTORY) + r |= FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY; + if (Win32Attributes & FILE_ATTRIBUTE_READONLY) + r |= FILESYSTEM_FILE_ATTRIBUTE_READ_ONLY; + if (Win32Attributes & FILE_ATTRIBUTE_COMPRESSED) + r |= FILESYSTEM_FILE_ATTRIBUTE_COMPRESSED; + + return r; +} + +static const u32 READ_DIRECTORY_CHANGES_NOTIFY_FILTER = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_CREATION; + +class ChangeNotifierWin32 : public FileSystem::ChangeNotifier +{ +public: + ChangeNotifierWin32(HANDLE hDirectory, const String& directoryPath, bool recursiveWatch) + : FileSystem::ChangeNotifier(directoryPath, recursiveWatch), m_hDirectory(hDirectory), + m_directoryChangeQueued(false) + { + m_bufferSize = 16384; + m_pBuffer = new byte[m_bufferSize]; + } + + virtual ~ChangeNotifierWin32() + { + // if there is outstanding io, cancel it + if (m_directoryChangeQueued) + { + CancelIo(m_hDirectory); + + DWORD bytesTransferred; + GetOverlappedResult(m_hDirectory, &m_overlapped, &bytesTransferred, TRUE); + } + + CloseHandle(m_hDirectory); + delete[] m_pBuffer; + } + + virtual void EnumerateChanges(EnumerateChangesCallback callback, void* pUserData) override + { + DWORD bytesRead; + if (!GetOverlappedResult(m_hDirectory, &m_overlapped, &bytesRead, FALSE)) + { + if (GetLastError() == ERROR_IO_INCOMPLETE) + return; + + CancelIo(m_hDirectory); + m_directoryChangeQueued = false; + + QueueReadDirectoryChanges(); + return; + } + + // not queued any more + m_directoryChangeQueued = false; + + // has any bytes? + if (bytesRead > 0) + { + const byte* pCurrentPointer = m_pBuffer; + PathString fileName; + for (;;) + { + const FILE_NOTIFY_INFORMATION* pFileNotifyInformation = + reinterpret_cast(pCurrentPointer); + + // translate the event + u32 changeEvent = 0; + if (pFileNotifyInformation->Action == FILE_ACTION_ADDED) + changeEvent = ChangeEvent_FileAdded; + else if (pFileNotifyInformation->Action == FILE_ACTION_REMOVED) + changeEvent = ChangeEvent_FileRemoved; + else if (pFileNotifyInformation->Action == FILE_ACTION_MODIFIED) + changeEvent = ChangeEvent_FileModified; + else if (pFileNotifyInformation->Action == FILE_ACTION_RENAMED_OLD_NAME) + changeEvent = ChangeEvent_RenamedOldName; + else if (pFileNotifyInformation->Action == FILE_ACTION_RENAMED_NEW_NAME) + changeEvent = ChangeEvent_RenamedNewName; + + // translate the filename + int fileNameLength = + WideCharToMultiByte(CP_UTF8, 0, pFileNotifyInformation->FileName, + pFileNotifyInformation->FileNameLength / sizeof(WCHAR), nullptr, 0, nullptr, nullptr); + DebugAssert(fileNameLength >= 0); + fileName.Resize(fileNameLength); + fileNameLength = WideCharToMultiByte(CP_UTF8, 0, pFileNotifyInformation->FileName, + pFileNotifyInformation->FileNameLength / sizeof(WCHAR), + fileName.GetWriteableCharArray(), fileName.GetLength(), nullptr, nullptr); + if (fileNameLength != (int)fileName.GetLength()) + fileName.Resize(fileNameLength); + + // prepend the base path + fileName.PrependFormattedString("%s\\", m_directoryPath.GetCharArray()); + + // construct change info + ChangeInfo changeInfo; + changeInfo.Path = fileName; + changeInfo.Event = changeEvent; + + // invoke callback + callback(&changeInfo, pUserData); + + // has a next entry? + if (pFileNotifyInformation->NextEntryOffset == 0) + break; + + pCurrentPointer += pFileNotifyInformation->NextEntryOffset; + DebugAssert(pCurrentPointer < (m_pBuffer + m_bufferSize)); + } + } + + // re-queue the operation + QueueReadDirectoryChanges(); + } + + bool QueueReadDirectoryChanges() + { + DebugAssert(!m_directoryChangeQueued); + + std::memset(&m_overlapped, 0, sizeof(m_overlapped)); + if (ReadDirectoryChangesW(m_hDirectory, m_pBuffer, m_bufferSize, m_recursiveWatch, + READ_DIRECTORY_CHANGES_NOTIFY_FILTER, nullptr, &m_overlapped, nullptr) == FALSE) + return false; + + m_directoryChangeQueued = true; + return true; + } + +private: + HANDLE m_hDirectory; + OVERLAPPED m_overlapped; + bool m_directoryChangeQueued; + byte* m_pBuffer; + u32 m_bufferSize; +}; + +std::unique_ptr CreateChangeNotifier(const char* path, bool recursiveWatch) +{ + // open the directory up + HANDLE hDirectory = CreateFileA(path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr); + if (hDirectory == nullptr) + return nullptr; + + // queue up the overlapped io + auto pChangeNotifier = std::make_unique(hDirectory, path, recursiveWatch); + if (!pChangeNotifier->QueueReadDirectoryChanges()) + return nullptr; + + return pChangeNotifier; +} + +static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, const char* Path, const char* Pattern, + u32 Flags, FileSystem::FindResultsArray* pResults) +{ + std::string tempStr; + if (Path) + { + if (ParentPath) + tempStr = StringUtil::StdStringFromFormat("%s\\%s\\%s\\*", OriginPath, ParentPath, Path); + else + tempStr = StringUtil::StdStringFromFormat("%s\\%s\\*", OriginPath, Path); + } + else + { + tempStr = StringUtil::StdStringFromFormat("%s\\*", OriginPath); + } + + WIN32_FIND_DATA wfd; + HANDLE hFind = FindFirstFileA(tempStr.c_str(), &wfd); + if (hFind == INVALID_HANDLE_VALUE) + return 0; + + // small speed optimization for '*' case + bool hasWildCards = false; + bool wildCardMatchAll = false; + u32 nFiles = 0; + if (std::strpbrk(Pattern, "*?") != nullptr) + { + hasWildCards = true; + wildCardMatchAll = !(std::strcmp(Pattern, "*")); + } + + // iterate results + do + { + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN && !(Flags & FILESYSTEM_FIND_HIDDEN_FILES)) + continue; + + if (wfd.cFileName[0] == '.') + { + if (wfd.cFileName[1] == '\0' || (wfd.cFileName[1] == '.' && wfd.cFileName[2] == '\0')) + continue; + + if (!(Flags & FILESYSTEM_FIND_HIDDEN_FILES)) + continue; + } + + FILESYSTEM_FIND_DATA outData; + outData.Attributes = 0; + + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (Flags & FILESYSTEM_FIND_RECURSIVE) + { + // recurse into this directory + if (ParentPath != nullptr) + { + const std::string recurseDir = StringUtil::StdStringFromFormat("%s\\%s", ParentPath, Path); + nFiles += RecursiveFindFiles(OriginPath, recurseDir.c_str(), wfd.cFileName, Pattern, Flags, pResults); + } + else + { + nFiles += RecursiveFindFiles(OriginPath, Path, wfd.cFileName, Pattern, Flags, pResults); + } + } + + if (!(Flags & FILESYSTEM_FIND_FOLDERS)) + continue; + + outData.Attributes |= FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY; + } + else + { + if (!(Flags & FILESYSTEM_FIND_FILES)) + continue; + } + + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + outData.Attributes |= FILESYSTEM_FILE_ATTRIBUTE_READ_ONLY; + + // match the filename + if (hasWildCards) + { + if (!wildCardMatchAll && !StringUtil::WildcardMatch(wfd.cFileName, Pattern)) + continue; + } + else + { + if (std::strcmp(wfd.cFileName, Pattern) != 0) + continue; + } + + // add file to list + // TODO string formatter, clean this mess.. + if (!(Flags & FILESYSTEM_FIND_RELATIVE_PATHS)) + { + if (ParentPath != nullptr) + outData.FileName = + StringUtil::StdStringFromFormat("%s\\%s\\%s\\%s", OriginPath, ParentPath, Path, wfd.cFileName); + else if (Path != nullptr) + outData.FileName = StringUtil::StdStringFromFormat("%s\\%s\\%s", OriginPath, Path, wfd.cFileName); + else + outData.FileName = StringUtil::StdStringFromFormat("%s\\%s", OriginPath, wfd.cFileName); + } + else + { + if (ParentPath != nullptr) + outData.FileName = StringUtil::StdStringFromFormat("%s\\%s\\%s", ParentPath, Path, wfd.cFileName); + else if (Path != nullptr) + outData.FileName = StringUtil::StdStringFromFormat("%s\\%s", Path, wfd.cFileName); + else + outData.FileName = wfd.cFileName; + } + + outData.ModificationTime.SetWindowsFileTime(&wfd.ftLastWriteTime); + outData.Size = (u64)wfd.nFileSizeHigh << 32 | (u64)wfd.nFileSizeLow; + + nFiles++; + pResults->push_back(std::move(outData)); + } while (FindNextFileA(hFind, &wfd) == TRUE); + FindClose(hFind); + + return nFiles; +} + +bool FileSystem::FindFiles(const char* Path, const char* Pattern, u32 Flags, FindResultsArray* pResults) +{ + // has a path + if (Path[0] == '\0') + return false; + + // clear result array + if (!(Flags & FILESYSTEM_FIND_KEEP_ARRAY)) + pResults->clear(); + + // enter the recursive function + return (RecursiveFindFiles(Path, nullptr, nullptr, Pattern, Flags, pResults) > 0); +} + +bool FileSystem::StatFile(const char* Path, FILESYSTEM_STAT_DATA* pStatData) +{ + // has a path + if (Path[0] == '\0') + return false; + + // determine attributes for the path. if it's a directory, things have to be handled differently.. + DWORD fileAttributes = GetFileAttributesA(Path); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + return false; + + // test if it is a directory + HANDLE hFile; + if (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + hFile = CreateFileA(Path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + } + else + { + hFile = CreateFileA(Path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, + OPEN_EXISTING, 0, nullptr); + } + + // createfile succeded? + if (hFile == INVALID_HANDLE_VALUE) + return false; + + // use GetFileInformationByHandle + BY_HANDLE_FILE_INFORMATION bhfi; + if (GetFileInformationByHandle(hFile, &bhfi) == FALSE) + { + CloseHandle(hFile); + return false; + } + + // close handle + CloseHandle(hFile); + + // fill in the stat data + pStatData->Attributes = TranslateWin32Attributes(bhfi.dwFileAttributes); + pStatData->ModificationTime.SetWindowsFileTime(&bhfi.ftLastWriteTime); + pStatData->Size = ((u64)bhfi.nFileSizeHigh) << 32 | (u64)bhfi.nFileSizeLow; + return true; +} + +bool FileSystem::FileExists(const char* Path) +{ + // has a path + if (Path[0] == '\0') + return false; + + // determine attributes for the path. if it's a directory, things have to be handled differently.. + DWORD fileAttributes = GetFileAttributesA(Path); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + return false; + + if (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) + return false; + else + return true; +} + +bool FileSystem::DirectoryExists(const char* Path) +{ + // has a path + if (Path[0] == '\0') + return false; + + // determine attributes for the path. if it's a directory, things have to be handled differently.. + DWORD fileAttributes = GetFileAttributesA(Path); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + return false; + + if (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) + return true; + else + return false; +} + +bool FileSystem::GetFileName(String& Destination, const char* FileName) +{ + // fastpath for non-existant files + DWORD fileAttributes = GetFileAttributesA(FileName); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + return false; + + // // temp buffer for storing string returned by windows + // char tempName[MAX_PATH]; + // DWORD tempNameLength; + // + // // query windows + // if ((tempNameLength = GetFullPathNameA(FileName, countof(tempName), tempName, nullptr)) == 0 || tempNameLength + // >= countof(tempName)) + // { + // // something went wrong, or buffer overflow + // return false; + // } + // + // // move it into destination buffer, doesn't matter if it's the same as FileName, as + // // we aren't going to use it any more. + // DebugAssert(Destination[tempNameLength] == '\0'); + // Destination = tempName; + if (Destination.GetWriteableCharArray() != FileName) + Destination = FileName; + + return true; +} + +bool FileSystem::GetFileName(String& FileName) +{ + return GetFileName(FileName, FileName); +} + +bool FileSystem::CreateDirectory(const char* Path, bool Recursive) +{ + u32 i; + DWORD lastError; + + // has a path + if (Path[0] == '\0') + return false; + + // try just flat-out, might work if there's no other segments that have to be made + if (CreateDirectoryA(Path, nullptr)) + return true; + + // check error + lastError = GetLastError(); + if (lastError == ERROR_ALREADY_EXISTS) + { + // check the attributes + u32 Attributes = GetFileAttributesA(Path); + if (Attributes != INVALID_FILE_ATTRIBUTES && Attributes & FILE_ATTRIBUTE_DIRECTORY) + return true; + else + return false; + } + else if (lastError == ERROR_PATH_NOT_FOUND) + { + // part of the path does not exist, so we'll create the parent folders, then + // the full path again. allocate another buffer with the same length + u32 pathLength = static_cast(std::strlen(Path)); + char* tempStr = (char*)alloca(pathLength + 1); + + // create directories along the path + for (i = 0; i < pathLength; i++) + { + if (Path[i] == '\\') + { + tempStr[i] = '\0'; + if (!CreateDirectoryA(tempStr, nullptr)) + { + lastError = GetLastError(); + if (lastError == ERROR_ALREADY_EXISTS) // fine, continue to next path segment + continue; + else // anything else is a fail + return false; + } + } + + tempStr[i] = Path[i]; + } + + // re-create the end if it's not a separator, check / as well because windows can interpret them + if (Path[pathLength - 1] != '\\' && Path[pathLength - 1] != '\\') + { + if (!CreateDirectoryA(Path, nullptr)) + { + lastError = GetLastError(); + if (lastError != ERROR_ALREADY_EXISTS) + return false; + } + } + + // ok + return true; + } + else + { + // unhandled error + return false; + } +} + +bool FileSystem::DeleteFile(const char* Path) +{ + if (Path[0] == '\0') + return false; + + DWORD fileAttributes = GetFileAttributesA(Path); + if (fileAttributes == INVALID_FILE_ATTRIBUTES) + return false; + + if (!(fileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + return (DeleteFileA(Path) == TRUE); + else + return false; +} + +bool FileSystem::DeleteDirectory(const char* Path, bool Recursive) +{ + // ensure it exists + DWORD fileAttributes = GetFileAttributesA(Path); + if (fileAttributes == INVALID_FILE_ATTRIBUTES || !(fileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + return false; + + // non-recursive case just try removing the directory + if (!Recursive) + return (RemoveDirectoryA(Path) == TRUE); + + // doing a recursive delete + SmallString fileName; + fileName.Format("%s\\*", Path); + + // is there any files? + WIN32_FIND_DATA findData; + HANDLE hFind = FindFirstFileA(fileName, &findData); + if (hFind == INVALID_HANDLE_VALUE) + return false; + + // search through files + do + { + // skip . and .. + if (findData.cFileName[0] == '.') + { + if ((findData.cFileName[1] == '\0') || (findData.cFileName[1] == '.' && findData.cFileName[2] == '\0')) + { + continue; + } + } + + // found a directory? + fileName.Format("%s\\%s", Path, findData.cFileName); + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + // recurse into that + if (!DeleteDirectory(fileName, true)) + return false; + } + else + { + // found a file, so delete it + if (!DeleteFileA(fileName)) + return false; + } + } while (FindNextFileA(hFind, &findData)); + FindClose(hFind); + + // nuke the directory itself + if (!RemoveDirectoryA(Path)) + return false; + + // done + return true; +} + +#else + +std::unique_ptr CreateChangeNotifier(const char* path, bool recursiveWatch) +{ + Log_ErrorPrintf("FileSystem::CreateChangeNotifier(%s) not implemented", path); + return nullptr; +} + +static u32 RecursiveFindFiles(const char* OriginPath, const char* ParentPath, const char* Path, const char* Pattern, + u32 Flags, FindResultsArray* pResults) +{ + std::string tempStr; + if (Path) + { + if (ParentPath) + tempStr = StringUtil::StdStringFromFormat("%s/%s/%s", OriginPath, ParentPath, Path); + else + tempStr = StringUtil::StdStringFromFormat("%s/%s", OriginPath, Path); + } + else + { + tempStr = StringUtil::StdStringFromFormat("%s", OriginPath); + } + + DIR* pDir = opendir(tempStr.c_str()); + if (pDir == nullptr) + return 0; + + // small speed optimization for '*' case + bool hasWildCards = false; + bool wildCardMatchAll = false; + u32 nFiles = 0; + if (std::strpbrk(Pattern, "*?")) + { + hasWildCards = true; + wildCardMatchAll = (std::strcmp(Pattern, "*") == 0); + } + + // iterate results + struct dirent* pDirEnt; + while ((pDirEnt = readdir(pDir)) != nullptr) + { + // if (wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN && !(Flags & FILESYSTEM_FIND_HIDDEN_FILES)) + // continue; + // + if (pDirEnt->d_name[0] == '.') + { + if (pDirEnt->d_name[1] == '\0' || (pDirEnt->d_name[1] == '.' && pDirEnt->d_name[2] == '\0')) + continue; + + if (!(Flags & FILESYSTEM_FIND_HIDDEN_FILES)) + continue; + } + + FILESYSTEM_FIND_DATA outData; + outData.Attributes = 0; + + if (pDirEnt->d_type == DT_DIR) + { + if (Flags & FILESYSTEM_FIND_RECURSIVE) + { + // recurse into this directory + if (ParentPath != nullptr) + { + std::string recursiveDir = StringUtil::StdStringFromFormat("%s/%s", ParentPath, Path); + nFiles += RecursiveFindFiles(OriginPath, recursiveDir.c_str(), pDirEnt->d_name, Pattern, Flags, pResults); + } + else + { + nFiles += RecursiveFindFiles(OriginPath, Path, pDirEnt->d_name, Pattern, Flags, pResults); + } + } + + if (!(Flags & FILESYSTEM_FIND_FOLDERS)) + continue; + + outData.Attributes |= FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY; + } + else + { + if (!(Flags & FILESYSTEM_FIND_FILES)) + continue; + } + + // if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + // outData.Attributes |= FILESYSTEM_FILE_ATTRIBUTE_READ_ONLY; + + // match the filename + if (hasWildCards) + { + if (!wildCardMatchAll && !StringUtil::WildcardMatch(pDirEnt->d_name, Pattern)) + continue; + } + else + { + if (std::strcmp(pDirEnt->d_name, Pattern) != 0) + continue; + } + + // add file to list + // TODO string formatter, clean this mess.. + if (!(Flags & FILESYSTEM_FIND_RELATIVE_PATHS)) + { + if (ParentPath != nullptr) + outData.FileName = + StringUtil::StdStringFromFormat("%s/%s/%s/%s", OriginPath, ParentPath, Path, pDirEnt->d_name); + else if (Path != nullptr) + outData.FileName = StringUtil::StdStringFromFormat("%s/%s/%s", OriginPath, Path, pDirEnt->d_name); + else + outData.FileName = StringUtil::StdStringFromFormat("%s/%s", OriginPath, pDirEnt->d_name); + } + else + { + if (ParentPath != nullptr) + outData.FileName = StringUtil::StdStringFromFormat("%s\\%s\\%s", ParentPath, Path, pDirEnt->d_name); + else if (Path != nullptr) + outData.FileName = StringUtil::StdStringFromFormat("%s\\%s", Path, pDirEnt->d_name); + else + outData.FileName = pDirEnt->d_name; + } + + nFiles++; + pResults->push_back(std::move(outData)); + } + + closedir(pDir); + return nFiles; +} + +bool FindFiles(const char* Path, const char* Pattern, u32 Flags, FindResultsArray* pResults) +{ + // has a path + if (Path[0] == '\0') + return false; + + // clear result array + if (!(Flags & FILESYSTEM_FIND_KEEP_ARRAY)) + pResults->clear(); + + // enter the recursive function + return (RecursiveFindFiles(Path, nullptr, nullptr, Pattern, Flags, pResults) > 0); +} + +bool StatFile(const char* Path, FILESYSTEM_STAT_DATA* pStatData) +{ + // has a path + if (Path[0] == '\0') + return false; + + // stat file + struct stat64 sysStatData; + if (stat64(Path, &sysStatData) < 0) + return false; + + // parse attributes + pStatData->Attributes = 0; + if (S_ISDIR(sysStatData.st_mode)) + pStatData->Attributes |= FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY; + + // parse times + pStatData->ModificationTime.SetUnixTimestamp((Timestamp::UnixTimestampValue)sysStatData.st_mtime); + + // parse size + if (S_ISREG(sysStatData.st_mode)) + pStatData->Size = static_cast(sysStatData.st_size); + else + pStatData->Size = 0; + + // ok + return true; +} + +bool FileExists(const char* Path) +{ + // has a path + if (Path[0] == '\0') + return false; + + // stat file + struct stat64 sysStatData; + if (stat64(Path, &sysStatData) < 0) + return false; + + if (S_ISDIR(sysStatData.st_mode)) + return false; + else + return true; +} + +bool DirectoryExists(const char* Path) +{ + // has a path + if (Path[0] == '\0') + return false; + + // stat file + struct stat64 sysStatData; + if (stat64(Path, &sysStatData) < 0) + return false; + + if (S_ISDIR(sysStatData.st_mode)) + return true; + else + return false; +} + +bool GetFileName(String& Destination, const char* FileName) +{ + // fastpath for non-existant files + struct stat sysStatData; + if (stat(FileName, &sysStatData) < 0) + return false; + + if (Destination.GetWriteableCharArray() != FileName) + Destination = FileName; + + return true; +} + +bool GetFileName(String& FileName) +{ + return GetFileName(FileName, FileName); +} + +bool CreateDirectory(const char* Path, bool Recursive) +{ + u32 i; + int lastError; + + // has a path + if (Path[0] == '\0') + return false; + + // try just flat-out, might work if there's no other segments that have to be made + if (mkdir(Path, 0777) == 0) + return true; + + // check error + lastError = errno; + if (lastError == EEXIST) + { + // check the attributes + struct stat sysStatData; + if (stat(Path, &sysStatData) == 0 && S_ISDIR(sysStatData.st_mode)) + return true; + else + return false; + } + else if (lastError == ENOENT) + { + // part of the path does not exist, so we'll create the parent folders, then + // the full path again. allocate another buffer with the same length + u32 pathLength = static_cast(std::strlen(Path)); + char* tempStr = (char*)alloca(pathLength + 1); + + // create directories along the path + for (i = 0; i < pathLength; i++) + { + if (Path[i] == '/') + { + tempStr[i] = '\0'; + if (mkdir(tempStr, 0777) < 0) + { + lastError = errno; + if (lastError == EEXIST) // fine, continue to next path segment + continue; + else // anything else is a fail + return false; + } + } + + tempStr[i] = Path[i]; + } + + // re-create the end if it's not a separator, check / as well because windows can interpret them + if (Path[pathLength - 1] != '/') + { + if (mkdir(Path, 0777) < 0) + { + lastError = errno; + if (lastError != EEXIST) + return false; + } + } + + // ok + return true; + } + else + { + // unhandled error + return false; + } +} + +bool DeleteFile(const char* Path) +{ + if (Path[0] == '\0') + return false; + + struct stat sysStatData; + if (stat(Path, &sysStatData) != 0 || S_ISDIR(sysStatData.st_mode)) + return false; + + return (unlink(Path) == 0); +} + +bool DeleteDirectory(const char* Path, bool Recursive) +{ + Log_ErrorPrintf("FileSystem::DeleteDirectory(%s) not implemented", Path); + return false; +} + +#endif + +} // namespace FileSystem \ No newline at end of file diff --git a/src/common/file_system.h b/src/common/file_system.h new file mode 100644 index 000000000..448887dc9 --- /dev/null +++ b/src/common/file_system.h @@ -0,0 +1,176 @@ +#pragma once +#include "timestamp.h" +#include "types.h" +#include +#include +#include +#include + +class ByteStream; + +#ifdef Y_PLATFORM_WINDOWS +#define FS_OSPATH_SEPERATOR_CHARACTER '\\' +#else +#define FS_OSPATH_SEPERATOR_CHARACTER '/' +#endif + +enum FILESYSTEM_FILE_ATTRIBUTES +{ + FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY = 1, + FILESYSTEM_FILE_ATTRIBUTE_READ_ONLY = 2, + FILESYSTEM_FILE_ATTRIBUTE_COMPRESSED = 4, +}; + +enum FILESYSTEM_FIND_FLAGS +{ + FILESYSTEM_FIND_RECURSIVE = (1 << 0), + FILESYSTEM_FIND_RELATIVE_PATHS = (1 << 1), + FILESYSTEM_FIND_HIDDEN_FILES = (1 << 2), + FILESYSTEM_FIND_FOLDERS = (1 << 3), + FILESYSTEM_FIND_FILES = (1 << 4), + FILESYSTEM_FIND_KEEP_ARRAY = (1 << 5), +}; + +struct FILESYSTEM_STAT_DATA +{ + u32 Attributes; + Timestamp ModificationTime; + u64 Size; +}; + +struct FILESYSTEM_FIND_DATA +{ + std::string FileName; + Timestamp ModificationTime; + u32 Attributes; + u64 Size; +}; + +struct FILESYSTEM_CHANGE_NOTIFY_DATA +{ + String DirectoryPath; + bool RecursiveWatch; + + void* pSystemData; +}; + +namespace FileSystem { + +using FindResultsArray = std::vector; + +class ChangeNotifier +{ +public: + enum ChangeEvent + { + ChangeEvent_FileAdded = (1 << 0), + ChangeEvent_FileRemoved = (1 << 1), + ChangeEvent_FileModified = (1 << 2), + ChangeEvent_RenamedOldName = (1 << 3), + ChangeEvent_RenamedNewName = (1 << 4), + }; + + struct ChangeInfo + { + const char* Path; + u32 Event; + }; + +public: + virtual ~ChangeNotifier(); + + const String& GetDirectoryPath() const { return m_directoryPath; } + const bool GetRecursiveWatch() const { return m_recursiveWatch; } + + typedef void (*EnumerateChangesCallback)(const ChangeInfo* pChangeInfo, void* pUserData); + virtual void EnumerateChanges(EnumerateChangesCallback callback, void* pUserData) = 0; + +private: + template + static void EnumerateChangesTrampoline(const ChangeInfo* pChangeInfo, void* pUserData) + { + CALLBACK_TYPE* pRealCallback = reinterpret_cast(pUserData); + (*pRealCallback)(pChangeInfo); + } + +public: + template + void EnumerateChanges(CALLBACK_TYPE callback) + { + CALLBACK_TYPE* pCallback = &callback; + EnumerateChanges(&ChangeNotifier::EnumerateChangesTrampoline, reinterpret_cast(pCallback)); + } + +protected: + ChangeNotifier(const String& directoryPath, bool recursiveWatch); + + String m_directoryPath; + bool m_recursiveWatch; +}; + +// create a change notifier +std::unique_ptr CreateChangeNotifier(const char* path, bool recursiveWatch); + +// appends a path string to the current path string. optionally canonicalizes it. +void AppendPath(char* Path, u32 cbPath, const char* NewPath); +void AppendPath(String& Path, const char* NewPath); + +// canonicalize a path string (i.e. replace .. with actual folder name, etc), if OS path is used, on windows, the +// separators will be \, otherwise / +void CanonicalizePath(char* Destination, u32 cbDestination, const char* Path, bool OSPath = true); +void CanonicalizePath(String& Destination, const char* Path, bool OSPath = true); +void CanonicalizePath(String& Destination, bool OSPath = true); + +// translates the specified path into a string compatible with the hosting OS +void BuildOSPath(char* Destination, u32 cbDestination, const char* Path); +void BuildOSPath(String& Destination, const char* Path); +void BuildOSPath(String& Destination); + +// builds a path relative to the specified file, optionally canonicalizing it +void BuildPathRelativeToFile(char* Destination, u32 cbDestination, const char* CurrentFileName, const char* NewFileName, + bool OSPath = true, bool Canonicalize = true); +void BuildPathRelativeToFile(String& Destination, const char* CurrentFileName, const char* NewFileName, + bool OSPath = true, bool Canonicalize = true); + +// sanitizes a filename for use in a filesystem. +void SanitizeFileName(char* Destination, u32 cbDestination, const char* FileName, bool StripSlashes = true); +void SanitizeFileName(String& Destination, const char* FileName, bool StripSlashes = true); +void SanitizeFileName(String& Destination, bool StripSlashes = true); + +// search for files +bool FindFiles(const char* Path, const char* Pattern, u32 Flags, FindResultsArray* pResults); + +// stat file +bool StatFile(const char* Path, FILESYSTEM_STAT_DATA* pStatData); + +// file exists? +bool FileExists(const char* Path); + +// directory exists? +bool DirectoryExists(const char* Path); + +// delete file +bool DeleteFile(const char* Path); + +// reads file name +bool GetFileName(String& Destination, const char* FileName); +bool GetFileName(String& FileName); + +// open files +std::unique_ptr OpenFile(const char* FileName, u32 Flags); + +using ManagedCFilePtr = std::unique_ptr; +ManagedCFilePtr OpenManagedCFile(const char* filename, const char* mode); +std::FILE* OpenCFile(const char* filename, const char* mode); + +// creates a directory in the local filesystem +// if the directory already exists, the return value will be true. +// if Recursive is specified, all parent directories will be created +// if they do not exist. +bool CreateDirectory(const char* Path, bool Recursive); + +// deletes a directory in the local filesystem +// if the directory has files, unless the recursive flag is set, it will fail +bool DeleteDirectory(const char* Path, bool Recursive); + +}; // namespace FileSystem diff --git a/src/common/gl/program.cpp b/src/common/gl/program.cpp index 84c9ccfbe..99455bcad 100644 --- a/src/common/gl/program.cpp +++ b/src/common/gl/program.cpp @@ -1,6 +1,7 @@ #include "program.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/String.h" +#include "../assert.h" +#include "../log.h" +#include "../string_util.h" #include #include Log_SetChannel(GL); @@ -46,7 +47,7 @@ GLuint Program::CompileShader(GLenum type, const std::string_view source) { Log_ErrorPrintf("Shader failed to compile:\n%s", info_log.c_str()); - std::ofstream ofs(SmallString::FromFormat("bad_shader_%u.txt", s_next_bad_shader_id++), + std::ofstream ofs(StringUtil::StdStringFromFormat("bad_shader_%u.txt", s_next_bad_shader_id++).c_str(), std::ofstream::out | std::ofstream::binary); if (ofs.is_open()) { diff --git a/src/common/gl/stream_buffer.cpp b/src/common/gl/stream_buffer.cpp index b8b17827b..09c2d81ea 100644 --- a/src/common/gl/stream_buffer.cpp +++ b/src/common/gl/stream_buffer.cpp @@ -1,5 +1,6 @@ #include "stream_buffer.h" -#include "YBaseLib/Assert.h" +#include "../assert.h" +#include "../align.h" #include #include diff --git a/src/common/gl/texture.cpp b/src/common/gl/texture.cpp index e0b3fd596..edd8368f1 100644 --- a/src/common/gl/texture.cpp +++ b/src/common/gl/texture.cpp @@ -1,6 +1,6 @@ #include "texture.h" -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" +#include "../assert.h" +#include "../log.h" Log_SetChannel(GL); namespace GL { diff --git a/src/common/iso_reader.cpp b/src/common/iso_reader.cpp index d5910e316..8192ec1d7 100644 --- a/src/common/iso_reader.cpp +++ b/src/common/iso_reader.cpp @@ -1,5 +1,5 @@ #include "iso_reader.h" -#include "YBaseLib/Log.h" +#include "log.h" #include "cd_image.h" #include Log_SetChannel(ISOReader); diff --git a/src/common/jit_code_buffer.cpp b/src/common/jit_code_buffer.cpp index 8aea9bece..82282e55c 100644 --- a/src/common/jit_code_buffer.cpp +++ b/src/common/jit_code_buffer.cpp @@ -1,9 +1,12 @@ #include "jit_code_buffer.h" -#include "YBaseLib/Assert.h" +#include "align.h" +#include "assert.h" +#include "cpu_detect.h" +#include -#if defined(Y_PLATFORM_WINDOWS) -#include "YBaseLib/Windows/WindowsHeaders.h" -#elif defined(Y_PLATFORM_LINUX) || defined(Y_PLATFORM_ANDROID) +#if defined(WIN32) +#include "windows_headers.h" +#else #include #endif @@ -11,9 +14,9 @@ JitCodeBuffer::JitCodeBuffer(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_siz { m_total_size = size + far_code_size; -#if defined(Y_PLATFORM_WINDOWS) +#if defined(WIN32) m_code_ptr = static_cast(VirtualAlloc(nullptr, m_total_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE)); -#elif defined(Y_PLATFORM_LINUX) || defined(Y_PLATFORM_ANDROID) +#elif defined(__linux__) || defined(__ANDROID__) m_code_ptr = static_cast( mmap(nullptr, m_total_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); #else @@ -34,9 +37,9 @@ JitCodeBuffer::JitCodeBuffer(u32 size /* = 64 * 1024 * 1024 */, u32 far_code_siz JitCodeBuffer::~JitCodeBuffer() { -#if defined(Y_PLATFORM_WINDOWS) +#if defined(WIN32) VirtualFree(m_code_ptr, 0, MEM_RELEASE); -#elif defined(Y_PLATFORM_LINUX) || defined(Y_PLATFORM_ANDROID) +#elif defined(__linux__) || defined(__ANDROID__) munmap(m_code_ptr, m_total_size); #endif } @@ -46,7 +49,7 @@ void JitCodeBuffer::CommitCode(u32 length) if (length == 0) return; -#if defined(Y_CPU_ARM) || defined(Y_CPU_AARCH64) +#if defined(CPU_ARM) || defined(CPU_AARCH64) // ARM instruction and data caches are not coherent, we need to flush after every block. FlushInstructionCache(m_free_code_ptr, length); #endif @@ -61,7 +64,7 @@ void JitCodeBuffer::CommitFarCode(u32 length) if (length == 0) return; -#if defined(Y_CPU_ARM) || defined(Y_CPU_AARCH64) +#if defined(CPU_ARM) || defined(CPU_AARCH64) // ARM instruction and data caches are not coherent, we need to flush after every block. FlushInstructionCache(m_free_far_code_ptr, length); #endif @@ -101,9 +104,9 @@ void JitCodeBuffer::Align(u32 alignment, u8 padding_value) void JitCodeBuffer::FlushInstructionCache(void* address, u32 size) { -#if defined(Y_PLATFORM_WINDOWS) +#if defined(WIN32) ::FlushInstructionCache(GetCurrentProcess(), address, size); -#elif defined(Y_COMPILER_GCC) || defined(Y_COMPILER_CLANG) +#elif defined(__GNUC__) || defined(__clang__) __builtin___clear_cache(reinterpret_cast(address), reinterpret_cast(address) + size); #else #error Unknown platform. diff --git a/src/common/log.cpp b/src/common/log.cpp new file mode 100644 index 000000000..4b05bef5a --- /dev/null +++ b/src/common/log.cpp @@ -0,0 +1,343 @@ +#include "log.h" +#include "assert.h" +#include "string.h" +#include "timer.h" +#include +#include + +#if defined(WIN32) +#include "windows_headers.h" +#elif defined(__ANDROID__) +#include +#else +#include +#endif + +namespace Log { + +struct RegisteredCallback +{ + CallbackFunctionType Function; + void* Parameter; +}; + +std::vector s_callbacks; +static std::mutex s_callback_mutex; + +static LOGLEVEL s_filter_level = LOGLEVEL_TRACE; + +static Common::Timer::Value s_startTimeStamp = Common::Timer::GetValue(); + +static bool s_consoleOutputEnabled = false; +static String s_consoleOutputChannelFilter; +static LOGLEVEL s_consoleOutputLevelFilter = LOGLEVEL_TRACE; + +static bool s_debugOutputEnabled = false; +static String s_debugOutputChannelFilter; +static LOGLEVEL s_debugOutputLevelFilter = LOGLEVEL_TRACE; + +void RegisterCallback(CallbackFunctionType callbackFunction, void* pUserParam) +{ + RegisteredCallback Callback; + Callback.Function = callbackFunction; + Callback.Parameter = pUserParam; + + std::lock_guard guard(s_callback_mutex); + s_callbacks.push_back(std::move(Callback)); +} + +void UnregisterCallback(CallbackFunctionType callbackFunction, void* pUserParam) +{ + std::lock_guard guard(s_callback_mutex); + + for (auto iter = s_callbacks.begin(); iter != s_callbacks.end(); ++iter) + { + if (iter->Function == callbackFunction && iter->Parameter == pUserParam) + { + s_callbacks.erase(iter); + break; + } + } +} + +static void ExecuteCallbacks(const char* channelName, const char* functionName, LOGLEVEL level, const char* message) +{ + std::lock_guard guard(s_callback_mutex); + for (RegisteredCallback& callback : s_callbacks) + callback.Function(callback.Parameter, channelName, functionName, level, message); +} + +static void FormatLogMessageForDisplay(const char* channelName, const char* functionName, LOGLEVEL level, + const char* message, void (*printCallback)(const char*, void*), + void* pCallbackUserData) +{ + static const char levelCharacters[LOGLEVEL_COUNT] = {'X', 'E', 'W', 'P', 'S', 'I', 'D', 'R', 'B', 'T'}; + + // find time since start of process + float messageTime = + static_cast(Common::Timer::ConvertValueToSeconds(Common::Timer::GetValue() - s_startTimeStamp)); + + // write prefix +#ifndef Y_BUILD_CONFIG_SHIPPING + char prefix[256]; + if (level <= LOGLEVEL_PERF) + std::snprintf(prefix, countof(prefix), "[%10.4f] %c(%s): ", messageTime, levelCharacters[level], functionName); + else + std::snprintf(prefix, countof(prefix), "[%10.4f] %c/%s: ", messageTime, levelCharacters[level], channelName); + + printCallback(prefix, pCallbackUserData); +#else + char prefix[256]; + std::snprintf(prefix, countof(prefix), "[%10.4f] %c/%s: ", messageTime, levelCharacters[level], channelName); + printCallback(prefix, pCallbackUserData); +#endif + + // write message + printCallback(message, pCallbackUserData); +} + +#if defined(WIN32) + +static void ConsoleOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, + LOGLEVEL level, const char* message) +{ + if (!s_consoleOutputEnabled || level > s_consoleOutputLevelFilter || + s_consoleOutputChannelFilter.Find(channelName) >= 0) + return; + + if (level > LOGLEVEL_COUNT) + level = LOGLEVEL_TRACE; + + HANDLE hConsole = GetStdHandle((level <= LOGLEVEL_WARNING) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE); + if (hConsole != INVALID_HANDLE_VALUE) + { + static const WORD levelColors[LOGLEVEL_COUNT] = { + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, // NONE + FOREGROUND_RED | FOREGROUND_INTENSITY, // ERROR + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, // WARNING + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY, // PERF + FOREGROUND_GREEN | FOREGROUND_INTENSITY, // SUCCESS + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY, // INFO + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, // DEV + FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY, // PROFILE + FOREGROUND_GREEN, // DEBUG + FOREGROUND_BLUE, // TRACE + }; + + CONSOLE_SCREEN_BUFFER_INFO oldConsoleScreenBufferInfo; + GetConsoleScreenBufferInfo(hConsole, &oldConsoleScreenBufferInfo); + SetConsoleTextAttribute(hConsole, levelColors[level]); + + // write message in the formatted way + FormatLogMessageForDisplay( + channelName, functionName, level, message, + [](const char* text, void* hConsole) { + DWORD written; + WriteConsoleA(static_cast(hConsole), text, static_cast(std::strlen(text)), &written, nullptr); + }, + (void*)hConsole); + + // write newline + DWORD written; + WriteConsoleA(hConsole, "\r\n", 2, &written, nullptr); + + // restore color + SetConsoleTextAttribute(hConsole, oldConsoleScreenBufferInfo.wAttributes); + } +} + +static void DebugOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, LOGLEVEL level, + const char* message) +{ + if (!s_debugOutputEnabled || level > s_debugOutputLevelFilter || s_debugOutputChannelFilter.Find(channelName) >= 0) + return; + + FormatLogMessageForDisplay(channelName, functionName, level, message, + [](const char* text, void*) { OutputDebugStringA(text); }, nullptr); + + OutputDebugStringA("\n"); +} + +#elif defined(__ANDROID__) + +static void ConsoleOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, + LOGLEVEL level, const char* message) +{ +} + +static void DebugOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, LOGLEVEL level, + const char* message) +{ + if (!s_debugOutputEnabled || level > s_debugOutputLevelFilter || s_debugOutputChannelFilter.Find(functionName) >= 0) + return; + + static const int logPriority[LOGLEVEL_COUNT] = { + ANDROID_LOG_INFO, // NONE + ANDROID_LOG_ERROR, // ERROR + ANDROID_LOG_WARN, // WARNING + ANDROID_LOG_INFO, // PERF + ANDROID_LOG_INFO, // SUCCESS + ANDROID_LOG_INFO, // INFO + ANDROID_LOG_DEBUG, // DEV + ANDROID_LOG_DEBUG, // PROFILE + ANDROID_LOG_DEBUG, // DEBUG + ANDROID_LOG_DEBUG, // TRACE + }; + + __android_log_write(logPriority[level], channelName, message); +} + +#else + +static void ConsoleOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, + LOGLEVEL level, const char* message) +{ + if (!s_consoleOutputEnabled || level > s_consoleOutputLevelFilter || + s_consoleOutputChannelFilter.Find(channelName) >= 0) + return; + + static const char* colorCodes[LOGLEVEL_COUNT] = { + "\033[0m", // NONE + "\033[1;31m", // ERROR + "\033[1;33m", // WARNING + "\033[1;35m", // PERF + "\033[1;32m", // SUCCESS + "\033[1;37m", // INFO + "\033[0;37m", // DEV + "\033[1;36m", // PROFILE + "\033[0;32m", // DEBUG + "\033[0;34m", // TRACE + }; + + int outputFd = (level <= LOGLEVEL_WARNING) ? STDERR_FILENO : STDOUT_FILENO; + + write(outputFd, colorCodes[level], std::strlen(colorCodes[level])); + + Log::FormatLogMessageForDisplay( + channelName, functionName, level, message, + [](const char* text, void* outputFd) { write((int)(intptr_t)outputFd, text, std::strlen(text)); }, + (void*)(intptr_t)outputFd); + + write(outputFd, colorCodes[0], std::strlen(colorCodes[0])); + write(outputFd, "\n", 1); +} + +static void DebugOutputLogCallback(void* pUserParam, const char* channelName, const char* functionName, LOGLEVEL level, + const char* message) +{ +} + +#endif + +void SetConsoleOutputParams(bool Enabled, const char* ChannelFilter, LOGLEVEL LevelFilter) +{ + if (s_consoleOutputEnabled != Enabled) + { + s_consoleOutputEnabled = Enabled; + if (Enabled) + RegisterCallback(ConsoleOutputLogCallback, NULL); + else + UnregisterCallback(ConsoleOutputLogCallback, NULL); + +#if defined(WIN32) + // On windows, no console is allocated by default on a windows based application + static bool consoleWasAllocated = false; + if (Enabled) + { + if (GetConsoleWindow() == NULL) + { + DebugAssert(!consoleWasAllocated); + consoleWasAllocated = true; + AllocConsole(); + + std::FILE* fp; + freopen_s(&fp, "CONIN$", "r", stdin); + freopen_s(&fp, "CONOUT$", "w", stdout); + freopen_s(&fp, "CONOUT$", "w", stderr); + } + } + else + { + if (consoleWasAllocated) + { + FreeConsole(); + consoleWasAllocated = false; + } + } +#endif + } + + s_consoleOutputChannelFilter = (ChannelFilter != NULL) ? ChannelFilter : ""; + s_consoleOutputLevelFilter = LevelFilter; +} + +void SetDebugOutputParams(bool enabled, const char* channelFilter /* = nullptr */, + LOGLEVEL levelFilter /* = LOGLEVEL_TRACE */) +{ + if (s_debugOutputEnabled != enabled) + { + s_debugOutputEnabled = enabled; + if (enabled) + RegisterCallback(DebugOutputLogCallback, nullptr); + else + UnregisterCallback(DebugOutputLogCallback, nullptr); + } + + s_debugOutputChannelFilter = (channelFilter != nullptr) ? channelFilter : ""; + s_debugOutputLevelFilter = levelFilter; +} + +void SetFilterLevel(LOGLEVEL level) +{ + DebugAssert(level < LOGLEVEL_COUNT); + s_filter_level = level; +} + +void Write(const char* channelName, const char* functionName, LOGLEVEL level, const char* message) +{ + if (level > s_filter_level) + return; + + ExecuteCallbacks(channelName, functionName, level, message); +} + +void Writef(const char* channelName, const char* functionName, LOGLEVEL level, const char* format, ...) +{ + if (level > s_filter_level) + return; + + va_list ap; + va_start(ap, format); + Writev(channelName, functionName, level, format, ap); + va_end(ap); +} + +void Writev(const char* channelName, const char* functionName, LOGLEVEL level, const char* format, va_list ap) +{ + if (level > s_filter_level) + return; + + va_list apCopy; + va_copy(apCopy, ap); + +#ifdef WIN32 + u32 requiredSize = static_cast(_vscprintf(format, ap)); +#else + u32 requiredSize = std::vsnprintf(nullptr, 0, format, ap); +#endif + if (requiredSize < 256) + { + char buffer[256]; + std::vsnprintf(buffer, countof(buffer), format, ap); + ExecuteCallbacks(channelName, functionName, level, buffer); + } + else + { + char* buffer = new char[requiredSize + 1]; + std::vsnprintf(buffer, requiredSize + 1, format, ap); + ExecuteCallbacks(channelName, functionName, level, buffer); + delete[] buffer; + } +} + +} // namespace Log \ No newline at end of file diff --git a/src/common/log.h b/src/common/log.h new file mode 100644 index 000000000..a4c561d1c --- /dev/null +++ b/src/common/log.h @@ -0,0 +1,92 @@ +#pragma once +#include "types.h" +#include +#include + +enum LOGLEVEL +{ + LOGLEVEL_NONE = 0, // Silences all log traffic + LOGLEVEL_ERROR = 1, // "ErrorPrint" + LOGLEVEL_WARNING = 2, // "WarningPrint" + LOGLEVEL_PERF = 3, // "PerfPrint" + LOGLEVEL_SUCCESS = 4, // "SuccessPrint" + LOGLEVEL_INFO = 5, // "InfoPrint" + LOGLEVEL_DEV = 6, // "DevPrint" + LOGLEVEL_PROFILE = 7, // "ProfilePrint" + LOGLEVEL_DEBUG = 8, // "DebugPrint" + LOGLEVEL_TRACE = 9, // "TracePrint" + LOGLEVEL_COUNT = 10 +}; + +namespace Log { +// log message callback type +using CallbackFunctionType = void (*)(void* pUserParam, const char* channelName, const char* functionName, + LOGLEVEL level, const char* message); + +// registers a log callback +void RegisterCallback(CallbackFunctionType callbackFunction, void* pUserParam); + +// unregisters a log callback +void UnregisterCallback(CallbackFunctionType callbackFunction, void* pUserParam); + +// adds a standard console output +void SetConsoleOutputParams(bool enabled, const char* channelFilter = nullptr, LOGLEVEL levelFilter = LOGLEVEL_TRACE); + +// adds a debug console output [win32/android only] +void SetDebugOutputParams(bool enabled, const char* channelFilter = nullptr, LOGLEVEL levelFilter = LOGLEVEL_TRACE); + +// Sets global filtering level, messages below this level won't be sent to any of the logging sinks. +void SetFilterLevel(LOGLEVEL level); + +// writes a message to the log +void Write(const char* channelName, const char* functionName, LOGLEVEL level, const char* message); +void Writef(const char* channelName, const char* functionName, LOGLEVEL level, const char* format, ...); +void Writev(const char* channelName, const char* functionName, LOGLEVEL level, const char* format, va_list ap); +} // namespace Log + +#ifdef Y_BUILD_CONFIG_SHIPPING +#define LOG_MESSAGE_FUNCTION_NAME "" +#else +#define LOG_MESSAGE_FUNCTION_NAME __FUNCTION__ +#endif + +// log wrappers +#define Log_SetChannel(ChannelName) static const char* ___LogChannel___ = #ChannelName; +#define Log_ErrorPrint(msg) Log::Write(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_ERROR, msg) +#define Log_ErrorPrintf(...) Log::Writef(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_ERROR, __VA_ARGS__) +#define Log_WarningPrint(msg) Log::Write(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_WARNING, msg) +#define Log_WarningPrintf(...) Log::Writef(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_WARNING, __VA_ARGS__) +#define Log_SuccessPrint(msg) Log::Write(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_SUCCESS, msg) +#define Log_SuccessPrintf(...) Log::Writef(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_SUCCESS, __VA_ARGS__) +#define Log_InfoPrint(msg) Log::Write(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_INFO, msg) +#define Log_InfoPrintf(...) Log::Writef(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_INFO, __VA_ARGS__) +#define Log_PerfPrint(msg) Log::Write(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_PERF, msg) +#define Log_PerfPrintf(...) Log::Writef(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_PERF, __VA_ARGS__) +#define Log_DevPrint(msg) Log::Write(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_DEV, msg) +#define Log_DevPrintf(...) Log::Writef(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_DEV, __VA_ARGS__) +#define Log_ProfilePrint(msg) Log::Write(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_PROFILE, msg) +#define Log_ProfilePrintf(...) Log::Writef(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_PROFILE, __VA_ARGS__) + +#ifdef _DEBUG +#define Log_DebugPrint(msg) Log::Write(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_DEBUG, msg) +#define Log_DebugPrintf(...) Log::Writef(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_DEBUG, __VA_ARGS__) +#define Log_TracePrint(msg) Log::Write(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_TRACE, msg) +#define Log_TracePrintf(...) Log::Writef(___LogChannel___, LOG_MESSAGE_FUNCTION_NAME, LOGLEVEL_TRACE, __VA_ARGS__) +#else +#define Log_DebugPrint(msg) \ + do \ + { \ + } while (0) +#define Log_DebugPrintf(...) \ + do \ + { \ + } while (0) +#define Log_TracePrint(msg) \ + do \ + { \ + } while (0) +#define Log_TracePrintf(...) \ + do \ + { \ + } while (0) +#endif diff --git a/src/common/md5_digest.cpp b/src/common/md5_digest.cpp new file mode 100644 index 000000000..4a2aea540 --- /dev/null +++ b/src/common/md5_digest.cpp @@ -0,0 +1,226 @@ +#include "md5_digest.h" + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char* buf, unsigned longs) +{ + u32 t; + do + { + t = (u32)((unsigned)buf[3] << 8 | buf[2]) << 16 | ((unsigned)buf[1] << 8 | buf[0]); + *(u32*)buf = t; + buf += 4; + } while (--longs); +} +#endif + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(u32 buf[4], u32 in[16]) +{ + // register u32 a, b, c, d; + u32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +MD5Digest::MD5Digest() +{ + Reset(); +} + +void MD5Digest::Reset() +{ + this->buf[0] = 0x67452301; + this->buf[1] = 0xefcdab89; + this->buf[2] = 0x98badcfe; + this->buf[3] = 0x10325476; + + this->bits[0] = 0; + this->bits[1] = 0; +} + +void MD5Digest::Update(const void* pData, u32 cbData) +{ + u32 t; + const u8* pByteData = reinterpret_cast(pData); + + /* Update bitcount */ + + t = this->bits[0]; + if ((this->bits[0] = t + ((u32)cbData << 3)) < t) + this->bits[1]++; /* Carry from low to high */ + this->bits[1] += cbData >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) + { + u8* p = (u8*)this->in + t; + + t = 64 - t; + if (cbData < t) + { + std::memcpy(p, pByteData, cbData); + return; + } + std::memcpy(p, pByteData, t); + byteReverse(this->in, 16); + MD5Transform(this->buf, (u32*)this->in); + pByteData += t; + cbData -= t; + } + /* Process data in 64-byte chunks */ + + while (cbData >= 64) + { + std::memcpy(this->in, pByteData, 64); + byteReverse(this->in, 16); + MD5Transform(this->buf, (u32*)this->in); + pByteData += 64; + cbData -= 64; + } + + /* Handle any remaining bytes of data. */ + + std::memcpy(this->in, pByteData, cbData); +} + +void MD5Digest::Final(u8 Digest[16]) +{ + u32 count; + u8* p; + + /* Compute number of bytes mod 64 */ + count = (this->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = this->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) + { + /* Two lots of padding: Pad the first block to 64 bytes */ + std::memset(p, 0, count); + byteReverse(this->in, 16); + MD5Transform(this->buf, (u32*)this->in); + + /* Now fill the next block with 56 bytes */ + std::memset(this->in, 0, 56); + } + else + { + /* Pad block to 56 bytes */ + std::memset(p, 0, count - 8); + } + byteReverse(this->in, 14); + + /* Append length in bits and transform */ + ((u32*)this->in)[14] = this->bits[0]; + ((u32*)this->in)[15] = this->bits[1]; + + MD5Transform(this->buf, (u32*)this->in); + byteReverse((unsigned char*)this->buf, 4); + std::memcpy(Digest, this->buf, 16); +} diff --git a/src/common/md5_digest.h b/src/common/md5_digest.h new file mode 100644 index 000000000..3ff5a473f --- /dev/null +++ b/src/common/md5_digest.h @@ -0,0 +1,20 @@ +#pragma once +#include "types.h" + +// based heavily on this implementation: +// http://www.fourmilab.ch/md5/ + +class MD5Digest +{ +public: + MD5Digest(); + + void Update(const void* pData, u32 cbData); + void Final(u8 Digest[16]); + void Reset(); + +private: + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; diff --git a/src/common/rectangle.h b/src/common/rectangle.h index 01c99ea2d..0198fc81c 100644 --- a/src/common/rectangle.h +++ b/src/common/rectangle.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include diff --git a/src/common/state_wrapper.cpp b/src/common/state_wrapper.cpp index 1d7060f81..34e5d6cbc 100644 --- a/src/common/state_wrapper.cpp +++ b/src/common/state_wrapper.cpp @@ -1,6 +1,6 @@ #include "state_wrapper.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/String.h" +#include "log.h" +#include "string.h" #include #include Log_SetChannel(StateWrapper); diff --git a/src/common/state_wrapper.h b/src/common/state_wrapper.h index b4964e0cd..e6aeb0d39 100644 --- a/src/common/state_wrapper.h +++ b/src/common/state_wrapper.h @@ -1,5 +1,5 @@ #pragma once -#include "YBaseLib/ByteStream.h" +#include "byte_stream.h" #include "fifo_queue.h" #include "heap_array.h" #include "types.h" diff --git a/src/common/string.cpp b/src/common/string.cpp new file mode 100644 index 000000000..2b9682585 --- /dev/null +++ b/src/common/string.cpp @@ -0,0 +1,1039 @@ +#include "string.h" +#include "assert.h" +#include +#include +#include +#include + +#ifdef _MSC_VER +#define CASE_COMPARE _stricmp +#define CASE_N_COMPARE _strnicmp +#else +#define CASE_COMPARE strcasecmp +#define CASE_N_COMPARE strncasecmp +#endif + +// globals +const String::StringData String::s_EmptyStringData = {const_cast(""), 0, 1, -1, true}; +const String EmptyString; + +// helper functions +static String::StringData* StringDataAllocate(u32 allocSize) +{ + DebugAssert(allocSize > 0); + + String::StringData* pStringData = + reinterpret_cast(std::malloc(sizeof(String::StringData) + allocSize)); + pStringData->pBuffer = reinterpret_cast(pStringData + 1); + pStringData->StringLength = 0; + pStringData->BufferSize = allocSize; + pStringData->ReadOnly = false; + pStringData->ReferenceCount = 1; + + // if in debug build, set all to zero, otherwise only the first to zero. +#ifdef _DEBUG + std::memset(pStringData->pBuffer, 0, allocSize); +#else + pStringData->pBuffer[0] = 0; + if (allocSize > 1) + pStringData->pBuffer[allocSize - 1] = 0; +#endif + + return pStringData; +} + +static inline void StringDataAddRef(String::StringData* pStringData) +{ + DebugAssert(pStringData->ReferenceCount > 0); + ++pStringData->ReferenceCount; +} + +static inline void StringDataRelease(String::StringData* pStringData) +{ + if (pStringData->ReferenceCount == -1) + return; + + DebugAssert(pStringData->ReferenceCount > 0); + u32 newRefCount = --pStringData->ReferenceCount; + if (!newRefCount) + std::free(pStringData); +} + +static String::StringData* StringDataClone(const String::StringData* pStringData, u32 newSize, bool copyPastString) +{ + DebugAssert(newSize >= 0); + + String::StringData* pClone = StringDataAllocate(newSize); + if (pStringData->StringLength > 0) + { + u32 copyLength; + + if (copyPastString) + { + copyLength = std::min(newSize, pStringData->BufferSize); + if (copyLength > 0) + { + std::memcpy(pClone->pBuffer, pStringData->pBuffer, copyLength); + if (copyLength < pStringData->BufferSize) + pClone->pBuffer[copyLength - 1] = 0; + } + } + else + { + copyLength = std::min(newSize, pStringData->StringLength); + if (copyLength > 0) + { + std::memcpy(pClone->pBuffer, pStringData->pBuffer, copyLength); + pClone->pBuffer[copyLength] = 0; + } + } + + pClone->StringLength = copyLength; + } + + return pClone; +} + +static String::StringData* StringDataReallocate(String::StringData* pStringData, u32 newSize) +{ + DebugAssert(newSize > pStringData->StringLength); + DebugAssert(pStringData->ReferenceCount == 1); + + // perform realloc + pStringData = reinterpret_cast(std::realloc(pStringData, sizeof(String::StringData) + newSize)); + pStringData->pBuffer = reinterpret_cast(pStringData + 1); + + // zero bytes in debug +#ifdef _DEBUG + if (newSize > pStringData->BufferSize) + { + u32 bytesToZero = newSize - pStringData->BufferSize; + std::memset(pStringData->pBuffer + (newSize - bytesToZero), 0, bytesToZero); + } +#else + if (newSize > pStringData->BufferSize) + { + pStringData->pBuffer[newSize - 1] = 0; + } +#endif + + // update size + pStringData->BufferSize = newSize; + return pStringData; +} + +static bool StringDataIsSharable(const String::StringData* pStringData) +{ + return pStringData->ReferenceCount != -1; +} + +static bool StringDataIsShared(const String::StringData* pStringData) +{ + return pStringData->ReferenceCount > 1; +} + +String::String() : m_pStringData(const_cast(&s_EmptyStringData)) {} + +String::String(const String& copyString) +{ + // special case: empty strings + if (copyString.IsEmpty()) + { + m_pStringData = const_cast(&s_EmptyStringData); + } + // is the string data sharable? + else if (StringDataIsSharable(copyString.m_pStringData)) + { + m_pStringData = copyString.m_pStringData; + StringDataAddRef(m_pStringData); + } + // create a clone for ourselves + else + { + // since we're going to the effort of creating a clone, we might as well create it as the smallest size possible + m_pStringData = StringDataClone(copyString.m_pStringData, copyString.m_pStringData->StringLength + 1, false); + } +} + +String::String(const char* Text) : m_pStringData(const_cast(&s_EmptyStringData)) +{ + Assign(Text); +} + +String::String(String&& moveString) +{ + Assign(moveString); +} + +String::~String() +{ + StringDataRelease(m_pStringData); +} + +void String::EnsureOwnWritableCopy() +{ + if (StringDataIsShared(m_pStringData) || m_pStringData->ReadOnly) + { + StringData* pNewStringData = StringDataClone(m_pStringData, m_pStringData->StringLength + 1, false); + StringDataRelease(m_pStringData); + m_pStringData = pNewStringData; + } +} + +void String::EnsureRemainingSpace(u32 spaceRequired) +{ + StringData* pNewStringData; + u32 requiredReserve = m_pStringData->StringLength + spaceRequired + 1; + + if (StringDataIsShared(m_pStringData) || m_pStringData->ReadOnly) + { + pNewStringData = StringDataClone(m_pStringData, std::max(requiredReserve, m_pStringData->BufferSize), false); + StringDataRelease(m_pStringData); + m_pStringData = pNewStringData; + } + else if (m_pStringData->BufferSize < requiredReserve) + { + u32 newSize = std::max(requiredReserve, m_pStringData->BufferSize * 2); + + // if we are the only owner of the buffer, we can simply realloc it + if (m_pStringData->ReferenceCount == 1) + { + // do realloc and update pointer + m_pStringData = StringDataReallocate(m_pStringData, newSize); + } + else + { + // clone and release old + pNewStringData = StringDataClone(m_pStringData, std::max(requiredReserve, newSize), false); + StringDataRelease(m_pStringData); + m_pStringData = pNewStringData; + } + } +} + +void String::InternalAppend(const char* pString, u32 Length) +{ + EnsureRemainingSpace(Length); + + DebugAssert((Length + m_pStringData->StringLength) < m_pStringData->BufferSize); + DebugAssert(m_pStringData->ReferenceCount <= 1 && !m_pStringData->ReadOnly); + + std::memcpy(m_pStringData->pBuffer + m_pStringData->StringLength, pString, Length); + m_pStringData->StringLength += Length; + m_pStringData->pBuffer[m_pStringData->StringLength] = 0; +} + +void String::InternalPrepend(const char* pString, u32 Length) +{ + EnsureRemainingSpace(Length); + + DebugAssert((Length + m_pStringData->StringLength) < m_pStringData->BufferSize); + DebugAssert(m_pStringData->ReferenceCount <= 1 && !m_pStringData->ReadOnly); + + std::memmove(m_pStringData->pBuffer + Length, m_pStringData->pBuffer, m_pStringData->StringLength); + std::memcpy(m_pStringData->pBuffer, pString, Length); + m_pStringData->StringLength += Length; + m_pStringData->pBuffer[m_pStringData->StringLength] = 0; +} + +void String::AppendCharacter(char c) +{ + InternalAppend(&c, 1); +} + +void String::AppendString(const String& appendStr) +{ + if (appendStr.GetLength() > 0) + InternalAppend(appendStr.GetCharArray(), appendStr.GetLength()); +} + +void String::AppendString(const char* appendText) +{ + u32 textLength = static_cast(std::strlen(appendText)); + if (textLength > 0) + InternalAppend(appendText, textLength); +} + +void String::AppendString(const char* appendString, u32 Count) +{ + if (Count > 0) + InternalAppend(appendString, Count); +} + +void String::AppendSubString(const String& appendStr, s32 Offset /* = 0 */, s32 Count /* = INT_std::max */) +{ + u32 appendStrLength = appendStr.GetLength(); + + // calc real offset + u32 realOffset; + if (Offset < 0) + realOffset = (u32)std::max((s32)0, (s32)appendStrLength + Offset); + else + realOffset = std::min((u32)Offset, appendStrLength); + + // calc real count + u32 realCount; + if (Count < 0) + realCount = std::min(appendStrLength - realOffset, (u32)std::max((s32)0, (s32)appendStrLength + Count)); + else + realCount = std::min(appendStrLength - realOffset, (u32)Count); + + // should be safe + DebugAssert((realOffset + realCount) <= appendStrLength); + if (realCount > 0) + InternalAppend(appendStr.GetCharArray() + realOffset, realCount); +} + +void String::AppendSubString(const char* appendText, s32 Offset /* = 0 */, s32 Count /* = INT_std::max */) +{ + u32 appendTextLength = static_cast(std::strlen(appendText)); + + // calc real offset + u32 realOffset; + if (Offset < 0) + realOffset = (u32)std::max((s32)0, (s32)appendTextLength + Offset); + else + realOffset = std::min((u32)Offset, appendTextLength); + + // calc real count + u32 realCount; + if (Count < 0) + realCount = std::min(appendTextLength - realOffset, (u32)std::max((s32)0, (s32)appendTextLength + Count)); + else + realCount = std::min(appendTextLength - realOffset, (u32)Count); + + // should be safe + DebugAssert((realOffset + realCount) <= appendTextLength); + if (realCount > 0) + InternalAppend(appendText + realOffset, realCount); +} + +void String::AppendFormattedString(const char* FormatString, ...) +{ + va_list ap; + va_start(ap, FormatString); + AppendFormattedStringVA(FormatString, ap); + va_end(ap); +} + +void String::AppendFormattedStringVA(const char* FormatString, va_list ArgPtr) +{ + // We have a 1KB byte buffer on the stack here. If this is too little, we'll grow it via the heap, + // but 1KB should be enough for most strings. + char stackBuffer[1024]; + char* pHeapBuffer = NULL; + char* pBuffer = stackBuffer; + u32 currentBufferSize = countof(stackBuffer); + u32 charsWritten; + + for (;;) + { + int ret = std::vsnprintf(pBuffer, currentBufferSize, FormatString, ArgPtr); + if (ret < 0 || ((u32)ret >= (currentBufferSize - 1))) + { + currentBufferSize *= 2; + pBuffer = pHeapBuffer = reinterpret_cast(std::realloc(pHeapBuffer, currentBufferSize)); + continue; + } + + charsWritten = (u32)ret; + break; + } + + InternalAppend(pBuffer, charsWritten); + + if (pHeapBuffer != NULL) + std::free(pHeapBuffer); +} + +void String::PrependCharacter(char c) +{ + InternalPrepend(&c, 1); +} + +void String::PrependString(const String& appendStr) +{ + if (appendStr.GetLength() > 0) + InternalPrepend(appendStr.GetCharArray(), appendStr.GetLength()); +} + +void String::PrependString(const char* appendText) +{ + u32 textLength = static_cast(std::strlen(appendText)); + if (textLength > 0) + InternalPrepend(appendText, textLength); +} + +void String::PrependString(const char* appendString, u32 Count) +{ + if (Count > 0) + InternalPrepend(appendString, Count); +} + +void String::PrependSubString(const String& appendStr, s32 Offset /* = 0 */, s32 Count /* = INT_std::max */) +{ + u32 appendStrLength = appendStr.GetLength(); + + // calc real offset + u32 realOffset; + if (Offset < 0) + realOffset = (u32)std::max((s32)0, (s32)appendStrLength + Offset); + else + realOffset = std::min((u32)Offset, appendStrLength); + + // calc real count + u32 realCount; + if (Count < 0) + realCount = std::min(appendStrLength - realOffset, (u32)std::max((s32)0, (s32)appendStrLength + Count)); + else + realCount = std::min(appendStrLength - realOffset, (u32)Count); + + // should be safe + DebugAssert((realOffset + realCount) <= appendStrLength); + if (realCount > 0) + InternalPrepend(appendStr.GetCharArray() + realOffset, realCount); +} + +void String::PrependSubString(const char* appendText, s32 Offset /* = 0 */, s32 Count /* = INT_std::max */) +{ + u32 appendTextLength = static_cast(std::strlen(appendText)); + + // calc real offset + u32 realOffset; + if (Offset < 0) + realOffset = (u32)std::max((s32)0, (s32)appendTextLength + Offset); + else + realOffset = std::min((u32)Offset, appendTextLength); + + // calc real count + u32 realCount; + if (Count < 0) + realCount = std::min(appendTextLength - realOffset, (u32)std::max((s32)0, (s32)appendTextLength + Count)); + else + realCount = std::min(appendTextLength - realOffset, (u32)Count); + + // should be safe + DebugAssert((realOffset + realCount) <= appendTextLength); + if (realCount > 0) + InternalPrepend(appendText + realOffset, realCount); +} + +void String::PrependFormattedString(const char* FormatString, ...) +{ + va_list ap; + va_start(ap, FormatString); + PrependFormattedStringVA(FormatString, ap); + va_end(ap); +} + +void String::PrependFormattedStringVA(const char* FormatString, va_list ArgPtr) +{ + // We have a 1KB byte buffer on the stack here. If this is too little, we'll grow it via the heap, + // but 1KB should be enough for most strings. + char stackBuffer[1024]; + char* pHeapBuffer = NULL; + char* pBuffer = stackBuffer; + u32 currentBufferSize = countof(stackBuffer); + u32 charsWritten; + + for (;;) + { + int ret = std::vsnprintf(pBuffer, currentBufferSize, FormatString, ArgPtr); + if (ret < 0 || ((u32)ret >= (currentBufferSize - 1))) + { + currentBufferSize *= 2; + pBuffer = pHeapBuffer = reinterpret_cast(std::realloc(pHeapBuffer, currentBufferSize)); + continue; + } + + charsWritten = (u32)ret; + break; + } + + InternalPrepend(pBuffer, charsWritten); + + if (pHeapBuffer != NULL) + std::free(pHeapBuffer); +} + +void String::InsertString(s32 offset, const String& appendStr) +{ + InsertString(offset, appendStr, appendStr.GetLength()); +} + +void String::InsertString(s32 offset, const char* appendStr) +{ + InsertString(offset, appendStr, static_cast(std::strlen(appendStr))); +} + +void String::InsertString(s32 offset, const char* appendStr, u32 appendStrLength) +{ + if (appendStrLength == 0) + return; + + EnsureRemainingSpace(appendStrLength); + + // calc real offset + u32 realOffset; + if (offset < 0) + realOffset = (u32)std::max((s32)0, (s32)m_pStringData->StringLength + offset); + else + realOffset = std::min((u32)offset, m_pStringData->StringLength); + + // determine number of characters after offset + DebugAssert(realOffset <= m_pStringData->StringLength); + u32 charactersAfterOffset = m_pStringData->StringLength - realOffset; + if (charactersAfterOffset > 0) + std::memmove(m_pStringData->pBuffer + offset + appendStrLength, m_pStringData->pBuffer + offset, + charactersAfterOffset); + + // insert the string + std::memcpy(m_pStringData->pBuffer + realOffset, appendStr, appendStrLength); + m_pStringData->StringLength += appendStrLength; + + // ensure null termination + m_pStringData->pBuffer[m_pStringData->StringLength] = 0; +} + +void String::Format(const char* FormatString, ...) +{ + va_list ap; + va_start(ap, FormatString); + FormatVA(FormatString, ap); + va_end(ap); +} + +void String::FormatVA(const char* FormatString, va_list ArgPtr) +{ + if (GetLength() > 0) + Clear(); + + AppendFormattedStringVA(FormatString, ArgPtr); +} + +void String::Assign(const String& copyString) +{ + // special case: empty strings + if (copyString.IsEmpty()) + { + m_pStringData = const_cast(&s_EmptyStringData); + } + // is the string data sharable? + else if (StringDataIsSharable(copyString.m_pStringData)) + { + m_pStringData = copyString.m_pStringData; + StringDataAddRef(m_pStringData); + } + // create a clone for ourselves + else + { + // since we're going to the effort of creating a clone, we might as well create it as the smallest size possible + m_pStringData = StringDataClone(copyString.m_pStringData, copyString.m_pStringData->StringLength + 1, false); + } +} + +void String::Assign(const char* copyText) +{ + Clear(); + AppendString(copyText); +} + +void String::Assign(String&& moveString) +{ + Clear(); + m_pStringData = moveString.m_pStringData; + moveString.m_pStringData = const_cast(&s_EmptyStringData); +} + +void String::AssignCopy(const String& copyString) +{ + Clear(); + AppendString(copyString); +} + +void String::Swap(String& swapString) +{ + std::swap(m_pStringData, swapString.m_pStringData); +} + +bool String::Compare(const String& otherString) const +{ + return (std::strcmp(m_pStringData->pBuffer, otherString.m_pStringData->pBuffer) == 0); +} + +bool String::Compare(const char* otherText) const +{ + return (std::strcmp(m_pStringData->pBuffer, otherText) == 0); +} + +bool String::SubCompare(const String& otherString, u32 Length) const +{ + return (std::strncmp(m_pStringData->pBuffer, otherString.m_pStringData->pBuffer, Length) == 0); +} + +bool String::SubCompare(const char* otherText, u32 Length) const +{ + return (std::strncmp(m_pStringData->pBuffer, otherText, Length) == 0); +} + +bool String::CompareInsensitive(const String& otherString) const +{ + return (CASE_COMPARE(m_pStringData->pBuffer, otherString.m_pStringData->pBuffer) == 0); +} + +bool String::CompareInsensitive(const char* otherText) const +{ + return (CASE_COMPARE(m_pStringData->pBuffer, otherText) == 0); +} + +bool String::SubCompareInsensitive(const String& otherString, u32 Length) const +{ + return (CASE_N_COMPARE(m_pStringData->pBuffer, otherString.m_pStringData->pBuffer, Length) == 0); +} + +bool String::SubCompareInsensitive(const char* otherText, u32 Length) const +{ + return (CASE_N_COMPARE(m_pStringData->pBuffer, otherText, Length) == 0); +} + +int String::NumericCompare(const String& otherString) const +{ + return std::strcmp(m_pStringData->pBuffer, otherString.m_pStringData->pBuffer); +} + +int String::NumericCompare(const char* otherText) const +{ + return std::strcmp(m_pStringData->pBuffer, otherText); +} + +int String::NumericCompareInsensitive(const String& otherString) const +{ + return CASE_COMPARE(m_pStringData->pBuffer, otherString.m_pStringData->pBuffer); +} + +int String::NumericCompareInsensitive(const char* otherText) const +{ + return CASE_COMPARE(m_pStringData->pBuffer, otherText); +} + +bool String::StartsWith(const char* compareString, bool caseSensitive /*= true*/) const +{ + u32 compareStringLength = static_cast(std::strlen(compareString)); + if (compareStringLength > m_pStringData->StringLength) + return false; + + return (caseSensitive) ? (std::strncmp(compareString, m_pStringData->pBuffer, compareStringLength) == 0) : + (CASE_N_COMPARE(compareString, m_pStringData->pBuffer, compareStringLength) == 0); +} + +bool String::StartsWith(const String& compareString, bool caseSensitive /*= true*/) const +{ + u32 compareStringLength = compareString.GetLength(); + if (compareStringLength > m_pStringData->StringLength) + return false; + + return (caseSensitive) ? + (std::strncmp(compareString.m_pStringData->pBuffer, m_pStringData->pBuffer, compareStringLength) == 0) : + (CASE_N_COMPARE(compareString.m_pStringData->pBuffer, m_pStringData->pBuffer, compareStringLength) == 0); +} + +bool String::EndsWith(const char* compareString, bool caseSensitive /*= true*/) const +{ + u32 compareStringLength = static_cast(std::strlen(compareString)); + if (compareStringLength > m_pStringData->StringLength) + return false; + + u32 startOffset = m_pStringData->StringLength - compareStringLength; + return (caseSensitive) ? + (std::strncmp(compareString, m_pStringData->pBuffer + startOffset, compareStringLength) == 0) : + (CASE_N_COMPARE(compareString, m_pStringData->pBuffer + startOffset, compareStringLength) == 0); +} + +bool String::EndsWith(const String& compareString, bool caseSensitive /*= true*/) const +{ + u32 compareStringLength = compareString.GetLength(); + if (compareStringLength > m_pStringData->StringLength) + return false; + + u32 startOffset = m_pStringData->StringLength - compareStringLength; + return (caseSensitive) ? (std::strncmp(compareString.m_pStringData->pBuffer, m_pStringData->pBuffer + startOffset, + compareStringLength) == 0) : + (CASE_N_COMPARE(compareString.m_pStringData->pBuffer, m_pStringData->pBuffer + startOffset, + compareStringLength) == 0); +} + +void String::Clear() +{ + if (m_pStringData == &s_EmptyStringData) + return; + + // Do we have a shared buffer? If so, cancel it and allocate a new one when we need to. + // Otherwise, clear the current buffer. + if (StringDataIsShared(m_pStringData) || m_pStringData->ReadOnly) + { + // replace with empty string data + Obliterate(); + } + else + { + // in debug, zero whole string, in release, zero only the first character +#if _DEBUG + std::memset(m_pStringData->pBuffer, 0, m_pStringData->BufferSize); +#else + m_pStringData->pBuffer[0] = '\0'; +#endif + m_pStringData->StringLength = 0; + } +} + +void String::Obliterate() +{ + if (m_pStringData == &s_EmptyStringData) + return; + + // Force a release of the current buffer. + StringDataRelease(m_pStringData); + m_pStringData = const_cast(&s_EmptyStringData); +} + +s32 String::Find(char c, u32 Offset /* = 0*/) const +{ + DebugAssert(Offset <= m_pStringData->StringLength); + char* pAt = std::strchr(m_pStringData->pBuffer + Offset, c); + return (pAt == NULL) ? -1 : s32(pAt - m_pStringData->pBuffer); +} + +s32 String::RFind(char c, u32 Offset /* = 0*/) const +{ + DebugAssert(Offset <= m_pStringData->StringLength); + char* pAt = std::strrchr(m_pStringData->pBuffer + Offset, c); + return (pAt == NULL) ? -1 : s32(pAt - m_pStringData->pBuffer); +} + +s32 String::Find(const char* str, u32 Offset /* = 0 */) const +{ + DebugAssert(Offset <= m_pStringData->StringLength); + char* pAt = std::strstr(m_pStringData->pBuffer + Offset, str); + return (pAt == NULL) ? -1 : s32(pAt - m_pStringData->pBuffer); +} + +void String::Reserve(u32 newReserve, bool Force /* = false */) +{ + DebugAssert(!Force || newReserve >= m_pStringData->StringLength); + + u32 newSize = (Force) ? newReserve + 1 : std::max(newReserve + 1, m_pStringData->BufferSize); + StringData* pNewStringData; + + if (StringDataIsShared(m_pStringData) || m_pStringData->ReadOnly) + { + pNewStringData = StringDataClone(m_pStringData, newSize, false); + StringDataRelease(m_pStringData); + m_pStringData = pNewStringData; + } + else + { + // skip if smaller, and not forced + if (newSize <= m_pStringData->BufferSize && !Force) + return; + + // if we are the only owner of the buffer, we can simply realloc it + if (m_pStringData->ReferenceCount == 1) + { + // do realloc and update pointer + m_pStringData = StringDataReallocate(m_pStringData, newSize); + } + else + { + // clone and release old + pNewStringData = StringDataClone(m_pStringData, newSize, false); + StringDataRelease(m_pStringData); + m_pStringData = pNewStringData; + } + } +} + +void String::Resize(u32 newSize, char fillerCharacter /* = ' ' */, bool skrinkIfSmaller /* = false */) +{ + StringData* pNewStringData; + + // if going larger, or we don't own the buffer, realloc + if (StringDataIsShared(m_pStringData) || m_pStringData->ReadOnly || newSize >= m_pStringData->BufferSize) + { + pNewStringData = StringDataClone(m_pStringData, newSize + 1, true); + StringDataRelease(m_pStringData); + m_pStringData = pNewStringData; + + if (m_pStringData->StringLength < newSize) + { + std::memset(m_pStringData->pBuffer + m_pStringData->StringLength, fillerCharacter, + m_pStringData->BufferSize - m_pStringData->StringLength - 1); + } + + m_pStringData->StringLength = newSize; + } + else + { + // owns the buffer, and going smaller + DebugAssert(newSize < m_pStringData->BufferSize); + + // update length and terminator +#if _DEBUG + std::memset(m_pStringData->pBuffer + newSize, 0, m_pStringData->BufferSize - newSize); +#else + m_pStringData->pBuffer[newSize] = 0; +#endif + m_pStringData->StringLength = newSize; + + // shrink if requested + if (skrinkIfSmaller) + Shrink(false); + } +} + +void String::UpdateSize() +{ + EnsureOwnWritableCopy(); + m_pStringData->StringLength = static_cast(std::strlen(m_pStringData->pBuffer)); +} + +void String::Shrink(bool Force /* = false */) +{ + // only shrink of we own the buffer, or forced + if (Force || m_pStringData->ReferenceCount == 1) + Reserve(m_pStringData->StringLength); +} + +String String::SubString(s32 Offset, s32 Count /* = -1 */) const +{ + String returnStr; + returnStr.AppendSubString(*this, Offset, Count); + return returnStr; +} + +void String::Erase(s32 Offset, s32 Count /* = INT_std::max */) +{ + u32 currentLength = m_pStringData->StringLength; + + // calc real offset + u32 realOffset; + if (Offset < 0) + realOffset = (u32)std::max((s32)0, (s32)currentLength + Offset); + else + realOffset = std::min((u32)Offset, currentLength); + + // calc real count + u32 realCount; + if (Count < 0) + realCount = std::min(currentLength - realOffset, (u32)std::max((s32)0, (s32)currentLength + Count)); + else + realCount = std::min(currentLength - realOffset, (u32)Count); + + // Fastpath: offset == 0, count < 0, wipe whole string. + if (realOffset == 0 && realCount == currentLength) + { + Clear(); + return; + } + + // Fastpath: offset >= 0, count < 0, wipe everything after offset + count + if ((realOffset + realCount) == m_pStringData->StringLength) + { + m_pStringData->StringLength -= realCount; +#ifdef _DEBUG + std::memset(m_pStringData->pBuffer + m_pStringData->StringLength, 0, + m_pStringData->BufferSize - m_pStringData->StringLength); +#else + m_pStringData->pBuffer[m_pStringData->StringLength] = 0; +#endif + } + // Slowpath: offset >= 0, count < length + else + { + u32 afterEraseBlock = m_pStringData->StringLength - realOffset - realCount; + DebugAssert(afterEraseBlock > 0); + + std::memmove(m_pStringData->pBuffer + Offset, m_pStringData->pBuffer + realOffset + realCount, afterEraseBlock); + m_pStringData->StringLength = m_pStringData->StringLength - realCount; + +#ifdef _DEBUG + std::memset(m_pStringData->pBuffer + m_pStringData->StringLength, 0, + m_pStringData->BufferSize - m_pStringData->StringLength); +#else + m_pStringData->pBuffer[m_pStringData->StringLength] = 0; +#endif + } +} + +u32 String::Replace(char searchCharacter, char replaceCharacter) +{ + u32 nReplacements = 0; + char* pCurrent = std::strchr(m_pStringData->pBuffer, searchCharacter); + while (pCurrent != NULL) + { + if ((nReplacements++) == 0) + EnsureOwnWritableCopy(); + + *pCurrent = replaceCharacter; + pCurrent = std::strchr(pCurrent + 1, searchCharacter); + } + + return nReplacements; +} + +u32 String::Replace(const char* searchString, const char* replaceString) +{ + u32 nReplacements = 0; + u32 searchStringLength = static_cast(std::strlen(searchString)); + +#if 0 + u32 replaceStringLength = static_cast(std::strlen(replaceString)); + s32 lengthDifference = (s32)replaceStringLength - (s32)searchStringLength; + + char *pCurrent = std::strchr(m_pStringData->pBuffer, searchString); + while (pCurrent != NULL) + { + if ((nReplacements++) == 0) + { + if (lengthDifference > 0) + EnsureRemainingSpace(lengthDifference); + else + EnsureOwnCopy(); + } + else if (lengthDifference > 0) + EnsureRemainingSpace(lengthDifference); + } +#endif + + // TODO: Fastpath if strlen(searchString) == strlen(replaceString) + + String tempString; + char* pStart = m_pStringData->pBuffer; + char* pCurrent = std::strstr(pStart, searchString); + char* pLast = NULL; + while (pCurrent != NULL) + { + if ((nReplacements++) == 0) + tempString.Reserve(m_pStringData->StringLength); + + tempString.AppendSubString(*this, s32(pStart - pCurrent), s32(pStart - pCurrent - 1)); + tempString.AppendString(replaceString); + pLast = pCurrent + searchStringLength; + nReplacements++; + + pCurrent = std::strstr(pLast, searchString); + } + + if (pLast != NULL) + tempString.AppendSubString(*this, s32(pLast - pStart)); + + if (nReplacements) + Swap(tempString); + + return nReplacements; +} + +void String::ToLower() +{ + // fixme for utf8 + EnsureOwnWritableCopy(); + for (u32 i = 0; i < m_pStringData->StringLength; i++) + { + if (std::isprint(m_pStringData->pBuffer[i])) + m_pStringData->pBuffer[i] = static_cast(std::tolower(m_pStringData->pBuffer[i])); + } +} + +void String::ToUpper() +{ + // fixme for utf8 + EnsureOwnWritableCopy(); + for (u32 i = 0; i < m_pStringData->StringLength; i++) + { + if (std::isprint(m_pStringData->pBuffer[i])) + m_pStringData->pBuffer[i] = static_cast(std::toupper(m_pStringData->pBuffer[i])); + } +} + +void String::LStrip(const char* szStripCharacters /* = " " */) +{ + u32 stripCharactersLen = static_cast(std::strlen(szStripCharacters)); + u32 removeCount = 0; + u32 i = 0; + u32 j; + + // for each character in str + for (i = 0; i < m_pStringData->StringLength; i++) + { + char ch = m_pStringData->pBuffer[i]; + + // if it exists in szStripCharacters + for (j = 0; j < stripCharactersLen; j++) + { + if (ch == szStripCharacters[j]) + { + removeCount++; + goto OUTER; + } + } + + // not found, exit + break; + OUTER: + continue; + } + + // chars to remove? + if (removeCount > 0) + Erase(0, removeCount); +} + +void String::RStrip(const char* szStripCharacters /* = " " */) +{ + u32 stripCharactersLen = static_cast(std::strlen(szStripCharacters)); + u32 removeCount = 0; + u32 i = 0; + u32 j; + + // for each character in str + for (i = 0; i < m_pStringData->StringLength; i++) + { + char ch = m_pStringData->pBuffer[m_pStringData->StringLength - i - 1]; + + // if it exists in szStripCharacters + for (j = 0; j < stripCharactersLen; j++) + { + if (ch == szStripCharacters[j]) + { + removeCount++; + goto OUTER; + } + } + + // not found, exit + break; + OUTER: + continue; + } + + // chars to remove? + if (removeCount > 0) + Erase(m_pStringData->StringLength - removeCount); +} + +void String::Strip(const char* szStripCharacters /* = " " */) +{ + RStrip(szStripCharacters); + LStrip(szStripCharacters); +} + +String String::FromFormat(const char* FormatString, ...) +{ + String returnStr; + va_list ap; + + va_start(ap, FormatString); + returnStr.FormatVA(FormatString, ap); + va_end(ap); + + return returnStr; +} diff --git a/src/common/string.h b/src/common/string.h new file mode 100644 index 000000000..4543f9c49 --- /dev/null +++ b/src/common/string.h @@ -0,0 +1,373 @@ +#pragma once +#include "types.h" +#include +#include +#include +#include + +// +// String +// Implements a UTF-8 string container with copy-on-write behavior. +// The data class is not currently threadsafe (creating a mutex on each container would be overkill), +// so locking is still required when multiple threads are involved. +// +class String +{ +public: + // Internal StringData class. + struct StringData + { + // Pointer to memory where the string is located + char* pBuffer; + + // Length of the string located in pBuffer (in characters) + u32 StringLength; + + // Size of the buffer pointed to by pBuffer + u32 BufferSize; + + // Reference count of this data object. If set to -1, + // it is considered noncopyable and any copies of the string + // will always create their own copy. + std::atomic ReferenceCount; + + // Whether the memory pointed to by pBuffer is writable. + bool ReadOnly; + }; + +public: + // Creates an empty string. + String(); + + // Creates a string containing the specified text. + // Note that this will incur a heap allocation, even if Text is on the stack. + // For strings that do not allocate any space on the heap, see StaticString. + String(const char* Text); + + // Creates a string using the same buffer as another string (copy-on-write). + String(const String& copyString); + + // Move constructor, take reference from other string. + String(String&& moveString); + + // Destructor. Child classes may not have any destructors, as this is not virtual. + ~String(); + + // manual assignment + void Assign(const String& copyString); + void Assign(const char* copyText); + void Assign(String&& moveString); + + // assignment but ensures that we have our own copy. + void AssignCopy(const String& copyString); + + // Ensures that the string has its own unique copy of the data. + void EnsureOwnWritableCopy(); + + // Ensures that we have our own copy of the buffer, and spaceRequired bytes free in the buffer. + void EnsureRemainingSpace(u32 spaceRequired); + + // clears the contents of the string + void Clear(); + + // clear the contents of the string, and free any memory currently being used + void Obliterate(); + + // swaps strings + void Swap(String& swapString); + + // append a single character to this string + void AppendCharacter(char c); + + // append a string to this string + void AppendString(const String& appendStr); + void AppendString(const char* appendText); + void AppendString(const char* appendString, u32 Count); + + // append a substring of the specified string to this string + void AppendSubString(const String& appendStr, s32 Offset = 0, s32 Count = std::numeric_limits::max()); + void AppendSubString(const char* appendText, s32 Offset = 0, s32 Count = std::numeric_limits::max()); + + // append formatted string to this string + void AppendFormattedString(const char* FormatString, ...); + void AppendFormattedStringVA(const char* FormatString, va_list ArgPtr); + + // append a single character to this string + void PrependCharacter(char c); + + // append a string to this string + void PrependString(const String& appendStr); + void PrependString(const char* appendText); + void PrependString(const char* appendString, u32 Count); + + // append a substring of the specified string to this string + void PrependSubString(const String& appendStr, s32 Offset = 0, s32 Count = std::numeric_limits::max()); + void PrependSubString(const char* appendText, s32 Offset = 0, s32 Count = std::numeric_limits::max()); + + // append formatted string to this string + void PrependFormattedString(const char* FormatString, ...); + void PrependFormattedStringVA(const char* FormatString, va_list ArgPtr); + + // insert a string at the specified offset + void InsertString(s32 offset, const String& appendStr); + void InsertString(s32 offset, const char* appendStr); + void InsertString(s32 offset, const char* appendStr, u32 appendStrLength); + + // set to formatted string + void Format(const char* FormatString, ...); + void FormatVA(const char* FormatString, va_list ArgPtr); + + // compare one string to another + bool Compare(const String& otherString) const; + bool Compare(const char* otherText) const; + bool SubCompare(const String& otherString, u32 Length) const; + bool SubCompare(const char* otherText, u32 Length) const; + bool CompareInsensitive(const String& otherString) const; + bool CompareInsensitive(const char* otherText) const; + bool SubCompareInsensitive(const String& otherString, u32 Length) const; + bool SubCompareInsensitive(const char* otherText, u32 Length) const; + + // numerical compares + int NumericCompare(const String& otherString) const; + int NumericCompare(const char* otherText) const; + int NumericCompareInsensitive(const String& otherString) const; + int NumericCompareInsensitive(const char* otherText) const; + + // starts with / ends with + bool StartsWith(const char* compareString, bool caseSensitive = true) const; + bool StartsWith(const String& compareString, bool caseSensitive = true) const; + bool EndsWith(const char* compareString, bool caseSensitive = true) const; + bool EndsWith(const String& compareString, bool caseSensitive = true) const; + + // searches for a character inside a string + // rfind is the same except it starts at the end instead of the start + // returns -1 if it is not found, otherwise the offset in the string + s32 Find(char c, u32 Offset = 0) const; + s32 RFind(char c, u32 Offset = 0) const; + + // searches for a string inside a string + // rfind is the same except it starts at the end instead of the start + // returns -1 if it is not found, otherwise the offset in the string + s32 Find(const char* str, u32 Offset = 0) const; + + // alters the length of the string to be at least len bytes long + void Reserve(u32 newReserve, bool Force = false); + + // Cuts characters off the string to reduce it to len bytes long. + void Resize(u32 newSize, char fillerCharacter = ' ', bool skrinkIfSmaller = false); + + // updates the internal length counter when the string is externally modified + void UpdateSize(); + + // shrink the string to the minimum size possible + void Shrink(bool Force = false); + + // gets the size of the string + u32 GetLength() const { return m_pStringData->StringLength; } + bool IsEmpty() const { return (m_pStringData->StringLength == 0); } + + // gets the maximum number of bytes we can write to the string, currently + u32 GetBufferSize() const { return m_pStringData->BufferSize; } + u32 GetWritableBufferSize() + { + EnsureOwnWritableCopy(); + return m_pStringData->BufferSize; + } + + // creates a new string using part of this string + String SubString(s32 Offset, s32 Count = std::numeric_limits::max()) const; + + // erase count characters at offset from this string. if count is less than zero, everything past offset is erased + void Erase(s32 Offset, s32 Count = std::numeric_limits::max()); + + // replaces all instances of character c with character r in this string + // returns the number of changes + u32 Replace(char searchCharacter, char replaceCharacter); + + // replaces all instances of string s with string r in this string + // returns the number of changes + u32 Replace(const char* searchString, const char* replaceString); + + // convert string to lowercase + void ToLower(); + + // convert string to upper + void ToUpper(); + + // strip characters from start and end of the string + void LStrip(const char* szStripCharacters = " \t\r\n"); + void RStrip(const char* szStripCharacters = " \t\r\n"); + void Strip(const char* szStripCharacters = " \t\r\n"); + + // gets a constant pointer to the string + const char* GetCharArray() const { return m_pStringData->pBuffer; } + + // gets a writable char array, do not write more than reserve characters to it. + char* GetWriteableCharArray() + { + EnsureOwnWritableCopy(); + return m_pStringData->pBuffer; + } + + // creates a new string from the specified format + static String FromFormat(const char* FormatString, ...); + + // accessor operators + // const char &operator[](u32 i) const { DebugAssert(i < m_pStringData->StringLength); return + // m_pStringData->pBuffer[i]; } char &operator[](u32 i) { DebugAssert(i < m_pStringData->StringLength); return + // m_pStringData->pBuffer[i]; } + operator const char*() const { return GetCharArray(); } + operator char*() { return GetWriteableCharArray(); } + + // Will use the string data provided. + String& operator=(const String& copyString) + { + Assign(copyString); + return *this; + } + + // Allocates own buffer and copies text. + String& operator=(const char* Text) + { + Assign(Text); + return *this; + } + + // Move operator. + String& operator=(String&& moveString) + { + Assign(moveString); + return *this; + } + + // comparative operators + bool operator==(const String& compString) const { return Compare(compString); } + bool operator==(const char* compString) const { return Compare(compString); } + bool operator!=(const String& compString) const { return !Compare(compString); } + bool operator!=(const char* compString) const { return !Compare(compString); } + bool operator<(const String& compString) const { return (NumericCompare(compString) < 0); } + bool operator<(const char* compString) const { return (NumericCompare(compString) < 0); } + bool operator>(const String& compString) const { return (NumericCompare(compString) > 0); } + bool operator>(const char* compString) const { return (NumericCompare(compString) > 0); } + +protected: + // Hidden constructor for creating string child classes. + // It does not increment the reference count on the string data, therefore dangerous to be public. + String(StringData* pStringData) : m_pStringData(pStringData) {} + + // Internal append function. + void InternalPrepend(const char* pString, u32 Length); + void InternalAppend(const char* pString, u32 Length); + + // Pointer to string data. + StringData* m_pStringData; + + // Empty string data. + static const StringData s_EmptyStringData; +}; + +// static string, stored in .rodata +class StaticString : public String +{ +public: + StaticString(const char* Text) + { + m_sStringData.pBuffer = const_cast(Text); + m_sStringData.StringLength = static_cast(std::strlen(Text)); + m_sStringData.BufferSize = m_sStringData.StringLength + 1; + m_sStringData.ReadOnly = true; + m_sStringData.ReferenceCount = -1; + } + +private: + StringData m_sStringData; +}; + +// stack-allocated string +template +class StackString : public String +{ +public: + StackString() : String(&m_sStringData) { InitStackStringData(); } + + StackString(const char* Text) : String(&m_sStringData) + { + InitStackStringData(); + Assign(Text); + } + + StackString(const String& copyString) : String(&m_sStringData) + { + // force a copy by passing it a string pointer, instead of a string object + InitStackStringData(); + Assign(copyString.GetCharArray()); + } + + StackString(const StackString& copyString) : String(&m_sStringData) + { + // force a copy by passing it a string pointer, instead of a string object + InitStackStringData(); + Assign(copyString.GetCharArray()); + } + + // Override the fromstring method + static StackString FromFormat(const char* FormatString, ...) + { + va_list argPtr; + va_start(argPtr, FormatString); + + StackString returnValue; + returnValue.FormatVA(FormatString, argPtr); + + va_end(argPtr); + + return returnValue; + } + + // Will use the string data provided. + StackString& operator=(const StackString& copyString) + { + Assign(copyString.GetCharArray()); + return *this; + } + StackString& operator=(const String& copyString) + { + Assign(copyString.GetCharArray()); + return *this; + } + + // Allocates own buffer and copies text. + StackString& operator=(const char* Text) + { + Assign(Text); + return *this; + } + +private: + StringData m_sStringData; + char m_strStackBuffer[L + 1]; + + inline void InitStackStringData() + { + m_sStringData.pBuffer = m_strStackBuffer; + m_sStringData.StringLength = 0; + m_sStringData.BufferSize = countof(m_strStackBuffer); + m_sStringData.ReadOnly = false; + m_sStringData.ReferenceCount = -1; + +#ifdef _DEBUG + std::memset(m_strStackBuffer, 0, sizeof(m_strStackBuffer)); +#else + m_strStackBuffer[0] = '\0'; +#endif + } +}; + +// stack string types +typedef StackString<64> TinyString; +typedef StackString<256> SmallString; +typedef StackString<512> LargeString; +typedef StackString<512> PathString; + +// empty string global +extern const String EmptyString; diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp new file mode 100644 index 000000000..401007007 --- /dev/null +++ b/src/common/string_util.cpp @@ -0,0 +1,140 @@ +#include "string_util.h" +#include + +namespace StringUtil { + +std::string StdStringFromFormat(const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + std::string ret = StdStringFromFormatV(format, ap); + va_end(ap); + return ret; +} + +std::string StdStringFromFormatV(const char* format, std::va_list ap) +{ +#ifdef WIN32 + int len = _vscprintf(format, ap); +#else + int len = std::vsnprintf(nullptr, 0, format, ap); +#endif + + std::string ret; + ret.resize(len); + std::vsnprintf(ret.data(), ret.size() + 1, format, ap); + return ret; +} + +bool WildcardMatch(const char* subject, const char* mask, bool case_sensitive /*= true*/) +{ + if (case_sensitive) + { + const char* cp = nullptr; + const char* mp = nullptr; + + while ((*subject) && (*mask != '*')) + { + if ((*mask != '?') && (std::tolower(*mask) != std::tolower(*subject))) + return false; + + mask++; + subject++; + } + + while (*subject) + { + if (*mask == '*') + { + if (*++mask == 0) + return true; + + mp = mask; + cp = subject + 1; + } + else + { + if ((*mask == '?') || (std::tolower(*mask) == std::tolower(*subject))) + { + mask++; + subject++; + } + else + { + mask = mp; + subject = cp++; + } + } + } + + while (*mask == '*') + { + mask++; + } + + return *mask == 0; + } + else + { + const char* cp = nullptr; + const char* mp = nullptr; + + while ((*subject) && (*mask != '*')) + { + if ((*mask != *subject) && (*mask != '?')) + return false; + + mask++; + subject++; + } + + while (*subject) + { + if (*mask == '*') + { + if (*++mask == 0) + return true; + + mp = mask; + cp = subject + 1; + } + else + { + if ((*mask == *subject) || (*mask == '?')) + { + mask++; + subject++; + } + else + { + mask = mp; + subject = cp++; + } + } + } + + while (*mask == '*') + { + mask++; + } + + return *mask == 0; + } +} + +std::size_t Strlcpy(char* dst, const char* src, std::size_t size) +{ + std::size_t len = std::strlen(src); + if (len < size) + { + std::memcpy(dst, src, len + 1); + } + else + { + std::memcpy(dst, src, size - 1); + dst[size] = '\0'; + } + return len; +} + +} // namespace StringUtil \ No newline at end of file diff --git a/src/common/string_util.h b/src/common/string_util.h new file mode 100644 index 000000000..a0100173e --- /dev/null +++ b/src/common/string_util.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include +#include +#include + +namespace StringUtil { + +/// Constructs a std::string from a format string. +std::string StdStringFromFormat(const char* format, ...); +std::string StdStringFromFormatV(const char* format, std::va_list ap); + +/// Checks if a wildcard matches a search string. +bool WildcardMatch(const char* subject, const char* mask, bool case_sensitive = true); + +/// Safe version of strlcpy. +std::size_t Strlcpy(char* dst, const char* src, std::size_t size); + +/// Platform-independent strcasecmp +inline int Strcasecmp(const char* s1, const char* s2) +{ +#ifdef _MSC_VER + return _stricmp(s1, s2); +#else + return strcasecmp(s1, s2); +#endif +} + +} // namespace StringUtil \ No newline at end of file diff --git a/src/common/timer.cpp b/src/common/timer.cpp new file mode 100644 index 000000000..84589eaf8 --- /dev/null +++ b/src/common/timer.cpp @@ -0,0 +1,127 @@ +#include "timer.h" + +#ifdef WIN32 +#include "windows_headers.h" +#else +#include +#include +#endif + +namespace Common { + +#ifdef WIN32 + +static double s_counter_frequency; +static bool s_counter_initialized = false; + +Timer::Value Timer::GetValue() +{ + // even if this races, it should still result in the same value.. + if (!s_counter_initialized) + { + LARGE_INTEGER Freq; + QueryPerformanceFrequency(&Freq); + s_counter_frequency = static_cast(Freq.QuadPart) / 1000000000.0; + s_counter_initialized = true; + } + + Timer::Value ReturnValue; + QueryPerformanceCounter(reinterpret_cast(&ReturnValue)); + return ReturnValue; +} + +double Timer::ConvertValueToNanoseconds(Timer::Value value) +{ + return (static_cast(value) / s_counter_frequency); +} + +double Timer::ConvertValueToMilliseconds(Timer::Value value) +{ + return ((static_cast(value) / s_counter_frequency) / 1000000.0); +} + +double Timer::ConvertValueToSeconds(Timer::Value value) +{ + return ((static_cast(value) / s_counter_frequency) / 1000000000.0); +} + +#else + +#if 1 // using clock_gettime() + +Timer::Value Timer::GetValue() +{ + struct timespec tv; + clock_gettime(CLOCK_MONOTONIC, &tv); + return ((Value)tv.tv_nsec + (Value)tv.tv_sec * 1000000000); +} + +double Timer::ConvertValueToNanoseconds(Timer::Value value) +{ + return static_cast(value); +} + +double Timer::ConvertValueToMilliseconds(Timer::Value value) +{ + return (static_cast(value) / 1000000.0); +} + +double Timer::ConvertValueToSeconds(Timer::Value value) +{ + return (static_cast(value) / 1000000000.0); +} + +#else // using gettimeofday() + +Timer::Value Timer::GetValue() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return ((Value)tv.tv_usec) + ((Value)tv.tv_sec * (Value)1000000); +} + +double Timer::ConvertValueToNanoseconds(Timer::Value value) +{ + return ((double)value * 1000.0); +} + +double Timer::ConvertValueToMilliseconds(Timer::Value value) +{ + return ((double)value / 1000.0); +} + +double Timer::ConvertValueToSeconds(Timer::Value value) +{ + return ((double)value / 1000000.0); +} + +#endif + +#endif + +Timer::Timer() +{ + Reset(); +} + +void Timer::Reset() +{ + m_tvStartValue = GetValue(); +} + +double Timer::GetTimeSeconds() const +{ + return ConvertValueToSeconds(GetValue() - m_tvStartValue); +} + +double Timer::GetTimeMilliseconds() const +{ + return ConvertValueToMilliseconds(GetValue() - m_tvStartValue); +} + +double Timer::GetTimeNanoseconds() const +{ + return ConvertValueToNanoseconds(GetValue() - m_tvStartValue); +} + +} // namespace Common \ No newline at end of file diff --git a/src/common/timer.h b/src/common/timer.h new file mode 100644 index 000000000..5d105d868 --- /dev/null +++ b/src/common/timer.h @@ -0,0 +1,29 @@ +#pragma once +#include "types.h" +#include + +namespace Common { + +class Timer +{ +public: + using Value = u64; + + Timer(); + + static Value GetValue(); + static double ConvertValueToSeconds(Value value); + static double ConvertValueToMilliseconds(Value value); + static double ConvertValueToNanoseconds(Value value); + + void Reset(); + + double GetTimeSeconds() const; + double GetTimeMilliseconds() const; + double GetTimeNanoseconds() const; + +private: + Value m_tvStartValue; +}; + +} // namespace Common \ No newline at end of file diff --git a/src/common/timestamp.cpp b/src/common/timestamp.cpp new file mode 100644 index 000000000..7a8cba19d --- /dev/null +++ b/src/common/timestamp.cpp @@ -0,0 +1,518 @@ +#include "timestamp.h" +#include +#include + +#if defined(WIN32) + +static void UnixTimeToSystemTime(time_t t, LPSYSTEMTIME pst); +static time_t SystemTimeToUnixTime(const SYSTEMTIME* pst); + +#endif + +Timestamp::Timestamp() +{ +#if defined(WIN32) + m_value.wYear = 1970; + m_value.wMonth = 1; + m_value.wDayOfWeek = 0; + m_value.wDay = 1; + m_value.wHour = 0; + m_value.wMinute = 0; + m_value.wSecond = 0; + m_value.wMilliseconds = 0; +#else + m_value.tv_sec = 0; + m_value.tv_usec = 0; +#endif +} + +Timestamp::Timestamp(const Timestamp& copy) +{ +#if defined(WIN32) + std::memcpy(&m_value, ©.m_value, sizeof(m_value)); +#else + std::memcpy(&m_value, ©.m_value, sizeof(m_value)); +#endif +} + +double Timestamp::DifferenceInSeconds(Timestamp& other) const +{ +#if defined(WIN32) + FILETIME lft, rft; + SystemTimeToFileTime(&m_value, &lft); + SystemTimeToFileTime(&other.m_value, &rft); + + u64 lval = ((u64)lft.dwHighDateTime) << 32 | (u64)lft.dwLowDateTime; + u64 rval = ((u64)rft.dwHighDateTime) << 32 | (u64)rft.dwLowDateTime; + s64 diff = ((s64)lval - (s64)rval); + return double(diff / 10000000ULL) + (double(diff % 10000000ULL) / 10000000.0); + +#else + return (double)(m_value.tv_sec - other.m_value.tv_sec) + + (((double)(m_value.tv_usec - other.m_value.tv_usec)) / 1000000.0); +#endif +} + +s64 Timestamp::DifferenceInSecondsInt(Timestamp& other) const +{ +#if defined(WIN32) + FILETIME lft, rft; + SystemTimeToFileTime(&m_value, &lft); + SystemTimeToFileTime(&other.m_value, &rft); + + u64 lval = ((u64)lft.dwHighDateTime) << 32 | (u64)lft.dwLowDateTime; + u64 rval = ((u64)rft.dwHighDateTime) << 32 | (u64)rft.dwLowDateTime; + s64 diff = ((s64)lval - (s64)rval); + return diff / 10000000ULL; + +#else + return static_cast(m_value.tv_sec - other.m_value.tv_sec); +#endif +} + +Timestamp::UnixTimestampValue Timestamp::AsUnixTimestamp() const +{ +#if defined(WIN32) + return (UnixTimestampValue)SystemTimeToUnixTime(&m_value); +#else + return (UnixTimestampValue)m_value.tv_sec; +#endif +} + +Timestamp::ExpandedTime Timestamp::AsExpandedTime() const +{ + ExpandedTime et; + +#if defined(WIN32) + et.Year = m_value.wYear; + et.Month = m_value.wMonth; + et.DayOfMonth = m_value.wDay; + et.DayOfWeek = m_value.wDayOfWeek; + et.Hour = m_value.wHour; + et.Minute = m_value.wMinute; + et.Second = m_value.wSecond; + et.Milliseconds = m_value.wMilliseconds; +#else + struct tm t; + time_t unixTime = (time_t)m_value.tv_sec; + gmtime_r(&unixTime, &t); + et.Year = t.tm_year + 1900; + et.Month = t.tm_mon + 1; + et.DayOfMonth = t.tm_mday; + et.DayOfWeek = t.tm_wday; + et.Hour = t.tm_hour; + et.Minute = t.tm_min; + et.Second = t.tm_sec; + et.Milliseconds = m_value.tv_usec / 1000; +#endif + + return et; +} + +void Timestamp::SetNow() +{ +#if defined(WIN32) + GetSystemTime(&m_value); +#else + gettimeofday(&m_value, NULL); +#endif +} + +void Timestamp::SetUnixTimestamp(UnixTimestampValue value) +{ +#if defined(WIN32) + UnixTimeToSystemTime((time_t)value, &m_value); +#else + m_value.tv_sec = (time_t)value; + m_value.tv_usec = 0; +#endif +} + +void Timestamp::SetExpandedTime(const ExpandedTime& value) +{ +#if defined(WIN32) + // bit of a hacky way to fill in the missing fields + SYSTEMTIME st; + st.wYear = (WORD)value.Year; + st.wMonth = (WORD)value.Month; + st.wDay = (WORD)value.DayOfMonth; + st.wDayOfWeek = (WORD)0; + st.wHour = (WORD)value.Hour; + st.wMinute = (WORD)value.Minute; + st.wSecond = (WORD)value.Second; + st.wMilliseconds = (WORD)value.Milliseconds; + FILETIME ft; + SystemTimeToFileTime(&st, &ft); + FileTimeToSystemTime(&ft, &m_value); +#else + struct tm t; + std::memset(&t, 0, sizeof(t)); + t.tm_sec = value.Second; + t.tm_min = value.Minute; + t.tm_hour = value.Hour; + t.tm_mday = value.DayOfMonth; + t.tm_mon = value.Month - 1; + t.tm_year = value.Year - 1900; + time_t unixTime = mktime(&t); + SetUnixTimestamp((UnixTimestampValue)unixTime); +#endif +} + +String Timestamp::ToString(const char* format) const +{ + SmallString destination; + ToString(destination, format); + return String(destination); +} + +void Timestamp::ToString(String& destination, const char* format) const +{ + time_t unixTime = (time_t)AsUnixTimestamp(); + tm localTime; + +#if defined(WIN32) + localtime_s(&localTime, &unixTime); +#else + localtime_r(&unixTime, &localTime); +#endif + + char buffer[256]; + strftime(buffer, countof(buffer) - 1, format, &localTime); + buffer[countof(buffer) - 1] = 0; + + destination.Clear(); + destination.AppendString(buffer); +} + +Timestamp Timestamp::Now() +{ + Timestamp t; + t.SetNow(); + return t; +} + +Timestamp Timestamp::FromUnixTimestamp(UnixTimestampValue value) +{ + Timestamp t; + t.SetUnixTimestamp(value); + return t; +} + +Timestamp Timestamp::FromExpandedTime(const ExpandedTime& value) +{ + Timestamp t; + t.SetExpandedTime(value); + return t; +} + +bool Timestamp::operator==(const Timestamp& other) const +{ +#if defined(WIN32) + return std::memcmp(&m_value, &other.m_value, sizeof(m_value)) == 0; +#else + return std::memcmp(&m_value, &other.m_value, sizeof(m_value)) == 0; +#endif +} + +bool Timestamp::operator!=(const Timestamp& other) const +{ +#if defined(WIN32) + return std::memcmp(&m_value, &other.m_value, sizeof(m_value)) != 0; +#else + return std::memcmp(&m_value, &other.m_value, sizeof(m_value)) != 0; +#endif +} + +bool Timestamp::operator<(const Timestamp& other) const +{ +#if defined(WIN32) + + if (m_value.wYear > other.m_value.wYear) + return false; + else if (m_value.wYear < other.m_value.wYear) + return true; + + if (m_value.wMonth > other.m_value.wMonth) + return false; + else if (m_value.wMonth < other.m_value.wMonth) + return true; + + if (m_value.wDay > other.m_value.wDay) + return false; + else if (m_value.wDay < other.m_value.wDay) + return true; + + if (m_value.wHour > other.m_value.wHour) + return false; + else if (m_value.wHour < other.m_value.wHour) + return true; + + if (m_value.wMinute > other.m_value.wMinute) + return false; + else if (m_value.wMinute < other.m_value.wMinute) + return true; + + if (m_value.wHour > other.m_value.wHour) + return false; + else if (m_value.wHour < other.m_value.wHour) + return true; + + if (m_value.wSecond > other.m_value.wSecond) + return false; + else if (m_value.wSecond < other.m_value.wSecond) + return true; + + return false; + +#else + + if (m_value.tv_sec > other.m_value.tv_sec) + return false; + else if (m_value.tv_sec < other.m_value.tv_sec) + return true; + + if (m_value.tv_usec > other.m_value.tv_usec) + return false; + else if (m_value.tv_usec < other.m_value.tv_usec) + return true; + + return false; + +#endif +} + +bool Timestamp::operator<=(const Timestamp& other) const +{ +#if defined(WIN32) + + if (m_value.wYear > other.m_value.wYear) + return false; + else if (m_value.wYear < other.m_value.wYear) + return true; + + if (m_value.wMonth > other.m_value.wMonth) + return false; + else if (m_value.wMonth < other.m_value.wMonth) + return true; + + if (m_value.wDay > other.m_value.wDay) + return false; + else if (m_value.wDay < other.m_value.wDay) + return true; + + if (m_value.wHour > other.m_value.wHour) + return false; + else if (m_value.wHour < other.m_value.wHour) + return true; + + if (m_value.wMinute > other.m_value.wMinute) + return false; + else if (m_value.wMinute < other.m_value.wMinute) + return true; + + if (m_value.wHour > other.m_value.wHour) + return false; + else if (m_value.wHour < other.m_value.wHour) + return true; + + if (m_value.wSecond > other.m_value.wSecond) + return false; + else if (m_value.wSecond <= other.m_value.wSecond) + return true; + + return false; + +#else + + if (m_value.tv_sec > other.m_value.tv_sec) + return false; + else if (m_value.tv_sec < other.m_value.tv_sec) + return true; + + if (m_value.tv_usec > other.m_value.tv_usec) + return false; + else if (m_value.tv_usec <= other.m_value.tv_usec) + return true; + + return false; + +#endif +} + +bool Timestamp::operator>(const Timestamp& other) const +{ +#if defined(WIN32) + + if (m_value.wYear < other.m_value.wYear) + return false; + else if (m_value.wYear > other.m_value.wYear) + return true; + + if (m_value.wMonth < other.m_value.wMonth) + return false; + else if (m_value.wMonth > other.m_value.wMonth) + return true; + + if (m_value.wDay < other.m_value.wDay) + return false; + else if (m_value.wDay > other.m_value.wDay) + return true; + + if (m_value.wHour < other.m_value.wHour) + return false; + else if (m_value.wHour > other.m_value.wHour) + return true; + + if (m_value.wMinute < other.m_value.wMinute) + return false; + else if (m_value.wMinute > other.m_value.wMinute) + return true; + + if (m_value.wHour < other.m_value.wHour) + return false; + else if (m_value.wHour > other.m_value.wHour) + return true; + + if (m_value.wSecond < other.m_value.wSecond) + return false; + else if (m_value.wSecond > other.m_value.wSecond) + return true; + + return false; + +#else + + if (m_value.tv_sec < other.m_value.tv_sec) + return false; + else if (m_value.tv_sec > other.m_value.tv_sec) + return true; + + if (m_value.tv_usec < other.m_value.tv_usec) + return false; + else if (m_value.tv_usec > other.m_value.tv_usec) + return true; + + return false; + +#endif +} + +bool Timestamp::operator>=(const Timestamp& other) const +{ +#if defined(WIN32) + + if (m_value.wYear < other.m_value.wYear) + return false; + else if (m_value.wYear > other.m_value.wYear) + return true; + + if (m_value.wMonth < other.m_value.wMonth) + return false; + else if (m_value.wMonth > other.m_value.wMonth) + return true; + + if (m_value.wDay < other.m_value.wDay) + return false; + else if (m_value.wDay > other.m_value.wDay) + return true; + + if (m_value.wHour < other.m_value.wHour) + return false; + else if (m_value.wHour > other.m_value.wHour) + return true; + + if (m_value.wMinute < other.m_value.wMinute) + return false; + else if (m_value.wMinute > other.m_value.wMinute) + return true; + + if (m_value.wHour < other.m_value.wHour) + return false; + else if (m_value.wHour > other.m_value.wHour) + return true; + + if (m_value.wSecond < other.m_value.wSecond) + return false; + else if (m_value.wSecond >= other.m_value.wSecond) + return true; + + return false; + +#else + + if (m_value.tv_sec < other.m_value.tv_sec) + return false; + else if (m_value.tv_sec > other.m_value.tv_sec) + return true; + + if (m_value.tv_usec < other.m_value.tv_usec) + return false; + else if (m_value.tv_usec >= other.m_value.tv_usec) + return true; + + return false; + +#endif +} + +Timestamp& Timestamp::operator=(const Timestamp& other) +{ +#if defined(WIN32) + std::memcpy(&m_value, &other.m_value, sizeof(m_value)); +#else + std::memcpy(&m_value, &other.m_value, sizeof(m_value)); +#endif + + return *this; +} + +#if defined(WIN32) + +// http://support.microsoft.com/kb/167296 +static void UnixTimeToFileTime(time_t t, LPFILETIME pft) +{ + LONGLONG ll; + ll = Int32x32To64(t, 10000000ULL) + 116444736000000000ULL; + pft->dwLowDateTime = (DWORD)ll; + pft->dwHighDateTime = ll >> 32; +} +static void UnixTimeToSystemTime(time_t t, LPSYSTEMTIME pst) +{ + FILETIME ft; + UnixTimeToFileTime(t, &ft); + FileTimeToSystemTime(&ft, pst); +} +static time_t FileTimeToUnixTime(const FILETIME* pft) +{ + LONGLONG ll = ((LONGLONG)pft->dwHighDateTime) << 32 | (LONGLONG)pft->dwLowDateTime; + ll -= 116444736000000000ULL; + ll /= 10000000ULL; + return (time_t)ll; +} +static time_t SystemTimeToUnixTime(const SYSTEMTIME* pst) +{ + FILETIME ft; + SystemTimeToFileTime(pst, &ft); + return FileTimeToUnixTime(&ft); +} + +FILETIME Timestamp::AsFileTime() +{ + FILETIME ft; + SystemTimeToFileTime(&m_value, &ft); + return ft; +} + +void Timestamp::SetWindowsFileTime(const FILETIME* pFileTime) +{ + FileTimeToSystemTime(pFileTime, &m_value); +} + +Timestamp Timestamp::FromWindowsFileTime(const FILETIME* pFileTime) +{ + Timestamp ts; + ts.SetWindowsFileTime(pFileTime); + return ts; +} + +#endif diff --git a/src/common/timestamp.h b/src/common/timestamp.h new file mode 100644 index 000000000..73d4622c4 --- /dev/null +++ b/src/common/timestamp.h @@ -0,0 +1,75 @@ +#pragma once +#include "types.h" +#include "string.h" + +#if defined(WIN32) +#include "windows_headers.h" +#else +#include +#endif + +class Timestamp +{ +public: + using UnixTimestampValue = u64; + struct ExpandedTime + { + u32 Year; // 0-... + u32 Month; // 1-12 + u32 DayOfMonth; // 1-31 + u32 DayOfWeek; // 0-6, starting at Sunday + u32 Hour; // 0-23 + u32 Minute; // 0-59 + u32 Second; // 0-59 + u32 Milliseconds; // 0-999 + }; + +public: + Timestamp(); + Timestamp(const Timestamp& copy); + + // readers + UnixTimestampValue AsUnixTimestamp() const; + ExpandedTime AsExpandedTime() const; + + // calculators + double DifferenceInSeconds(Timestamp& other) const; + s64 DifferenceInSecondsInt(Timestamp& other) const; + + // setters + void SetNow(); + void SetUnixTimestamp(UnixTimestampValue value); + void SetExpandedTime(const ExpandedTime& value); + + // string conversion + String ToString(const char* format) const; + void ToString(String& destination, const char* format) const; + + // creators + static Timestamp Now(); + static Timestamp FromUnixTimestamp(UnixTimestampValue value); + static Timestamp FromExpandedTime(const ExpandedTime& value); + +// windows-specific +#ifdef WIN32 + FILETIME AsFileTime(); + void SetWindowsFileTime(const FILETIME* pFileTime); + static Timestamp FromWindowsFileTime(const FILETIME* pFileTime); +#endif + + // operators + bool operator==(const Timestamp& other) const; + bool operator!=(const Timestamp& other) const; + bool operator<(const Timestamp& other) const; + bool operator<=(const Timestamp& other) const; + bool operator>(const Timestamp& other) const; + bool operator>=(const Timestamp& other) const; + Timestamp& operator=(const Timestamp& other); + +private: +#if defined(WIN32) + SYSTEMTIME m_value; +#else + struct timeval m_value; +#endif +}; diff --git a/src/common/types.h b/src/common/types.h index f79e75b90..ab2711fa4 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -1,8 +1,7 @@ #pragma once - -#include "YBaseLib/Common.h" #include #include +#include #include // Force inline helper @@ -16,6 +15,41 @@ #endif #endif +// unreferenced parameter macro +#ifndef UNREFERENCED_VARIABLE +#if defined(_MSC_VER) +#define UNREFERENCED_VARIABLE(P) (P) +#elif defined(__GNUC__) || defined(__clang__) || defined(__EMSCRIPTEN__) +#define UNREFERENCED_VARIABLE(P) (void)(P) +#else +#define UNREFERENCED_VARIABLE(P) (P) +#endif +#endif + +// countof macro +#ifndef countof +#ifdef _countof +#define countof _countof +#else +template +char (&__countof_ArraySizeHelper(T (&array)[N]))[N]; +#define countof(array) (sizeof(__countof_ArraySizeHelper(array))) +#endif +#endif + +// offsetof macro +#ifndef offsetof +#define offsetof(st, m) ((size_t)((char*)&((st*)(0))->m - (char*)0)) +#endif + +// disable warnings that show up at warning level 4 +// TODO: Move to build system instead +#ifdef _MSC_VER +#pragma warning(disable : 4201) // warning C4201: nonstandard extension used : nameless struct/union +#pragma warning(disable : 4100) // warning C4100: 'Platform' : unreferenced formal parameter +#pragma warning(disable : 4355) // warning C4355: 'this' : used in base member initializer list +#endif + using s8 = int8_t; using u8 = uint8_t; using s16 = int16_t; @@ -202,38 +236,38 @@ ALWAYS_INLINE constexpr T SignExtendN(T value) // Enum class bitwise operators #define IMPLEMENT_ENUM_CLASS_BITWISE_OPERATORS(type_) \ - ALWAYS_INLINE constexpr type_ operator&(type_ lhs, type_ rhs) \ + ALWAYS_INLINE constexpr type_ operator&(type_ lhs, type_ rhs) \ { \ return static_cast(static_cast::type>(lhs) & \ static_cast::type>(rhs)); \ } \ - ALWAYS_INLINE constexpr type_ operator|(type_ lhs, type_ rhs) \ + ALWAYS_INLINE constexpr type_ operator|(type_ lhs, type_ rhs) \ { \ return static_cast(static_cast::type>(lhs) | \ static_cast::type>(rhs)); \ } \ - ALWAYS_INLINE constexpr type_ operator^(type_ lhs, type_ rhs) \ + ALWAYS_INLINE constexpr type_ operator^(type_ lhs, type_ rhs) \ { \ return static_cast(static_cast::type>(lhs) ^ \ static_cast::type>(rhs)); \ } \ - ALWAYS_INLINE constexpr type_ operator~(type_ val) \ + ALWAYS_INLINE constexpr type_ operator~(type_ val) \ { \ return static_cast(~static_cast::type>(val)); \ } \ - ALWAYS_INLINE constexpr type_& operator&=(type_& lhs, type_ rhs) \ + ALWAYS_INLINE constexpr type_& operator&=(type_& lhs, type_ rhs) \ { \ lhs = static_cast(static_cast::type>(lhs) & \ static_cast::type>(rhs)); \ return lhs; \ } \ - ALWAYS_INLINE constexpr type_& operator|=(type_& lhs, type_ rhs) \ + ALWAYS_INLINE constexpr type_& operator|=(type_& lhs, type_ rhs) \ { \ lhs = static_cast(static_cast::type>(lhs) | \ static_cast::type>(rhs)); \ return lhs; \ } \ - ALWAYS_INLINE constexpr type_& operator^=(type_& lhs, type_ rhs) \ + ALWAYS_INLINE constexpr type_& operator^=(type_& lhs, type_ rhs) \ { \ lhs = static_cast(static_cast::type>(lhs) ^ \ static_cast::type>(rhs)); \ diff --git a/src/common/windows_headers.h b/src/common/windows_headers.h new file mode 100644 index 000000000..178a8160e --- /dev/null +++ b/src/common/windows_headers.h @@ -0,0 +1,39 @@ +#pragma once + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#ifndef NOMINMAX +#define NOMINMAX 1 +#endif + +// require vista+ +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT _WIN32_WINNT_VISTA + +#include + +// hurr i'm windows, i like to conflict, fixme properly later please... +#if defined(FindTexture) +#undef FindTexture +#endif +#if defined(DrawText) +#undef DrawText +#endif +#if defined(CreateDirectory) +#undef CreateDirectory +#endif +#if defined(CopyFile) +#undef CopyFile +#endif +#if defined(DeleteFile) +#undef DeleteFile +#endif +#if defined(Yield) +#undef Yield +#endif +#if defined(LoadIcon) +#undef LoadIcon +#endif diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e122ad7eb..954daba5f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -79,7 +79,7 @@ set(RECOMPILER_SRCS target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") -target_link_libraries(core PUBLIC Threads::Threads YBaseLib common imgui tinyxml2) +target_link_libraries(core PUBLIC Threads::Threads common imgui tinyxml2) target_link_libraries(core PRIVATE glad stb) if(WIN32) diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index 665d70afb..cc99e15b8 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -1,5 +1,5 @@ #include "analog_controller.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "common/state_wrapper.h" Log_SetChannel(AnalogController); @@ -133,7 +133,7 @@ void AnalogController::SetMotorState(u8 motor, u8 value) bool AnalogController::Transfer(const u8 data_in, u8* data_out) { bool ack; -#ifdef Y_BUILD_CONFIG_DEBUG +#ifdef _DEBUG u8 old_state = static_cast(m_state); #endif diff --git a/src/core/bios.cpp b/src/core/bios.cpp index fe43ff7ba..ed624d2d0 100644 --- a/src/core/bios.cpp +++ b/src/core/bios.cpp @@ -1,6 +1,7 @@ #include "bios.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/MD5Digest.h" +#include "common/log.h" +#include "common/assert.h" +#include "common/md5_digest.h" #include "cpu_disasm.h" #include Log_SetChannel(BIOS); diff --git a/src/core/bus.cpp b/src/core/bus.cpp index e78aeef71..c2f5e1eb7 100644 --- a/src/core/bus.cpp +++ b/src/core/bus.cpp @@ -1,9 +1,8 @@ #include "bus.h" -#include "YBaseLib/ByteStream.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/MD5Digest.h" -#include "YBaseLib/String.h" #include "cdrom.h" +#include "common/align.h" +#include "common/assert.h" +#include "common/log.h" #include "common/state_wrapper.h" #include "cpu_code_cache.h" #include "cpu_core.h" @@ -344,19 +343,19 @@ void Bus::DoWriteEXP2(MemoryAccessSize size, u32 offset, u32 value) if (value == '\n') { - if (!m_tty_line_buffer.IsEmpty()) + if (!m_tty_line_buffer.empty()) { - Log_InfoPrintf("TTY: %s", m_tty_line_buffer.GetCharArray()); + Log_InfoPrintf("TTY: %s", m_tty_line_buffer.c_str()); #ifdef _DEBUG if (CPU::LOG_EXECUTION) - CPU::WriteToExecutionLog("TTY: %s\n", m_tty_line_buffer.GetCharArray()); + CPU::WriteToExecutionLog("TTY: %s\n", m_tty_line_buffer.c_str()); #endif } - m_tty_line_buffer.Clear(); + m_tty_line_buffer.clear(); } else { - m_tty_line_buffer.AppendCharacter(Truncate8(value)); + m_tty_line_buffer += static_cast(Truncate8(value)); } return; diff --git a/src/core/bus.h b/src/core/bus.h index e06acdec3..2a859a422 100644 --- a/src/core/bus.h +++ b/src/core/bus.h @@ -1,10 +1,9 @@ #pragma once - -#include "YBaseLib/String.h" #include "common/bitfield.h" #include "types.h" #include #include +#include #include class StateWrapper; @@ -270,7 +269,7 @@ private: MEMCTRL m_MEMCTRL = {}; u32 m_ram_size_reg = 0; - String m_tty_line_buffer; + std::string m_tty_line_buffer; }; #include "bus.inl" diff --git a/src/core/cdrom.cpp b/src/core/cdrom.cpp index 5b827f708..8a886db38 100644 --- a/src/core/cdrom.cpp +++ b/src/core/cdrom.cpp @@ -1,5 +1,5 @@ #include "cdrom.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "common/cd_image.h" #include "common/state_wrapper.h" #include "dma.h" @@ -1421,7 +1421,7 @@ static void ResampleXAADPCM(const s16* samples_in, u32 num_samples_in, SPU* spu, if constexpr (!STEREO) { - UNREFERENCED_PARAMETER(right); + UNREFERENCED_VARIABLE(right); } for (u32 sample_dup = 0; sample_dup < (SAMPLE_RATE ? 2 : 1); sample_dup++) diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index d3ae04ee9..e083a791c 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -129,9 +129,6 @@ {933118a9-68c5-47b4-b151-b03c93961623} - - {b56ce698-7300-4fa5-9609-942f1d05c5a2} - {ee054e08-3799-4a59-a422-18259c105ffd} @@ -284,7 +281,7 @@ WITH_RECOMPILER=1;TINYXML2_IMPORT;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -306,7 +303,7 @@ WITH_RECOMPILER=1;TINYXML2_IMPORT;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -328,7 +325,7 @@ WITH_RECOMPILER=1;TINYXML2_IMPORT;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default true false @@ -353,7 +350,7 @@ WITH_RECOMPILER=1;TINYXML2_IMPORT;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default true false @@ -377,7 +374,7 @@ MaxSpeed true WITH_RECOMPILER=1;TINYXML2_IMPORT;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -400,7 +397,7 @@ MaxSpeed true WITH_RECOMPILER=1;TINYXML2_IMPORT;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 @@ -424,7 +421,7 @@ MaxSpeed true WITH_RECOMPILER=1;TINYXML2_IMPORT;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -447,7 +444,7 @@ MaxSpeed true WITH_RECOMPILER=1;TINYXML2_IMPORT;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index 9783516af..f8d7e4c55 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -1,5 +1,5 @@ #include "cpu_code_cache.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "cpu_core.h" #include "cpu_disasm.h" #include "system.h" diff --git a/src/core/cpu_core.cpp b/src/core/cpu_core.cpp index 3f4f6701e..801dee847 100644 --- a/src/core/cpu_core.cpp +++ b/src/core/cpu_core.cpp @@ -1,6 +1,7 @@ #include "cpu_core.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "common/state_wrapper.h" +#include "common/align.h" #include "cpu_disasm.h" #include Log_SetChannel(CPU::Core); diff --git a/src/core/cpu_core.inl b/src/core/cpu_core.inl index c21155afb..ffc5584cf 100644 --- a/src/core/cpu_core.inl +++ b/src/core/cpu_core.inl @@ -1,5 +1,5 @@ #pragma once -#include "YBaseLib/Assert.h" +#include "common/align.h" #include "bus.h" #include "cpu_core.h" diff --git a/src/core/cpu_disasm.h b/src/core/cpu_disasm.h index 27c6d98ac..5b11eebdf 100644 --- a/src/core/cpu_disasm.h +++ b/src/core/cpu_disasm.h @@ -1,5 +1,5 @@ #pragma once -#include "YBaseLib/String.h" +#include "common/string.h" #include "cpu_types.h" namespace CPU { diff --git a/src/core/cpu_recompiler_code_generator.cpp b/src/core/cpu_recompiler_code_generator.cpp index 3be30309e..6244944c7 100644 --- a/src/core/cpu_recompiler_code_generator.cpp +++ b/src/core/cpu_recompiler_code_generator.cpp @@ -1,5 +1,5 @@ #include "cpu_recompiler_code_generator.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "cpu_core.h" #include "cpu_disasm.h" Log_SetChannel(CPU::Recompiler); @@ -12,7 +12,7 @@ namespace CPU::Recompiler { u32 CodeGenerator::CalculateRegisterOffset(Reg reg) { - return uint32(offsetof(Core, m_regs.r[0]) + (static_cast(reg) * sizeof(u32))); + return u32(offsetof(Core, m_regs.r[0]) + (static_cast(reg) * sizeof(u32))); } bool CodeGenerator::CompileBlock(const CodeBlock* block, CodeBlock::HostCodePointer* out_host_code, @@ -837,7 +837,7 @@ void CodeGenerator::BlockPrologue() void CodeGenerator::BlockEpilogue() { -#if defined(_DEBUG) && defined(Y_CPU_X64) +#if defined(_DEBUG) && defined(CPU_X64) m_emit->nop(); #endif @@ -851,7 +851,7 @@ void CodeGenerator::BlockEpilogue() void CodeGenerator::InstructionPrologue(const CodeBlockInstruction& cbi, TickCount cycles, bool force_sync /* = false */) { -#if defined(_DEBUG) && defined(Y_CPU_X64) +#if defined(_DEBUG) && defined(CPU_X64) m_emit->nop(); #endif diff --git a/src/core/cpu_recompiler_code_generator.h b/src/core/cpu_recompiler_code_generator.h index 84355a2f7..2c932188b 100644 --- a/src/core/cpu_recompiler_code_generator.h +++ b/src/core/cpu_recompiler_code_generator.h @@ -11,17 +11,6 @@ #include "cpu_recompiler_types.h" #include "cpu_types.h" -// ABI selection -#if defined(Y_CPU_X64) -#if defined(Y_PLATFORM_WINDOWS) -#define ABI_WIN64 1 -#elif defined(Y_PLATFORM_LINUX) || defined(Y_PLATFORM_OSX) || defined(Y_PLATFORM_ANDROID) -#define ABI_SYSV 1 -#else -#error Unknown ABI. -#endif -#endif - namespace CPU::Recompiler { class CodeGenerator diff --git a/src/core/cpu_recompiler_code_generator_aarch64.cpp b/src/core/cpu_recompiler_code_generator_aarch64.cpp index 1f5b74518..8bd676439 100644 --- a/src/core/cpu_recompiler_code_generator_aarch64.cpp +++ b/src/core/cpu_recompiler_code_generator_aarch64.cpp @@ -1,4 +1,4 @@ -#include "YBaseLib/Log.h" +#include "common/log.h" #include "cpu_recompiler_code_generator.h" #include "cpu_recompiler_thunks.h" #include "cpu_core.h" diff --git a/src/core/cpu_recompiler_register_cache.cpp b/src/core/cpu_recompiler_register_cache.cpp index bc39f9744..d108dd993 100644 --- a/src/core/cpu_recompiler_register_cache.cpp +++ b/src/core/cpu_recompiler_register_cache.cpp @@ -1,5 +1,5 @@ #include "cpu_recompiler_register_cache.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "cpu_recompiler_code_generator.h" #include Log_SetChannel(CPU::Recompiler); diff --git a/src/core/cpu_recompiler_register_cache.h b/src/core/cpu_recompiler_register_cache.h index 22f636dc1..b5bee1503 100644 --- a/src/core/cpu_recompiler_register_cache.h +++ b/src/core/cpu_recompiler_register_cache.h @@ -1,5 +1,5 @@ #pragma once -#include "YBaseLib/Assert.h" +#include "common/assert.h" #include "cpu_recompiler_types.h" #include "cpu_types.h" diff --git a/src/core/cpu_recompiler_types.h b/src/core/cpu_recompiler_types.h index fe6ea5538..a27c777cb 100644 --- a/src/core/cpu_recompiler_types.h +++ b/src/core/cpu_recompiler_types.h @@ -1,12 +1,22 @@ #pragma once +#include "common/cpu_detect.h" #include "cpu_types.h" -#if defined(Y_CPU_X64) +#if defined(CPU_X64) + +// We need to include windows.h before xbyak does.. +#ifdef WIN32 +#include "common/windows_headers.h" +#endif + #define XBYAK_NO_OP_NAMES 1 #include "xbyak.h" -#elif defined(Y_CPU_AARCH64) + +#elif defined(CPU_AARCH64) + #include #include + #endif namespace CPU { @@ -48,7 +58,7 @@ enum class Condition : u8 Zero }; -#if defined(Y_CPU_X64) +#if defined(CPU_X64) using HostReg = Xbyak::Operand::Code; using CodeEmitter = Xbyak::CodeGenerator; @@ -67,14 +77,23 @@ constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128; // Are shifts implicitly masked to 0..31? constexpr bool SHIFTS_ARE_IMPLICITLY_MASKED = true; -#elif defined(Y_CPU_AARCH64) +// ABI selection +#if defined(WIN32) +#define ABI_WIN64 1 +#elif defined(__linux__) || defined(__ANDROID__) +#define ABI_SYSV 1 +#else +#error Unknown ABI. +#endif + +#elif defined(CPU_AARCH64) using HostReg = unsigned; using CodeEmitter = vixl::aarch64::MacroAssembler; using LabelType = vixl::aarch64::Label; enum : u32 { - HostReg_Count = vixl::aarch64::kNumberOfRegisters + HostReg_Count = vixl::aarch64::kNumberOfRegisters }; constexpr HostReg HostReg_Invalid = static_cast(HostReg_Count); constexpr RegSize HostPointerSize = RegSize_64; diff --git a/src/core/cpu_types.cpp b/src/core/cpu_types.cpp index c8fd684d3..3fe02b593 100644 --- a/src/core/cpu_types.cpp +++ b/src/core/cpu_types.cpp @@ -1,5 +1,5 @@ #include "cpu_types.h" -#include "YBaseLib/Assert.h" +#include "common/assert.h" #include namespace CPU { diff --git a/src/core/digital_controller.cpp b/src/core/digital_controller.cpp index a794cd9bb..821b5c0d0 100644 --- a/src/core/digital_controller.cpp +++ b/src/core/digital_controller.cpp @@ -1,5 +1,5 @@ #include "digital_controller.h" -#include "YBaseLib/Assert.h" +#include "common/assert.h" DigitalController::DigitalController() = default; diff --git a/src/core/dma.cpp b/src/core/dma.cpp index 16f919d40..e8c735a76 100644 --- a/src/core/dma.cpp +++ b/src/core/dma.cpp @@ -1,5 +1,5 @@ #include "dma.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "bus.h" #include "cdrom.h" #include "common/state_wrapper.h" diff --git a/src/core/game_list.cpp b/src/core/game_list.cpp index edcb4c669..32f4f5350 100644 --- a/src/core/game_list.cpp +++ b/src/core/game_list.cpp @@ -1,13 +1,12 @@ #include "game_list.h" -#include "YBaseLib/AutoReleasePtr.h" -#include "YBaseLib/BinaryReader.h" -#include "YBaseLib/BinaryWriter.h" -#include "YBaseLib/ByteStream.h" -#include "YBaseLib/FileSystem.h" -#include "YBaseLib/Log.h" #include "bios.h" +#include "common/assert.h" +#include "common/byte_stream.h" #include "common/cd_image.h" +#include "common/file_system.h" #include "common/iso_reader.h" +#include "common/log.h" +#include "common/string_util.h" #include "settings.h" #include #include @@ -17,12 +16,6 @@ #include Log_SetChannel(GameList); -#ifdef _MSC_VER -#define CASE_COMPARE _stricmp -#else -#define CASE_COMPARE strcasecmp -#endif - GameList::GameList() = default; GameList::~GameList() = default; @@ -91,7 +84,7 @@ std::string GameList::GetGameCodeForImage(CDImage* cdi) // Find the BOOT line auto iter = std::find_if(lines.begin(), lines.end(), - [](const auto& it) { return CASE_COMPARE(it.first.c_str(), "boot") == 0; }); + [](const auto& it) { return StringUtil::Strcasecmp(it.first.c_str(), "boot") == 0; }); if (iter == lines.end()) return {}; @@ -203,7 +196,8 @@ std::optional GameList::GetRegionForPath(const char* image_path) bool GameList::IsExeFileName(const char* path) { const char* extension = std::strrchr(path, '.'); - return (extension && (CASE_COMPARE(extension, ".exe") == 0 || CASE_COMPARE(extension, ".psexe") == 0)); + return (extension && + (StringUtil::Strcasecmp(extension, ".exe") == 0 || StringUtil::Strcasecmp(extension, ".psexe") == 0)); } static std::string_view GetFileNameFromPath(const char* path) @@ -340,45 +334,93 @@ void GameList::LoadCache() if (m_cache_filename.empty()) return; - ByteStream* stream = FileSystem::OpenFile(m_cache_filename.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); + std::unique_ptr stream = + FileSystem::OpenFile(m_cache_filename.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); if (!stream) return; - if (!LoadEntriesFromCache(stream)) + if (!LoadEntriesFromCache(stream.get())) { Log_WarningPrintf("Deleting corrupted cache file '%s'", m_cache_filename.c_str()); - stream->Release(); + stream.reset(); m_cache_map.clear(); DeleteCacheFile(); return; } +} + +static bool ReadString(ByteStream* stream, std::string* dest) +{ + u32 size; + if (!stream->Read2(&size, sizeof(size))) + return false; + + dest->resize(size); + if (!stream->Read2(dest->data(), size)) + return false; + + return true; +} + +static bool ReadU8(ByteStream* stream, u8* dest) +{ + return stream->Read2(dest, sizeof(u8)); +} + +static bool ReadU32(ByteStream* stream, u32* dest) +{ + return stream->Read2(dest, sizeof(u32)); +} + +static bool ReadU64(ByteStream* stream, u64* dest) +{ + return stream->Read2(dest, sizeof(u64)); +} + +static bool WriteString(ByteStream* stream, const std::string& str) +{ + const u32 size = static_cast(str.size()); + return (stream->Write2(&size, sizeof(size)) && (size == 0 || stream->Write2(str.data(), size))); +} - stream->Release(); +static bool WriteU8(ByteStream* stream, u8 dest) +{ + return stream->Write2(&dest, sizeof(u8)); +} + +static bool WriteU32(ByteStream* stream, u32 dest) +{ + return stream->Write2(&dest, sizeof(u32)); +} + +static bool WriteU64(ByteStream* stream, u64 dest) +{ + return stream->Write2(&dest, sizeof(u64)); } bool GameList::LoadEntriesFromCache(ByteStream* stream) { - BinaryReader reader(stream); - if (reader.ReadUInt32() != GAME_LIST_CACHE_SIGNATURE || reader.ReadUInt32() != GAME_LIST_CACHE_VERSION) + u32 file_signature, file_version; + if (!ReadU32(stream, &file_signature) || !ReadU32(stream, &file_version) || + file_signature != GAME_LIST_CACHE_SIGNATURE || file_version != GAME_LIST_CACHE_VERSION) { Log_WarningPrintf("Game list cache is corrupted"); return false; } - String path; - TinyString code; - SmallString title; - u64 total_size; - u64 last_modified_time; - u8 region; - u8 type; - while (stream->GetPosition() != stream->GetSize()) { - if (!reader.SafeReadSizePrefixedString(&path) || !reader.SafeReadSizePrefixedString(&code) || - !reader.SafeReadSizePrefixedString(&title) || !reader.SafeReadUInt64(&total_size) || - !reader.SafeReadUInt64(&last_modified_time) || !reader.SafeReadUInt8(®ion) || - region >= static_cast(ConsoleRegion::Count) || !reader.SafeReadUInt8(&type) || + std::string path; + std::string code; + std::string title; + u64 total_size; + u64 last_modified_time; + u8 region; + u8 type; + + if (!ReadString(stream, &path) || !ReadString(stream, &code) || !ReadString(stream, &title) || + !ReadU64(stream, &total_size) || !ReadU64(stream, &last_modified_time) || !ReadU8(stream, ®ion) || + region >= static_cast(ConsoleRegion::Count) || !ReadU8(stream, &type) || type > static_cast(EntryType::PSExe)) { Log_WarningPrintf("Game list cache entry is corrupted"); @@ -386,9 +428,9 @@ bool GameList::LoadEntriesFromCache(ByteStream* stream) } GameListEntry ge; - ge.path = path; - ge.code = code; - ge.title = title; + ge.path = std::move(path); + ge.code = std::move(code); + ge.title = std::move(title); ge.total_size = total_size; ge.last_modified_time = last_modified_time; ge.region = static_cast(region); @@ -419,12 +461,11 @@ bool GameList::OpenCacheForWriting() if (m_cache_write_stream->GetPosition() == 0) { // new cache file, write header - BinaryWriter writer(m_cache_write_stream); - if (!writer.SafeWriteUInt32(GAME_LIST_CACHE_SIGNATURE) || !writer.SafeWriteUInt32(GAME_LIST_CACHE_VERSION)) + if (!WriteU32(m_cache_write_stream.get(), GAME_LIST_CACHE_SIGNATURE) || + !WriteU32(m_cache_write_stream.get(), GAME_LIST_CACHE_VERSION)) { Log_ErrorPrintf("Failed to write game list cache header"); - m_cache_write_stream->Release(); - m_cache_write_stream = nullptr; + m_cache_write_stream.reset(); FileSystem::DeleteFile(m_cache_filename.c_str()); return false; } @@ -435,14 +476,13 @@ bool GameList::OpenCacheForWriting() bool GameList::WriteEntryToCache(const GameListEntry* entry, ByteStream* stream) { - BinaryWriter writer(stream); - bool result = writer.SafeWriteSizePrefixedString(entry->path.c_str()); - result &= writer.SafeWriteSizePrefixedString(entry->code.c_str()); - result &= writer.SafeWriteSizePrefixedString(entry->title.c_str()); - result &= writer.SafeWriteUInt64(entry->total_size); - result &= writer.SafeWriteUInt64(entry->last_modified_time); - result &= writer.SafeWriteUInt8(static_cast(entry->region)); - result &= writer.SafeWriteUInt8(static_cast(entry->type)); + bool result = WriteString(stream, entry->path); + result &= WriteString(stream, entry->code); + result &= WriteString(stream, entry->title); + result &= WriteU64(stream, entry->total_size); + result &= WriteU64(stream, entry->last_modified_time); + result &= WriteU8(stream, static_cast(entry->region)); + result &= WriteU8(stream, static_cast(entry->type)); return result; } @@ -452,8 +492,7 @@ void GameList::CloseCacheFileStream() return; m_cache_write_stream->Commit(); - m_cache_write_stream->Release(); - m_cache_write_stream = nullptr; + m_cache_write_stream.reset(); } void GameList::DeleteCacheFile() @@ -479,14 +518,14 @@ void GameList::ScanDirectory(const char* path, bool recursive) for (const FILESYSTEM_FIND_DATA& ffd : files) { // if this is a .bin, check if we have a .cue. if there is one, skip it - const char* extension = std::strrchr(ffd.FileName, '.'); - if (extension && CASE_COMPARE(extension, ".bin") == 0) + const char* extension = std::strrchr(ffd.FileName.c_str(), '.'); + if (extension && StringUtil::Strcasecmp(extension, ".bin") == 0) { #if 0 std::string temp(ffd.FileName, extension - ffd.FileName); temp += ".cue"; if (std::any_of(files.begin(), files.end(), - [&temp](const FILESYSTEM_FIND_DATA& it) { return CASE_COMPARE(it.FileName, temp.c_str()) == 0; })) + [&temp](const FILESYSTEM_FIND_DATA& it) { return StringUtil::Strcasecmp(it.FileName, temp.c_str()) == 0; })) { Log_DebugPrintf("Skipping due to '%s' existing", temp.c_str()); continue; @@ -512,7 +551,7 @@ void GameList::ScanDirectory(const char* path, bool recursive) { if (m_cache_write_stream || OpenCacheForWriting()) { - if (!WriteEntryToCache(&entry, m_cache_write_stream)) + if (!WriteEntryToCache(&entry, m_cache_write_stream.get())) Log_WarningPrintf("Failed to write entry '%s' to cache", entry.path.c_str()); } } @@ -554,10 +593,10 @@ public: bool VisitEnter(const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* firstAttribute) override { // recurse into gamelist - if (CASE_COMPARE(element.Name(), "datafile") == 0) + if (StringUtil::Strcasecmp(element.Name(), "datafile") == 0) return true; - if (CASE_COMPARE(element.Name(), "game") != 0) + if (StringUtil::Strcasecmp(element.Name(), "game") != 0) return false; const char* name = element.Attribute("name"); diff --git a/src/core/game_list.h b/src/core/game_list.h index c4c56d235..a67aa51f9 100644 --- a/src/core/game_list.h +++ b/src/core/game_list.h @@ -1,5 +1,6 @@ #pragma once #include "types.h" +#include #include #include #include @@ -48,6 +49,9 @@ public: static const char* EntryTypeToString(EntryType type); + /// Returns true if the filename is a PlayStation executable we can inject. + static bool IsExeFileName(const char* path); + static std::string GetGameCodeForImage(CDImage* cdi); static std::string GetGameCodeForPath(const char* image_path); static std::optional GetRegionForCode(std::string_view code); @@ -76,7 +80,6 @@ private: bool recursive; }; - static bool IsExeFileName(const char* path); static bool GetExeListEntry(const char* path, GameListEntry* entry); bool GetGameListEntry(const std::string& path, GameListEntry* entry); @@ -96,7 +99,7 @@ private: DatabaseMap m_database; EntryList m_entries; CacheMap m_cache_map; - ByteStream* m_cache_write_stream = nullptr; + std::unique_ptr m_cache_write_stream; std::vector m_search_directories; std::string m_cache_filename; diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 2a8aa826a..a0d2d85bf 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -1,5 +1,5 @@ #include "gpu.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "common/heap_array.h" #include "common/state_wrapper.h" #include "dma.h" diff --git a/src/core/gpu.h b/src/core/gpu.h index d532652a3..9806ec148 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -3,6 +3,7 @@ #include "common/rectangle.h" #include "timers.h" #include "types.h" +#include #include #include #include diff --git a/src/core/gpu_commands.cpp b/src/core/gpu_commands.cpp index 14a271151..a448036df 100644 --- a/src/core/gpu_commands.cpp +++ b/src/core/gpu_commands.cpp @@ -1,5 +1,6 @@ -#include "YBaseLib/Log.h" -#include "YBaseLib/String.h" +#include "common/assert.h" +#include "common/log.h" +#include "common/string_util.h" #include "gpu.h" #include "interrupt_controller.h" #include "system.h" @@ -356,8 +357,8 @@ bool GPU::HandleCopyRectangleCPUToVRAMCommand(const u32*& command_ptr, u32 comma if (m_system->GetSettings().debugging.dump_cpu_to_vram_copies) { - DumpVRAMToFile(SmallString::FromFormat("cpu_to_vram_copy_%u.png", s_cpu_to_vram_dump_id++), copy_width, copy_height, - sizeof(u16) * copy_width, &command_ptr[3], true); + DumpVRAMToFile(StringUtil::StdStringFromFormat("cpu_to_vram_copy_%u.png", s_cpu_to_vram_dump_id++).c_str(), + copy_width, copy_height, sizeof(u16) * copy_width, &command_ptr[3], true); } FlushRender(); @@ -390,8 +391,8 @@ bool GPU::HandleCopyRectangleVRAMToCPUCommand(const u32*& command_ptr, u32 comma if (m_system->GetSettings().debugging.dump_vram_to_cpu_copies) { - DumpVRAMToFile(SmallString::FromFormat("vram_to_cpu_copy_%u.png", s_vram_to_cpu_dump_id++), m_vram_transfer.width, - m_vram_transfer.height, sizeof(u16) * VRAM_WIDTH, + DumpVRAMToFile(StringUtil::StdStringFromFormat("vram_to_cpu_copy_%u.png", s_vram_to_cpu_dump_id++).c_str(), + m_vram_transfer.width, m_vram_transfer.height, sizeof(u16) * VRAM_WIDTH, &m_vram_ptr[m_vram_transfer.y * VRAM_WIDTH + m_vram_transfer.x], true); } diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 912871bb9..e411306fb 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -1,6 +1,6 @@ #include "gpu_hw.h" -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" +#include "common/assert.h" +#include "common/log.h" #include "common/state_wrapper.h" #include "settings.h" #include "system.h" diff --git a/src/core/gpu_hw_d3d11.cpp b/src/core/gpu_hw_d3d11.cpp index 98ab201db..da1b38c0b 100644 --- a/src/core/gpu_hw_d3d11.cpp +++ b/src/core/gpu_hw_d3d11.cpp @@ -1,7 +1,6 @@ #include "gpu_hw_d3d11.h" -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/String.h" +#include "common/assert.h" +#include "common/log.h" #include "common/d3d11/shader_compiler.h" #include "gpu_hw_shadergen.h" #include "host_display.h" diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index 9ce5955de..4d42a1584 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -1,7 +1,6 @@ #include "gpu_hw_opengl.h" -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/String.h" +#include "common/assert.h" +#include "common/log.h" #include "gpu_hw_shadergen.h" #include "host_display.h" #include "system.h" diff --git a/src/core/gpu_hw_opengl_es.cpp b/src/core/gpu_hw_opengl_es.cpp index 9c12c9eb9..fd31763db 100644 --- a/src/core/gpu_hw_opengl_es.cpp +++ b/src/core/gpu_hw_opengl_es.cpp @@ -1,7 +1,6 @@ #include "gpu_hw_opengl_es.h" -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/String.h" +#include "common/assert.h" +#include "common/log.h" #include "gpu_hw_shadergen.h" #include "host_display.h" #include "system.h" diff --git a/src/core/gpu_hw_shadergen.cpp b/src/core/gpu_hw_shadergen.cpp index 034457c38..095cd5dad 100644 --- a/src/core/gpu_hw_shadergen.cpp +++ b/src/core/gpu_hw_shadergen.cpp @@ -1,6 +1,6 @@ #include "gpu_hw_shadergen.h" -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" +#include "common/assert.h" +#include "common/log.h" #include #include Log_SetChannel(GPU_HW_ShaderGen); diff --git a/src/core/gpu_sw.cpp b/src/core/gpu_sw.cpp index 0b072bc8d..231ea5691 100644 --- a/src/core/gpu_sw.cpp +++ b/src/core/gpu_sw.cpp @@ -1,5 +1,5 @@ #include "gpu_sw.h" -#include "YBaseLib/Assert.h" +#include "common/assert.h" #include "host_display.h" #include "system.h" #include @@ -593,7 +593,7 @@ void GPU_SW::ShadePixel(u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 tex } else { - UNREFERENCED_PARAMETER(transparent); + UNREFERENCED_VARIABLE(transparent); } const u16 mask_and = m_GPUSTAT.GetMaskAND(); diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp index 8f6ed75cb..224242f81 100644 --- a/src/core/host_interface.cpp +++ b/src/core/host_interface.cpp @@ -1,10 +1,12 @@ #include "host_interface.h" -#include "YBaseLib/ByteStream.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/Timer.h" #include "bios.h" #include "cdrom.h" #include "common/audio_stream.h" +#include "common/byte_stream.h" +#include "common/file_system.h" +#include "common/log.h" +#include "common/string_util.h" +#include "common/timer.h" #include "dma.h" #include "gpu.h" #include "host_display.h" @@ -18,7 +20,7 @@ Log_SetChannel(HostInterface); #ifdef _WIN32 -#include "YBaseLib/Windows/WindowsHeaders.h" +#include "common/windows_headers.h" #else #include #endif @@ -50,7 +52,7 @@ static std::string GetRelativePath(const std::string& path, const char* new_file HostInterface::HostInterface() { m_settings.SetDefaults(); - m_last_throttle_time = Y_TimerGetValue(); + m_last_throttle_time = Common::Timer::GetValue(); } HostInterface::~HostInterface() = default; @@ -104,6 +106,26 @@ void HostInterface::ReportMessage(const char* message) Log_InfoPrintf(message); } +void HostInterface::ReportFormattedError(const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + std::string message = StringUtil::StdStringFromFormatV(format, ap); + va_end(ap); + + ReportError(message.c_str()); +} + +void HostInterface::ReportFormattedMessage(const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + std::string message = StringUtil::StdStringFromFormatV(format, ap); + va_end(ap); + + ReportMessage(message.c_str()); +} + void HostInterface::DrawFPSWindow() { const bool show_fps = true; @@ -183,6 +205,21 @@ void HostInterface::AddOSDMessage(const char* message, float duration /*= 2.0f*/ m_osd_messages.push_back(std::move(msg)); } +void HostInterface::AddFormattedOSDMessage(float duration, const char* format, ...) +{ + std::va_list ap; + va_start(ap, format); + std::string message = StringUtil::StdStringFromFormatV(format, ap); + va_end(ap); + + OSDMessage msg; + msg.text = std::move(message); + msg.duration = duration; + + std::unique_lock lock(m_osd_messages_lock); + m_osd_messages.push_back(std::move(msg)); +} + void HostInterface::DrawOSDMessages() { constexpr ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | @@ -213,7 +250,10 @@ void HostInterface::DrawOSDMessages() ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); ImGui::PushStyleVar(ImGuiStyleVar_Alpha, opacity); - if (ImGui::Begin(SmallString::FromFormat("osd_%u", index++), nullptr, window_flags)) + char buf[64]; + std::snprintf(buf, sizeof(buf), "osd_%u", index++); + + if (ImGui::Begin(buf, nullptr, window_flags)) { ImGui::TextUnformatted(msg.text.c_str()); position_y += ImGui::GetWindowSize().y + (4.0f * scale); @@ -339,47 +379,42 @@ void HostInterface::Throttle() bool HostInterface::LoadState(const char* filename) { - ByteStream* stream; - if (!ByteStream_OpenFileStream(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED, &stream)) + std::unique_ptr stream = FileSystem::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); + if (!stream) return false; - AddOSDMessage(SmallString::FromFormat("Loading state from %s...", filename)); + AddFormattedOSDMessage(2.0f, "Loading state from %s...", filename); - const bool result = m_system->LoadState(stream); + const bool result = m_system->LoadState(stream.get()); if (!result) { - ReportError(SmallString::FromFormat("Loading state from %s failed. Resetting.", filename)); + ReportFormattedError("Loading state from %s failed. Resetting.", filename); m_system->Reset(); } - stream->Release(); return result; } bool HostInterface::SaveState(const char* filename) { - ByteStream* stream; - if (!ByteStream_OpenFileStream(filename, - BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE | - BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED, - &stream)) - { + std::unique_ptr stream = + FileSystem::OpenFile(filename, BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE | + BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED); + if (!stream) return false; - } - const bool result = m_system->SaveState(stream); + const bool result = m_system->SaveState(stream.get()); if (!result) { - ReportError(SmallString::FromFormat("Saving state to %s failed.", filename)); + ReportFormattedError("Saving state to %s failed.", filename); stream->Discard(); } else { - AddOSDMessage(SmallString::FromFormat("State saved to %s.", filename)); + ReportFormattedError("State saved to %s.", filename); stream->Commit(); } - stream->Release(); return result; } diff --git a/src/core/host_interface.h b/src/core/host_interface.h index c0fa2e5d2..af63a9709 100644 --- a/src/core/host_interface.h +++ b/src/core/host_interface.h @@ -1,5 +1,5 @@ #pragma once -#include "YBaseLib/Timer.h" +#include "common/timer.h" #include "settings.h" #include "types.h" #include @@ -41,8 +41,12 @@ public: virtual void ReportError(const char* message); virtual void ReportMessage(const char* message); + void ReportFormattedError(const char* format, ...); + void ReportFormattedMessage(const char* format, ...); + /// Adds OSD messages, duration is in seconds. void AddOSDMessage(const char* message, float duration = 2.0f); + void AddFormattedOSDMessage(float duration, const char* format, ...); /// Loads the BIOS image for the specified region. virtual std::optional> GetBIOSImage(ConsoleRegion region); @@ -64,7 +68,7 @@ protected: struct OSDMessage { std::string text; - Timer time; + Common::Timer time; float duration; }; @@ -88,8 +92,8 @@ protected: u64 m_last_throttle_time = 0; s64 m_throttle_period = INT64_C(1000000000) / 60; - Timer m_throttle_timer; - Timer m_speed_lost_time_timestamp; + Common::Timer m_throttle_timer; + Common::Timer m_speed_lost_time_timestamp; float m_vps = 0.0f; float m_fps = 0.0f; @@ -97,7 +101,7 @@ protected: u32 m_last_frame_number = 0; u32 m_last_internal_frame_number = 0; u32 m_last_global_tick_counter = 0; - Timer m_fps_timer; + Common::Timer m_fps_timer; std::deque m_osd_messages; std::mutex m_osd_messages_lock; diff --git a/src/core/interrupt_controller.cpp b/src/core/interrupt_controller.cpp index 7b14753c1..0c03bb95b 100644 --- a/src/core/interrupt_controller.cpp +++ b/src/core/interrupt_controller.cpp @@ -1,5 +1,5 @@ #include "interrupt_controller.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "common/state_wrapper.h" #include "cpu_core.h" Log_SetChannel(InterruptController); diff --git a/src/core/mdec.cpp b/src/core/mdec.cpp index 81c641879..3dc2b3d79 100644 --- a/src/core/mdec.cpp +++ b/src/core/mdec.cpp @@ -1,5 +1,5 @@ #include "mdec.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "common/state_wrapper.h" #include "dma.h" #include "interrupt_controller.h" diff --git a/src/core/memory_card.cpp b/src/core/memory_card.cpp index 378d3ee45..a92915a9f 100644 --- a/src/core/memory_card.cpp +++ b/src/core/memory_card.cpp @@ -1,8 +1,7 @@ #include "memory_card.h" -#include "YBaseLib/AutoReleasePtr.h" -#include "YBaseLib/ByteStream.h" -#include "YBaseLib/FileSystem.h" -#include "YBaseLib/Log.h" +#include "common/byte_stream.h" +#include "common/file_system.h" +#include "common/log.h" #include "common/state_wrapper.h" #include "host_interface.h" #include "system.h" @@ -328,7 +327,7 @@ u8* MemoryCard::GetSectorPtr(u32 sector) bool MemoryCard::LoadFromFile() { - AutoReleasePtr stream = + std::unique_ptr stream = FileSystem::OpenFile(m_filename.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED); if (!stream) return false; @@ -348,7 +347,7 @@ bool MemoryCard::SaveToFile() if (m_filename.empty()) return false; - AutoReleasePtr stream = + std::unique_ptr stream = FileSystem::OpenFile(m_filename.c_str(), BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_TRUNCATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED); if (!stream) diff --git a/src/core/pad.cpp b/src/core/pad.cpp index 80308813b..8e3501739 100644 --- a/src/core/pad.cpp +++ b/src/core/pad.cpp @@ -1,5 +1,5 @@ #include "pad.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "common/state_wrapper.h" #include "controller.h" #include "host_interface.h" @@ -42,9 +42,9 @@ bool Pad::DoState(StateWrapper& sw) if (controller_type != state_controller_type) { - m_system->GetHostInterface()->AddOSDMessage(SmallString::FromFormat( - "Save state contains controller type %s in port %u, but %s is used. Switching.", - Settings::GetControllerTypeName(state_controller_type), i, Settings::GetControllerTypeName(controller_type))); + m_system->GetHostInterface()->AddFormattedOSDMessage( + 2.0f, "Save state contains controller type %s in port %u, but %s is used. Switching.", + Settings::GetControllerTypeName(state_controller_type), i, Settings::GetControllerTypeName(controller_type)); m_controllers[i].reset(); if (state_controller_type != ControllerType::None) @@ -62,20 +62,14 @@ bool Pad::DoState(StateWrapper& sw) if (card_present && !m_memory_cards[i]) { - const TinyString message = TinyString::FromFormat( - "Memory card %c present in save state but not in system. Creating temporary card.", 'A' + i); - m_system->GetHostInterface()->AddOSDMessage(message); - Log_WarningPrint(message); - + m_system->GetHostInterface()->AddFormattedOSDMessage( + 2.0f, "Memory card %c present in save state but not in system. Creating temporary card.", 'A' + i); m_memory_cards[i] = MemoryCard::Create(m_system); } else if (!card_present && m_memory_cards[i]) { - const TinyString message = - TinyString::FromFormat("Memory card %u present system but not save state. Removing card.", 'A' + i); - m_system->GetHostInterface()->AddOSDMessage(message); - Log_WarningPrint(message); - + m_system->GetHostInterface()->AddFormattedOSDMessage( + 2.0f, "Memory card %u present system but not save state. Removing card.", 'A' + i); m_memory_cards[i].reset(); } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 88d6d788d..b088c7aac 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -1,10 +1,6 @@ #include "settings.h" +#include "common/string_util.h" #include -#include - -#ifdef _MSC_VER -#define strcasecmp stricmp -#endif Settings::Settings() = default; @@ -132,7 +128,7 @@ std::optional Settings::ParseConsoleRegionName(const char* str) int index = 0; for (const char* name : s_console_region_names) { - if (strcasecmp(name, str) == 0) + if (StringUtil::Strcasecmp(name, str) == 0) return static_cast(index); index++; @@ -160,7 +156,7 @@ std::optional Settings::ParseCPUExecutionMode(const char* str) u8 index = 0; for (const char* name : s_cpu_execution_mode_names) { - if (strcasecmp(name, str) == 0) + if (StringUtil::Strcasecmp(name, str) == 0) return static_cast(index); index++; @@ -195,7 +191,7 @@ std::optional Settings::ParseRendererName(const char* str) int index = 0; for (const char* name : s_gpu_renderer_names) { - if (strcasecmp(name, str) == 0) + if (StringUtil::Strcasecmp(name, str) == 0) return static_cast(index); index++; @@ -222,7 +218,7 @@ std::optional Settings::ParseAudioBackend(const char* str) int index = 0; for (const char* name : s_audio_backend_names) { - if (strcasecmp(name, str) == 0) + if (StringUtil::Strcasecmp(name, str) == 0) return static_cast(index); index++; @@ -250,7 +246,7 @@ std::optional Settings::ParseControllerTypeName(const char* str) int index = 0; for (const char* name : s_controller_type_names) { - if (strcasecmp(name, str) == 0) + if (StringUtil::Strcasecmp(name, str) == 0) return static_cast(index); index++; diff --git a/src/core/sio.cpp b/src/core/sio.cpp index c521acdc1..75f5126fe 100644 --- a/src/core/sio.cpp +++ b/src/core/sio.cpp @@ -1,5 +1,5 @@ #include "sio.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "common/state_wrapper.h" #include "host_interface.h" #include "interrupt_controller.h" diff --git a/src/core/spu.cpp b/src/core/spu.cpp index c2c9465f4..a60d1441d 100644 --- a/src/core/spu.cpp +++ b/src/core/spu.cpp @@ -1,5 +1,5 @@ #include "spu.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "common/audio_stream.h" #include "common/state_wrapper.h" #include "dma.h" diff --git a/src/core/system.cpp b/src/core/system.cpp index 427f83c43..2c286669c 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -1,9 +1,8 @@ #include "system.h" -#include "YBaseLib/AutoReleasePtr.h" -#include "YBaseLib/Log.h" #include "bios.h" #include "bus.h" #include "cdrom.h" +#include "common/log.h" #include "common/state_wrapper.h" #include "controller.h" #include "cpu_code_cache.h" @@ -43,12 +42,6 @@ System::System(HostInterface* host_interface) : m_host_interface(host_interface) System::~System() = default; -bool System::IsPSExe(const char* filename) -{ - const StaticString filename_str(filename); - return filename_str.EndsWith(".psexe", false) || filename_str.EndsWith(".exe", false); -} - std::unique_ptr System::Create(HostInterface* host_interface) { std::unique_ptr system(new System(host_interface)); @@ -61,8 +54,8 @@ std::unique_ptr System::Create(HostInterface* host_interface) bool System::RecreateGPU() { // save current state - AutoReleasePtr state_stream = ByteStream_CreateGrowableMemoryStream(); - StateWrapper sw(state_stream, StateWrapper::Mode::Write); + std::unique_ptr state_stream = ByteStream_CreateGrowableMemoryStream(); + StateWrapper sw(state_stream.get(), StateWrapper::Mode::Write); const bool state_valid = m_gpu->DoState(sw); if (!state_valid) Log_ErrorPrintf("Failed to save old GPU state when switching renderers"); @@ -92,7 +85,7 @@ bool System::Boot(const char* filename) bool exe_boot = false; if (filename) { - exe_boot = IsPSExe(filename); + exe_boot = GameList::IsExeFileName(filename); if (exe_boot) { if (m_region == ConsoleRegion::Auto) @@ -107,7 +100,7 @@ bool System::Boot(const char* filename) media = CDImage::Open(filename); if (!media) { - m_host_interface->ReportError(SmallString::FromFormat("Failed to load CD image '%s'", filename)); + m_host_interface->ReportFormattedError("Failed to load CD image '%s'", filename); return false; } @@ -139,8 +132,7 @@ bool System::Boot(const char* filename) std::optional bios_image = m_host_interface->GetBIOSImage(m_region); if (!bios_image) { - m_host_interface->ReportError( - TinyString::FromFormat("Failed to load %s BIOS", Settings::GetConsoleRegionName(m_region))); + m_host_interface->ReportFormattedError("Failed to load %s BIOS", Settings::GetConsoleRegionName(m_region)); return false; } @@ -158,7 +150,7 @@ bool System::Boot(const char* filename) // Load EXE late after BIOS. if (exe_boot && !LoadEXE(filename, *bios_image)) { - m_host_interface->ReportError(SmallString::FromFormat("Failed to load EXE file '%s'", filename)); + m_host_interface->ReportFormattedError("Failed to load EXE file '%s'", filename); return false; } @@ -457,11 +449,13 @@ Controller* System::GetController(u32 slot) const void System::UpdateControllers() { const Settings& settings = m_host_interface->GetSettings(); - for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { + for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) + { m_pad->SetController(i, nullptr); const ControllerType type = settings.controller_types[i]; - if (type != ControllerType::None) { + if (type != ControllerType::None) + { std::unique_ptr controller = Controller::Create(type); if (controller) m_pad->SetController(i, std::move(controller)); diff --git a/src/core/system.h b/src/core/system.h index da2b8e84d..1f78664c4 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -30,9 +30,6 @@ class System public: ~System(); - /// Returns true if the filename is a PlayStation executable we can inject. - static bool IsPSExe(const char* filename); - /// Creates a new System. static std::unique_ptr Create(HostInterface* host_interface); diff --git a/src/core/timers.cpp b/src/core/timers.cpp index 8dfde9f19..00c9f493f 100644 --- a/src/core/timers.cpp +++ b/src/core/timers.cpp @@ -1,5 +1,5 @@ #include "timers.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include "common/state_wrapper.h" #include "interrupt_controller.h" #include "system.h" diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt index c69df1723..6b98f003d 100644 --- a/src/duckstation-qt/CMakeLists.txt +++ b/src/duckstation-qt/CMakeLists.txt @@ -12,6 +12,9 @@ add_executable(duckstation-qt gamelistsettingswidget.ui gamelistwidget.cpp gamelistwidget.h + gpusettingswidget.cpp + gpusettingswidget.h + gpusettingswidget.ui hotkeysettingswidget.cpp hotkeysettingswidget.h inputbindingwidgets.cpp @@ -24,8 +27,6 @@ add_executable(duckstation-qt opengldisplaywindow.h portsettingswidget.cpp portsettingswidget.h - qtaudiostream.cpp - qtaudiostream.h qtdisplaywindow.cpp qtdisplaywindow.h qthostinterface.cpp @@ -39,7 +40,7 @@ add_executable(duckstation-qt settingsdialog.ui ) -target_link_libraries(duckstation-qt PRIVATE YBaseLib core common imgui glad Qt5::Core Qt5::Gui Qt5::Widgets) +target_link_libraries(duckstation-qt PRIVATE core common imgui glad Qt5::Core Qt5::Gui Qt5::Widgets) if(WIN32) target_sources(duckstation-qt PRIVATE diff --git a/src/duckstation-qt/d3d11displaywindow.cpp b/src/duckstation-qt/d3d11displaywindow.cpp index a03aede2e..7f2cd2972 100644 --- a/src/duckstation-qt/d3d11displaywindow.cpp +++ b/src/duckstation-qt/d3d11displaywindow.cpp @@ -1,6 +1,7 @@ #include "d3d11displaywindow.h" -#include "YBaseLib/Log.h" +#include "common/assert.h" #include "common/d3d11/shader_compiler.h" +#include "common/log.h" #include #include #include diff --git a/src/duckstation-qt/d3d11displaywindow.h b/src/duckstation-qt/d3d11displaywindow.h index 57a355800..56e6bf865 100644 --- a/src/duckstation-qt/d3d11displaywindow.h +++ b/src/duckstation-qt/d3d11displaywindow.h @@ -1,5 +1,5 @@ #pragma once -#include "YBaseLib/Windows/WindowsHeaders.h" +#include "common/windows_headers.h" #include "common/d3d11/stream_buffer.h" #include "common/d3d11/texture.h" #include "core/host_display.h" diff --git a/src/duckstation-qt/duckstation-qt.vcxproj b/src/duckstation-qt/duckstation-qt.vcxproj index 0e1c20aea..f971d6d77 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj +++ b/src/duckstation-qt/duckstation-qt.vcxproj @@ -79,9 +79,6 @@ {bb08260f-6fbc-46af-8924-090ee71360c6} - - {b56ce698-7300-4fa5-9609-942f1d05c5a2} - {ee054e08-3799-4a59-a422-18259c105ffd} @@ -277,7 +274,7 @@ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -298,7 +295,7 @@ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -319,7 +316,7 @@ _ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories) Default true false @@ -342,7 +339,7 @@ _ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories) Default true false @@ -364,7 +361,7 @@ MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -386,7 +383,7 @@ MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x86\include;%(AdditionalIncludeDirectories) true true stdcpp17 @@ -410,7 +407,7 @@ MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -432,7 +429,7 @@ MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)src;$(SolutionDir)dep\msvc\qt5-x64\include;%(AdditionalIncludeDirectories) true true stdcpp17 diff --git a/src/duckstation-qt/main.cpp b/src/duckstation-qt/main.cpp index 835bc79c3..4df999202 100644 --- a/src/duckstation-qt/main.cpp +++ b/src/duckstation-qt/main.cpp @@ -1,4 +1,4 @@ -#include "YBaseLib/Log.h" +#include "common/log.h" #include "mainwindow.h" #include "qthostinterface.h" #include @@ -8,11 +8,11 @@ static void InitLogging() { // set log flags #ifdef Y_BUILD_CONFIG_DEBUG - g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); - g_pLog->SetFilterLevel(LOGLEVEL_DEBUG); + Log::SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); + Log::SetFilterLevel(LOGLEVEL_DEBUG); #else - g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_INFO); - g_pLog->SetFilterLevel(LOGLEVEL_INFO); + Log::SetConsoleOutputParams(true, nullptr, LOGLEVEL_INFO); + Log::SetFilterLevel(LOGLEVEL_INFO); #endif } diff --git a/src/duckstation-qt/opengldisplaywindow.cpp b/src/duckstation-qt/opengldisplaywindow.cpp index 76f123391..ebd7cdd03 100644 --- a/src/duckstation-qt/opengldisplaywindow.cpp +++ b/src/duckstation-qt/opengldisplaywindow.cpp @@ -1,6 +1,6 @@ #include "opengldisplaywindow.h" -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" +#include "common/assert.h" +#include "common/log.h" #include "imgui.h" #include "qthostinterface.h" #include @@ -21,7 +21,7 @@ static void* GetProcAddressCallback(const char* name) } #ifdef WIN32 -#include "YBaseLib/Windows/WindowsHeaders.h" +#include "common/windows_headers.h" #endif /// Changes the swap interval on a window. Since Qt doesn't expose this functionality, we need to change it manually diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index 6992173f3..53d1da168 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -1,9 +1,9 @@ #include "qthostinterface.h" -#include "YBaseLib/AutoReleasePtr.h" -#include "YBaseLib/ByteStream.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/String.h" +#include "common/assert.h" +#include "common/byte_stream.h" +#include "common/log.h" #include "common/null_audio_stream.h" +#include "common/string_util.h" #include "core/controller.h" #include "core/game_list.h" #include "core/gpu.h" @@ -284,12 +284,12 @@ void QtHostInterface::updateHotkeyInputMap() { hk(QStringLiteral("LoadState%1").arg(i), [this, i](bool pressed) { if (!pressed) - HostInterface::LoadState(TinyString::FromFormat("savestate_%u.bin", i)); + HostInterface::LoadState(StringUtil::StdStringFromFormat("savestate_%u.bin", i).c_str()); }); hk(QStringLiteral("SaveState%1").arg(i), [this, i](bool pressed) { if (!pressed) - HostInterface::SaveState(TinyString::FromFormat("savestate_%u.bin", i)); + HostInterface::SaveState(StringUtil::StdStringFromFormat("savestate_%u.bin", i).c_str()); }); } } @@ -390,9 +390,9 @@ void QtHostInterface::loadStateFromMemory(QByteArray arr) return; } - AutoReleasePtr stream = ByteStream_CreateGrowableMemoryStream(); - if (!m_system || !QtUtils::WriteQByteArrayToStream(arr, stream) || !stream->SeekAbsolute(0) || - !m_system->LoadState(stream)) + std::unique_ptr stream = ByteStream_CreateGrowableMemoryStream(); + if (!m_system || !QtUtils::WriteQByteArrayToStream(arr, stream.get()) || !stream->SeekAbsolute(0) || + !m_system->LoadState(stream.get())) { Log_ErrorPrintf("Failed to load memory state"); return; @@ -413,9 +413,9 @@ QByteArray QtHostInterface::saveStateToMemory() if (!m_system) return {}; - AutoReleasePtr stream = ByteStream_CreateGrowableMemoryStream(); - if (m_system->SaveState(stream)) - return QtUtils::ReadStreamToQByteArray(stream, true); + std::unique_ptr stream = ByteStream_CreateGrowableMemoryStream(); + if (m_system->SaveState(stream.get())) + return QtUtils::ReadStreamToQByteArray(stream.get(), true); else return {}; } diff --git a/src/duckstation-qt/qtutils.cpp b/src/duckstation-qt/qtutils.cpp index a85cd651c..ebdd5f58b 100644 --- a/src/duckstation-qt/qtutils.cpp +++ b/src/duckstation-qt/qtutils.cpp @@ -1,5 +1,5 @@ #include "qtutils.h" -#include "YBaseLib/ByteStream.h" +#include "common/byte_stream.h" #include #include #include @@ -583,13 +583,13 @@ int KeyEventToInt(const QKeyEvent* ke) QByteArray ReadStreamToQByteArray(ByteStream* stream, bool rewind /*= false*/) { QByteArray ret; - const uint64 old_pos = stream->GetPosition(); + const u64 old_pos = stream->GetPosition(); if (rewind && !stream->SeekAbsolute(0)) return {}; - const uint64 stream_size = stream->GetSize() - stream->GetPosition(); + const u64 stream_size = stream->GetSize() - stream->GetPosition(); ret.resize(static_cast(stream_size)); - if (stream_size > 0 && !stream->Read2(ret.data(), static_cast(stream_size), nullptr)) + if (stream_size > 0 && !stream->Read2(ret.data(), static_cast(stream_size), nullptr)) return {}; stream->SeekAbsolute(old_pos); @@ -598,7 +598,7 @@ QByteArray ReadStreamToQByteArray(ByteStream* stream, bool rewind /*= false*/) bool WriteQByteArrayToStream(QByteArray& arr, ByteStream* stream) { - return arr.isEmpty() || stream->Write2(arr.data(), static_cast(arr.size())); + return arr.isEmpty() || stream->Write2(arr.data(), static_cast(arr.size())); } } // namespace QtUtils \ No newline at end of file diff --git a/src/duckstation/CMakeLists.txt b/src/duckstation/CMakeLists.txt index 905ae7dbb..1385244a1 100644 --- a/src/duckstation/CMakeLists.txt +++ b/src/duckstation/CMakeLists.txt @@ -22,4 +22,4 @@ if(WIN32) target_link_libraries(duckstation PRIVATE d3d11.lib winmm.lib) endif() -target_link_libraries(duckstation PRIVATE YBaseLib core common imgui nativefiledialog glad simpleini SDL2::Main) \ No newline at end of file +target_link_libraries(duckstation PRIVATE core common imgui nativefiledialog glad simpleini SDL2::Main) diff --git a/src/duckstation/d3d11_host_display.cpp b/src/duckstation/d3d11_host_display.cpp index 4f5ed5640..52e9da6ed 100644 --- a/src/duckstation/d3d11_host_display.cpp +++ b/src/duckstation/d3d11_host_display.cpp @@ -1,6 +1,7 @@ #include "d3d11_host_display.h" -#include "YBaseLib/Log.h" +#include "common/assert.h" #include "common/d3d11/shader_compiler.h" +#include "common/log.h" #include #include #include diff --git a/src/duckstation/d3d11_host_display.h b/src/duckstation/d3d11_host_display.h index 64e6bf735..21f6f9088 100644 --- a/src/duckstation/d3d11_host_display.h +++ b/src/duckstation/d3d11_host_display.h @@ -1,7 +1,7 @@ #pragma once -#include "YBaseLib/Windows/WindowsHeaders.h" #include "common/d3d11/stream_buffer.h" #include "common/d3d11/texture.h" +#include "common/windows_headers.h" #include "core/host_display.h" #include #include diff --git a/src/duckstation/duckstation.vcxproj b/src/duckstation/duckstation.vcxproj index bd30ea886..a12f01415 100644 --- a/src/duckstation/duckstation.vcxproj +++ b/src/duckstation/duckstation.vcxproj @@ -41,9 +41,6 @@ {ace32f47-2960-4fb3-9f77-2c375625bf61} - - {b56ce698-7300-4fa5-9609-942f1d05c5a2} - {ee054e08-3799-4a59-a422-18259c105ffd} @@ -212,7 +209,7 @@ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -234,7 +231,7 @@ _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -256,7 +253,7 @@ _ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default true false @@ -281,7 +278,7 @@ _ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) Default true false @@ -305,7 +302,7 @@ MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -329,7 +326,7 @@ MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 @@ -354,7 +351,7 @@ MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true false stdcpp17 @@ -378,7 +375,7 @@ MaxSpeed true _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\YBaseLib\Include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) + $(SolutionDir)dep\msvc\include;$(SolutionDir)dep\msvc\include\SDL;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\nativefiledialog\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)src;%(AdditionalIncludeDirectories) true true stdcpp17 diff --git a/src/duckstation/main.cpp b/src/duckstation/main.cpp index dde7e60e1..9d42438ac 100644 --- a/src/duckstation/main.cpp +++ b/src/duckstation/main.cpp @@ -1,26 +1,10 @@ -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" -#include "YBaseLib/StringConverter.h" +#include "common/assert.h" +#include "common/log.h" #include "core/system.h" #include "sdl_host_interface.h" #include #include -#if 0 -static int NoGUITest() -{ - std::unique_ptr system = std::make_unique(); - if (!system->Initialize()) - return -1; - - system->Reset(); - - while (true) - system->RunFrame(); - return 0; -} -#endif - static int Run(int argc, char* argv[]) { // init sdl @@ -33,7 +17,7 @@ static int Run(int argc, char* argv[]) // parameters const char* filename = nullptr; const char* exp1_filename = nullptr; - TinyString state_filename; + std::string state_filename; for (int i = 1; i < argc; i++) { #define CHECK_ARG(str) !std::strcmp(argv[i], str) @@ -51,8 +35,8 @@ static int Run(int argc, char* argv[]) } // create display and host interface - std::unique_ptr host_interface = SDLHostInterface::Create( - filename, exp1_filename, state_filename.IsEmpty() ? nullptr : state_filename.GetCharArray()); + std::unique_ptr host_interface = + SDLHostInterface::Create(filename, exp1_filename, state_filename.empty() ? nullptr : state_filename.c_str()); if (!host_interface) { Panic("Failed to create host interface"); @@ -78,15 +62,15 @@ int main(int argc, char* argv[]) const LOGLEVEL level = LOGLEVEL_INFO; // const LOGLEVEL level = LOGLEVEL_DEV; // const LOGLEVEL level = LOGLEVEL_PROFILE; - g_pLog->SetConsoleOutputParams(true, nullptr, level); - g_pLog->SetFilterLevel(level); + Log::SetConsoleOutputParams(true, nullptr, level); + Log::SetFilterLevel(level); #else - g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); - // g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG); - // g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU + Log::SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); + // Log::SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG); + // Log::SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL Pad DigitalController MemoryCard InterruptController SPU // MDEC", LOGLEVEL_DEBUG); g_pLog->SetFilterLevel(LOGLEVEL_TRACE); - g_pLog->SetFilterLevel(LOGLEVEL_DEBUG); - // g_pLog->SetFilterLevel(LOGLEVEL_DEV); + Log::SetFilterLevel(LOGLEVEL_DEBUG); + // Log::SetFilterLevel(LOGLEVEL_DEV); #endif // return NoGUITest(); diff --git a/src/duckstation/opengl_host_display.cpp b/src/duckstation/opengl_host_display.cpp index 532ca80da..57ca189da 100644 --- a/src/duckstation/opengl_host_display.cpp +++ b/src/duckstation/opengl_host_display.cpp @@ -1,5 +1,6 @@ #include "opengl_host_display.h" -#include "YBaseLib/Log.h" +#include "common/assert.h" +#include "common/log.h" #include #include #include diff --git a/src/duckstation/sdl_audio_stream.cpp b/src/duckstation/sdl_audio_stream.cpp index 9f3c3724a..96987df1f 100644 --- a/src/duckstation/sdl_audio_stream.cpp +++ b/src/duckstation/sdl_audio_stream.cpp @@ -1,6 +1,6 @@ #include "sdl_audio_stream.h" -#include "YBaseLib/Assert.h" -#include "YBaseLib/Log.h" +#include "common/assert.h" +#include "common/log.h" #include Log_SetChannel(SDLAudioStream); diff --git a/src/duckstation/sdl_host_interface.cpp b/src/duckstation/sdl_host_interface.cpp index 7168727de..6bc50d263 100644 --- a/src/duckstation/sdl_host_interface.cpp +++ b/src/duckstation/sdl_host_interface.cpp @@ -1,9 +1,9 @@ #include "sdl_host_interface.h" -#include "YBaseLib/AutoReleasePtr.h" -#include "YBaseLib/ByteStream.h" -#include "YBaseLib/Error.h" -#include "YBaseLib/Log.h" +#include "common/assert.h" +#include "common/byte_stream.h" +#include "common/log.h" #include "common/null_audio_stream.h" +#include "common/string_util.h" #include "core/controller.h" #include "core/gpu.h" #include "core/host_display.h" @@ -20,8 +20,8 @@ #include Log_SetChannel(SDLHostInterface); -#ifdef Y_PLATFORM_WINDOWS -#include "YBaseLib/Windows/WindowsHeaders.h" +#ifdef WIN32 +#include "common/windows_headers.h" #include "d3d11_host_display.h" #include #endif @@ -29,7 +29,7 @@ Log_SetChannel(SDLHostInterface); SDLHostInterface::SDLHostInterface() : m_settings_filename("settings.ini") { // Increase timer/sleep resolution since we use it for throttling. -#ifdef Y_PLATFORM_WINDOWS +#ifdef WIN32 timeBeginPeriod(1); #endif @@ -45,7 +45,7 @@ SDLHostInterface::~SDLHostInterface() if (m_window) SDL_DestroyWindow(m_window); -#ifdef Y_PLATFORM_WINDOWS +#ifdef WIN32 timeEndPeriod(1); #endif } @@ -162,11 +162,11 @@ void SDLHostInterface::QueueSwitchGPURenderer() void SDLHostInterface::SwitchGPURenderer() { // Due to the GPU class owning textures, we have to shut the system down. - AutoReleasePtr stream; + std::unique_ptr stream; if (m_system) { stream = ByteStream_CreateGrowableMemoryStream(nullptr, 8 * 1024); - if (!m_system->SaveState(stream) || !stream->SeekAbsolute(0)) + if (!m_system->SaveState(stream.get()) || !stream->SeekAbsolute(0)) ReportError("Failed to save state before GPU renderer switch"); DestroySystem(); @@ -187,7 +187,7 @@ void SDLHostInterface::SwitchGPURenderer() if (stream) { CreateSystem(); - if (!BootSystem(nullptr, nullptr) || !m_system->LoadState(stream)) + if (!BootSystem(nullptr, nullptr) || !m_system->LoadState(stream.get())) { ReportError("Failed to load state after GPU renderer switch, resetting"); m_system->Reset(); @@ -272,9 +272,9 @@ std::unique_ptr SDLHostInterface::Create(const char* filename return intf; } -TinyString SDLHostInterface::GetSaveStateFilename(u32 index) +std::string SDLHostInterface::GetSaveStateFilename(u32 index) { - return TinyString::FromFormat("savestate_%u.bin", index); + return StringUtil::StdStringFromFormat("savestate_%u.bin", index); } void SDLHostInterface::ReportError(const char* message) @@ -816,7 +816,9 @@ void SDLHostInterface::DrawMainMenuBar() { for (u32 i = 1; i <= NUM_QUICK_SAVE_STATES; i++) { - if (ImGui::MenuItem(TinyString::FromFormat("State %u", i).GetCharArray())) + char buf[16]; + std::snprintf(buf, sizeof(buf), "State %u", i); + if (ImGui::MenuItem(buf)) DoLoadState(i); } ImGui::EndMenu(); @@ -826,7 +828,9 @@ void SDLHostInterface::DrawMainMenuBar() { for (u32 i = 1; i <= NUM_QUICK_SAVE_STATES; i++) { - if (ImGui::MenuItem(TinyString::FromFormat("State %u", i).GetCharArray())) + char buf[16]; + std::snprintf(buf, sizeof(buf), "State %u", i); + if (ImGui::MenuItem(buf)) DoSaveState(i); } ImGui::EndMenu(); @@ -971,9 +975,10 @@ void SDLHostInterface::DrawQuickSettingsMenu() const u32 current_internal_resolution = m_settings.gpu_resolution_scale; for (u32 scale = 1; scale <= m_settings.max_gpu_resolution_scale; scale++) { - if (ImGui::MenuItem( - TinyString::FromFormat("%ux (%ux%u)", scale, scale * GPU::VRAM_WIDTH, scale * GPU::VRAM_HEIGHT), nullptr, - current_internal_resolution == scale)) + char buf[32]; + std::snprintf(buf, sizeof(buf), "%ux (%ux%u)", scale, scale * GPU::VRAM_WIDTH, scale * GPU::VRAM_HEIGHT); + + if (ImGui::MenuItem(buf, nullptr, current_internal_resolution == scale)) { m_settings.gpu_resolution_scale = scale; gpu_settings_changed = true; @@ -1078,7 +1083,9 @@ void SDLHostInterface::DrawPoweredOffWindow() { for (u32 i = 1; i <= NUM_QUICK_SAVE_STATES; i++) { - if (ImGui::MenuItem(TinyString::FromFormat("State %u", i).GetCharArray())) + char buf[16]; + std::snprintf(buf, sizeof(buf), "State %u", i); + if (ImGui::MenuItem(buf)) DoLoadState(i); } ImGui::EndPopup(); @@ -1200,7 +1207,10 @@ void SDLHostInterface::DrawSettingsWindow() { for (int i = 0; i < 2; i++) { - if (DrawSettingsSectionHeader(TinyString::FromFormat("Front Port %d", 1 + i))) + char buf[32]; + std::snprintf(buf, sizeof(buf), "Front Port %d", 1 + i); + + if (DrawSettingsSectionHeader(buf)) { ImGui::Text("Controller:"); ImGui::SameLine(indent); @@ -1228,7 +1238,8 @@ void SDLHostInterface::DrawSettingsWindow() ImGui::SameLine(indent); std::string* path_ptr = &m_settings.memory_card_paths[i]; - if (DrawFileChooser(TinyString::FromFormat("##memcard_%c_path", 'a' + i), path_ptr)) + std::snprintf(buf, sizeof(buf), "##memcard_%c_path", 'a' + i); + if (DrawFileChooser(buf, path_ptr)) { settings_changed = true; if (m_system) @@ -1456,7 +1467,7 @@ void SDLHostInterface::DoStartDisc() if (!NFD_OpenDialog("bin,img,cue,exe,psexe", nullptr, &path) || !path || std::strlen(path) == 0) return; - AddOSDMessage(SmallString::FromFormat("Starting disc from '%s'...", path)); + AddFormattedOSDMessage(2.0f, "Starting disc from '%s'...", path); if (!CreateSystem() || !BootSystem(path, nullptr)) { DestroySystem(); @@ -1493,7 +1504,7 @@ void SDLHostInterface::DoChangeDisc() return; if (m_system->InsertMedia(path)) - AddOSDMessage(SmallString::FromFormat("Switched CD to '%s'", path)); + AddFormattedOSDMessage(2.0f, "Switched CD to '%s'", path); else AddOSDMessage("Failed to switch CD. The log may contain further information."); @@ -1505,11 +1516,11 @@ void SDLHostInterface::DoLoadState(u32 index) { if (HasSystem()) { - LoadState(GetSaveStateFilename(index)); + LoadState(GetSaveStateFilename(index).c_str()); } else { - if (!CreateSystem() || !BootSystem(nullptr, GetSaveStateFilename(index))) + if (!CreateSystem() || !BootSystem(nullptr, GetSaveStateFilename(index).c_str())) { DestroySystem(); return; @@ -1524,7 +1535,7 @@ void SDLHostInterface::DoLoadState(u32 index) void SDLHostInterface::DoSaveState(u32 index) { Assert(m_system); - SaveState(GetSaveStateFilename(index)); + SaveState(GetSaveStateFilename(index).c_str()); ClearImGuiFocus(); } @@ -1590,9 +1601,9 @@ void SDLHostInterface::DoModifyInternalResolution(s32 increment) if (m_system) m_system->GetGPU()->UpdateSettings(); - AddOSDMessage(TinyString::FromFormat("Resolution scale set to %ux (%ux%u)", m_settings.gpu_resolution_scale, - GPU::VRAM_WIDTH * m_settings.gpu_resolution_scale, - GPU::VRAM_HEIGHT * m_settings.gpu_resolution_scale)); + AddFormattedOSDMessage(2.0f, "Resolution scale set to %ux (%ux%u)", m_settings.gpu_resolution_scale, + GPU::VRAM_WIDTH * m_settings.gpu_resolution_scale, + GPU::VRAM_HEIGHT * m_settings.gpu_resolution_scale); } void SDLHostInterface::Run() diff --git a/src/duckstation/sdl_host_interface.h b/src/duckstation/sdl_host_interface.h index b546da092..b66c3f3e9 100644 --- a/src/duckstation/sdl_host_interface.h +++ b/src/duckstation/sdl_host_interface.h @@ -1,6 +1,4 @@ #pragma once -#include "YBaseLib/String.h" -#include "YBaseLib/Timer.h" #include "common/gl/program.h" #include "common/gl/texture.h" #include "core/host_display.h" @@ -11,6 +9,7 @@ #include #include #include +#include class System; class AudioStream; @@ -26,7 +25,7 @@ public: static std::unique_ptr Create(const char* filename = nullptr, const char* exp1_filename = nullptr, const char* save_state_filename = nullptr); - static TinyString GetSaveStateFilename(u32 index); + static std::string GetSaveStateFilename(u32 index); void ReportError(const char* message) override; void ReportMessage(const char* message) override; diff --git a/src/duckstation/sdl_settings_interface.cpp b/src/duckstation/sdl_settings_interface.cpp index 5cbd8afca..0d3b32977 100644 --- a/src/duckstation/sdl_settings_interface.cpp +++ b/src/duckstation/sdl_settings_interface.cpp @@ -1,5 +1,5 @@ #include "sdl_settings_interface.h" -#include "YBaseLib/Log.h" +#include "common/log.h" #include Log_SetChannel(SDLSettingsInterface);