Compare commits

..

1 Commits

Author SHA1 Message Date
Zangetsu38 83437e59e2 Some Change in Gui. 2016-01-07 09:22:27 +01:00
1091 changed files with 86211 additions and 72890 deletions
-2
View File
@@ -38,8 +38,6 @@
/rpcs3/Debug
/rpcs3/Release
/llvm_build
/Vulkan/Vulkan-build
/Vulkan/glslang-build
/wxWidgets/lib
/bin/rpcs3.ini
+4 -19
View File
@@ -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
View File
@@ -1,3 +1,3 @@
{
"userBlacklist": ["AlexAltea"]
"userBlacklist": [AlexAltea]
}
+5 -9
View File
@@ -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
-1
Submodule 3rdparty/GSL deleted from fc5fce4f4f
-1282
View File
File diff suppressed because it is too large Load Diff
-263
View File
@@ -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__
-59
View File
@@ -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_
-18
View File
@@ -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_
-718
View File
@@ -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__
-1
Submodule 3rdparty/libpng deleted from ea77a6fd49
-1
Submodule 3rdparty/pugixml deleted from f205aaf6e1
-6698
View File
File diff suppressed because it is too large Load Diff
-3301
View File
File diff suppressed because it is too large Load Diff
-9
View File
@@ -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 )
View File
+7 -9
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+93 -43
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>;
-203
View File
@@ -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;
}
-523
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+179 -242
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
#include "Platform.h"
#include "GNU.h"
#ifdef __APPLE__
#include <sys/types.h>
+184 -115
View File
@@ -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
View File
@@ -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
View File
@@ -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)...));
}
+155
View File
@@ -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();
}
};
-79
View File
@@ -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
+2 -2
View File
@@ -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)
{
+1 -1
View File
@@ -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
+103
View File
@@ -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
View File
@@ -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);
}
}
};
+55
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
};
+6 -6
View File
@@ -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
}
}
+172
View File
@@ -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);
}
+163
View File
@@ -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&);
};
+279
View File
@@ -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);
}
}
+42
View File
@@ -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");
}
}
+40
View File
@@ -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;
};
+235
View File
@@ -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));
}
+102
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
};
}
+1 -4
View File
@@ -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.
-99
View File
@@ -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>
-92
View File
@@ -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>
-4
View File
@@ -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>
-9
View File
@@ -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()
-66
View File
@@ -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
View File
@@ -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
View File
@@ -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">
View File

Some files were not shown because too many files have changed in this diff Show More