diff options
Diffstat (limited to 'src/common')
27 files changed, 831 insertions, 1547 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 8c87deaa4..daa2d59de 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -4,8 +4,6 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU set(SRCS break_points.cpp emu_window.cpp - extended_trace.cpp - file_search.cpp file_util.cpp hash.cpp key_map.cpp @@ -16,13 +14,12 @@ set(SRCS mem_arena.cpp memory_util.cpp misc.cpp - msg_handler.cpp + profiler.cpp scm_rev.cpp string_util.cpp symbols.cpp thread.cpp timer.cpp - utf8.cpp ) set(HEADERS @@ -38,9 +35,7 @@ set(HEADERS cpu_detect.h debug_interface.h emu_window.h - extended_trace.h fifo_queue.h - file_search.h file_util.h hash.h key_map.h @@ -53,18 +48,19 @@ set(HEADERS math_util.h mem_arena.h memory_util.h - msg_handler.h platform.h + profiler.h + profiler_reporting.h scm_rev.h scope_exit.h string_util.h swap.h symbols.h + synchronized_wrapper.h thread.h thread_queue_list.h thunk.h timer.h - utf8.h ) create_directory_groups(${SRCS} ${HEADERS}) diff --git a/src/common/chunk_file.h b/src/common/chunk_file.h index dc27da088..3f97d56bf 100644 --- a/src/common/chunk_file.h +++ b/src/common/chunk_file.h @@ -637,7 +637,7 @@ public: Do(cookie); if(mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) { - PanicAlertT("Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); + LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); SetError(ERROR_FAILURE); } } diff --git a/src/common/common.h b/src/common/common.h index ad2de6f2e..f7d0f55c5 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -28,7 +28,6 @@ private: #include "common/assert.h" #include "common/logging/log.h" #include "common/common_types.h" -#include "common/msg_handler.h" #include "common/common_funcs.h" #include "common/common_paths.h" #include "common/platform.h" @@ -36,13 +35,11 @@ private: #ifdef __APPLE__ // The Darwin ABI requires that stack frames be aligned to 16-byte boundaries. // This is only needed on i386 gcc - x86_64 already aligns to 16 bytes. -#if defined __i386__ && defined __GNUC__ -#undef STACKALIGN -#define STACKALIGN __attribute__((__force_align_arg_pointer__)) -#endif - + #if defined __i386__ && defined __GNUC__ + #undef STACKALIGN + #define STACKALIGN __attribute__((__force_align_arg_pointer__)) + #endif #elif defined _WIN32 - // Check MSC ver #if defined _MSC_VER && _MSC_VER <= 1000 #error needs at least version 1000 of MSC @@ -52,9 +49,6 @@ private: #define NOMINMAX #endif -// Memory leak checks - #define CHECK_HEAP_INTEGRITY() - // Alignment #define MEMORY_ALIGNED16(x) __declspec(align(16)) x #define MEMORY_ALIGNED32(x) __declspec(align(32)) x @@ -62,57 +56,34 @@ private: #define MEMORY_ALIGNED128(x) __declspec(align(128)) x #define MEMORY_ALIGNED16_DECL(x) __declspec(align(16)) x #define MEMORY_ALIGNED64_DECL(x) __declspec(align(64)) x - -// Since they are always around on windows - #define HAVE_WX 1 - #define HAVE_OPENAL 1 - - #define HAVE_PORTAUDIO 1 - -// Debug definitions - #if defined(_DEBUG) - #include <crtdbg.h> - #undef CHECK_HEAP_INTEGRITY - #define CHECK_HEAP_INTEGRITY() {if (!_CrtCheckMemory()) PanicAlert("memory corruption detected. see log.");} - // If you want to see how much a pain in the ass singletons are, for example: - // {614} normal block at 0x030C5310, 188 bytes long. - // Data: <Master Log > 4D 61 73 74 65 72 20 4C 6F 67 00 00 00 00 00 00 - struct CrtDebugBreak { CrtDebugBreak(int spot) { _CrtSetBreakAlloc(spot); } }; - //CrtDebugBreak breakAt(614); - #endif // end DEBUG/FAST - #endif // Windows compatibility #ifndef _WIN32 -#ifdef _LP64 -#define _M_X64 1 -#else -#define _M_IX86 1 -#endif -#define __forceinline inline __attribute__((always_inline)) -#define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x -#define MEMORY_ALIGNED32(x) __attribute__((aligned(32))) x -#define MEMORY_ALIGNED64(x) __attribute__((aligned(64))) x -#define MEMORY_ALIGNED128(x) __attribute__((aligned(128))) x -#define MEMORY_ALIGNED16_DECL(x) __attribute__((aligned(16))) x -#define MEMORY_ALIGNED64_DECL(x) __attribute__((aligned(64))) x + #ifdef _LP64 + #define _M_X64 1 + #else + #define _M_IX86 1 + #endif + #define __forceinline inline __attribute__((always_inline)) + #define MEMORY_ALIGNED16(x) __attribute__((aligned(16))) x + #define MEMORY_ALIGNED32(x) __attribute__((aligned(32))) x + #define MEMORY_ALIGNED64(x) __attribute__((aligned(64))) x + #define MEMORY_ALIGNED128(x) __attribute__((aligned(128))) x + #define MEMORY_ALIGNED16_DECL(x) __attribute__((aligned(16))) x + #define MEMORY_ALIGNED64_DECL(x) __attribute__((aligned(64))) x #endif #ifdef _MSC_VER -#define __strdup _strdup -#define __getcwd _getcwd -#define __chdir _chdir + #define __strdup _strdup + #define __getcwd _getcwd + #define __chdir _chdir #else -#define __strdup strdup -#define __getcwd getcwd -#define __chdir chdir + #define __strdup strdup + #define __getcwd getcwd + #define __chdir chdir #endif -// Dummy macro for marking translatable strings that can not be immediately translated. -// wxWidgets does not have a true dummy macro for this. -#define _trans(a) a - #if defined _M_GENERIC # define _M_SSE 0x0 #elif defined __GNUC__ @@ -146,40 +117,4 @@ enum EMUSTATE_CHANGE EMUSTATE_CHANGE_STOP }; - -#ifdef _MSC_VER -inline unsigned long long bswap64(unsigned long long x) { return _byteswap_uint64(x); } -inline unsigned int bswap32(unsigned int x) { return _byteswap_ulong(x); } -inline unsigned short bswap16(unsigned short x) { return _byteswap_ushort(x); } -#else -// TODO: speedup -inline unsigned short bswap16(unsigned short x) { return (x << 8) | (x >> 8); } -inline unsigned int bswap32(unsigned int x) { return (x >> 24) | ((x & 0xFF0000) >> 8) | ((x & 0xFF00) << 8) | (x << 24);} -inline unsigned long long bswap64(unsigned long long x) {return ((unsigned long long)bswap32(x) << 32) | bswap32(x >> 32); } -#endif - -inline float bswapf(float f) { - union { - float f; - unsigned int u32; - } dat1, dat2; - - dat1.f = f; - dat2.u32 = bswap32(dat1.u32); - - return dat2.f; -} - -inline double bswapd(double f) { - union { - double f; - unsigned long long u64; - } dat1, dat2; - - dat1.f = f; - dat2.u64 = bswap64(dat1.u64); - - return dat2.f; -} - #include "swap.h" diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 44d8ae11f..e76cb7d68 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -14,8 +14,6 @@ #define SLEEP(x) usleep(x*1000) #endif -template <bool> struct CompileTimeAssert; -template<> struct CompileTimeAssert<true> {}; #define b2(x) ( (x) | ( (x) >> 1) ) #define b4(x) ( b2(x) | ( b2(x) >> 2) ) @@ -24,25 +22,21 @@ template<> struct CompileTimeAssert<true> {}; #define b32(x) (b16(x) | (b16(x) >>16) ) #define ROUND_UP_POW2(x) (b32(x - 1) + 1) -#define MIN(a, b) ((a)<(b)?(a):(b)) -#define MAX(a, b) ((a)>(b)?(a):(b)) - -#define CLAMP(x, min, max) (((x) > max) ? max : (((x) < min) ? min : (x))) - #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) /// Textually concatenates two tokens. The double-expansion is required by the C preprocessor. #define CONCAT2(x, y) DO_CONCAT2(x, y) #define DO_CONCAT2(x, y) x ## y +// helper macro to properly align structure members. +// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121", +// depending on the current source line to make sure variable names are unique. +#define INSERT_PADDING_BYTES(num_bytes) u8 CONCAT2(pad, __LINE__)[(num_bytes)] +#define INSERT_PADDING_WORDS(num_words) u32 CONCAT2(pad, __LINE__)[(num_words)] + #ifndef _MSC_VER #include <errno.h> -#ifdef __linux__ -#include <byteswap.h> -#elif defined __FreeBSD__ -#include <sys/endian.h> -#endif #if defined(__x86_64__) || defined(_M_X64) #define Crash() __asm__ __volatile__("int $3") @@ -141,98 +135,8 @@ inline u64 _rotr64(u64 x, unsigned int shift){ #define Crash() {DebugBreak();} #endif // _MSC_VER ndef -// Dolphin's min and max functions -#undef min -#undef max - -template<class T> -inline T min(const T& a, const T& b) {return a > b ? b : a;} -template<class T> -inline T max(const T& a, const T& b) {return a > b ? a : b;} - // Generic function to get last error message. // Call directly after the command or use the error num. // This function might change the error code. // Defined in Misc.cpp. const char* GetLastErrorMsg(); - -namespace Common -{ -inline u8 swap8(u8 _data) {return _data;} -inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];} - -#ifdef ANDROID -#undef swap16 -#undef swap32 -#undef swap64 -#endif - -#ifdef _MSC_VER -inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);} -inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);} -inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);} -#elif _M_ARM -inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;} -inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;} -inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);} -#elif __linux__ -inline u16 swap16(u16 _data) {return bswap_16(_data);} -inline u32 swap32(u32 _data) {return bswap_32(_data);} -inline u64 swap64(u64 _data) {return bswap_64(_data);} -#elif __APPLE__ -inline __attribute__((always_inline)) u16 swap16(u16 _data) - {return (_data >> 8) | (_data << 8);} -inline __attribute__((always_inline)) u32 swap32(u32 _data) - {return __builtin_bswap32(_data);} -inline __attribute__((always_inline)) u64 swap64(u64 _data) - {return __builtin_bswap64(_data);} -#elif __FreeBSD__ -inline u16 swap16(u16 _data) {return bswap16(_data);} -inline u32 swap32(u32 _data) {return bswap32(_data);} -inline u64 swap64(u64 _data) {return bswap64(_data);} -#else -// Slow generic implementation. -inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);} -inline u32 swap32(u32 data) {return (swap16(data) << 16) | swap16(data >> 16);} -inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 32);} -#endif - -inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);} -inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);} -inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);} - -template <int count> -void swap(u8*); - -template <> -inline void swap<1>(u8* data) -{} - -template <> -inline void swap<2>(u8* data) -{ - *reinterpret_cast<u16*>(data) = swap16(data); -} - -template <> -inline void swap<4>(u8* data) -{ - *reinterpret_cast<u32*>(data) = swap32(data); -} - -template <> -inline void swap<8>(u8* data) -{ - *reinterpret_cast<u64*>(data) = swap64(data); -} - -template <typename T> -inline T FromBigEndian(T data) -{ - //static_assert(std::is_arithmetic<T>::value, "function only makes sense with arithmetic types"); - - swap<sizeof(data)>(reinterpret_cast<u8*>(&data)); - return data; -} - -} // Namespace Common diff --git a/src/common/common_paths.h b/src/common/common_paths.h index 0ecf2d9de..eb43d589f 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h @@ -25,7 +25,7 @@ #ifdef USER_DIR #define EMU_DATA_DIR USER_DIR #else - #define EMU_DATA_DIR ".citra-emu" + #define EMU_DATA_DIR "citra-emu" #endif #endif diff --git a/src/common/emu_window.cpp b/src/common/emu_window.cpp index 48bb35db5..6459d2f32 100644 --- a/src/common/emu_window.cpp +++ b/src/common/emu_window.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include "emu_window.h" +#include "video_core/video_core.h" void EmuWindow::KeyPressed(KeyMap::HostDeviceKey key) { Service::HID::PadState mapped_key = KeyMap::GetPadKey(key); @@ -15,3 +16,52 @@ void EmuWindow::KeyReleased(KeyMap::HostDeviceKey key) { Service::HID::PadButtonRelease(mapped_key); } + +EmuWindow::FramebufferLayout EmuWindow::FramebufferLayout::DefaultScreenLayout(unsigned width, unsigned height) { + ASSERT(width > 0); + ASSERT(height > 0); + + EmuWindow::FramebufferLayout res = { width, height, {}, {} }; + + float window_aspect_ratio = static_cast<float>(height) / width; + float emulation_aspect_ratio = static_cast<float>(VideoCore::kScreenTopHeight * 2) / + VideoCore::kScreenTopWidth; + + if (window_aspect_ratio > emulation_aspect_ratio) { + // Window is narrower than the emulation content => apply borders to the top and bottom + int viewport_height = static_cast<int>(std::round(emulation_aspect_ratio * width)); + + res.top_screen.left = 0; + res.top_screen.right = res.top_screen.left + width; + res.top_screen.top = (height - viewport_height) / 2; + res.top_screen.bottom = res.top_screen.top + viewport_height / 2; + + int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) / + VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left)); + int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; + + res.bottom_screen.left = bottom_border; + res.bottom_screen.right = res.bottom_screen.left + bottom_width; + res.bottom_screen.top = res.top_screen.bottom; + res.bottom_screen.bottom = res.bottom_screen.top + viewport_height / 2; + } else { + // Otherwise, apply borders to the left and right sides of the window. + int viewport_width = static_cast<int>(std::round(height / emulation_aspect_ratio)); + + res.top_screen.left = (width - viewport_width) / 2; + res.top_screen.right = res.top_screen.left + viewport_width; + res.top_screen.top = 0; + res.top_screen.bottom = res.top_screen.top + height / 2; + + int bottom_width = static_cast<int>((static_cast<float>(VideoCore::kScreenBottomWidth) / + VideoCore::kScreenTopWidth) * (res.top_screen.right - res.top_screen.left)); + int bottom_border = ((res.top_screen.right - res.top_screen.left) - bottom_width) / 2; + + res.bottom_screen.left = res.top_screen.left + bottom_border; + res.bottom_screen.right = res.bottom_screen.left + bottom_width; + res.bottom_screen.top = res.top_screen.bottom; + res.bottom_screen.bottom = res.bottom_screen.top + height / 2; + } + + return res; +} diff --git a/src/common/emu_window.h b/src/common/emu_window.h index 1ad4b82a3..f6099fdb6 100644 --- a/src/common/emu_window.h +++ b/src/common/emu_window.h @@ -8,6 +8,7 @@ #include "common/scm_rev.h" #include "common/string_util.h" #include "common/key_map.h" +#include "common/math_util.h" /** * Abstraction class used to provide an interface between emulation code and the frontend @@ -38,6 +39,23 @@ public: std::pair<unsigned,unsigned> min_client_area_size; }; + /// Describes the layout of the window framebuffer (size and top/bottom screen positions) + struct FramebufferLayout { + + /** + * Factory method for constructing a default FramebufferLayout + * @param width Window framebuffer width in pixels + * @param height Window framebuffer height in pixels + * @return Newly created FramebufferLayout object with default screen regions initialized + */ + static FramebufferLayout DefaultScreenLayout(unsigned width, unsigned height); + + unsigned width; + unsigned height; + MathUtil::Rectangle<unsigned> top_screen; + MathUtil::Rectangle<unsigned> bottom_screen; + }; + /// Swap buffers to display the next frame virtual void SwapBuffers() = 0; @@ -75,11 +93,11 @@ public: } /** - * Gets the framebuffer size in pixels. + * Gets the framebuffer layout (width, height, and screen regions) * @note This method is thread-safe */ - const std::pair<unsigned,unsigned> GetFramebufferSize() const { - return framebuffer_size; + const FramebufferLayout& GetFramebufferLayout() const { + return framebuffer_layout; } /** @@ -118,11 +136,11 @@ protected: } /** - * Update internal framebuffer size with the given parameter. + * Update framebuffer layout with the given parameter. * @note EmuWindow implementations will usually use this in window resize event handlers. */ - void NotifyFramebufferSizeChanged(const std::pair<unsigned,unsigned>& size) { - framebuffer_size = size; + void NotifyFramebufferLayoutChanged(const FramebufferLayout& layout) { + framebuffer_layout = layout; } /** @@ -143,7 +161,7 @@ private: // By default, ignore this request and do nothing. } - std::pair<unsigned,unsigned> framebuffer_size; + FramebufferLayout framebuffer_layout; ///< Current framebuffer layout unsigned client_area_width; ///< Current client width, should be set by window impl. unsigned client_area_height; ///< Current client height, should be set by window impl. diff --git a/src/common/extended_trace.cpp b/src/common/extended_trace.cpp deleted file mode 100644 index cf7c346d4..000000000 --- a/src/common/extended_trace.cpp +++ /dev/null @@ -1,428 +0,0 @@ -// -------------------------------------------------------------------------------------- -// -// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com -// For companies(Austin,TX): If you would like to get my resume, send an email. -// -// The source is free, but if you want to use it, mention my name and e-mail address -// -// History: -// 1.0 Initial version Zoltan Csizmadia -// 1.1 WhineCube version Masken -// 1.2 Dolphin version Masken -// -// -------------------------------------------------------------------------------------- - -#if defined(WIN32) -#include <cstdio> -#include <windows.h> -#include "common/extended_trace.h" -#include "common/string_util.h" -using namespace std; - -#include <tchar.h> -#include <ImageHlp.h> - -#define BUFFERSIZE 0x200 -#pragma warning(disable:4996) - -// Unicode safe char* -> TCHAR* conversion -void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut ) -{ -#if defined(UNICODE)||defined(_UNICODE) - ULONG index = 0; - PCSTR lpAct = lpszIn; - - for( ; ; lpAct++ ) - { - lpszOut[index++] = (TCHAR)(*lpAct); - if ( *lpAct == 0 ) - break; - } -#else - // This is trivial :) - strcpy( lpszOut, lpszIn ); -#endif -} - -// Let's figure out the path for the symbol files -// Search path= ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + lpszIniPath -// Note: There is no size check for lpszSymbolPath! -static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath ) -{ - CHAR lpszPath[BUFFERSIZE]; - - // Creating the default path - // ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" - strcpy( lpszSymbolPath, "." ); - - // environment variable _NT_SYMBOL_PATH - if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE ) ) - { - strcat( lpszSymbolPath, ";" ); - strcat( lpszSymbolPath, lpszPath ); - } - - // environment variable _NT_ALTERNATE_SYMBOL_PATH - if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", lpszPath, BUFFERSIZE ) ) - { - strcat( lpszSymbolPath, ";" ); - strcat( lpszSymbolPath, lpszPath ); - } - - // environment variable SYSTEMROOT - if ( GetEnvironmentVariableA( "SYSTEMROOT", lpszPath, BUFFERSIZE ) ) - { - strcat( lpszSymbolPath, ";" ); - strcat( lpszSymbolPath, lpszPath ); - strcat( lpszSymbolPath, ";" ); - - // SYSTEMROOT\System32 - strcat( lpszSymbolPath, lpszPath ); - strcat( lpszSymbolPath, "\\System32" ); - } - - // Add user defined path - if ( lpszIniPath != nullptr ) - if ( lpszIniPath[0] != '\0' ) - { - strcat( lpszSymbolPath, ";" ); - strcat( lpszSymbolPath, lpszIniPath ); - } -} - -// Uninitialize the loaded symbol files -BOOL UninitSymInfo() { - return SymCleanup( GetCurrentProcess() ); -} - -// Initializes the symbol files -BOOL InitSymInfo( PCSTR lpszInitialSymbolPath ) -{ - CHAR lpszSymbolPath[BUFFERSIZE]; - DWORD symOptions = SymGetOptions(); - - symOptions |= SYMOPT_LOAD_LINES; - symOptions &= ~SYMOPT_UNDNAME; - SymSetOptions( symOptions ); - InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath ); - - return SymInitialize( GetCurrentProcess(), lpszSymbolPath, TRUE); -} - -// Get the module name from a given address -static BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule ) -{ - BOOL ret = FALSE; - IMAGEHLP_MODULE moduleInfo; - - ::ZeroMemory( &moduleInfo, sizeof(moduleInfo) ); - moduleInfo.SizeOfStruct = sizeof(moduleInfo); - - if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) ) - { - // Got it! - PCSTR2LPTSTR( moduleInfo.ModuleName, lpszModule ); - ret = TRUE; - } - else - // Not found :( - _tcscpy( lpszModule, _T("?") ); - - return ret; -} - -// Get function prototype and parameter info from ip address and stack address -static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol ) -{ - BOOL ret = FALSE; - DWORD dwSymSize = 10000; - TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?"); - CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; - LPTSTR lpszParamSep = nullptr; - LPTSTR lpszParsed = lpszUnDSymbol; - PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize ); - - ::ZeroMemory( pSym, dwSymSize ); - pSym->SizeOfStruct = dwSymSize; - pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL); - - // Set the default to unknown - _tcscpy( lpszSymbol, _T("?") ); - - // Get symbol info for IP -#ifndef _M_X64 - DWORD dwDisp = 0; - if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) ) -#else - //makes it compile but hell im not sure if this works... - DWORD64 dwDisp = 0; - if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) ) -#endif - { - // Make the symbol readable for humans - UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, - UNDNAME_COMPLETE | - UNDNAME_NO_THISTYPE | - UNDNAME_NO_SPECIAL_SYMS | - UNDNAME_NO_MEMBER_TYPE | - UNDNAME_NO_MS_KEYWORDS | - UNDNAME_NO_ACCESS_SPECIFIERS ); - - // Symbol information is ANSI string - PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol ); - - // I am just smarter than the symbol file :) - if (_tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0) - _tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)")); - else if (_tcscmp(lpszUnDSymbol, _T("_main")) == 0) - _tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)")); - else if (_tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0) - _tcscpy(lpszUnDSymbol, _T("mainCRTStartup()")); - else if (_tcscmp(lpszUnDSymbol, _T("_wmain")) == 0) - _tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)")); - else if (_tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0) - _tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()")); - - lpszSymbol[0] = _T('\0'); - - // Let's go through the stack, and modify the function prototype, and insert the actual - // parameter values from the stack - if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == nullptr && _tcsstr( lpszUnDSymbol, _T("()") ) == nullptr) - { - ULONG index = 0; - for( ; ; index++ ) - { - lpszParamSep = _tcschr( lpszParsed, _T(',') ); - if ( lpszParamSep == nullptr ) - break; - - *lpszParamSep = _T('\0'); - - _tcscat( lpszSymbol, lpszParsed ); - _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) ); - - lpszParsed = lpszParamSep + 1; - } - - lpszParamSep = _tcschr( lpszParsed, _T(')') ); - if ( lpszParamSep != nullptr ) - { - *lpszParamSep = _T('\0'); - - _tcscat( lpszSymbol, lpszParsed ); - _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) ); - - lpszParsed = lpszParamSep + 1; - } - } - - _tcscat( lpszSymbol, lpszParsed ); - - ret = TRUE; - } - GlobalFree( pSym ); - - return ret; -} - -// Get source file name and line number from IP address -// The output format is: "sourcefile(linenumber)" or -// "modulename!address" or -// "address" -static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo ) -{ - BOOL ret = FALSE; - IMAGEHLP_LINE lineInfo; - DWORD dwDisp; - TCHAR lpszFileName[BUFFERSIZE] = _T(""); - TCHAR lpModuleInfo[BUFFERSIZE] = _T(""); - - _tcscpy( lpszSourceInfo, _T("?(?)") ); - - ::ZeroMemory( &lineInfo, sizeof( lineInfo ) ); - lineInfo.SizeOfStruct = sizeof( lineInfo ); - - if ( SymGetLineFromAddr( GetCurrentProcess(), address, &dwDisp, &lineInfo ) ) - { - // Got it. Let's use "sourcefile(linenumber)" format - PCSTR2LPTSTR( lineInfo.FileName, lpszFileName ); - TCHAR fname[_MAX_FNAME]; - TCHAR ext[_MAX_EXT]; - _tsplitpath(lpszFileName, nullptr, nullptr, fname, ext); - _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber ); - ret = TRUE; - } - else - { - // There is no source file information. :( - // Let's use the "modulename!address" format - GetModuleNameFromAddress( address, lpModuleInfo ); - - if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0')) - // There is no modulename information. :(( - // Let's use the "address" format - _stprintf( lpszSourceInfo, _T("0x%08X"), address ); - else - _stprintf( lpszSourceInfo, _T("%s!0x%08X"), lpModuleInfo, address ); - - ret = FALSE; - } - - return ret; -} - -void PrintFunctionAndSourceInfo(FILE* file, const STACKFRAME& callstack) -{ - TCHAR symInfo[BUFFERSIZE] = _T("?"); - TCHAR srcInfo[BUFFERSIZE] = _T("?"); - - GetFunctionInfoFromAddresses((ULONG)callstack.AddrPC.Offset, (ULONG)callstack.AddrFrame.Offset, symInfo); - GetSourceInfoFromAddress((ULONG)callstack.AddrPC.Offset, srcInfo); - etfprint(file, " " + Common::TStrToUTF8(srcInfo) + " : " + Common::TStrToUTF8(symInfo) + "\n"); -} - -void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file ) -{ - STACKFRAME callStack; - BOOL bResult; - CONTEXT context; - HANDLE hProcess = GetCurrentProcess(); - - // If it's not this thread, let's suspend it, and resume it at the end - if ( hThread != GetCurrentThread() ) - if ( SuspendThread( hThread ) == -1 ) - { - // whaaat ?! - etfprint(file, "Call stack info failed\n"); - return; - } - - ::ZeroMemory( &context, sizeof(context) ); - context.ContextFlags = CONTEXT_FULL; - - if ( !GetThreadContext( hThread, &context ) ) - { - etfprint(file, "Call stack info failed\n"); - return; - } - - ::ZeroMemory( &callStack, sizeof(callStack) ); -#ifndef _M_X64 - callStack.AddrPC.Offset = context.Eip; - callStack.AddrStack.Offset = context.Esp; - callStack.AddrFrame.Offset = context.Ebp; -#else - callStack.AddrPC.Offset = context.Rip; - callStack.AddrStack.Offset = context.Rsp; - callStack.AddrFrame.Offset = context.Rbp; -#endif - callStack.AddrPC.Mode = AddrModeFlat; - callStack.AddrStack.Mode = AddrModeFlat; - callStack.AddrFrame.Mode = AddrModeFlat; - - etfprint(file, "Call stack info: \n"); - etfprint(file, lpszMessage); - - PrintFunctionAndSourceInfo(file, callStack); - - for( ULONG index = 0; ; index++ ) - { - bResult = StackWalk( - IMAGE_FILE_MACHINE_I386, - hProcess, - hThread, - &callStack, - nullptr, - nullptr, - SymFunctionTableAccess, - SymGetModuleBase, - nullptr); - - if ( index == 0 ) - continue; - - if( !bResult || callStack.AddrFrame.Offset == 0 ) - break; - - PrintFunctionAndSourceInfo(file, callStack); - - } - - if ( hThread != GetCurrentThread() ) - ResumeThread( hThread ); -} - -void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip, DWORD esp, DWORD ebp ) -{ - STACKFRAME callStack; - BOOL bResult; - TCHAR symInfo[BUFFERSIZE] = _T("?"); - TCHAR srcInfo[BUFFERSIZE] = _T("?"); - HANDLE hProcess = GetCurrentProcess(); - - // If it's not this thread, let's suspend it, and resume it at the end - if ( hThread != GetCurrentThread() ) - if ( SuspendThread( hThread ) == -1 ) - { - // whaaat ?! - etfprint(file, "Call stack info failed\n"); - return; - } - - ::ZeroMemory( &callStack, sizeof(callStack) ); - callStack.AddrPC.Offset = eip; - callStack.AddrStack.Offset = esp; - callStack.AddrFrame.Offset = ebp; - callStack.AddrPC.Mode = AddrModeFlat; - callStack.AddrStack.Mode = AddrModeFlat; - callStack.AddrFrame.Mode = AddrModeFlat; - - etfprint(file, "Call stack info: \n"); - etfprint(file, lpszMessage); - - PrintFunctionAndSourceInfo(file, callStack); - - for( ULONG index = 0; ; index++ ) - { - bResult = StackWalk( - IMAGE_FILE_MACHINE_I386, - hProcess, - hThread, - &callStack, - nullptr, - nullptr, - SymFunctionTableAccess, - SymGetModuleBase, - nullptr); - - if ( index == 0 ) - continue; - - if( !bResult || callStack.AddrFrame.Offset == 0 ) - break; - - PrintFunctionAndSourceInfo(file, callStack); - } - - if ( hThread != GetCurrentThread() ) - ResumeThread( hThread ); -} - -char g_uefbuf[2048]; - -void etfprintf(FILE *file, const char *format, ...) -{ - va_list ap; - va_start(ap, format); - int len = vsprintf(g_uefbuf, format, ap); - fwrite(g_uefbuf, 1, len, file); - va_end(ap); -} - -void etfprint(FILE *file, const std::string &text) -{ - size_t len = text.length(); - fwrite(text.data(), 1, len, file); -} - -#endif //WIN32 diff --git a/src/common/extended_trace.h b/src/common/extended_trace.h deleted file mode 100644 index ed3113a24..000000000 --- a/src/common/extended_trace.h +++ /dev/null @@ -1,50 +0,0 @@ -// ----------------------------------------------------------------------------------------- -// -// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com -// For companies(Austin,TX): If you would like to get my resume, send an email. -// -// The source is free, but if you want to use it, mention my name and e-mail address -// -// History: -// 1.0 Initial version Zoltan Csizmadia -// 1.1 WhineCube version Masken -// 1.2 Dolphin version Masken -// -// ---------------------------------------------------------------------------------------- - -#pragma once - -#if defined(WIN32) - -#include <windows.h> -#include <tchar.h> - -#include <string> - -#pragma comment( lib, "imagehlp.lib" ) - -#define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) InitSymInfo( IniSymbolPath ) -#define EXTENDEDTRACEUNINITIALIZE() UninitSymInfo() -#define STACKTRACE(file) StackTrace( GetCurrentThread(), "", file) -#define STACKTRACE2(file, eip, esp, ebp) StackTrace(GetCurrentThread(), "", file, eip, esp, ebp) -// class File; - -BOOL InitSymInfo( PCSTR ); -BOOL UninitSymInfo(); -void StackTrace(HANDLE, char const* msg, FILE *file); -void StackTrace(HANDLE, char const* msg, FILE *file, DWORD eip, DWORD esp, DWORD ebp); - -// functions by Masken -void etfprintf(FILE *file, const char *format, ...); -void etfprint(FILE *file, const std::string &text); -#define UEFBUFSIZE 2048 -extern char g_uefbuf[UEFBUFSIZE]; - -#else // not WIN32 - -#define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) ((void)0) -#define EXTENDEDTRACEUNINITIALIZE() ((void)0) -#define STACKTRACE(file) ((void)0) -#define STACKTRACE2(file, eip, esp, ebp) ((void)0) - -#endif // WIN32 diff --git a/src/common/file_search.cpp b/src/common/file_search.cpp deleted file mode 100644 index b3a0a84fb..000000000 --- a/src/common/file_search.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - - -#include "common/common.h" - -#ifndef _WIN32 -#include <dirent.h> -#else -#include <windows.h> -#endif - -#include <algorithm> - -#include "common/file_search.h" -#include "common/string_util.h" - - -CFileSearch::CFileSearch(const CFileSearch::XStringVector& _rSearchStrings, const CFileSearch::XStringVector& _rDirectories) -{ - // Reverse the loop order for speed? - for (size_t j = 0; j < _rSearchStrings.size(); j++) - { - for (size_t i = 0; i < _rDirectories.size(); i++) - { - FindFiles(_rSearchStrings[j], _rDirectories[i]); - } - } -} - - -void CFileSearch::FindFiles(const std::string& _searchString, const std::string& _strPath) -{ - std::string GCMSearchPath; - Common::BuildCompleteFilename(GCMSearchPath, _strPath, _searchString); -#ifdef _WIN32 - WIN32_FIND_DATA findData; - HANDLE FindFirst = FindFirstFile(Common::UTF8ToTStr(GCMSearchPath).c_str(), &findData); - - if (FindFirst != INVALID_HANDLE_VALUE) - { - bool bkeepLooping = true; - - while (bkeepLooping) - { - if (findData.cFileName[0] != '.') - { - std::string strFilename; - Common::BuildCompleteFilename(strFilename, _strPath, Common::TStrToUTF8(findData.cFileName)); - m_FileNames.push_back(strFilename); - } - - bkeepLooping = FindNextFile(FindFirst, &findData) ? true : false; - } - } - FindClose(FindFirst); - - -#else - // TODO: super lame/broken - - auto end_match(_searchString); - - // assuming we have a "*.blah"-like pattern - if (!end_match.empty() && end_match[0] == '*') - end_match.erase(0, 1); - - // ugly - if (end_match == ".*") - end_match.clear(); - - DIR* dir = opendir(_strPath.c_str()); - - if (!dir) - return; - - while (auto const dp = readdir(dir)) - { - std::string found(dp->d_name); - - if ((found != ".") && (found != "..") - && (found.size() >= end_match.size()) - && std::equal(end_match.rbegin(), end_match.rend(), found.rbegin())) - { - std::string full_name; - if (_strPath.c_str()[_strPath.size()-1] == DIR_SEP_CHR) - full_name = _strPath + found; - else - full_name = _strPath + DIR_SEP + found; - - m_FileNames.push_back(full_name); - } - } - - closedir(dir); -#endif -} - -const CFileSearch::XStringVector& CFileSearch::GetFileNames() const -{ - return m_FileNames; -} diff --git a/src/common/file_search.h b/src/common/file_search.h deleted file mode 100644 index 55ca02638..000000000 --- a/src/common/file_search.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <string> -#include <vector> - -class CFileSearch -{ -public: - typedef std::vector<std::string>XStringVector; - - CFileSearch(const XStringVector& _rSearchStrings, const XStringVector& _rDirectories); - const XStringVector& GetFileNames() const; - -private: - - void FindFiles(const std::string& _searchString, const std::string& _strPath); - - XStringVector m_FileNames; -}; diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 706e7c842..4ef4918d7 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp @@ -16,7 +16,10 @@ #include <tchar.h> #else #include <sys/param.h> +#include <sys/types.h> #include <dirent.h> +#include <pwd.h> +#include <unistd.h> #endif #if defined(__APPLE__) @@ -622,15 +625,64 @@ std::string GetBundleDirectory() #ifdef _WIN32 std::string& GetExeDirectory() { - static std::string DolphinPath; - if (DolphinPath.empty()) + static std::string exe_path; + if (exe_path.empty()) { - TCHAR Dolphin_exe_Path[2048]; - GetModuleFileName(nullptr, Dolphin_exe_Path, 2048); - DolphinPath = Common::TStrToUTF8(Dolphin_exe_Path); - DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\')); + TCHAR tchar_exe_path[2048]; + GetModuleFileName(nullptr, tchar_exe_path, 2048); + exe_path = Common::TStrToUTF8(tchar_exe_path); + exe_path = exe_path.substr(0, exe_path.find_last_of('\\')); } - return DolphinPath; + return exe_path; +} +#else +/** + * @return The user’s home directory on POSIX systems + */ +static const std::string& GetHomeDirectory() { + static std::string home_path; + if (home_path.empty()) { + const char* envvar = getenv("HOME"); + if (envvar) { + home_path = envvar; + } else { + auto pw = getpwuid(getuid()); + ASSERT_MSG(pw, "$HOME isn’t defined, and the current user can’t be found in /etc/passwd."); + home_path = pw->pw_dir; + } + } + return home_path; +} + +/** + * Follows the XDG Base Directory Specification to get a directory path + * @param envvar The XDG environment variable to get the value from + * @return The directory path + * @sa http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + */ +static const std::string GetUserDirectory(const std::string& envvar) { + const char* directory = getenv(envvar.c_str()); + + std::string user_dir; + if (directory) { + user_dir = directory; + } else { + std::string subdirectory; + if (envvar == "XDG_DATA_HOME") + subdirectory = DIR_SEP ".local" DIR_SEP "share"; + else if (envvar == "XDG_CONFIG_HOME") + subdirectory = DIR_SEP ".config"; + else if (envvar == "XDG_CACHE_HOME") + subdirectory = DIR_SEP ".cache"; + else + ASSERT_MSG(false, "Unknown XDG variable %s.", envvar.c_str()); + user_dir = GetHomeDirectory() + subdirectory; + } + + ASSERT_MSG(!user_dir.empty(), "User directory %s musn’t be empty.", envvar.c_str()); + ASSERT_MSG(user_dir[0] == '/', "User directory %s must be absolute.", envvar.c_str()); + + return user_dir; } #endif @@ -661,20 +713,27 @@ const std::string& GetUserPath(const unsigned int DirIDX, const std::string &new if (paths[D_USER_IDX].empty()) { #ifdef _WIN32 - paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; + paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; + paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; + paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; #else - if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) - paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; - else - paths[D_USER_IDX] = std::string(getenv("HOME") ? - getenv("HOME") : getenv("PWD") ? - getenv("PWD") : "") + DIR_SEP EMU_DATA_DIR DIR_SEP; + if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) { + paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; + paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; + paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; + } else { + std::string data_dir = GetUserDirectory("XDG_DATA_HOME"); + std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME"); + std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME"); + + paths[D_USER_IDX] = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; + paths[D_CONFIG_IDX] = config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; + paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; + } #endif - paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; - paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; diff --git a/src/common/hash.cpp b/src/common/hash.cpp index fe2c9e636..0624dab8d 100644 --- a/src/common/hash.cpp +++ b/src/common/hash.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include "common/hash.h" #if _M_SSE >= 0x402 @@ -155,7 +156,7 @@ u64 GetMurmurHash3(const u8 *src, int len, u32 samples) const u8 * data = (const u8*)src; const int nblocks = len / 16; u32 Step = (len / 8); - if(samples == 0) samples = max(Step, 1u); + if(samples == 0) samples = std::max(Step, 1u); Step = Step / samples; if(Step < 1) Step = 1; @@ -233,7 +234,7 @@ u64 GetCRC32(const u8 *src, int len, u32 samples) u32 Step = (len / 8); const u64 *data = (const u64 *)src; const u64 *end = data + Step; - if(samples == 0) samples = max(Step, 1u); + if(samples == 0) samples = std::max(Step, 1u); Step = Step / samples; if(Step < 1) Step = 1; while(data < end) @@ -265,7 +266,7 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) u32 Step = (len / 8); const u64 *data = (const u64 *)src; const u64 *end = data + Step; - if(samples == 0) samples = max(Step, 1u); + if(samples == 0) samples = std::max(Step, 1u); Step = Step / samples; if(Step < 1) Step = 1; while(data < end) @@ -308,7 +309,7 @@ u64 GetCRC32(const u8 *src, int len, u32 samples) u32 Step = (len/4); const u32 *data = (const u32 *)src; const u32 *end = data + Step; - if(samples == 0) samples = max(Step, 1u); + if(samples == 0) samples = std::max(Step, 1u); Step = Step / samples; if(Step < 1) Step = 1; while(data < end) @@ -380,7 +381,7 @@ u64 GetMurmurHash3(const u8* src, int len, u32 samples) u32 out[2]; const int nblocks = len / 8; u32 Step = (len / 4); - if(samples == 0) samples = max(Step, 1u); + if(samples == 0) samples = std::max(Step, 1u); Step = Step / samples; if(Step < 1) Step = 1; @@ -456,7 +457,7 @@ u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) u32 Step = (len / 8); const u64 *data = (const u64 *)src; const u64 *end = data + Step; - if(samples == 0) samples = max(Step, 1u); + if(samples == 0) samples = std::max(Step, 1u); Step = Step / samples; if(Step < 1) Step = 1; while(data < end) diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 8fee20a83..7c1010b22 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp @@ -33,6 +33,7 @@ static std::shared_ptr<Logger> global_logger; CLS(Service) \ SUB(Service, SRV) \ SUB(Service, FS) \ + SUB(Service, ERR) \ SUB(Service, APT) \ SUB(Service, GSP) \ SUB(Service, AC) \ diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 6c5ca3968..7b67b3c07 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h @@ -53,6 +53,7 @@ enum class Class : ClassType { /// should have its own subclass. Service_SRV, ///< The SRV (Service Directory) implementation Service_FS, ///< The FS (Filesystem) service implementation + Service_ERR, ///< The ERR (Error) port implementation Service_APT, ///< The APT (Applets) service Service_GSP, ///< The GSP (GPU control) service Service_AC, ///< The AC (WiFi status) service diff --git a/src/common/mem_arena.cpp b/src/common/mem_arena.cpp index a20361d6f..76c70701d 100644 --- a/src/common/mem_arena.cpp +++ b/src/common/mem_arena.cpp @@ -116,7 +116,7 @@ void MemArena::GrabLowMemSpace(size_t size) GetSystemInfo(&sysInfo); #elif defined(ANDROID) // Use ashmem so we don't have to allocate a file on disk! - fd = ashmem_create_region("PPSSPP_RAM", size); + fd = ashmem_create_region("Citra_RAM", size); // Note that it appears that ashmem is pinned by default, so no need to pin. if (fd < 0) { @@ -218,7 +218,7 @@ u8* MemArena::Find4GBBase() void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); if (base == MAP_FAILED) { - PanicAlert("Failed to map 256 MB of memory space: %s", strerror(errno)); + LOG_ERROR(Common_Memory, "Failed to map 256 MB of memory space: %s", strerror(errno)); return 0; } munmap(base, 0x10000000); @@ -338,7 +338,7 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena // address space. if (!Memory_TryBase(base, views, num_views, flags, arena)) { - PanicAlert("MemoryMap_Setup: Failed finding a memory base."); + LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base."); return 0; } #elif defined(_WIN32) @@ -363,12 +363,11 @@ u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena if (!Memory_TryBase(base, views, num_views, flags, arena)) { LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base."); - PanicAlert("MemoryMap_Setup: Failed finding a memory base."); return 0; } #endif if (base_attempts) - PanicAlert("No possible memory base pointer found!"); + LOG_ERROR(Common_Memory, "No possible memory base pointer found!"); return base; } diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index 8f982da89..7e69d31cb 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp @@ -56,7 +56,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { ptr = nullptr; #endif - PanicAlert("Failed to allocate executable memory"); + LOG_ERROR(Common_Memory, "Failed to allocate executable memory"); } #if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) else @@ -72,7 +72,7 @@ void* AllocateExecutableMemory(size_t size, bool low) #if defined(_M_X64) if ((u64)ptr >= 0x80000000 && low == true) - PanicAlert("Executable memory ended up above 2GB!"); + LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!"); #endif return ptr; @@ -94,7 +94,7 @@ void* AllocateMemoryPages(size_t size) // (unsigned long)size); if (ptr == nullptr) - PanicAlert("Failed to allocate raw memory"); + LOG_ERROR(Common_Memory, "Failed to allocate raw memory"); return ptr; } @@ -117,7 +117,7 @@ void* AllocateAlignedMemory(size_t size,size_t alignment) // (unsigned long)size); if (ptr == nullptr) - PanicAlert("Failed to allocate aligned memory"); + LOG_ERROR(Common_Memory, "Failed to allocate aligned memory"); return ptr; } @@ -129,7 +129,7 @@ void FreeMemoryPages(void* ptr, size_t size) #ifdef _WIN32 if (!VirtualFree(ptr, 0, MEM_RELEASE)) - PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg()); + LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n%s", GetLastErrorMsg()); ptr = nullptr; // Is this our responsibility? #else @@ -155,7 +155,7 @@ void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) #ifdef _WIN32 DWORD oldValue; if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) - PanicAlert("WriteProtectMemory failed!\n%s", GetLastErrorMsg()); + LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n%s", GetLastErrorMsg()); #else mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ); #endif @@ -166,7 +166,7 @@ void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) #ifdef _WIN32 DWORD oldValue; if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue)) - PanicAlert("UnWriteProtectMemory failed!\n%s", GetLastErrorMsg()); + LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n%s", GetLastErrorMsg()); #else mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ); #endif diff --git a/src/common/msg_handler.cpp b/src/common/msg_handler.cpp deleted file mode 100644 index 4a47b518e..000000000 --- a/src/common/msg_handler.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cstdio> - -#include "common/common.h" // Local -#include "common/string_util.h" - -bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style); -static MsgAlertHandler msg_handler = DefaultMsgHandler; -static bool AlertEnabled = true; - -std::string DefaultStringTranslator(const char* text); -static StringTranslator str_translator = DefaultStringTranslator; - -// Select which of these functions that are used for message boxes. If -// wxWidgets is enabled we will use wxMsgAlert() that is defined in Main.cpp -void RegisterMsgAlertHandler(MsgAlertHandler handler) -{ - msg_handler = handler; -} - -// Select translation function. For wxWidgets use wxStringTranslator in Main.cpp -void RegisterStringTranslator(StringTranslator translator) -{ - str_translator = translator; -} - -// enable/disable the alert handler -void SetEnableAlert(bool enable) -{ - AlertEnabled = enable; -} - -// This is the first stop for gui alerts where the log is updated and the -// correct window is shown -bool MsgAlert(bool yes_no, int Style, const char* format, ...) -{ - // Read message and write it to the log - std::string caption; - char buffer[2048]; - - static std::string info_caption; - static std::string warn_caption; - static std::string ques_caption; - static std::string crit_caption; - - if (!info_caption.length()) - { - info_caption = str_translator(_trans("Information")); - ques_caption = str_translator(_trans("Question")); - warn_caption = str_translator(_trans("Warning")); - crit_caption = str_translator(_trans("Critical")); - } - - switch(Style) - { - case INFORMATION: - caption = info_caption; - break; - case QUESTION: - caption = ques_caption; - break; - case WARNING: - caption = warn_caption; - break; - case CRITICAL: - caption = crit_caption; - break; - } - - va_list args; - va_start(args, format); - Common::CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args); - va_end(args); - - LOG_INFO(Common, "%s: %s", caption.c_str(), buffer); - - // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored - if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL)) - return msg_handler(caption.c_str(), buffer, yes_no, Style); - - return true; -} - -// Default non library dependent panic alert -bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style) -{ -//#ifdef _WIN32 -// int STYLE = MB_ICONINFORMATION; -// if (Style == QUESTION) STYLE = MB_ICONQUESTION; -// if (Style == WARNING) STYLE = MB_ICONWARNING; -// -// return IDYES == MessageBox(0, UTF8ToTStr(text).c_str(), UTF8ToTStr(caption).c_str(), STYLE | (yes_no ? MB_YESNO : MB_OK)); -//#else - printf("%s\n", text); - return true; -//#endif -} - -// Default (non) translator -std::string DefaultStringTranslator(const char* text) -{ - return text; -} - diff --git a/src/common/msg_handler.h b/src/common/msg_handler.h deleted file mode 100644 index 421f93e23..000000000 --- a/src/common/msg_handler.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <string> - -// Message alerts -enum MSG_TYPE -{ - INFORMATION, - QUESTION, - WARNING, - CRITICAL -}; - -typedef bool (*MsgAlertHandler)(const char* caption, const char* text, - bool yes_no, int Style); -typedef std::string (*StringTranslator)(const char* text); - -void RegisterMsgAlertHandler(MsgAlertHandler handler); -void RegisterStringTranslator(StringTranslator translator); - -extern bool MsgAlert(bool yes_no, int Style, const char* format, ...) -#ifdef __GNUC__ - __attribute__((format(printf, 3, 4))) -#endif - ; -void SetEnableAlert(bool enable); - -#ifdef _MSC_VER - #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) - #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) - #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) - #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) - #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) - // Use these macros (that do the same thing) if the message should be translated. - #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) - #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) - #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) - #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) - #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) -#else - #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) - #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) - #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) - #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) - #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) - // Use these macros (that do the same thing) if the message should be translated. - #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) - #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) - #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) - #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) - #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) -#endif diff --git a/src/common/profiler.cpp b/src/common/profiler.cpp new file mode 100644 index 000000000..65c3df167 --- /dev/null +++ b/src/common/profiler.cpp @@ -0,0 +1,182 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/profiler.h" +#include "common/profiler_reporting.h" +#include "common/assert.h" + +#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013. +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> // For QueryPerformanceCounter/Frequency +#endif + +namespace Common { +namespace Profiling { + +#if ENABLE_PROFILING +thread_local Timer* Timer::current_timer = nullptr; +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013 +QPCClock::time_point QPCClock::now() { + static LARGE_INTEGER freq; + // Use this dummy local static to ensure this gets initialized once. + static BOOL dummy = QueryPerformanceFrequency(&freq); + + LARGE_INTEGER ticks; + QueryPerformanceCounter(&ticks); + + // This is prone to overflow when multiplying, which is why I'm using micro instead of nano. The + // correct way to approach this would be to just return ticks as a time_point and then subtract + // and do this conversion when creating a duration from two time_points, however, as far as I + // could tell the C++ requirements for these types are incompatible with this approach. + return time_point(duration(ticks.QuadPart * std::micro::den / freq.QuadPart)); +} +#endif + +TimingCategory::TimingCategory(const char* name, TimingCategory* parent) + : accumulated_duration(0) { + + ProfilingManager& manager = GetProfilingManager(); + category_id = manager.RegisterTimingCategory(this, name); + if (parent != nullptr) + manager.SetTimingCategoryParent(category_id, parent->category_id); +} + +ProfilingManager::ProfilingManager() + : last_frame_end(Clock::now()), this_frame_start(Clock::now()) { +} + +unsigned int ProfilingManager::RegisterTimingCategory(TimingCategory* category, const char* name) { + TimingCategoryInfo info; + info.category = category; + info.name = name; + info.parent = TimingCategoryInfo::NO_PARENT; + + unsigned int id = (unsigned int)timing_categories.size(); + timing_categories.push_back(std::move(info)); + + return id; +} + +void ProfilingManager::SetTimingCategoryParent(unsigned int category, unsigned int parent) { + ASSERT(category < timing_categories.size()); + ASSERT(parent < timing_categories.size()); + + timing_categories[category].parent = parent; +} + +void ProfilingManager::BeginFrame() { + this_frame_start = Clock::now(); +} + +void ProfilingManager::FinishFrame() { + Clock::time_point now = Clock::now(); + + results.interframe_time = now - last_frame_end; + results.frame_time = now - this_frame_start; + + results.time_per_category.resize(timing_categories.size()); + for (size_t i = 0; i < timing_categories.size(); ++i) { + results.time_per_category[i] = timing_categories[i].category->GetAccumulatedTime(); + } + + last_frame_end = now; +} + +TimingResultsAggregator::TimingResultsAggregator(size_t window_size) + : max_window_size(window_size), window_size(0) { + interframe_times.resize(window_size, Duration::zero()); + frame_times.resize(window_size, Duration::zero()); +} + +void TimingResultsAggregator::Clear() { + window_size = cursor = 0; +} + +void TimingResultsAggregator::SetNumberOfCategories(size_t n) { + size_t old_size = times_per_category.size(); + if (n == old_size) + return; + + times_per_category.resize(n); + + for (size_t i = old_size; i < n; ++i) { + times_per_category[i].resize(max_window_size, Duration::zero()); + } +} + +void TimingResultsAggregator::AddFrame(const ProfilingFrameResult& frame_result) { + SetNumberOfCategories(frame_result.time_per_category.size()); + + interframe_times[cursor] = frame_result.interframe_time; + frame_times[cursor] = frame_result.frame_time; + for (size_t i = 0; i < frame_result.time_per_category.size(); ++i) { + times_per_category[i][cursor] = frame_result.time_per_category[i]; + } + + ++cursor; + if (cursor == max_window_size) + cursor = 0; + if (window_size < max_window_size) + ++window_size; +} + +static AggregatedDuration AggregateField(const std::vector<Duration>& v, size_t len) { + AggregatedDuration result; + result.avg = Duration::zero(); + + result.min = result.max = (len == 0 ? Duration::zero() : v[0]); + + for (size_t i = 1; i < len; ++i) { + Duration value = v[i]; + result.avg += value; + result.min = std::min(result.min, value); + result.max = std::max(result.max, value); + } + if (len != 0) + result.avg /= len; + + return result; +} + +static float tof(Common::Profiling::Duration dur) { + using FloatMs = std::chrono::duration<float, std::chrono::milliseconds::period>; + return std::chrono::duration_cast<FloatMs>(dur).count(); +} + +AggregatedFrameResult TimingResultsAggregator::GetAggregatedResults() const { + AggregatedFrameResult result; + + result.interframe_time = AggregateField(interframe_times, window_size); + result.frame_time = AggregateField(frame_times, window_size); + + if (result.interframe_time.avg != Duration::zero()) { + result.fps = 1000.0f / tof(result.interframe_time.avg); + } else { + result.fps = 0.0f; + } + + result.time_per_category.resize(times_per_category.size()); + for (size_t i = 0; i < times_per_category.size(); ++i) { + result.time_per_category[i] = AggregateField(times_per_category[i], window_size); + } + + return result; +} + +ProfilingManager& GetProfilingManager() { + // Takes advantage of "magic" static initialization for race-free initialization. + static ProfilingManager manager; + return manager; +} + +SynchronizedRef<TimingResultsAggregator> GetTimingResultsAggregator() { + static SynchronizedWrapper<TimingResultsAggregator> aggregator(30); + return SynchronizedRef<TimingResultsAggregator>(aggregator); +} + +} // namespace Profiling +} // namespace Common diff --git a/src/common/profiler.h b/src/common/profiler.h new file mode 100644 index 000000000..3e967b4bc --- /dev/null +++ b/src/common/profiler.h @@ -0,0 +1,152 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <atomic> +#include <chrono> + +#include "common/assert.h" +#include "common/thread.h" + +namespace Common { +namespace Profiling { + +// If this is defined to 0, it turns all Timers into no-ops. +#ifndef ENABLE_PROFILING +#define ENABLE_PROFILING 1 +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 // MSVC 2013 +// MSVC up to 2013 doesn't use QueryPerformanceCounter for high_resolution_clock, so it has bad +// precision. We manually implement a clock based on QPC to get good results. + +struct QPCClock { + using duration = std::chrono::microseconds; + using time_point = std::chrono::time_point<QPCClock>; + using rep = duration::rep; + using period = duration::period; + static const bool is_steady = false; + + static time_point now(); +}; + +using Clock = QPCClock; +#else +using Clock = std::chrono::high_resolution_clock; +#endif + +using Duration = Clock::duration; + +/** + * Represents a timing category that measured time can be accounted towards. Should be declared as a + * global variable and passed to Timers. + */ +class TimingCategory final { +public: + TimingCategory(const char* name, TimingCategory* parent = nullptr); + + unsigned int GetCategoryId() const { + return category_id; + } + + /// Adds some time to this category. Can safely be called from multiple threads at the same time. + void AddTime(Duration amount) { + std::atomic_fetch_add_explicit( + &accumulated_duration, amount.count(), + std::memory_order_relaxed); + } + + /** + * Atomically retrieves the accumulated measured time for this category and resets the counter + * to zero. Can be safely called concurrently with AddTime. + */ + Duration GetAccumulatedTime() { + return Duration(std::atomic_exchange_explicit( + &accumulated_duration, (Duration::rep)0, + std::memory_order_relaxed)); + } + +private: + unsigned int category_id; + std::atomic<Duration::rep> accumulated_duration; +}; + +/** + * Measures time elapsed between a call to Start and a call to Stop and attributes it to the given + * TimingCategory. Start/Stop can be called multiple times on the same timer, but each call must be + * appropriately paired. + * + * When a Timer is started, it automatically pauses a previously running timer on the same thread, + * which is resumed when it is stopped. As such, no special action needs to be taken to avoid + * double-accounting of time on two categories. + */ +class Timer { +public: + Timer(TimingCategory& category) : category(category) { + } + + void Start() { +#if ENABLE_PROFILING + ASSERT(!running); + previous_timer = current_timer; + current_timer = this; + if (previous_timer != nullptr) + previous_timer->StopTiming(); + + StartTiming(); +#endif + } + + void Stop() { +#if ENABLE_PROFILING + ASSERT(running); + StopTiming(); + + if (previous_timer != nullptr) + previous_timer->StartTiming(); + current_timer = previous_timer; +#endif + } + +private: +#if ENABLE_PROFILING + void StartTiming() { + start = Clock::now(); + running = true; + } + + void StopTiming() { + auto duration = Clock::now() - start; + running = false; + category.AddTime(std::chrono::duration_cast<Duration>(duration)); + } + + Clock::time_point start; + bool running = false; + + Timer* previous_timer; + static thread_local Timer* current_timer; +#endif + + TimingCategory& category; +}; + +/** + * A Timer that automatically starts timing when created and stops at the end of the scope. Should + * be used in the majority of cases. + */ +class ScopeTimer : public Timer { +public: + ScopeTimer(TimingCategory& category) : Timer(category) { + Start(); + } + + ~ScopeTimer() { + Stop(); + } +}; + +} // namespace Profiling +} // namespace Common diff --git a/src/common/profiler_reporting.h b/src/common/profiler_reporting.h new file mode 100644 index 000000000..3abb73315 --- /dev/null +++ b/src/common/profiler_reporting.h @@ -0,0 +1,108 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <chrono> +#include <mutex> +#include <utility> +#include <vector> + +#include "common/profiler.h" +#include "common/synchronized_wrapper.h" + +namespace Common { +namespace Profiling { + +struct TimingCategoryInfo { + static const unsigned int NO_PARENT = -1; + + TimingCategory* category; + const char* name; + unsigned int parent; +}; + +struct ProfilingFrameResult { + /// Time since the last delivered frame + Duration interframe_time; + + /// Time spent processing a frame, excluding VSync + Duration frame_time; + + /// Total amount of time spent inside each category in this frame. Indexed by the category id + std::vector<Duration> time_per_category; +}; + +class ProfilingManager final { +public: + ProfilingManager(); + + unsigned int RegisterTimingCategory(TimingCategory* category, const char* name); + void SetTimingCategoryParent(unsigned int category, unsigned int parent); + + const std::vector<TimingCategoryInfo>& GetTimingCategoriesInfo() const { + return timing_categories; + } + + /// This should be called after swapping screen buffers. + void BeginFrame(); + /// This should be called before swapping screen buffers. + void FinishFrame(); + + /// Get the timing results from the previous frame. This is updated when you call FinishFrame(). + const ProfilingFrameResult& GetPreviousFrameResults() const { + return results; + } + +private: + std::vector<TimingCategoryInfo> timing_categories; + Clock::time_point last_frame_end; + Clock::time_point this_frame_start; + + ProfilingFrameResult results; +}; + +struct AggregatedDuration { + Duration avg, min, max; +}; + +struct AggregatedFrameResult { + /// Time since the last delivered frame + AggregatedDuration interframe_time; + + /// Time spent processing a frame, excluding VSync + AggregatedDuration frame_time; + + float fps; + + /// Total amount of time spent inside each category in this frame. Indexed by the category id + std::vector<AggregatedDuration> time_per_category; +}; + +class TimingResultsAggregator final { +public: + TimingResultsAggregator(size_t window_size); + + void Clear(); + void SetNumberOfCategories(size_t n); + + void AddFrame(const ProfilingFrameResult& frame_result); + + AggregatedFrameResult GetAggregatedResults() const; + + size_t max_window_size; + size_t window_size; + size_t cursor; + + std::vector<Duration> interframe_times; + std::vector<Duration> frame_times; + std::vector<std::vector<Duration>> times_per_category; +}; + +ProfilingManager& GetProfilingManager(); +SynchronizedRef<TimingResultsAggregator> GetTimingResultsAggregator(); + +} // namespace Profiling +} // namespace Common diff --git a/src/common/swap.h b/src/common/swap.h index e2d918362..7e37655bf 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -17,18 +17,14 @@ #pragma once -// Android -#if defined(ANDROID) +#if defined(__linux__) +#include <byteswap.h> +#elif defined(__FreeBSD__) #include <sys/endian.h> - -#if _BYTE_ORDER == _LITTLE_ENDIAN && !defined(COMMON_LITTLE_ENDIAN) -#define COMMON_LITTLE_ENDIAN 1 -#elif _BYTE_ORDER == _BIG_ENDIAN && !defined(COMMON_BIG_ENDIAN) -#define COMMON_BIG_ENDIAN 1 #endif // GCC 4.6+ -#elif __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) #if __BYTE_ORDER__ && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) && !defined(COMMON_LITTLE_ENDIAN) #define COMMON_LITTLE_ENDIAN 1 @@ -49,7 +45,6 @@ #elif defined(_MSC_VER) && !defined(COMMON_BIG_ENDIAN) && !defined(COMMON_LITTLE_ENDIAN) #define COMMON_LITTLE_ENDIAN 1 - #endif // Worst case, default to little endian. @@ -57,6 +52,93 @@ #define COMMON_LITTLE_ENDIAN 1 #endif +namespace Common { + +inline u8 swap8(u8 _data) {return _data;} +inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];} + +#ifdef _MSC_VER +inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);} +inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);} +inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);} +#elif _M_ARM +inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;} +inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;} +inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);} +#elif __linux__ +inline u16 swap16(u16 _data) {return bswap_16(_data);} +inline u32 swap32(u32 _data) {return bswap_32(_data);} +inline u64 swap64(u64 _data) {return bswap_64(_data);} +#elif __APPLE__ +inline __attribute__((always_inline)) u16 swap16(u16 _data) +{return (_data >> 8) | (_data << 8);} +inline __attribute__((always_inline)) u32 swap32(u32 _data) +{return __builtin_bswap32(_data);} +inline __attribute__((always_inline)) u64 swap64(u64 _data) +{return __builtin_bswap64(_data);} +#elif __FreeBSD__ +inline u16 swap16(u16 _data) {return bswap16(_data);} +inline u32 swap32(u32 _data) {return bswap32(_data);} +inline u64 swap64(u64 _data) {return bswap64(_data);} +#else +// Slow generic implementation. +inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);} +inline u32 swap32(u32 data) {return (swap16(data) << 16) | swap16(data >> 16);} +inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 32);} +#endif + +inline float swapf(float f) { + union { + float f; + unsigned int u32; + } dat1, dat2; + + dat1.f = f; + dat2.u32 = swap32(dat1.u32); + + return dat2.f; +} + +inline double swapd(double f) { + union { + double f; + unsigned long long u64; + } dat1, dat2; + + dat1.f = f; + dat2.u64 = swap64(dat1.u64); + + return dat2.f; +} + +inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);} +inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);} +inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);} + +template <int count> +void swap(u8*); + +template <> +inline void swap<1>(u8* data) { } + +template <> +inline void swap<2>(u8* data) { + *reinterpret_cast<u16*>(data) = swap16(data); +} + +template <> +inline void swap<4>(u8* data) { + *reinterpret_cast<u32*>(data) = swap32(data); +} + +template <> +inline void swap<8>(u8* data) { + *reinterpret_cast<u64*>(data) = swap64(data); +} + +} // Namespace Common + + template <typename T, typename F> struct swap_struct_t { typedef swap_struct_t<T, F> swapped_t; @@ -448,35 +530,35 @@ bool operator==(const S &p, const swap_struct_t<T, F> v) { template <typename T> struct swap_64_t { static T swap(T x) { - return (T)bswap64(*(u64 *)&x); + return (T)Common::swap64(*(u64 *)&x); } }; template <typename T> struct swap_32_t { static T swap(T x) { - return (T)bswap32(*(u32 *)&x); + return (T)Common::swap32(*(u32 *)&x); } }; template <typename T> struct swap_16_t { static T swap(T x) { - return (T)bswap16(*(u16 *)&x); + return (T)Common::swap16(*(u16 *)&x); } }; template <typename T> struct swap_float_t { static T swap(T x) { - return (T)bswapf(*(float *)&x); + return (T)Common::swapf(*(float *)&x); } }; template <typename T> struct swap_double_t { static T swap(T x) { - return (T)bswapd(*(double *)&x); + return (T)Common::swapd(*(double *)&x); } }; @@ -527,4 +609,5 @@ typedef s64 s64_be; typedef float float_be; typedef double double_be; + #endif diff --git a/src/common/synchronized_wrapper.h b/src/common/synchronized_wrapper.h new file mode 100644 index 000000000..946252b8c --- /dev/null +++ b/src/common/synchronized_wrapper.h @@ -0,0 +1,69 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <mutex> + +namespace Common { + +/** + * Wraps an object, only allowing access to it via a locking reference wrapper. Good to ensure no + * one forgets to lock a mutex before acessing an object. To access the wrapped object construct a + * SyncronizedRef on this wrapper. Inspired by Rust's Mutex type (http://doc.rust-lang.org/std/sync/struct.Mutex.html). + */ +template <typename T> +class SynchronizedWrapper { +public: + template <typename... Args> + SynchronizedWrapper(Args&&... args) : + data(std::forward<Args>(args)...) { + } + +private: + template <typename U> + friend class SynchronizedRef; + + std::mutex mutex; + T data; +}; + +/** + * Synchronized reference, that keeps a SynchronizedWrapper's mutex locked during its lifetime. This + * greatly reduces the chance that someone will access the wrapped resource without locking the + * mutex. + */ +template <typename T> +class SynchronizedRef { +public: + SynchronizedRef(SynchronizedWrapper<T>& wrapper) : wrapper(&wrapper) { + wrapper.mutex.lock(); + } + + SynchronizedRef(SynchronizedRef&) = delete; + SynchronizedRef(SynchronizedRef&& o) : wrapper(o.wrapper) { + o.wrapper = nullptr; + } + + ~SynchronizedRef() { + if (wrapper) + wrapper->mutex.unlock(); + } + + SynchronizedRef& operator=(SynchronizedRef&) = delete; + SynchronizedRef& operator=(SynchronizedRef&& o) { + std::swap(wrapper, o.wrapper); + } + + T& operator*() { return wrapper->data; } + const T& operator*() const { return wrapper->data; } + + T* operator->() { return &wrapper->data; } + const T* operator->() const { return &wrapper->data; } + +private: + SynchronizedWrapper<T>* wrapper; +}; + +} // namespace Common diff --git a/src/common/thread.h b/src/common/thread.h index eaf1ba00c..a45728e1e 100644 --- a/src/common/thread.h +++ b/src/common/thread.h @@ -24,6 +24,25 @@ #include <unistd.h> #endif +// Support for C++11's thread_local keyword was surprisingly spotty in compilers until very +// recently. Fortunately, thread local variables have been well supported for compilers for a while, +// but with semantics supporting only POD types, so we can use a few defines to get some amount of +// backwards compat support. +// WARNING: This only works correctly with POD types. +#if defined(__clang__) +# if !__has_feature(cxx_thread_local) +# define thread_local __thread +# endif +#elif defined(__GNUC__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) +# define thread_local __thread +# endif +#elif defined(_MSC_VER) +# if _MSC_VER < 1900 +# define thread_local __declspec(thread) +# endif +#endif + namespace Common { diff --git a/src/common/utf8.cpp b/src/common/utf8.cpp deleted file mode 100644 index 56609634c..000000000 --- a/src/common/utf8.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/* - Basic UTF-8 manipulation routines - by Jeff Bezanson - placed in the public domain Fall 2005 - - This code is designed to provide the utilities you need to manipulate - UTF-8 as an internal string encoding. These functions do not perform the - error checking normally needed when handling UTF-8 data, so if you happen - to be from the Unicode Consortium you will want to flay me alive. - I do this because error checking can be performed at the boundaries (I/O), - with these routines reserved for higher performance on data known to be - valid. -*/ - -#ifdef _WIN32 -#include <windows.h> -#undef min -#undef max -#endif - -#include <cstdlib> -#include <cstring> -#include <algorithm> - -#include "common/common_types.h" -#include "common/utf8.h" - -// is start of UTF sequence -inline bool isutf(char c) { - return (c & 0xC0) != 0x80; -} - -static const u32 offsetsFromUTF8[6] = { - 0x00000000UL, 0x00003080UL, 0x000E2080UL, - 0x03C82080UL, 0xFA082080UL, 0x82082080UL -}; - -static const u8 trailingBytesForUTF8[256] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5, -}; - -/* returns length of next utf-8 sequence */ -int u8_seqlen(const char *s) -{ - return trailingBytesForUTF8[(unsigned int)(unsigned char)s[0]] + 1; -} - -/* conversions without error checking - only works for valid UTF-8, i.e. no 5- or 6-byte sequences - srcsz = source size in bytes, or -1 if 0-terminated - sz = dest size in # of wide characters - - returns # characters converted - dest will always be L'\0'-terminated, even if there isn't enough room - for all the characters. - if sz = srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space. -*/ -int u8_toucs(u32 *dest, int sz, const char *src, int srcsz) -{ - u32 ch; - const char *src_end = src + srcsz; - int nb; - int i=0; - - while (i < sz-1) { - nb = trailingBytesForUTF8[(unsigned char)*src]; - if (srcsz == -1) { - if (*src == 0) - goto done_toucs; - } - else { - if (src + nb >= src_end) - goto done_toucs; - } - ch = 0; - switch (nb) { - /* these fall through deliberately */ - case 3: ch += (unsigned char)*src++; ch <<= 6; - case 2: ch += (unsigned char)*src++; ch <<= 6; - case 1: ch += (unsigned char)*src++; ch <<= 6; - case 0: ch += (unsigned char)*src++; - } - ch -= offsetsFromUTF8[nb]; - dest[i++] = ch; - } - done_toucs: - dest[i] = 0; - return i; -} - -/* srcsz = number of source characters, or -1 if 0-terminated - sz = size of dest buffer in bytes - - returns # characters converted - dest will only be '\0'-terminated if there is enough space. this is - for consistency; imagine there are 2 bytes of space left, but the next - character requires 3 bytes. in this case we could NUL-terminate, but in - general we can't when there's insufficient space. therefore this function - only NUL-terminates if all the characters fit, and there's space for - the NUL as well. - the destination string will never be bigger than the source string. -*/ -int u8_toutf8(char *dest, int sz, u32 *src, int srcsz) -{ - u32 ch; - int i = 0; - char *dest_end = dest + sz; - - while (srcsz<0 ? src[i]!=0 : i < srcsz) { - ch = src[i]; - if (ch < 0x80) { - if (dest >= dest_end) - return i; - *dest++ = (char)ch; - } - else if (ch < 0x800) { - if (dest >= dest_end-1) - return i; - *dest++ = (ch>>6) | 0xC0; - *dest++ = (ch & 0x3F) | 0x80; - } - else if (ch < 0x10000) { - if (dest >= dest_end-2) - return i; - *dest++ = (ch>>12) | 0xE0; - *dest++ = ((ch>>6) & 0x3F) | 0x80; - *dest++ = (ch & 0x3F) | 0x80; - } - else if (ch < 0x110000) { - if (dest >= dest_end-3) - return i; - *dest++ = (ch>>18) | 0xF0; - *dest++ = ((ch>>12) & 0x3F) | 0x80; - *dest++ = ((ch>>6) & 0x3F) | 0x80; - *dest++ = (ch & 0x3F) | 0x80; - } - i++; - } - if (dest < dest_end) - *dest = '\0'; - return i; -} - -int u8_wc_toutf8(char *dest, u32 ch) -{ - if (ch < 0x80) { - dest[0] = (char)ch; - return 1; - } - if (ch < 0x800) { - dest[0] = (ch>>6) | 0xC0; - dest[1] = (ch & 0x3F) | 0x80; - return 2; - } - if (ch < 0x10000) { - dest[0] = (ch>>12) | 0xE0; - dest[1] = ((ch>>6) & 0x3F) | 0x80; - dest[2] = (ch & 0x3F) | 0x80; - return 3; - } - if (ch < 0x110000) { - dest[0] = (ch>>18) | 0xF0; - dest[1] = ((ch>>12) & 0x3F) | 0x80; - dest[2] = ((ch>>6) & 0x3F) | 0x80; - dest[3] = (ch & 0x3F) | 0x80; - return 4; - } - return 0; -} - -/* charnum => byte offset */ -int u8_offset(const char *str, int charnum) -{ - int offs=0; - - while (charnum > 0 && str[offs]) { - (void)(isutf(str[++offs]) || isutf(str[++offs]) || - isutf(str[++offs]) || ++offs); - charnum--; - } - return offs; -} - -/* byte offset => charnum */ -int u8_charnum(const char *s, int offset) -{ - int charnum = 0, offs=0; - - while (offs < offset && s[offs]) { - (void)(isutf(s[++offs]) || isutf(s[++offs]) || - isutf(s[++offs]) || ++offs); - charnum++; - } - return charnum; -} - -/* number of characters */ -int u8_strlen(const char *s) -{ - int count = 0; - int i = 0; - - while (u8_nextchar(s, &i) != 0) - count++; - - return count; -} - -/* reads the next utf-8 sequence out of a string, updating an index */ -u32 u8_nextchar(const char *s, int *i) -{ - u32 ch = 0; - int sz = 0; - - do { - ch <<= 6; - ch += (unsigned char)s[(*i)++]; - sz++; - } while (s[*i] && !isutf(s[*i])); - ch -= offsetsFromUTF8[sz-1]; - - return ch; -} - -void u8_inc(const char *s, int *i) -{ - (void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) || - isutf(s[++(*i)]) || ++(*i)); -} - -void u8_dec(const char *s, int *i) -{ - (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) || - isutf(s[--(*i)]) || --(*i)); -} - -int octal_digit(char c) -{ - return (c >= '0' && c <= '7'); -} - -int hex_digit(char c) -{ - return ((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'F') || - (c >= 'a' && c <= 'f')); -} - -/* assumes that src points to the character after a backslash - returns number of input characters processed */ -int u8_read_escape_sequence(const char *str, u32 *dest) -{ - u32 ch; - char digs[9]="\0\0\0\0\0\0\0\0"; - int dno=0, i=1; - - ch = (u32)str[0]; /* take literal character */ - if (str[0] == 'n') - ch = L'\n'; - else if (str[0] == 't') - ch = L'\t'; - else if (str[0] == 'r') - ch = L'\r'; - else if (str[0] == 'b') - ch = L'\b'; - else if (str[0] == 'f') - ch = L'\f'; - else if (str[0] == 'v') - ch = L'\v'; - else if (str[0] == 'a') - ch = L'\a'; - else if (octal_digit(str[0])) { - i = 0; - do { - digs[dno++] = str[i++]; - } while (octal_digit(str[i]) && dno < 3); - ch = strtol(digs, nullptr, 8); - } - else if (str[0] == 'x') { - while (hex_digit(str[i]) && dno < 2) { - digs[dno++] = str[i++]; - } - if (dno > 0) - ch = strtol(digs, nullptr, 16); - } - else if (str[0] == 'u') { - while (hex_digit(str[i]) && dno < 4) { - digs[dno++] = str[i++]; - } - if (dno > 0) - ch = strtol(digs, nullptr, 16); - } - else if (str[0] == 'U') { - while (hex_digit(str[i]) && dno < 8) { - digs[dno++] = str[i++]; - } - if (dno > 0) - ch = strtol(digs, nullptr, 16); - } - *dest = ch; - - return i; -} - -/* convert a string with literal \uxxxx or \Uxxxxxxxx characters to UTF-8 - example: u8_unescape(mybuf, 256, "hello\\u220e") - note the double backslash is needed if called on a C string literal */ -int u8_unescape(char *buf, int sz, char *src) -{ - int c=0, amt; - u32 ch; - char temp[4]; - - while (*src && c < sz) { - if (*src == '\\') { - src++; - amt = u8_read_escape_sequence(src, &ch); - } - else { - ch = (u32)*src; - amt = 1; - } - src += amt; - amt = u8_wc_toutf8(temp, ch); - if (amt > sz-c) - break; - memcpy(&buf[c], temp, amt); - c += amt; - } - if (c < sz) - buf[c] = '\0'; - return c; -} - -const char *u8_strchr(const char *s, u32 ch, int *charn) -{ - int i = 0, lasti=0; - u32 c; - - *charn = 0; - while (s[i]) { - c = u8_nextchar(s, &i); - if (c == ch) { - return &s[lasti]; - } - lasti = i; - (*charn)++; - } - return nullptr; -} - -const char *u8_memchr(const char *s, u32 ch, size_t sz, int *charn) -{ - u32 i = 0, lasti=0; - u32 c; - int csz; - - *charn = 0; - while (i < sz) { - c = csz = 0; - do { - c <<= 6; - c += (unsigned char)s[i++]; - csz++; - } while (i < sz && !isutf(s[i])); - c -= offsetsFromUTF8[csz-1]; - - if (c == ch) { - return &s[lasti]; - } - lasti = i; - (*charn)++; - } - return nullptr; -} - -int u8_is_locale_utf8(const char *locale) -{ - /* this code based on libutf8 */ - const char* cp = locale; - - for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++) { - if (*cp == '.') { - const char* encoding = ++cp; - for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++) - ; - if ((cp-encoding == 5 && !strncmp(encoding, "UTF-8", 5)) - || (cp-encoding == 4 && !strncmp(encoding, "utf8", 4))) - return 1; /* it's UTF-8 */ - break; - } - } - return 0; -} - -int UTF8StringNonASCIICount(const char *utf8string) { - UTF8 utf(utf8string); - int count = 0; - while (!utf.end()) { - int c = utf.next(); - if (c > 127) - ++count; - } - return count; -} - -bool UTF8StringHasNonASCII(const char *utf8string) { - return UTF8StringNonASCIICount(utf8string) > 0; -} - -#ifdef _WIN32 - -std::string ConvertWStringToUTF8(const wchar_t *wstr) { - int len = (int)wcslen(wstr); - int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr, len, 0, 0, nullptr, nullptr); - std::string s; - s.resize(size); - if (size > 0) { - WideCharToMultiByte(CP_UTF8, 0, wstr, len, &s[0], size, nullptr, nullptr); - } - return s; -} - -std::string ConvertWStringToUTF8(const std::wstring &wstr) { - int len = (int)wstr.size(); - int size = (int)WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, 0, 0, nullptr, nullptr); - std::string s; - s.resize(size); - if (size > 0) { - WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), len, &s[0], size, nullptr, nullptr); - } - return s; -} - -void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source) { - int len = (int)source.size(); - int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0); - MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, dest, std::min((int)destSize, size)); -} - -std::wstring ConvertUTF8ToWString(const std::string &source) { - int len = (int)source.size(); - int size = (int)MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, nullptr, 0); - std::wstring str; - str.resize(size); - if (size > 0) { - MultiByteToWideChar(CP_UTF8, 0, source.c_str(), len, &str[0], size); - } - return str; -} - -#endif diff --git a/src/common/utf8.h b/src/common/utf8.h deleted file mode 100644 index a6e84913b..000000000 --- a/src/common/utf8.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - Basic UTF-8 manipulation routines - by Jeff Bezanson - placed in the public domain Fall 2005 - - This code is designed to provide the utilities you need to manipulate - UTF-8 as an internal string encoding. These functions do not perform the - error checking normally needed when handling UTF-8 data, so if you happen - to be from the Unicode Consortium you will want to flay me alive. - I do this because error checking can be performed at the boundaries (I/O), - with these routines reserved for higher performance on data known to be - valid. -*/ - -// Further modified, and C++ stuff added, by hrydgard@gmail.com. - -#pragma once - -#include "common/common_types.h" -#include <string> - -u32 u8_nextchar(const char *s, int *i); -int u8_wc_toutf8(char *dest, u32 ch); -int u8_strlen(const char *s); - -class UTF8 { -public: - static const u32 INVALID = (u32)-1; - UTF8(const char *c) : c_(c), index_(0) {} - bool end() const { return c_[index_] == 0; } - u32 next() { - return u8_nextchar(c_, &index_); - } - u32 peek() { - int tempIndex = index_; - return u8_nextchar(c_, &tempIndex); - } - int length() const { - return u8_strlen(c_); - } - int byteIndex() const { - return index_; - } - static int encode(char *dest, u32 ch) { - return u8_wc_toutf8(dest, ch); - } - -private: - const char *c_; - int index_; -}; - -int UTF8StringNonASCIICount(const char *utf8string); - -bool UTF8StringHasNonASCII(const char *utf8string); - - -// UTF8 to Win32 UTF-16 -// Should be used when calling Win32 api calls -#ifdef _WIN32 - -std::string ConvertWStringToUTF8(const std::wstring &wstr); -std::string ConvertWStringToUTF8(const wchar_t *wstr); -void ConvertUTF8ToWString(wchar_t *dest, size_t destSize, const std::string &source); -std::wstring ConvertUTF8ToWString(const std::string &source); - -#endif |