Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 83437e59e2 |
@@ -38,8 +38,6 @@
|
||||
/rpcs3/Debug
|
||||
/rpcs3/Release
|
||||
/llvm_build
|
||||
/Vulkan/Vulkan-build
|
||||
/Vulkan/glslang-build
|
||||
|
||||
/wxWidgets/lib
|
||||
/bin/rpcs3.ini
|
||||
|
||||
+4
-19
@@ -3,7 +3,7 @@
|
||||
url = https://github.com/wxWidgets/wxWidgets
|
||||
ignore = dirty
|
||||
[submodule "rpcs3-ffmpeg"]
|
||||
path = 3rdparty/ffmpeg
|
||||
path = ffmpeg
|
||||
url = https://github.com/hrydgard/ppsspp-ffmpeg
|
||||
[submodule "asmjit"]
|
||||
path = asmjit
|
||||
@@ -13,24 +13,9 @@
|
||||
path = llvm
|
||||
url = https://github.com/llvm-mirror/llvm
|
||||
branch = release_36
|
||||
[submodule "minidx9"]
|
||||
path = minidx9
|
||||
url = https://github.com/hrydgard/minidx9.git
|
||||
[submodule "rsx_program_decompiler"]
|
||||
path = rsx_program_decompiler
|
||||
url = https://github.com/RPCS3/rsx_program_decompiler
|
||||
[submodule "GSL"]
|
||||
path = 3rdparty/GSL
|
||||
url = https://github.com/Microsoft/GSL.git
|
||||
[submodule "libpng"]
|
||||
path = 3rdparty/libpng
|
||||
url = https://github.com/RPCS3/libpng
|
||||
[submodule "Vulkan/glslang"]
|
||||
path = Vulkan/glslang
|
||||
url = https://github.com/KhronosGroup/glslang.git
|
||||
[submodule "Vulkan/Vulkan-LoaderAndValidationLayers"]
|
||||
path = Vulkan/Vulkan-LoaderAndValidationLayers
|
||||
url = https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers
|
||||
[submodule "Utilities/yaml-cpp"]
|
||||
path = Utilities/yaml-cpp
|
||||
url = https://github.com/RPCS3/yaml-cpp
|
||||
[submodule "3rdparty/pugixml"]
|
||||
path = 3rdparty/pugixml
|
||||
url = https://github.com/RPCS3/pugixml
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"userBlacklist": ["AlexAltea"]
|
||||
"userBlacklist": [AlexAltea]
|
||||
}
|
||||
+5
-9
@@ -33,13 +33,11 @@ git:
|
||||
before_install:
|
||||
# shutdown services on Travis, which may have a memory impact
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
echo "yes" | sudo apt-add-repository 'deb http://repos.codelite.org/wx3.0/ubuntu/ precise universe';
|
||||
sudo apt-get install libwxgtk3.0-dev;
|
||||
sudo apt-add-repository -y ppa:libreoffice/ppa;
|
||||
sudo apt-get update;
|
||||
sudo apt-get install libglew-dev;
|
||||
fi;
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CXX" = "g++" ]; then
|
||||
export CXX="g++-5" CC="gcc-5" CXXFLAGS="-Wno-format-security";
|
||||
export CXX="g++-4.9" CC="gcc-4.9" CXXFLAGS="-Wno-format-security";
|
||||
export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01';
|
||||
elif [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||
export CXX="clang++-3.6" CC="clang-3.6";
|
||||
@@ -53,7 +51,7 @@ before_install:
|
||||
fi;
|
||||
|
||||
before_script:
|
||||
- git submodule update --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers Utilities/yaml-cpp
|
||||
- git submodule update --init asmjit ffmpeg rsx_program_decompiler
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then cmake ..; else cmake .. -DLLVM_DIR=/usr/local/opt/llvm36/lib/llvm-3.6/share/llvm/cmake; fi
|
||||
@@ -75,17 +73,15 @@ addons:
|
||||
- cmake
|
||||
- libopenal-dev
|
||||
- freeglut3-dev
|
||||
# - libglew-dev apt version is too old
|
||||
- libglew-dev
|
||||
- libc6-dev
|
||||
- llvm-3.6
|
||||
- llvm-3.6-dev
|
||||
- libedit-dev
|
||||
- g++-5
|
||||
- gcc-5
|
||||
- g++-4.9
|
||||
- clang-3.6
|
||||
- libstdc++-4.8-dev
|
||||
- lib32stdc++6
|
||||
- zlib1g-dev
|
||||
coverity_scan:
|
||||
project:
|
||||
name: $TRAVIS_REPO_SLUG
|
||||
|
||||
Vendored
-1
Submodule 3rdparty/GSL deleted from fc5fce4f4f
Vendored
-1282
File diff suppressed because it is too large
Load Diff
Vendored
-263
@@ -1,263 +0,0 @@
|
||||
/***************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* File: audiodefs.h
|
||||
* Content: Basic constants and data types for audio work.
|
||||
*
|
||||
* Remarks: This header file defines all of the audio format constants and
|
||||
* structures required for XAudio2 and XACT work. Providing these
|
||||
* in a single location avoids certain dependency problems in the
|
||||
* legacy audio headers (mmreg.h, mmsystem.h, ksmedia.h).
|
||||
*
|
||||
* NOTE: Including the legacy headers after this one may cause a
|
||||
* compilation error, because they define some of the same types
|
||||
* defined here without preprocessor guards to avoid multiple
|
||||
* definitions. If a source file needs one of the old headers,
|
||||
* it must include it before including audiodefs.h.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef __AUDIODEFS_INCLUDED__
|
||||
#define __AUDIODEFS_INCLUDED__
|
||||
|
||||
#include <windef.h> // For WORD, DWORD, etc.
|
||||
|
||||
#pragma pack(push, 1) // Pack structures to 1-byte boundaries
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* WAVEFORMATEX: Base structure for many audio formats. Format-specific
|
||||
* extensions can be defined for particular formats by using a non-zero
|
||||
* cbSize value and adding extra fields to the end of this structure.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _WAVEFORMATEX_
|
||||
|
||||
#define _WAVEFORMATEX_
|
||||
typedef struct tWAVEFORMATEX
|
||||
{
|
||||
WORD wFormatTag; // Integer identifier of the format
|
||||
WORD nChannels; // Number of audio channels
|
||||
DWORD nSamplesPerSec; // Audio sample rate
|
||||
DWORD nAvgBytesPerSec; // Bytes per second (possibly approximate)
|
||||
WORD nBlockAlign; // Size in bytes of a sample block (all channels)
|
||||
WORD wBitsPerSample; // Size in bits of a single per-channel sample
|
||||
WORD cbSize; // Bytes of extra data appended to this struct
|
||||
} WAVEFORMATEX;
|
||||
|
||||
#endif
|
||||
|
||||
// Defining pointer types outside of the #if block to make sure they are
|
||||
// defined even if mmreg.h or mmsystem.h is #included before this file
|
||||
|
||||
typedef WAVEFORMATEX *PWAVEFORMATEX, *NPWAVEFORMATEX, *LPWAVEFORMATEX;
|
||||
typedef const WAVEFORMATEX *PCWAVEFORMATEX, *LPCWAVEFORMATEX;
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* WAVEFORMATEXTENSIBLE: Extended version of WAVEFORMATEX that should be
|
||||
* used as a basis for all new audio formats. The format tag is replaced
|
||||
* with a GUID, allowing new formats to be defined without registering a
|
||||
* format tag with Microsoft. There are also new fields that can be used
|
||||
* to specify the spatial positions for each channel and the bit packing
|
||||
* used for wide samples (e.g. 24-bit PCM samples in 32-bit containers).
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _WAVEFORMATEXTENSIBLE_
|
||||
|
||||
#define _WAVEFORMATEXTENSIBLE_
|
||||
typedef struct
|
||||
{
|
||||
WAVEFORMATEX Format; // Base WAVEFORMATEX data
|
||||
union
|
||||
{
|
||||
WORD wValidBitsPerSample; // Valid bits in each sample container
|
||||
WORD wSamplesPerBlock; // Samples per block of audio data; valid
|
||||
// if wBitsPerSample=0 (but rarely used).
|
||||
WORD wReserved; // Zero if neither case above applies.
|
||||
} Samples;
|
||||
DWORD dwChannelMask; // Positions of the audio channels
|
||||
GUID SubFormat; // Format identifier GUID
|
||||
} WAVEFORMATEXTENSIBLE;
|
||||
|
||||
#endif
|
||||
|
||||
typedef WAVEFORMATEXTENSIBLE *PWAVEFORMATEXTENSIBLE, *LPWAVEFORMATEXTENSIBLE;
|
||||
typedef const WAVEFORMATEXTENSIBLE *PCWAVEFORMATEXTENSIBLE, *LPCWAVEFORMATEXTENSIBLE;
|
||||
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Define the most common wave format tags used in WAVEFORMATEX formats.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef WAVE_FORMAT_PCM // Pulse Code Modulation
|
||||
|
||||
// If WAVE_FORMAT_PCM is not defined, we need to define some legacy types
|
||||
// for compatibility with the Windows mmreg.h / mmsystem.h header files.
|
||||
|
||||
// Old general format structure (information common to all formats)
|
||||
typedef struct waveformat_tag
|
||||
{
|
||||
WORD wFormatTag;
|
||||
WORD nChannels;
|
||||
DWORD nSamplesPerSec;
|
||||
DWORD nAvgBytesPerSec;
|
||||
WORD nBlockAlign;
|
||||
} WAVEFORMAT, *PWAVEFORMAT, NEAR *NPWAVEFORMAT, FAR *LPWAVEFORMAT;
|
||||
|
||||
// Specific format structure for PCM data
|
||||
typedef struct pcmwaveformat_tag
|
||||
{
|
||||
WAVEFORMAT wf;
|
||||
WORD wBitsPerSample;
|
||||
} PCMWAVEFORMAT, *PPCMWAVEFORMAT, NEAR *NPPCMWAVEFORMAT, FAR *LPPCMWAVEFORMAT;
|
||||
|
||||
#define WAVE_FORMAT_PCM 0x0001
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef WAVE_FORMAT_ADPCM // Microsoft Adaptive Differental PCM
|
||||
|
||||
// Replicate the Microsoft ADPCM type definitions from mmreg.h.
|
||||
|
||||
typedef struct adpcmcoef_tag
|
||||
{
|
||||
short iCoef1;
|
||||
short iCoef2;
|
||||
} ADPCMCOEFSET;
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4200) // Disable zero-sized array warnings
|
||||
|
||||
typedef struct adpcmwaveformat_tag {
|
||||
WAVEFORMATEX wfx;
|
||||
WORD wSamplesPerBlock;
|
||||
WORD wNumCoef;
|
||||
ADPCMCOEFSET aCoef[]; // Always 7 coefficient pairs for MS ADPCM
|
||||
} ADPCMWAVEFORMAT;
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#define WAVE_FORMAT_ADPCM 0x0002
|
||||
|
||||
#endif
|
||||
|
||||
// Other frequently used format tags
|
||||
|
||||
#ifndef WAVE_FORMAT_UNKNOWN
|
||||
#define WAVE_FORMAT_UNKNOWN 0x0000 // Unknown or invalid format tag
|
||||
#endif
|
||||
|
||||
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
||||
#define WAVE_FORMAT_IEEE_FLOAT 0x0003 // 32-bit floating-point
|
||||
#endif
|
||||
|
||||
#ifndef WAVE_FORMAT_MPEGLAYER3
|
||||
#define WAVE_FORMAT_MPEGLAYER3 0x0055 // ISO/MPEG Layer3
|
||||
#endif
|
||||
|
||||
#ifndef WAVE_FORMAT_DOLBY_AC3_SPDIF
|
||||
#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 // Dolby Audio Codec 3 over S/PDIF
|
||||
#endif
|
||||
|
||||
#ifndef WAVE_FORMAT_WMAUDIO2
|
||||
#define WAVE_FORMAT_WMAUDIO2 0x0161 // Windows Media Audio
|
||||
#endif
|
||||
|
||||
#ifndef WAVE_FORMAT_WMAUDIO3
|
||||
#define WAVE_FORMAT_WMAUDIO3 0x0162 // Windows Media Audio Pro
|
||||
#endif
|
||||
|
||||
#ifndef WAVE_FORMAT_WMASPDIF
|
||||
#define WAVE_FORMAT_WMASPDIF 0x0164 // Windows Media Audio over S/PDIF
|
||||
#endif
|
||||
|
||||
#ifndef WAVE_FORMAT_EXTENSIBLE
|
||||
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE // All WAVEFORMATEXTENSIBLE formats
|
||||
#endif
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Define the most common wave format GUIDs used in WAVEFORMATEXTENSIBLE
|
||||
* formats. Note that including the Windows ksmedia.h header after this
|
||||
* one will cause build problems; this cannot be avoided, since ksmedia.h
|
||||
* defines these macros without preprocessor guards.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef __cplusplus // uuid() and __uuidof() are only available in C++
|
||||
|
||||
#ifndef KSDATAFORMAT_SUBTYPE_PCM
|
||||
struct __declspec(uuid("00000001-0000-0010-8000-00aa00389b71")) KSDATAFORMAT_SUBTYPE_PCM_STRUCT;
|
||||
#define KSDATAFORMAT_SUBTYPE_PCM __uuidof(KSDATAFORMAT_SUBTYPE_PCM_STRUCT)
|
||||
#endif
|
||||
|
||||
#ifndef KSDATAFORMAT_SUBTYPE_ADPCM
|
||||
struct __declspec(uuid("00000002-0000-0010-8000-00aa00389b71")) KSDATAFORMAT_SUBTYPE_ADPCM_STRUCT;
|
||||
#define KSDATAFORMAT_SUBTYPE_ADPCM __uuidof(KSDATAFORMAT_SUBTYPE_ADPCM_STRUCT)
|
||||
#endif
|
||||
|
||||
#ifndef KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
|
||||
struct __declspec(uuid("00000003-0000-0010-8000-00aa00389b71")) KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_STRUCT;
|
||||
#define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT __uuidof(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT_STRUCT)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Speaker positions used in the WAVEFORMATEXTENSIBLE dwChannelMask field.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef SPEAKER_FRONT_LEFT
|
||||
#define SPEAKER_FRONT_LEFT 0x00000001
|
||||
#define SPEAKER_FRONT_RIGHT 0x00000002
|
||||
#define SPEAKER_FRONT_CENTER 0x00000004
|
||||
#define SPEAKER_LOW_FREQUENCY 0x00000008
|
||||
#define SPEAKER_BACK_LEFT 0x00000010
|
||||
#define SPEAKER_BACK_RIGHT 0x00000020
|
||||
#define SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040
|
||||
#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080
|
||||
#define SPEAKER_BACK_CENTER 0x00000100
|
||||
#define SPEAKER_SIDE_LEFT 0x00000200
|
||||
#define SPEAKER_SIDE_RIGHT 0x00000400
|
||||
#define SPEAKER_TOP_CENTER 0x00000800
|
||||
#define SPEAKER_TOP_FRONT_LEFT 0x00001000
|
||||
#define SPEAKER_TOP_FRONT_CENTER 0x00002000
|
||||
#define SPEAKER_TOP_FRONT_RIGHT 0x00004000
|
||||
#define SPEAKER_TOP_BACK_LEFT 0x00008000
|
||||
#define SPEAKER_TOP_BACK_CENTER 0x00010000
|
||||
#define SPEAKER_TOP_BACK_RIGHT 0x00020000
|
||||
#define SPEAKER_RESERVED 0x7FFC0000
|
||||
#define SPEAKER_ALL 0x80000000
|
||||
#define _SPEAKER_POSITIONS_
|
||||
#endif
|
||||
|
||||
#ifndef SPEAKER_STEREO
|
||||
#define SPEAKER_MONO (SPEAKER_FRONT_CENTER)
|
||||
#define SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
|
||||
#define SPEAKER_2POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY)
|
||||
#define SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER)
|
||||
#define SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
|
||||
#define SPEAKER_4POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
|
||||
#define SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
|
||||
#define SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER)
|
||||
#define SPEAKER_5POINT1_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
|
||||
#define SPEAKER_7POINT1_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
|
||||
#endif
|
||||
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // #ifndef __AUDIODEFS_INCLUDED__
|
||||
Vendored
-59
@@ -1,59 +0,0 @@
|
||||
// comdecl.h: Macros to facilitate COM interface and GUID declarations.
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
#ifndef _COMDECL_H_
|
||||
#define _COMDECL_H_
|
||||
|
||||
#ifndef _XBOX
|
||||
#include <basetyps.h> // For standard COM interface macros
|
||||
#else
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4061)
|
||||
#include <xtl.h> // Required by xobjbase.h
|
||||
#include <xobjbase.h> // Special definitions for Xbox build
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// The DEFINE_CLSID() and DEFINE_IID() macros defined below allow COM GUIDs to
|
||||
// be declared and defined in such a way that clients can obtain the GUIDs using
|
||||
// either the __uuidof() extension or the old-style CLSID_Foo / IID_IFoo names.
|
||||
// If using the latter approach, the client can also choose whether to get the
|
||||
// GUID definitions by defining the INITGUID preprocessor constant or by linking
|
||||
// to a GUID library. This works in either C or C++.
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#define DECLSPEC_UUID_WRAPPER(x) __declspec(uuid(#x))
|
||||
#ifdef INITGUID
|
||||
|
||||
#define DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
class DECLSPEC_UUID_WRAPPER(l##-##w1##-##w2##-##b1##b2##-##b3##b4##b5##b6##b7##b8) className; \
|
||||
EXTERN_C const GUID DECLSPEC_SELECTANY CLSID_##className = __uuidof(className)
|
||||
|
||||
#define DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
interface DECLSPEC_UUID_WRAPPER(l##-##w1##-##w2##-##b1##b2##-##b3##b4##b5##b6##b7##b8) interfaceName; \
|
||||
EXTERN_C const GUID DECLSPEC_SELECTANY IID_##interfaceName = __uuidof(interfaceName)
|
||||
|
||||
#else // INITGUID
|
||||
|
||||
#define DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
class DECLSPEC_UUID_WRAPPER(l##-##w1##-##w2##-##b1##b2##-##b3##b4##b5##b6##b7##b8) className; \
|
||||
EXTERN_C const GUID CLSID_##className
|
||||
|
||||
#define DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
interface DECLSPEC_UUID_WRAPPER(l##-##w1##-##w2##-##b1##b2##-##b3##b4##b5##b6##b7##b8) interfaceName; \
|
||||
EXTERN_C const GUID IID_##interfaceName
|
||||
|
||||
#endif // INITGUID
|
||||
|
||||
#else // __cplusplus
|
||||
|
||||
#define DEFINE_CLSID(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
DEFINE_GUID(CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
|
||||
|
||||
#define DEFINE_IID(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
DEFINE_GUID(IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // #ifndef _COMDECL_H_
|
||||
Vendored
-18
@@ -1,18 +0,0 @@
|
||||
/*==========================================================================;
|
||||
*
|
||||
*
|
||||
* File: dxsdkver.h
|
||||
* Content: DirectX SDK Version Include File
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef _DXSDKVER_H_
|
||||
#define _DXSDKVER_H_
|
||||
|
||||
#define _DXSDK_PRODUCT_MAJOR 9
|
||||
#define _DXSDK_PRODUCT_MINOR 29
|
||||
#define _DXSDK_BUILD_MAJOR 1962
|
||||
#define _DXSDK_BUILD_MINOR 0
|
||||
|
||||
#endif // _DXSDKVER_H_
|
||||
|
||||
Vendored
-718
@@ -1,718 +0,0 @@
|
||||
/***************************************************************************
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*
|
||||
* File: xma2defs.h
|
||||
* Content: Constants, data types and functions for XMA2 compressed audio.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef __XMA2DEFS_INCLUDED__
|
||||
#define __XMA2DEFS_INCLUDED__
|
||||
|
||||
#include <sal.h> // Markers for documenting API semantics
|
||||
#include <winerror.h> // For S_OK, E_FAIL
|
||||
#include <audiodefs.h> // Basic data types and constants for audio work
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* Overview
|
||||
***************************************************************************/
|
||||
|
||||
// A typical XMA2 file contains these RIFF chunks:
|
||||
//
|
||||
// 'fmt' or 'XMA2' chunk (or both): A description of the XMA data's structure
|
||||
// and characteristics (length, channels, sample rate, loops, block size, etc).
|
||||
//
|
||||
// 'seek' chunk: A seek table to help navigate the XMA data.
|
||||
//
|
||||
// 'data' chunk: The encoded XMA2 data.
|
||||
//
|
||||
// The encoded XMA2 data is structured as a set of BLOCKS, which contain PACKETS,
|
||||
// which contain FRAMES, which contain SUBFRAMES (roughly speaking). The frames
|
||||
// in a file may also be divided into several subsets, called STREAMS.
|
||||
//
|
||||
// FRAME: A variable-sized segment of XMA data that decodes to exactly 512 mono
|
||||
// or stereo PCM samples. This is the smallest unit of XMA data that can
|
||||
// be decoded in isolation. Frames are an arbitrary number of bits in
|
||||
// length, and need not be byte-aligned. See "XMA frame structure" below.
|
||||
//
|
||||
// SUBFRAME: A region of bits in an XMA frame that decodes to 128 mono or stereo
|
||||
// samples. The XMA decoder cannot decode a subframe in isolation; it needs
|
||||
// a whole frame to work with. However, it can begin emitting the frame's
|
||||
// decoded samples at any one of the four subframe boundaries. Subframes
|
||||
// can be addressed for seeking and looping purposes.
|
||||
//
|
||||
// PACKET: A 2Kb region containing a 32-bit header and some XMA frames. Frames
|
||||
// can (and usually do) span packets. A packet's header includes the offset
|
||||
// in bits of the first frame that begins within that packet. All of the
|
||||
// frames that begin in a given packet belong to the same "stream" (see the
|
||||
// Multichannel Audio section below).
|
||||
//
|
||||
// STREAM: A set of packets within an XMA file that all contain data for the
|
||||
// same mono or stereo component of a PCM file with more than two channels.
|
||||
// The packets comprising a given stream may be interleaved with each other
|
||||
// more or less arbitrarily; see Multichannel Audio.
|
||||
//
|
||||
// BLOCK: An array of XMA packets; or, to break it down differently, a series of
|
||||
// consecutive XMA frames, padded at the end with reserved data. A block
|
||||
// must contain at least one 2Kb packet per stream, and it can hold up to
|
||||
// 4095 packets (8190Kb), but its size is typically in the 32Kb-128Kb range.
|
||||
// (The size chosen involves a trade-off between memory use and efficiency
|
||||
// of reading from permanent storage.)
|
||||
//
|
||||
// XMA frames do not span blocks, so a block is guaranteed to begin with a
|
||||
// set of complete frames, one per stream. Also, a block in a multi-stream
|
||||
// XMA2 file always contains the same number of samples for each stream;
|
||||
// see Multichannel Audio.
|
||||
//
|
||||
// The 'data' chunk in an XMA2 file is an array of XMA2WAVEFORMAT.BlockCount XMA
|
||||
// blocks, all the same size (as specified in XMA2WAVEFORMAT.BlockSizeInBytes)
|
||||
// except for the last one, which may be shorter.
|
||||
|
||||
|
||||
// MULTICHANNEL AUDIO: the XMA decoder can only decode raw XMA data into either
|
||||
// mono or stereo PCM data. In order to encode a 6-channel file (say), the file
|
||||
// must be deinterleaved into 3 stereo streams that are encoded independently,
|
||||
// producing 3 encoded XMA data streams. Then the packets in these 3 streams
|
||||
// are interleaved to produce a single XMA2 file, and some information is added
|
||||
// to the file so that the original 6-channel audio can be reconstructed at
|
||||
// decode time. This works using the concept of an XMA stream (see above).
|
||||
//
|
||||
// The frames for all the streams in an XMA file are interleaved in an arbitrary
|
||||
// order. To locate a frame that belongs to a given stream in a given XMA block,
|
||||
// you must examine the first few packets in the block. Here (and only here) the
|
||||
// packets are guaranteed to be presented in stream order, so that all frames
|
||||
// beginning in packet 0 belong to stream 0 (the first stereo pair), etc.
|
||||
//
|
||||
// (This means that when decoding multi-stream XMA files, only entire XMA blocks
|
||||
// should be submitted to the decoder; otherwise it cannot know which frames
|
||||
// belong to which stream.)
|
||||
//
|
||||
// Once you have one frame that belongs to a given stream, you can find the next
|
||||
// one by looking at the frame's 'NextFrameOffsetBits' value (which is stored in
|
||||
// its first 15 bits; see XMAFRAME below). The GetXmaFrameBitPosition function
|
||||
// uses this technique.
|
||||
|
||||
|
||||
// SEEKING IN XMA2 FILES: Here is some pseudocode to find the byte position and
|
||||
// subframe in an XMA2 file which will contain sample S when decoded.
|
||||
//
|
||||
// 1. Traverse the seek table to find the XMA2 block containing sample S. The
|
||||
// seek table is an array of big-endian DWORDs, one per block in the file.
|
||||
// The Nth DWORD is the total number of PCM samples that would be obtained
|
||||
// by decoding the entire XMA file up to the end of block N. Hence, the
|
||||
// block we want is the first one whose seek table entry is greater than S.
|
||||
// (See the GetXmaBlockContainingSample helper function.)
|
||||
//
|
||||
// 2. Calculate which frame F within the block found above contains sample S.
|
||||
// Since each frame decodes to 512 samples, this is straightforward. The
|
||||
// first frame in the block produces samples X to X + 512, where X is the
|
||||
// seek table entry for the prior block. So F is (S - X) / 512.
|
||||
//
|
||||
// 3. Find the bit offset within the block where frame F starts. Since frames
|
||||
// are variable-sized, this can only be done by traversing all the frames in
|
||||
// the block until we reach frame F. (See GetXmaFrameBitPosition.)
|
||||
//
|
||||
// 4. Frame F has four 128-sample subframes. To find the subframe containing S,
|
||||
// we can use the formula (S % 512) / 128.
|
||||
//
|
||||
// In the case of multi-stream XMA files, sample S is a multichannel sample with
|
||||
// parts coming from several frames, one per stream. To find all these frames,
|
||||
// steps 2-4 need to be repeated for each stream N, using the knowledge that the
|
||||
// first packets in a block are presented in stream order. The frame traversal
|
||||
// in step 3 must be started at the first frame in the Nth packet of the block,
|
||||
// which will be the first frame for stream N. (And the packet header will tell
|
||||
// you the first frame's start position within the packet.)
|
||||
//
|
||||
// Step 1 can be performed using the GetXmaBlockContainingSample function below,
|
||||
// and steps 2-4 by calling GetXmaDecodePositionForSample once for each stream.
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* XMA constants
|
||||
***************************************************************************/
|
||||
|
||||
// Size of the PCM samples produced by the XMA decoder
|
||||
#define XMA_OUTPUT_SAMPLE_BYTES 2u
|
||||
#define XMA_OUTPUT_SAMPLE_BITS (XMA_OUTPUT_SAMPLE_BYTES * 8u)
|
||||
|
||||
// Size of an XMA packet
|
||||
#define XMA_BYTES_PER_PACKET 2048u
|
||||
#define XMA_BITS_PER_PACKET (XMA_BYTES_PER_PACKET * 8u)
|
||||
|
||||
// Size of an XMA packet header
|
||||
#define XMA_PACKET_HEADER_BYTES 4u
|
||||
#define XMA_PACKET_HEADER_BITS (XMA_PACKET_HEADER_BYTES * 8u)
|
||||
|
||||
// Sample blocks in a decoded XMA frame
|
||||
#define XMA_SAMPLES_PER_FRAME 512u
|
||||
|
||||
// Sample blocks in a decoded XMA subframe
|
||||
#define XMA_SAMPLES_PER_SUBFRAME 128u
|
||||
|
||||
// Maximum encoded data that can be submitted to the XMA decoder at a time
|
||||
#define XMA_READBUFFER_MAX_PACKETS 4095u
|
||||
#define XMA_READBUFFER_MAX_BYTES (XMA_READBUFFER_MAX_PACKETS * XMA_BYTES_PER_PACKET)
|
||||
|
||||
// Maximum size allowed for the XMA decoder's output buffers
|
||||
#define XMA_WRITEBUFFER_MAX_BYTES (31u * 256u)
|
||||
|
||||
// Required byte alignment of the XMA decoder's output buffers
|
||||
#define XMA_WRITEBUFFER_BYTE_ALIGNMENT 256u
|
||||
|
||||
// Decode chunk sizes for the XMA_PLAYBACK_INIT.subframesToDecode field
|
||||
#define XMA_MIN_SUBFRAMES_TO_DECODE 1u
|
||||
#define XMA_MAX_SUBFRAMES_TO_DECODE 8u
|
||||
#define XMA_OPTIMAL_SUBFRAMES_TO_DECODE 4u
|
||||
|
||||
// LoopCount<255 means finite repetitions; LoopCount=255 means infinite looping
|
||||
#define XMA_MAX_LOOPCOUNT 254u
|
||||
#define XMA_INFINITE_LOOP 255u
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* XMA format structures
|
||||
***************************************************************************/
|
||||
|
||||
// The currently recommended way to express format information for XMA2 files
|
||||
// is the XMA2WAVEFORMATEX structure. This structure is fully compliant with
|
||||
// the WAVEFORMATEX standard and contains all the information needed to parse
|
||||
// and manage XMA2 files in a compact way.
|
||||
|
||||
#define WAVE_FORMAT_XMA2 0x166
|
||||
|
||||
typedef struct XMA2WAVEFORMATEX
|
||||
{
|
||||
WAVEFORMATEX wfx;
|
||||
// Meaning of the WAVEFORMATEX fields here:
|
||||
// wFormatTag; // Audio format type; always WAVE_FORMAT_XMA2
|
||||
// nChannels; // Channel count of the decoded audio
|
||||
// nSamplesPerSec; // Sample rate of the decoded audio
|
||||
// nAvgBytesPerSec; // Used internally by the XMA encoder
|
||||
// nBlockAlign; // Decoded sample size; channels * wBitsPerSample / 8
|
||||
// wBitsPerSample; // Bits per decoded mono sample; always 16 for XMA
|
||||
// cbSize; // Size in bytes of the rest of this structure (34)
|
||||
|
||||
WORD NumStreams; // Number of audio streams (1 or 2 channels each)
|
||||
DWORD ChannelMask; // Spatial positions of the channels in this file,
|
||||
// stored as SPEAKER_xxx values (see audiodefs.h)
|
||||
DWORD SamplesEncoded; // Total number of PCM samples the file decodes to
|
||||
DWORD BytesPerBlock; // XMA block size (but the last one may be shorter)
|
||||
DWORD PlayBegin; // First valid sample in the decoded audio
|
||||
DWORD PlayLength; // Length of the valid part of the decoded audio
|
||||
DWORD LoopBegin; // Beginning of the loop region in decoded sample terms
|
||||
DWORD LoopLength; // Length of the loop region in decoded sample terms
|
||||
BYTE LoopCount; // Number of loop repetitions; 255 = infinite
|
||||
BYTE EncoderVersion; // Version of XMA encoder that generated the file
|
||||
WORD BlockCount; // XMA blocks in file (and entries in its seek table)
|
||||
} XMA2WAVEFORMATEX, *PXMA2WAVEFORMATEX;
|
||||
|
||||
|
||||
// The legacy XMA format structures are described here for reference, but they
|
||||
// should not be used in new content. XMAWAVEFORMAT was the structure used in
|
||||
// XMA version 1 files. XMA2WAVEFORMAT was used in early XMA2 files; it is not
|
||||
// placed in the usual 'fmt' RIFF chunk but in its own 'XMA2' chunk.
|
||||
|
||||
#ifndef WAVE_FORMAT_XMA
|
||||
#define WAVE_FORMAT_XMA 0x0165
|
||||
|
||||
// Values used in the ChannelMask fields below. Similar to the SPEAKER_xxx
|
||||
// values defined in audiodefs.h, but modified to fit in a single byte.
|
||||
#ifndef XMA_SPEAKER_LEFT
|
||||
#define XMA_SPEAKER_LEFT 0x01
|
||||
#define XMA_SPEAKER_RIGHT 0x02
|
||||
#define XMA_SPEAKER_CENTER 0x04
|
||||
#define XMA_SPEAKER_LFE 0x08
|
||||
#define XMA_SPEAKER_LEFT_SURROUND 0x10
|
||||
#define XMA_SPEAKER_RIGHT_SURROUND 0x20
|
||||
#define XMA_SPEAKER_LEFT_BACK 0x40
|
||||
#define XMA_SPEAKER_RIGHT_BACK 0x80
|
||||
#endif
|
||||
|
||||
|
||||
// Used in XMAWAVEFORMAT for per-stream data
|
||||
typedef struct XMASTREAMFORMAT
|
||||
{
|
||||
DWORD PsuedoBytesPerSec; // Used by the XMA encoder (typo preserved for legacy reasons)
|
||||
DWORD SampleRate; // The stream's decoded sample rate (in XMA2 files,
|
||||
// this is the same for all streams in the file).
|
||||
DWORD LoopStart; // Bit offset of the frame containing the loop start
|
||||
// point, relative to the beginning of the stream.
|
||||
DWORD LoopEnd; // Bit offset of the frame containing the loop end.
|
||||
BYTE SubframeData; // Two 4-bit numbers specifying the exact location of
|
||||
// the loop points within the frames that contain them.
|
||||
// SubframeEnd: Subframe of the loop end frame where
|
||||
// the loop ends. Ranges from 0 to 3.
|
||||
// SubframeSkip: Subframes to skip in the start frame to
|
||||
// reach the loop. Ranges from 0 to 4.
|
||||
BYTE Channels; // Number of channels in the stream (1 or 2)
|
||||
WORD ChannelMask; // Spatial positions of the channels in the stream
|
||||
} XMASTREAMFORMAT;
|
||||
|
||||
// Legacy XMA1 format structure
|
||||
typedef struct XMAWAVEFORMAT
|
||||
{
|
||||
WORD FormatTag; // Audio format type (always WAVE_FORMAT_XMA)
|
||||
WORD BitsPerSample; // Bit depth (currently required to be 16)
|
||||
WORD EncodeOptions; // Options for XMA encoder/decoder
|
||||
WORD LargestSkip; // Largest skip used in interleaving streams
|
||||
WORD NumStreams; // Number of interleaved audio streams
|
||||
BYTE LoopCount; // Number of loop repetitions; 255 = infinite
|
||||
BYTE Version; // XMA encoder version that generated the file.
|
||||
// Always 3 or higher for XMA2 files.
|
||||
XMASTREAMFORMAT XmaStreams[1]; // Per-stream format information; the actual
|
||||
// array length is in the NumStreams field.
|
||||
} XMAWAVEFORMAT;
|
||||
|
||||
|
||||
// Used in XMA2WAVEFORMAT for per-stream data
|
||||
typedef struct XMA2STREAMFORMAT
|
||||
{
|
||||
BYTE Channels; // Number of channels in the stream (1 or 2)
|
||||
BYTE RESERVED; // Reserved for future use
|
||||
WORD ChannelMask; // Spatial positions of the channels in the stream
|
||||
} XMA2STREAMFORMAT;
|
||||
|
||||
// Legacy XMA2 format structure (big-endian byte ordering)
|
||||
typedef struct XMA2WAVEFORMAT
|
||||
{
|
||||
BYTE Version; // XMA encoder version that generated the file.
|
||||
// Always 3 or higher for XMA2 files.
|
||||
BYTE NumStreams; // Number of interleaved audio streams
|
||||
BYTE RESERVED; // Reserved for future use
|
||||
BYTE LoopCount; // Number of loop repetitions; 255 = infinite
|
||||
DWORD LoopBegin; // Loop begin point, in samples
|
||||
DWORD LoopEnd; // Loop end point, in samples
|
||||
DWORD SampleRate; // The file's decoded sample rate
|
||||
DWORD EncodeOptions; // Options for the XMA encoder/decoder
|
||||
DWORD PsuedoBytesPerSec; // Used internally by the XMA encoder
|
||||
DWORD BlockSizeInBytes; // Size in bytes of this file's XMA blocks (except
|
||||
// possibly the last one). Always a multiple of
|
||||
// 2Kb, since XMA blocks are arrays of 2Kb packets.
|
||||
DWORD SamplesEncoded; // Total number of PCM samples encoded in this file
|
||||
DWORD SamplesInSource; // Actual number of PCM samples in the source
|
||||
// material used to generate this file
|
||||
DWORD BlockCount; // Number of XMA blocks in this file (and hence
|
||||
// also the number of entries in its seek table)
|
||||
XMA2STREAMFORMAT Streams[1]; // Per-stream format information; the actual
|
||||
// array length is in the NumStreams field.
|
||||
} XMA2WAVEFORMAT;
|
||||
|
||||
#endif // #ifndef WAVE_FORMAT_XMA
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* XMA packet structure (in big-endian form)
|
||||
***************************************************************************/
|
||||
|
||||
typedef struct XMA2PACKET
|
||||
{
|
||||
int FrameCount : 6; // Number of XMA frames that begin in this packet
|
||||
int FrameOffsetInBits : 15; // Bit of XmaData where the first complete frame begins
|
||||
int PacketMetaData : 3; // Metadata stored in the packet (always 1 for XMA2)
|
||||
int PacketSkipCount : 8; // How many packets belonging to other streams must be
|
||||
// skipped to find the next packet belonging to this one
|
||||
BYTE XmaData[XMA_BYTES_PER_PACKET - sizeof(DWORD)]; // XMA encoded data
|
||||
} XMA2PACKET;
|
||||
|
||||
// E.g. if the first DWORD of a packet is 0x30107902:
|
||||
//
|
||||
// 001100 000001000001111 001 00000010
|
||||
// | | | |____ Skip 2 packets to find the next one for this stream
|
||||
// | | |___________ XMA2 signature (always 001)
|
||||
// | |_____________________ First frame starts 527 bits into packet
|
||||
// |________________________________ Packet contains 12 frames
|
||||
|
||||
|
||||
// Helper functions to extract the fields above from an XMA packet. (Note that
|
||||
// the bitfields cannot be read directly on little-endian architectures such as
|
||||
// the Intel x86, as they are laid out in big-endian form.)
|
||||
|
||||
__inline DWORD GetXmaPacketFrameCount(__in_bcount(1) const BYTE* pPacket)
|
||||
{
|
||||
return (DWORD)(pPacket[0] >> 2);
|
||||
}
|
||||
|
||||
__inline DWORD GetXmaPacketFirstFrameOffsetInBits(__in_bcount(3) const BYTE* pPacket)
|
||||
{
|
||||
return ((DWORD)(pPacket[0] & 0x3) << 13) |
|
||||
((DWORD)(pPacket[1]) << 5) |
|
||||
((DWORD)(pPacket[2]) >> 3);
|
||||
}
|
||||
|
||||
__inline DWORD GetXmaPacketMetadata(__in_bcount(3) const BYTE* pPacket)
|
||||
{
|
||||
return (DWORD)(pPacket[2] & 0x7);
|
||||
}
|
||||
|
||||
__inline DWORD GetXmaPacketSkipCount(__in_bcount(4) const BYTE* pPacket)
|
||||
{
|
||||
return (DWORD)(pPacket[3]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* XMA frame structure
|
||||
***************************************************************************/
|
||||
|
||||
// There is no way to represent the XMA frame as a C struct, since it is a
|
||||
// variable-sized string of bits that need not be stored at a byte-aligned
|
||||
// position in memory. This is the layout:
|
||||
//
|
||||
// XMAFRAME
|
||||
// {
|
||||
// LengthInBits: A 15-bit number representing the length of this frame.
|
||||
// XmaData: Encoded XMA data; its size in bits is (LengthInBits - 15).
|
||||
// }
|
||||
|
||||
// Size in bits of the frame's initial LengthInBits field
|
||||
#define XMA_BITS_IN_FRAME_LENGTH_FIELD 15
|
||||
|
||||
// Special LengthInBits value that marks an invalid final frame
|
||||
#define XMA_FINAL_FRAME_MARKER 0x7FFF
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
* XMA helper functions
|
||||
***************************************************************************/
|
||||
|
||||
// We define a local ASSERT macro to equal the global one if it exists.
|
||||
// You can define XMA2DEFS_ASSERT in advance to override this default.
|
||||
#ifndef XMA2DEFS_ASSERT
|
||||
#ifdef ASSERT
|
||||
#define XMA2DEFS_ASSERT ASSERT
|
||||
#else
|
||||
#define XMA2DEFS_ASSERT(a) /* No-op by default */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
// GetXmaBlockContainingSample: Use a given seek table to find the XMA block
|
||||
// containing a given decoded sample. Note that the seek table entries in an
|
||||
// XMA file are stored in big-endian form and may need to be converted prior
|
||||
// to calling this function.
|
||||
|
||||
__inline HRESULT GetXmaBlockContainingSample
|
||||
(
|
||||
DWORD nBlockCount, // Blocks in the file (= seek table entries)
|
||||
__in_ecount(nBlockCount) const DWORD* pSeekTable, // Pointer to the seek table data
|
||||
DWORD nDesiredSample, // Decoded sample to locate
|
||||
__out DWORD* pnBlockContainingSample, // Index of the block containing the sample
|
||||
__out DWORD* pnSampleOffsetWithinBlock // Position of the sample in this block
|
||||
)
|
||||
{
|
||||
DWORD nPreviousTotalSamples = 0;
|
||||
DWORD nBlock;
|
||||
DWORD nTotalSamplesSoFar;
|
||||
|
||||
XMA2DEFS_ASSERT(pSeekTable);
|
||||
XMA2DEFS_ASSERT(pnBlockContainingSample);
|
||||
XMA2DEFS_ASSERT(pnSampleOffsetWithinBlock);
|
||||
|
||||
for (nBlock = 0; nBlock < nBlockCount; ++nBlock)
|
||||
{
|
||||
nTotalSamplesSoFar = pSeekTable[nBlock];
|
||||
if (nTotalSamplesSoFar > nDesiredSample)
|
||||
{
|
||||
*pnBlockContainingSample = nBlock;
|
||||
*pnSampleOffsetWithinBlock = nDesiredSample - nPreviousTotalSamples;
|
||||
return S_OK;
|
||||
}
|
||||
nPreviousTotalSamples = nTotalSamplesSoFar;
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
|
||||
// GetXmaFrameLengthInBits: Reads a given frame's LengthInBits field.
|
||||
|
||||
__inline DWORD GetXmaFrameLengthInBits
|
||||
(
|
||||
__in_bcount(nBitPosition / 8 + 3)
|
||||
__in const BYTE* pPacket, // Pointer to XMA packet[s] containing the frame
|
||||
DWORD nBitPosition // Bit offset of the frame within this packet
|
||||
)
|
||||
{
|
||||
DWORD nRegion;
|
||||
DWORD nBytePosition = nBitPosition / 8;
|
||||
DWORD nBitOffset = nBitPosition % 8;
|
||||
|
||||
if (nBitOffset < 2) // Only need to read 2 bytes (and might not be safe to read more)
|
||||
{
|
||||
nRegion = (DWORD)(pPacket[nBytePosition+0]) << 8 |
|
||||
(DWORD)(pPacket[nBytePosition+1]);
|
||||
return (nRegion >> (1 - nBitOffset)) & 0x7FFF; // Last 15 bits
|
||||
}
|
||||
else // Need to read 3 bytes
|
||||
{
|
||||
nRegion = (DWORD)(pPacket[nBytePosition+0]) << 16 |
|
||||
(DWORD)(pPacket[nBytePosition+1]) << 8 |
|
||||
(DWORD)(pPacket[nBytePosition+2]);
|
||||
return (nRegion >> (9 - nBitOffset)) & 0x7FFF; // Last 15 bits
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// GetXmaFrameBitPosition: Calculates the bit offset of a given frame within
|
||||
// an XMA block or set of blocks. Returns 0 on failure.
|
||||
|
||||
__inline DWORD GetXmaFrameBitPosition
|
||||
(
|
||||
__in_bcount(nXmaDataBytes) const BYTE* pXmaData, // Pointer to XMA block[s]
|
||||
DWORD nXmaDataBytes, // Size of pXmaData in bytes
|
||||
DWORD nStreamIndex, // Stream within which to seek
|
||||
DWORD nDesiredFrame // Frame sought
|
||||
)
|
||||
{
|
||||
const BYTE* pCurrentPacket;
|
||||
DWORD nPacketsExamined = 0;
|
||||
DWORD nFrameCountSoFar = 0;
|
||||
DWORD nFramesToSkip;
|
||||
DWORD nFrameBitOffset;
|
||||
|
||||
XMA2DEFS_ASSERT(pXmaData);
|
||||
XMA2DEFS_ASSERT(nXmaDataBytes % XMA_BYTES_PER_PACKET == 0);
|
||||
|
||||
// Get the first XMA packet belonging to the desired stream, relying on the
|
||||
// fact that the first packets for each stream are in consecutive order at
|
||||
// the beginning of an XMA block.
|
||||
|
||||
pCurrentPacket = pXmaData + nStreamIndex * XMA_BYTES_PER_PACKET;
|
||||
for (;;)
|
||||
{
|
||||
// If we have exceeded the size of the XMA data, return failure
|
||||
if (pCurrentPacket + XMA_BYTES_PER_PACKET > pXmaData + nXmaDataBytes)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the current packet contains the frame we are looking for...
|
||||
if (nFrameCountSoFar + GetXmaPacketFrameCount(pCurrentPacket) > nDesiredFrame)
|
||||
{
|
||||
// See how many frames in this packet we need to skip to get to it
|
||||
XMA2DEFS_ASSERT(nDesiredFrame >= nFrameCountSoFar);
|
||||
nFramesToSkip = nDesiredFrame - nFrameCountSoFar;
|
||||
|
||||
// Get the bit offset of the first frame in this packet
|
||||
nFrameBitOffset = XMA_PACKET_HEADER_BITS + GetXmaPacketFirstFrameOffsetInBits(pCurrentPacket);
|
||||
|
||||
// Advance nFrameBitOffset to the frame of interest
|
||||
while (nFramesToSkip--)
|
||||
{
|
||||
nFrameBitOffset += GetXmaFrameLengthInBits(pCurrentPacket, nFrameBitOffset);
|
||||
}
|
||||
|
||||
// The bit offset to return is the number of bits from pXmaData to
|
||||
// pCurrentPacket plus the bit offset of the frame of interest
|
||||
return (DWORD)(pCurrentPacket - pXmaData) * 8 + nFrameBitOffset;
|
||||
}
|
||||
|
||||
// If we haven't found the right packet yet, advance our counters
|
||||
++nPacketsExamined;
|
||||
nFrameCountSoFar += GetXmaPacketFrameCount(pCurrentPacket);
|
||||
|
||||
// And skip to the next packet belonging to the same stream
|
||||
pCurrentPacket += XMA_BYTES_PER_PACKET * (GetXmaPacketSkipCount(pCurrentPacket) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// GetLastXmaFrameBitPosition: Calculates the bit offset of the last complete
|
||||
// frame in an XMA block or set of blocks.
|
||||
|
||||
__inline DWORD GetLastXmaFrameBitPosition
|
||||
(
|
||||
__in_bcount(nXmaDataBytes) const BYTE* pXmaData, // Pointer to XMA block[s]
|
||||
DWORD nXmaDataBytes, // Size of pXmaData in bytes
|
||||
DWORD nStreamIndex // Stream within which to seek
|
||||
)
|
||||
{
|
||||
const BYTE* pLastPacket;
|
||||
DWORD nBytesToNextPacket;
|
||||
DWORD nFrameBitOffset;
|
||||
DWORD nFramesInLastPacket;
|
||||
|
||||
XMA2DEFS_ASSERT(pXmaData);
|
||||
XMA2DEFS_ASSERT(nXmaDataBytes % XMA_BYTES_PER_PACKET == 0);
|
||||
XMA2DEFS_ASSERT(nXmaDataBytes >= XMA_BYTES_PER_PACKET * (nStreamIndex + 1));
|
||||
|
||||
// Get the first XMA packet belonging to the desired stream, relying on the
|
||||
// fact that the first packets for each stream are in consecutive order at
|
||||
// the beginning of an XMA block.
|
||||
pLastPacket = pXmaData + nStreamIndex * XMA_BYTES_PER_PACKET;
|
||||
|
||||
// Search for the last packet belonging to the desired stream
|
||||
for (;;)
|
||||
{
|
||||
nBytesToNextPacket = XMA_BYTES_PER_PACKET * (GetXmaPacketSkipCount(pLastPacket) + 1);
|
||||
XMA2DEFS_ASSERT(nBytesToNextPacket);
|
||||
if (pLastPacket + nBytesToNextPacket + XMA_BYTES_PER_PACKET > pXmaData + nXmaDataBytes)
|
||||
{
|
||||
break; // The next packet would extend beyond the end of pXmaData
|
||||
}
|
||||
pLastPacket += nBytesToNextPacket;
|
||||
}
|
||||
|
||||
// The last packet can sometimes have no seekable frames, in which case we
|
||||
// have to use the previous one
|
||||
if (GetXmaPacketFrameCount(pLastPacket) == 0)
|
||||
{
|
||||
pLastPacket -= nBytesToNextPacket;
|
||||
}
|
||||
|
||||
// Found the last packet. Get the bit offset of its first frame.
|
||||
nFrameBitOffset = XMA_PACKET_HEADER_BITS + GetXmaPacketFirstFrameOffsetInBits(pLastPacket);
|
||||
|
||||
// Traverse frames until we reach the last one
|
||||
nFramesInLastPacket = GetXmaPacketFrameCount(pLastPacket);
|
||||
while (--nFramesInLastPacket)
|
||||
{
|
||||
nFrameBitOffset += GetXmaFrameLengthInBits(pLastPacket, nFrameBitOffset);
|
||||
}
|
||||
|
||||
// The bit offset to return is the number of bits from pXmaData to
|
||||
// pLastPacket plus the offset of the last frame in this packet.
|
||||
return (DWORD)(pLastPacket - pXmaData) * 8 + nFrameBitOffset;
|
||||
}
|
||||
|
||||
|
||||
// GetXmaDecodePositionForSample: Obtains the information needed to make the
|
||||
// decoder generate audio starting at a given sample position relative to the
|
||||
// beginning of the given XMA block: the bit offset of the appropriate frame,
|
||||
// and the right subframe within that frame. This data can be passed directly
|
||||
// to the XMAPlaybackSetDecodePosition function.
|
||||
|
||||
__inline HRESULT GetXmaDecodePositionForSample
|
||||
(
|
||||
__in_bcount(nXmaDataBytes) const BYTE* pXmaData, // Pointer to XMA block[s]
|
||||
DWORD nXmaDataBytes, // Size of pXmaData in bytes
|
||||
DWORD nStreamIndex, // Stream within which to seek
|
||||
DWORD nDesiredSample, // Sample sought
|
||||
__out DWORD* pnBitOffset, // Returns the bit offset within pXmaData of
|
||||
// the frame containing the sample sought
|
||||
__out DWORD* pnSubFrame // Returns the subframe containing the sample
|
||||
)
|
||||
{
|
||||
DWORD nDesiredFrame = nDesiredSample / XMA_SAMPLES_PER_FRAME;
|
||||
DWORD nSubFrame = (nDesiredSample % XMA_SAMPLES_PER_FRAME) / XMA_SAMPLES_PER_SUBFRAME;
|
||||
DWORD nBitOffset = GetXmaFrameBitPosition(pXmaData, nXmaDataBytes, nStreamIndex, nDesiredFrame);
|
||||
|
||||
XMA2DEFS_ASSERT(pnBitOffset);
|
||||
XMA2DEFS_ASSERT(pnSubFrame);
|
||||
|
||||
if (nBitOffset)
|
||||
{
|
||||
*pnBitOffset = nBitOffset;
|
||||
*pnSubFrame = nSubFrame;
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// GetXmaSampleRate: Obtains the legal XMA sample rate (24, 32, 44.1 or 48Khz)
|
||||
// corresponding to a generic sample rate.
|
||||
|
||||
__inline DWORD GetXmaSampleRate(DWORD dwGeneralRate)
|
||||
{
|
||||
DWORD dwXmaRate = 48000; // Default XMA rate for all rates above 44100Hz
|
||||
|
||||
if (dwGeneralRate <= 24000) dwXmaRate = 24000;
|
||||
else if (dwGeneralRate <= 32000) dwXmaRate = 32000;
|
||||
else if (dwGeneralRate <= 44100) dwXmaRate = 44100;
|
||||
|
||||
return dwXmaRate;
|
||||
}
|
||||
|
||||
|
||||
// Functions to convert between WAVEFORMATEXTENSIBLE channel masks (combinations
|
||||
// of the SPEAKER_xxx flags defined in audiodefs.h) and XMA channel masks (which
|
||||
// are limited to eight possible speaker positions: left, right, center, low
|
||||
// frequency, side left, side right, back left and back right).
|
||||
|
||||
__inline DWORD GetStandardChannelMaskFromXmaMask(BYTE bXmaMask)
|
||||
{
|
||||
DWORD dwStandardMask = 0;
|
||||
|
||||
if (bXmaMask & XMA_SPEAKER_LEFT) dwStandardMask |= SPEAKER_FRONT_LEFT;
|
||||
if (bXmaMask & XMA_SPEAKER_RIGHT) dwStandardMask |= SPEAKER_FRONT_RIGHT;
|
||||
if (bXmaMask & XMA_SPEAKER_CENTER) dwStandardMask |= SPEAKER_FRONT_CENTER;
|
||||
if (bXmaMask & XMA_SPEAKER_LFE) dwStandardMask |= SPEAKER_LOW_FREQUENCY;
|
||||
if (bXmaMask & XMA_SPEAKER_LEFT_SURROUND) dwStandardMask |= SPEAKER_SIDE_LEFT;
|
||||
if (bXmaMask & XMA_SPEAKER_RIGHT_SURROUND) dwStandardMask |= SPEAKER_SIDE_RIGHT;
|
||||
if (bXmaMask & XMA_SPEAKER_LEFT_BACK) dwStandardMask |= SPEAKER_BACK_LEFT;
|
||||
if (bXmaMask & XMA_SPEAKER_RIGHT_BACK) dwStandardMask |= SPEAKER_BACK_RIGHT;
|
||||
|
||||
return dwStandardMask;
|
||||
}
|
||||
|
||||
__inline BYTE GetXmaChannelMaskFromStandardMask(DWORD dwStandardMask)
|
||||
{
|
||||
BYTE bXmaMask = 0;
|
||||
|
||||
if (dwStandardMask & SPEAKER_FRONT_LEFT) bXmaMask |= XMA_SPEAKER_LEFT;
|
||||
if (dwStandardMask & SPEAKER_FRONT_RIGHT) bXmaMask |= XMA_SPEAKER_RIGHT;
|
||||
if (dwStandardMask & SPEAKER_FRONT_CENTER) bXmaMask |= XMA_SPEAKER_CENTER;
|
||||
if (dwStandardMask & SPEAKER_LOW_FREQUENCY) bXmaMask |= XMA_SPEAKER_LFE;
|
||||
if (dwStandardMask & SPEAKER_SIDE_LEFT) bXmaMask |= XMA_SPEAKER_LEFT_SURROUND;
|
||||
if (dwStandardMask & SPEAKER_SIDE_RIGHT) bXmaMask |= XMA_SPEAKER_RIGHT_SURROUND;
|
||||
if (dwStandardMask & SPEAKER_BACK_LEFT) bXmaMask |= XMA_SPEAKER_LEFT_BACK;
|
||||
if (dwStandardMask & SPEAKER_BACK_RIGHT) bXmaMask |= XMA_SPEAKER_RIGHT_BACK;
|
||||
|
||||
return bXmaMask;
|
||||
}
|
||||
|
||||
|
||||
// LocalizeXma2Format: Modifies a XMA2WAVEFORMATEX structure in place to comply
|
||||
// with the current platform's byte-ordering rules (little- or big-endian).
|
||||
|
||||
__inline HRESULT LocalizeXma2Format(__inout XMA2WAVEFORMATEX* pXma2Format)
|
||||
{
|
||||
#define XMASWAP2BYTES(n) ((WORD)(((n) >> 8) | (((n) & 0xff) << 8)))
|
||||
#define XMASWAP4BYTES(n) ((DWORD)((n) >> 24 | (n) << 24 | ((n) & 0xff00) << 8 | ((n) & 0xff0000) >> 8))
|
||||
|
||||
if (pXma2Format->wfx.wFormatTag == WAVE_FORMAT_XMA2)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
else if (XMASWAP2BYTES(pXma2Format->wfx.wFormatTag) == WAVE_FORMAT_XMA2)
|
||||
{
|
||||
pXma2Format->wfx.wFormatTag = XMASWAP2BYTES(pXma2Format->wfx.wFormatTag);
|
||||
pXma2Format->wfx.nChannels = XMASWAP2BYTES(pXma2Format->wfx.nChannels);
|
||||
pXma2Format->wfx.nSamplesPerSec = XMASWAP4BYTES(pXma2Format->wfx.nSamplesPerSec);
|
||||
pXma2Format->wfx.nAvgBytesPerSec = XMASWAP4BYTES(pXma2Format->wfx.nAvgBytesPerSec);
|
||||
pXma2Format->wfx.nBlockAlign = XMASWAP2BYTES(pXma2Format->wfx.nBlockAlign);
|
||||
pXma2Format->wfx.wBitsPerSample = XMASWAP2BYTES(pXma2Format->wfx.wBitsPerSample);
|
||||
pXma2Format->wfx.cbSize = XMASWAP2BYTES(pXma2Format->wfx.cbSize);
|
||||
pXma2Format->NumStreams = XMASWAP2BYTES(pXma2Format->NumStreams);
|
||||
pXma2Format->ChannelMask = XMASWAP4BYTES(pXma2Format->ChannelMask);
|
||||
pXma2Format->SamplesEncoded = XMASWAP4BYTES(pXma2Format->SamplesEncoded);
|
||||
pXma2Format->BytesPerBlock = XMASWAP4BYTES(pXma2Format->BytesPerBlock);
|
||||
pXma2Format->PlayBegin = XMASWAP4BYTES(pXma2Format->PlayBegin);
|
||||
pXma2Format->PlayLength = XMASWAP4BYTES(pXma2Format->PlayLength);
|
||||
pXma2Format->LoopBegin = XMASWAP4BYTES(pXma2Format->LoopBegin);
|
||||
pXma2Format->LoopLength = XMASWAP4BYTES(pXma2Format->LoopLength);
|
||||
pXma2Format->BlockCount = XMASWAP2BYTES(pXma2Format->BlockCount);
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return E_FAIL; // Not a recognizable XMA2 format
|
||||
}
|
||||
|
||||
#undef XMASWAP2BYTES
|
||||
#undef XMASWAP4BYTES
|
||||
}
|
||||
|
||||
|
||||
#endif // #ifndef __XMA2DEFS_INCLUDED__
|
||||
Vendored
-1
Submodule 3rdparty/libpng deleted from ea77a6fd49
Vendored
-1
Submodule 3rdparty/pugixml deleted from f205aaf6e1
Vendored
-6698
File diff suppressed because it is too large
Load Diff
Vendored
-3301
File diff suppressed because it is too large
Load Diff
@@ -11,17 +11,8 @@ if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
|
||||
endif()
|
||||
|
||||
# We use libpng's static library and don't need to build the shared library and run the tests
|
||||
set(PNG_SHARED OFF CACHE BOOL "Build shared lib." FORCE)
|
||||
set(PNG_TESTS OFF CACHE BOOL "Build tests." FORCE)
|
||||
|
||||
add_definitions(-DCMAKE_BUILD)
|
||||
|
||||
add_subdirectory( asmjit )
|
||||
add_subdirectory( 3rdparty/libpng )
|
||||
# TODO: do real installation, including copying directory structure
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/bin")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/bin")
|
||||
add_subdirectory( Vulkan )
|
||||
add_subdirectory( rpcs3 )
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ You can find some basic information in the [FAQ](https://github.com/RPCS3/rpcs3/
|
||||
|
||||
### Development
|
||||
|
||||
If you want to contribute please take a look at the [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) pages. You should as well contact any of the developers in the forum in order to know about the current situation of the emulator.
|
||||
If you want to contribute please take a took at the [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) pages. You should as well contact any of the developers in the forum in order to know about the current situation of the emulator.
|
||||
|
||||
|
||||
### Dependencies
|
||||
@@ -21,12 +21,12 @@ If you want to contribute please take a look at the [Coding Style](https://githu
|
||||
__Windows__
|
||||
* [Visual Studio 2015](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx)
|
||||
* [Visual C++ Redistributable Packages for Visual Studio 2015](http://www.microsoft.com/en-us/download/details.aspx?id=48145)
|
||||
* [Cmake 3.1.0+](http://www.cmake.org/download/) (required; add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (required; add to PATH)
|
||||
* [Cmake 3.1.0+](http://www.cmake.org/download/) (optional, required only for LLVM build; add to PATH)
|
||||
* [Python](https://www.python.org/downloads/) (optional, required only for LLVM build; add to PATH)
|
||||
|
||||
__Linux__
|
||||
* GCC 5.1+ or Clang 3.5.0+
|
||||
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libopenal-dev libwxgtk3.0-dev libglew-dev zlib1g-dev libedit-dev`
|
||||
* GCC 4.9.0+ or Clang 3.5.0+
|
||||
* Debian & Ubuntu: `sudo apt-get install libopenal-dev libwxgtk3.0-dev build-essential libglew-dev`
|
||||
* Arch: `sudo pacman -S glew openal wxgtk cmake llvm`
|
||||
|
||||
__Mac OSX__
|
||||
@@ -37,11 +37,9 @@ __Mac OSX__
|
||||
|
||||
### Building
|
||||
|
||||
To initialize the repository don't forget to execute `git submodule update --init` to pull the submodules.
|
||||
To initialize the repository don't forget to execute `git submodule update --init` to pull the wxWidgets source.
|
||||
* __Windows__:
|
||||
1) Open the *.SLN* file.
|
||||
2) Build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*.
|
||||
3) Press *BUILD* > *Build Solution* or *Rebuild Solution*.
|
||||
Open the *.SLN* file, and press *Build* > *Clean Solution*, then *Build Solution*. *Rebuild* may not work correctly.
|
||||
* __Linux & Mac OSX__:
|
||||
If you want to build with LLVM, then LLVM 3.6.2 is required.
|
||||
`cd rpcs3 && cmake CMakeLists.txt && make && cd ../` then run with `cd bin && ./rpcs3`.
|
||||
|
||||
+603
-886
File diff suppressed because it is too large
Load Diff
+93
-43
@@ -1,29 +1,57 @@
|
||||
#include "stdafx.h"
|
||||
#include "Config.h"
|
||||
#include "Emu/System.h"
|
||||
#include "AutoPause.h"
|
||||
#include "Utilities/Log.h"
|
||||
#include "Utilities/File.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/state.h"
|
||||
|
||||
cfg::bool_entry g_cfg_debug_autopause_syscall(cfg::root.misc, "Auto Pause at System Call");
|
||||
cfg::bool_entry g_cfg_debug_autopause_func_call(cfg::root.misc, "Auto Pause at Function Call");
|
||||
using namespace Debug;
|
||||
|
||||
debug::autopause& debug::autopause::get_instance()
|
||||
std::unique_ptr<AutoPause> g_autopause;
|
||||
|
||||
AutoPause& AutoPause::getInstance(void)
|
||||
{
|
||||
// Use magic static
|
||||
static autopause instance;
|
||||
return instance;
|
||||
if (!g_autopause)
|
||||
{
|
||||
g_autopause.reset(new AutoPause);
|
||||
}
|
||||
|
||||
return *g_autopause;
|
||||
}
|
||||
|
||||
// Load Auto Pause Configuration from file "pause.bin"
|
||||
void debug::autopause::reload(void)
|
||||
//Still use binary format. Default Setting should be "disable all auto pause".
|
||||
AutoPause::AutoPause(void)
|
||||
{
|
||||
auto& instance = get_instance();
|
||||
m_pause_function.reserve(16);
|
||||
m_pause_syscall.reserve(16);
|
||||
initialized = false;
|
||||
//Reload(false, false);
|
||||
Reload();
|
||||
}
|
||||
|
||||
instance.m_pause_function.clear();
|
||||
instance.m_pause_syscall.clear();
|
||||
//Notice: I would not allow to write the binary to file in this command.
|
||||
AutoPause::~AutoPause(void)
|
||||
{
|
||||
initialized = false;
|
||||
m_pause_function.clear();
|
||||
m_pause_syscall.clear();
|
||||
m_pause_function_enable = false;
|
||||
m_pause_syscall_enable = false;
|
||||
}
|
||||
|
||||
// TODO: better format, possibly a config entry
|
||||
if (fs::file list{ fs::get_config_dir() + "pause.bin" })
|
||||
//Load Auto Pause Configuration from file "pause.bin"
|
||||
//This would be able to create in a GUI window.
|
||||
void AutoPause::Reload(void)
|
||||
{
|
||||
if (fs::is_file(fs::get_config_dir() + "pause.bin"))
|
||||
{
|
||||
m_pause_function.clear();
|
||||
m_pause_function.reserve(16);
|
||||
m_pause_syscall.clear();
|
||||
m_pause_syscall.reserve(16);
|
||||
|
||||
fs::file list(fs::get_config_dir() + "pause.bin");
|
||||
//System calls ID and Function calls ID are all u32 iirc.
|
||||
u32 num;
|
||||
size_t fmax = list.size();
|
||||
size_t fcur = 0;
|
||||
@@ -36,38 +64,60 @@ void debug::autopause::reload(void)
|
||||
|
||||
if (num < 1024)
|
||||
{
|
||||
instance.m_pause_syscall.emplace(num);
|
||||
LOG_WARNING(HLE, "Set autopause at syscall %lld", num);
|
||||
//Less than 1024 - be regarded as a system call.
|
||||
//emplace_back may not cause reductant move/copy operation.
|
||||
m_pause_syscall.emplace_back(num);
|
||||
LOG_WARNING(HLE, "Auto Pause: Find System Call ID 0x%x", num);
|
||||
}
|
||||
else
|
||||
{
|
||||
instance.m_pause_function.emplace(num);
|
||||
LOG_WARNING(HLE, "Set autopause at function 0x%08x", num);
|
||||
m_pause_function.emplace_back(num);
|
||||
LOG_WARNING(HLE, "Auto Pause: Find Function Call ID 0x%x", num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_pause_syscall_enable = rpcs3::config.misc.debug.auto_pause_syscall.value();
|
||||
m_pause_function_enable = rpcs3::config.misc.debug.auto_pause_func_call.value();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void AutoPause::TryPause(u32 code)
|
||||
{
|
||||
if (code < 1024)
|
||||
{
|
||||
//Would first check Enable setting. Then the list length.
|
||||
if ((!m_pause_syscall_enable)
|
||||
|| (m_pause_syscall.size() <= 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < m_pause_syscall.size(); ++i)
|
||||
{
|
||||
if (code == m_pause_syscall[i])
|
||||
{
|
||||
Emu.Pause();
|
||||
LOG_ERROR(HLE, "Auto Pause Triggered: System call 0x%x", code); // Used Error
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Well similiar.. Seperate the list caused by possible setting difference.
|
||||
if ((!m_pause_function_enable)
|
||||
|| (m_pause_function.size() <= 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < m_pause_function.size(); ++i)
|
||||
{
|
||||
if (code == m_pause_function[i])
|
||||
{
|
||||
Emu.Pause();
|
||||
LOG_ERROR(HLE, "Auto Pause Triggered: Function call 0x%x", code); // Used Error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool debug::autopause::pause_syscall(u64 code)
|
||||
{
|
||||
if (g_cfg_debug_autopause_syscall && get_instance().m_pause_syscall.count(code) != 0)
|
||||
{
|
||||
Emu.Pause();
|
||||
LOG_SUCCESS(HLE, "Autopause triggered at syscall %lld", code);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool debug::autopause::pause_function(u32 code)
|
||||
{
|
||||
if (g_cfg_debug_autopause_func_call && get_instance().m_pause_function.count(code) != 0)
|
||||
{
|
||||
Emu.Pause();
|
||||
LOG_SUCCESS(HLE, "Autopause triggered at function 0x%08x", code);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+17
-13
@@ -1,20 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
// Regarded as a Debugger Enchantment
|
||||
namespace debug
|
||||
{
|
||||
// To store the pause function/call id, and let those pause there.
|
||||
// Would be with a GUI to configure those.
|
||||
class autopause
|
||||
//Regarded as a Debugger Enchantment
|
||||
namespace Debug {
|
||||
//To store the pause function/call id, and let those pause there.
|
||||
//Would be with a GUI to configure those.
|
||||
struct AutoPause
|
||||
{
|
||||
std::unordered_set<u64> m_pause_syscall;
|
||||
std::unordered_set<u32> m_pause_function;
|
||||
std::vector<u32> m_pause_syscall;
|
||||
std::vector<u32> m_pause_function;
|
||||
bool initialized;
|
||||
bool m_pause_syscall_enable;
|
||||
bool m_pause_function_enable;
|
||||
|
||||
static autopause& get_instance();
|
||||
AutoPause();
|
||||
~AutoPause();
|
||||
public:
|
||||
static AutoPause& getInstance(void);
|
||||
|
||||
static void reload();
|
||||
static bool pause_syscall(u64 code);
|
||||
static bool pause_function(u32 code);
|
||||
void Reload(void);
|
||||
|
||||
void TryPause(u32 code);
|
||||
};
|
||||
}
|
||||
}
|
||||
+232
-221
@@ -1,14 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "Platform.h"
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
union alignas(16) v128
|
||||
#define IS_LE_MACHINE // only draft
|
||||
|
||||
union v128
|
||||
{
|
||||
char _bytes[16];
|
||||
|
||||
template<typename T, std::size_t N, std::size_t M>
|
||||
struct masked_array_t // array type accessed as (index ^ M)
|
||||
template<typename T, std::size_t N, std::size_t M> class masked_array_t // array type accessed as (index ^ M)
|
||||
{
|
||||
T m_data[N];
|
||||
|
||||
@@ -22,11 +24,24 @@ union alignas(16) v128
|
||||
{
|
||||
return m_data[index ^ M];
|
||||
}
|
||||
|
||||
T& at(std::size_t index)
|
||||
{
|
||||
return (index ^ M) < N ? m_data[index ^ M] : throw std::out_of_range(__FUNCTION__);
|
||||
}
|
||||
|
||||
const T& at(std::size_t index) const
|
||||
{
|
||||
return (index ^ M) < N ? m_data[index ^ M] : throw std::out_of_range(__FUNCTION__);
|
||||
}
|
||||
};
|
||||
|
||||
#if IS_LE_MACHINE == 1
|
||||
#ifdef IS_LE_MACHINE
|
||||
template<typename T, std::size_t N = 16 / sizeof(T)> using normal_array_t = masked_array_t<T, N, 0>;
|
||||
template<typename T, std::size_t N = 16 / sizeof(T)> using reversed_array_t = masked_array_t<T, N, N - 1>;
|
||||
#else
|
||||
template<typename T, std::size_t N = 16 / sizeof(T)> using normal_array_t = masked_array_t<T, N, N - 1>;
|
||||
template<typename T, std::size_t N = 16 / sizeof(T)> using reversed_array_t = masked_array_t<T, N, 0>;
|
||||
#endif
|
||||
|
||||
normal_array_t<u64> _u64;
|
||||
@@ -58,7 +73,7 @@ union alignas(16) v128
|
||||
__m128i vi;
|
||||
__m128d vd;
|
||||
|
||||
struct bit_array_128
|
||||
class bit_array_128
|
||||
{
|
||||
u64 m_data[2];
|
||||
|
||||
@@ -110,18 +125,36 @@ union alignas(16) v128
|
||||
// Index 0 returns the MSB and index 127 returns the LSB
|
||||
bit_element operator [](u32 index)
|
||||
{
|
||||
#if IS_LE_MACHINE == 1
|
||||
#ifdef IS_LE_MACHINE
|
||||
return bit_element(m_data[1 - (index >> 6)], 0x8000000000000000ull >> (index & 0x3F));
|
||||
#else
|
||||
return bit_element(m_data[index >> 6], 0x8000000000000000ull >> (index & 0x3F));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Index 0 returns the MSB and index 127 returns the LSB
|
||||
bool operator [](u32 index) const
|
||||
{
|
||||
#if IS_LE_MACHINE == 1
|
||||
#ifdef IS_LE_MACHINE
|
||||
return (m_data[1 - (index >> 6)] & (0x8000000000000000ull >> (index & 0x3F))) != 0;
|
||||
#else
|
||||
return (m_data[index >> 6] & (0x8000000000000000ull >> (index & 0x3F))) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bit_element at(u32 index)
|
||||
{
|
||||
if (index >= 128) throw std::out_of_range(__FUNCTION__);
|
||||
|
||||
return operator[](index);
|
||||
}
|
||||
|
||||
bool at(u32 index) const
|
||||
{
|
||||
if (index >= 128) throw std::out_of_range(__FUNCTION__);
|
||||
|
||||
return operator[](index);
|
||||
}
|
||||
}
|
||||
_bit;
|
||||
|
||||
@@ -287,6 +320,16 @@ union alignas(16) v128
|
||||
return _u64[0] != right._u64[0] || _u64[1] != right._u64[1];
|
||||
}
|
||||
|
||||
bool is_any_1() const // check if any bit is 1
|
||||
{
|
||||
return _u64[0] || _u64[1];
|
||||
}
|
||||
|
||||
bool is_any_0() const // check if any bit is 0
|
||||
{
|
||||
return ~_u64[0] || ~_u64[1];
|
||||
}
|
||||
|
||||
// result = (~left) & (right)
|
||||
static inline v128 andnot(const v128& left, const v128& right)
|
||||
{
|
||||
@@ -302,8 +345,15 @@ union alignas(16) v128
|
||||
std::string to_hex() const;
|
||||
|
||||
std::string to_xyzw() const;
|
||||
|
||||
static inline v128 byteswap(const v128 val)
|
||||
{
|
||||
return fromV(_mm_shuffle_epi8(val.vi, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)));
|
||||
}
|
||||
};
|
||||
|
||||
CHECK_SIZE_ALIGN(v128, 16, 16);
|
||||
|
||||
inline v128 operator |(const v128& left, const v128& right)
|
||||
{
|
||||
return v128::fromV(_mm_or_si128(left.vi, right.vi));
|
||||
@@ -324,21 +374,21 @@ inline v128 operator ~(const v128& other)
|
||||
return v128::from64(~other._u64[0], ~other._u64[1]);
|
||||
}
|
||||
|
||||
#define IS_INTEGER(t) (std::is_integral<t>::value || std::is_enum<t>::value)
|
||||
#define IS_BINARY_COMPARABLE(t1, t2) (IS_INTEGER(t1) && IS_INTEGER(t2) && sizeof(t1) == sizeof(t2))
|
||||
|
||||
template<typename T, std::size_t Size = sizeof(T)>
|
||||
struct se_storage
|
||||
template<typename T, std::size_t Size = sizeof(T)> struct se_storage
|
||||
{
|
||||
static_assert(!Size, "Bad se_storage<> type");
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct se_storage<T, 2>
|
||||
template<typename T> struct se_storage<T, 2>
|
||||
{
|
||||
using type = u16;
|
||||
|
||||
static constexpr u16 swap(u16 src)
|
||||
[[deprecated]] static constexpr u16 _swap(u16 src) // for reference
|
||||
{
|
||||
return (src >> 8) | (src << 8);
|
||||
}
|
||||
|
||||
static inline u16 swap(u16 src)
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap16(src);
|
||||
@@ -359,12 +409,16 @@ struct se_storage<T, 2>
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct se_storage<T, 4>
|
||||
template<typename T> struct se_storage<T, 4>
|
||||
{
|
||||
using type = u32;
|
||||
|
||||
static constexpr u32 swap(u32 src)
|
||||
[[deprecated]] static constexpr u32 _swap(u32 src) // for reference
|
||||
{
|
||||
return (src >> 24) | (src << 24) | ((src >> 8) & 0x0000ff00) | ((src << 8) & 0x00ff0000);
|
||||
}
|
||||
|
||||
static inline u32 swap(u32 src)
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap32(src);
|
||||
@@ -385,12 +439,22 @@ struct se_storage<T, 4>
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct se_storage<T, 8>
|
||||
template<typename T> struct se_storage<T, 8>
|
||||
{
|
||||
using type = u64;
|
||||
|
||||
static constexpr u64 swap(u64 src)
|
||||
[[deprecated]] static constexpr u64 _swap(u64 src) // for reference
|
||||
{
|
||||
return (src >> 56) | (src << 56) |
|
||||
((src >> 40) & 0x000000000000ff00) |
|
||||
((src >> 24) & 0x0000000000ff0000) |
|
||||
((src >> 8) & 0x00000000ff000000) |
|
||||
((src << 8) & 0x000000ff00000000) |
|
||||
((src << 24) & 0x0000ff0000000000) |
|
||||
((src << 40) & 0x00ff000000000000);
|
||||
}
|
||||
|
||||
static inline u64 swap(u64 src)
|
||||
{
|
||||
#if defined(__GNUG__)
|
||||
return __builtin_bswap64(src);
|
||||
@@ -411,32 +475,25 @@ struct se_storage<T, 8>
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct se_storage<T, 16>
|
||||
template<typename T> struct se_storage<T, 16>
|
||||
{
|
||||
using type = v128;
|
||||
|
||||
static inline v128 swap(const v128& src)
|
||||
{
|
||||
return v128::fromV(_mm_shuffle_epi8(src.vi, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)));
|
||||
}
|
||||
|
||||
static inline v128 to(const T& src)
|
||||
{
|
||||
return swap(reinterpret_cast<const v128&>(src));
|
||||
return v128::byteswap(reinterpret_cast<const v128&>(src));
|
||||
}
|
||||
|
||||
static inline T from(const v128& src)
|
||||
{
|
||||
const v128 result = swap(src);
|
||||
const v128 result = v128::byteswap(src);
|
||||
return reinterpret_cast<const T&>(result);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> using se_storage_t = typename se_storage<T>::type;
|
||||
|
||||
template<typename T1, typename T2>
|
||||
struct se_convert
|
||||
template<typename T1, typename T2> struct se_convert
|
||||
{
|
||||
using type_from = std::remove_cv_t<T1>;
|
||||
using type_to = std::remove_cv_t<T2>;
|
||||
@@ -458,12 +515,10 @@ struct se_convert
|
||||
|
||||
static struct se_raw_tag_t {} constexpr se_raw{};
|
||||
|
||||
template<typename T, bool Se = true>
|
||||
class se_t;
|
||||
template<typename T, bool Se = true> class se_t;
|
||||
|
||||
// Switched endianness
|
||||
template<typename T>
|
||||
class se_t<T, true>
|
||||
// se_t with switched endianness
|
||||
template<typename T> class se_t<T, true>
|
||||
{
|
||||
using type = typename std::remove_cv<T>::type;
|
||||
using stype = se_storage_t<type>;
|
||||
@@ -471,13 +526,14 @@ class se_t<T, true>
|
||||
|
||||
stype m_data;
|
||||
|
||||
static_assert(!std::is_union<type>::value && !std::is_class<type>::value || std::is_same<type, v128>::value || std::is_same<type, u128>::value, "se_t<> error: invalid type (struct or union)");
|
||||
static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)");
|
||||
static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)");
|
||||
static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)");
|
||||
static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment");
|
||||
static_assert(!std::is_enum<type>::value, "se_t<> error: invalid type (enumeration), use integral type instead");
|
||||
static_assert(alignof(type) == alignof(stype), "se_t<> error: unexpected alignment");
|
||||
|
||||
template<typename T2, typename = void>
|
||||
struct bool_converter
|
||||
template<typename T2, typename = void> struct bool_converter
|
||||
{
|
||||
static inline bool to_bool(const se_t<T2>& value)
|
||||
{
|
||||
@@ -485,8 +541,7 @@ class se_t<T, true>
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T2>
|
||||
struct bool_converter<T2, std::enable_if_t<std::is_integral<T2>::value>>
|
||||
template<typename T2> struct bool_converter<T2, std::enable_if_t<std::is_integral<T2>::value>>
|
||||
{
|
||||
static inline bool to_bool(const se_t<T2>& value)
|
||||
{
|
||||
@@ -504,7 +559,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
// Construct directly from raw data (don't use)
|
||||
// construct directly from raw data (don't use)
|
||||
constexpr se_t(const stype& raw_value, const se_raw_tag_t&)
|
||||
: m_data(raw_value)
|
||||
{
|
||||
@@ -515,7 +570,7 @@ public:
|
||||
return storage::from(m_data);
|
||||
}
|
||||
|
||||
// Access underlying raw data (don't use)
|
||||
// access underlying raw data (don't use)
|
||||
constexpr const stype& raw_data() const noexcept
|
||||
{
|
||||
return m_data;
|
||||
@@ -528,96 +583,78 @@ public:
|
||||
return m_data = storage::to(value), *this;
|
||||
}
|
||||
|
||||
using simple_type = simple_t<T>;
|
||||
|
||||
operator type() const
|
||||
{
|
||||
return storage::from(m_data);
|
||||
}
|
||||
|
||||
// Optimization
|
||||
// optimization
|
||||
explicit operator bool() const
|
||||
{
|
||||
return bool_converter<type>::to_bool(*this);
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T2>
|
||||
std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator &=(const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T2> std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator &=(const se_t<T2>& right)
|
||||
{
|
||||
return m_data &= right.raw_data(), *this;
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename CT>
|
||||
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator &=(CT right)
|
||||
// optimization
|
||||
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator &=(CT right)
|
||||
{
|
||||
return m_data &= storage::to(right), *this;
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T2>
|
||||
std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator |=(const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T2> std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator |=(const se_t<T2>& right)
|
||||
{
|
||||
return m_data |= right.raw_data(), *this;
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename CT>
|
||||
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator |=(CT right)
|
||||
// optimization
|
||||
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator |=(CT right)
|
||||
{
|
||||
return m_data |= storage::to(right), *this;
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T2>
|
||||
std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator ^=(const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T2> std::enable_if_t<IS_BINARY_COMPARABLE(T, T2), se_t&> operator ^=(const se_t<T2>& right)
|
||||
{
|
||||
return m_data ^= right.raw_data(), *this;
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename CT>
|
||||
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator ^=(CT right)
|
||||
// optimization
|
||||
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator ^=(CT right)
|
||||
{
|
||||
return m_data ^= storage::to(right), *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Native endianness
|
||||
template<typename T>
|
||||
class se_t<T, false>
|
||||
// se_t with native endianness
|
||||
template<typename T> class se_t<T, false>
|
||||
{
|
||||
using type = typename std::remove_cv<T>::type;
|
||||
|
||||
type m_data;
|
||||
|
||||
static_assert(!std::is_union<type>::value && !std::is_class<type>::value || std::is_same<type, v128>::value || std::is_same<type, u128>::value, "se_t<> error: invalid type (struct or union)");
|
||||
static_assert(!std::is_pointer<type>::value, "se_t<> error: invalid type (pointer)");
|
||||
static_assert(!std::is_reference<type>::value, "se_t<> error: invalid type (reference)");
|
||||
static_assert(!std::is_array<type>::value, "se_t<> error: invalid type (array)");
|
||||
static_assert(sizeof(type) == alignof(type), "se_t<> error: unexpected alignment");
|
||||
|
||||
type m_data;
|
||||
static_assert(!std::is_enum<type>::value, "se_t<> error: invalid type (enumeration), use integral type instead");
|
||||
|
||||
public:
|
||||
se_t() = default;
|
||||
|
||||
se_t(const se_t&) = default;
|
||||
|
||||
constexpr se_t(type value)
|
||||
: m_data(value)
|
||||
{
|
||||
}
|
||||
|
||||
// Construct directly from raw data (don't use)
|
||||
constexpr se_t(const type& raw_value, const se_raw_tag_t&)
|
||||
: m_data(raw_value)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr type value() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
// Access underlying raw data (don't use)
|
||||
constexpr const type& raw_data() const noexcept
|
||||
type value() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
@@ -629,27 +666,22 @@ public:
|
||||
return m_data = value, *this;
|
||||
}
|
||||
|
||||
using simple_type = simple_t<T>;
|
||||
|
||||
constexpr operator type() const
|
||||
operator type() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
template<typename CT>
|
||||
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator &=(const CT& right)
|
||||
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator &=(const CT& right)
|
||||
{
|
||||
return m_data &= right, *this;
|
||||
}
|
||||
|
||||
template<typename CT>
|
||||
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator |=(const CT& right)
|
||||
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator |=(const CT& right)
|
||||
{
|
||||
return m_data |= right, *this;
|
||||
}
|
||||
|
||||
template<typename CT>
|
||||
std::enable_if_t<std::is_integral<T>::value && std::is_convertible<CT, T>::value, se_t&> operator ^=(const CT& right)
|
||||
template<typename CT> std::enable_if_t<IS_INTEGRAL(T) && std::is_convertible<CT, T>::value, se_t&> operator ^=(const CT& right)
|
||||
{
|
||||
return m_data ^= right, *this;
|
||||
}
|
||||
@@ -658,57 +690,49 @@ public:
|
||||
// se_t with native endianness (alias)
|
||||
template<typename T> using nse_t = se_t<T, false>;
|
||||
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator +=(se_t<T, Se>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator +=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value += right);
|
||||
}
|
||||
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator -=(se_t<T, Se>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator -=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value -= right);
|
||||
}
|
||||
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator *=(se_t<T, Se>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator *=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value *= right);
|
||||
}
|
||||
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator /=(se_t<T, Se>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator /=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value /= right);
|
||||
}
|
||||
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator %=(se_t<T, Se>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator %=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value %= right);
|
||||
}
|
||||
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator <<=(se_t<T, Se>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator <<=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value <<= right);
|
||||
}
|
||||
|
||||
template<typename T, bool Se, typename T1>
|
||||
inline se_t<T, Se>& operator >>=(se_t<T, Se>& left, const T1& right)
|
||||
template<typename T, bool Se, typename T1> inline se_t<T, Se>& operator >>=(se_t<T, Se>& left, const T1& right)
|
||||
{
|
||||
auto value = left.value();
|
||||
return left = (value >>= right);
|
||||
}
|
||||
|
||||
template<typename T, bool Se>
|
||||
inline se_t<T, Se> operator ++(se_t<T, Se>& left, int)
|
||||
template<typename T, bool Se> inline se_t<T, Se> operator ++(se_t<T, Se>& left, int)
|
||||
{
|
||||
auto value = left.value();
|
||||
auto result = value++;
|
||||
@@ -716,8 +740,7 @@ inline se_t<T, Se> operator ++(se_t<T, Se>& left, int)
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, bool Se>
|
||||
inline se_t<T, Se> operator --(se_t<T, Se>& left, int)
|
||||
template<typename T, bool Se> inline se_t<T, Se> operator --(se_t<T, Se>& left, int)
|
||||
{
|
||||
auto value = left.value();
|
||||
auto result = value--;
|
||||
@@ -725,205 +748,193 @@ inline se_t<T, Se> operator --(se_t<T, Se>& left, int)
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T, bool Se>
|
||||
inline se_t<T, Se>& operator ++(se_t<T, Se>& right)
|
||||
template<typename T, bool Se> inline se_t<T, Se>& operator ++(se_t<T, Se>& right)
|
||||
{
|
||||
auto value = right.value();
|
||||
return right = ++value;
|
||||
}
|
||||
|
||||
template<typename T, bool Se>
|
||||
inline se_t<T, Se>& operator --(se_t<T, Se>& right)
|
||||
template<typename T, bool Se> inline se_t<T, Se>& operator --(se_t<T, Se>& right)
|
||||
{
|
||||
auto value = right.value();
|
||||
return right = --value;
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2), bool> operator ==(const se_t<T1>& left, const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2), bool> operator ==(const se_t<T1>& left, const se_t<T2>& right)
|
||||
{
|
||||
return left.raw_data() == right.raw_data();
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator ==(const se_t<T1>& left, T2 right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGRAL(T1) && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator ==(const se_t<T1>& left, T2 right)
|
||||
{
|
||||
return left.raw_data() == se_storage<T1>::to(right);
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2), bool> operator ==(T1 left, const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGER(T1) && IS_INTEGRAL(T2) && sizeof(T1) <= sizeof(T2), bool> operator ==(T1 left, const se_t<T2>& right)
|
||||
{
|
||||
return se_storage<T2>::to(left) == right.raw_data();
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2), bool> operator !=(const se_t<T1>& left, const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2), bool> operator !=(const se_t<T1>& left, const se_t<T2>& right)
|
||||
{
|
||||
return left.raw_data() != right.raw_data();
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator !=(const se_t<T1>& left, T2 right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGRAL(T1) && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2), bool> operator !=(const se_t<T1>& left, T2 right)
|
||||
{
|
||||
return left.raw_data() != se_storage<T1>::to(right);
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2), bool> operator !=(T1 left, const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGER(T1) && IS_INTEGRAL(T2) && sizeof(T1) <= sizeof(T2), bool> operator !=(T1 left, const se_t<T2>& right)
|
||||
{
|
||||
return se_storage<T2>::to(left) != right.raw_data();
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() & T2())>> operator &(const se_t<T1>& left, const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() & T2())>> operator &(const se_t<T1>& left, const se_t<T2>& right)
|
||||
{
|
||||
return{ left.raw_data() & right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() & T2())>> operator &(const se_t<T1>& left, T2 right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGRAL(T1) && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() & T2())>> operator &(const se_t<T1>& left, T2 right)
|
||||
{
|
||||
return{ left.raw_data() & se_storage<T1>::to(right), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() & T2())>> operator &(T1 left, const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGER(T1) && IS_INTEGRAL(T2) && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() & T2())>> operator &(T1 left, const se_t<T2>& right)
|
||||
{
|
||||
return{ se_storage<T2>::to(left) & right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() | T2())>> operator |(const se_t<T1>& left, const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() | T2())>> operator |(const se_t<T1>& left, const se_t<T2>& right)
|
||||
{
|
||||
return{ left.raw_data() | right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() | T2())>> operator |(const se_t<T1>& left, T2 right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGRAL(T1) && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() | T2())>> operator |(const se_t<T1>& left, T2 right)
|
||||
{
|
||||
return{ left.raw_data() | se_storage<T1>::to(right), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() | T2())>> operator |(T1 left, const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGER(T1) && IS_INTEGRAL(T2) && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() | T2())>> operator |(T1 left, const se_t<T2>& right)
|
||||
{
|
||||
return{ se_storage<T2>::to(left) | right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(const se_t<T1>& left, const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_BINARY_COMPARABLE(T1, T2) && sizeof(T1) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(const se_t<T1>& left, const se_t<T2>& right)
|
||||
{
|
||||
return{ left.raw_data() ^ right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<std::is_integral<T1>::value && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(const se_t<T1>& left, T2 right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGRAL(T1) && IS_INTEGER(T2) && sizeof(T1) >= sizeof(T2) && sizeof(T1) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(const se_t<T1>& left, T2 right)
|
||||
{
|
||||
return{ left.raw_data() ^ se_storage<T1>::to(right), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T1, typename T2>
|
||||
inline std::enable_if_t<IS_INTEGER(T1) && std::is_integral<T2>::value && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(T1 left, const se_t<T2>& right)
|
||||
// optimization
|
||||
template<typename T1, typename T2> inline std::enable_if_t<IS_INTEGER(T1) && IS_INTEGRAL(T2) && sizeof(T1) <= sizeof(T2) && sizeof(T2) >= 4, se_t<decltype(T1() ^ T2())>> operator ^(T1 left, const se_t<T2>& right)
|
||||
{
|
||||
return{ se_storage<T2>::to(left) ^ right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
// Optimization
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_integral<T>::value && sizeof(T) >= 4, se_t<decltype(~T())>> operator ~(const se_t<T>& right)
|
||||
// optimization
|
||||
template<typename T> inline std::enable_if_t<IS_INTEGRAL(T) && sizeof(T) >= 4, se_t<decltype(~T())>> operator ~(const se_t<T>& right)
|
||||
{
|
||||
return{ ~right.raw_data(), se_raw };
|
||||
}
|
||||
|
||||
#if IS_LE_MACHINE == 1
|
||||
#ifdef IS_LE_MACHINE
|
||||
template<typename T> using be_t = se_t<T, true>;
|
||||
template<typename T> using le_t = se_t<T, false>;
|
||||
#else
|
||||
template<typename T> using be_t = se_t<T, false>;
|
||||
template<typename T> using le_t = se_t<T, true>;
|
||||
#endif
|
||||
|
||||
// Type converter: converts native endianness arithmetic/enum types to appropriate se_t<> type
|
||||
template<typename T, bool Se, typename = void>
|
||||
struct to_se
|
||||
|
||||
template<typename T, bool Se, typename = void> struct to_se
|
||||
{
|
||||
// Convert arithmetic and enum types
|
||||
using type = typename std::conditional<std::is_arithmetic<T>::value || std::is_enum<T>::value, se_t<T, Se>, T>::type;
|
||||
};
|
||||
|
||||
template<typename T, bool Se> struct to_se<const T, Se, std::enable_if_t<!std::is_array<T>::value>> // move const qualifier
|
||||
{
|
||||
using type = const typename to_se<T, Se>::type;
|
||||
};
|
||||
|
||||
template<typename T, bool Se> struct to_se<volatile T, Se, std::enable_if_t<!std::is_array<T>::value && !std::is_const<T>::value>> // move volatile qualifier
|
||||
{
|
||||
using type = volatile typename to_se<T, Se>::type;
|
||||
};
|
||||
|
||||
template<typename T, bool Se> struct to_se<T[], Se>
|
||||
{
|
||||
using type = typename to_se<T, Se>::type[];
|
||||
};
|
||||
|
||||
template<typename T, bool Se, std::size_t N> struct to_se<T[N], Se>
|
||||
{
|
||||
using type = typename to_se<T, Se>::type[N];
|
||||
};
|
||||
|
||||
template<bool Se> struct to_se<u128, Se> { using type = se_t<u128, Se>; };
|
||||
template<bool Se> struct to_se<v128, Se> { using type = se_t<v128, Se>; };
|
||||
template<bool Se> struct to_se<bool, Se> { using type = bool; };
|
||||
template<bool Se> struct to_se<char, Se> { using type = char; };
|
||||
template<bool Se> struct to_se<u8, Se> { using type = u8; };
|
||||
template<bool Se> struct to_se<s8, Se> { using type = s8; };
|
||||
|
||||
template<typename T, bool Se>
|
||||
struct to_se<const T, Se, std::enable_if_t<!std::is_array<T>::value>>
|
||||
{
|
||||
// Move const qualifier
|
||||
using type = const typename to_se<T, Se>::type;
|
||||
};
|
||||
|
||||
template<typename T, bool Se>
|
||||
struct to_se<volatile T, Se, std::enable_if_t<!std::is_array<T>::value && !std::is_const<T>::value>>
|
||||
{
|
||||
// Move volatile qualifier
|
||||
using type = volatile typename to_se<T, Se>::type;
|
||||
};
|
||||
|
||||
template<typename T, bool Se>
|
||||
struct to_se<T[], Se>
|
||||
{
|
||||
// Move array qualifier
|
||||
using type = typename to_se<T, Se>::type[];
|
||||
};
|
||||
|
||||
template<typename T, bool Se, std::size_t N>
|
||||
struct to_se<T[N], Se>
|
||||
{
|
||||
// Move array qualifier
|
||||
using type = typename to_se<T, Se>::type[N];
|
||||
};
|
||||
|
||||
// BE/LE aliases for to_se<>
|
||||
#if IS_LE_MACHINE == 1
|
||||
#ifdef IS_LE_MACHINE
|
||||
template<typename T> using to_be_t = typename to_se<T, true>::type;
|
||||
template<typename T> using to_le_t = typename to_se<T, false>::type;
|
||||
#else
|
||||
template<typename T> using to_be_t = typename to_se<T, false>::type;
|
||||
template<typename T> using to_le_t = typename to_se<T, true>::type;
|
||||
#endif
|
||||
|
||||
// BE/LE aliases for atomic_t
|
||||
#if IS_LE_MACHINE == 1
|
||||
template<typename T> using atomic_be_t = atomic_t<be_t<T>>;
|
||||
template<typename T> using atomic_le_t = atomic_t<le_t<T>>;
|
||||
#endif
|
||||
|
||||
namespace fmt
|
||||
template<typename T, typename = void> struct to_ne
|
||||
{
|
||||
// Formatting for BE/LE data
|
||||
template<typename T, bool Se>
|
||||
struct unveil<se_t<T, Se>, void>
|
||||
{
|
||||
using result_type = typename unveil<T>::result_type;
|
||||
using type = T;
|
||||
};
|
||||
|
||||
static inline result_type get_value(const se_t<T, Se>& arg)
|
||||
{
|
||||
return unveil<T>::get_value(arg);
|
||||
}
|
||||
};
|
||||
}
|
||||
template<typename T, bool Se> struct to_ne<se_t<T, Se>>
|
||||
{
|
||||
using type = typename std::remove_cv<T>::type;
|
||||
};
|
||||
|
||||
#undef IS_BINARY_COMPARABLE
|
||||
#undef IS_INTEGER
|
||||
template<typename T> struct to_ne<const T, std::enable_if_t<!std::is_array<T>::value>> // move const qualifier
|
||||
{
|
||||
using type = const typename to_ne<T>::type;
|
||||
};
|
||||
|
||||
template<typename T> struct to_ne<volatile T, std::enable_if_t<!std::is_array<T>::value && !std::is_const<T>::value>> // move volatile qualifier
|
||||
{
|
||||
using type = volatile typename to_ne<T>::type;
|
||||
};
|
||||
|
||||
template<typename T> struct to_ne<T[]>
|
||||
{
|
||||
using type = typename to_ne<T>::type[];
|
||||
};
|
||||
|
||||
template<typename T, std::size_t N> struct to_ne<T[N]>
|
||||
{
|
||||
using type = typename to_ne<T>::type[N];
|
||||
};
|
||||
|
||||
// restore native endianness for T: returns T for be_t<T> or le_t<T>, T otherwise
|
||||
template<typename T> using to_ne_t = typename to_ne<T>::type;
|
||||
|
||||
+50
-154
@@ -1,106 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
template<typename T, uint N>
|
||||
struct bf_base
|
||||
// BitField access helper class (N bits from I position), intended to be put in union
|
||||
template<typename T, u32 I, u32 N> class bf_t
|
||||
{
|
||||
using type = T;
|
||||
using vtype = simple_t<type>;
|
||||
// Checks
|
||||
static_assert(I < sizeof(T) * 8, "bf_t<> error: I out of bounds");
|
||||
static_assert(N < sizeof(T) * 8, "bf_t<> error: N out of bounds");
|
||||
static_assert(I + N <= sizeof(T) * 8, "bf_t<> error: values out of bounds");
|
||||
|
||||
// Datatype bitsize
|
||||
static constexpr uint bitmax = sizeof(T) * CHAR_BIT; static_assert(N - 1 < bitmax, "bf_base<> error: N out of bounds");
|
||||
|
||||
// Field bitsize
|
||||
static constexpr uint bitsize = N;
|
||||
// Underlying data type
|
||||
using type = typename std::remove_cv<T>::type;
|
||||
|
||||
// Value mask
|
||||
static constexpr vtype vmask = static_cast<vtype>(~std::make_unsigned_t<vtype>{} >> (bitmax - bitsize));
|
||||
// Underlying value type (native endianness)
|
||||
using vtype = typename to_ne<type>::type;
|
||||
|
||||
protected:
|
||||
// Mask of size N
|
||||
constexpr static vtype s_mask = (static_cast<vtype>(1) << N) - 1;
|
||||
|
||||
// Underlying data member
|
||||
type m_data;
|
||||
};
|
||||
|
||||
// Bitfield accessor (N bits from I position, 0 is LSB)
|
||||
template<typename T, uint I, uint N>
|
||||
struct bf_t : bf_base<T, N>
|
||||
{
|
||||
using type = typename bf_t::type;
|
||||
using vtype = typename bf_t::vtype;
|
||||
// Conversion operator helper (uses SFINAE)
|
||||
template<typename T2, typename = void> struct converter {};
|
||||
|
||||
// Field offset
|
||||
static constexpr uint bitpos = I; static_assert(bitpos + N <= bf_t::bitmax, "bf_t<> error: I out of bounds");
|
||||
|
||||
// Get bitmask of size N, at I pos
|
||||
static constexpr vtype data_mask()
|
||||
{
|
||||
return bf_t::vmask << bitpos;
|
||||
}
|
||||
|
||||
// Bitfield extraction helper
|
||||
template<typename T2, typename = void>
|
||||
struct extract_impl
|
||||
{
|
||||
static_assert(!sizeof(T2), "bf_t<> error: Invalid type");
|
||||
};
|
||||
|
||||
template<typename T2>
|
||||
struct extract_impl<T2, std::enable_if_t<std::is_unsigned<T2>::value>>
|
||||
template<typename T2> struct converter<T2, std::enable_if_t<std::is_unsigned<T2>::value>>
|
||||
{
|
||||
// Load unsigned value
|
||||
static constexpr T2 extract(const T& data)
|
||||
static inline T2 convert(const type& data)
|
||||
{
|
||||
return (data >> bitpos) & bf_t::vmask;
|
||||
return (data >> I) & s_mask;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T2>
|
||||
struct extract_impl<T2, std::enable_if_t<std::is_signed<T2>::value>>
|
||||
template<typename T2> struct converter<T2, std::enable_if_t<std::is_signed<T2>::value>>
|
||||
{
|
||||
// Load signed value (sign-extended)
|
||||
static constexpr T2 extract(const T& data)
|
||||
static inline T2 convert(const type& data)
|
||||
{
|
||||
return data << (bf_t::bitmax - bitpos - N) >> (bf_t::bitmax - N);
|
||||
return data << (sizeof(T) * 8 - I - N) >> (sizeof(T) * 8 - N);
|
||||
}
|
||||
};
|
||||
|
||||
// Bitfield extraction
|
||||
static constexpr vtype extract(const T& data)
|
||||
public:
|
||||
// Assignment operator (store bitfield value)
|
||||
bf_t& operator =(vtype value)
|
||||
{
|
||||
return extract_impl<vtype>::extract(data);
|
||||
m_data = (m_data & ~(s_mask << I)) | (value & s_mask) << I;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Bitfield insertion
|
||||
static constexpr vtype insert(vtype value)
|
||||
// Conversion operator (load bitfield value)
|
||||
operator vtype() const
|
||||
{
|
||||
return (value & bf_t::vmask) << bitpos;
|
||||
return converter<vtype>::convert(m_data);
|
||||
}
|
||||
|
||||
// Load bitfield value
|
||||
constexpr operator vtype() const
|
||||
// Get raw data with mask applied
|
||||
type unshifted() const
|
||||
{
|
||||
return extract(this->m_data);
|
||||
return (m_data & (s_mask << I));
|
||||
}
|
||||
|
||||
// Load raw data with mask applied
|
||||
constexpr T unshifted() const
|
||||
{
|
||||
return this->m_data & data_mask();
|
||||
}
|
||||
|
||||
// Optimized bool conversion (must be removed if inappropriate)
|
||||
explicit constexpr operator bool() const
|
||||
// Optimized bool conversion
|
||||
explicit operator bool() const
|
||||
{
|
||||
return unshifted() != 0;
|
||||
}
|
||||
|
||||
// Store bitfield value
|
||||
bf_t& operator =(vtype value)
|
||||
{
|
||||
this->m_data = (this->m_data & ~data_mask()) | insert(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Postfix increment operator
|
||||
vtype operator ++(int)
|
||||
{
|
||||
vtype result = *this;
|
||||
@@ -108,11 +75,13 @@ struct bf_t : bf_base<T, N>
|
||||
return result;
|
||||
}
|
||||
|
||||
// Prefix increment operator
|
||||
bf_t& operator ++()
|
||||
{
|
||||
return *this = *this + 1;
|
||||
}
|
||||
|
||||
// Postfix decrement operator
|
||||
vtype operator --(int)
|
||||
{
|
||||
vtype result = *this;
|
||||
@@ -120,125 +89,52 @@ struct bf_t : bf_base<T, N>
|
||||
return result;
|
||||
}
|
||||
|
||||
// Prefix decrement operator
|
||||
bf_t& operator --()
|
||||
{
|
||||
return *this = *this - 1;
|
||||
}
|
||||
|
||||
// Addition assignment operator
|
||||
bf_t& operator +=(vtype right)
|
||||
{
|
||||
return *this = *this + right;
|
||||
}
|
||||
|
||||
// Subtraction assignment operator
|
||||
bf_t& operator -=(vtype right)
|
||||
{
|
||||
return *this = *this - right;
|
||||
}
|
||||
|
||||
// Multiplication assignment operator
|
||||
bf_t& operator *=(vtype right)
|
||||
{
|
||||
return *this = *this * right;
|
||||
}
|
||||
|
||||
// Bitwise AND assignment operator
|
||||
bf_t& operator &=(vtype right)
|
||||
{
|
||||
this->m_data &= (right & bf_t::vmask) << bitpos;
|
||||
m_data &= (right & s_mask) << I;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Bitwise OR assignment operator
|
||||
bf_t& operator |=(vtype right)
|
||||
{
|
||||
this->m_data |= (right & bf_t::vmask) << bitpos;
|
||||
m_data |= (right & s_mask) << I;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Bitwise XOR assignment operator
|
||||
bf_t& operator ^=(vtype right)
|
||||
{
|
||||
this->m_data ^= (right & bf_t::vmask) << bitpos;
|
||||
m_data ^= (right & s_mask) << I;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Field pack (concatenated from left to right)
|
||||
template<typename F = void, typename... Fields>
|
||||
struct cf_t : bf_base<typename F::type, F::bitsize + cf_t<Fields...>::bitsize>
|
||||
{
|
||||
using type = typename cf_t::type;
|
||||
using vtype = typename cf_t::vtype;
|
||||
template<typename T, u32 I, u32 N> using bf_be_t = bf_t<be_t<T>, I, N>;
|
||||
|
||||
// Get disjunction of all "data" masks of concatenated values
|
||||
static constexpr vtype data_mask()
|
||||
{
|
||||
return F::data_mask() | cf_t<Fields...>::data_mask();
|
||||
}
|
||||
|
||||
// Extract all bitfields and concatenate
|
||||
static constexpr vtype extract(const type& data)
|
||||
{
|
||||
return F::extract(data) << cf_t<Fields...>::bitsize | cf_t<Fields...>::extract(data);
|
||||
}
|
||||
|
||||
// Split bitfields and insert them
|
||||
static constexpr vtype insert(vtype value)
|
||||
{
|
||||
return F::insert(value >> cf_t<Fields...>::bitsize) | cf_t<Fields...>::insert(value);
|
||||
}
|
||||
|
||||
// Load value
|
||||
constexpr operator vtype() const
|
||||
{
|
||||
return extract(this->m_data);
|
||||
}
|
||||
|
||||
// Store value
|
||||
cf_t& operator =(vtype value)
|
||||
{
|
||||
this->m_data = (this->m_data & ~data_mask()) | insert(value);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Empty field pack (recursion terminator)
|
||||
template<>
|
||||
struct cf_t<void>
|
||||
{
|
||||
static constexpr uint bitsize = 0;
|
||||
|
||||
static constexpr uint data_mask()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr auto extract(const T& data) -> decltype(+T())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static constexpr T insert(T value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Fixed field (provides constant values in field pack)
|
||||
template<typename T, T V, uint N>
|
||||
struct ff_t : bf_base<T, N>
|
||||
{
|
||||
using type = typename ff_t::type;
|
||||
using vtype = typename ff_t::vtype;
|
||||
|
||||
// Return constant value
|
||||
static constexpr vtype extract(const type& data)
|
||||
{
|
||||
static_assert((V & ff_t::vmask) == V, "ff_t<> error: V out of bounds");
|
||||
return V;
|
||||
}
|
||||
|
||||
// Get value
|
||||
operator vtype() const
|
||||
{
|
||||
return V;
|
||||
}
|
||||
};
|
||||
template<typename T, u32 I, u32 N> using bf_le_t = bf_t<le_t<T>, I, N>;
|
||||
|
||||
@@ -1,203 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include "yaml-cpp/yaml.h"
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
_log::channel cfg("CFG", _log::level::notice);
|
||||
|
||||
entry_base::entry_base(type _type)
|
||||
: m_type(_type)
|
||||
{
|
||||
if (_type != type::node)
|
||||
{
|
||||
throw std::logic_error("Invalid root node");
|
||||
}
|
||||
}
|
||||
|
||||
entry_base::entry_base(type _type, node& owner, const std::string& name)
|
||||
: m_type(_type)
|
||||
{
|
||||
if (!owner.m_nodes.emplace(name, this).second)
|
||||
{
|
||||
throw std::logic_error("Node already exists");
|
||||
}
|
||||
}
|
||||
|
||||
entry_base& entry_base::operator[](const std::string& name) const
|
||||
{
|
||||
if (m_type == type::node)
|
||||
{
|
||||
return *static_cast<const node&>(*this).m_nodes.at(name);
|
||||
}
|
||||
|
||||
throw std::logic_error("Invalid node type");
|
||||
}
|
||||
|
||||
entry_base& entry_base::operator[](const char* name) const
|
||||
{
|
||||
if (m_type == type::node)
|
||||
{
|
||||
return *static_cast<const node&>(*this).m_nodes.at(name);
|
||||
}
|
||||
|
||||
throw std::logic_error("Invalid node type");
|
||||
}
|
||||
|
||||
// Emit YAML
|
||||
static void encode(YAML::Emitter& out, const class entry_base& rhs);
|
||||
|
||||
// Incrementally load config entries from YAML::Node.
|
||||
// The config value is preserved if the corresponding YAML node doesn't exist.
|
||||
static void decode(const YAML::Node& data, class entry_base& rhs);
|
||||
}
|
||||
|
||||
bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||
{
|
||||
// TODO: this could be rewritten without exceptions (but it should be as safe as possible and provide logs)
|
||||
s64 result;
|
||||
std::size_t pos;
|
||||
|
||||
try
|
||||
{
|
||||
result = std::stoll(value, &pos, 0 /* Auto-detect numeric base */);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): exception: %s", value, e.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pos != value.size())
|
||||
{
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out) cfg.error("cfg::try_to_int('%s'): out of bounds (%lld..%lld)", value, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out) *out = result;
|
||||
return true;
|
||||
}
|
||||
|
||||
void cfg::encode(YAML::Emitter& out, const cfg::entry_base& rhs)
|
||||
{
|
||||
switch (rhs.get_type())
|
||||
{
|
||||
case type::node:
|
||||
{
|
||||
out << YAML::BeginMap;
|
||||
for (const auto& np : static_cast<const node&>(rhs).get_nodes())
|
||||
{
|
||||
out << YAML::Key << np.first;
|
||||
out << YAML::Value; encode(out, *np.second);
|
||||
}
|
||||
|
||||
out << YAML::EndMap;
|
||||
return;
|
||||
}
|
||||
case type::set:
|
||||
{
|
||||
out << YAML::BeginSeq;
|
||||
for (const auto& str : static_cast<const set_entry&>(rhs).get_set())
|
||||
{
|
||||
out << str;
|
||||
}
|
||||
|
||||
out << YAML::EndSeq;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
out << rhs.to_string();
|
||||
}
|
||||
|
||||
void cfg::decode(const YAML::Node& data, cfg::entry_base& rhs)
|
||||
{
|
||||
switch (rhs.get_type())
|
||||
{
|
||||
case type::node:
|
||||
{
|
||||
if (data.IsScalar() || data.IsSequence())
|
||||
{
|
||||
return; // ???
|
||||
}
|
||||
|
||||
for (const auto& pair : data)
|
||||
{
|
||||
if (!pair.first.IsScalar()) continue;
|
||||
|
||||
// Find the key among existing nodes
|
||||
const auto name = pair.first.Scalar();
|
||||
const auto found = static_cast<node&>(rhs).get_nodes().find(name);
|
||||
|
||||
if (found != static_cast<node&>(rhs).get_nodes().cend())
|
||||
{
|
||||
decode(pair.second, *found->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ???
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case type::set:
|
||||
{
|
||||
std::vector<std::string> values;
|
||||
|
||||
if (YAML::convert<decltype(values)>::decode(data, values))
|
||||
{
|
||||
rhs.from_list(std::move(values));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::string value;
|
||||
|
||||
if (YAML::convert<std::string>::decode(data, value))
|
||||
{
|
||||
rhs.from_string(value);
|
||||
}
|
||||
|
||||
break; // ???
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string cfg::node::to_string() const
|
||||
{
|
||||
YAML::Emitter out;
|
||||
cfg::encode(out, *this);
|
||||
|
||||
return{ out.c_str(), out.size() };
|
||||
}
|
||||
|
||||
bool cfg::node::from_string(const std::string& value)
|
||||
{
|
||||
cfg::decode(YAML::Load(value), *this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void cfg::node::from_default()
|
||||
{
|
||||
for (auto& node : m_nodes)
|
||||
{
|
||||
node.second->from_default();
|
||||
}
|
||||
}
|
||||
|
||||
cfg::root_node& cfg::get_root()
|
||||
{
|
||||
// Magic static
|
||||
static root_node root;
|
||||
return root;
|
||||
}
|
||||
@@ -1,523 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/Atomic.h"
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace cfg
|
||||
{
|
||||
// Convert string to signed integer
|
||||
bool try_to_int64(s64* out, const std::string& value, s64 min, s64 max);
|
||||
|
||||
// Config tree entry type.
|
||||
enum class type : uint
|
||||
{
|
||||
node = 0, // cfg::node type
|
||||
boolean, // cfg::bool_entry type
|
||||
fixed_map, // cfg::map_entry type
|
||||
enumeration, // cfg::enum_entry type
|
||||
integer, // cfg::int_entry type
|
||||
string, // cfg::string_entry type
|
||||
set, // cfg::set_entry type
|
||||
};
|
||||
|
||||
// Config tree entry abstract base class
|
||||
class entry_base
|
||||
{
|
||||
const type m_type;
|
||||
|
||||
protected:
|
||||
// Ownerless entry constructor
|
||||
entry_base(type _type);
|
||||
|
||||
// Owned entry constructor
|
||||
entry_base(type _type, class node& owner, const std::string& name);
|
||||
|
||||
public:
|
||||
// Disallow copy/move constructors and assignments
|
||||
entry_base(const entry_base&) = delete;
|
||||
|
||||
// Get type
|
||||
type get_type() const { return m_type; }
|
||||
|
||||
// Access child node (must exist)
|
||||
entry_base& operator [](const std::string& name) const; entry_base& operator [](const char* name) const;
|
||||
|
||||
// Reset defaults
|
||||
virtual void from_default() = 0;
|
||||
|
||||
// Convert to string (optional)
|
||||
virtual std::string to_string() const
|
||||
{
|
||||
return{};
|
||||
}
|
||||
|
||||
// Try to convert from string (optional)
|
||||
virtual bool from_string(const std::string&)
|
||||
{
|
||||
throw std::logic_error("from_string() not specified");
|
||||
}
|
||||
|
||||
// Get string list (optional)
|
||||
virtual std::vector<std::string> to_list() const
|
||||
{
|
||||
return{};
|
||||
}
|
||||
|
||||
// Set multiple values. Implementation-specific, optional.
|
||||
virtual bool from_list(std::vector<std::string>&&)
|
||||
{
|
||||
throw std::logic_error("from_list() not specified");
|
||||
}
|
||||
};
|
||||
|
||||
// Config tree node which contains another nodes
|
||||
class node : public entry_base
|
||||
{
|
||||
std::map<std::string, entry_base*> m_nodes;
|
||||
|
||||
friend class entry_base;
|
||||
|
||||
public:
|
||||
// Root node constructor
|
||||
node()
|
||||
: entry_base(type::node)
|
||||
{
|
||||
}
|
||||
|
||||
// Registered node constructor
|
||||
node(node& owner, const std::string& name)
|
||||
: entry_base(type::node, owner, name)
|
||||
{
|
||||
}
|
||||
|
||||
// Get child nodes
|
||||
const std::map<std::string, entry_base*>& get_nodes() const
|
||||
{
|
||||
return m_nodes;
|
||||
}
|
||||
|
||||
// Serialize node
|
||||
std::string to_string() const override;
|
||||
|
||||
// Deserialize node
|
||||
bool from_string(const std::string& value) override;
|
||||
|
||||
// Set default values
|
||||
void from_default() override;
|
||||
};
|
||||
|
||||
struct bool_entry final : public entry_base
|
||||
{
|
||||
atomic_t<bool> value;
|
||||
|
||||
const bool def;
|
||||
|
||||
bool_entry(node& owner, const std::string& name, bool def = false)
|
||||
: entry_base(type::boolean, owner, name)
|
||||
, value(def)
|
||||
, def(def)
|
||||
{
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return value.load();
|
||||
}
|
||||
|
||||
bool_entry& operator =(bool value)
|
||||
{
|
||||
value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
value = def;
|
||||
}
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
return value.load() ? "true" : "false";
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value) override
|
||||
{
|
||||
if (value == "false")
|
||||
this->value = false;
|
||||
else if (value == "true")
|
||||
this->value = true;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Value node with fixed set of possible values, each maps to a value of type T.
|
||||
template<typename T>
|
||||
struct map_entry final : public entry_base
|
||||
{
|
||||
using init_type = std::initializer_list<std::pair<std::string, T>>;
|
||||
using map_type = std::unordered_map<std::string, T>;
|
||||
using list_type = std::vector<std::string>;
|
||||
using value_type = typename map_type::value_type;
|
||||
|
||||
static map_type make_map(init_type init)
|
||||
{
|
||||
map_type map(init.size());
|
||||
|
||||
for (const auto& v : init)
|
||||
{
|
||||
// Ensure elements are unique
|
||||
ASSERT(map.emplace(v.first, v.second).second);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static list_type make_list(init_type init)
|
||||
{
|
||||
list_type list; list.reserve(init.size());
|
||||
|
||||
for (const auto& v : init)
|
||||
{
|
||||
list.emplace_back(v.first);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public:
|
||||
const map_type map;
|
||||
const list_type list; // Element list sorted in original order
|
||||
const value_type& def; // Pointer to the default value
|
||||
|
||||
private:
|
||||
atomic_t<const value_type*> m_value;
|
||||
|
||||
public:
|
||||
map_entry(node& owner, const std::string& name, const std::string& def, init_type init)
|
||||
: entry_base(type::fixed_map, owner, name)
|
||||
, map(make_map(init))
|
||||
, list(make_list(init))
|
||||
, def(*map.find(def))
|
||||
, m_value(&this->def)
|
||||
{
|
||||
}
|
||||
|
||||
map_entry(node& owner, const std::string& name, std::size_t def_index, init_type init)
|
||||
: map_entry(owner, name, def_index < init.size() ? (init.begin() + def_index)->first : throw std::logic_error("Invalid default value index"), init)
|
||||
{
|
||||
}
|
||||
|
||||
map_entry(node& owner, const std::string& name, init_type init)
|
||||
: map_entry(owner, name, 0, init)
|
||||
{
|
||||
}
|
||||
|
||||
const T& get() const
|
||||
{
|
||||
return m_value.load()->second;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = &def;
|
||||
}
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
return m_value.load()->first;
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value) override
|
||||
{
|
||||
const auto found = map.find(value);
|
||||
|
||||
if (found == map.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_value = &*found;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
return list;
|
||||
}
|
||||
};
|
||||
|
||||
// Value node with fixed set of possible values, each maps to an enum value of type T.
|
||||
template<typename T, bool External = false>
|
||||
class enum_entry final : public entry_base
|
||||
{
|
||||
// Value or reference
|
||||
std::conditional_t<External, atomic_t<T>&, atomic_t<T>> m_value;
|
||||
|
||||
public:
|
||||
const T def;
|
||||
|
||||
enum_entry(node& owner, const std::string& name, std::conditional_t<External, atomic_t<T>&, T> value)
|
||||
: entry_base(type::enumeration, owner, name)
|
||||
, m_value(value)
|
||||
, def(value)
|
||||
{
|
||||
}
|
||||
|
||||
operator T() const
|
||||
{
|
||||
return m_value.load();
|
||||
}
|
||||
|
||||
enum_entry& operator =(T value)
|
||||
{
|
||||
m_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = def;
|
||||
}
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
for (const auto& pair : bijective<T, const char*>::map)
|
||||
{
|
||||
if (pair.first == m_value)
|
||||
{
|
||||
return pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
return{}; // TODO: ???
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value) override
|
||||
{
|
||||
for (const auto& pair : bijective<T, const char*>::map)
|
||||
{
|
||||
if (pair.second == value)
|
||||
{
|
||||
m_value = pair.first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (const auto& pair : bijective<T, const char*>::map)
|
||||
{
|
||||
result.emplace_back(pair.second);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Signed 32/64-bit integer entry with custom Min/Max range.
|
||||
template<s64 Min, s64 Max>
|
||||
class int_entry final : public entry_base
|
||||
{
|
||||
static_assert(Min < Max, "Invalid cfg::int_entry range");
|
||||
|
||||
// Prefer 32 bit type if possible
|
||||
using int_type = std::conditional_t<Min >= INT32_MIN && Max <= INT32_MAX, s32, s64>;
|
||||
|
||||
atomic_t<int_type> m_value;
|
||||
|
||||
public:
|
||||
const int_type def;
|
||||
|
||||
int_entry(node& owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
|
||||
: entry_base(type::integer, owner, name)
|
||||
, m_value(def)
|
||||
, def(def)
|
||||
{
|
||||
}
|
||||
|
||||
operator int_type() const
|
||||
{
|
||||
return m_value.load();
|
||||
}
|
||||
|
||||
int_entry& operator =(int_type value)
|
||||
{
|
||||
if (value < Min || value > Max)
|
||||
{
|
||||
throw fmt::exception("Value out of the valid range: %lld" HERE, s64{ value });
|
||||
}
|
||||
|
||||
m_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
m_value = def;
|
||||
}
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
return std::to_string(m_value.load());
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value) override
|
||||
{
|
||||
s64 result;
|
||||
if (try_to_int64(&result, value, Min, Max))
|
||||
{
|
||||
m_value = static_cast<int_type>(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Alias for 32 bit int
|
||||
using int32_entry = int_entry<INT32_MIN, INT32_MAX>;
|
||||
|
||||
// Alias for 64 bit int
|
||||
using int64_entry = int_entry<INT64_MIN, INT64_MAX>;
|
||||
|
||||
// Simple string entry with mutex
|
||||
class string_entry final : public entry_base
|
||||
{
|
||||
mutable std::mutex m_mutex;
|
||||
std::string m_value;
|
||||
|
||||
public:
|
||||
const std::string def;
|
||||
|
||||
string_entry(node& owner, const std::string& name, const std::string& def = {})
|
||||
: entry_base(type::string, owner, name)
|
||||
, m_value(def)
|
||||
, def(def)
|
||||
{
|
||||
}
|
||||
|
||||
operator std::string() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_value;
|
||||
}
|
||||
|
||||
std::string get() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
string_entry& operator =(const std::string& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_value.size();
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
*this = def;
|
||||
}
|
||||
|
||||
std::string to_string() const override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_value;
|
||||
}
|
||||
|
||||
bool from_string(const std::string& value) override
|
||||
{
|
||||
*this = value;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Simple set entry with mutex (TODO: template for various types)
|
||||
class set_entry final : public entry_base
|
||||
{
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
std::set<std::string> m_set;
|
||||
|
||||
public:
|
||||
// Default value is empty list in current implementation
|
||||
set_entry(node& owner, const std::string& name)
|
||||
: entry_base(type::set, owner, name)
|
||||
{
|
||||
}
|
||||
|
||||
std::set<std::string> get_set() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
return m_set;
|
||||
}
|
||||
|
||||
void set_set(std::set<std::string>&& set)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_set = std::move(set);
|
||||
}
|
||||
|
||||
void from_default() override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_set = {};
|
||||
}
|
||||
|
||||
std::vector<std::string> to_list() const override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
return{ m_set.begin(), m_set.end() };
|
||||
}
|
||||
|
||||
bool from_list(std::vector<std::string>&& list) override
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_set = { std::make_move_iterator(list.begin()), std::make_move_iterator(list.end()) };
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Root type with some predefined nodes. Don't change it, this is not mandatory for adding nodes.
|
||||
struct root_node : node
|
||||
{
|
||||
node core { *this, "Core" };
|
||||
node vfs { *this, "VFS" };
|
||||
node log { *this, "Log" };
|
||||
node video { *this, "Video" };
|
||||
node audio { *this, "Audio" };
|
||||
node io { *this, "Input/Output" };
|
||||
node sys { *this, "System" };
|
||||
node net { *this, "Net" };
|
||||
node misc { *this, "Miscellaneous" };
|
||||
};
|
||||
|
||||
// Get global configuration root instance
|
||||
extern root_node& get_root();
|
||||
|
||||
// Global configuration root instance (cached reference)
|
||||
static root_node& root = get_root();
|
||||
}
|
||||
|
||||
// Registered log channel
|
||||
#define LOG_CHANNEL(name) _log::channel name(#name, _log::level::notice); namespace _log { cfg::enum_entry<_log::level, true> name(cfg::root.log, #name, ::name.enabled); }
|
||||
+527
-1084
File diff suppressed because it is too large
Load Diff
+179
-242
@@ -1,47 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
enum class fsm : u32 // file seek mode
|
||||
{
|
||||
begin,
|
||||
cur,
|
||||
end,
|
||||
};
|
||||
|
||||
#include "types.h"
|
||||
namespace fom // file open mode
|
||||
{
|
||||
enum : u32
|
||||
{
|
||||
read = 1 << 0, // enable reading
|
||||
write = 1 << 1, // enable writing
|
||||
append = 1 << 2, // enable appending (always write to the end of file)
|
||||
create = 1 << 3, // create file if it doesn't exist
|
||||
trunc = 1 << 4, // clear opened file if it's not empty
|
||||
excl = 1 << 5, // failure if the file already exists (used with `create`)
|
||||
|
||||
rewrite = write | create | trunc, // write + create + trunc
|
||||
};
|
||||
};
|
||||
|
||||
enum class fse : u32 // filesystem (file or dir) error
|
||||
{
|
||||
ok, // no error
|
||||
invalid_arguments,
|
||||
};
|
||||
|
||||
namespace fs
|
||||
{
|
||||
// File open mode flags
|
||||
enum struct open_mode : u32
|
||||
{
|
||||
read,
|
||||
write,
|
||||
append,
|
||||
create,
|
||||
trunc,
|
||||
excl,
|
||||
};
|
||||
thread_local extern fse g_tls_error;
|
||||
|
||||
constexpr mset<open_mode> read = open_mode::read; // Enable reading
|
||||
constexpr mset<open_mode> write = open_mode::write; // Enable writing
|
||||
constexpr mset<open_mode> append = open_mode::append; // Always append to the end of the file
|
||||
constexpr mset<open_mode> create = open_mode::create; // Create file if it doesn't exist
|
||||
constexpr mset<open_mode> trunc = open_mode::trunc; // Clear opened file if it's not empty
|
||||
constexpr mset<open_mode> excl = open_mode::excl; // Failure if the file already exists (used with `create`)
|
||||
|
||||
constexpr mset<open_mode> rewrite = write + create + trunc;
|
||||
|
||||
// File seek mode
|
||||
enum class seek_mode : u32
|
||||
{
|
||||
seek_set,
|
||||
seek_cur,
|
||||
seek_end,
|
||||
};
|
||||
|
||||
constexpr auto seek_set = seek_mode::seek_set; // From beginning
|
||||
constexpr auto seek_cur = seek_mode::seek_cur; // From current position
|
||||
constexpr auto seek_end = seek_mode::seek_end; // From end
|
||||
|
||||
// File attributes (TODO)
|
||||
struct stat_t
|
||||
{
|
||||
bool is_directory;
|
||||
@@ -52,59 +42,6 @@ namespace fs
|
||||
s64 ctime;
|
||||
};
|
||||
|
||||
// File handle base
|
||||
struct file_base
|
||||
{
|
||||
virtual ~file_base() = default;
|
||||
|
||||
virtual stat_t stat() = 0;
|
||||
virtual bool trunc(u64 length) = 0;
|
||||
virtual u64 read(void* buffer, u64 size) = 0;
|
||||
virtual u64 write(const void* buffer, u64 size) = 0;
|
||||
virtual u64 seek(s64 offset, seek_mode whence) = 0;
|
||||
virtual u64 size() = 0;
|
||||
};
|
||||
|
||||
// Directory entry (TODO)
|
||||
struct dir_entry : stat_t
|
||||
{
|
||||
std::string name;
|
||||
};
|
||||
|
||||
// Directory handle base
|
||||
struct dir_base
|
||||
{
|
||||
virtual ~dir_base() = default;
|
||||
|
||||
virtual bool read(dir_entry&) = 0;
|
||||
virtual void rewind() = 0;
|
||||
};
|
||||
|
||||
// Virtual device
|
||||
struct device_base
|
||||
{
|
||||
virtual ~device_base() = default;
|
||||
|
||||
virtual bool stat(const std::string& path, stat_t& info) = 0;
|
||||
virtual bool remove_dir(const std::string& path) = 0;
|
||||
virtual bool create_dir(const std::string& path) = 0;
|
||||
virtual bool rename(const std::string& from, const std::string& to) = 0;
|
||||
virtual bool remove(const std::string& path) = 0;
|
||||
virtual bool trunc(const std::string& path, u64 length) = 0;
|
||||
|
||||
virtual std::unique_ptr<file_base> open(const std::string& path, mset<open_mode> mode) = 0;
|
||||
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
|
||||
};
|
||||
|
||||
// Get virtual device for specified path (nullptr for real path)
|
||||
std::shared_ptr<device_base> get_virtual_device(const std::string& path);
|
||||
|
||||
// Set virtual device with specified name (nullptr for deletion)
|
||||
std::shared_ptr<device_base> set_virtual_device(const std::string& root_name, const std::shared_ptr<device_base>&);
|
||||
|
||||
// Try to get parent directory (returns empty string on failure)
|
||||
std::string get_parent_dir(const std::string& path);
|
||||
|
||||
// Get file information
|
||||
bool stat(const std::string& path, stat_t& info);
|
||||
|
||||
@@ -112,16 +49,16 @@ namespace fs
|
||||
bool exists(const std::string& path);
|
||||
|
||||
// Check whether the file exists and is NOT a directory
|
||||
bool is_file(const std::string& path);
|
||||
bool is_file(const std::string& file);
|
||||
|
||||
// Check whether the directory exists and is NOT a file
|
||||
bool is_dir(const std::string& path);
|
||||
bool is_dir(const std::string& dir);
|
||||
|
||||
// Delete empty directory
|
||||
bool remove_dir(const std::string& path);
|
||||
bool remove_dir(const std::string& dir);
|
||||
|
||||
// Create directory
|
||||
bool create_dir(const std::string& path);
|
||||
bool create_dir(const std::string& dir);
|
||||
|
||||
// Create directories
|
||||
bool create_path(const std::string& path);
|
||||
@@ -133,238 +70,238 @@ namespace fs
|
||||
bool copy_file(const std::string& from, const std::string& to, bool overwrite);
|
||||
|
||||
// Delete file
|
||||
bool remove_file(const std::string& path);
|
||||
bool remove_file(const std::string& file);
|
||||
|
||||
// Change file size (possibly appending zeros)
|
||||
bool truncate_file(const std::string& path, u64 length);
|
||||
bool truncate_file(const std::string& file, u64 length);
|
||||
|
||||
class file final
|
||||
{
|
||||
std::unique_ptr<file_base> m_file;
|
||||
using handle_type = std::intptr_t;
|
||||
|
||||
[[noreturn]] void xnull() const;
|
||||
[[noreturn]] void xfail() const;
|
||||
constexpr static handle_type null = -1;
|
||||
|
||||
handle_type m_fd = null;
|
||||
|
||||
friend class file_ptr;
|
||||
|
||||
public:
|
||||
// Default constructor
|
||||
file() = default;
|
||||
|
||||
// Open file with specified mode
|
||||
explicit file(const std::string& path, mset<open_mode> mode = ::fs::read)
|
||||
explicit file(const std::string& filename, u32 mode = fom::read)
|
||||
{
|
||||
open(path, mode);
|
||||
open(filename, mode);
|
||||
}
|
||||
|
||||
// Open file with specified mode
|
||||
bool open(const std::string& path, mset<open_mode> mode = ::fs::read);
|
||||
file(file&& other)
|
||||
: m_fd(other.m_fd)
|
||||
{
|
||||
other.m_fd = null;
|
||||
}
|
||||
|
||||
// Open memory for read
|
||||
explicit file(const void* ptr, std::size_t size);
|
||||
file& operator =(file&& right)
|
||||
{
|
||||
std::swap(m_fd, right.m_fd);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Open vector
|
||||
explicit file(std::vector<char>& vec);
|
||||
~file();
|
||||
|
||||
// Check whether the handle is valid (opened file)
|
||||
bool is_opened() const
|
||||
{
|
||||
return m_fd != null;
|
||||
}
|
||||
|
||||
// Check whether the handle is valid (opened file)
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_file.operator bool();
|
||||
return is_opened();
|
||||
}
|
||||
|
||||
// Close the file explicitly
|
||||
void close()
|
||||
{
|
||||
m_file.reset();
|
||||
}
|
||||
|
||||
void reset(std::unique_ptr<file_base>&& ptr)
|
||||
{
|
||||
m_file = std::move(ptr);
|
||||
}
|
||||
|
||||
std::unique_ptr<file_base> release()
|
||||
{
|
||||
return std::move(m_file);
|
||||
}
|
||||
// Open specified file with specified mode
|
||||
bool open(const std::string& filename, u32 mode = fom::read);
|
||||
|
||||
// Change file size (possibly appending zero bytes)
|
||||
bool trunc(u64 length) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->trunc(length);
|
||||
}
|
||||
bool trunc(u64 size) const;
|
||||
|
||||
// Get file information
|
||||
stat_t stat() const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->stat();
|
||||
}
|
||||
bool stat(stat_t& info) const;
|
||||
|
||||
// Close the file explicitly (destructor automatically closes the file)
|
||||
bool close();
|
||||
|
||||
// Read the data from the file and return the amount of data written in buffer
|
||||
u64 read(void* buffer, u64 count) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->read(buffer, count);
|
||||
}
|
||||
u64 read(void* buffer, u64 count) const;
|
||||
|
||||
// Write the data to the file and return the amount of data actually written
|
||||
u64 write(const void* buffer, u64 count) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->write(buffer, count);
|
||||
}
|
||||
u64 write(const void* buffer, u64 count) const;
|
||||
|
||||
// Change current position, returns previous position
|
||||
u64 seek(s64 offset, seek_mode whence = seek_set) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->seek(offset, whence);
|
||||
}
|
||||
// Move file pointer
|
||||
u64 seek(s64 offset, fsm seek_mode = fsm::begin) const;
|
||||
|
||||
// Get file size
|
||||
u64 size() const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->size();
|
||||
}
|
||||
u64 size() const;
|
||||
|
||||
// Get current position
|
||||
u64 pos() const
|
||||
// Write std::string
|
||||
const file& operator <<(const std::string& str) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
return m_file->seek(0, seek_cur);
|
||||
}
|
||||
|
||||
// Write std::string unconditionally
|
||||
const file& write(const std::string& str) const
|
||||
{
|
||||
if (write(str.data(), str.size()) != str.size()) xfail();
|
||||
CHECK_ASSERTION(write(str.data(), str.size()) == str.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Write POD unconditionally
|
||||
// Write POD
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, const file&> write(const T& data) const
|
||||
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, const file&> operator <<(const T& data) const
|
||||
{
|
||||
if (write(std::addressof(data), sizeof(T)) != sizeof(T)) xfail();
|
||||
CHECK_ASSERTION(write(std::addressof(data), sizeof(T)) == sizeof(T));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Write POD std::vector unconditionally
|
||||
// Write POD std::vector
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, const file&> write(const std::vector<T>& vec) const
|
||||
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, const file&> operator <<(const std::vector<T>& vec) const
|
||||
{
|
||||
if (write(vec.data(), vec.size() * sizeof(T)) != vec.size() * sizeof(T)) xfail();
|
||||
CHECK_ASSERTION(write(vec.data(), vec.size() * sizeof(T)) == vec.size() * sizeof(T));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Read std::string, size must be set by resize() method
|
||||
// Read std::string
|
||||
bool read(std::string& str) const
|
||||
{
|
||||
return read(&str[0], str.size()) == str.size();
|
||||
}
|
||||
|
||||
// Read POD, sizeof(T) is used
|
||||
// Read POD
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, bool> read(T& data) const
|
||||
{
|
||||
return read(&data, sizeof(T)) == sizeof(T);
|
||||
}
|
||||
|
||||
// Read POD std::vector, size must be set by resize() method
|
||||
// Read POD std::vector
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, bool> read(std::vector<T>& vec) const
|
||||
{
|
||||
return read(vec.data(), sizeof(T) * vec.size()) == sizeof(T) * vec.size();
|
||||
}
|
||||
|
||||
// Read POD (experimental)
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, T> read() const
|
||||
{
|
||||
T result;
|
||||
if (!read(result)) xfail();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read full file to std::string
|
||||
std::string to_string() const
|
||||
// Convert to std::string
|
||||
operator std::string() const
|
||||
{
|
||||
std::string result;
|
||||
result.resize(size());
|
||||
if (seek(0), !read(result)) xfail();
|
||||
result.resize(size() - seek(0, fsm::cur));
|
||||
CHECK_ASSERTION(read(result));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// Read full file to std::vector
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_pod<T>::value && !std::is_pointer<T>::value, std::vector<T>> to_vector() const
|
||||
class file_ptr final
|
||||
{
|
||||
char* m_ptr = nullptr;
|
||||
u64 m_size;
|
||||
|
||||
public:
|
||||
file_ptr() = default;
|
||||
|
||||
file_ptr(file_ptr&& right)
|
||||
: m_ptr(right.m_ptr)
|
||||
, m_size(right.m_size)
|
||||
{
|
||||
std::vector<T> result;
|
||||
result.resize(size() / sizeof(T));
|
||||
if (seek(0), !read(result)) xfail();
|
||||
return result;
|
||||
right.m_ptr = 0;
|
||||
}
|
||||
|
||||
file_ptr& operator =(file_ptr&& right)
|
||||
{
|
||||
std::swap(m_ptr, right.m_ptr);
|
||||
std::swap(m_size, right.m_size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
file_ptr(const file& f)
|
||||
{
|
||||
reset(f);
|
||||
}
|
||||
|
||||
~file_ptr()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
// Open file mapping
|
||||
void reset(const file& f);
|
||||
|
||||
// Close file mapping
|
||||
void reset();
|
||||
|
||||
// Get pointer
|
||||
operator char*() const
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
};
|
||||
|
||||
class dir final
|
||||
{
|
||||
std::unique_ptr<dir_base> m_dir;
|
||||
|
||||
[[noreturn]] void xnull() const;
|
||||
std::unique_ptr<char[]> m_path;
|
||||
std::intptr_t m_dd; // handle (aux)
|
||||
|
||||
public:
|
||||
dir() = default;
|
||||
|
||||
// Open dir handle
|
||||
explicit dir(const std::string& path)
|
||||
explicit dir(const std::string& dirname)
|
||||
{
|
||||
open(path);
|
||||
open(dirname);
|
||||
}
|
||||
|
||||
// Open specified directory
|
||||
bool open(const std::string& path);
|
||||
dir(dir&& other)
|
||||
: m_dd(other.m_dd)
|
||||
, m_path(std::move(other.m_path))
|
||||
{
|
||||
}
|
||||
|
||||
dir& operator =(dir&& right)
|
||||
{
|
||||
std::swap(m_dd, right.m_dd);
|
||||
std::swap(m_path, right.m_path);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~dir();
|
||||
|
||||
// Check whether the handle is valid (opened directory)
|
||||
bool is_opened() const
|
||||
{
|
||||
return m_path.operator bool();
|
||||
}
|
||||
|
||||
// Check whether the handle is valid (opened directory)
|
||||
explicit operator bool() const
|
||||
{
|
||||
return m_dir.operator bool();
|
||||
return is_opened();
|
||||
}
|
||||
|
||||
// Close the directory explicitly
|
||||
void close()
|
||||
{
|
||||
m_dir.reset();
|
||||
}
|
||||
|
||||
void reset(std::unique_ptr<dir_base>&& ptr)
|
||||
{
|
||||
m_dir = std::move(ptr);
|
||||
}
|
||||
// Open specified directory
|
||||
bool open(const std::string& dirname);
|
||||
|
||||
std::unique_ptr<dir_base> release()
|
||||
{
|
||||
return std::move(m_dir);
|
||||
}
|
||||
// Close the directory explicitly (destructor automatically closes the directory)
|
||||
bool close();
|
||||
|
||||
// Get next directory entry
|
||||
bool read(dir_entry& out) const
|
||||
{
|
||||
if (!m_dir) xnull();
|
||||
return m_dir->read(out);
|
||||
}
|
||||
// Get next directory entry (UTF-8 name and file stat)
|
||||
bool read(std::string& name, stat_t& info);
|
||||
|
||||
// Reset to the beginning
|
||||
void rewind() const
|
||||
bool first(std::string& name, stat_t& info);
|
||||
|
||||
struct entry
|
||||
{
|
||||
if (!m_dir) xnull();
|
||||
return m_dir->rewind();
|
||||
}
|
||||
std::string name;
|
||||
stat_t info;
|
||||
};
|
||||
|
||||
class iterator
|
||||
{
|
||||
entry m_entry;
|
||||
dir* m_parent;
|
||||
dir_entry m_entry;
|
||||
|
||||
public:
|
||||
enum class mode
|
||||
@@ -381,18 +318,24 @@ namespace fs
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_ok;
|
||||
|
||||
if (mode_ == mode::from_first)
|
||||
{
|
||||
m_parent->rewind();
|
||||
is_ok = m_parent->first(m_entry.name, m_entry.info);
|
||||
}
|
||||
else
|
||||
{
|
||||
is_ok = m_parent->read(m_entry.name, m_entry.info);
|
||||
}
|
||||
|
||||
if (!m_parent->read(m_entry))
|
||||
if (!is_ok)
|
||||
{
|
||||
m_parent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
dir_entry& operator *()
|
||||
entry& operator *()
|
||||
{
|
||||
return m_entry;
|
||||
}
|
||||
@@ -411,7 +354,7 @@ namespace fs
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return{ m_dir ? this : nullptr };
|
||||
return{ this };
|
||||
}
|
||||
|
||||
iterator end()
|
||||
@@ -421,14 +364,8 @@ namespace fs
|
||||
};
|
||||
|
||||
// Get configuration directory
|
||||
const std::string& get_config_dir();
|
||||
std::string get_config_dir();
|
||||
|
||||
// Get executable directory
|
||||
const std::string& get_executable_dir();
|
||||
|
||||
// Delete directory and all its contents recursively
|
||||
void remove_all(const std::string& path);
|
||||
|
||||
// Get size of all files recursively
|
||||
u64 get_dir_size(const std::string& path);
|
||||
std::string get_executable_dir();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Platform.h"
|
||||
#include "GNU.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/types.h>
|
||||
@@ -1,28 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <immintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
#define IS_LE_MACHINE 1
|
||||
#define IS_BE_MACHINE 0
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <x86intrin.h>
|
||||
#endif
|
||||
|
||||
// Some platforms don't support thread_local well yet.
|
||||
#ifndef _MSC_VER
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
#define thread_local __declspec(thread)
|
||||
#elif __APPLE__
|
||||
#define thread_local __thread
|
||||
#define __assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define safe_buffers __declspec(safebuffers)
|
||||
#else
|
||||
#define safe_buffers
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
@@ -31,15 +14,30 @@
|
||||
#define never_inline __attribute__((noinline))
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define safe_buffers __declspec(safebuffers)
|
||||
#else
|
||||
#define safe_buffers
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define force_inline __forceinline
|
||||
#else
|
||||
#define force_inline __attribute__((always_inline)) inline
|
||||
#define force_inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
#define alignas(x) _CRT_ALIGN(x)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUG__)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef __APPLE__
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#define _fpclass(x) std::fpclassify(x)
|
||||
#define INFINITE 0xFFFFFFFF
|
||||
@@ -65,6 +63,170 @@ int clock_gettime(clockid_t clk_id, struct timespec *tp);
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* __GNUG__ */
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
// Unsigned 128-bit integer implementation
|
||||
struct alignas(16) u128
|
||||
{
|
||||
std::uint64_t lo, hi;
|
||||
|
||||
u128() = default;
|
||||
|
||||
u128(const u128&) = default;
|
||||
|
||||
u128(std::uint64_t l)
|
||||
: lo(l)
|
||||
, hi(0)
|
||||
{
|
||||
}
|
||||
|
||||
u128 operator +(const u128& r) const
|
||||
{
|
||||
u128 value;
|
||||
_addcarry_u64(_addcarry_u64(0, r.lo, lo, &value.lo), r.hi, hi, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator +(const u128& l, std::uint64_t r)
|
||||
{
|
||||
u128 value;
|
||||
_addcarry_u64(_addcarry_u64(0, r, l.lo, &value.lo), l.hi, 0, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator +(std::uint64_t l, const u128& r)
|
||||
{
|
||||
u128 value;
|
||||
_addcarry_u64(_addcarry_u64(0, r.lo, l, &value.lo), 0, r.hi, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
u128 operator -(const u128& r) const
|
||||
{
|
||||
u128 value;
|
||||
_subborrow_u64(_subborrow_u64(0, r.lo, lo, &value.lo), r.hi, hi, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator -(const u128& l, std::uint64_t r)
|
||||
{
|
||||
u128 value;
|
||||
_subborrow_u64(_subborrow_u64(0, r, l.lo, &value.lo), 0, l.hi, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator -(std::uint64_t l, const u128& r)
|
||||
{
|
||||
u128 value;
|
||||
_subborrow_u64(_subborrow_u64(0, r.lo, l, &value.lo), r.hi, 0, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
u128 operator +() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128 operator -() const
|
||||
{
|
||||
u128 value;
|
||||
_subborrow_u64(_subborrow_u64(0, lo, 0, &value.lo), hi, 0, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
u128& operator ++()
|
||||
{
|
||||
_addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi);
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128 operator ++(int)
|
||||
{
|
||||
u128 value = *this;
|
||||
_addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
u128& operator --()
|
||||
{
|
||||
_subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi);
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128 operator --(int)
|
||||
{
|
||||
u128 value = *this;
|
||||
_subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
u128 operator ~() const
|
||||
{
|
||||
u128 value;
|
||||
value.lo = ~lo;
|
||||
value.hi = ~hi;
|
||||
return value;
|
||||
}
|
||||
|
||||
u128 operator &(const u128& r) const
|
||||
{
|
||||
u128 value;
|
||||
value.lo = lo & r.lo;
|
||||
value.hi = hi & r.hi;
|
||||
return value;
|
||||
}
|
||||
|
||||
u128 operator |(const u128& r) const
|
||||
{
|
||||
u128 value;
|
||||
value.lo = lo | r.lo;
|
||||
value.hi = hi | r.hi;
|
||||
return value;
|
||||
}
|
||||
|
||||
u128 operator ^(const u128& r) const
|
||||
{
|
||||
u128 value;
|
||||
value.lo = lo ^ r.lo;
|
||||
value.hi = hi ^ r.hi;
|
||||
return value;
|
||||
}
|
||||
|
||||
u128& operator +=(const u128& r)
|
||||
{
|
||||
_addcarry_u64(_addcarry_u64(0, r.lo, lo, &lo), r.hi, hi, &hi);
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128& operator +=(uint64_t r)
|
||||
{
|
||||
_addcarry_u64(_addcarry_u64(0, r, lo, &lo), 0, hi, &hi);
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128& operator &=(const u128& r)
|
||||
{
|
||||
lo &= r.lo;
|
||||
hi &= r.hi;
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128& operator |=(const u128& r)
|
||||
{
|
||||
lo |= r.lo;
|
||||
hi |= r.hi;
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128& operator ^=(const u128& r)
|
||||
{
|
||||
lo ^= r.lo;
|
||||
hi ^= r.hi;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
inline std::uint32_t cntlz32(std::uint32_t arg)
|
||||
{
|
||||
#if defined(_MSC_VER)
|
||||
@@ -85,67 +247,7 @@ inline std::uint64_t cntlz64(std::uint64_t arg)
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct add_flags_result_t
|
||||
{
|
||||
T result;
|
||||
bool carry;
|
||||
//bool overflow;
|
||||
bool zero;
|
||||
bool sign;
|
||||
|
||||
add_flags_result_t() = default;
|
||||
|
||||
// Straighforward ADD with flags
|
||||
add_flags_result_t(T a, T b)
|
||||
: result(a + b)
|
||||
, carry(result < a)
|
||||
//, overflow((result ^ ~(a ^ b)) >> (sizeof(T) * 8 - 1) != 0)
|
||||
, zero(result == 0)
|
||||
, sign(result >> (sizeof(T) * 8 - 1) != 0)
|
||||
{
|
||||
}
|
||||
|
||||
// Straighforward ADC with flags
|
||||
add_flags_result_t(T a, T b, bool c)
|
||||
: add_flags_result_t(a, b)
|
||||
{
|
||||
add_flags_result_t r(result, c);
|
||||
result = r.result;
|
||||
carry |= r.carry;
|
||||
//overflow |= r.overflow;
|
||||
zero = r.zero;
|
||||
sign = r.sign;
|
||||
}
|
||||
};
|
||||
|
||||
inline add_flags_result_t<std::uint32_t> add32_flags(std::uint32_t a, std::uint32_t b)
|
||||
{
|
||||
//add_flags_result_t<std::uint32_t> r;
|
||||
//r.carry = _addcarry_u32(0, a, b, &r.result) != 0;
|
||||
//r.zero = r.result == 0;
|
||||
//r.sign = r.result >> 31;
|
||||
//return r;
|
||||
|
||||
return{ a, b };
|
||||
}
|
||||
|
||||
inline add_flags_result_t<std::uint32_t> add32_flags(std::uint32_t a, std::uint32_t b, bool c)
|
||||
{
|
||||
return{ a, b, c };
|
||||
}
|
||||
|
||||
inline add_flags_result_t<std::uint64_t> add64_flags(std::uint64_t a, std::uint64_t b)
|
||||
{
|
||||
return{ a, b };
|
||||
}
|
||||
|
||||
inline add_flags_result_t<std::uint64_t> add64_flags(std::uint64_t a, std::uint64_t b, bool c)
|
||||
{
|
||||
return{ a, b, c };
|
||||
}
|
||||
|
||||
// Compare 16 packed unsigned bytes (greater than)
|
||||
// compare 16 packed unsigned bytes (greater than)
|
||||
inline __m128i sse_cmpgt_epu8(__m128i A, __m128i B)
|
||||
{
|
||||
// (A xor 0x80) > (B xor 0x80)
|
||||
@@ -192,36 +294,3 @@ inline __m128 sse_log2_ps(__m128 A)
|
||||
const auto x8 = _mm_cvtepi32_ps(_mm_sub_epi32(_mm_srli_epi32(_mm_castps_si128(x0), 23), _mm_set1_epi32(127)));
|
||||
return _mm_add_ps(_mm_mul_ps(_mm_mul_ps(_mm_mul_ps(_mm_mul_ps(x5, x6), x7), x4), _c), _mm_add_ps(_mm_mul_ps(x4, _c), x8));
|
||||
}
|
||||
|
||||
// Helper function, used by ""_u16, ""_u32, ""_u64
|
||||
constexpr std::uint8_t to_u8(char c)
|
||||
{
|
||||
return static_cast<std::uint8_t>(c);
|
||||
}
|
||||
|
||||
// Convert 2-byte string to u16 value like reinterpret_cast does
|
||||
constexpr std::uint16_t operator""_u16(const char* s, std::size_t length)
|
||||
{
|
||||
return length != 2 ? throw s :
|
||||
#if IS_LE_MACHINE == 1
|
||||
to_u8(s[1]) << 8 | to_u8(s[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Convert 4-byte string to u32 value like reinterpret_cast does
|
||||
constexpr std::uint32_t operator""_u32(const char* s, std::size_t length)
|
||||
{
|
||||
return length != 4 ? throw s :
|
||||
#if IS_LE_MACHINE == 1
|
||||
to_u8(s[3]) << 24 | to_u8(s[2]) << 16 | to_u8(s[1]) << 8 | to_u8(s[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Convert 8-byte string to u64 value like reinterpret_cast does
|
||||
constexpr std::uint64_t operator""_u64(const char* s, std::size_t length)
|
||||
{
|
||||
return length != 8 ? throw s :
|
||||
#if IS_LE_MACHINE == 1
|
||||
static_cast<std::uint64_t>(to_u8(s[7]) << 24 | to_u8(s[6]) << 16 | to_u8(s[5]) << 8 | to_u8(s[4])) << 32 | to_u8(s[3]) << 24 | to_u8(s[2]) << 16 | to_u8(s[1]) << 8 | to_u8(s[0]);
|
||||
#endif
|
||||
}
|
||||
+260
-73
@@ -1,98 +1,285 @@
|
||||
#include "Log.h"
|
||||
#include "stdafx.h"
|
||||
#include <iostream>
|
||||
#include <cinttypes>
|
||||
#include "Thread.h"
|
||||
#include "File.h"
|
||||
#include "Log.h"
|
||||
|
||||
namespace _log
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
using namespace Log;
|
||||
|
||||
std::unique_ptr<LogManager> g_log_manager;
|
||||
|
||||
u32 LogMessage::size() const
|
||||
{
|
||||
static file_listener& get_logger()
|
||||
//1 byte for NULL terminator
|
||||
return (u32)(sizeof(LogMessage::size_type) + sizeof(LogType) + sizeof(Severity) + sizeof(std::string::value_type) * mText.size() + 1);
|
||||
}
|
||||
|
||||
void LogMessage::serialize(char *output) const
|
||||
{
|
||||
LogMessage::size_type size = this->size();
|
||||
memcpy(output, &size, sizeof(LogMessage::size_type));
|
||||
output += sizeof(LogMessage::size_type);
|
||||
memcpy(output, &mType, sizeof(LogType));
|
||||
output += sizeof(LogType);
|
||||
memcpy(output, &mServerity, sizeof(Severity));
|
||||
output += sizeof(Severity);
|
||||
memcpy(output, mText.c_str(), mText.size() );
|
||||
output += sizeof(std::string::value_type)*mText.size();
|
||||
*output = '\0';
|
||||
|
||||
}
|
||||
LogMessage LogMessage::deserialize(char *input, u32* size_out)
|
||||
{
|
||||
LogMessage msg;
|
||||
LogMessage::size_type msgSize = *(reinterpret_cast<LogMessage::size_type*>(input));
|
||||
input += sizeof(LogMessage::size_type);
|
||||
msg.mType = *(reinterpret_cast<LogType*>(input));
|
||||
input += sizeof(LogType);
|
||||
msg.mServerity = *(reinterpret_cast<Severity*>(input));
|
||||
input += sizeof(Severity);
|
||||
if (msgSize > 9000)
|
||||
{
|
||||
// Use magic static
|
||||
static file_listener logger("RPCS3.log");
|
||||
return logger;
|
||||
int wtf = 6;
|
||||
}
|
||||
|
||||
file_writer g_tty_file("TTY.log");
|
||||
|
||||
channel GENERAL(nullptr, level::notice);
|
||||
channel LOADER("LDR", level::notice);
|
||||
channel MEMORY("MEM", level::notice);
|
||||
channel RSX("RSX", level::notice);
|
||||
channel HLE("HLE", level::notice);
|
||||
channel PPU("PPU", level::notice);
|
||||
channel SPU("SPU", level::notice);
|
||||
channel ARMv7("ARMv7");
|
||||
|
||||
thread_local std::string(*g_tls_make_prefix)(const channel&, level, const std::string&) = nullptr;
|
||||
msg.mText.append(input, msgSize - 1 - sizeof(Severity) - sizeof(LogType));
|
||||
if (size_out){(*size_out) = msgSize;}
|
||||
return msg;
|
||||
}
|
||||
|
||||
void _log::broadcast(const _log::channel& ch, _log::level sev, const std::string& text)
|
||||
{
|
||||
get_logger().log(ch, sev, text);
|
||||
}
|
||||
|
||||
[[noreturn]] extern void catch_all_exceptions();
|
||||
|
||||
_log::file_writer::file_writer(const std::string& name)
|
||||
LogChannel::LogChannel() : LogChannel("unknown")
|
||||
{}
|
||||
|
||||
LogChannel::LogChannel(const std::string& name) :
|
||||
name(name)
|
||||
, mEnabled(true)
|
||||
, mLogLevel(Severity::Warning)
|
||||
{}
|
||||
|
||||
void LogChannel::log(const LogMessage &msg)
|
||||
{
|
||||
try
|
||||
std::lock_guard<std::mutex> lock(mListenerLock);
|
||||
for (auto &listener : mListeners)
|
||||
{
|
||||
if (!m_file.open(fs::get_config_dir() + name, fs::rewrite + fs::append))
|
||||
listener->log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void LogChannel::addListener(std::shared_ptr<LogListener> listener)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mListenerLock);
|
||||
mListeners.insert(listener);
|
||||
}
|
||||
void LogChannel::removeListener(std::shared_ptr<LogListener> listener)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mListenerLock);
|
||||
mListeners.erase(listener);
|
||||
}
|
||||
|
||||
struct CoutListener : LogListener
|
||||
{
|
||||
void log(const LogMessage &msg) override
|
||||
{
|
||||
std::cerr << msg.mText << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
struct FileListener : LogListener
|
||||
{
|
||||
fs::file mFile;
|
||||
bool mPrependChannelName;
|
||||
|
||||
FileListener(const std::string& name = _PRGNAME_ ".log", bool prependChannel = true)
|
||||
: mFile(fs::get_config_dir() + name, fom::rewrite)
|
||||
, mPrependChannelName(prependChannel)
|
||||
{
|
||||
if (!mFile)
|
||||
{
|
||||
throw fmt::exception("Can't create log file %s (error %d)", name, errno);
|
||||
#ifdef _WIN32
|
||||
MessageBoxA(0, ("Can't create log file: " + name).c_str(), "Error", MB_ICONERROR);
|
||||
#else
|
||||
std::printf("Can't create log file: %s\n", name.c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
catch_all_exceptions();
|
||||
}
|
||||
}
|
||||
|
||||
void _log::file_writer::log(const std::string& text)
|
||||
void log(const LogMessage &msg) override
|
||||
{
|
||||
std::string text = msg.mText;
|
||||
if (mPrependChannelName)
|
||||
{
|
||||
text.insert(0, gTypeNameTable[static_cast<u32>(msg.mType)].mName);
|
||||
|
||||
if (msg.mType == Log::TTY)
|
||||
{
|
||||
text = fmt::escape(text);
|
||||
if (text[text.length() - 1] != '\n')
|
||||
{
|
||||
text += '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mFile << text;
|
||||
}
|
||||
};
|
||||
|
||||
LogManager::LogManager()
|
||||
#ifdef BUFFERED_LOGGING
|
||||
: mExiting(false), mLogConsumer()
|
||||
#endif
|
||||
{
|
||||
m_file.write(text);
|
||||
auto it = mChannels.begin();
|
||||
std::shared_ptr<LogListener> listener(new FileListener());
|
||||
for (const LogTypeName& name : gTypeNameTable)
|
||||
{
|
||||
it->name = name.mName;
|
||||
it->addListener(listener);
|
||||
it++;
|
||||
}
|
||||
std::shared_ptr<LogListener> TTYListener(new FileListener("TTY.log", false));
|
||||
getChannel(TTY).addListener(TTYListener);
|
||||
#ifdef BUFFERED_LOGGING
|
||||
mLogConsumer = std::thread(&LogManager::consumeLog, this);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::size_t _log::file_writer::size() const
|
||||
LogManager::~LogManager()
|
||||
{
|
||||
return m_file.pos();
|
||||
#ifdef BUFFERED_LOGGING
|
||||
mExiting = true;
|
||||
mBufferReady.notify_all();
|
||||
mLogConsumer.join();
|
||||
}
|
||||
|
||||
void _log::file_listener::log(const _log::channel& ch, _log::level sev, const std::string& text)
|
||||
void LogManager::consumeLog()
|
||||
{
|
||||
std::string msg; msg.reserve(text.size() + 200);
|
||||
|
||||
// Used character: U+00B7 (Middle Dot)
|
||||
switch (sev)
|
||||
std::unique_lock<std::mutex> lock(mStatusMut);
|
||||
while (!mExiting)
|
||||
{
|
||||
case level::always: msg = u8"·A "; break;
|
||||
case level::fatal: msg = u8"·F "; break;
|
||||
case level::error: msg = u8"·E "; break;
|
||||
case level::todo: msg = u8"·U "; break;
|
||||
case level::success: msg = u8"·S "; break;
|
||||
case level::warning: msg = u8"·W "; break;
|
||||
case level::notice: msg = u8"·! "; break;
|
||||
case level::trace: msg = u8"·T "; break;
|
||||
}
|
||||
mBufferReady.wait(lock);
|
||||
mBuffer.lockGet();
|
||||
size_t size = mBuffer.size();
|
||||
std::vector<char> local_messages(size);
|
||||
mBuffer.popN(&local_messages.front(), size);
|
||||
mBuffer.unlockGet();
|
||||
|
||||
// TODO: print time?
|
||||
|
||||
if (auto func = g_tls_make_prefix)
|
||||
{
|
||||
msg += '{';
|
||||
msg += func(ch, sev, text);
|
||||
msg += "} ";
|
||||
u32 cursor = 0;
|
||||
u32 removed = 0;
|
||||
while (cursor < size)
|
||||
{
|
||||
Log::LogMessage msg = Log::LogMessage::deserialize(local_messages.data() + cursor, &removed);
|
||||
cursor += removed;
|
||||
getChannel(msg.mType).log(msg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void LogManager::log(LogMessage msg)
|
||||
{
|
||||
//don't do any formatting changes or filtering to the TTY output since we
|
||||
//use the raw output to do diffs with the output of a real PS3 and some
|
||||
//programs write text in single bytes to the console
|
||||
if (msg.mType != TTY)
|
||||
{
|
||||
std::string prefix;
|
||||
switch (msg.mServerity)
|
||||
{
|
||||
case Severity::Success:
|
||||
prefix = "S ";
|
||||
break;
|
||||
case Severity::Notice:
|
||||
prefix = "! ";
|
||||
break;
|
||||
case Severity::Warning:
|
||||
prefix = "W ";
|
||||
break;
|
||||
case Severity::Error:
|
||||
prefix = "E ";
|
||||
break;
|
||||
}
|
||||
if (auto thr = thread_ctrl::get_current())
|
||||
{
|
||||
prefix += "{" + thr->get_name() + "} ";
|
||||
}
|
||||
msg.mText.insert(0, prefix);
|
||||
msg.mText.append(1,'\n');
|
||||
}
|
||||
#ifdef BUFFERED_LOGGING
|
||||
size_t size = msg.size();
|
||||
std::vector<char> temp_buffer(size);
|
||||
msg.serialize(temp_buffer.data());
|
||||
mBuffer.pushRange(temp_buffer.begin(), temp_buffer.end());
|
||||
mBufferReady.notify_one();
|
||||
#else
|
||||
mChannels[static_cast<u32>(msg.mType)].log(msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LogManager::addListener(std::shared_ptr<LogListener> listener)
|
||||
{
|
||||
for (auto& channel : mChannels)
|
||||
{
|
||||
channel.addListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
void LogManager::removeListener(std::shared_ptr<LogListener> listener)
|
||||
{
|
||||
for (auto& channel : mChannels)
|
||||
{
|
||||
channel.removeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
LogManager& LogManager::getInstance()
|
||||
{
|
||||
if (!g_log_manager)
|
||||
{
|
||||
g_log_manager.reset(new LogManager());
|
||||
}
|
||||
|
||||
return *g_log_manager;
|
||||
}
|
||||
|
||||
LogChannel &LogManager::getChannel(LogType type)
|
||||
{
|
||||
return mChannels[static_cast<u32>(type)];
|
||||
}
|
||||
|
||||
void log_message(Log::LogType type, Log::Severity sev, const char* text)
|
||||
{
|
||||
log_message(type, sev, std::string(text));
|
||||
}
|
||||
|
||||
void log_message(Log::LogType type, Log::Severity sev, std::string text)
|
||||
{
|
||||
if (g_log_manager)
|
||||
{
|
||||
g_log_manager->log({ type, sev, std::move(text) });
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto severity =
|
||||
sev == Severity::Notice ? "Notice" :
|
||||
sev == Severity::Warning ? "Warning" :
|
||||
sev == Severity::Success ? "Success" :
|
||||
sev == Severity::Error ? "Error" : "Unknown";
|
||||
|
||||
#ifdef _WIN32
|
||||
MessageBoxA(0, text.c_str(), severity,
|
||||
sev == Severity::Notice ? MB_ICONINFORMATION :
|
||||
sev == Severity::Warning ? MB_ICONEXCLAMATION :
|
||||
sev == Severity::Error ? MB_ICONERROR : MB_ICONINFORMATION);
|
||||
#else
|
||||
std::printf("[Log:%s] %s\n", severity, text.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ch.name)
|
||||
{
|
||||
msg += ch.name;
|
||||
msg += sev == level::todo ? " TODO: " : ": ";
|
||||
}
|
||||
else if (sev == level::todo)
|
||||
{
|
||||
msg += "TODO: ";
|
||||
}
|
||||
|
||||
msg += text;
|
||||
msg += '\n';
|
||||
|
||||
file_writer::log(msg);
|
||||
}
|
||||
|
||||
+109
-132
@@ -1,157 +1,134 @@
|
||||
#pragma once
|
||||
#include "Utilities/MTRingbuffer.h"
|
||||
|
||||
#include "types.h"
|
||||
#include "Atomic.h"
|
||||
#include "File.h"
|
||||
#include "StrFmt.h"
|
||||
//#define BUFFERED_LOGGING 1
|
||||
|
||||
namespace _log
|
||||
//first parameter is of type Log::LogType and text is of type std::string
|
||||
|
||||
#define LOG_SUCCESS(logType, text, ...) log_message(logType, Log::Severity::Success, text, ##__VA_ARGS__)
|
||||
#define LOG_NOTICE(logType, text, ...) log_message(logType, Log::Severity::Notice, text, ##__VA_ARGS__)
|
||||
#define LOG_WARNING(logType, text, ...) log_message(logType, Log::Severity::Warning, text, ##__VA_ARGS__)
|
||||
#define LOG_ERROR(logType, text, ...) log_message(logType, Log::Severity::Error, text, ##__VA_ARGS__)
|
||||
|
||||
namespace Log
|
||||
{
|
||||
enum class level : uint
|
||||
const unsigned int MAX_LOG_BUFFER_LENGTH = 1024*1024;
|
||||
const unsigned int gBuffSize = 1000;
|
||||
|
||||
enum LogType : u32
|
||||
{
|
||||
always, // highest level (unused, cannot be disabled)
|
||||
fatal,
|
||||
error,
|
||||
todo,
|
||||
success,
|
||||
warning,
|
||||
notice,
|
||||
trace, // lowest level (usually disabled)
|
||||
GENERAL = 0,
|
||||
LOADER,
|
||||
MEMORY,
|
||||
RSX,
|
||||
HLE,
|
||||
PPU,
|
||||
SPU,
|
||||
ARMv7,
|
||||
TTY,
|
||||
};
|
||||
|
||||
struct channel;
|
||||
struct listener;
|
||||
|
||||
// Send log message to global logger instance
|
||||
void broadcast(const channel& ch, level sev, const std::string& text);
|
||||
|
||||
// Log channel
|
||||
struct channel
|
||||
struct LogTypeName
|
||||
{
|
||||
// Channel prefix (added to every log message)
|
||||
const char* const name;
|
||||
|
||||
// The lowest logging level enabled for this channel (used for early filtering)
|
||||
atomic_t<level> enabled;
|
||||
|
||||
// Constant initialization: name and initial log level
|
||||
constexpr channel(const char* name, level enabled = level::trace)
|
||||
: name{ name }
|
||||
, enabled{ enabled }
|
||||
{
|
||||
}
|
||||
|
||||
// Log without formatting
|
||||
force_inline void log(level sev, const std::string& text) const
|
||||
{
|
||||
if (sev <= enabled)
|
||||
broadcast(*this, sev, text);
|
||||
}
|
||||
|
||||
// Log with formatting
|
||||
template<typename... Args>
|
||||
force_inline safe_buffers void format(level sev, const char* fmt, const Args&... args) const
|
||||
{
|
||||
if (sev <= enabled)
|
||||
broadcast(*this, sev, fmt::format(fmt, fmt::do_unveil(args)...));
|
||||
}
|
||||
|
||||
#define GEN_LOG_METHOD(_sev)\
|
||||
template<typename... Args>\
|
||||
force_inline void _sev(const char* fmt, const Args&... args) const\
|
||||
{\
|
||||
return format<Args...>(level::_sev, fmt, args...);\
|
||||
}
|
||||
|
||||
GEN_LOG_METHOD(fatal)
|
||||
GEN_LOG_METHOD(error)
|
||||
GEN_LOG_METHOD(todo)
|
||||
GEN_LOG_METHOD(success)
|
||||
GEN_LOG_METHOD(warning)
|
||||
GEN_LOG_METHOD(notice)
|
||||
GEN_LOG_METHOD(trace)
|
||||
|
||||
#undef GEN_LOG_METHOD
|
||||
LogType mType;
|
||||
std::string mName;
|
||||
};
|
||||
|
||||
// Log listener (destination)
|
||||
struct listener
|
||||
{
|
||||
listener() = default;
|
||||
|
||||
virtual ~listener() = default;
|
||||
//well I'd love make_array() but alas manually counting is not the end of the world
|
||||
static const std::array<LogTypeName, 9> gTypeNameTable = { {
|
||||
{ GENERAL, "G: " },
|
||||
{ LOADER, "LDR: " },
|
||||
{ MEMORY, "MEM: " },
|
||||
{ RSX, "RSX: " },
|
||||
{ HLE, "HLE: " },
|
||||
{ PPU, "PPU: " },
|
||||
{ SPU, "SPU: " },
|
||||
{ ARMv7, "ARM: " },
|
||||
{ TTY, "TTY: " }
|
||||
} };
|
||||
|
||||
virtual void log(const channel& ch, level sev, const std::string& text) = 0;
|
||||
enum class Severity : u32
|
||||
{
|
||||
Notice = 0,
|
||||
Warning,
|
||||
Success,
|
||||
Error,
|
||||
};
|
||||
|
||||
class file_writer
|
||||
struct LogMessage
|
||||
{
|
||||
// Could be memory-mapped file
|
||||
fs::file m_file;
|
||||
using size_type = u32;
|
||||
LogType mType;
|
||||
Severity mServerity;
|
||||
std::string mText;
|
||||
|
||||
public:
|
||||
file_writer(const std::string& name);
|
||||
|
||||
virtual ~file_writer() = default;
|
||||
|
||||
// Append raw data
|
||||
void log(const std::string& text);
|
||||
|
||||
// Get current file size (may be used by secondary readers)
|
||||
std::size_t size() const;
|
||||
u32 size() const;
|
||||
void serialize(char *output) const;
|
||||
static LogMessage deserialize(char *input, u32* size_out=nullptr);
|
||||
};
|
||||
|
||||
struct file_listener : public file_writer, public listener
|
||||
struct LogListener
|
||||
{
|
||||
file_listener(const std::string& name)
|
||||
: file_writer(name)
|
||||
, listener()
|
||||
{
|
||||
}
|
||||
|
||||
// Encode level, current thread name, channel name and write log message
|
||||
virtual void log(const channel& ch, level sev, const std::string& text) override;
|
||||
virtual ~LogListener() {};
|
||||
virtual void log(const LogMessage &msg) = 0;
|
||||
};
|
||||
|
||||
// Global variable for TTY.log
|
||||
extern file_writer g_tty_file;
|
||||
struct LogChannel
|
||||
{
|
||||
LogChannel();
|
||||
LogChannel(const std::string& name);
|
||||
LogChannel(LogChannel& other) = delete;
|
||||
void log(const LogMessage &msg);
|
||||
void addListener(std::shared_ptr<LogListener> listener);
|
||||
void removeListener(std::shared_ptr<LogListener> listener);
|
||||
std::string name;
|
||||
private:
|
||||
bool mEnabled;
|
||||
Severity mLogLevel;
|
||||
std::mutex mListenerLock;
|
||||
std::set<std::shared_ptr<LogListener>> mListeners;
|
||||
};
|
||||
|
||||
// Small set of predefined channels:
|
||||
|
||||
extern channel GENERAL;
|
||||
extern channel LOADER;
|
||||
extern channel MEMORY;
|
||||
extern channel RSX;
|
||||
extern channel HLE;
|
||||
extern channel PPU;
|
||||
extern channel SPU;
|
||||
extern channel ARMv7;
|
||||
|
||||
extern thread_local std::string(*g_tls_make_prefix)(const channel&, level, const std::string&);
|
||||
struct LogManager
|
||||
{
|
||||
LogManager();
|
||||
~LogManager();
|
||||
static LogManager& getInstance();
|
||||
LogChannel& getChannel(LogType type);
|
||||
void log(LogMessage msg);
|
||||
void addListener(std::shared_ptr<LogListener> listener);
|
||||
void removeListener(std::shared_ptr<LogListener> listener);
|
||||
#ifdef BUFFERED_LOGGING
|
||||
void consumeLog();
|
||||
#endif
|
||||
private:
|
||||
#ifdef BUFFERED_LOGGING
|
||||
MTRingbuffer<char, MAX_LOG_BUFFER_LENGTH> mBuffer;
|
||||
std::condition_variable mBufferReady;
|
||||
std::mutex mStatusMut;
|
||||
std::atomic<bool> mExiting;
|
||||
std::thread mLogConsumer;
|
||||
#endif
|
||||
std::array<LogChannel, std::tuple_size<decltype(gTypeNameTable)>::value> mChannels;
|
||||
//std::array<LogChannel,gTypeNameTable.size()> mChannels; //TODO: use this once Microsoft sorts their shit out
|
||||
};
|
||||
}
|
||||
|
||||
template<>
|
||||
struct bijective<_log::level, const char*>
|
||||
static struct { inline operator Log::LogType() { return Log::LogType::GENERAL; } } GENERAL;
|
||||
static struct { inline operator Log::LogType() { return Log::LogType::LOADER; } } LOADER;
|
||||
static struct { inline operator Log::LogType() { return Log::LogType::MEMORY; } } MEMORY;
|
||||
static struct { inline operator Log::LogType() { return Log::LogType::RSX; } } RSX;
|
||||
static struct { inline operator Log::LogType() { return Log::LogType::HLE; } } HLE;
|
||||
static struct { inline operator Log::LogType() { return Log::LogType::PPU; } } PPU;
|
||||
static struct { inline operator Log::LogType() { return Log::LogType::SPU; } } SPU;
|
||||
static struct { inline operator Log::LogType() { return Log::LogType::ARMv7; } } ARMv7;
|
||||
static struct { inline operator Log::LogType() { return Log::LogType::TTY; } } TTY;
|
||||
|
||||
void log_message(Log::LogType type, Log::Severity sev, const char* text);
|
||||
void log_message(Log::LogType type, Log::Severity sev, std::string text);
|
||||
|
||||
template<typename... Args> never_inline void log_message(Log::LogType type, Log::Severity sev, const char* fmt, Args... args)
|
||||
{
|
||||
static constexpr std::pair<_log::level, const char*> map[]
|
||||
{
|
||||
{ _log::level::always, "Nothing" },
|
||||
{ _log::level::fatal, "Fatal" },
|
||||
{ _log::level::error, "Error" },
|
||||
{ _log::level::todo, "TODO" },
|
||||
{ _log::level::success, "Success" },
|
||||
{ _log::level::warning, "Warning" },
|
||||
{ _log::level::notice, "Notice" },
|
||||
{ _log::level::trace, "Trace" },
|
||||
};
|
||||
};
|
||||
|
||||
// Legacy:
|
||||
|
||||
#define LOG_SUCCESS(ch, fmt, ...) _log::ch.success(fmt, ##__VA_ARGS__)
|
||||
#define LOG_NOTICE(ch, fmt, ...) _log::ch.notice (fmt, ##__VA_ARGS__)
|
||||
#define LOG_WARNING(ch, fmt, ...) _log::ch.warning(fmt, ##__VA_ARGS__)
|
||||
#define LOG_ERROR(ch, fmt, ...) _log::ch.error (fmt, ##__VA_ARGS__)
|
||||
#define LOG_TODO(ch, fmt, ...) _log::ch.todo (fmt, ##__VA_ARGS__)
|
||||
#define LOG_TRACE(ch, fmt, ...) _log::ch.trace (fmt, ##__VA_ARGS__)
|
||||
#define LOG_FATAL(ch, fmt, ...) _log::ch.fatal (fmt, ##__VA_ARGS__)
|
||||
log_message(type, sev, fmt::format(fmt, fmt::do_unveil(args)...));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
#pragma once
|
||||
|
||||
//Simple non-resizable FIFO Ringbuffer that can be simultaneously be read from and written to
|
||||
//if we ever get to use boost please replace this with boost::circular_buffer, there's no reason
|
||||
//why we would have to keep this amateur attempt at such a fundamental data-structure around
|
||||
template< typename T, unsigned int MAX_MTRINGBUFFER_BUFFER_SIZE>
|
||||
class MTRingbuffer{
|
||||
std::array<T, MAX_MTRINGBUFFER_BUFFER_SIZE> mBuffer;
|
||||
//this is a recursive mutex because the get methods lock it but the only
|
||||
//way to be sure that they do not block is to check the size and the only
|
||||
//way to check the size and use get atomically is to lock this mutex,
|
||||
//so it goes:
|
||||
//lock get mutex-->check size-->call get-->lock get mutex-->unlock get mutex-->return from get-->unlock get mutex
|
||||
std::recursive_mutex mMutGet;
|
||||
std::mutex mMutPut;
|
||||
|
||||
size_t mGet;
|
||||
size_t mPut;
|
||||
size_t moveGet(size_t by = 1){ return (mGet + by) % MAX_MTRINGBUFFER_BUFFER_SIZE; }
|
||||
size_t movePut(size_t by = 1){ return (mPut + by) % MAX_MTRINGBUFFER_BUFFER_SIZE; }
|
||||
public:
|
||||
MTRingbuffer() : mGet(0), mPut(0){}
|
||||
|
||||
//blocks until there's something to get, so check "spaceLeft()" if you want to avoid blocking
|
||||
//also lock the get mutex around the spaceLeft() check and the pop if you want to avoid racing
|
||||
T pop()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mMutGet);
|
||||
while (mGet == mPut)
|
||||
{
|
||||
//wait until there's actually something to get
|
||||
//throwing an exception might be better, blocking here is a little awkward
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
|
||||
}
|
||||
size_t ret = mGet;
|
||||
mGet = moveGet();
|
||||
return mBuffer[ret];
|
||||
}
|
||||
|
||||
//blocks if the buffer is full until there's enough room
|
||||
void push(T &putEle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutPut);
|
||||
while (movePut() == mGet)
|
||||
{
|
||||
//if this is reached a lot it's time to increase the buffer size
|
||||
//or implement dynamic re-sizing
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
|
||||
}
|
||||
mBuffer[mPut] = std::forward(putEle);
|
||||
mPut = movePut();
|
||||
}
|
||||
|
||||
bool empty()
|
||||
{
|
||||
return mGet == mPut;
|
||||
}
|
||||
|
||||
//returns the amount of free places, this is the amount of actual free spaces-1
|
||||
//since mGet==mPut signals an empty buffer we can't actually use the last free
|
||||
//space, so we shouldn't report it as free.
|
||||
size_t spaceLeft() //apparently free() is a macro definition in msvc in some conditions
|
||||
{
|
||||
if (mGet < mPut)
|
||||
{
|
||||
return mBuffer.size() - (mPut - mGet) - 1;
|
||||
}
|
||||
else if (mGet > mPut)
|
||||
{
|
||||
return mGet - mPut - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return mBuffer.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
//the magic -1 is the same magic 1 that is explained in the spaceLeft() function
|
||||
return mBuffer.size() - spaceLeft() - 1;
|
||||
}
|
||||
|
||||
//takes random access iterator to T
|
||||
template<typename IteratorType>
|
||||
void pushRange(IteratorType from, IteratorType until)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutPut);
|
||||
size_t length = until - from;
|
||||
|
||||
//if whatever we're trying to store is greater than the entire buffer the following loop will be infinite
|
||||
assert(mBuffer.size() > length);
|
||||
while (spaceLeft() < length)
|
||||
{
|
||||
//if this is reached a lot it's time to increase the buffer size
|
||||
//or implement dynamic re-sizing
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
|
||||
}
|
||||
if (mPut + length <= mBuffer.size())
|
||||
{
|
||||
std::copy(from, until, mBuffer.begin() + mPut);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t tillEnd = mBuffer.size() - mPut;
|
||||
std::copy(from, from + tillEnd, mBuffer.begin() + mPut);
|
||||
std::copy(from + tillEnd, until, mBuffer.begin());
|
||||
}
|
||||
mPut = movePut(length);
|
||||
|
||||
}
|
||||
|
||||
//takes output iterator to T
|
||||
template<typename IteratorType>
|
||||
void popN(IteratorType output, size_t n)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mMutGet);
|
||||
//make sure we're not trying to retrieve more than is in
|
||||
assert(n <= size());
|
||||
peekN<IteratorType>(output, n);
|
||||
mGet = moveGet(n);
|
||||
}
|
||||
|
||||
//takes output iterator to T
|
||||
template<typename IteratorType>
|
||||
void peekN(IteratorType output, size_t n)
|
||||
{
|
||||
size_t lGet = mGet;
|
||||
if (lGet + n <= mBuffer.size())
|
||||
{
|
||||
std::copy_n(mBuffer.begin() + lGet, n, output);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto next = std::copy(mBuffer.begin() + lGet, mBuffer.end(), output);
|
||||
std::copy_n(mBuffer.begin(), n - (mBuffer.size() - lGet), next);
|
||||
}
|
||||
}
|
||||
|
||||
//well this is just asking for trouble
|
||||
//but the comment above the declaration of mMutGet explains why it's there
|
||||
//if there's a better way please remove this
|
||||
void lockGet()
|
||||
{
|
||||
mMutGet.lock();
|
||||
}
|
||||
|
||||
//well this is just asking for trouble
|
||||
//but the comment above the declaration of mMutGet explains why it's there
|
||||
//if there's a better way please remove this
|
||||
void unlockGet()
|
||||
{
|
||||
mMutGet.unlock();
|
||||
}
|
||||
};
|
||||
@@ -1,79 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
|
||||
template<typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
|
||||
constexpr T align(const T& value, std::uint64_t align)
|
||||
{
|
||||
return static_cast<T>((value + (align - 1)) & ~(align - 1));
|
||||
}
|
||||
|
||||
template<typename To, typename From>
|
||||
constexpr To narrow_impl(const To& result, const From& value, const char* message)
|
||||
{
|
||||
return static_cast<From>(result) != value ? throw std::runtime_error(message) : result;
|
||||
}
|
||||
|
||||
// Narrow cast (similar to gsl::narrow) with fixed message
|
||||
template<typename To, typename From>
|
||||
constexpr auto narrow(const From& value, const char* fixed_msg = "::narrow() failed") -> decltype(static_cast<To>(static_cast<From>(std::declval<To>())))
|
||||
{
|
||||
return narrow_impl(static_cast<To>(value), value, fixed_msg);
|
||||
}
|
||||
|
||||
// Return 32 bit .size() for container
|
||||
template<typename CT>
|
||||
constexpr auto size32(const CT& container, const char* fixed_msg = "::size32() failed") -> decltype(static_cast<std::uint32_t>(container.size()))
|
||||
{
|
||||
return narrow<std::uint32_t>(container.size(), fixed_msg);
|
||||
}
|
||||
|
||||
// Return 32 bit size for an array
|
||||
template<typename T, std::size_t Size>
|
||||
constexpr std::uint32_t size32(const T(&)[Size])
|
||||
{
|
||||
static_assert(Size <= UINT32_MAX, "size32() error: too big");
|
||||
return static_cast<std::uint32_t>(Size);
|
||||
}
|
||||
|
||||
#define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, "Invalid " #type " type size")
|
||||
#define CHECK_ALIGN(type, align) static_assert(alignof(type) == align, "Invalid " #type " type alignment")
|
||||
#define CHECK_MAX_SIZE(type, size) static_assert(sizeof(type) <= size, #type " type size is too big")
|
||||
#define CHECK_SIZE_ALIGN(type, size, align) CHECK_SIZE(type, size); CHECK_ALIGN(type, align)
|
||||
|
||||
// Return 32 bit sizeof() to avoid widening/narrowing conversions with size_t
|
||||
#define SIZE_32(type) static_cast<std::uint32_t>(sizeof(type))
|
||||
|
||||
// Return 32 bit alignof() to avoid widening/narrowing conversions with size_t
|
||||
#define ALIGN_32(type) static_cast<std::uint32_t>(alignof(type))
|
||||
|
||||
// Return 32 bit custom offsetof()
|
||||
#define OFFSET_32(type, x) static_cast<std::uint32_t>(reinterpret_cast<std::uintptr_t>(&reinterpret_cast<const volatile char&>(reinterpret_cast<type*>(0ull)->x)))
|
||||
|
||||
// Sometimes to avoid writing std::remove_cv_t<>, example: std::is_same<CV T1, CV T2>
|
||||
#define CV const volatile
|
||||
|
||||
#define CONCATENATE_DETAIL(x, y) x ## y
|
||||
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
|
||||
|
||||
#define STRINGIZE_DETAIL(x) #x
|
||||
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
|
||||
|
||||
// Macro set, allows to hide "return" in simple lambda expressions.
|
||||
#define WRAP_EXPR(expr, ...) [&](__VA_ARGS__) { return expr; }
|
||||
#define COPY_EXPR(expr, ...) [=](__VA_ARGS__) { return expr; }
|
||||
#define PURE_EXPR(expr, ...) [] (__VA_ARGS__) { return expr; }
|
||||
|
||||
#define HERE "\n(in file " __FILE__ ":" STRINGIZE(__LINE__) ")"
|
||||
|
||||
// Ensure that the expression is evaluated to true. Always evaluated and allowed to have side effects (unlike assert() macro).
|
||||
#define ASSERT(expr) if (!(expr)) throw std::runtime_error("Assertion failed: " #expr HERE)
|
||||
|
||||
// Expects() and Ensures() are intended to check function arguments and results.
|
||||
// Expressions are not guaranteed to evaluate. Redefinition with ASSERT macro for better unification.
|
||||
#define Expects ASSERT
|
||||
#define Ensures ASSERT
|
||||
|
||||
#define DECLARE(static_member) decltype(static_member) static_member
|
||||
#define STR_CASE(value) case value: return #value
|
||||
@@ -10,7 +10,7 @@ bool semaphore_t::try_wait()
|
||||
}
|
||||
|
||||
// try to decrement m_value atomically
|
||||
const auto old = m_var.fetch_op([](sync_var_t& var)
|
||||
const auto old = m_var.atomic_op([](sync_var_t& var)
|
||||
{
|
||||
if (var.value)
|
||||
{
|
||||
@@ -36,7 +36,7 @@ bool semaphore_t::try_post()
|
||||
}
|
||||
|
||||
// try to increment m_value atomically
|
||||
const auto old = m_var.fetch_op([&](sync_var_t& var)
|
||||
const auto old = m_var.atomic_op([&](sync_var_t& var)
|
||||
{
|
||||
if (var.value < max_value)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ class semaphore_t
|
||||
// semaphore condition variable
|
||||
std::condition_variable m_cv;
|
||||
|
||||
struct alignas(8) sync_var_t
|
||||
struct sync_var_t
|
||||
{
|
||||
u32 value; // current semaphore value
|
||||
u32 waiters; // current amount of waiters
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "stdafx.h"
|
||||
#include "SharedMutex.h"
|
||||
|
||||
void shared_mutex::impl_lock_shared(u32 old_value)
|
||||
{
|
||||
// Throw if reader count breaks the "second" limit (it should be impossible)
|
||||
CHECK_ASSERTION((old_value & SM_READER_COUNT) != SM_READER_COUNT);
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
// Notify non-zero reader queue size
|
||||
m_ctrl |= SM_READER_QUEUE;
|
||||
|
||||
// Compensate incorrectly increased reader count
|
||||
if ((--m_ctrl & SM_READER_COUNT) == 0 && m_wq_size)
|
||||
{
|
||||
// Notify current exclusive owner (condition passed)
|
||||
m_ocv.notify_one();
|
||||
}
|
||||
|
||||
CHECK_ASSERTION(++m_rq_size);
|
||||
|
||||
// Obtain the reader lock
|
||||
while (!atomic_op(m_ctrl, op_lock_shared))
|
||||
{
|
||||
m_rcv.wait(lock);
|
||||
}
|
||||
|
||||
CHECK_ASSERTION(m_rq_size--);
|
||||
|
||||
if (m_rq_size == 0)
|
||||
{
|
||||
m_ctrl &= ~SM_READER_QUEUE;
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::impl_unlock_shared(u32 new_value)
|
||||
{
|
||||
// Throw if reader count was zero
|
||||
CHECK_ASSERTION((new_value & SM_READER_COUNT) != SM_READER_COUNT);
|
||||
|
||||
// Mutex cannot be unlocked before notification because m_ctrl has been changed outside
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
if (m_wq_size && (new_value & SM_READER_COUNT) == 0)
|
||||
{
|
||||
// Notify current exclusive owner that the latest reader is gone
|
||||
m_ocv.notify_one();
|
||||
}
|
||||
else if (m_rq_size)
|
||||
{
|
||||
m_rcv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::impl_lock_excl(u32 value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
// Notify non-zero writer queue size
|
||||
m_ctrl |= SM_WRITER_QUEUE;
|
||||
|
||||
CHECK_ASSERTION(++m_wq_size);
|
||||
|
||||
// Obtain the writer lock
|
||||
while (!atomic_op(m_ctrl, op_lock_excl))
|
||||
{
|
||||
m_wcv.wait(lock);
|
||||
}
|
||||
|
||||
// Wait for remaining readers
|
||||
while ((m_ctrl & SM_READER_COUNT) != 0)
|
||||
{
|
||||
m_ocv.wait(lock);
|
||||
}
|
||||
|
||||
CHECK_ASSERTION(m_wq_size--);
|
||||
|
||||
if (m_wq_size == 0)
|
||||
{
|
||||
m_ctrl &= ~SM_WRITER_QUEUE;
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::impl_unlock_excl(u32 value)
|
||||
{
|
||||
// Throw if was not locked exclusively
|
||||
CHECK_ASSERTION(value & SM_WRITER_LOCK);
|
||||
|
||||
// Mutex cannot be unlocked before notification because m_ctrl has been changed outside
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
if (m_wq_size)
|
||||
{
|
||||
// Notify next exclusive owner
|
||||
m_wcv.notify_one();
|
||||
}
|
||||
else if (m_rq_size)
|
||||
{
|
||||
// Notify all readers
|
||||
m_rcv.notify_all();
|
||||
}
|
||||
}
|
||||
+53
-144
@@ -1,156 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "Atomic.h"
|
||||
|
||||
//! An attempt to create effective implementation of "shared mutex", lock-free in optimistic case.
|
||||
//! All locking and unlocking may be done by single LOCK XADD or LOCK CMPXCHG instructions.
|
||||
//! MSVC implementation of std::shared_timed_mutex seems suboptimal.
|
||||
//! std::shared_mutex is not available until C++17.
|
||||
class shared_mutex final
|
||||
{
|
||||
using ctrl_type = u32;
|
||||
|
||||
enum : ctrl_type
|
||||
enum : u32
|
||||
{
|
||||
SM_WRITER_LOCK = 1u << 31, // Exclusive lock flag, must be MSB
|
||||
SM_WAITERS_BIT = 1u << 30, // Flag set if m_wq_size or m_rq_size is non-zero
|
||||
SM_INVALID_BIT = 1u << 29, // Unreachable reader count bit (may be set by incorrect unlock_shared() call)
|
||||
SM_WRITER_LOCK = 1u << 31, // Exclusive lock flag, must be MSB
|
||||
SM_WRITER_QUEUE = 1u << 30, // Flag set if m_wq_size != 0
|
||||
SM_READER_QUEUE = 1u << 29, // Flag set if m_rq_size != 0
|
||||
|
||||
SM_READER_MASK = SM_WAITERS_BIT - 1, // Valid reader count bit mask
|
||||
SM_READER_MAX = 1u << 24, // Max reader count
|
||||
SM_READER_COUNT = SM_READER_QUEUE - 1, // Valid reader count bit mask
|
||||
SM_READER_MAX = 1u << 24, // Max reader count
|
||||
};
|
||||
|
||||
atomic_t<ctrl_type> m_ctrl{}; // Control atomic variable: reader count | SM_* flags
|
||||
std::atomic<u32> m_ctrl{}; // Control atomic variable: reader count | SM_* flags
|
||||
std::thread::id m_owner{}; // Current exclusive owner (TODO: implement only for debug mode?)
|
||||
|
||||
std::mutex m_mutex;
|
||||
|
||||
std::size_t m_rq_size{}; // Reader queue size (threads waiting on m_rcv)
|
||||
std::size_t m_wq_size{}; // Writer queue size (threads waiting on m_wcv and m_ocv)
|
||||
u32 m_rq_size{}; // Reader queue size (threads waiting on m_rcv)
|
||||
u32 m_wq_size{}; // Writer queue size (threads waiting on m_wcv+m_ocv)
|
||||
|
||||
std::condition_variable m_rcv; // Reader queue
|
||||
std::condition_variable m_wcv; // Writer queue
|
||||
std::condition_variable m_ocv; // For current exclusive owner
|
||||
|
||||
void lock_shared_hard()
|
||||
static bool op_lock_shared(u32& ctrl)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
// Validate
|
||||
if ((m_ctrl & SM_INVALID_BIT) != 0) throw std::runtime_error("shared_mutex::lock_shared(): Invalid bit");
|
||||
if ((m_ctrl & SM_READER_MASK) == 0) throw std::runtime_error("shared_mutex::lock_shared(): No readers");
|
||||
|
||||
// Notify non-zero reader queue size
|
||||
m_ctrl |= SM_WAITERS_BIT, m_rq_size++;
|
||||
|
||||
// Fix excess reader count
|
||||
if ((--m_ctrl & SM_READER_MASK) == 0 && m_wq_size)
|
||||
{
|
||||
// Notify exclusive owner
|
||||
m_ocv.notify_one();
|
||||
}
|
||||
|
||||
// Obtain the reader lock
|
||||
while (true)
|
||||
{
|
||||
const auto ctrl = m_ctrl.load();
|
||||
|
||||
// Check writers and reader limit
|
||||
if (m_wq_size || (ctrl & ~SM_WAITERS_BIT) >= SM_READER_MAX)
|
||||
{
|
||||
m_rcv.wait(lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_ctrl.compare_and_swap_test(ctrl, ctrl + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!--m_rq_size && !m_wq_size)
|
||||
{
|
||||
m_ctrl &= ~SM_WAITERS_BIT;
|
||||
}
|
||||
// Check writer flags and reader limit
|
||||
return (ctrl & ~SM_READER_QUEUE) < SM_READER_MAX ? ctrl++, true : false;
|
||||
}
|
||||
|
||||
void unlock_shared_notify()
|
||||
static bool op_lock_excl(u32& ctrl)
|
||||
{
|
||||
// Mutex is locked for reliable notification because m_ctrl has been changed outside
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
if ((m_ctrl & SM_READER_MASK) == 0 && m_wq_size)
|
||||
{
|
||||
// Notify exclusive owner
|
||||
m_ocv.notify_one();
|
||||
}
|
||||
else if (m_rq_size)
|
||||
{
|
||||
// Notify other readers
|
||||
m_rcv.notify_one();
|
||||
}
|
||||
// Test and set writer lock
|
||||
return (ctrl & SM_WRITER_LOCK) == 0 ? ctrl |= SM_WRITER_LOCK, true : false;
|
||||
}
|
||||
|
||||
void lock_hard()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
|
||||
// Validate
|
||||
if ((m_ctrl & SM_INVALID_BIT) != 0) throw std::runtime_error("shared_mutex::lock(): Invalid bit");
|
||||
|
||||
// Notify non-zero writer queue size
|
||||
m_ctrl |= SM_WAITERS_BIT, m_wq_size++;
|
||||
|
||||
// Obtain the writer lock
|
||||
while (true)
|
||||
{
|
||||
const auto ctrl = m_ctrl.load();
|
||||
|
||||
if (ctrl & SM_WRITER_LOCK)
|
||||
{
|
||||
m_wcv.wait(lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_ctrl.compare_and_swap_test(ctrl, ctrl | SM_WRITER_LOCK))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for remaining readers
|
||||
while ((m_ctrl & SM_READER_MASK) != 0)
|
||||
{
|
||||
m_ocv.wait(lock);
|
||||
}
|
||||
|
||||
if (!--m_wq_size && !m_rq_size)
|
||||
{
|
||||
m_ctrl &= ~SM_WAITERS_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_notify()
|
||||
{
|
||||
// Mutex is locked for reliable notification because m_ctrl has been changed outside
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
if (m_wq_size)
|
||||
{
|
||||
// Notify next exclusive owner
|
||||
m_wcv.notify_one();
|
||||
}
|
||||
else if (m_rq_size)
|
||||
{
|
||||
// Notify all readers
|
||||
m_rcv.notify_all();
|
||||
}
|
||||
}
|
||||
void impl_lock_shared(u32 old_ctrl);
|
||||
void impl_unlock_shared(u32 new_ctrl);
|
||||
void impl_lock_excl(u32 ctrl);
|
||||
void impl_unlock_excl(u32 ctrl);
|
||||
|
||||
public:
|
||||
shared_mutex() = default;
|
||||
@@ -158,49 +51,65 @@ public:
|
||||
// Lock in shared mode
|
||||
void lock_shared()
|
||||
{
|
||||
if (m_ctrl++ >= SM_READER_MAX)
|
||||
const u32 old_ctrl = m_ctrl++;
|
||||
|
||||
// Check flags and reader limit
|
||||
if (old_ctrl >= SM_READER_MAX)
|
||||
{
|
||||
lock_shared_hard();
|
||||
impl_lock_shared(old_ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to lock in shared mode
|
||||
bool try_lock_shared()
|
||||
{
|
||||
auto ctrl = m_ctrl.load();
|
||||
|
||||
return ctrl < SM_READER_MAX && m_ctrl.compare_and_swap_test(ctrl, ctrl + 1);
|
||||
return atomic_op(m_ctrl, [](u32& ctrl)
|
||||
{
|
||||
// Check flags and reader limit
|
||||
return ctrl < SM_READER_MAX ? ctrl++, true : false;
|
||||
});
|
||||
}
|
||||
|
||||
// Unlock in shared mode
|
||||
void unlock_shared()
|
||||
{
|
||||
if (m_ctrl-- >= SM_READER_MAX)
|
||||
const u32 new_ctrl = --m_ctrl;
|
||||
|
||||
// Check if notification required
|
||||
if (new_ctrl >= SM_READER_MAX)
|
||||
{
|
||||
unlock_shared_notify();
|
||||
impl_unlock_shared(new_ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
// Lock exclusively
|
||||
void lock()
|
||||
{
|
||||
u32 value = 0;
|
||||
|
||||
if (!m_ctrl.compare_exchange_strong(value, SM_WRITER_LOCK))
|
||||
{
|
||||
impl_lock_excl(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to lock exclusively
|
||||
bool try_lock()
|
||||
{
|
||||
return m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK);
|
||||
}
|
||||
u32 value = 0;
|
||||
|
||||
// Lock exclusively
|
||||
void lock()
|
||||
{
|
||||
if (m_ctrl.compare_and_swap_test(0, SM_WRITER_LOCK)) return;
|
||||
|
||||
lock_hard();
|
||||
return m_ctrl.compare_exchange_strong(value, SM_WRITER_LOCK);
|
||||
}
|
||||
|
||||
// Unlock exclusively
|
||||
void unlock()
|
||||
{
|
||||
if (m_ctrl.fetch_sub(SM_WRITER_LOCK) != SM_WRITER_LOCK)
|
||||
const u32 value = m_ctrl.fetch_add(SM_WRITER_LOCK);
|
||||
|
||||
// Check if notification required
|
||||
if (value != SM_WRITER_LOCK)
|
||||
{
|
||||
unlock_notify();
|
||||
impl_unlock_excl(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
|
||||
#include "SleepQueue.h"
|
||||
|
||||
void sleep_queue_entry_t::add_entry()
|
||||
{
|
||||
m_queue.emplace_back(std::static_pointer_cast<CPUThread>(m_thread.shared_from_this()));
|
||||
}
|
||||
|
||||
void sleep_queue_entry_t::remove_entry()
|
||||
{
|
||||
for (auto it = m_queue.begin(); it != m_queue.end(); it++)
|
||||
{
|
||||
if (it->get() == &m_thread)
|
||||
{
|
||||
m_queue.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool sleep_queue_entry_t::find() const
|
||||
{
|
||||
for (auto it = m_queue.begin(); it != m_queue.end(); it++)
|
||||
{
|
||||
if (it->get() == &m_thread)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
sleep_queue_entry_t::sleep_queue_entry_t(sleep_entry_t& cpu, sleep_queue_t& queue)
|
||||
: m_thread(cpu)
|
||||
, m_queue(queue)
|
||||
{
|
||||
add_entry();
|
||||
cpu.sleep();
|
||||
}
|
||||
|
||||
sleep_queue_entry_t::sleep_queue_entry_t(sleep_entry_t& cpu, sleep_queue_t& queue, const defer_sleep_t&)
|
||||
: m_thread(cpu)
|
||||
, m_queue(queue)
|
||||
{
|
||||
cpu.sleep();
|
||||
}
|
||||
|
||||
sleep_queue_entry_t::~sleep_queue_entry_t()
|
||||
{
|
||||
remove_entry();
|
||||
m_thread.awake();
|
||||
}
|
||||
+23
-53
@@ -1,75 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
using sleep_entry_t = class CPUThread;
|
||||
using sleep_queue_t = std::deque<std::shared_ptr<sleep_entry_t>>;
|
||||
|
||||
// Tag used in sleep_entry<> constructor
|
||||
static struct defer_sleep_tag {} constexpr defer_sleep{};
|
||||
static struct defer_sleep_t {} const defer_sleep{};
|
||||
|
||||
// Define sleep queue as std::deque with T* pointers, T - thread type
|
||||
template<typename T> using sleep_queue = std::deque<T*>;
|
||||
|
||||
// Automatic object handling a thread pointer (T*) in the sleep queue
|
||||
// Sleep is called in the constructor (if not null)
|
||||
// Awake is called in the destructor (if not null)
|
||||
// Sleep queue is actually std::deque with pointers, be careful about the lifetime
|
||||
template<typename T, void(T::*Sleep)() = &T::sleep, void(T::*Awake)() = &T::awake>
|
||||
class sleep_entry final
|
||||
// automatic object handling a thread entry in the sleep queue
|
||||
class sleep_queue_entry_t final
|
||||
{
|
||||
sleep_queue<T>& m_queue;
|
||||
T& m_thread;
|
||||
sleep_entry_t& m_thread;
|
||||
sleep_queue_t& m_queue;
|
||||
|
||||
void add_entry();
|
||||
void remove_entry();
|
||||
bool find() const;
|
||||
|
||||
public:
|
||||
// Constructor; enter() not called
|
||||
sleep_entry(sleep_queue<T>& queue, T& entry, const defer_sleep_tag&)
|
||||
: m_queue(queue)
|
||||
, m_thread(entry)
|
||||
{
|
||||
if (Sleep) (m_thread.*Sleep)();
|
||||
}
|
||||
// add specified thread to the sleep queue
|
||||
sleep_queue_entry_t(sleep_entry_t& entry, sleep_queue_t& queue);
|
||||
|
||||
// Constructor; calls enter()
|
||||
sleep_entry(sleep_queue<T>& queue, T& entry)
|
||||
: sleep_entry(queue, entry, defer_sleep)
|
||||
{
|
||||
enter();
|
||||
}
|
||||
// don't add specified thread to the sleep queue
|
||||
sleep_queue_entry_t(sleep_entry_t& entry, sleep_queue_t& queue, const defer_sleep_t&);
|
||||
|
||||
// Destructor; calls leave()
|
||||
~sleep_entry()
|
||||
{
|
||||
leave();
|
||||
if (Awake) (m_thread.*Awake)();
|
||||
}
|
||||
// removes specified thread from the sleep queue if added
|
||||
~sleep_queue_entry_t();
|
||||
|
||||
// Add thread to the sleep queue
|
||||
// add thread to the sleep queue
|
||||
void enter()
|
||||
{
|
||||
for (auto t : m_queue)
|
||||
{
|
||||
if (t == &m_thread)
|
||||
{
|
||||
// Already exists, is it an error?
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_queue.emplace_back(&m_thread);
|
||||
add_entry();
|
||||
}
|
||||
|
||||
// Remove thread from the sleep queue
|
||||
// remove thread from the sleep queue
|
||||
void leave()
|
||||
{
|
||||
auto it = std::find(m_queue.begin(), m_queue.end(), &m_thread);
|
||||
|
||||
if (it != m_queue.end())
|
||||
{
|
||||
m_queue.erase(it);
|
||||
}
|
||||
remove_entry();
|
||||
}
|
||||
|
||||
// Check whether the thread exists in the sleep queue
|
||||
// check whether the thread exists in the sleep queue
|
||||
explicit operator bool() const
|
||||
{
|
||||
return std::find(m_queue.begin(), m_queue.end(), &m_thread) != m_queue.end();
|
||||
return find();
|
||||
}
|
||||
};
|
||||
|
||||
+105
-54
@@ -1,5 +1,9 @@
|
||||
#include "StrFmt.h"
|
||||
#include "BEType.h"
|
||||
#include "stdafx.h"
|
||||
#pragma warning(push)
|
||||
#pragma message("TODO: remove wx dependency: <wx/string.h>")
|
||||
#pragma warning(disable : 4996)
|
||||
#include <wx/string.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
std::string v128::to_hex() const
|
||||
{
|
||||
@@ -15,7 +19,7 @@ std::string fmt::to_hex(u64 value, u64 count)
|
||||
{
|
||||
if (count - 1 >= 16)
|
||||
{
|
||||
throw exception("fmt::to_hex(): invalid count: 0x%llx", count);
|
||||
throw EXCEPTION("Invalid count: 0x%llx", count);
|
||||
}
|
||||
|
||||
count = std::max<u64>(count, 16 - cntlz64(value) / 4);
|
||||
@@ -74,6 +78,8 @@ std::string fmt::to_sdec(s64 svalue)
|
||||
return std::string(&res[first], sizeof(res) - first);
|
||||
}
|
||||
|
||||
//extern const std::string fmt::placeholder = "???";
|
||||
|
||||
std::string fmt::replace_first(const std::string& src, const std::string& from, const std::string& to)
|
||||
{
|
||||
auto pos = src.find(from);
|
||||
@@ -98,6 +104,83 @@ std::string fmt::replace_all(const std::string &src, const std::string& from, co
|
||||
return target;
|
||||
}
|
||||
|
||||
//TODO: move this wx Stuff somewhere else
|
||||
//convert a wxString to a std::string encoded in utf8
|
||||
//CAUTION, only use this to interface with wxWidgets classes
|
||||
std::string fmt::ToUTF8(const wxString& right)
|
||||
{
|
||||
auto ret = std::string(((const char *)right.utf8_str()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
//convert a std::string encoded in utf8 to a wxString
|
||||
//CAUTION, only use this to interface with wxWidgets classes
|
||||
wxString fmt::FromUTF8(const std::string& right)
|
||||
{
|
||||
auto ret = wxString::FromUTF8(right.c_str());
|
||||
return ret;
|
||||
}
|
||||
|
||||
//TODO: remove this after every snippet that uses it is gone
|
||||
//WARNING: not fully compatible with CmpNoCase from wxString
|
||||
int fmt::CmpNoCase(const std::string& a, const std::string& b)
|
||||
{
|
||||
if (a.length() != b.length())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::equal(a.begin(),
|
||||
a.end(),
|
||||
b.begin(),
|
||||
[](const char& a, const char& b){return ::tolower(a) == ::tolower(b); })
|
||||
? 0 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: remove this after every snippet that uses it is gone
|
||||
//WARNING: not fully compatible with CmpNoCase from wxString
|
||||
void fmt::Replace(std::string &str, const std::string &searchterm, const std::string& replaceterm)
|
||||
{
|
||||
size_t cursor = 0;
|
||||
do
|
||||
{
|
||||
cursor = str.find(searchterm, cursor);
|
||||
if (cursor != std::string::npos)
|
||||
{
|
||||
str.replace(cursor, searchterm.size(), replaceterm);
|
||||
cursor += replaceterm.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
std::vector<std::string> fmt::rSplit(const std::string& source, const std::string& delim)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
size_t cursor = 0;
|
||||
do
|
||||
{
|
||||
size_t prevcurs = cursor;
|
||||
cursor = source.find(delim, cursor);
|
||||
if (cursor != std::string::npos)
|
||||
{
|
||||
ret.push_back(source.substr(prevcurs,cursor-prevcurs));
|
||||
cursor += delim.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.push_back(source.substr(prevcurs));
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string> fmt::split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
@@ -139,7 +222,21 @@ std::string fmt::trim(const std::string& source, const std::string& values)
|
||||
return source.substr(begin, source.find_last_not_of(values) + 1);
|
||||
}
|
||||
|
||||
std::string fmt::escape(const std::string& source, std::initializer_list<char> more)
|
||||
std::string fmt::tolower(std::string source)
|
||||
{
|
||||
std::transform(source.begin(), source.end(), source.begin(), ::tolower);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
std::string fmt::toupper(std::string source)
|
||||
{
|
||||
std::transform(source.begin(), source.end(), source.begin(), ::toupper);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
std::string fmt::escape(std::string source)
|
||||
{
|
||||
const std::pair<std::string, std::string> escape_list[] =
|
||||
{
|
||||
@@ -147,66 +244,20 @@ std::string fmt::escape(const std::string& source, std::initializer_list<char> m
|
||||
{ "\a", "\\a" },
|
||||
{ "\b", "\\b" },
|
||||
{ "\f", "\\f" },
|
||||
{ "\n", "\\n" },
|
||||
{ "\n", "\\n\n" },
|
||||
{ "\r", "\\r" },
|
||||
{ "\t", "\\t" },
|
||||
{ "\v", "\\v" },
|
||||
};
|
||||
|
||||
std::string result = fmt::replace_all(source, escape_list);
|
||||
source = fmt::replace_all(source, escape_list);
|
||||
|
||||
for (char c = 0; c < 32; c++)
|
||||
{
|
||||
result = fmt::replace_all(result, std::string(1, c), fmt::format("\\x%02X", c));
|
||||
if (c != '\n') source = fmt::replace_all(source, std::string(1, c), fmt::format("\\x%02X", c));
|
||||
}
|
||||
|
||||
for (char c : more)
|
||||
{
|
||||
result = fmt::replace_all(result, std::string(1, c), fmt::format("\\x%02X", c));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string fmt::unescape(const std::string& source)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
for (auto it = source.begin(); it != source.end();)
|
||||
{
|
||||
const char bs = *it++;
|
||||
|
||||
if (bs == '\\' && it != source.end())
|
||||
{
|
||||
switch (const char code = *it++)
|
||||
{
|
||||
case 'a': result += '\a'; break;
|
||||
case 'b': result += '\b'; break;
|
||||
case 'f': result += '\f'; break;
|
||||
case 'n': result += '\n'; break;
|
||||
case 'r': result += '\r'; break;
|
||||
case 't': result += '\t'; break;
|
||||
case 'v': result += '\v'; break;
|
||||
case 'x':
|
||||
{
|
||||
// Detect hexadecimal character code (TODO)
|
||||
if (source.end() - it >= 2)
|
||||
{
|
||||
result += std::stoi(std::string{ *it++, *it++ }, 0, 16);
|
||||
}
|
||||
|
||||
}
|
||||
// Octal/unicode not supported
|
||||
default: result += code;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result += bs;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return source;
|
||||
}
|
||||
|
||||
bool fmt::match(const std::string &source, const std::string &mask)
|
||||
|
||||
+172
-107
@@ -1,38 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "Platform.h"
|
||||
#include "types.h"
|
||||
class wxString;
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
// Copy null-terminated string from std::string to char array with truncation
|
||||
template<std::size_t N>
|
||||
inline void strcpy_trunc(char(&dst)[N], const std::string& src)
|
||||
{
|
||||
const std::size_t count = src.size() >= N ? N - 1 : src.size();
|
||||
std::memcpy(dst, src.c_str(), count);
|
||||
dst[count] = '\0';
|
||||
}
|
||||
|
||||
// Copy null-terminated string from char array to another char array with truncation
|
||||
template<std::size_t N, std::size_t N2>
|
||||
inline void strcpy_trunc(char(&dst)[N], const char(&src)[N2])
|
||||
{
|
||||
const std::size_t count = N2 >= N ? N - 1 : N2;
|
||||
std::memcpy(dst, src, count);
|
||||
dst[count] = '\0';
|
||||
}
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
//struct empty_t{};
|
||||
|
||||
//extern const std::string placeholder;
|
||||
|
||||
template <typename T>
|
||||
std::string AfterLast(const std::string& source, T searchstr)
|
||||
{
|
||||
size_t search_pos = source.rfind(searchstr);
|
||||
search_pos = search_pos == std::string::npos ? 0 : search_pos;
|
||||
return source.substr(search_pos);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string BeforeLast(const std::string& source, T searchstr)
|
||||
{
|
||||
size_t search_pos = source.rfind(searchstr);
|
||||
search_pos = search_pos == std::string::npos ? 0 : search_pos;
|
||||
return source.substr(0, search_pos);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string AfterFirst(const std::string& source, T searchstr)
|
||||
{
|
||||
size_t search_pos = source.find(searchstr);
|
||||
search_pos = search_pos == std::string::npos ? 0 : search_pos;
|
||||
return source.substr(search_pos);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::string BeforeFirst(const std::string& source, T searchstr)
|
||||
{
|
||||
size_t search_pos = source.find(searchstr);
|
||||
search_pos = search_pos == std::string::npos ? 0 : search_pos;
|
||||
return source.substr(0, search_pos);
|
||||
}
|
||||
|
||||
// write `fmt` from `pos` to the first occurence of `fmt::placeholder` to
|
||||
// the stream `os`. Then write `arg` to to the stream. If there's no
|
||||
// `fmt::placeholder` after `pos` everything in `fmt` after pos is written
|
||||
// to `os`. Then `arg` is written to `os` after appending a space character
|
||||
//template<typename T>
|
||||
//empty_t write(const std::string &fmt, std::ostream &os, std::string::size_type &pos, T &&arg)
|
||||
//{
|
||||
// std::string::size_type ins = fmt.find(placeholder, pos);
|
||||
|
||||
// if (ins == std::string::npos)
|
||||
// {
|
||||
// os.write(fmt.data() + pos, fmt.size() - pos);
|
||||
// os << ' ' << arg;
|
||||
|
||||
// pos = fmt.size();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// os.write(fmt.data() + pos, ins - pos);
|
||||
// os << arg;
|
||||
|
||||
// pos = ins + placeholder.size();
|
||||
// }
|
||||
// return{};
|
||||
//}
|
||||
|
||||
// typesafe version of a sprintf-like function. Returns the printed to
|
||||
// string. To mark positions where the arguments are supposed to be
|
||||
// inserted use `fmt::placeholder`. If there's not enough placeholders
|
||||
// the rest of the arguments are appended at the end, seperated by spaces
|
||||
//template<typename ... Args>
|
||||
//std::string SFormat(const std::string &fmt, Args&& ... parameters)
|
||||
//{
|
||||
// std::ostringstream os;
|
||||
// std::string::size_type pos = 0;
|
||||
// std::initializer_list<empty_t> { write(fmt, os, pos, parameters)... };
|
||||
|
||||
// if (!fmt.empty())
|
||||
// {
|
||||
// os.write(fmt.data() + pos, fmt.size() - pos);
|
||||
// }
|
||||
|
||||
// std::string result = os.str();
|
||||
// return result;
|
||||
//}
|
||||
|
||||
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
|
||||
std::string replace_all(const std::string &src, const std::string& from, const std::string& to);
|
||||
|
||||
@@ -88,8 +145,7 @@ namespace fmt
|
||||
std::string to_udec(u64 value);
|
||||
std::string to_sdec(s64 value);
|
||||
|
||||
template<typename T, typename>
|
||||
struct unveil
|
||||
template<typename T, bool is_enum = std::is_enum<T>::value> struct unveil
|
||||
{
|
||||
using result_type = T;
|
||||
|
||||
@@ -99,41 +155,37 @@ namespace fmt
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct unveil<const char*, void>
|
||||
{
|
||||
using result_type = const char* const;
|
||||
|
||||
static result_type get_value(const char* const& arg)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t N>
|
||||
struct unveil<char[N], void>
|
||||
{
|
||||
using result_type = const char* const;
|
||||
|
||||
static result_type get_value(const char(&arg)[N])
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct unveil<std::string, void>
|
||||
template<> struct unveil<char*, false>
|
||||
{
|
||||
using result_type = const char*;
|
||||
|
||||
static result_type get_value(const std::string& arg)
|
||||
force_inline static result_type get_value(const char* arg)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
|
||||
template<std::size_t N> struct unveil<const char[N], false>
|
||||
{
|
||||
using result_type = const char*;
|
||||
|
||||
force_inline static result_type get_value(const char(&arg)[N])
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct unveil<std::string, false>
|
||||
{
|
||||
using result_type = const char*;
|
||||
|
||||
force_inline static result_type get_value(const std::string& arg)
|
||||
{
|
||||
return arg.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct unveil<T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
template<typename T> struct unveil<T, true>
|
||||
{
|
||||
using result_type = std::underlying_type_t<T>;
|
||||
|
||||
@@ -143,15 +195,32 @@ namespace fmt
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, bool Se> struct unveil<se_t<T, Se>, false>
|
||||
{
|
||||
using result_type = typename unveil<T>::result_type;
|
||||
|
||||
force_inline static result_type get_value(const se_t<T, Se>& arg)
|
||||
{
|
||||
return unveil<T>::get_value(arg);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
force_inline typename unveil<T>::result_type do_unveil(const T& arg)
|
||||
{
|
||||
return unveil<T>::get_value(arg);
|
||||
}
|
||||
|
||||
// Formatting function with special functionality (fmt::unveil)
|
||||
template<typename... Args>
|
||||
safe_buffers std::string format(const char* fmt, const Args&... args)
|
||||
// Formatting function with special functionality:
|
||||
//
|
||||
// std::string is forced to .c_str()
|
||||
// be_t<> is forced to .value() (fmt::do_unveil reverts byte order automatically)
|
||||
//
|
||||
// External specializations for fmt::do_unveil (can be found in another headers):
|
||||
// vm::ptr, vm::bptr, ... (fmt::do_unveil) (vm_ptr.h) (with appropriate address type, using .addr() can be avoided)
|
||||
// vm::ref, vm::bref, ... (fmt::do_unveil) (vm_ref.h)
|
||||
//
|
||||
template<typename... Args> safe_buffers std::string format(const char* fmt, Args... args)
|
||||
{
|
||||
// fixed stack buffer for the first attempt
|
||||
std::array<char, 4096> fixed_buf;
|
||||
@@ -186,28 +255,51 @@ namespace fmt
|
||||
}
|
||||
}
|
||||
|
||||
// Create exception of type T (std::runtime_error by default) with formatting
|
||||
template<typename T = std::runtime_error, typename... Args>
|
||||
never_inline safe_buffers T exception(const char* fmt, const Args&... args) noexcept(noexcept(T{ fmt }))
|
||||
struct exception : public std::exception
|
||||
{
|
||||
return T{ format(fmt, do_unveil(args)...).c_str() };
|
||||
}
|
||||
std::unique_ptr<char[]> message;
|
||||
|
||||
// Create exception of type T (std::runtime_error by default) without formatting
|
||||
template<typename T = std::runtime_error>
|
||||
safe_buffers T exception(const char* msg) noexcept(noexcept(T{ msg }))
|
||||
{
|
||||
return T{ msg };
|
||||
}
|
||||
template<typename... Args> never_inline safe_buffers exception(const char* file, int line, const char* func, const char* text, Args... args) noexcept
|
||||
{
|
||||
const std::string data = format(text, args...) + format("\n(in file %s:%d, in function %s)", file, line, func);
|
||||
|
||||
// Narrow cast (similar to gsl::narrow) with exception message formatting
|
||||
template<typename To, typename From, typename... Args>
|
||||
inline auto narrow(const char* format_str, const From& value, const Args&... args) -> decltype(static_cast<To>(static_cast<From>(std::declval<To>())))
|
||||
{
|
||||
const auto result = static_cast<To>(value);
|
||||
if (static_cast<From>(result) != value) throw fmt::exception(format_str, fmt::do_unveil(value), fmt::do_unveil(args)...);
|
||||
return result;
|
||||
}
|
||||
message.reset(new char[data.size() + 1]);
|
||||
|
||||
std::memcpy(message.get(), data.c_str(), data.size() + 1);
|
||||
}
|
||||
|
||||
exception(const exception& other) noexcept
|
||||
{
|
||||
const std::size_t size = std::strlen(other.message.get());
|
||||
|
||||
message.reset(new char[size + 1]);
|
||||
|
||||
std::memcpy(message.get(), other.message.get(), size + 1);
|
||||
}
|
||||
|
||||
virtual const char* what() const noexcept override
|
||||
{
|
||||
return message.get();
|
||||
}
|
||||
};
|
||||
|
||||
//convert a wxString to a std::string encoded in utf8
|
||||
//CAUTION, only use this to interface with wxWidgets classes
|
||||
std::string ToUTF8(const wxString& right);
|
||||
|
||||
//convert a std::string encoded in utf8 to a wxString
|
||||
//CAUTION, only use this to interface with wxWidgets classes
|
||||
wxString FromUTF8(const std::string& right);
|
||||
|
||||
//TODO: remove this after every snippet that uses it is gone
|
||||
//WARNING: not fully compatible with CmpNoCase from wxString
|
||||
int CmpNoCase(const std::string& a, const std::string& b);
|
||||
|
||||
//TODO: remove this after every snippet that uses it is gone
|
||||
//WARNING: not fully compatible with Replace from wxString
|
||||
void Replace(std::string &str, const std::string &searchterm, const std::string& replaceterm);
|
||||
|
||||
std::vector<std::string> rSplit(const std::string& source, const std::string& delim);
|
||||
|
||||
std::vector<std::string> split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty = true);
|
||||
std::string trim(const std::string& source, const std::string& values = " \t");
|
||||
@@ -259,35 +351,8 @@ namespace fmt
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename IT>
|
||||
std::string to_lower(IT _begin, IT _end)
|
||||
{
|
||||
std::string result; result.resize(_end - _begin);
|
||||
std::transform(_begin, _end, result.begin(), ::tolower);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string to_lower(const T& string)
|
||||
{
|
||||
return to_lower(std::begin(string), std::end(string));
|
||||
}
|
||||
|
||||
template<typename IT>
|
||||
std::string to_upper(IT _begin, IT _end)
|
||||
{
|
||||
std::string result; result.resize(_end - _begin);
|
||||
std::transform(_begin, _end, result.begin(), ::toupper);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string to_upper(const T& string)
|
||||
{
|
||||
return to_upper(std::begin(string), std::end(string));
|
||||
}
|
||||
|
||||
std::string escape(const std::string& source, std::initializer_list<char> more = {});
|
||||
std::string unescape(const std::string& source);
|
||||
std::string tolower(std::string source);
|
||||
std::string toupper(std::string source);
|
||||
std::string escape(std::string source);
|
||||
bool match(const std::string &source, const std::string &mask);
|
||||
}
|
||||
|
||||
+142
-131
@@ -1,8 +1,11 @@
|
||||
#include "stdafx.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Log.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/state.h"
|
||||
#include "Emu/CPU/CPUThreadManager.h"
|
||||
#include "Emu/CPU/CPUThread.h"
|
||||
#include "Emu/Cell/RawSPUThread.h"
|
||||
#include "Emu/SysCalls/SysCalls.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -16,21 +19,13 @@
|
||||
#include <ucontext.h>
|
||||
#endif
|
||||
|
||||
static void report_fatal_error(const std::string& msg)
|
||||
void report_fatal_error(const std::string& msg)
|
||||
{
|
||||
std::string _msg = msg + "\n"
|
||||
"HOW TO REPORT ERRORS:\n"
|
||||
"1) Check the FAQ, readme, other sources. Please ensure that your hardware and software configuration is compliant.\n"
|
||||
"2) You must provide FULL information: how to reproduce the error (your actions), RPCS3.log file, other *.log files whenever requested.\n"
|
||||
"3) Please ensure that your software (game) is 'Playable' or close. Please note that 'Non-playable' games will be ignored.\n"
|
||||
"4) If the software (game) is not 'Playable', please ensure that this error is unexpected, i.e. it didn't happen before or similar.\n"
|
||||
"Please, don't send incorrect reports. Thanks for understanding.\n";
|
||||
|
||||
#ifdef _WIN32
|
||||
_msg += "Press (Ctrl+C) to copy this message.";
|
||||
MessageBoxA(0, _msg.c_str(), "Fatal error", MB_ICONERROR); // TODO: unicode message
|
||||
const auto& text = msg + "\n\nPlease report this error to the developers. Press (Ctrl+C) to copy this message.";
|
||||
MessageBoxA(0, text.c_str(), "Fatal error", MB_ICONERROR); // TODO: unicode message
|
||||
#else
|
||||
std::printf("Fatal error: \n%s", _msg.c_str());
|
||||
std::printf("Fatal error: %s\nPlease report this error to the developers.\n", msg.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -40,9 +35,9 @@ static void report_fatal_error(const std::string& msg)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
report_fatal_error("Unhandled exception of type '"s + typeid(e).name() + "': "s + e.what());
|
||||
report_fatal_error("Unhandled exception: "s + ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -781,7 +776,8 @@ size_t get_x64_access_size(x64_context* context, x64_op_t op, x64_reg_t reg, siz
|
||||
|
||||
if (op == X64OP_CMPXCHG)
|
||||
{
|
||||
// Detect whether the instruction can't actually modify memory to avoid breaking reservation
|
||||
// detect whether this instruction can't actually modify memory to avoid breaking reservation;
|
||||
// this may theoretically cause endless loop, but it shouldn't be a problem if only load_sync() generates such instruction
|
||||
u64 cmp, exch;
|
||||
if (!get_x64_reg_value(context, reg, d_size, i_size, cmp) || !get_x64_reg_value(context, X64R_RAX, d_size, i_size, exch))
|
||||
{
|
||||
@@ -798,18 +794,16 @@ size_t get_x64_access_size(x64_context* context, x64_op_t op, x64_reg_t reg, siz
|
||||
return d_size;
|
||||
}
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
extern std::function<bool(u32 addr, bool is_writing)> g_access_violation_handler;
|
||||
}
|
||||
/**
|
||||
* Callback that can be customised by GSRender backends to track memory access.
|
||||
* Backends can protect memory pages and get this callback called when an access
|
||||
* violation is met.
|
||||
* Should return true if the backend handles the access violation.
|
||||
*/
|
||||
std::function<bool(u32 addr)> gfxHandler = [](u32) { return false; };
|
||||
|
||||
bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
||||
{
|
||||
if (rsx::g_access_violation_handler && rsx::g_access_violation_handler(addr, is_writing))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto code = (const u8*)RIP(context);
|
||||
|
||||
x64_op_t op;
|
||||
@@ -817,6 +811,9 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
||||
size_t d_size;
|
||||
size_t i_size;
|
||||
|
||||
if (gfxHandler(addr))
|
||||
return true;
|
||||
|
||||
// decode single x64 instruction that causes memory access
|
||||
decode_x64_reg_op(code, op, reg, d_size, i_size);
|
||||
|
||||
@@ -848,7 +845,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
||||
// check if address is RawSPU MMIO register
|
||||
if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET)
|
||||
{
|
||||
auto thread = idm::get<RawSPUThread>((addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET);
|
||||
auto thread = Emu.GetCPU().GetRawSPUThread((addr - RAW_SPU_BASE_ADDR) / RAW_SPU_OFFSET);
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
@@ -1056,10 +1053,10 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
||||
|
||||
switch (d_size)
|
||||
{
|
||||
case 1: reg_value = ((atomic_t<u8>*)vm::base_priv(addr))->exchange((u8)reg_value); break;
|
||||
case 2: reg_value = ((atomic_t<u16>*)vm::base_priv(addr))->exchange((u16)reg_value); break;
|
||||
case 4: reg_value = ((atomic_t<u32>*)vm::base_priv(addr))->exchange((u32)reg_value); break;
|
||||
case 8: reg_value = ((atomic_t<u64>*)vm::base_priv(addr))->exchange((u64)reg_value); break;
|
||||
case 1: reg_value = sync_lock_test_and_set((u8*)vm::base_priv(addr), (u8)reg_value); break;
|
||||
case 2: reg_value = sync_lock_test_and_set((u16*)vm::base_priv(addr), (u16)reg_value); break;
|
||||
case 4: reg_value = sync_lock_test_and_set((u32*)vm::base_priv(addr), (u32)reg_value); break;
|
||||
case 8: reg_value = sync_lock_test_and_set((u64*)vm::base_priv(addr), (u64)reg_value); break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
@@ -1079,10 +1076,10 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
||||
|
||||
switch (d_size)
|
||||
{
|
||||
case 1: old_value = ((atomic_t<u8>*)vm::base_priv(addr))->compare_and_swap((u8)cmp_value, (u8)reg_value); break;
|
||||
case 2: old_value = ((atomic_t<u16>*)vm::base_priv(addr))->compare_and_swap((u16)cmp_value, (u16)reg_value); break;
|
||||
case 4: old_value = ((atomic_t<u32>*)vm::base_priv(addr))->compare_and_swap((u32)cmp_value, (u32)reg_value); break;
|
||||
case 8: old_value = ((atomic_t<u64>*)vm::base_priv(addr))->compare_and_swap((u64)cmp_value, (u64)reg_value); break;
|
||||
case 1: old_value = sync_val_compare_and_swap((u8*)vm::base_priv(addr), (u8)cmp_value, (u8)reg_value); break;
|
||||
case 2: old_value = sync_val_compare_and_swap((u16*)vm::base_priv(addr), (u16)cmp_value, (u16)reg_value); break;
|
||||
case 4: old_value = sync_val_compare_and_swap((u32*)vm::base_priv(addr), (u32)cmp_value, (u32)reg_value); break;
|
||||
case 8: old_value = sync_val_compare_and_swap((u64*)vm::base_priv(addr), (u64)cmp_value, (u64)reg_value); break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
@@ -1102,10 +1099,10 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
||||
|
||||
switch (d_size)
|
||||
{
|
||||
case 1: value = *(atomic_t<u8>*)vm::base_priv(addr) &= (u8)value; break;
|
||||
case 2: value = *(atomic_t<u16>*)vm::base_priv(addr) &= (u16)value; break;
|
||||
case 4: value = *(atomic_t<u32>*)vm::base_priv(addr) &= (u32)value; break;
|
||||
case 8: value = *(atomic_t<u64>*)vm::base_priv(addr) &= (u64)value; break;
|
||||
case 1: value &= sync_fetch_and_and((u8*)vm::base_priv(addr), (u8)value); break;
|
||||
case 2: value &= sync_fetch_and_and((u16*)vm::base_priv(addr), (u16)value); break;
|
||||
case 4: value &= sync_fetch_and_and((u32*)vm::base_priv(addr), (u32)value); break;
|
||||
case 8: value &= sync_fetch_and_and((u64*)vm::base_priv(addr), (u64)value); break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
@@ -1131,14 +1128,14 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
||||
// TODO: allow recovering from a page fault as a feature of PS3 virtual memory
|
||||
}
|
||||
|
||||
[[noreturn]] static void throw_access_violation(const char* cause, u64 addr)
|
||||
// Throw virtual memory access violation exception
|
||||
[[noreturn]] void throw_access_violation(const char* cause, u32 address) // Don't change function definition
|
||||
{
|
||||
vm::throw_access_violation(addr, cause);
|
||||
std::abort();
|
||||
throw EXCEPTION("Access violation %s location 0x%08x", cause, address);
|
||||
}
|
||||
|
||||
// Modify context in order to convert hardware exception to C++ exception
|
||||
static void prepare_throw_access_violation(x64_context* context, const char* cause, u32 address)
|
||||
void prepare_throw_access_violation(x64_context* context, const char* cause, u32 address)
|
||||
{
|
||||
// Set throw_access_violation() call args (old register values are lost)
|
||||
ARG1(context) = (u64)cause;
|
||||
@@ -1151,25 +1148,22 @@ static void prepare_throw_access_violation(x64_context* context, const char* cau
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static LONG exception_handler(PEXCEPTION_POINTERS pExp)
|
||||
const auto g_exception_handler = AddVectoredExceptionHandler(1, [](PEXCEPTION_POINTERS pExp) -> LONG
|
||||
{
|
||||
const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
|
||||
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
|
||||
|
||||
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && addr64 < 0x100000000ull)
|
||||
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && addr64 < 0x100000000ull && thread_ctrl::get_current() && handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
|
||||
{
|
||||
vm::g_tls_fault_count++;
|
||||
|
||||
if (thread_ctrl::get_current() && handle_access_violation((u32)addr64, is_writing, pExp->ContextRecord))
|
||||
{
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
});
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
static LONG exception_filter(PEXCEPTION_POINTERS pExp)
|
||||
const auto g_exception_filter = SetUnhandledExceptionFilter([](PEXCEPTION_POINTERS pExp) -> LONG
|
||||
{
|
||||
std::string msg = fmt::format("Unhandled Win32 exception 0x%08X.\n", pExp->ExceptionRecord->ExceptionCode);
|
||||
|
||||
@@ -1178,9 +1172,8 @@ static LONG exception_filter(PEXCEPTION_POINTERS pExp)
|
||||
const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
|
||||
const auto cause = pExp->ExceptionRecord->ExceptionInformation[0] != 0 ? "writing" : "reading";
|
||||
|
||||
if (!(vm::g_tls_fault_count & (1ull << 63)) && addr64 < 0x100000000ull)
|
||||
if (addr64 < 0x100000000ull)
|
||||
{
|
||||
vm::g_tls_fault_count |= (1ull << 63);
|
||||
// Setup throw_access_violation() call on the context
|
||||
prepare_throw_access_violation(pExp->ContextRecord, cause, (u32)addr64);
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
@@ -1198,43 +1191,16 @@ static LONG exception_filter(PEXCEPTION_POINTERS pExp)
|
||||
}
|
||||
}
|
||||
|
||||
msg += fmt::format("Instruction address: %p.\n", pExp->ContextRecord->Rip);
|
||||
msg += fmt::format("Image base: %p.\n", GetModuleHandle(NULL));
|
||||
|
||||
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
|
||||
{
|
||||
msg += "\n"
|
||||
"Illegal instruction exception occured.\n"
|
||||
"Note that your CPU must support SSSE3 extension.\n";
|
||||
}
|
||||
|
||||
// TODO: print registers and the callstack
|
||||
msg += fmt::format("Image base: %p.", GetModuleHandle(NULL));
|
||||
|
||||
// Report fatal error
|
||||
report_fatal_error(msg);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
const bool s_exception_handler_set = []() -> bool
|
||||
{
|
||||
if (!AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)exception_handler))
|
||||
{
|
||||
report_fatal_error("AddVectoredExceptionHandler() failed.");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (!SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)exception_filter))
|
||||
{
|
||||
report_fatal_error("SetUnhandledExceptionFilter() failed.");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return true;
|
||||
}();
|
||||
});
|
||||
|
||||
#else
|
||||
|
||||
static void signal_handler(int sig, siginfo_t* info, void* uct)
|
||||
void signal_handler(int sig, siginfo_t* info, void* uct)
|
||||
{
|
||||
x64_context* context = (ucontext_t*)uct;
|
||||
|
||||
@@ -1247,12 +1213,10 @@ static void signal_handler(int sig, siginfo_t* info, void* uct)
|
||||
const u64 addr64 = (u64)info->si_addr - (u64)vm::base(0);
|
||||
const auto cause = is_writing ? "writing" : "reading";
|
||||
|
||||
if (addr64 < 0x100000000ull)
|
||||
if (addr64 < 0x100000000ull && thread_ctrl::get_current())
|
||||
{
|
||||
vm::g_tls_fault_count++;
|
||||
|
||||
// Try to process access violation
|
||||
if (!thread_ctrl::get_current() || !handle_access_violation((u32)addr64, is_writing, context))
|
||||
if (!handle_access_violation((u32)addr64, is_writing, context))
|
||||
{
|
||||
// Setup throw_access_violation() call on the context
|
||||
prepare_throw_access_violation(context, cause, (u32)addr64);
|
||||
@@ -1266,51 +1230,41 @@ static void signal_handler(int sig, siginfo_t* info, void* uct)
|
||||
}
|
||||
}
|
||||
|
||||
const bool s_exception_handler_set = []() -> bool
|
||||
int setup_signal_handler()
|
||||
{
|
||||
struct ::sigaction sa;
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_sigaction = signal_handler;
|
||||
return sigaction(SIGSEGV, &sa, NULL);
|
||||
}
|
||||
|
||||
if (::sigaction(SIGSEGV, &sa, NULL) == -1)
|
||||
{
|
||||
std::printf("sigaction() failed (0x%x).", errno);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return true;
|
||||
}();
|
||||
const int g_sigaction_result = setup_signal_handler();
|
||||
|
||||
#endif
|
||||
|
||||
const bool s_self_test = []() -> bool
|
||||
{
|
||||
// Find ret instruction
|
||||
if ((*(u8*)throw_access_violation & 0xF6) == 0xC2)
|
||||
{
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return true;
|
||||
}();
|
||||
|
||||
thread_local DECLARE(thread_ctrl::g_tls_this_thread) = nullptr;
|
||||
thread_local thread_ctrl* thread_ctrl::g_tls_this_thread = nullptr;
|
||||
|
||||
// TODO
|
||||
atomic_t<u32> g_thread_count{ 0 };
|
||||
std::atomic<u32> g_thread_count{ 0 };
|
||||
|
||||
void thread_ctrl::initialize()
|
||||
{
|
||||
SetCurrentThreadDebugName(g_tls_this_thread->m_name.c_str());
|
||||
SetCurrentThreadDebugName(g_tls_this_thread->m_name().c_str());
|
||||
|
||||
_log::g_tls_make_prefix = [](const auto&, auto, const auto&)
|
||||
#ifdef _WIN32
|
||||
if (!g_exception_handler || !g_exception_filter)
|
||||
#else
|
||||
if (g_sigaction_result == -1)
|
||||
#endif
|
||||
{
|
||||
return g_tls_this_thread->m_name;
|
||||
};
|
||||
report_fatal_error("Exception handler is not set correctly.");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
// TODO
|
||||
++g_thread_count;
|
||||
g_thread_count++;
|
||||
}
|
||||
|
||||
void thread_ctrl::finalize() noexcept
|
||||
@@ -1319,36 +1273,85 @@ void thread_ctrl::finalize() noexcept
|
||||
vm::reservation_free();
|
||||
|
||||
// TODO
|
||||
--g_thread_count;
|
||||
g_thread_count--;
|
||||
|
||||
// Call atexit functions
|
||||
g_tls_this_thread->m_atexit.exec();
|
||||
for (const auto& func : decltype(m_atexit)(std::move(g_tls_this_thread->m_atexit)))
|
||||
{
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
std::string named_thread::get_name() const
|
||||
thread_ctrl::~thread_ctrl()
|
||||
{
|
||||
m_thread.detach();
|
||||
|
||||
if (m_future.valid())
|
||||
{
|
||||
try
|
||||
{
|
||||
m_future.get();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
catch_all_exceptions();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string thread_ctrl::get_name() const
|
||||
{
|
||||
CHECK_ASSERTION(m_name);
|
||||
|
||||
return m_name();
|
||||
}
|
||||
|
||||
std::string named_thread_t::get_name() const
|
||||
{
|
||||
return fmt::format("('%s') Unnamed Thread", typeid(*this).name());
|
||||
}
|
||||
|
||||
void named_thread::start()
|
||||
void named_thread_t::start()
|
||||
{
|
||||
Expects(!m_thread);
|
||||
CHECK_ASSERTION(!m_thread);
|
||||
|
||||
// Get shared_ptr instance (will throw if called from the constructor or the object has been created incorrectly)
|
||||
auto&& ptr = shared_from_this();
|
||||
auto ptr = shared_from_this();
|
||||
|
||||
// Make name getter
|
||||
auto name = [wptr = std::weak_ptr<named_thread_t>(ptr), type = &typeid(*this)]()
|
||||
{
|
||||
// Return actual name if available
|
||||
if (const auto ptr = wptr.lock())
|
||||
{
|
||||
return ptr->get_name();
|
||||
}
|
||||
else
|
||||
{
|
||||
return fmt::format("('%s') Deleted Thread", type->name());
|
||||
}
|
||||
};
|
||||
|
||||
// Run thread
|
||||
m_thread = thread_ctrl::spawn(get_name(), [thread = std::move(ptr)]()
|
||||
m_thread = thread_ctrl::spawn(std::move(name), [thread = std::move(ptr)]()
|
||||
{
|
||||
try
|
||||
{
|
||||
LOG_TRACE(GENERAL, "Thread started");
|
||||
if (rpcs3::config.misc.log.hle_logging.value())
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Thread started");
|
||||
}
|
||||
|
||||
thread->on_task();
|
||||
LOG_TRACE(GENERAL, "Thread ended");
|
||||
|
||||
if (rpcs3::config.misc.log.hle_logging.value())
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Thread ended");
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_FATAL(GENERAL, "%s thrown: %s", typeid(e).name(), e.what());
|
||||
LOG_ERROR(GENERAL, "Exception: %s\nPlease report this to the developers.", e.what());
|
||||
Emu.Pause();
|
||||
}
|
||||
catch (EmulationStopped)
|
||||
@@ -1360,9 +1363,9 @@ void named_thread::start()
|
||||
});
|
||||
}
|
||||
|
||||
void named_thread::join()
|
||||
void named_thread_t::join()
|
||||
{
|
||||
Expects(m_thread);
|
||||
CHECK_ASSERTION(m_thread != nullptr);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -1375,3 +1378,11 @@ void named_thread::join()
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
const std::function<bool()> SQUEUE_ALWAYS_EXIT = [](){ return true; };
|
||||
const std::function<bool()> SQUEUE_NEVER_EXIT = [](){ return false; };
|
||||
|
||||
bool squeue_test_exit()
|
||||
{
|
||||
return Emu.IsStopped();
|
||||
}
|
||||
|
||||
+388
-150
@@ -1,96 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
// Will report exception and call std::abort() if put in catch(...)
|
||||
[[noreturn]] void catch_all_exceptions();
|
||||
|
||||
// Simple list of void() functors
|
||||
class task_stack
|
||||
{
|
||||
struct task_base
|
||||
{
|
||||
std::unique_ptr<task_base> next;
|
||||
|
||||
virtual ~task_base() = default;
|
||||
|
||||
virtual void exec()
|
||||
{
|
||||
if (next)
|
||||
{
|
||||
next->exec();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<task_base> m_stack;
|
||||
|
||||
never_inline void push(std::unique_ptr<task_base> task)
|
||||
{
|
||||
m_stack.swap(task->next);
|
||||
m_stack.swap(task);
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename F>
|
||||
void push(F&& func)
|
||||
{
|
||||
struct task_t : task_base
|
||||
{
|
||||
std::remove_reference_t<F> func;
|
||||
|
||||
task_t(F&& func)
|
||||
: func(std::forward<F>(func))
|
||||
{
|
||||
}
|
||||
|
||||
void exec() override
|
||||
{
|
||||
func();
|
||||
task_base::exec();
|
||||
}
|
||||
};
|
||||
|
||||
return push(std::unique_ptr<task_base>{ new task_t(std::forward<F>(func)) });
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
m_stack.reset();
|
||||
}
|
||||
|
||||
void exec() const
|
||||
{
|
||||
if (m_stack)
|
||||
{
|
||||
m_stack->exec();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Thread control class
|
||||
class thread_ctrl final
|
||||
{
|
||||
static thread_local thread_ctrl* g_tls_this_thread;
|
||||
|
||||
// Fixed name
|
||||
std::string m_name;
|
||||
// Name getter
|
||||
std::function<std::string()> m_name;
|
||||
|
||||
// Thread handle (be careful)
|
||||
std::thread m_thread;
|
||||
|
||||
// Thread result (exception)
|
||||
std::exception_ptr m_exception;
|
||||
// Thread result
|
||||
std::future<void> m_future;
|
||||
|
||||
// Functions scheduled at thread exit
|
||||
task_stack m_atexit;
|
||||
std::deque<std::function<void()>> m_atexit;
|
||||
|
||||
// Called at the thread start
|
||||
static void initialize();
|
||||
@@ -99,41 +27,24 @@ class thread_ctrl final
|
||||
static void finalize() noexcept;
|
||||
|
||||
public:
|
||||
template<typename N>
|
||||
thread_ctrl(N&& name)
|
||||
: m_name(std::forward<N>(name))
|
||||
template<typename T>
|
||||
thread_ctrl(T&& name)
|
||||
: m_name(std::forward<T>(name))
|
||||
{
|
||||
}
|
||||
|
||||
// Disable copy/move constructors and operators
|
||||
thread_ctrl(const thread_ctrl&) = delete;
|
||||
|
||||
~thread_ctrl()
|
||||
{
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_thread.detach();
|
||||
}
|
||||
}
|
||||
~thread_ctrl();
|
||||
|
||||
// Get thread name
|
||||
const std::string& get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
std::string get_name() const;
|
||||
|
||||
// Get thread result (may throw)
|
||||
// Get future result (may throw)
|
||||
void join()
|
||||
{
|
||||
if (m_thread.joinable())
|
||||
{
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
if (auto&& e = std::move(m_exception))
|
||||
{
|
||||
std::rethrow_exception(e);
|
||||
}
|
||||
return m_future.get();
|
||||
}
|
||||
|
||||
// Get current thread (may be nullptr)
|
||||
@@ -143,10 +54,12 @@ public:
|
||||
}
|
||||
|
||||
// Register function at thread exit (for the current thread)
|
||||
template<typename F>
|
||||
static inline void at_exit(F&& func)
|
||||
template<typename T>
|
||||
static inline void at_exit(T&& func)
|
||||
{
|
||||
return g_tls_this_thread->m_atexit.push(std::forward<F>(func));
|
||||
CHECK_ASSERTION(g_tls_this_thread);
|
||||
|
||||
g_tls_this_thread->m_atexit.emplace_front(std::forward<T>(func));
|
||||
}
|
||||
|
||||
// Named thread factory
|
||||
@@ -155,9 +68,12 @@ public:
|
||||
{
|
||||
auto ctrl = std::make_shared<thread_ctrl>(std::forward<N>(name));
|
||||
|
||||
ctrl->m_thread = std::thread([ctrl, task = std::forward<F>(func)]()
|
||||
std::promise<void> promise;
|
||||
|
||||
ctrl->m_future = promise.get_future();
|
||||
|
||||
ctrl->m_thread = std::thread([ctrl, task = std::forward<F>(func)](std::promise<void> promise)
|
||||
{
|
||||
// Initialize TLS variable
|
||||
g_tls_this_thread = ctrl.get();
|
||||
|
||||
try
|
||||
@@ -165,21 +81,21 @@ public:
|
||||
initialize();
|
||||
task();
|
||||
finalize();
|
||||
promise.set_value();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
finalize();
|
||||
|
||||
// Set exception
|
||||
ctrl->m_exception = std::current_exception();
|
||||
promise.set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
}, std::move(promise));
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
};
|
||||
|
||||
class named_thread : public std::enable_shared_from_this<named_thread>
|
||||
class named_thread_t : public std::enable_shared_from_this<named_thread_t>
|
||||
{
|
||||
// Pointer to managed resource (shared with actual thread)
|
||||
std::shared_ptr<thread_ctrl> m_thread;
|
||||
@@ -191,27 +107,6 @@ public:
|
||||
// Thread mutex for external use (can be used with `cv`)
|
||||
std::mutex mutex;
|
||||
|
||||
// Lock mutex, notify condition variable
|
||||
void safe_notify()
|
||||
{
|
||||
// Lock for reliable notification, condition is assumed to be changed externally
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
// ID initialization
|
||||
virtual void on_init()
|
||||
{
|
||||
start();
|
||||
}
|
||||
|
||||
// ID finalization
|
||||
virtual void on_stop()
|
||||
{
|
||||
join();
|
||||
}
|
||||
|
||||
protected:
|
||||
// Thread task (called in the thread)
|
||||
virtual void on_task() = 0;
|
||||
@@ -219,13 +114,19 @@ protected:
|
||||
// Thread finalization (called after on_task)
|
||||
virtual void on_exit() {}
|
||||
|
||||
public:
|
||||
named_thread() = default;
|
||||
// ID initialization (called through id_aux_initialize)
|
||||
virtual void on_id_aux_initialize() { start(); }
|
||||
|
||||
virtual ~named_thread() = default;
|
||||
// ID finalization (called through id_aux_finalize)
|
||||
virtual void on_id_aux_finalize() { join(); }
|
||||
|
||||
public:
|
||||
named_thread_t() = default;
|
||||
|
||||
virtual ~named_thread_t() = default;
|
||||
|
||||
// Deleted copy/move constructors + copy/move operators
|
||||
named_thread(const named_thread&) = delete;
|
||||
named_thread_t(const named_thread_t&) = delete;
|
||||
|
||||
// Get thread name
|
||||
virtual std::string get_name() const;
|
||||
@@ -233,40 +134,377 @@ public:
|
||||
// Start thread (cannot be called from the constructor: should throw bad_weak_ptr in such case)
|
||||
void start();
|
||||
|
||||
// Join thread (get thread result)
|
||||
// Join thread (get future result)
|
||||
void join();
|
||||
|
||||
// Get thread_ctrl
|
||||
const thread_ctrl* get_thread_ctrl() const
|
||||
{
|
||||
return m_thread.get();
|
||||
}
|
||||
// Check whether the thread is not in "empty state"
|
||||
bool is_started() const { return m_thread.operator bool(); }
|
||||
|
||||
// Compare with the current thread
|
||||
bool is_current() const
|
||||
{
|
||||
return m_thread && thread_ctrl::get_current() == m_thread.get();
|
||||
}
|
||||
bool is_current() const { CHECK_ASSERTION(m_thread); return thread_ctrl::get_current() == m_thread.get(); }
|
||||
|
||||
// Get thread_ctrl
|
||||
const thread_ctrl* get_thread_ctrl() const { return m_thread.get(); }
|
||||
|
||||
friend void id_aux_initialize(named_thread_t* ptr) { ptr->on_id_aux_initialize(); }
|
||||
friend void id_aux_finalize(named_thread_t* ptr) { ptr->on_id_aux_finalize(); }
|
||||
};
|
||||
|
||||
// Wrapper for named thread, joins automatically in the destructor, can only be used in function scope
|
||||
class scope_thread final
|
||||
class scope_thread_t final
|
||||
{
|
||||
std::shared_ptr<thread_ctrl> m_thread;
|
||||
|
||||
public:
|
||||
template<typename N, typename F>
|
||||
scope_thread(N&& name, F&& func)
|
||||
scope_thread_t(N&& name, F&& func)
|
||||
: m_thread(thread_ctrl::spawn(std::forward<N>(name), std::forward<F>(func)))
|
||||
{
|
||||
}
|
||||
|
||||
// Deleted copy/move constructors + copy/move operators
|
||||
scope_thread(const scope_thread&) = delete;
|
||||
scope_thread_t(const scope_thread_t&) = delete;
|
||||
|
||||
// Destructor with exceptions allowed
|
||||
~scope_thread() noexcept(false)
|
||||
~scope_thread_t() noexcept(false)
|
||||
{
|
||||
m_thread->join();
|
||||
}
|
||||
};
|
||||
|
||||
extern const std::function<bool()> SQUEUE_ALWAYS_EXIT;
|
||||
extern const std::function<bool()> SQUEUE_NEVER_EXIT;
|
||||
|
||||
bool squeue_test_exit();
|
||||
|
||||
template<typename T, u32 sq_size = 256>
|
||||
class squeue_t
|
||||
{
|
||||
struct squeue_sync_var_t
|
||||
{
|
||||
struct
|
||||
{
|
||||
u32 position : 31;
|
||||
u32 pop_lock : 1;
|
||||
};
|
||||
struct
|
||||
{
|
||||
u32 count : 31;
|
||||
u32 push_lock : 1;
|
||||
};
|
||||
};
|
||||
|
||||
atomic_t<squeue_sync_var_t> m_sync;
|
||||
|
||||
mutable std::mutex m_rcv_mutex;
|
||||
mutable std::mutex m_wcv_mutex;
|
||||
mutable std::condition_variable m_rcv;
|
||||
mutable std::condition_variable m_wcv;
|
||||
|
||||
T m_data[sq_size];
|
||||
|
||||
enum squeue_sync_var_result : u32
|
||||
{
|
||||
SQSVR_OK = 0,
|
||||
SQSVR_LOCKED = 1,
|
||||
SQSVR_FAILED = 2,
|
||||
};
|
||||
|
||||
public:
|
||||
squeue_t()
|
||||
: m_sync(squeue_sync_var_t{})
|
||||
{
|
||||
}
|
||||
|
||||
u32 get_max_size() const
|
||||
{
|
||||
return sq_size;
|
||||
}
|
||||
|
||||
bool is_full() const
|
||||
{
|
||||
return m_sync.load().count == sq_size;
|
||||
}
|
||||
|
||||
bool push(const T& data, const std::function<bool()>& test_exit)
|
||||
{
|
||||
u32 pos = 0;
|
||||
|
||||
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
|
||||
if (sync.push_lock)
|
||||
{
|
||||
return SQSVR_LOCKED;
|
||||
}
|
||||
if (sync.count == sq_size)
|
||||
{
|
||||
return SQSVR_FAILED;
|
||||
}
|
||||
|
||||
sync.push_lock = 1;
|
||||
pos = sync.position + sync.count;
|
||||
return SQSVR_OK;
|
||||
}))
|
||||
{
|
||||
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> wcv_lock(m_wcv_mutex);
|
||||
m_wcv.wait_for(wcv_lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
m_data[pos >= sq_size ? pos - sq_size : pos] = data;
|
||||
|
||||
m_sync.atomic_op([](squeue_sync_var_t& sync)
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
assert(sync.push_lock);
|
||||
sync.push_lock = 0;
|
||||
sync.count++;
|
||||
});
|
||||
|
||||
m_rcv.notify_one();
|
||||
m_wcv.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool push(const T& data, const volatile bool* do_exit)
|
||||
{
|
||||
return push(data, [do_exit](){ return do_exit && *do_exit; });
|
||||
}
|
||||
|
||||
force_inline bool push(const T& data)
|
||||
{
|
||||
return push(data, SQUEUE_NEVER_EXIT);
|
||||
}
|
||||
|
||||
force_inline bool try_push(const T& data)
|
||||
{
|
||||
return push(data, SQUEUE_ALWAYS_EXIT);
|
||||
}
|
||||
|
||||
bool pop(T& data, const std::function<bool()>& test_exit)
|
||||
{
|
||||
u32 pos = 0;
|
||||
|
||||
while (u32 res = m_sync.atomic_op([&pos](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
|
||||
if (!sync.count)
|
||||
{
|
||||
return SQSVR_FAILED;
|
||||
}
|
||||
if (sync.pop_lock)
|
||||
{
|
||||
return SQSVR_LOCKED;
|
||||
}
|
||||
|
||||
sync.pop_lock = 1;
|
||||
pos = sync.position;
|
||||
return SQSVR_OK;
|
||||
}))
|
||||
{
|
||||
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
|
||||
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
data = m_data[pos];
|
||||
|
||||
m_sync.atomic_op([](squeue_sync_var_t& sync)
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
assert(sync.pop_lock);
|
||||
sync.pop_lock = 0;
|
||||
sync.position++;
|
||||
sync.count--;
|
||||
if (sync.position == sq_size)
|
||||
{
|
||||
sync.position = 0;
|
||||
}
|
||||
});
|
||||
|
||||
m_rcv.notify_one();
|
||||
m_wcv.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pop(T& data, const volatile bool* do_exit)
|
||||
{
|
||||
return pop(data, [do_exit](){ return do_exit && *do_exit; });
|
||||
}
|
||||
|
||||
force_inline bool pop(T& data)
|
||||
{
|
||||
return pop(data, SQUEUE_NEVER_EXIT);
|
||||
}
|
||||
|
||||
force_inline bool try_pop(T& data)
|
||||
{
|
||||
return pop(data, SQUEUE_ALWAYS_EXIT);
|
||||
}
|
||||
|
||||
bool peek(T& data, u32 start_pos, const std::function<bool()>& test_exit)
|
||||
{
|
||||
assert(start_pos < sq_size);
|
||||
u32 pos = 0;
|
||||
|
||||
while (u32 res = m_sync.atomic_op([&pos, start_pos](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
|
||||
if (sync.count <= start_pos)
|
||||
{
|
||||
return SQSVR_FAILED;
|
||||
}
|
||||
if (sync.pop_lock)
|
||||
{
|
||||
return SQSVR_LOCKED;
|
||||
}
|
||||
|
||||
sync.pop_lock = 1;
|
||||
pos = sync.position + start_pos;
|
||||
return SQSVR_OK;
|
||||
}))
|
||||
{
|
||||
if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
|
||||
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
data = m_data[pos >= sq_size ? pos - sq_size : pos];
|
||||
|
||||
m_sync.atomic_op([](squeue_sync_var_t& sync)
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
assert(sync.pop_lock);
|
||||
sync.pop_lock = 0;
|
||||
});
|
||||
|
||||
m_rcv.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool peek(T& data, u32 start_pos, const volatile bool* do_exit)
|
||||
{
|
||||
return peek(data, start_pos, [do_exit](){ return do_exit && *do_exit; });
|
||||
}
|
||||
|
||||
force_inline bool peek(T& data, u32 start_pos = 0)
|
||||
{
|
||||
return peek(data, start_pos, SQUEUE_NEVER_EXIT);
|
||||
}
|
||||
|
||||
force_inline bool try_peek(T& data, u32 start_pos = 0)
|
||||
{
|
||||
return peek(data, start_pos, SQUEUE_ALWAYS_EXIT);
|
||||
}
|
||||
|
||||
class squeue_data_t
|
||||
{
|
||||
T* const m_data;
|
||||
const u32 m_pos;
|
||||
const u32 m_count;
|
||||
|
||||
squeue_data_t(T* data, u32 pos, u32 count)
|
||||
: m_data(data)
|
||||
, m_pos(pos)
|
||||
, m_count(count)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
T& operator [] (u32 index)
|
||||
{
|
||||
assert(index < m_count);
|
||||
index += m_pos;
|
||||
index = index < sq_size ? index : index - sq_size;
|
||||
return m_data[index];
|
||||
}
|
||||
};
|
||||
|
||||
void process(void(*proc)(squeue_data_t data))
|
||||
{
|
||||
u32 pos, count;
|
||||
|
||||
while (m_sync.atomic_op([&pos, &count](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
|
||||
if (sync.pop_lock || sync.push_lock)
|
||||
{
|
||||
return SQSVR_LOCKED;
|
||||
}
|
||||
|
||||
pos = sync.position;
|
||||
count = sync.count;
|
||||
sync.pop_lock = 1;
|
||||
sync.push_lock = 1;
|
||||
return SQSVR_OK;
|
||||
}))
|
||||
{
|
||||
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
|
||||
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
proc(squeue_data_t(m_data, pos, count));
|
||||
|
||||
m_sync.atomic_op([](squeue_sync_var_t& sync)
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
assert(sync.pop_lock && sync.push_lock);
|
||||
sync.pop_lock = 0;
|
||||
sync.push_lock = 0;
|
||||
});
|
||||
|
||||
m_wcv.notify_one();
|
||||
m_rcv.notify_one();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
while (m_sync.atomic_op([](squeue_sync_var_t& sync) -> u32
|
||||
{
|
||||
assert(sync.count <= sq_size);
|
||||
assert(sync.position < sq_size);
|
||||
|
||||
if (sync.pop_lock || sync.push_lock)
|
||||
{
|
||||
return SQSVR_LOCKED;
|
||||
}
|
||||
|
||||
sync.pop_lock = 1;
|
||||
sync.push_lock = 1;
|
||||
return SQSVR_OK;
|
||||
}))
|
||||
{
|
||||
std::unique_lock<std::mutex> rcv_lock(m_rcv_mutex);
|
||||
m_rcv.wait_for(rcv_lock, std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
m_sync.exchange({});
|
||||
m_wcv.notify_one();
|
||||
m_rcv.notify_one();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -17,10 +17,10 @@ namespace memory_helper
|
||||
{
|
||||
#ifdef _WIN32
|
||||
void* ret = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
|
||||
Ensures(ret != NULL);
|
||||
CHECK_ASSERTION(ret != NULL);
|
||||
#else
|
||||
void* ret = mmap(nullptr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
Ensures(ret != 0);
|
||||
CHECK_ASSERTION(ret != 0);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
@@ -28,18 +28,18 @@ namespace memory_helper
|
||||
void commit_page_memory(void* pointer, size_t page_size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualAlloc((u8*)pointer, page_size, MEM_COMMIT, PAGE_READWRITE) != NULL);
|
||||
CHECK_ASSERTION(VirtualAlloc((u8*)pointer, page_size, MEM_COMMIT, PAGE_READWRITE) != NULL);
|
||||
#else
|
||||
ASSERT(mprotect((u8*)pointer, page_size, PROT_READ | PROT_WRITE) != -1);
|
||||
CHECK_ASSERTION(mprotect((u8*)pointer, page_size, PROT_READ | PROT_WRITE) != -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void free_reserved_memory(void* pointer, size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(pointer, 0, MEM_RELEASE) != 0);
|
||||
CHECK_ASSERTION(VirtualFree(pointer, 0, MEM_RELEASE) != 0);
|
||||
#else
|
||||
ASSERT(munmap(pointer, size) == 0);
|
||||
CHECK_ASSERTION(munmap(pointer, size) == 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
#include "stdafx.h"
|
||||
#include "config_context.h"
|
||||
#include "StrFmt.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
void config_context_t::group::init()
|
||||
{
|
||||
if(!m_cfg->m_groups[full_name()])
|
||||
m_cfg->m_groups[full_name()] = this;
|
||||
}
|
||||
|
||||
config_context_t::group::group(config_context_t* cfg, const std::string& name)
|
||||
: m_cfg(cfg)
|
||||
, m_name(name)
|
||||
, m_parent(nullptr)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
config_context_t::group::group(group* parent, const std::string& name)
|
||||
: m_cfg(parent->m_cfg)
|
||||
, m_name(name)
|
||||
, m_parent(parent)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void config_context_t::group::set_parent(config_context_t* cfg)
|
||||
{
|
||||
m_cfg = cfg;
|
||||
init();
|
||||
}
|
||||
|
||||
std::string config_context_t::group::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
std::string config_context_t::group::full_name() const
|
||||
{
|
||||
if (m_parent)
|
||||
return m_parent->full_name() + "/" + m_name;
|
||||
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void config_context_t::assign(const config_context_t& rhs)
|
||||
{
|
||||
for (auto &rhs_g : rhs.m_groups)
|
||||
{
|
||||
auto g = m_groups.at(rhs_g.first);
|
||||
|
||||
for (auto rhs_e : rhs_g.second->entries)
|
||||
{
|
||||
if (g->entries[rhs_e.first])
|
||||
g->entries[rhs_e.first]->value_from(rhs_e.second);
|
||||
else
|
||||
g->add_entry(rhs_e.first, rhs_e.second->string_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void config_context_t::deserialize(std::istream& stream)
|
||||
{
|
||||
set_defaults();
|
||||
|
||||
uint line_index = 0;
|
||||
std::string line;
|
||||
group *current_group = nullptr;
|
||||
|
||||
while (std::getline(stream, line))
|
||||
{
|
||||
++line_index;
|
||||
line = fmt::trim(line);
|
||||
|
||||
if (line.empty())
|
||||
continue;
|
||||
|
||||
if (line.front() == '[' && line.back() == ']')
|
||||
{
|
||||
std::string group_name = line.substr(1, line.length() - 2);
|
||||
|
||||
auto found = m_groups.find(group_name);
|
||||
|
||||
if (found == m_groups.end())
|
||||
{
|
||||
std::cerr << line_index << ": group '" << group_name << "' not exists. ignored" << std::endl;
|
||||
current_group = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
current_group = found->second;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_group == nullptr)
|
||||
{
|
||||
std::cerr << line_index << ": line '" << line << "' ignored, no group." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto name_value = fmt::split(line, { "=" });
|
||||
switch (name_value.size())
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
if (current_group->entries[fmt::trim(name_value[0])])
|
||||
current_group->entries[fmt::trim(name_value[0])]->string_value({});
|
||||
|
||||
else
|
||||
current_group->add_entry(fmt::trim(name_value[0]), std::string{});
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
std::cerr << line_index << ": line '" << line << "' has more than one symbol '='. used only first" << std::endl;
|
||||
case 2:
|
||||
{
|
||||
if (current_group->entries[fmt::trim(name_value[0])])
|
||||
current_group->entries[fmt::trim(name_value[0])]->string_value(fmt::trim(name_value[1]));
|
||||
|
||||
else
|
||||
current_group->add_entry(fmt::trim(name_value[0]), fmt::trim(name_value[1]));
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void config_context_t::serialize(std::ostream& stream) const
|
||||
{
|
||||
for (auto &g : m_groups)
|
||||
{
|
||||
stream << "[" + g.first + "]" << std::endl;
|
||||
|
||||
for (auto &e : g.second->entries)
|
||||
{
|
||||
stream << e.first << "=" << e.second->string_value() << std::endl;
|
||||
}
|
||||
|
||||
stream << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void config_context_t::set_defaults()
|
||||
{
|
||||
for (auto &g : m_groups)
|
||||
{
|
||||
for (auto &e : g.second->entries)
|
||||
{
|
||||
e.second->to_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string config_context_t::to_string() const
|
||||
{
|
||||
std::ostringstream result;
|
||||
|
||||
serialize(result);
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
void config_context_t::from_string(const std::string& str)
|
||||
{
|
||||
std::istringstream source(str);
|
||||
|
||||
deserialize(source);
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include "convert.h"
|
||||
|
||||
class config_context_t
|
||||
{
|
||||
public:
|
||||
class entry_base;
|
||||
|
||||
protected:
|
||||
class group
|
||||
{
|
||||
group* m_parent;
|
||||
config_context_t* m_cfg;
|
||||
std::string m_name;
|
||||
std::vector<std::unique_ptr<entry_base>> m_entries;
|
||||
|
||||
void init();
|
||||
|
||||
public:
|
||||
std::unordered_map<std::string, entry_base *> entries;
|
||||
|
||||
group(config_context_t* cfg, const std::string& name);
|
||||
group(group* parent, const std::string& name);
|
||||
void set_parent(config_context_t* cfg);
|
||||
|
||||
std::string name() const;
|
||||
std::string full_name() const;
|
||||
|
||||
template<typename T>
|
||||
void add_entry(const std::string& name, const T& def_value)
|
||||
{
|
||||
m_entries.emplace_back(std::make_unique<entry<T>>(this, name, def_value));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T get_entry_value(const std::string& name, const T& def_value)
|
||||
{
|
||||
if (!entries[name])
|
||||
add_entry(name, def_value);
|
||||
|
||||
return convert::to<T>(entries[name]->string_value());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void set_entry_value(const std::string& name, const T& value)
|
||||
{
|
||||
if (entries[name])
|
||||
entries[name]->string_value(convert::to<std::string>(value));
|
||||
|
||||
else
|
||||
add_entry(name, value);
|
||||
}
|
||||
|
||||
friend config_context_t;
|
||||
};
|
||||
|
||||
public:
|
||||
class entry_base
|
||||
{
|
||||
public:
|
||||
virtual std::string name() = 0;
|
||||
virtual void to_default() = 0;
|
||||
virtual std::string string_value() = 0;
|
||||
virtual void string_value(const std::string& value) = 0;
|
||||
virtual void value_from(const entry_base* rhs) = 0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class entry : public entry_base
|
||||
{
|
||||
T m_default_value;
|
||||
T m_value;
|
||||
group* m_parent;
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
entry(group* parent, const std::string& name, const T& default_value)
|
||||
: m_parent(parent)
|
||||
, m_name(name)
|
||||
, m_default_value(default_value)
|
||||
, m_value(default_value)
|
||||
{
|
||||
if(!parent->entries[name])
|
||||
parent->entries[name] = this;
|
||||
}
|
||||
|
||||
T default_value() const
|
||||
{
|
||||
return m_default_value;
|
||||
}
|
||||
|
||||
T value() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void value(const T& new_value)
|
||||
{
|
||||
m_value = new_value;
|
||||
}
|
||||
|
||||
std::string name() override
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void to_default() override
|
||||
{
|
||||
value(default_value());
|
||||
}
|
||||
|
||||
std::string string_value() override
|
||||
{
|
||||
return convert::to<std::string>(value());
|
||||
}
|
||||
|
||||
void string_value(const std::string &new_value) override
|
||||
{
|
||||
value(convert::to<T>(new_value));
|
||||
}
|
||||
|
||||
void value_from(const entry_base* rhs) override
|
||||
{
|
||||
value(static_cast<const entry*>(rhs)->value());
|
||||
}
|
||||
|
||||
entry& operator = (const T& new_value)
|
||||
{
|
||||
value(new_value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T2>
|
||||
entry& operator = (const T2& new_value)
|
||||
{
|
||||
value(static_cast<T>(new_value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator const T&() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, group*> m_groups;
|
||||
|
||||
public:
|
||||
config_context_t() = default;
|
||||
|
||||
void assign(const config_context_t& rhs);
|
||||
|
||||
void serialize(std::ostream& stream) const;
|
||||
void deserialize(std::istream& stream);
|
||||
|
||||
void set_defaults();
|
||||
|
||||
std::string to_string() const;
|
||||
void from_string(const std::string&);
|
||||
};
|
||||
@@ -0,0 +1,279 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "types.h"
|
||||
|
||||
namespace convert
|
||||
{
|
||||
template<typename ReturnType, typename FromType>
|
||||
struct to_impl_t;
|
||||
|
||||
template<typename Type>
|
||||
struct to_impl_t<Type, Type>
|
||||
{
|
||||
static Type func(const Type& value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, bool>
|
||||
{
|
||||
static std::string func(bool value)
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<bool, std::string>
|
||||
{
|
||||
static bool func(const std::string& value)
|
||||
{
|
||||
return value == "true" ? true : false;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, char>
|
||||
{
|
||||
static std::string func(char value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, unsigned char>
|
||||
{
|
||||
static std::string func(unsigned char value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, short>
|
||||
{
|
||||
static std::string func(short value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, unsigned short>
|
||||
{
|
||||
static std::string func(unsigned short value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, int>
|
||||
{
|
||||
static std::string func(int value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, unsigned int>
|
||||
{
|
||||
static std::string func(unsigned int value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, long>
|
||||
{
|
||||
static std::string func(long value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, unsigned long>
|
||||
{
|
||||
static std::string func(unsigned long value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, long long>
|
||||
{
|
||||
static std::string func(long long value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, unsigned long long>
|
||||
{
|
||||
static std::string func(unsigned long long value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, float>
|
||||
{
|
||||
static std::string func(float value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, double>
|
||||
{
|
||||
static std::string func(double value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, long double>
|
||||
{
|
||||
static std::string func(long double value)
|
||||
{
|
||||
return std::to_string(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, size2i>
|
||||
{
|
||||
static std::string func(size2i value)
|
||||
{
|
||||
return std::to_string(value.width) + "x" + std::to_string(value.height);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<std::string, position2i>
|
||||
{
|
||||
static std::string func(position2i value)
|
||||
{
|
||||
return std::to_string(value.x) + ":" + std::to_string(value.y);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<int, std::string>
|
||||
{
|
||||
static int func(const std::string& value)
|
||||
{
|
||||
return std::stoi(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<unsigned int, std::string>
|
||||
{
|
||||
static unsigned int func(const std::string& value)
|
||||
{
|
||||
return (unsigned long)std::stoul(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<long, std::string>
|
||||
{
|
||||
static long func(const std::string& value)
|
||||
{
|
||||
return std::stol(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<unsigned long, std::string>
|
||||
{
|
||||
static unsigned long func(const std::string& value)
|
||||
{
|
||||
return std::stoul(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<long long, std::string>
|
||||
{
|
||||
static long long func(const std::string& value)
|
||||
{
|
||||
return std::stoll(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<unsigned long long, std::string>
|
||||
{
|
||||
static unsigned long long func(const std::string& value)
|
||||
{
|
||||
return std::stoull(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<float, std::string>
|
||||
{
|
||||
static float func(const std::string& value)
|
||||
{
|
||||
return std::stof(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<double, std::string>
|
||||
{
|
||||
static double func(const std::string& value)
|
||||
{
|
||||
return std::stod(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<long double, std::string>
|
||||
{
|
||||
static long double func(const std::string& value)
|
||||
{
|
||||
return std::stold(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<size2i, std::string>
|
||||
{
|
||||
static size2i func(const std::string& value)
|
||||
{
|
||||
const auto& data = fmt::split(value, { "x" });
|
||||
return { std::stoi(data[0]), std::stoi(data[1]) };
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct to_impl_t<position2i, std::string>
|
||||
{
|
||||
static position2i func(const std::string& value)
|
||||
{
|
||||
const auto& data = fmt::split(value, { ":" });
|
||||
return { std::stoi(data[0]), std::stoi(data[1]) };
|
||||
}
|
||||
};
|
||||
|
||||
template<typename ReturnType, typename FromType>
|
||||
ReturnType to(FromType value)
|
||||
{
|
||||
return to_impl_t<std::remove_all_extents_t<ReturnType>, std::remove_all_extents_t<FromType>>::func(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "stdafx.h"
|
||||
#include "restore_new.h"
|
||||
#include "Utilities/Log.h"
|
||||
#pragma warning(push)
|
||||
#pragma message("TODO: remove wx dependency: <wx/image.h>")
|
||||
#pragma warning(disable : 4996)
|
||||
#include <wx/image.h>
|
||||
#pragma warning(pop)
|
||||
#include "define_new_memleakdetect.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "rPlatform.h"
|
||||
|
||||
rImage::rImage()
|
||||
{
|
||||
handle = static_cast<void*>(new wxImage());
|
||||
}
|
||||
|
||||
rImage::~rImage()
|
||||
{
|
||||
delete static_cast<wxImage*>(handle);
|
||||
}
|
||||
|
||||
void rImage::Create(int width, int height, void *data, void *alpha)
|
||||
{
|
||||
static_cast<wxImage*>(handle)->Create(width, height, static_cast<unsigned char*>(data), static_cast<unsigned char*>(alpha));
|
||||
}
|
||||
void rImage::SaveFile(const std::string& name, rImageType type)
|
||||
{
|
||||
if (type == rBITMAP_TYPE_PNG)
|
||||
{
|
||||
static_cast<wxImage*>(handle)->SaveFile(fmt::FromUTF8(name),wxBITMAP_TYPE_PNG);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw EXCEPTION("unsupported type");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
/**********************************************************************
|
||||
*********** RSX Debugger
|
||||
************************************************************************/
|
||||
|
||||
struct RSXDebuggerProgram
|
||||
{
|
||||
u32 id;
|
||||
u32 vp_id;
|
||||
u32 fp_id;
|
||||
std::string vp_shader;
|
||||
std::string fp_shader;
|
||||
bool modified;
|
||||
|
||||
RSXDebuggerProgram()
|
||||
: modified(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
extern std::vector<RSXDebuggerProgram> m_debug_programs;
|
||||
|
||||
/**********************************************************************
|
||||
*********** Image stuff
|
||||
************************************************************************/
|
||||
enum rImageType
|
||||
{
|
||||
rBITMAP_TYPE_PNG
|
||||
};
|
||||
struct rImage
|
||||
{
|
||||
rImage();
|
||||
rImage(const rImage &) = delete;
|
||||
~rImage();
|
||||
void Create(int width , int height, void *data, void *alpha);
|
||||
void SaveFile(const std::string& name, rImageType type);
|
||||
|
||||
void *handle;
|
||||
};
|
||||
@@ -0,0 +1,235 @@
|
||||
#include "stdafx.h"
|
||||
#include "rTime.h"
|
||||
#pragma warning(push)
|
||||
#pragma message("TODO: remove wx dependency: <wx/datetime.h>")
|
||||
#pragma warning(disable : 4996)
|
||||
#include <wx/datetime.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
std::string rDefaultDateTimeFormat = "%c";
|
||||
|
||||
rTimeSpan::rTimeSpan()
|
||||
{
|
||||
handle = static_cast<void *>(new wxTimeSpan());
|
||||
}
|
||||
|
||||
rTimeSpan::~rTimeSpan()
|
||||
{
|
||||
delete static_cast<wxTimeSpan*>(handle);
|
||||
}
|
||||
|
||||
rTimeSpan::rTimeSpan(const rTimeSpan& other)
|
||||
|
||||
{
|
||||
handle = static_cast<void *>(new wxTimeSpan(*static_cast<wxTimeSpan*>(other.handle)));
|
||||
|
||||
}
|
||||
|
||||
rTimeSpan::rTimeSpan(int a, int b , int c, int d)
|
||||
{
|
||||
handle = static_cast<void *>(new wxTimeSpan(a,b,c,d));
|
||||
}
|
||||
|
||||
|
||||
rDateSpan::rDateSpan()
|
||||
{
|
||||
handle = static_cast<void *>(new wxDateSpan());
|
||||
}
|
||||
|
||||
rDateSpan::~rDateSpan()
|
||||
{
|
||||
delete static_cast<wxDateSpan*>(handle);
|
||||
}
|
||||
|
||||
rDateSpan::rDateSpan(const rDateSpan& other)
|
||||
{
|
||||
handle = static_cast<void *>(new wxDateSpan(*static_cast<wxDateSpan*>(other.handle)));
|
||||
}
|
||||
|
||||
rDateSpan::rDateSpan(int a, int b, int c, int d)
|
||||
{
|
||||
handle = static_cast<void *>(new wxDateSpan(a,b,c,d));
|
||||
}
|
||||
|
||||
rDateTime::rDateTime()
|
||||
{
|
||||
handle = static_cast<void *>(new wxDateTime());
|
||||
}
|
||||
|
||||
rDateTime::~rDateTime()
|
||||
{
|
||||
delete static_cast<wxDateTime*>(handle);
|
||||
}
|
||||
|
||||
rDateTime::rDateTime(const rDateTime& other)
|
||||
{
|
||||
handle = static_cast<void *>(new wxDateTime(*static_cast<wxDateTime*>(other.handle)));
|
||||
}
|
||||
|
||||
rDateTime::rDateTime(const time_t& time)
|
||||
{
|
||||
handle = static_cast<void *>(new wxDateTime(time));
|
||||
}
|
||||
|
||||
rDateTime::rDateTime(u16 day, rDateTime::Month month, u16 year, u16 hour, u16 minute, u16 second, u32 millisecond)
|
||||
{
|
||||
handle = static_cast<void *>(new wxDateTime(day,(wxDateTime::Month)month,year,hour,minute,second,millisecond));
|
||||
}
|
||||
|
||||
rDateTime rDateTime::UNow()
|
||||
{
|
||||
rDateTime time;
|
||||
delete static_cast<wxDateTime*>(time.handle);
|
||||
time.handle = static_cast<void *>(new wxDateTime(wxDateTime::UNow()));
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
rDateTime rDateTime::FromUTC(bool val)
|
||||
{
|
||||
rDateTime time(*this);
|
||||
void *temp = time.handle;
|
||||
|
||||
time.handle = static_cast<void *>(new wxDateTime(static_cast<wxDateTime*>(temp)->FromTimezone(wxDateTime::GMT0, val)));
|
||||
delete static_cast<wxDateTime*>(temp);
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
rDateTime rDateTime::ToUTC(bool val)
|
||||
{
|
||||
rDateTime time(*this);
|
||||
void *temp = time.handle;
|
||||
|
||||
time.handle = static_cast<void *>(new wxDateTime(static_cast<wxDateTime*>(temp)->ToTimezone(wxDateTime::GMT0, val)));
|
||||
delete static_cast<wxDateTime*>(temp);
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
time_t rDateTime::GetTicks()
|
||||
{
|
||||
return static_cast<wxDateTime*>(handle)->GetTicks();
|
||||
}
|
||||
|
||||
void rDateTime::Add(const rTimeSpan& span)
|
||||
{
|
||||
static_cast<wxDateTime*>(handle)->Add(*static_cast<wxTimeSpan*>(span.handle));
|
||||
}
|
||||
|
||||
void rDateTime::Add(const rDateSpan& span)
|
||||
{
|
||||
static_cast<wxDateTime*>(handle)->Add(*static_cast<wxDateSpan*>(span.handle));
|
||||
}
|
||||
|
||||
wxDateTime::TimeZone convertTZ(rDateTime::rTimeZone tz)
|
||||
{
|
||||
switch (tz)
|
||||
{
|
||||
case rDateTime::Local:
|
||||
return wxDateTime::Local;
|
||||
case rDateTime::GMT0:
|
||||
return wxDateTime::GMT0;
|
||||
case rDateTime::UTC:
|
||||
return wxDateTime::UTC;
|
||||
default:
|
||||
throw EXCEPTION("WRONG DATETIME");
|
||||
}
|
||||
}
|
||||
|
||||
std::string rDateTime::Format(const std::string &format, const rTimeZone &tz) const
|
||||
{
|
||||
return fmt::ToUTF8(static_cast<wxDateTime*>(handle)->Format(fmt::FromUTF8(format),convertTZ(tz)));
|
||||
}
|
||||
|
||||
void rDateTime::ParseDateTime(const char* format)
|
||||
{
|
||||
static_cast<wxDateTime*>(handle)->ParseDateTime(format);
|
||||
}
|
||||
|
||||
u32 rDateTime::GetAsDOS()
|
||||
{
|
||||
return static_cast<wxDateTime*>(handle)->GetAsDOS();
|
||||
}
|
||||
|
||||
rDateTime &rDateTime::SetFromDOS(u32 fromdos)
|
||||
{
|
||||
static_cast<wxDateTime*>(handle)->SetFromDOS(fromdos);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool rDateTime::IsLeapYear(int year, rDateTime::Calender cal)
|
||||
{
|
||||
if (cal == Gregorian)
|
||||
{
|
||||
return wxDateTime::IsLeapYear(year, wxDateTime::Gregorian);
|
||||
}
|
||||
else
|
||||
{
|
||||
return wxDateTime::IsLeapYear(year, wxDateTime::Julian);
|
||||
}
|
||||
}
|
||||
|
||||
int rDateTime::GetNumberOfDays(rDateTime::Month month, int year, rDateTime::Calender cal)
|
||||
{
|
||||
if (cal == Gregorian)
|
||||
{
|
||||
return wxDateTime::GetNumberOfDays(static_cast<wxDateTime::Month>(month), year, wxDateTime::Gregorian);
|
||||
}
|
||||
else
|
||||
{
|
||||
return wxDateTime::GetNumberOfDays(static_cast<wxDateTime::Month>(month), year, wxDateTime::Julian);
|
||||
}
|
||||
}
|
||||
|
||||
void rDateTime::SetToWeekDay(rDateTime::WeekDay day, int n, rDateTime::Month month, int year)
|
||||
{
|
||||
static_cast<wxDateTime*>(handle)->SetToWeekDay(
|
||||
static_cast<wxDateTime::WeekDay>(day)
|
||||
, n
|
||||
, static_cast<wxDateTime::Month>(month)
|
||||
, year
|
||||
);
|
||||
}
|
||||
|
||||
int rDateTime::GetWeekDay()
|
||||
{
|
||||
return static_cast<wxDateTime*>(handle)->GetWeekDay();
|
||||
}
|
||||
|
||||
u16 rDateTime::GetYear(rDateTime::TZ timezone)
|
||||
{
|
||||
return static_cast<wxDateTime*>(handle)->GetYear(convertTZ(timezone));
|
||||
}
|
||||
|
||||
u16 rDateTime::GetMonth(rDateTime::TZ timezone)
|
||||
{
|
||||
return static_cast<wxDateTime*>(handle)->GetMonth(convertTZ(timezone));
|
||||
}
|
||||
|
||||
u16 rDateTime::GetDay(rDateTime::TZ timezone)
|
||||
{
|
||||
return static_cast<wxDateTime*>(handle)->GetDay(convertTZ(timezone));
|
||||
}
|
||||
|
||||
u16 rDateTime::GetHour(rDateTime::TZ timezone)
|
||||
{
|
||||
return static_cast<wxDateTime*>(handle)->GetHour(convertTZ(timezone));
|
||||
}
|
||||
|
||||
u16 rDateTime::GetMinute(rDateTime::TZ timezone)
|
||||
{
|
||||
return static_cast<wxDateTime*>(handle)->GetMinute(convertTZ(timezone));
|
||||
}
|
||||
|
||||
u16 rDateTime::GetSecond(rDateTime::TZ timezone)
|
||||
{
|
||||
return static_cast<wxDateTime*>(handle)->GetSecond(convertTZ(timezone));
|
||||
}
|
||||
|
||||
u32 rDateTime::GetMillisecond(rDateTime::TZ timezone)
|
||||
{
|
||||
return static_cast<wxDateTime*>(handle)->GetMillisecond(convertTZ(timezone));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
extern std::string rDefaultDateTimeFormat;
|
||||
|
||||
|
||||
struct rTimeSpan
|
||||
{
|
||||
rTimeSpan();
|
||||
~rTimeSpan();
|
||||
rTimeSpan(const rTimeSpan& other);
|
||||
rTimeSpan(int, int, int, int);
|
||||
|
||||
void *handle;
|
||||
};
|
||||
|
||||
struct rDateSpan
|
||||
{
|
||||
rDateSpan();
|
||||
~rDateSpan();
|
||||
rDateSpan(const rDateSpan& other);
|
||||
rDateSpan(int, int, int, int);
|
||||
|
||||
void *handle;
|
||||
};
|
||||
|
||||
struct rDateTime
|
||||
{
|
||||
enum TZ
|
||||
{
|
||||
Local, GMT0,UTC
|
||||
};
|
||||
enum Calender
|
||||
{
|
||||
Gregorian, Julian
|
||||
};
|
||||
|
||||
using rTimeZone = TZ;
|
||||
|
||||
enum WeekDay
|
||||
{
|
||||
Sun = 0,
|
||||
Mon,
|
||||
Tue,
|
||||
Wed,
|
||||
Thu,
|
||||
Fri,
|
||||
Sat,
|
||||
Inv_WeekDay
|
||||
};
|
||||
|
||||
enum Month {
|
||||
Jan = 0,
|
||||
Feb = 1,
|
||||
Mar = 2,
|
||||
Apr = 3,
|
||||
May = 4,
|
||||
Jun = 5,
|
||||
Jul = 6,
|
||||
Aug = 7,
|
||||
Sep = 8,
|
||||
Oct = 9,
|
||||
Nov = 10,
|
||||
Dec = 11,
|
||||
Inv_Month = 12
|
||||
};
|
||||
|
||||
rDateTime();
|
||||
~rDateTime();
|
||||
rDateTime(const rDateTime& other);
|
||||
rDateTime(const time_t &time);
|
||||
rDateTime(u16 day, rDateTime::Month month, u16 year, u16 hour, u16 minute, u16 second, u32 millisecond);
|
||||
|
||||
static rDateTime UNow();
|
||||
rDateTime FromUTC(bool val);
|
||||
rDateTime ToUTC(bool val);
|
||||
time_t GetTicks();
|
||||
void Add(const rTimeSpan& span);
|
||||
void Add(const rDateSpan& span);
|
||||
void Close();
|
||||
std::string Format(const std::string &format = rDefaultDateTimeFormat, const rTimeZone &tz = Local) const;
|
||||
|
||||
void ParseDateTime(const char* format);
|
||||
|
||||
u32 GetAsDOS();
|
||||
rDateTime &SetFromDOS(u32 fromdos);
|
||||
|
||||
static bool IsLeapYear(int year, rDateTime::Calender cal);
|
||||
static int GetNumberOfDays(rDateTime::Month month, int year, rDateTime::Calender cal);
|
||||
void SetToWeekDay(rDateTime::WeekDay day, int n, rDateTime::Month month, int year);
|
||||
int GetWeekDay();
|
||||
|
||||
u16 GetYear( rDateTime::TZ timezone);
|
||||
u16 GetMonth(rDateTime::TZ timezone);
|
||||
u16 GetDay(rDateTime::TZ timezone);
|
||||
u16 GetHour(rDateTime::TZ timezone);
|
||||
u16 GetMinute(rDateTime::TZ timezone);
|
||||
u16 GetSecond(rDateTime::TZ timezone);
|
||||
u32 GetMillisecond(rDateTime::TZ timezone);
|
||||
|
||||
void *handle;
|
||||
};
|
||||
|
||||
+44
-43
@@ -1,116 +1,117 @@
|
||||
#include "stdafx.h"
|
||||
#include "Utilities/rXml.h"
|
||||
#pragma warning(push)
|
||||
#pragma message("TODO: remove wx dependency: <wx/xml/xml.h>")
|
||||
#pragma warning(disable : 4996)
|
||||
#include <wx/xml/xml.h>
|
||||
#pragma warning(pop)
|
||||
|
||||
rXmlNode::rXmlNode()
|
||||
{
|
||||
ownPtr = true;
|
||||
handle = new pugi::xml_node;
|
||||
handle = reinterpret_cast<void *>(new wxXmlNode());
|
||||
}
|
||||
|
||||
rXmlNode::rXmlNode(pugi::xml_node *ptr)
|
||||
rXmlNode::rXmlNode(void *ptr)
|
||||
{
|
||||
ownPtr = false;
|
||||
handle = ptr;
|
||||
}
|
||||
|
||||
rXmlNode::~rXmlNode()
|
||||
{
|
||||
if (ownPtr)
|
||||
{
|
||||
delete handle;
|
||||
}
|
||||
}
|
||||
|
||||
rXmlNode::rXmlNode(const rXmlNode& other)
|
||||
{
|
||||
ownPtr = true;
|
||||
handle = new pugi::xml_node(*other.handle);
|
||||
handle = reinterpret_cast<void *>(new wxXmlNode(*reinterpret_cast<wxXmlNode*>(other.handle)));
|
||||
}
|
||||
|
||||
rXmlNode &rXmlNode::operator=(const rXmlNode& other)
|
||||
{
|
||||
if (ownPtr)
|
||||
{
|
||||
delete handle;
|
||||
delete reinterpret_cast<wxXmlNode*>(handle);
|
||||
}
|
||||
handle = new pugi::xml_node(*other.handle);
|
||||
handle = reinterpret_cast<void *>(new wxXmlNode(*reinterpret_cast<wxXmlNode*>(other.handle)));
|
||||
ownPtr = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
rXmlNode::~rXmlNode()
|
||||
{
|
||||
if (ownPtr)
|
||||
{
|
||||
delete reinterpret_cast<wxXmlNode*>(handle);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<rXmlNode> rXmlNode::GetChildren()
|
||||
{
|
||||
// it.begin() returns node_iterator*, *it.begin() return node*.
|
||||
pugi::xml_object_range<pugi::xml_node_iterator> it = handle->children();
|
||||
pugi::xml_node begin = *it.begin();
|
||||
|
||||
if (begin)
|
||||
wxXmlNode* result = reinterpret_cast<wxXmlNode*>(handle)->GetChildren();
|
||||
if (result)
|
||||
{
|
||||
return std::make_shared<rXmlNode>(&begin);
|
||||
return std::make_shared<rXmlNode>(reinterpret_cast<void*>(result));
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
return std::shared_ptr<rXmlNode>(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<rXmlNode> rXmlNode::GetNext()
|
||||
{
|
||||
pugi::xml_node result = handle->next_sibling();
|
||||
wxXmlNode* result = reinterpret_cast<wxXmlNode*>(handle)->GetNext();
|
||||
if (result)
|
||||
{
|
||||
return std::make_shared<rXmlNode>(&result);
|
||||
return std::make_shared<rXmlNode>(reinterpret_cast<void*>(result));
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr;
|
||||
return std::shared_ptr<rXmlNode>(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::string rXmlNode::GetName()
|
||||
{
|
||||
return handle->name();
|
||||
return fmt::ToUTF8(reinterpret_cast<wxXmlNode*>(handle)->GetName());
|
||||
}
|
||||
|
||||
std::string rXmlNode::GetAttribute(const std::string &name)
|
||||
{
|
||||
auto pred = [&name](pugi::xml_attribute attr) { return (name == attr.name()); };
|
||||
return handle->find_attribute(pred).value();
|
||||
return fmt::ToUTF8(reinterpret_cast<wxXmlNode*>(handle)->GetAttribute(fmt::FromUTF8(name)));
|
||||
}
|
||||
|
||||
std::string rXmlNode::GetNodeContent()
|
||||
{
|
||||
return handle->text().get();
|
||||
}
|
||||
|
||||
void *rXmlNode::AsVoidPtr()
|
||||
{
|
||||
return static_cast<void*>(handle);
|
||||
return fmt::ToUTF8(reinterpret_cast<wxXmlNode*>(handle)->GetNodeContent());
|
||||
}
|
||||
|
||||
rXmlDocument::rXmlDocument()
|
||||
{
|
||||
handle = new pugi::xml_document;
|
||||
handle = reinterpret_cast<void *>(new wxXmlDocument());
|
||||
}
|
||||
|
||||
rXmlDocument::rXmlDocument(const rXmlDocument& other)
|
||||
{
|
||||
handle = reinterpret_cast<void *>(new wxXmlDocument(*reinterpret_cast<wxXmlDocument*>(other.handle)));
|
||||
}
|
||||
|
||||
rXmlDocument &rXmlDocument::operator = (const rXmlDocument& other)
|
||||
{
|
||||
delete reinterpret_cast<wxXmlDocument*>(handle);
|
||||
handle = reinterpret_cast<void *>(new wxXmlDocument(*reinterpret_cast<wxXmlDocument*>(other.handle)));
|
||||
return *this;
|
||||
}
|
||||
|
||||
rXmlDocument::~rXmlDocument()
|
||||
{
|
||||
delete handle;
|
||||
delete reinterpret_cast<wxXmlDocument*>(handle);
|
||||
}
|
||||
|
||||
void rXmlDocument::Load(const std::string & path)
|
||||
{
|
||||
// TODO: Unsure of use of c_str.
|
||||
handle->load_string(path.c_str());
|
||||
reinterpret_cast<wxXmlDocument*>(handle)->Load(fmt::FromUTF8(path));
|
||||
}
|
||||
|
||||
std::shared_ptr<rXmlNode> rXmlDocument::GetRoot()
|
||||
{
|
||||
pugi::xml_node root = handle->root();
|
||||
return std::make_shared<rXmlNode>(&root);
|
||||
}
|
||||
|
||||
void *rXmlDocument::AsVoidPtr()
|
||||
{
|
||||
return static_cast<void*>(handle);
|
||||
return std::make_shared<rXmlNode>(reinterpret_cast<void*>(reinterpret_cast<wxXmlDocument*>(handle)->GetRoot()));
|
||||
}
|
||||
|
||||
+5
-13
@@ -1,15 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef PUGIXML_HEADER_ONLY
|
||||
#define PUGIXML_HEADER_ONLY 1
|
||||
#endif // !PUGIXML_HEADER_ONLY
|
||||
#include "pugixml.hpp"
|
||||
#undef PUGIXML_HEADER_ONLY
|
||||
|
||||
struct rXmlNode
|
||||
{
|
||||
rXmlNode();
|
||||
rXmlNode(pugi::xml_node *);
|
||||
rXmlNode(void *);
|
||||
rXmlNode(const rXmlNode& other);
|
||||
rXmlNode &operator=(const rXmlNode& other);
|
||||
~rXmlNode();
|
||||
@@ -18,21 +12,19 @@ struct rXmlNode
|
||||
std::string GetName();
|
||||
std::string GetAttribute( const std::string &name);
|
||||
std::string GetNodeContent();
|
||||
void *AsVoidPtr();
|
||||
|
||||
pugi::xml_node *handle;
|
||||
void *handle;
|
||||
bool ownPtr;
|
||||
};
|
||||
|
||||
struct rXmlDocument
|
||||
{
|
||||
rXmlDocument();
|
||||
rXmlDocument(const rXmlDocument& other) = delete;
|
||||
rXmlDocument &operator=(const rXmlDocument& other) = delete;
|
||||
rXmlDocument(const rXmlDocument& other);
|
||||
rXmlDocument &operator=(const rXmlDocument& other);
|
||||
~rXmlDocument();
|
||||
void Load(const std::string & path);
|
||||
std::shared_ptr<rXmlNode> GetRoot();
|
||||
void *AsVoidPtr();
|
||||
|
||||
pugi::xml_document *handle;
|
||||
void *handle;
|
||||
};
|
||||
+14
-658
@@ -1,21 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <new>
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include <exception>
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
using schar = signed char;
|
||||
using uchar = unsigned char;
|
||||
using ushort = unsigned short;
|
||||
using uint = unsigned int;
|
||||
using ulong = unsigned long;
|
||||
using ullong = unsigned long long;
|
||||
|
||||
using llong = long long;
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
@@ -28,350 +23,6 @@ using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
|
||||
// Specialization with static constexpr pair<T1, T2> map[] member expected
|
||||
template<typename T1, typename T2>
|
||||
struct bijective;
|
||||
|
||||
template<typename T, std::size_t Size = sizeof(T)>
|
||||
struct atomic_storage;
|
||||
|
||||
template<typename T1, typename T2, typename = void>
|
||||
struct atomic_add;
|
||||
|
||||
template<typename T1, typename T2, typename = void>
|
||||
struct atomic_sub;
|
||||
|
||||
template<typename T1, typename T2, typename = void>
|
||||
struct atomic_and;
|
||||
|
||||
template<typename T1, typename T2, typename = void>
|
||||
struct atomic_or;
|
||||
|
||||
template<typename T1, typename T2, typename = void>
|
||||
struct atomic_xor;
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct atomic_pre_inc;
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct atomic_post_inc;
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct atomic_pre_dec;
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct atomic_post_dec;
|
||||
|
||||
template<typename T1, typename T2, typename = void>
|
||||
struct atomic_test_and_set;
|
||||
|
||||
template<typename T1, typename T2, typename = void>
|
||||
struct atomic_test_and_reset;
|
||||
|
||||
template<typename T1, typename T2, typename = void>
|
||||
struct atomic_test_and_complement;
|
||||
|
||||
template<typename T>
|
||||
class atomic_t;
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
template<typename T, typename = void>
|
||||
struct unveil;
|
||||
}
|
||||
|
||||
// TODO: replace with std::void_t when available
|
||||
namespace void_details
|
||||
{
|
||||
template<class... >
|
||||
struct make_void
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
}
|
||||
|
||||
template<class... T> using void_t = typename void_details::make_void<T...>::type;
|
||||
|
||||
// Extract T::simple_type if available, remove cv qualifiers
|
||||
template<typename T, typename = void>
|
||||
struct simple_type_helper
|
||||
{
|
||||
using type = typename std::remove_cv<T>::type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct simple_type_helper<T, void_t<typename T::simple_type>>
|
||||
{
|
||||
using type = typename T::simple_type;
|
||||
};
|
||||
|
||||
template<typename T> using simple_t = typename simple_type_helper<T>::type;
|
||||
|
||||
// Bool type equivalent
|
||||
class b8
|
||||
{
|
||||
std::uint8_t m_value;
|
||||
|
||||
public:
|
||||
b8() = default;
|
||||
|
||||
constexpr b8(bool value)
|
||||
: m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr operator bool() const
|
||||
{
|
||||
return m_value != 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Bool wrapper for restricting bool result conversions
|
||||
struct explicit_bool_t
|
||||
{
|
||||
const bool value;
|
||||
|
||||
constexpr explicit_bool_t(bool value)
|
||||
: value(value)
|
||||
{
|
||||
}
|
||||
|
||||
explicit constexpr operator bool() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef _MSC_VER
|
||||
using u128 = __uint128_t;
|
||||
using s128 = __int128_t;
|
||||
#else
|
||||
|
||||
#include "intrin.h"
|
||||
|
||||
// Unsigned 128-bit integer implementation (TODO)
|
||||
struct alignas(16) u128
|
||||
{
|
||||
std::uint64_t lo, hi;
|
||||
|
||||
u128() = default;
|
||||
|
||||
constexpr u128(std::uint64_t l)
|
||||
: lo(l)
|
||||
, hi(0)
|
||||
{
|
||||
}
|
||||
|
||||
friend u128 operator +(const u128& l, const u128& r)
|
||||
{
|
||||
u128 value;
|
||||
_addcarry_u64(_addcarry_u64(0, r.lo, l.lo, &value.lo), r.hi, l.hi, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator +(const u128& l, std::uint64_t r)
|
||||
{
|
||||
u128 value;
|
||||
_addcarry_u64(_addcarry_u64(0, r, l.lo, &value.lo), l.hi, 0, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator +(std::uint64_t l, const u128& r)
|
||||
{
|
||||
u128 value;
|
||||
_addcarry_u64(_addcarry_u64(0, r.lo, l, &value.lo), 0, r.hi, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator -(const u128& l, const u128& r)
|
||||
{
|
||||
u128 value;
|
||||
_subborrow_u64(_subborrow_u64(0, r.lo, l.lo, &value.lo), r.hi, l.hi, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator -(const u128& l, std::uint64_t r)
|
||||
{
|
||||
u128 value;
|
||||
_subborrow_u64(_subborrow_u64(0, r, l.lo, &value.lo), 0, l.hi, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator -(std::uint64_t l, const u128& r)
|
||||
{
|
||||
u128 value;
|
||||
_subborrow_u64(_subborrow_u64(0, r.lo, l, &value.lo), r.hi, 0, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
u128 operator +() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128 operator -() const
|
||||
{
|
||||
u128 value;
|
||||
_subborrow_u64(_subborrow_u64(0, lo, 0, &value.lo), hi, 0, &value.hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
u128& operator ++()
|
||||
{
|
||||
_addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi);
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128 operator ++(int)
|
||||
{
|
||||
u128 value = *this;
|
||||
_addcarry_u64(_addcarry_u64(0, 1, lo, &lo), 0, hi, &hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
u128& operator --()
|
||||
{
|
||||
_subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi);
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128 operator --(int)
|
||||
{
|
||||
u128 value = *this;
|
||||
_subborrow_u64(_subborrow_u64(0, 1, lo, &lo), 0, hi, &hi);
|
||||
return value;
|
||||
}
|
||||
|
||||
u128 operator ~() const
|
||||
{
|
||||
u128 value;
|
||||
value.lo = ~lo;
|
||||
value.hi = ~hi;
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator &(const u128& l, const u128& r)
|
||||
{
|
||||
u128 value;
|
||||
value.lo = l.lo & r.lo;
|
||||
value.hi = l.hi & r.hi;
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator |(const u128& l, const u128& r)
|
||||
{
|
||||
u128 value;
|
||||
value.lo = l.lo | r.lo;
|
||||
value.hi = l.hi | r.hi;
|
||||
return value;
|
||||
}
|
||||
|
||||
friend u128 operator ^(const u128& l, const u128& r)
|
||||
{
|
||||
u128 value;
|
||||
value.lo = l.lo ^ r.lo;
|
||||
value.hi = l.hi ^ r.hi;
|
||||
return value;
|
||||
}
|
||||
|
||||
u128& operator +=(const u128& r)
|
||||
{
|
||||
_addcarry_u64(_addcarry_u64(0, r.lo, lo, &lo), r.hi, hi, &hi);
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128& operator +=(uint64_t r)
|
||||
{
|
||||
_addcarry_u64(_addcarry_u64(0, r, lo, &lo), 0, hi, &hi);
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128& operator &=(const u128& r)
|
||||
{
|
||||
lo &= r.lo;
|
||||
hi &= r.hi;
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128& operator |=(const u128& r)
|
||||
{
|
||||
lo |= r.lo;
|
||||
hi |= r.hi;
|
||||
return *this;
|
||||
}
|
||||
|
||||
u128& operator ^=(const u128& r)
|
||||
{
|
||||
lo ^= r.lo;
|
||||
hi ^= r.hi;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Signed 128-bit integer implementation (TODO)
|
||||
struct alignas(16) s128
|
||||
{
|
||||
std::uint64_t lo;
|
||||
std::int64_t hi;
|
||||
|
||||
s128() = default;
|
||||
|
||||
constexpr s128(std::int64_t l)
|
||||
: hi(l >> 63)
|
||||
, lo(l)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr s128(std::uint64_t l)
|
||||
: hi(0)
|
||||
, lo(l)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace std
|
||||
{
|
||||
/* Let's hack. */
|
||||
|
||||
template<>
|
||||
struct is_integral<u128> : true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct is_integral<s128> : true_type
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct make_unsigned<u128>
|
||||
{
|
||||
using type = u128;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct make_unsigned<s128>
|
||||
{
|
||||
using type = u128;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct make_signed<u128>
|
||||
{
|
||||
using type = s128;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct make_signed<s128>
|
||||
{
|
||||
using type = s128;
|
||||
};
|
||||
}
|
||||
|
||||
static_assert(std::is_arithmetic<u128>::value && std::is_integral<u128>::value && alignof(u128) == 16 && sizeof(u128) == 16, "Wrong u128 implementation");
|
||||
static_assert(std::is_arithmetic<s128>::value && std::is_integral<s128>::value && alignof(s128) == 16 && sizeof(s128) == 16, "Wrong s128 implementation");
|
||||
|
||||
union alignas(2) f16
|
||||
{
|
||||
u16 _u16;
|
||||
@@ -404,313 +55,6 @@ struct ignore
|
||||
}
|
||||
};
|
||||
|
||||
// Allows to define integer convertible to multiple enum types
|
||||
template<typename T = void, typename... Ts>
|
||||
struct multicast : multicast<Ts...>
|
||||
{
|
||||
static_assert(std::is_enum<T>::value, "multicast<> error: invalid conversion type (enum type expected)");
|
||||
|
||||
multicast() = default;
|
||||
|
||||
template<typename UT>
|
||||
constexpr multicast(const UT& value)
|
||||
: multicast<Ts...>(value)
|
||||
, m_value{ value } // Forbid narrowing
|
||||
{
|
||||
}
|
||||
|
||||
constexpr operator T() const
|
||||
{
|
||||
// Cast to enum type
|
||||
return static_cast<T>(m_value);
|
||||
}
|
||||
|
||||
private:
|
||||
std::underlying_type_t<T> m_value;
|
||||
};
|
||||
|
||||
// Recursion terminator
|
||||
template<>
|
||||
struct multicast<void>
|
||||
{
|
||||
multicast() = default;
|
||||
|
||||
template<typename UT>
|
||||
constexpr multicast(const UT& value)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Small bitset for enum class types with available values [0, bitsize).
|
||||
// T must be either enum type or convertible to (registered with via simple_t<T>).
|
||||
// Internal representation is single value of type T.
|
||||
template<typename T>
|
||||
struct mset
|
||||
{
|
||||
using type = simple_t<T>;
|
||||
using under = std::underlying_type_t<type>;
|
||||
|
||||
static constexpr auto bitsize = sizeof(type) * CHAR_BIT;
|
||||
|
||||
mset() = default;
|
||||
|
||||
constexpr mset(type _enum_const)
|
||||
: m_value(static_cast<type>(shift(_enum_const)))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr mset(under raw_value, const std::nothrow_t&)
|
||||
: m_value(static_cast<T>(raw_value))
|
||||
{
|
||||
}
|
||||
|
||||
// Get underlying value
|
||||
constexpr under _value() const
|
||||
{
|
||||
return static_cast<under>(m_value);
|
||||
}
|
||||
|
||||
explicit constexpr operator bool() const
|
||||
{
|
||||
return _value() ? true : false;
|
||||
}
|
||||
|
||||
mset& operator +=(mset rhs)
|
||||
{
|
||||
return *this = { _value() | rhs._value(), std::nothrow };
|
||||
}
|
||||
|
||||
mset& operator -=(mset rhs)
|
||||
{
|
||||
return *this = { _value() & ~rhs._value(), std::nothrow };
|
||||
}
|
||||
|
||||
mset& operator &=(mset rhs)
|
||||
{
|
||||
return *this = { _value() & rhs._value(), std::nothrow };
|
||||
}
|
||||
|
||||
mset& operator ^=(mset rhs)
|
||||
{
|
||||
return *this = { _value() ^ rhs._value(), std::nothrow };
|
||||
}
|
||||
|
||||
friend constexpr mset operator +(mset lhs, mset rhs)
|
||||
{
|
||||
return{ lhs._value() | rhs._value(), std::nothrow };
|
||||
}
|
||||
|
||||
friend constexpr mset operator -(mset lhs, mset rhs)
|
||||
{
|
||||
return{ lhs._value() & ~rhs._value(), std::nothrow };
|
||||
}
|
||||
|
||||
friend constexpr mset operator &(mset lhs, mset rhs)
|
||||
{
|
||||
return{ lhs._value() & rhs._value(), std::nothrow };
|
||||
}
|
||||
|
||||
friend constexpr mset operator ^(mset lhs, mset rhs)
|
||||
{
|
||||
return{ lhs._value() ^ rhs._value(), std::nothrow };
|
||||
}
|
||||
|
||||
bool test(mset rhs) const
|
||||
{
|
||||
const under v = _value();
|
||||
const under s = rhs._value();
|
||||
return (v & s) != 0;
|
||||
}
|
||||
|
||||
bool test_and_set(mset rhs)
|
||||
{
|
||||
const under v = _value();
|
||||
const under s = rhs._value();
|
||||
*this = { v | s, std::nothrow };
|
||||
return (v & s) != 0;
|
||||
}
|
||||
|
||||
bool test_and_reset(mset rhs)
|
||||
{
|
||||
const under v = _value();
|
||||
const under s = rhs._value();
|
||||
*this = { v & ~s, std::nothrow };
|
||||
return (v & s) != 0;
|
||||
}
|
||||
|
||||
bool test_and_complement(mset rhs)
|
||||
{
|
||||
const under v = _value();
|
||||
const under s = rhs._value();
|
||||
*this = { v ^ s, std::nothrow };
|
||||
return (v & s) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
[[noreturn]] static under xrange()
|
||||
{
|
||||
throw std::out_of_range("mset<>: bit out of range");
|
||||
}
|
||||
|
||||
static constexpr under shift(const T& value)
|
||||
{
|
||||
return static_cast<under>(value) < bitsize ? static_cast<under>(1) << static_cast<under>(value) : xrange();
|
||||
}
|
||||
|
||||
T m_value;
|
||||
};
|
||||
|
||||
template<typename T, typename RT = T>
|
||||
constexpr RT to_mset()
|
||||
{
|
||||
return RT{};
|
||||
}
|
||||
|
||||
// Fold enum constants into mset<>
|
||||
template<typename T = void, typename Arg, typename... Args, typename RT = std::conditional_t<std::is_void<T>::value, mset<Arg>, T>>
|
||||
constexpr RT to_mset(Arg&& _enum_const, Args&&... args)
|
||||
{
|
||||
return RT{ std::forward<Arg>(_enum_const) } + to_mset<RT>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T, typename CT>
|
||||
struct atomic_add<mset<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename mset<T>::under;
|
||||
|
||||
static force_inline mset<T> op1(mset<T>& left, mset<T> right)
|
||||
{
|
||||
return{ atomic_storage<under>::fetch_or(reinterpret_cast<under&>(left), right._value()), std::nothrow };
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static force_inline mset<T> op2(mset<T>& left, mset<T> right)
|
||||
{
|
||||
return{ atomic_storage<under>::or_fetch(reinterpret_cast<under&>(left), right._value()), std::nothrow };
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T, typename CT>
|
||||
struct atomic_sub<mset<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename mset<T>::under;
|
||||
|
||||
static force_inline mset<T> op1(mset<T>& left, mset<T> right)
|
||||
{
|
||||
return{ atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), ~right._value()), std::nothrow };
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static force_inline mset<T> op2(mset<T>& left, mset<T> right)
|
||||
{
|
||||
return{ atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), ~right._value()), std::nothrow };
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T, typename CT>
|
||||
struct atomic_and<mset<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename mset<T>::under;
|
||||
|
||||
static force_inline mset<T> op1(mset<T>& left, mset<T> right)
|
||||
{
|
||||
return{ atomic_storage<under>::fetch_and(reinterpret_cast<under&>(left), right._value()), std::nothrow };
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static force_inline mset<T> op2(mset<T>& left, mset<T> right)
|
||||
{
|
||||
return{ atomic_storage<under>::and_fetch(reinterpret_cast<under&>(left), right._value()), std::nothrow };
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T, typename CT>
|
||||
struct atomic_xor<mset<T>, CT, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename mset<T>::under;
|
||||
|
||||
static force_inline mset<T> op1(mset<T>& left, mset<T> right)
|
||||
{
|
||||
return{ atomic_storage<under>::fetch_xor(reinterpret_cast<under&>(left), right._value()), std::nothrow };
|
||||
}
|
||||
|
||||
static constexpr auto fetch_op = &op1;
|
||||
|
||||
static force_inline mset<T> op2(mset<T>& left, mset<T> right)
|
||||
{
|
||||
return{ atomic_storage<under>::xor_fetch(reinterpret_cast<under&>(left), right._value()), std::nothrow };
|
||||
}
|
||||
|
||||
static constexpr auto op_fetch = &op2;
|
||||
static constexpr auto atomic_op = &op2;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_set<mset<T>, T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename mset<T>::under;
|
||||
|
||||
static force_inline bool _op(mset<T>& left, const T& value)
|
||||
{
|
||||
return atomic_storage<under>::bts(reinterpret_cast<under&>(left), static_cast<uint>(value));
|
||||
}
|
||||
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_reset<mset<T>, T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename mset<T>::under;
|
||||
|
||||
static force_inline bool _op(mset<T>& left, const T& value)
|
||||
{
|
||||
return atomic_storage<under>::btr(reinterpret_cast<under&>(left), static_cast<uint>(value));
|
||||
}
|
||||
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct atomic_test_and_complement<mset<T>, T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
using under = typename mset<T>::under;
|
||||
|
||||
static force_inline bool _op(mset<T>& left, const T& value)
|
||||
{
|
||||
return atomic_storage<under>::btc(reinterpret_cast<under&>(left), static_cast<uint>(value));
|
||||
}
|
||||
|
||||
static constexpr auto atomic_op = &_op;
|
||||
};
|
||||
|
||||
template<typename T1, typename T2 = const char*, typename T = T1, typename DT = T2>
|
||||
T2 bijective_find(const T& left, const DT& def = {})
|
||||
{
|
||||
for (const auto& pair : bijective<T1, T2>::map)
|
||||
{
|
||||
if (pair.first == left)
|
||||
{
|
||||
return pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct size2_base
|
||||
{
|
||||
@@ -1744,3 +1088,15 @@ using color2d = color2_base<double>;
|
||||
using color1i = color1_base<int>;
|
||||
using color1f = color1_base<float>;
|
||||
using color1d = color1_base<double>;
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<>
|
||||
struct hash<::position2i>
|
||||
{
|
||||
size_t operator()(const ::position2i& position) const
|
||||
{
|
||||
return (static_cast<size_t>(position.x) << 32) | position.y;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
#define wxUSE_GUI 1
|
||||
#endif // wxUSE_GUI
|
||||
|
||||
// No winRT support
|
||||
#define wxUSE_WINRT 0
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// compatibility settings
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -300,7 +297,7 @@
|
||||
// Recommended setting: 0 as the options below already provide a relatively
|
||||
// good level of interoperability and changing this option arguably isn't worth
|
||||
// diverging from the official builds of the library.
|
||||
#define wxUSE_STL 1
|
||||
#define wxUSE_STL 0
|
||||
|
||||
// This is not a real option but is used as the default value for
|
||||
// wxUSE_STD_IOSTREAM, wxUSE_STD_STRING and wxUSE_STD_CONTAINERS_COMPATIBLY.
|
||||
|
||||
Submodule Utilities/yaml-cpp deleted from bc71686835
@@ -1,99 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{FDC361C5-7734-493B-8CFB-037308B35122}</ProjectGuid>
|
||||
<RootNamespace>yamlcpp</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\rpcs3_default.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="..\rpcs3_release.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="yaml-cpp\src\binary.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\convert.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\directives.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emit.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emitfromevents.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emitter.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emitterstate.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\emitterutils.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\exp.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\memory.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\node.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\nodebuilder.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\nodeevents.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\node_data.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\null.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\ostream_wrapper.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\parse.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\parser.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\regex_yaml.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\scanner.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\scanscalar.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\scantag.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\scantoken.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\simplekey.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\singledocparser.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\stream.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\tag.cpp">
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\binary.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\convert.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\directives.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\emit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\emitfromevents.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\emitter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\emitterstate.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\emitterutils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\exp.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\memory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\node.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\node_data.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\nodebuilder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\nodeevents.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\null.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ostream_wrapper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\parse.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\parser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\regex_yaml.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\scanner.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\scanscalar.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\scantag.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\scantoken.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\simplekey.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\singledocparser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\stream.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\tag.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
@@ -1,9 +0,0 @@
|
||||
if (NOT APPLE)
|
||||
add_subdirectory( glslang )
|
||||
set(BUILD_TESTS OFF CACHE BOOL "Build tests" FORCE)
|
||||
set(BUILD_DEMOS OFF CACHE BOOL "Build demos" FORCE)
|
||||
# TravisCI break build with layers and vkjson
|
||||
set(BUILD_LAYERS OFF CACHE BOOL "Build demos" FORCE)
|
||||
set(BUILD_VKJSON OFF CACHE BOOL "Build demos" FORCE)
|
||||
add_subdirectory( Vulkan-LoaderAndValidationLayers )
|
||||
endif()
|
||||
Submodule Vulkan/Vulkan-LoaderAndValidationLayers deleted from 1affe90f0e
@@ -1,66 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{58B40697-B15E-429E-B325-D52C28AEBCBF}</ProjectGuid>
|
||||
<Keyword>MakeFileProj</Keyword>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release</NMakeCleanCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release
|
||||
</NMakeReBuildCommandLine>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug
|
||||
</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF ../Vulkan-LoaderAndValidationLayers
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
|
||||
Submodule Vulkan/glslang deleted from 3c5b1e6b31
@@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{8F85B6CC-250F-4ACA-A617-E820A74E3E3C}</ProjectGuid>
|
||||
<Keyword>MakeFileProj</Keyword>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NMakeBuildCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug
|
||||
</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug
|
||||
</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>
|
||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug
|
||||
</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
|
||||
+4
-6
@@ -14,11 +14,10 @@ branches:
|
||||
|
||||
before_build:
|
||||
# until git for win 2.5 release with commit checkout
|
||||
- git submodule update --init 3rdparty/ffmpeg 3rdparty/pugixml asmjit 3rdparty/GSL 3rdparty/libpng Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers Utilities/yaml-cpp
|
||||
- git submodule update --init ffmpeg asmjit minidx9 rsx_program_decompiler
|
||||
- 7z x wxWidgets.7z -aos -oC:\rpcs3\wxWidgets > null
|
||||
- 7z x zlib.7z -aos -oC:\rpcs3\ > null
|
||||
- if %configuration%==Release (cmake -G "Visual Studio 14 Win64" -DZLIB_ROOT=C:/rpcs3/zlib/)
|
||||
else (7z x llvmlibs.7z -aos -oC:\rpcs3 > null && cmake -G "Visual Studio 14 Win64" -DLLVM_DIR=C:/rpcs3/llvm_build/share/llvm/cmake -DZLIB_ROOT=C:/rpcs3/zlib/)
|
||||
- if %configuration%==Release (cmake -G "Visual Studio 14 Win64")
|
||||
else (7z x llvmlibs.7z -aos -oC:\rpcs3 > null && cmake -G "Visual Studio 14 Win64" -DLLVM_DIR=C:/rpcs3/llvm_build/share/llvm/cmake)
|
||||
|
||||
build_script:
|
||||
- cmake --build . --config Release -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||
@@ -26,9 +25,8 @@ build_script:
|
||||
install:
|
||||
- ps: Start-FileDownload 'https://402331b94f8e4b87ae2ef4677347f7956cf3861f.googledrive.com/host/0B6v_qtb9hkicfmt0NG0wTTRtUmF4X3VTQk5Oc2JidEVKVnUteDA1dXdrYlNsVW9kREpsSHc/wxWidgets.7z'
|
||||
- ps: Start-FileDownload 'https://402331b94f8e4b87ae2ef4677347f7956cf3861f.googledrive.com/host/0B6v_qtb9hkicfmt0NG0wTTRtUmF4X3VTQk5Oc2JidEVKVnUteDA1dXdrYlNsVW9kREpsSHc/llvmlibs.7z'
|
||||
- ps: Start-FileDownload 'https://402331b94f8e4b87ae2ef4677347f7956cf3861f.googledrive.com/host/0B6v_qtb9hkicfmt0NG0wTTRtUmF4X3VTQk5Oc2JidEVKVnUteDA1dXdrYlNsVW9kREpsSHc/zlib.7z'
|
||||
- set WXWIN=C:\rpcs3\wxWidgets
|
||||
- set OPENALDIR=C:\rpcs3\3rdparty\OpenAL
|
||||
- set OPENALDIR=C:\rpcs3\OpenAL
|
||||
- set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;C:\wxWidgets;%PATH%
|
||||
- set COMMIT_SHA=%APPVEYOR_REPO_COMMIT:~0,8%
|
||||
|
||||
|
||||
+46
-36
@@ -1,22 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug - LLVM|x64">
|
||||
<Configuration>Debug - LLVM</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug - MemLeak|x64">
|
||||
<Configuration>Debug - MemLeak</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release - LLVM|x64">
|
||||
<Configuration>Release - LLVM</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
@@ -83,42 +71,64 @@
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\rpcs3_default.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'" Label="PropertySheets">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
<Import Project="..\rpcs3_memleak.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'" Label="PropertySheets">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
<Import Project="..\rpcs3_llvm.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="..\rpcs3_release.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'" Label="PropertySheets">
|
||||
<Import Project="..\rpcs3_release.props" />
|
||||
<Import Project="..\rpcs3_llvm.props" />
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
|
||||
<IntDir>
|
||||
</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<OutDir>$(SolutionDir)lib\$(Configuration)-$(Platform)\</OutDir>
|
||||
<IntDir>
|
||||
</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>ASMJIT_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>ASMJIT_STATIC;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>ASMJIT_STATIC;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user