diff options
| author | 2013-09-04 20:17:46 -0400 | |
|---|---|---|
| committer | 2013-09-04 20:17:46 -0400 | |
| commit | 7564d28faf780bc0967ab3f6ff11b29b44e4b1aa (patch) | |
| tree | 48c87985296d0bc8d62785b8f3209f4b727d0cc0 | |
| parent | deleted gekko's common files (diff) | |
| download | yuzu-7564d28faf780bc0967ab3f6ff11b29b44e4b1aa.tar.gz yuzu-7564d28faf780bc0967ab3f6ff11b29b44e4b1aa.tar.xz yuzu-7564d28faf780bc0967ab3f6ff11b29b44e4b1aa.zip | |
replaced common code with dolphin common
54 files changed, 8640 insertions, 163 deletions
| @@ -1,23 +1,7 @@ | |||
| 1 | Microsoft Visual Studio Solution File, Format Version 11.00 | 1 | Microsoft Visual Studio Solution File, Format Version 11.00 |
| 2 | # Visual Studio 2010 | 2 | # Visual Studio 2010 |
| 3 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "akiru_qt", "src\akiru_qt\akiru_qt.vcxproj", "{A587F714-490F-407A-9E36-7AB7FA0D7BAB}" | ||
| 4 | ProjectSection(ProjectDependencies) = postProject | ||
| 5 | {8AEA7F29-3466-4786-A10D-6A4BD0610977} = {8AEA7F29-3466-4786-A10D-6A4BD0610977} | ||
| 6 | {6678D1A3-33A6-48A9-878B-48E5D2903D27} = {6678D1A3-33A6-48A9-878B-48E5D2903D27} | ||
| 7 | {E34D07D0-7DE6-4C46-A00D-C20C691789E4} = {E34D07D0-7DE6-4C46-A00D-C20C691789E4} | ||
| 8 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB} = {DFE335FC-755D-4BAA-8452-94434F8A1EDB} | ||
| 9 | EndProjectSection | ||
| 10 | EndProject | ||
| 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "src\core\core.vcxproj", "{8AEA7F29-3466-4786-A10D-6A4BD0610977}" | ||
| 12 | ProjectSection(ProjectDependencies) = postProject | ||
| 13 | {6678D1A3-33A6-48A9-878B-48E5D2903D27} = {6678D1A3-33A6-48A9-878B-48E5D2903D27} | ||
| 14 | {E34D07D0-7DE6-4C46-A00D-C20C691789E4} = {E34D07D0-7DE6-4C46-A00D-C20C691789E4} | ||
| 15 | EndProjectSection | ||
| 16 | EndProject | ||
| 17 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "src\common\common.vcxproj", "{DFE335FC-755D-4BAA-8452-94434F8A1EDB}" | 3 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "src\common\common.vcxproj", "{DFE335FC-755D-4BAA-8452-94434F8A1EDB}" |
| 18 | EndProject | 4 | EndProject |
| 19 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "akiru", "src\akiru\akiru.vcxproj", "{CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}" | ||
| 20 | EndProject | ||
| 21 | Global | 5 | Global |
| 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution | 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution |
| 23 | Debug|Win32 = Debug|Win32 | 7 | Debug|Win32 = Debug|Win32 |
| @@ -26,22 +10,6 @@ Global | |||
| 26 | Release|x64 = Release|x64 | 10 | Release|x64 = Release|x64 |
| 27 | EndGlobalSection | 11 | EndGlobalSection |
| 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution | 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution |
| 29 | {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Debug|Win32.ActiveCfg = Debug|Win32 | ||
| 30 | {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Debug|Win32.Build.0 = Debug|Win32 | ||
| 31 | {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Debug|x64.ActiveCfg = Debug|x64 | ||
| 32 | {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Debug|x64.Build.0 = Debug|x64 | ||
| 33 | {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Release|Win32.ActiveCfg = Release|Win32 | ||
| 34 | {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Release|Win32.Build.0 = Release|Win32 | ||
| 35 | {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Release|x64.ActiveCfg = Release|x64 | ||
| 36 | {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Release|x64.Build.0 = Release|x64 | ||
| 37 | {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Debug|Win32.ActiveCfg = Debug|Win32 | ||
| 38 | {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Debug|Win32.Build.0 = Debug|Win32 | ||
| 39 | {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Debug|x64.ActiveCfg = Debug|x64 | ||
| 40 | {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Debug|x64.Build.0 = Debug|x64 | ||
| 41 | {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Release|Win32.ActiveCfg = Release|Win32 | ||
| 42 | {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Release|Win32.Build.0 = Release|Win32 | ||
| 43 | {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Release|x64.ActiveCfg = Release|x64 | ||
| 44 | {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Release|x64.Build.0 = Release|x64 | ||
| 45 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Debug|Win32.ActiveCfg = Debug|Win32 | 13 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Debug|Win32.ActiveCfg = Debug|Win32 |
| 46 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Debug|Win32.Build.0 = Debug|Win32 | 14 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Debug|Win32.Build.0 = Debug|Win32 |
| 47 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Debug|x64.ActiveCfg = Debug|x64 | 15 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Debug|x64.ActiveCfg = Debug|x64 |
| @@ -50,30 +18,6 @@ Global | |||
| 50 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Release|Win32.Build.0 = Release|Win32 | 18 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Release|Win32.Build.0 = Release|Win32 |
| 51 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Release|x64.ActiveCfg = Release|x64 | 19 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Release|x64.ActiveCfg = Release|x64 |
| 52 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Release|x64.Build.0 = Release|x64 | 20 | {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Release|x64.Build.0 = Release|x64 |
| 53 | {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Debug|Win32.ActiveCfg = Debug|Win32 | ||
| 54 | {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Debug|Win32.Build.0 = Debug|Win32 | ||
| 55 | {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Debug|x64.ActiveCfg = Debug|x64 | ||
| 56 | {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Debug|x64.Build.0 = Debug|x64 | ||
| 57 | {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Release|Win32.ActiveCfg = Release|Win32 | ||
| 58 | {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Release|Win32.Build.0 = Release|Win32 | ||
| 59 | {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Release|x64.ActiveCfg = Release|x64 | ||
| 60 | {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Release|x64.Build.0 = Release|x64 | ||
| 61 | {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Debug|Win32.ActiveCfg = Debug|Win32 | ||
| 62 | {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Debug|Win32.Build.0 = Debug|Win32 | ||
| 63 | {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Debug|x64.ActiveCfg = Debug|x64 | ||
| 64 | {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Debug|x64.Build.0 = Debug|x64 | ||
| 65 | {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Release|Win32.ActiveCfg = Release|Win32 | ||
| 66 | {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Release|Win32.Build.0 = Release|Win32 | ||
| 67 | {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Release|x64.ActiveCfg = Release|x64 | ||
| 68 | {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Release|x64.Build.0 = Release|x64 | ||
| 69 | {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Debug|Win32.ActiveCfg = Debug|Win32 | ||
| 70 | {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Debug|Win32.Build.0 = Debug|Win32 | ||
| 71 | {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Debug|x64.ActiveCfg = Debug|x64 | ||
| 72 | {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Debug|x64.Build.0 = Debug|x64 | ||
| 73 | {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Release|Win32.ActiveCfg = Release|Win32 | ||
| 74 | {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Release|Win32.Build.0 = Release|Win32 | ||
| 75 | {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Release|x64.ActiveCfg = Release|x64 | ||
| 76 | {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Release|x64.Build.0 = Release|x64 | ||
| 77 | EndGlobalSection | 21 | EndGlobalSection |
| 78 | GlobalSection(SolutionProperties) = preSolution | 22 | GlobalSection(SolutionProperties) = preSolution |
| 79 | HideSolutionNode = FALSE | 23 | HideSolutionNode = FALSE |
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index c61ba6d9d..1cfe0bb37 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj | |||
| @@ -1,6 +1,14 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | 2 | <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
| 3 | <ItemGroup Label="ProjectConfigurations"> | 3 | <ItemGroup Label="ProjectConfigurations"> |
| 4 | <ProjectConfiguration Include="DebugFast|Win32"> | ||
| 5 | <Configuration>DebugFast</Configuration> | ||
| 6 | <Platform>Win32</Platform> | ||
| 7 | </ProjectConfiguration> | ||
| 8 | <ProjectConfiguration Include="DebugFast|x64"> | ||
| 9 | <Configuration>DebugFast</Configuration> | ||
| 10 | <Platform>x64</Platform> | ||
| 11 | </ProjectConfiguration> | ||
| 4 | <ProjectConfiguration Include="Debug|Win32"> | 12 | <ProjectConfiguration Include="Debug|Win32"> |
| 5 | <Configuration>Debug</Configuration> | 13 | <Configuration>Debug</Configuration> |
| 6 | <Platform>Win32</Platform> | 14 | <Platform>Win32</Platform> |
| @@ -20,102 +28,82 @@ | |||
| 20 | </ItemGroup> | 28 | </ItemGroup> |
| 21 | <PropertyGroup Label="Globals"> | 29 | <PropertyGroup Label="Globals"> |
| 22 | <ProjectGuid>{DFE335FC-755D-4BAA-8452-94434F8A1EDB}</ProjectGuid> | 30 | <ProjectGuid>{DFE335FC-755D-4BAA-8452-94434F8A1EDB}</ProjectGuid> |
| 23 | <RootNamespace>common</RootNamespace> | 31 | <RootNamespace>Common</RootNamespace> |
| 24 | </PropertyGroup> | 32 | </PropertyGroup> |
| 25 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | 33 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> |
| 26 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> | 34 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> |
| 27 | <UseDebugLibraries>true</UseDebugLibraries> | 35 | <UseDebugLibraries>true</UseDebugLibraries> |
| 28 | <ConfigurationType>StaticLibrary</ConfigurationType> | 36 | <ConfigurationType>StaticLibrary</ConfigurationType> |
| 37 | <CharacterSet>MultiByte</CharacterSet> | ||
| 29 | </PropertyGroup> | 38 | </PropertyGroup> |
| 30 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> | 39 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> |
| 31 | <ConfigurationType>StaticLibrary</ConfigurationType> | ||
| 32 | <UseDebugLibraries>true</UseDebugLibraries> | 40 | <UseDebugLibraries>true</UseDebugLibraries> |
| 41 | <ConfigurationType>StaticLibrary</ConfigurationType> | ||
| 42 | <CharacterSet>MultiByte</CharacterSet> | ||
| 33 | </PropertyGroup> | 43 | </PropertyGroup> |
| 34 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> | 44 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> |
| 45 | <UseDebugLibraries>false</UseDebugLibraries> | ||
| 46 | <ConfigurationType>StaticLibrary</ConfigurationType> | ||
| 47 | <CharacterSet>Unicode</CharacterSet> | ||
| 48 | </PropertyGroup> | ||
| 49 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="Configuration"> | ||
| 35 | <ConfigurationType>StaticLibrary</ConfigurationType> | 50 | <ConfigurationType>StaticLibrary</ConfigurationType> |
| 36 | <UseDebugLibraries>false</UseDebugLibraries> | 51 | <UseDebugLibraries>false</UseDebugLibraries> |
| 52 | <CharacterSet>MultiByte</CharacterSet> | ||
| 37 | </PropertyGroup> | 53 | </PropertyGroup> |
| 38 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> | 54 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> |
| 55 | <UseDebugLibraries>false</UseDebugLibraries> | ||
| 56 | <ConfigurationType>StaticLibrary</ConfigurationType> | ||
| 57 | <CharacterSet>Unicode</CharacterSet> | ||
| 58 | </PropertyGroup> | ||
| 59 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="Configuration"> | ||
| 39 | <ConfigurationType>StaticLibrary</ConfigurationType> | 60 | <ConfigurationType>StaticLibrary</ConfigurationType> |
| 40 | <UseDebugLibraries>false</UseDebugLibraries> | 61 | <UseDebugLibraries>false</UseDebugLibraries> |
| 62 | <CharacterSet>MultiByte</CharacterSet> | ||
| 41 | </PropertyGroup> | 63 | </PropertyGroup> |
| 42 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | 64 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> |
| 43 | <ImportGroup Label="ExtensionSettings"> | 65 | <ImportGroup Label="ExtensionSettings"> |
| 44 | </ImportGroup> | 66 | </ImportGroup> |
| 45 | <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | 67 | <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |
| 46 | <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | 68 | <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
| 47 | <Import Project="..\..\vsprops\Base.props" /> | 69 | <Import Project="..\..\vsprops\base.props" /> |
| 48 | <Import Project="..\..\vsprops\CodeGen_Debug.props" /> | 70 | <Import Project="..\..\vsprops\code_generation_debug.props" /> |
| 49 | <Import Project="..\..\vsprops\Optimization_Debug.props" /> | ||
| 50 | <Import Project="..\..\vsprops\Externals.props" /> | ||
| 51 | </ImportGroup> | 71 | </ImportGroup> |
| 52 | <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> | 72 | <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets"> |
| 53 | <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | 73 | <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
| 54 | <Import Project="..\..\vsprops\Base.props" /> | 74 | <Import Project="..\..\vsprops\base.props" /> |
| 55 | <Import Project="..\..\vsprops\CodeGen_Debug.props" /> | 75 | <Import Project="..\..\vsprops\code_generation_debug.props" /> |
| 56 | <Import Project="..\..\vsprops\Optimization_Debug.props" /> | ||
| 57 | <Import Project="..\..\vsprops\Externals.props" /> | ||
| 58 | </ImportGroup> | 76 | </ImportGroup> |
| 59 | <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | 77 | <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> |
| 60 | <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | 78 | <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
| 61 | <Import Project="..\..\vsprops\Base.props" /> | 79 | <Import Project="..\..\vsprops\base.props" /> |
| 62 | <Import Project="..\..\vsprops\CodeGen_Release.props" /> | 80 | <Import Project="..\..\vsprops\code_generation_release.props" /> |
| 63 | <Import Project="..\..\vsprops\Optimization_Release.props" /> | ||
| 64 | <Import Project="..\..\vsprops\Externals.props" /> | ||
| 65 | </ImportGroup> | 81 | </ImportGroup> |
| 66 | <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> | 82 | <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets"> |
| 67 | <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | 83 | <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> |
| 68 | <Import Project="..\..\vsprops\Base.props" /> | 84 | <Import Project="..\..\vsprops\base.props" /> |
| 69 | <Import Project="..\..\vsprops\CodeGen_Release.props" /> | 85 | <Import Project="..\..\vsprops\code_generation_release.props" /> |
| 70 | <Import Project="..\..\vsprops\Optimization_Release.props" /> | ||
| 71 | <Import Project="..\..\vsprops\Externals.props" /> | ||
| 72 | </ImportGroup> | 86 | </ImportGroup> |
| 73 | <PropertyGroup Label="UserMacros" /> | 87 | <PropertyGroup Label="UserMacros" /> |
| 74 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> | 88 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" /> |
| 75 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> | ||
| 76 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | ||
| 77 | <CustomBuildBeforeTargets> | ||
| 78 | </CustomBuildBeforeTargets> | ||
| 79 | </PropertyGroup> | ||
| 80 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | ||
| 81 | <CustomBuildBeforeTargets /> | ||
| 82 | </PropertyGroup> | ||
| 83 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> | 89 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" /> |
| 90 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" /> | ||
| 91 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" /> | ||
| 84 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> | 92 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" /> |
| 85 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | 93 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" /> |
| 86 | <CustomBuildBeforeTargets> | ||
| 87 | </CustomBuildBeforeTargets> | ||
| 88 | </PropertyGroup> | ||
| 89 | <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||
| 90 | <CustomBuildBeforeTargets /> | ||
| 91 | </PropertyGroup> | ||
| 92 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> | 94 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> |
| 93 | <ClCompile /> | 95 | <ClCompile /> |
| 94 | <Link> | 96 | <Link> |
| 95 | <GenerateDebugInformation>true</GenerateDebugInformation> | 97 | <GenerateDebugInformation>true</GenerateDebugInformation> |
| 96 | </Link> | 98 | </Link> |
| 97 | <CustomBuildStep> | 99 | <Lib /> |
| 98 | <Command> | ||
| 99 | </Command> | ||
| 100 | <Message> | ||
| 101 | </Message> | ||
| 102 | <Outputs> | ||
| 103 | </Outputs> | ||
| 104 | </CustomBuildStep> | ||
| 105 | </ItemDefinitionGroup> | 100 | </ItemDefinitionGroup> |
| 106 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | 101 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> |
| 107 | <ClCompile /> | 102 | <ClCompile /> |
| 108 | <Link> | 103 | <Link> |
| 109 | <GenerateDebugInformation>true</GenerateDebugInformation> | 104 | <GenerateDebugInformation>true</GenerateDebugInformation> |
| 110 | </Link> | 105 | </Link> |
| 111 | <CustomBuildStep> | 106 | <Lib /> |
| 112 | <Command> | ||
| 113 | </Command> | ||
| 114 | <Message> | ||
| 115 | </Message> | ||
| 116 | <Outputs> | ||
| 117 | </Outputs> | ||
| 118 | </CustomBuildStep> | ||
| 119 | </ItemDefinitionGroup> | 107 | </ItemDefinitionGroup> |
| 120 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> | 108 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> |
| 121 | <ClCompile /> | 109 | <ClCompile /> |
| @@ -124,15 +112,16 @@ | |||
| 124 | <EnableCOMDATFolding>true</EnableCOMDATFolding> | 112 | <EnableCOMDATFolding>true</EnableCOMDATFolding> |
| 125 | <OptimizeReferences>true</OptimizeReferences> | 113 | <OptimizeReferences>true</OptimizeReferences> |
| 126 | </Link> | 114 | </Link> |
| 127 | <CustomBuildStep> | 115 | <Lib /> |
| 128 | <Command> | 116 | </ItemDefinitionGroup> |
| 129 | </Command> | 117 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'"> |
| 130 | <Message> | ||
| 131 | </Message> | ||
| 132 | <Outputs> | ||
| 133 | </Outputs> | ||
| 134 | </CustomBuildStep> | ||
| 135 | <ClCompile /> | 118 | <ClCompile /> |
| 119 | <Link> | ||
| 120 | <GenerateDebugInformation>true</GenerateDebugInformation> | ||
| 121 | <EnableCOMDATFolding>true</EnableCOMDATFolding> | ||
| 122 | <OptimizeReferences>true</OptimizeReferences> | ||
| 123 | </Link> | ||
| 124 | <Lib /> | ||
| 136 | </ItemDefinitionGroup> | 125 | </ItemDefinitionGroup> |
| 137 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | 126 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> |
| 138 | <ClCompile /> | 127 | <ClCompile /> |
| @@ -141,46 +130,72 @@ | |||
| 141 | <EnableCOMDATFolding>true</EnableCOMDATFolding> | 130 | <EnableCOMDATFolding>true</EnableCOMDATFolding> |
| 142 | <OptimizeReferences>true</OptimizeReferences> | 131 | <OptimizeReferences>true</OptimizeReferences> |
| 143 | </Link> | 132 | </Link> |
| 144 | <CustomBuildStep> | 133 | <Lib /> |
| 145 | <Command> | 134 | </ItemDefinitionGroup> |
| 146 | </Command> | 135 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'"> |
| 147 | <Message> | 136 | <ClCompile /> |
| 148 | </Message> | 137 | <Link> |
| 149 | <Outputs> | 138 | <GenerateDebugInformation>true</GenerateDebugInformation> |
| 150 | </Outputs> | 139 | <EnableCOMDATFolding>true</EnableCOMDATFolding> |
| 151 | </CustomBuildStep> | 140 | <OptimizeReferences>true</OptimizeReferences> |
| 141 | </Link> | ||
| 142 | <Lib /> | ||
| 152 | </ItemDefinitionGroup> | 143 | </ItemDefinitionGroup> |
| 153 | <ItemGroup> | ||
| 154 | <ClCompile Include="src\config.cpp" /> | ||
| 155 | <ClCompile Include="src\crc.cpp" /> | ||
| 156 | <ClCompile Include="src\file_utils.cpp" /> | ||
| 157 | <ClCompile Include="src\hash.cpp" /> | ||
| 158 | <ClCompile Include="src\log.cpp" /> | ||
| 159 | <ClCompile Include="src\misc_utils.cpp" /> | ||
| 160 | <ClCompile Include="src\timer.cpp" /> | ||
| 161 | <ClCompile Include="src\x86_utils.cpp" /> | ||
| 162 | <ClCompile Include="src\xml.cpp" /> | ||
| 163 | </ItemGroup> | ||
| 164 | <ItemGroup> | 144 | <ItemGroup> |
| 165 | <ClInclude Include="src\atomic.h" /> | 145 | <ClInclude Include="src\atomic.h" /> |
| 166 | <ClInclude Include="src\atomic_gcc.h" /> | 146 | <ClInclude Include="src\atomic_gcc.h" /> |
| 167 | <ClInclude Include="src\atomic_win32.h" /> | 147 | <ClInclude Include="src\atomic_win32.h" /> |
| 148 | <ClInclude Include="src\break_points.h" /> | ||
| 149 | <ClInclude Include="src\chunk_file.h" /> | ||
| 168 | <ClInclude Include="src\common.h" /> | 150 | <ClInclude Include="src\common.h" /> |
| 169 | <ClInclude Include="src\config.h" /> | 151 | <ClInclude Include="src\common_funcs.h" /> |
| 170 | <ClInclude Include="src\crc.h" /> | 152 | <ClInclude Include="src\common_paths.h" /> |
| 171 | <ClInclude Include="src\file_utils.h" /> | 153 | <ClInclude Include="src\common_types.h" /> |
| 154 | <ClInclude Include="src\console_listener.h" /> | ||
| 155 | <ClInclude Include="src\cpu_detect.h" /> | ||
| 156 | <ClInclude Include="src\debug_interface.h" /> | ||
| 157 | <ClInclude Include="src\extended_trace.h" /> | ||
| 158 | <ClInclude Include="src\fifo_queue.h" /> | ||
| 159 | <ClInclude Include="src\file_search.h" /> | ||
| 160 | <ClInclude Include="src\file_util.h" /> | ||
| 161 | <ClInclude Include="src\fixed_size_queue.h" /> | ||
| 172 | <ClInclude Include="src\hash.h" /> | 162 | <ClInclude Include="src\hash.h" /> |
| 173 | <ClInclude Include="src\hash_container.h" /> | 163 | <ClInclude Include="src\linear_disk_cache.h" /> |
| 174 | <ClInclude Include="src\log.h" /> | 164 | <ClInclude Include="src\log.h" /> |
| 175 | <ClInclude Include="src\misc_utils.h" /> | 165 | <ClInclude Include="src\log_manager.h" /> |
| 176 | <ClInclude Include="src\platform.h" /> | 166 | <ClInclude Include="src\math_util.h" /> |
| 167 | <ClInclude Include="src\memory_util.h" /> | ||
| 168 | <ClInclude Include="src\mem_arena.h" /> | ||
| 169 | <ClInclude Include="src\msg_handler.h" /> | ||
| 170 | <ClInclude Include="src\scm_rev.h" /> | ||
| 177 | <ClInclude Include="src\std_condition_variable.h" /> | 171 | <ClInclude Include="src\std_condition_variable.h" /> |
| 178 | <ClInclude Include="src\std_mutex.h" /> | 172 | <ClInclude Include="src\std_mutex.h" /> |
| 179 | <ClInclude Include="src\std_thread.h" /> | 173 | <ClInclude Include="src\std_thread.h" /> |
| 174 | <ClInclude Include="src\string_util.h" /> | ||
| 175 | <ClInclude Include="src\thread.h" /> | ||
| 176 | <ClInclude Include="src\thunk.h" /> | ||
| 180 | <ClInclude Include="src\timer.h" /> | 177 | <ClInclude Include="src\timer.h" /> |
| 181 | <ClInclude Include="src\types.h" /> | 178 | </ItemGroup> |
| 182 | <ClInclude Include="src\x86_utils.h" /> | 179 | <ItemGroup> |
| 183 | <ClInclude Include="src\xml.h" /> | 180 | <ClCompile Include="src\break_points.cpp" /> |
| 181 | <ClCompile Include="src\console_listener.cpp" /> | ||
| 182 | <ClCompile Include="src\extended_trace.cpp" /> | ||
| 183 | <ClCompile Include="src\file_search.cpp" /> | ||
| 184 | <ClCompile Include="src\file_util.cpp" /> | ||
| 185 | <ClCompile Include="src\hash.cpp" /> | ||
| 186 | <ClCompile Include="src\log_manager.cpp" /> | ||
| 187 | <ClCompile Include="src\math_util.cpp" /> | ||
| 188 | <ClCompile Include="src\memory_util.cpp" /> | ||
| 189 | <ClCompile Include="src\mem_arena.cpp" /> | ||
| 190 | <ClCompile Include="src\misc.cpp" /> | ||
| 191 | <ClCompile Include="src\msg_handler.cpp" /> | ||
| 192 | <ClCompile Include="src\string_util.cpp" /> | ||
| 193 | <ClCompile Include="src\thread.cpp" /> | ||
| 194 | <ClCompile Include="src\timer.cpp" /> | ||
| 195 | <ClCompile Include="src\version.cpp" /> | ||
| 196 | </ItemGroup> | ||
| 197 | <ItemGroup> | ||
| 198 | <None Include="CMakeLists.txt" /> | ||
| 184 | </ItemGroup> | 199 | </ItemGroup> |
| 185 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | 200 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> |
| 186 | <ImportGroup Label="ExtensionTargets"> | 201 | <ImportGroup Label="ExtensionTargets"> |
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 9d2b5c513..ab8e5d12e 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters | |||
| @@ -1,35 +1,59 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | 1 | <?xml version="1.0" encoding="utf-8"?> |
| 2 | <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | 2 | <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
| 3 | <ItemGroup> | 3 | <ItemGroup> |
| 4 | <ClCompile Include="src\crc.cpp" /> | 4 | <ClCompile Include="src\break_points.cpp" /> |
| 5 | <ClCompile Include="src\log.cpp" /> | 5 | <ClCompile Include="src\console_listener.cpp" /> |
| 6 | <ClCompile Include="src\timer.cpp" /> | 6 | <ClCompile Include="src\extended_trace.cpp" /> |
| 7 | <ClCompile Include="src\xml.cpp" /> | 7 | <ClCompile Include="src\file_search.cpp" /> |
| 8 | <ClCompile Include="src\config.cpp" /> | 8 | <ClCompile Include="src\file_util.cpp" /> |
| 9 | <ClCompile Include="src\misc_utils.cpp" /> | ||
| 10 | <ClCompile Include="src\hash.cpp" /> | 9 | <ClCompile Include="src\hash.cpp" /> |
| 11 | <ClCompile Include="src\x86_utils.cpp" /> | 10 | <ClCompile Include="src\log_manager.cpp" /> |
| 12 | <ClCompile Include="src\file_utils.cpp" /> | 11 | <ClCompile Include="src\math_util.cpp" /> |
| 12 | <ClCompile Include="src\memory_util.cpp" /> | ||
| 13 | <ClCompile Include="src\mem_arena.cpp" /> | ||
| 14 | <ClCompile Include="src\misc.cpp" /> | ||
| 15 | <ClCompile Include="src\msg_handler.cpp" /> | ||
| 16 | <ClCompile Include="src\string_util.cpp" /> | ||
| 17 | <ClCompile Include="src\thread.cpp" /> | ||
| 18 | <ClCompile Include="src\timer.cpp" /> | ||
| 19 | <ClCompile Include="src\version.cpp" /> | ||
| 13 | </ItemGroup> | 20 | </ItemGroup> |
| 14 | <ItemGroup> | 21 | <ItemGroup> |
| 15 | <ClInclude Include="src\crc.h" /> | 22 | <ClInclude Include="src\atomic.h" /> |
| 16 | <ClInclude Include="src\platform.h" /> | 23 | <ClInclude Include="src\break_points.h" /> |
| 24 | <ClInclude Include="src\chunk_file.h" /> | ||
| 17 | <ClInclude Include="src\common.h" /> | 25 | <ClInclude Include="src\common.h" /> |
| 18 | <ClInclude Include="src\types.h" /> | 26 | <ClInclude Include="src\common_funcs.h" /> |
| 27 | <ClInclude Include="src\common_paths.h" /> | ||
| 28 | <ClInclude Include="src\common_types.h" /> | ||
| 29 | <ClInclude Include="src\console_listener.h" /> | ||
| 30 | <ClInclude Include="src\cpu_detect.h" /> | ||
| 31 | <ClInclude Include="src\debug_interface.h" /> | ||
| 32 | <ClInclude Include="src\extended_trace.h" /> | ||
| 33 | <ClInclude Include="src\fifo_queue.h" /> | ||
| 34 | <ClInclude Include="src\file_search.h" /> | ||
| 35 | <ClInclude Include="src\file_util.h" /> | ||
| 36 | <ClInclude Include="src\fixed_size_queue.h" /> | ||
| 37 | <ClInclude Include="src\hash.h" /> | ||
| 38 | <ClInclude Include="src\linear_disk_cache.h" /> | ||
| 19 | <ClInclude Include="src\log.h" /> | 39 | <ClInclude Include="src\log.h" /> |
| 20 | <ClInclude Include="src\timer.h" /> | 40 | <ClInclude Include="src\log_manager.h" /> |
| 21 | <ClInclude Include="src\x86_utils.h" /> | 41 | <ClInclude Include="src\math_util.h" /> |
| 22 | <ClInclude Include="src\config.h" /> | 42 | <ClInclude Include="src\memory_util.h" /> |
| 23 | <ClInclude Include="src\xml.h" /> | 43 | <ClInclude Include="src\mem_arena.h" /> |
| 24 | <ClInclude Include="src\misc_utils.h" /> | 44 | <ClInclude Include="src\msg_handler.h" /> |
| 25 | <ClInclude Include="src\atomic.h" /> | 45 | <ClInclude Include="src\scm_rev.h" /> |
| 26 | <ClInclude Include="src\atomic_win32.h" /> | ||
| 27 | <ClInclude Include="src\atomic_gcc.h" /> | ||
| 28 | <ClInclude Include="src\std_condition_variable.h" /> | 46 | <ClInclude Include="src\std_condition_variable.h" /> |
| 29 | <ClInclude Include="src\std_mutex.h" /> | 47 | <ClInclude Include="src\std_mutex.h" /> |
| 30 | <ClInclude Include="src\std_thread.h" /> | 48 | <ClInclude Include="src\std_thread.h" /> |
| 31 | <ClInclude Include="src\hash_container.h" /> | 49 | <ClInclude Include="src\string_util.h" /> |
| 32 | <ClInclude Include="src\hash.h" /> | 50 | <ClInclude Include="src\thread.h" /> |
| 33 | <ClInclude Include="src\file_utils.h" /> | 51 | <ClInclude Include="src\thunk.h" /> |
| 52 | <ClInclude Include="src\timer.h" /> | ||
| 53 | <ClInclude Include="src\atomic_gcc.h" /> | ||
| 54 | <ClInclude Include="src\atomic_win32.h" /> | ||
| 55 | </ItemGroup> | ||
| 56 | <ItemGroup> | ||
| 57 | <None Include="CMakeLists.txt" /> | ||
| 34 | </ItemGroup> | 58 | </ItemGroup> |
| 35 | </Project> \ No newline at end of file | 59 | </Project> \ No newline at end of file |
diff --git a/src/common/src/atomic.h b/src/common/src/atomic.h new file mode 100644 index 000000000..883bc14fb --- /dev/null +++ b/src/common/src/atomic.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _ATOMIC_H_ | ||
| 6 | #define _ATOMIC_H_ | ||
| 7 | |||
| 8 | #ifdef _WIN32 | ||
| 9 | |||
| 10 | #include "atomic_win32.h" | ||
| 11 | |||
| 12 | #else | ||
| 13 | |||
| 14 | // GCC-compatible compiler assumed! | ||
| 15 | #include "atomic_gcc.h" | ||
| 16 | |||
| 17 | #endif | ||
| 18 | |||
| 19 | #endif | ||
diff --git a/src/common/src/atomic_gcc.h b/src/common/src/atomic_gcc.h new file mode 100644 index 000000000..0f820f4fa --- /dev/null +++ b/src/common/src/atomic_gcc.h | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _ATOMIC_GCC_H_ | ||
| 6 | #define _ATOMIC_GCC_H_ | ||
| 7 | |||
| 8 | #include "common.h" | ||
| 9 | |||
| 10 | // Atomic operations are performed in a single step by the CPU. It is | ||
| 11 | // impossible for other threads to see the operation "half-done." | ||
| 12 | // | ||
| 13 | // Some atomic operations can be combined with different types of memory | ||
| 14 | // barriers called "Acquire semantics" and "Release semantics", defined below. | ||
| 15 | // | ||
| 16 | // Acquire semantics: Future memory accesses cannot be relocated to before the | ||
| 17 | // operation. | ||
| 18 | // | ||
| 19 | // Release semantics: Past memory accesses cannot be relocated to after the | ||
| 20 | // operation. | ||
| 21 | // | ||
| 22 | // These barriers affect not only the compiler, but also the CPU. | ||
| 23 | |||
| 24 | namespace Common | ||
| 25 | { | ||
| 26 | |||
| 27 | inline void AtomicAdd(volatile u32& target, u32 value) { | ||
| 28 | __sync_add_and_fetch(&target, value); | ||
| 29 | } | ||
| 30 | |||
| 31 | inline void AtomicAnd(volatile u32& target, u32 value) { | ||
| 32 | __sync_and_and_fetch(&target, value); | ||
| 33 | } | ||
| 34 | |||
| 35 | inline void AtomicDecrement(volatile u32& target) { | ||
| 36 | __sync_add_and_fetch(&target, -1); | ||
| 37 | } | ||
| 38 | |||
| 39 | inline void AtomicIncrement(volatile u32& target) { | ||
| 40 | __sync_add_and_fetch(&target, 1); | ||
| 41 | } | ||
| 42 | |||
| 43 | inline u32 AtomicLoad(volatile u32& src) { | ||
| 44 | return src; // 32-bit reads are always atomic. | ||
| 45 | } | ||
| 46 | inline u32 AtomicLoadAcquire(volatile u32& src) { | ||
| 47 | //keep the compiler from caching any memory references | ||
| 48 | u32 result = src; // 32-bit reads are always atomic. | ||
| 49 | //__sync_synchronize(); // TODO: May not be necessary. | ||
| 50 | // Compiler instruction only. x86 loads always have acquire semantics. | ||
| 51 | __asm__ __volatile__ ( "":::"memory" ); | ||
| 52 | return result; | ||
| 53 | } | ||
| 54 | |||
| 55 | inline void AtomicOr(volatile u32& target, u32 value) { | ||
| 56 | __sync_or_and_fetch(&target, value); | ||
| 57 | } | ||
| 58 | |||
| 59 | inline void AtomicStore(volatile u32& dest, u32 value) { | ||
| 60 | dest = value; // 32-bit writes are always atomic. | ||
| 61 | } | ||
| 62 | inline void AtomicStoreRelease(volatile u32& dest, u32 value) { | ||
| 63 | __sync_lock_test_and_set(&dest, value); // TODO: Wrong! This function is has acquire semantics. | ||
| 64 | } | ||
| 65 | |||
| 66 | } | ||
| 67 | |||
| 68 | // Old code kept here for reference in case we need the parts with __asm__ __volatile__. | ||
| 69 | #if 0 | ||
| 70 | LONG SyncInterlockedIncrement(LONG *Dest) | ||
| 71 | { | ||
| 72 | #if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) | ||
| 73 | return __sync_add_and_fetch(Dest, 1); | ||
| 74 | #else | ||
| 75 | register int result; | ||
| 76 | __asm__ __volatile__("lock; xadd %0,%1" | ||
| 77 | : "=r" (result), "=m" (*Dest) | ||
| 78 | : "0" (1), "m" (*Dest) | ||
| 79 | : "memory"); | ||
| 80 | return result; | ||
| 81 | #endif | ||
| 82 | } | ||
| 83 | |||
| 84 | LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val) | ||
| 85 | { | ||
| 86 | #if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) | ||
| 87 | return __sync_add_and_fetch(Dest, Val); | ||
| 88 | #else | ||
| 89 | register int result; | ||
| 90 | __asm__ __volatile__("lock; xadd %0,%1" | ||
| 91 | : "=r" (result), "=m" (*Dest) | ||
| 92 | : "0" (Val), "m" (*Dest) | ||
| 93 | : "memory"); | ||
| 94 | return result; | ||
| 95 | #endif | ||
| 96 | } | ||
| 97 | |||
| 98 | LONG SyncInterlockedExchange(LONG *Dest, LONG Val) | ||
| 99 | { | ||
| 100 | #if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) | ||
| 101 | return __sync_lock_test_and_set(Dest, Val); | ||
| 102 | #else | ||
| 103 | register int result; | ||
| 104 | __asm__ __volatile__("lock; xchg %0,%1" | ||
| 105 | : "=r" (result), "=m" (*Dest) | ||
| 106 | : "0" (Val), "m" (*Dest) | ||
| 107 | : "memory"); | ||
| 108 | return result; | ||
| 109 | #endif | ||
| 110 | } | ||
| 111 | #endif | ||
| 112 | |||
| 113 | #endif | ||
diff --git a/src/common/src/atomic_win32.h b/src/common/src/atomic_win32.h new file mode 100644 index 000000000..31ee0b784 --- /dev/null +++ b/src/common/src/atomic_win32.h | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _ATOMIC_WIN32_H_ | ||
| 6 | #define _ATOMIC_WIN32_H_ | ||
| 7 | |||
| 8 | #include "common.h" | ||
| 9 | #include <intrin.h> | ||
| 10 | #include <Windows.h> | ||
| 11 | |||
| 12 | // Atomic operations are performed in a single step by the CPU. It is | ||
| 13 | // impossible for other threads to see the operation "half-done." | ||
| 14 | // | ||
| 15 | // Some atomic operations can be combined with different types of memory | ||
| 16 | // barriers called "Acquire semantics" and "Release semantics", defined below. | ||
| 17 | // | ||
| 18 | // Acquire semantics: Future memory accesses cannot be relocated to before the | ||
| 19 | // operation. | ||
| 20 | // | ||
| 21 | // Release semantics: Past memory accesses cannot be relocated to after the | ||
| 22 | // operation. | ||
| 23 | // | ||
| 24 | // These barriers affect not only the compiler, but also the CPU. | ||
| 25 | // | ||
| 26 | // NOTE: Acquire and Release are not differentiated right now. They perform a | ||
| 27 | // full memory barrier instead of a "one-way" memory barrier. The newest | ||
| 28 | // Windows SDK has Acquire and Release versions of some Interlocked* functions. | ||
| 29 | |||
| 30 | namespace Common | ||
| 31 | { | ||
| 32 | |||
| 33 | inline void AtomicAdd(volatile u32& target, u32 value) { | ||
| 34 | InterlockedExchangeAdd((volatile LONG*)&target, (LONG)value); | ||
| 35 | } | ||
| 36 | |||
| 37 | inline void AtomicAnd(volatile u32& target, u32 value) { | ||
| 38 | _InterlockedAnd((volatile LONG*)&target, (LONG)value); | ||
| 39 | } | ||
| 40 | |||
| 41 | inline void AtomicIncrement(volatile u32& target) { | ||
| 42 | InterlockedIncrement((volatile LONG*)&target); | ||
| 43 | } | ||
| 44 | |||
| 45 | inline void AtomicDecrement(volatile u32& target) { | ||
| 46 | InterlockedDecrement((volatile LONG*)&target); | ||
| 47 | } | ||
| 48 | |||
| 49 | inline u32 AtomicLoad(volatile u32& src) { | ||
| 50 | return src; // 32-bit reads are always atomic. | ||
| 51 | } | ||
| 52 | inline u32 AtomicLoadAcquire(volatile u32& src) { | ||
| 53 | u32 result = src; // 32-bit reads are always atomic. | ||
| 54 | _ReadBarrier(); // Compiler instruction only. x86 loads always have acquire semantics. | ||
| 55 | return result; | ||
| 56 | } | ||
| 57 | |||
| 58 | inline void AtomicOr(volatile u32& target, u32 value) { | ||
| 59 | _InterlockedOr((volatile LONG*)&target, (LONG)value); | ||
| 60 | } | ||
| 61 | |||
| 62 | inline void AtomicStore(volatile u32& dest, u32 value) { | ||
| 63 | dest = value; // 32-bit writes are always atomic. | ||
| 64 | } | ||
| 65 | inline void AtomicStoreRelease(volatile u32& dest, u32 value) { | ||
| 66 | _WriteBarrier(); // Compiler instruction only. x86 stores always have release semantics. | ||
| 67 | dest = value; // 32-bit writes are always atomic. | ||
| 68 | } | ||
| 69 | |||
| 70 | } | ||
| 71 | |||
| 72 | #endif | ||
diff --git a/src/common/src/break_points.cpp b/src/common/src/break_points.cpp new file mode 100644 index 000000000..9e5c65153 --- /dev/null +++ b/src/common/src/break_points.cpp | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common.h" | ||
| 6 | #include "debug_interface.h" | ||
| 7 | #include "break_points.h" | ||
| 8 | |||
| 9 | #include <sstream> | ||
| 10 | #include <algorithm> | ||
| 11 | |||
| 12 | bool BreakPoints::IsAddressBreakPoint(u32 _iAddress) | ||
| 13 | { | ||
| 14 | for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i) | ||
| 15 | if (i->iAddress == _iAddress) | ||
| 16 | return true; | ||
| 17 | return false; | ||
| 18 | } | ||
| 19 | |||
| 20 | bool BreakPoints::IsTempBreakPoint(u32 _iAddress) | ||
| 21 | { | ||
| 22 | for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i) | ||
| 23 | if (i->iAddress == _iAddress && i->bTemporary) | ||
| 24 | return true; | ||
| 25 | return false; | ||
| 26 | } | ||
| 27 | |||
| 28 | BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const | ||
| 29 | { | ||
| 30 | TBreakPointsStr bps; | ||
| 31 | for (TBreakPoints::const_iterator i = m_BreakPoints.begin(); | ||
| 32 | i != m_BreakPoints.end(); ++i) | ||
| 33 | { | ||
| 34 | if (!i->bTemporary) | ||
| 35 | { | ||
| 36 | std::stringstream bp; | ||
| 37 | bp << std::hex << i->iAddress << " " << (i->bOn ? "n" : ""); | ||
| 38 | bps.push_back(bp.str()); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | return bps; | ||
| 43 | } | ||
| 44 | |||
| 45 | void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) | ||
| 46 | { | ||
| 47 | for (TBreakPointsStr::const_iterator i = bps.begin(); i != bps.end(); ++i) | ||
| 48 | { | ||
| 49 | TBreakPoint bp; | ||
| 50 | std::stringstream bpstr; | ||
| 51 | bpstr << std::hex << *i; | ||
| 52 | bpstr >> bp.iAddress; | ||
| 53 | bp.bOn = i->find("n") != i->npos; | ||
| 54 | bp.bTemporary = false; | ||
| 55 | Add(bp); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | void BreakPoints::Add(const TBreakPoint& bp) | ||
| 60 | { | ||
| 61 | if (!IsAddressBreakPoint(bp.iAddress)) | ||
| 62 | { | ||
| 63 | m_BreakPoints.push_back(bp); | ||
| 64 | //if (jit) | ||
| 65 | // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | void BreakPoints::Add(u32 em_address, bool temp) | ||
| 70 | { | ||
| 71 | if (!IsAddressBreakPoint(em_address)) // only add new addresses | ||
| 72 | { | ||
| 73 | TBreakPoint pt; // breakpoint settings | ||
| 74 | pt.bOn = true; | ||
| 75 | pt.bTemporary = temp; | ||
| 76 | pt.iAddress = em_address; | ||
| 77 | |||
| 78 | m_BreakPoints.push_back(pt); | ||
| 79 | |||
| 80 | //if (jit) | ||
| 81 | // jit->GetBlockCache()->InvalidateICache(em_address, 4); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | void BreakPoints::Remove(u32 em_address) | ||
| 86 | { | ||
| 87 | for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i) | ||
| 88 | { | ||
| 89 | if (i->iAddress == em_address) | ||
| 90 | { | ||
| 91 | m_BreakPoints.erase(i); | ||
| 92 | //if (jit) | ||
| 93 | // jit->GetBlockCache()->InvalidateICache(em_address, 4); | ||
| 94 | return; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | void BreakPoints::Clear() | ||
| 100 | { | ||
| 101 | //if (jit) | ||
| 102 | //{ | ||
| 103 | // std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(), | ||
| 104 | // [](const TBreakPoint& bp) | ||
| 105 | // { | ||
| 106 | // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4); | ||
| 107 | // } | ||
| 108 | // ); | ||
| 109 | //} | ||
| 110 | |||
| 111 | m_BreakPoints.clear(); | ||
| 112 | } | ||
| 113 | |||
| 114 | MemChecks::TMemChecksStr MemChecks::GetStrings() const | ||
| 115 | { | ||
| 116 | TMemChecksStr mcs; | ||
| 117 | for (TMemChecks::const_iterator i = m_MemChecks.begin(); | ||
| 118 | i != m_MemChecks.end(); ++i) | ||
| 119 | { | ||
| 120 | std::stringstream mc; | ||
| 121 | mc << std::hex << i->StartAddress; | ||
| 122 | mc << " " << (i->bRange ? i->EndAddress : i->StartAddress) << " " << | ||
| 123 | (i->bRange ? "n" : "") << (i->OnRead ? "r" : "") << | ||
| 124 | (i->OnWrite ? "w" : "") << (i->Log ? "l" : "") << (i->Break ? "p" : ""); | ||
| 125 | mcs.push_back(mc.str()); | ||
| 126 | } | ||
| 127 | |||
| 128 | return mcs; | ||
| 129 | } | ||
| 130 | |||
| 131 | void MemChecks::AddFromStrings(const TMemChecksStr& mcs) | ||
| 132 | { | ||
| 133 | for (TMemChecksStr::const_iterator i = mcs.begin(); i != mcs.end(); ++i) | ||
| 134 | { | ||
| 135 | TMemCheck mc; | ||
| 136 | std::stringstream mcstr; | ||
| 137 | mcstr << std::hex << *i; | ||
| 138 | mcstr >> mc.StartAddress; | ||
| 139 | mc.bRange = i->find("n") != i->npos; | ||
| 140 | mc.OnRead = i->find("r") != i->npos; | ||
| 141 | mc.OnWrite = i->find("w") != i->npos; | ||
| 142 | mc.Log = i->find("l") != i->npos; | ||
| 143 | mc.Break = i->find("p") != i->npos; | ||
| 144 | if (mc.bRange) | ||
| 145 | mcstr >> mc.EndAddress; | ||
| 146 | else | ||
| 147 | mc.EndAddress = mc.StartAddress; | ||
| 148 | Add(mc); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | void MemChecks::Add(const TMemCheck& _rMemoryCheck) | ||
| 153 | { | ||
| 154 | if (GetMemCheck(_rMemoryCheck.StartAddress) == 0) | ||
| 155 | m_MemChecks.push_back(_rMemoryCheck); | ||
| 156 | } | ||
| 157 | |||
| 158 | void MemChecks::Remove(u32 _Address) | ||
| 159 | { | ||
| 160 | for (TMemChecks::iterator i = m_MemChecks.begin(); i != m_MemChecks.end(); ++i) | ||
| 161 | { | ||
| 162 | if (i->StartAddress == _Address) | ||
| 163 | { | ||
| 164 | m_MemChecks.erase(i); | ||
| 165 | return; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | TMemCheck *MemChecks::GetMemCheck(u32 address) | ||
| 171 | { | ||
| 172 | for (TMemChecks::iterator i = m_MemChecks.begin(); i != m_MemChecks.end(); ++i) | ||
| 173 | { | ||
| 174 | if (i->bRange) | ||
| 175 | { | ||
| 176 | if (address >= i->StartAddress && address <= i->EndAddress) | ||
| 177 | return &(*i); | ||
| 178 | } | ||
| 179 | else if (i->StartAddress == address) | ||
| 180 | return &(*i); | ||
| 181 | } | ||
| 182 | |||
| 183 | // none found | ||
| 184 | return 0; | ||
| 185 | } | ||
| 186 | |||
| 187 | void TMemCheck::Action(DebugInterface *debug_interface, u32 iValue, u32 addr, | ||
| 188 | bool write, int size, u32 pc) | ||
| 189 | { | ||
| 190 | if ((write && OnWrite) || (!write && OnRead)) | ||
| 191 | { | ||
| 192 | if (Log) | ||
| 193 | { | ||
| 194 | INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)", | ||
| 195 | pc, debug_interface->getDescription(pc).c_str(), | ||
| 196 | write ? "Write" : "Read", size*8, size*2, iValue, addr, | ||
| 197 | debug_interface->getDescription(addr).c_str() | ||
| 198 | ); | ||
| 199 | } | ||
| 200 | if (Break) | ||
| 201 | debug_interface->breakNow(); | ||
| 202 | } | ||
| 203 | } | ||
diff --git a/src/common/src/break_points.h b/src/common/src/break_points.h new file mode 100644 index 000000000..281de1004 --- /dev/null +++ b/src/common/src/break_points.h | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _DEBUGGER_BREAKPOINTS_H | ||
| 6 | #define _DEBUGGER_BREAKPOINTS_H | ||
| 7 | |||
| 8 | #include <vector> | ||
| 9 | #include <string> | ||
| 10 | |||
| 11 | #include "common.h" | ||
| 12 | |||
| 13 | class DebugInterface; | ||
| 14 | |||
| 15 | struct TBreakPoint | ||
| 16 | { | ||
| 17 | u32 iAddress; | ||
| 18 | bool bOn; | ||
| 19 | bool bTemporary; | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct TMemCheck | ||
| 23 | { | ||
| 24 | TMemCheck() { | ||
| 25 | numHits = 0; | ||
| 26 | StartAddress = EndAddress = 0; | ||
| 27 | bRange = OnRead = OnWrite = Log = Break = false; | ||
| 28 | } | ||
| 29 | u32 StartAddress; | ||
| 30 | u32 EndAddress; | ||
| 31 | |||
| 32 | bool bRange; | ||
| 33 | |||
| 34 | bool OnRead; | ||
| 35 | bool OnWrite; | ||
| 36 | |||
| 37 | bool Log; | ||
| 38 | bool Break; | ||
| 39 | |||
| 40 | u32 numHits; | ||
| 41 | |||
| 42 | void Action(DebugInterface *dbg_interface, u32 _iValue, u32 addr, | ||
| 43 | bool write, int size, u32 pc); | ||
| 44 | }; | ||
| 45 | |||
| 46 | // Code breakpoints. | ||
| 47 | class BreakPoints | ||
| 48 | { | ||
| 49 | public: | ||
| 50 | typedef std::vector<TBreakPoint> TBreakPoints; | ||
| 51 | typedef std::vector<std::string> TBreakPointsStr; | ||
| 52 | |||
| 53 | const TBreakPoints& GetBreakPoints() { return m_BreakPoints; } | ||
| 54 | |||
| 55 | TBreakPointsStr GetStrings() const; | ||
| 56 | void AddFromStrings(const TBreakPointsStr& bps); | ||
| 57 | |||
| 58 | // is address breakpoint | ||
| 59 | bool IsAddressBreakPoint(u32 _iAddress); | ||
| 60 | bool IsTempBreakPoint(u32 _iAddress); | ||
| 61 | |||
| 62 | // Add BreakPoint | ||
| 63 | void Add(u32 em_address, bool temp=false); | ||
| 64 | void Add(const TBreakPoint& bp); | ||
| 65 | |||
| 66 | // Remove Breakpoint | ||
| 67 | void Remove(u32 _iAddress); | ||
| 68 | void Clear(); | ||
| 69 | |||
| 70 | void DeleteByAddress(u32 _Address); | ||
| 71 | |||
| 72 | private: | ||
| 73 | TBreakPoints m_BreakPoints; | ||
| 74 | u32 m_iBreakOnCount; | ||
| 75 | }; | ||
| 76 | |||
| 77 | |||
| 78 | // Memory breakpoints | ||
| 79 | class MemChecks | ||
| 80 | { | ||
| 81 | public: | ||
| 82 | typedef std::vector<TMemCheck> TMemChecks; | ||
| 83 | typedef std::vector<std::string> TMemChecksStr; | ||
| 84 | |||
| 85 | TMemChecks m_MemChecks; | ||
| 86 | |||
| 87 | const TMemChecks& GetMemChecks() { return m_MemChecks; } | ||
| 88 | |||
| 89 | TMemChecksStr GetStrings() const; | ||
| 90 | void AddFromStrings(const TMemChecksStr& mcs); | ||
| 91 | |||
| 92 | void Add(const TMemCheck& _rMemoryCheck); | ||
| 93 | |||
| 94 | // memory breakpoint | ||
| 95 | TMemCheck *GetMemCheck(u32 address); | ||
| 96 | void Remove(u32 _Address); | ||
| 97 | |||
| 98 | void Clear() { m_MemChecks.clear(); }; | ||
| 99 | }; | ||
| 100 | |||
| 101 | #endif | ||
| 102 | |||
diff --git a/src/common/src/chunk_file.h b/src/common/src/chunk_file.h new file mode 100644 index 000000000..0c333b094 --- /dev/null +++ b/src/common/src/chunk_file.h | |||
| @@ -0,0 +1,384 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #ifndef _POINTERWRAP_H_ | ||
| 7 | #define _POINTERWRAP_H_ | ||
| 8 | |||
| 9 | // Extremely simple serialization framework. | ||
| 10 | |||
| 11 | // (mis)-features: | ||
| 12 | // + Super fast | ||
| 13 | // + Very simple | ||
| 14 | // + Same code is used for serialization and deserializaition (in most cases) | ||
| 15 | // - Zero backwards/forwards compatibility | ||
| 16 | // - Serialization code for anything complex has to be manually written. | ||
| 17 | |||
| 18 | #include <map> | ||
| 19 | #include <vector> | ||
| 20 | #include <list> | ||
| 21 | #include <deque> | ||
| 22 | #include <string> | ||
| 23 | #include <type_traits> | ||
| 24 | |||
| 25 | #include "common.h" | ||
| 26 | #include "file_util.h" | ||
| 27 | |||
| 28 | template <class T> | ||
| 29 | struct LinkedListItem : public T | ||
| 30 | { | ||
| 31 | LinkedListItem<T> *next; | ||
| 32 | }; | ||
| 33 | |||
| 34 | // Wrapper class | ||
| 35 | class PointerWrap | ||
| 36 | { | ||
| 37 | public: | ||
| 38 | enum Mode | ||
| 39 | { | ||
| 40 | MODE_READ = 1, // load | ||
| 41 | MODE_WRITE, // save | ||
| 42 | MODE_MEASURE, // calculate size | ||
| 43 | MODE_VERIFY, // compare | ||
| 44 | }; | ||
| 45 | |||
| 46 | u8 **ptr; | ||
| 47 | Mode mode; | ||
| 48 | |||
| 49 | public: | ||
| 50 | PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_) {} | ||
| 51 | |||
| 52 | void SetMode(Mode mode_) { mode = mode_; } | ||
| 53 | Mode GetMode() const { return mode; } | ||
| 54 | u8** GetPPtr() { return ptr; } | ||
| 55 | |||
| 56 | template <typename K, class V> | ||
| 57 | void Do(std::map<K, V>& x) | ||
| 58 | { | ||
| 59 | u32 count = (u32)x.size(); | ||
| 60 | Do(count); | ||
| 61 | |||
| 62 | switch (mode) | ||
| 63 | { | ||
| 64 | case MODE_READ: | ||
| 65 | for (x.clear(); count != 0; --count) | ||
| 66 | { | ||
| 67 | std::pair<K, V> pair; | ||
| 68 | Do(pair.first); | ||
| 69 | Do(pair.second); | ||
| 70 | x.insert(pair); | ||
| 71 | } | ||
| 72 | break; | ||
| 73 | |||
| 74 | case MODE_WRITE: | ||
| 75 | case MODE_MEASURE: | ||
| 76 | case MODE_VERIFY: | ||
| 77 | for (auto itr = x.begin(); itr != x.end(); ++itr) | ||
| 78 | { | ||
| 79 | Do(itr->first); | ||
| 80 | Do(itr->second); | ||
| 81 | } | ||
| 82 | break; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | template <typename T> | ||
| 87 | void DoContainer(T& x) | ||
| 88 | { | ||
| 89 | u32 size = (u32)x.size(); | ||
| 90 | Do(size); | ||
| 91 | x.resize(size); | ||
| 92 | |||
| 93 | for (auto itr = x.begin(); itr != x.end(); ++itr) | ||
| 94 | Do(*itr); | ||
| 95 | } | ||
| 96 | |||
| 97 | template <typename T> | ||
| 98 | void Do(std::vector<T>& x) | ||
| 99 | { | ||
| 100 | DoContainer(x); | ||
| 101 | } | ||
| 102 | |||
| 103 | template <typename T> | ||
| 104 | void Do(std::list<T>& x) | ||
| 105 | { | ||
| 106 | DoContainer(x); | ||
| 107 | } | ||
| 108 | |||
| 109 | template <typename T> | ||
| 110 | void Do(std::deque<T>& x) | ||
| 111 | { | ||
| 112 | DoContainer(x); | ||
| 113 | } | ||
| 114 | |||
| 115 | template <typename T> | ||
| 116 | void Do(std::basic_string<T>& x) | ||
| 117 | { | ||
| 118 | DoContainer(x); | ||
| 119 | } | ||
| 120 | |||
| 121 | template <typename T> | ||
| 122 | void DoArray(T* x, u32 count) | ||
| 123 | { | ||
| 124 | for (u32 i = 0; i != count; ++i) | ||
| 125 | Do(x[i]); | ||
| 126 | } | ||
| 127 | |||
| 128 | template <typename T> | ||
| 129 | void Do(T& x) | ||
| 130 | { | ||
| 131 | // Ideally this would be std::is_trivially_copyable, but not enough support yet | ||
| 132 | static_assert(std::is_pod<T>::value, "Only sane for POD types"); | ||
| 133 | |||
| 134 | DoVoid((void*)&x, sizeof(x)); | ||
| 135 | } | ||
| 136 | |||
| 137 | template <typename T> | ||
| 138 | void DoPOD(T& x) | ||
| 139 | { | ||
| 140 | DoVoid((void*)&x, sizeof(x)); | ||
| 141 | } | ||
| 142 | |||
| 143 | template <typename T> | ||
| 144 | void DoPointer(T*& x, T* const base) | ||
| 145 | { | ||
| 146 | // pointers can be more than 2^31 apart, but you're using this function wrong if you need that much range | ||
| 147 | s32 offset = x - base; | ||
| 148 | Do(offset); | ||
| 149 | if (mode == MODE_READ) | ||
| 150 | x = base + offset; | ||
| 151 | } | ||
| 152 | |||
| 153 | // Let's pretend std::list doesn't exist! | ||
| 154 | template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)> | ||
| 155 | void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end=0) | ||
| 156 | { | ||
| 157 | LinkedListItem<T>* list_cur = list_start; | ||
| 158 | LinkedListItem<T>* prev = 0; | ||
| 159 | |||
| 160 | while (true) | ||
| 161 | { | ||
| 162 | u8 shouldExist = (list_cur ? 1 : 0); | ||
| 163 | Do(shouldExist); | ||
| 164 | if (shouldExist == 1) | ||
| 165 | { | ||
| 166 | LinkedListItem<T>* cur = list_cur ? list_cur : TNew(); | ||
| 167 | TDo(*this, (T*)cur); | ||
| 168 | if (!list_cur) | ||
| 169 | { | ||
| 170 | if (mode == MODE_READ) | ||
| 171 | { | ||
| 172 | cur->next = 0; | ||
| 173 | list_cur = cur; | ||
| 174 | if (prev) | ||
| 175 | prev->next = cur; | ||
| 176 | else | ||
| 177 | list_start = cur; | ||
| 178 | } | ||
| 179 | else | ||
| 180 | { | ||
| 181 | TFree(cur); | ||
| 182 | continue; | ||
| 183 | } | ||
| 184 | } | ||
| 185 | } | ||
| 186 | else | ||
| 187 | { | ||
| 188 | if (mode == MODE_READ) | ||
| 189 | { | ||
| 190 | if (prev) | ||
| 191 | prev->next = 0; | ||
| 192 | if (list_end) | ||
| 193 | *list_end = prev; | ||
| 194 | if (list_cur) | ||
| 195 | { | ||
| 196 | if (list_start == list_cur) | ||
| 197 | list_start = 0; | ||
| 198 | do | ||
| 199 | { | ||
| 200 | LinkedListItem<T>* next = list_cur->next; | ||
| 201 | TFree(list_cur); | ||
| 202 | list_cur = next; | ||
| 203 | } | ||
| 204 | while (list_cur); | ||
| 205 | } | ||
| 206 | } | ||
| 207 | break; | ||
| 208 | } | ||
| 209 | prev = list_cur; | ||
| 210 | list_cur = list_cur->next; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | void DoMarker(const char* prevName, u32 arbitraryNumber = 0x42) | ||
| 215 | { | ||
| 216 | u32 cookie = arbitraryNumber; | ||
| 217 | Do(cookie); | ||
| 218 | |||
| 219 | if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) | ||
| 220 | { | ||
| 221 | PanicAlertT("Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...", | ||
| 222 | prevName, cookie, cookie, arbitraryNumber, arbitraryNumber); | ||
| 223 | mode = PointerWrap::MODE_MEASURE; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | private: | ||
| 228 | __forceinline void DoByte(u8& x) | ||
| 229 | { | ||
| 230 | switch (mode) | ||
| 231 | { | ||
| 232 | case MODE_READ: | ||
| 233 | x = **ptr; | ||
| 234 | break; | ||
| 235 | |||
| 236 | case MODE_WRITE: | ||
| 237 | **ptr = x; | ||
| 238 | break; | ||
| 239 | |||
| 240 | case MODE_MEASURE: | ||
| 241 | break; | ||
| 242 | |||
| 243 | case MODE_VERIFY: | ||
| 244 | _dbg_assert_msg_(COMMON, (x == **ptr), | ||
| 245 | "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n", | ||
| 246 | x, x, &x, **ptr, **ptr, *ptr); | ||
| 247 | break; | ||
| 248 | |||
| 249 | default: | ||
| 250 | break; | ||
| 251 | } | ||
| 252 | |||
| 253 | ++(*ptr); | ||
| 254 | } | ||
| 255 | |||
| 256 | void DoVoid(void *data, u32 size) | ||
| 257 | { | ||
| 258 | for(u32 i = 0; i != size; ++i) | ||
| 259 | DoByte(reinterpret_cast<u8*>(data)[i]); | ||
| 260 | } | ||
| 261 | }; | ||
| 262 | |||
| 263 | class CChunkFileReader | ||
| 264 | { | ||
| 265 | public: | ||
| 266 | // Load file template | ||
| 267 | template<class T> | ||
| 268 | static bool Load(const std::string& _rFilename, u32 _Revision, T& _class) | ||
| 269 | { | ||
| 270 | INFO_LOG(COMMON, "ChunkReader: Loading %s" , _rFilename.c_str()); | ||
| 271 | |||
| 272 | if (!File::Exists(_rFilename)) | ||
| 273 | return false; | ||
| 274 | |||
| 275 | // Check file size | ||
| 276 | const u64 fileSize = File::GetSize(_rFilename); | ||
| 277 | static const u64 headerSize = sizeof(SChunkHeader); | ||
| 278 | if (fileSize < headerSize) | ||
| 279 | { | ||
| 280 | ERROR_LOG(COMMON,"ChunkReader: File too small"); | ||
| 281 | return false; | ||
| 282 | } | ||
| 283 | |||
| 284 | File::IOFile pFile(_rFilename, "rb"); | ||
| 285 | if (!pFile) | ||
| 286 | { | ||
| 287 | ERROR_LOG(COMMON,"ChunkReader: Can't open file for reading"); | ||
| 288 | return false; | ||
| 289 | } | ||
| 290 | |||
| 291 | // read the header | ||
| 292 | SChunkHeader header; | ||
| 293 | if (!pFile.ReadArray(&header, 1)) | ||
| 294 | { | ||
| 295 | ERROR_LOG(COMMON,"ChunkReader: Bad header size"); | ||
| 296 | return false; | ||
| 297 | } | ||
| 298 | |||
| 299 | // Check revision | ||
| 300 | if (header.Revision != _Revision) | ||
| 301 | { | ||
| 302 | ERROR_LOG(COMMON,"ChunkReader: Wrong file revision, got %d expected %d", | ||
| 303 | header.Revision, _Revision); | ||
| 304 | return false; | ||
| 305 | } | ||
| 306 | |||
| 307 | // get size | ||
| 308 | const u32 sz = (u32)(fileSize - headerSize); | ||
| 309 | if (header.ExpectedSize != sz) | ||
| 310 | { | ||
| 311 | ERROR_LOG(COMMON,"ChunkReader: Bad file size, got %d expected %d", | ||
| 312 | sz, header.ExpectedSize); | ||
| 313 | return false; | ||
| 314 | } | ||
| 315 | |||
| 316 | // read the state | ||
| 317 | std::vector<u8> buffer(sz); | ||
| 318 | if (!pFile.ReadArray(&buffer[0], sz)) | ||
| 319 | { | ||
| 320 | ERROR_LOG(COMMON,"ChunkReader: Error reading file"); | ||
| 321 | return false; | ||
| 322 | } | ||
| 323 | |||
| 324 | u8* ptr = &buffer[0]; | ||
| 325 | PointerWrap p(&ptr, PointerWrap::MODE_READ); | ||
| 326 | _class.DoState(p); | ||
| 327 | |||
| 328 | INFO_LOG(COMMON, "ChunkReader: Done loading %s" , _rFilename.c_str()); | ||
| 329 | return true; | ||
| 330 | } | ||
| 331 | |||
| 332 | // Save file template | ||
| 333 | template<class T> | ||
| 334 | static bool Save(const std::string& _rFilename, u32 _Revision, T& _class) | ||
| 335 | { | ||
| 336 | INFO_LOG(COMMON, "ChunkReader: Writing %s" , _rFilename.c_str()); | ||
| 337 | File::IOFile pFile(_rFilename, "wb"); | ||
| 338 | if (!pFile) | ||
| 339 | { | ||
| 340 | ERROR_LOG(COMMON,"ChunkReader: Error opening file for write"); | ||
| 341 | return false; | ||
| 342 | } | ||
| 343 | |||
| 344 | // Get data | ||
| 345 | u8 *ptr = 0; | ||
| 346 | PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); | ||
| 347 | _class.DoState(p); | ||
| 348 | size_t const sz = (size_t)ptr; | ||
| 349 | std::vector<u8> buffer(sz); | ||
| 350 | ptr = &buffer[0]; | ||
| 351 | p.SetMode(PointerWrap::MODE_WRITE); | ||
| 352 | _class.DoState(p); | ||
| 353 | |||
| 354 | // Create header | ||
| 355 | SChunkHeader header; | ||
| 356 | header.Revision = _Revision; | ||
| 357 | header.ExpectedSize = (u32)sz; | ||
| 358 | |||
| 359 | // Write to file | ||
| 360 | if (!pFile.WriteArray(&header, 1)) | ||
| 361 | { | ||
| 362 | ERROR_LOG(COMMON,"ChunkReader: Failed writing header"); | ||
| 363 | return false; | ||
| 364 | } | ||
| 365 | |||
| 366 | if (!pFile.WriteArray(&buffer[0], sz)) | ||
| 367 | { | ||
| 368 | ERROR_LOG(COMMON,"ChunkReader: Failed writing data"); | ||
| 369 | return false; | ||
| 370 | } | ||
| 371 | |||
| 372 | INFO_LOG(COMMON,"ChunkReader: Done writing %s", _rFilename.c_str()); | ||
| 373 | return true; | ||
| 374 | } | ||
| 375 | |||
| 376 | private: | ||
| 377 | struct SChunkHeader | ||
| 378 | { | ||
| 379 | u32 Revision; | ||
| 380 | u32 ExpectedSize; | ||
| 381 | }; | ||
| 382 | }; | ||
| 383 | |||
| 384 | #endif // _POINTERWRAP_H_ | ||
diff --git a/src/common/src/common.h b/src/common/src/common.h new file mode 100644 index 000000000..f95d58074 --- /dev/null +++ b/src/common/src/common.h | |||
| @@ -0,0 +1,164 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _COMMON_H_ | ||
| 6 | #define _COMMON_H_ | ||
| 7 | |||
| 8 | // DO NOT EVER INCLUDE <windows.h> directly _or indirectly_ from this file | ||
| 9 | // since it slows down the build a lot. | ||
| 10 | |||
| 11 | #include <stdlib.h> | ||
| 12 | #include <stdio.h> | ||
| 13 | #include <string.h> | ||
| 14 | |||
| 15 | // SVN version number | ||
| 16 | extern const char *scm_rev_str; | ||
| 17 | extern const char *netplay_dolphin_ver; | ||
| 18 | |||
| 19 | // Force enable logging in the right modes. For some reason, something had changed | ||
| 20 | // so that debugfast no longer logged. | ||
| 21 | #if defined(_DEBUG) || defined(DEBUGFAST) | ||
| 22 | #undef LOGGING | ||
| 23 | #define LOGGING 1 | ||
| 24 | #endif | ||
| 25 | |||
| 26 | #define STACKALIGN | ||
| 27 | |||
| 28 | #if __cplusplus >= 201103 || defined(_MSC_VER) || defined(__GXX_EXPERIMENTAL_CXX0X__) | ||
| 29 | #define HAVE_CXX11_SYNTAX 1 | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #if HAVE_CXX11_SYNTAX | ||
| 33 | // An inheritable class to disallow the copy constructor and operator= functions | ||
| 34 | class NonCopyable | ||
| 35 | { | ||
| 36 | protected: | ||
| 37 | NonCopyable() {} | ||
| 38 | NonCopyable(const NonCopyable&&) {} | ||
| 39 | void operator=(const NonCopyable&&) {} | ||
| 40 | private: | ||
| 41 | NonCopyable(NonCopyable&); | ||
| 42 | NonCopyable& operator=(NonCopyable& other); | ||
| 43 | }; | ||
| 44 | #endif | ||
| 45 | |||
| 46 | #include "log.h" | ||
| 47 | #include "common_types.h" | ||
| 48 | #include "msg_handler.h" | ||
| 49 | #include "common_funcs.h" | ||
| 50 | |||
| 51 | #ifdef __APPLE__ | ||
| 52 | // The Darwin ABI requires that stack frames be aligned to 16-byte boundaries. | ||
| 53 | // This is only needed on i386 gcc - x86_64 already aligns to 16 bytes. | ||
| 54 | #if defined __i386__ && defined __GNUC__ | ||
| 55 | #undef STACKALIGN | ||
| 56 | #define STACKALIGN __attribute__((__force_align_arg_pointer__)) | ||
| 57 | #endif | ||
| 58 | |||
| 59 | #elif defined _WIN32 | ||
| 60 | |||
| 61 | // Check MSC ver | ||
| 62 | #if !defined _MSC_VER || _MSC_VER <= 1000 | ||
| 63 | #error needs at least version 1000 of MSC | ||
| 64 | #endif | ||
| 65 | |||
| 66 | #define NOMINMAX | ||
| 67 | |||
| 68 | // Memory leak checks | ||
| 69 | #define CHECK_HEAP_INTEGRITY() | ||
| 70 | |||
| 71 | // Alignment | ||
| 72 | #define GC_ALIGNED16(x) __declspec(align(16)) x | ||
| 73 | #define GC_ALIGNED32(x) __declspec(align(32)) x | ||
| 74 | #define GC_ALIGNED64(x) __declspec(align(64)) x | ||
| 75 | #define GC_ALIGNED128(x) __declspec(align(128)) x | ||
| 76 | #define GC_ALIGNED16_DECL(x) __declspec(align(16)) x | ||
| 77 | #define GC_ALIGNED64_DECL(x) __declspec(align(64)) x | ||
| 78 | |||
| 79 | // Since they are always around on windows | ||
| 80 | #define HAVE_WX 1 | ||
| 81 | #define HAVE_OPENAL 1 | ||
| 82 | |||
| 83 | #define HAVE_PORTAUDIO 1 | ||
| 84 | |||
| 85 | // Debug definitions | ||
| 86 | #if defined(_DEBUG) | ||
| 87 | #include <crtdbg.h> | ||
| 88 | #undef CHECK_HEAP_INTEGRITY | ||
| 89 | #define CHECK_HEAP_INTEGRITY() {if (!_CrtCheckMemory()) PanicAlert("memory corruption detected. see log.");} | ||
| 90 | // If you want to see how much a pain in the ass singletons are, for example: | ||
| 91 | // {614} normal block at 0x030C5310, 188 bytes long. | ||
| 92 | // Data: <Master Log > 4D 61 73 74 65 72 20 4C 6F 67 00 00 00 00 00 00 | ||
| 93 | struct CrtDebugBreak { CrtDebugBreak(int spot) { _CrtSetBreakAlloc(spot); } }; | ||
| 94 | //CrtDebugBreak breakAt(614); | ||
| 95 | #endif // end DEBUG/FAST | ||
| 96 | |||
| 97 | #endif | ||
| 98 | |||
| 99 | // Windows compatibility | ||
| 100 | #ifndef _WIN32 | ||
| 101 | #include <limits.h> | ||
| 102 | #define MAX_PATH PATH_MAX | ||
| 103 | #ifdef _LP64 | ||
| 104 | #define _M_X64 1 | ||
| 105 | #else | ||
| 106 | #define _M_IX86 1 | ||
| 107 | #endif | ||
| 108 | #define __forceinline inline __attribute__((always_inline)) | ||
| 109 | #define GC_ALIGNED16(x) __attribute__((aligned(16))) x | ||
| 110 | #define GC_ALIGNED32(x) __attribute__((aligned(32))) x | ||
| 111 | #define GC_ALIGNED64(x) __attribute__((aligned(64))) x | ||
| 112 | #define GC_ALIGNED128(x) __attribute__((aligned(128))) x | ||
| 113 | #define GC_ALIGNED16_DECL(x) __attribute__((aligned(16))) x | ||
| 114 | #define GC_ALIGNED64_DECL(x) __attribute__((aligned(64))) x | ||
| 115 | #endif | ||
| 116 | |||
| 117 | #ifdef _MSC_VER | ||
| 118 | #define __strdup _strdup | ||
| 119 | #define __getcwd _getcwd | ||
| 120 | #define __chdir _chdir | ||
| 121 | #else | ||
| 122 | #define __strdup strdup | ||
| 123 | #define __getcwd getcwd | ||
| 124 | #define __chdir chdir | ||
| 125 | #endif | ||
| 126 | |||
| 127 | // Dummy macro for marking translatable strings that can not be immediately translated. | ||
| 128 | // wxWidgets does not have a true dummy macro for this. | ||
| 129 | #define _trans(a) a | ||
| 130 | |||
| 131 | #if defined _M_GENERIC | ||
| 132 | # define _M_SSE 0x0 | ||
| 133 | #elif defined __GNUC__ | ||
| 134 | # if defined __SSE4_2__ | ||
| 135 | # define _M_SSE 0x402 | ||
| 136 | # elif defined __SSE4_1__ | ||
| 137 | # define _M_SSE 0x401 | ||
| 138 | # elif defined __SSSE3__ | ||
| 139 | # define _M_SSE 0x301 | ||
| 140 | # elif defined __SSE3__ | ||
| 141 | # define _M_SSE 0x300 | ||
| 142 | # endif | ||
| 143 | #elif (_MSC_VER >= 1500) || __INTEL_COMPILER // Visual Studio 2008 | ||
| 144 | # define _M_SSE 0x402 | ||
| 145 | #endif | ||
| 146 | |||
| 147 | // Host communication. | ||
| 148 | enum HOST_COMM | ||
| 149 | { | ||
| 150 | // Begin at 10 in case there is already messages with wParam = 0, 1, 2 and so on | ||
| 151 | WM_USER_STOP = 10, | ||
| 152 | WM_USER_CREATE, | ||
| 153 | WM_USER_SETCURSOR, | ||
| 154 | }; | ||
| 155 | |||
| 156 | // Used for notification on emulation state | ||
| 157 | enum EMUSTATE_CHANGE | ||
| 158 | { | ||
| 159 | EMUSTATE_CHANGE_PLAY = 1, | ||
| 160 | EMUSTATE_CHANGE_PAUSE, | ||
| 161 | EMUSTATE_CHANGE_STOP | ||
| 162 | }; | ||
| 163 | |||
| 164 | #endif // _COMMON_H_ | ||
diff --git a/src/common/src/common_funcs.h b/src/common/src/common_funcs.h new file mode 100644 index 000000000..73320a3ac --- /dev/null +++ b/src/common/src/common_funcs.h | |||
| @@ -0,0 +1,243 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _COMMONFUNCS_H_ | ||
| 6 | #define _COMMONFUNCS_H_ | ||
| 7 | |||
| 8 | #ifdef _WIN32 | ||
| 9 | #define SLEEP(x) Sleep(x) | ||
| 10 | #else | ||
| 11 | #include <unistd.h> | ||
| 12 | #define SLEEP(x) usleep(x*1000) | ||
| 13 | #endif | ||
| 14 | |||
| 15 | template <bool> struct CompileTimeAssert; | ||
| 16 | template<> struct CompileTimeAssert<true> {}; | ||
| 17 | |||
| 18 | #define b2(x) ( (x) | ( (x) >> 1) ) | ||
| 19 | #define b4(x) ( b2(x) | ( b2(x) >> 2) ) | ||
| 20 | #define b8(x) ( b4(x) | ( b4(x) >> 4) ) | ||
| 21 | #define b16(x) ( b8(x) | ( b8(x) >> 8) ) | ||
| 22 | #define b32(x) (b16(x) | (b16(x) >>16) ) | ||
| 23 | #define ROUND_UP_POW2(x) (b32(x - 1) + 1) | ||
| 24 | |||
| 25 | #if defined __GNUC__ && !defined __SSSE3__ && !defined _M_GENERIC | ||
| 26 | #include <emmintrin.h> | ||
| 27 | static __inline __m128i __attribute__((__always_inline__)) | ||
| 28 | _mm_shuffle_epi8(__m128i a, __m128i mask) | ||
| 29 | { | ||
| 30 | __m128i result; | ||
| 31 | __asm__("pshufb %1, %0" | ||
| 32 | : "=x" (result) | ||
| 33 | : "xm" (mask), "0" (a)); | ||
| 34 | return result; | ||
| 35 | } | ||
| 36 | #endif | ||
| 37 | |||
| 38 | #ifndef _WIN32 | ||
| 39 | |||
| 40 | #include <errno.h> | ||
| 41 | #ifdef __linux__ | ||
| 42 | #include <byteswap.h> | ||
| 43 | #elif defined __FreeBSD__ | ||
| 44 | #include <sys/endian.h> | ||
| 45 | #endif | ||
| 46 | |||
| 47 | // go to debugger mode | ||
| 48 | #ifdef GEKKO | ||
| 49 | #define Crash() | ||
| 50 | #elif defined _M_GENERIC | ||
| 51 | #define Crash() { exit(1); } | ||
| 52 | #else | ||
| 53 | #define Crash() {asm ("int $3");} | ||
| 54 | #endif | ||
| 55 | #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) | ||
| 56 | // GCC 4.8 defines all the rotate functions now | ||
| 57 | // Small issue with GCC's lrotl/lrotr intrinsics is they are still 32bit while we require 64bit | ||
| 58 | #ifndef _rotl | ||
| 59 | inline u32 _rotl(u32 x, int shift) { | ||
| 60 | shift &= 31; | ||
| 61 | if (!shift) return x; | ||
| 62 | return (x << shift) | (x >> (32 - shift)); | ||
| 63 | } | ||
| 64 | |||
| 65 | inline u32 _rotr(u32 x, int shift) { | ||
| 66 | shift &= 31; | ||
| 67 | if (!shift) return x; | ||
| 68 | return (x >> shift) | (x << (32 - shift)); | ||
| 69 | } | ||
| 70 | #endif | ||
| 71 | |||
| 72 | inline u64 _rotl64(u64 x, unsigned int shift){ | ||
| 73 | unsigned int n = shift % 64; | ||
| 74 | return (x << n) | (x >> (64 - n)); | ||
| 75 | } | ||
| 76 | |||
| 77 | inline u64 _rotr64(u64 x, unsigned int shift){ | ||
| 78 | unsigned int n = shift % 64; | ||
| 79 | return (x >> n) | (x << (64 - n)); | ||
| 80 | } | ||
| 81 | |||
| 82 | #else // WIN32 | ||
| 83 | // Function Cross-Compatibility | ||
| 84 | #define strcasecmp _stricmp | ||
| 85 | #define strncasecmp _strnicmp | ||
| 86 | #define unlink _unlink | ||
| 87 | #define snprintf _snprintf | ||
| 88 | #define vscprintf _vscprintf | ||
| 89 | |||
| 90 | // Locale Cross-Compatibility | ||
| 91 | #define locale_t _locale_t | ||
| 92 | #define freelocale _free_locale | ||
| 93 | #define newlocale(mask, locale, base) _create_locale(mask, locale) | ||
| 94 | |||
| 95 | #define LC_GLOBAL_LOCALE ((locale_t)-1) | ||
| 96 | #define LC_ALL_MASK LC_ALL | ||
| 97 | #define LC_COLLATE_MASK LC_COLLATE | ||
| 98 | #define LC_CTYPE_MASK LC_CTYPE | ||
| 99 | #define LC_MONETARY_MASK LC_MONETARY | ||
| 100 | #define LC_NUMERIC_MASK LC_NUMERIC | ||
| 101 | #define LC_TIME_MASK LC_TIME | ||
| 102 | |||
| 103 | inline locale_t uselocale(locale_t new_locale) | ||
| 104 | { | ||
| 105 | // Retrieve the current per thread locale setting | ||
| 106 | bool bIsPerThread = (_configthreadlocale(0) == _ENABLE_PER_THREAD_LOCALE); | ||
| 107 | |||
| 108 | // Retrieve the current thread-specific locale | ||
| 109 | locale_t old_locale = bIsPerThread ? _get_current_locale() : LC_GLOBAL_LOCALE; | ||
| 110 | |||
| 111 | if(new_locale == LC_GLOBAL_LOCALE) | ||
| 112 | { | ||
| 113 | // Restore the global locale | ||
| 114 | _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); | ||
| 115 | } | ||
| 116 | else if(new_locale != NULL) | ||
| 117 | { | ||
| 118 | // Configure the thread to set the locale only for this thread | ||
| 119 | _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); | ||
| 120 | |||
| 121 | // Set all locale categories | ||
| 122 | for(int i = LC_MIN; i <= LC_MAX; i++) | ||
| 123 | setlocale(i, new_locale->locinfo->lc_category[i].locale); | ||
| 124 | } | ||
| 125 | |||
| 126 | return old_locale; | ||
| 127 | } | ||
| 128 | |||
| 129 | // 64 bit offsets for windows | ||
| 130 | #define fseeko _fseeki64 | ||
| 131 | #define ftello _ftelli64 | ||
| 132 | #define atoll _atoi64 | ||
| 133 | #define stat64 _stat64 | ||
| 134 | #define fstat64 _fstat64 | ||
| 135 | #define fileno _fileno | ||
| 136 | |||
| 137 | #if _M_IX86 | ||
| 138 | #define Crash() {__asm int 3} | ||
| 139 | #else | ||
| 140 | extern "C" { | ||
| 141 | __declspec(dllimport) void __stdcall DebugBreak(void); | ||
| 142 | } | ||
| 143 | #define Crash() {DebugBreak();} | ||
| 144 | #endif // M_IX86 | ||
| 145 | #endif // WIN32 ndef | ||
| 146 | |||
| 147 | // Dolphin's min and max functions | ||
| 148 | #undef min | ||
| 149 | #undef max | ||
| 150 | |||
| 151 | template<class T> | ||
| 152 | inline T min(const T& a, const T& b) {return a > b ? b : a;} | ||
| 153 | template<class T> | ||
| 154 | inline T max(const T& a, const T& b) {return a > b ? a : b;} | ||
| 155 | |||
| 156 | // Generic function to get last error message. | ||
| 157 | // Call directly after the command or use the error num. | ||
| 158 | // This function might change the error code. | ||
| 159 | // Defined in Misc.cpp. | ||
| 160 | const char* GetLastErrorMsg(); | ||
| 161 | |||
| 162 | namespace Common | ||
| 163 | { | ||
| 164 | inline u8 swap8(u8 _data) {return _data;} | ||
| 165 | inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];} | ||
| 166 | |||
| 167 | #ifdef ANDROID | ||
| 168 | #undef swap16 | ||
| 169 | #undef swap32 | ||
| 170 | #undef swap64 | ||
| 171 | #endif | ||
| 172 | |||
| 173 | #ifdef _WIN32 | ||
| 174 | inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);} | ||
| 175 | inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);} | ||
| 176 | inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);} | ||
| 177 | #elif _M_ARM | ||
| 178 | inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;} | ||
| 179 | inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;} | ||
| 180 | inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);} | ||
| 181 | #elif __linux__ | ||
| 182 | inline u16 swap16(u16 _data) {return bswap_16(_data);} | ||
| 183 | inline u32 swap32(u32 _data) {return bswap_32(_data);} | ||
| 184 | inline u64 swap64(u64 _data) {return bswap_64(_data);} | ||
| 185 | #elif __APPLE__ | ||
| 186 | inline __attribute__((always_inline)) u16 swap16(u16 _data) | ||
| 187 | {return (_data >> 8) | (_data << 8);} | ||
| 188 | inline __attribute__((always_inline)) u32 swap32(u32 _data) | ||
| 189 | {return __builtin_bswap32(_data);} | ||
| 190 | inline __attribute__((always_inline)) u64 swap64(u64 _data) | ||
| 191 | {return __builtin_bswap64(_data);} | ||
| 192 | #elif __FreeBSD__ | ||
| 193 | inline u16 swap16(u16 _data) {return bswap16(_data);} | ||
| 194 | inline u32 swap32(u32 _data) {return bswap32(_data);} | ||
| 195 | inline u64 swap64(u64 _data) {return bswap64(_data);} | ||
| 196 | #else | ||
| 197 | // Slow generic implementation. | ||
| 198 | inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);} | ||
| 199 | inline u32 swap32(u32 data) {return (swap16(data) << 16) | swap16(data >> 16);} | ||
| 200 | inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 32);} | ||
| 201 | #endif | ||
| 202 | |||
| 203 | inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);} | ||
| 204 | inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);} | ||
| 205 | inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);} | ||
| 206 | |||
| 207 | template <int count> | ||
| 208 | void swap(u8*); | ||
| 209 | |||
| 210 | template <> | ||
| 211 | inline void swap<1>(u8* data) | ||
| 212 | {} | ||
| 213 | |||
| 214 | template <> | ||
| 215 | inline void swap<2>(u8* data) | ||
| 216 | { | ||
| 217 | *reinterpret_cast<u16*>(data) = swap16(data); | ||
| 218 | } | ||
| 219 | |||
| 220 | template <> | ||
| 221 | inline void swap<4>(u8* data) | ||
| 222 | { | ||
| 223 | *reinterpret_cast<u32*>(data) = swap32(data); | ||
| 224 | } | ||
| 225 | |||
| 226 | template <> | ||
| 227 | inline void swap<8>(u8* data) | ||
| 228 | { | ||
| 229 | *reinterpret_cast<u64*>(data) = swap64(data); | ||
| 230 | } | ||
| 231 | |||
| 232 | template <typename T> | ||
| 233 | inline T FromBigEndian(T data) | ||
| 234 | { | ||
| 235 | //static_assert(std::is_arithmetic<T>::value, "function only makes sense with arithmetic types"); | ||
| 236 | |||
| 237 | swap<sizeof(data)>(reinterpret_cast<u8*>(&data)); | ||
| 238 | return data; | ||
| 239 | } | ||
| 240 | |||
| 241 | } // Namespace Common | ||
| 242 | |||
| 243 | #endif // _COMMONFUNCS_H_ | ||
diff --git a/src/common/src/common_paths.h b/src/common/src/common_paths.h new file mode 100644 index 000000000..9af93ab74 --- /dev/null +++ b/src/common/src/common_paths.h | |||
| @@ -0,0 +1,71 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _COMMON_PATHS_H_ | ||
| 6 | #define _COMMON_PATHS_H_ | ||
| 7 | |||
| 8 | // Make sure we pick up USER_DIR if set in config.h | ||
| 9 | #include "common.h" | ||
| 10 | |||
| 11 | // Directory seperators, do we need this? | ||
| 12 | #define DIR_SEP "/" | ||
| 13 | #define DIR_SEP_CHR '/' | ||
| 14 | |||
| 15 | // The user data dir | ||
| 16 | #define ROOT_DIR "." | ||
| 17 | #ifdef _WIN32 | ||
| 18 | #define USERDATA_DIR "user" | ||
| 19 | #define DOLPHIN_DATA_DIR "akiru" | ||
| 20 | #else | ||
| 21 | #define USERDATA_DIR "user" | ||
| 22 | #ifdef USER_DIR | ||
| 23 | #define DOLPHIN_DATA_DIR USER_DIR | ||
| 24 | #else | ||
| 25 | #define DOLPHIN_DATA_DIR ".akiru" | ||
| 26 | #endif | ||
| 27 | #endif | ||
| 28 | |||
| 29 | // Shared data dirs (Sys and shared User for linux) | ||
| 30 | #ifdef _WIN32 | ||
| 31 | #define SYSDATA_DIR "sys" | ||
| 32 | #else | ||
| 33 | #ifdef DATA_DIR | ||
| 34 | #define SYSDATA_DIR DATA_DIR "sys" | ||
| 35 | #define SHARED_USER_DIR DATA_DIR USERDATA_DIR DIR_SEP | ||
| 36 | #else | ||
| 37 | #define SYSDATA_DIR "sys" | ||
| 38 | #define SHARED_USER_DIR ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP | ||
| 39 | #endif | ||
| 40 | #endif | ||
| 41 | |||
| 42 | // Dirs in both User and Sys | ||
| 43 | #define EUR_DIR "EUR" | ||
| 44 | #define USA_DIR "USA" | ||
| 45 | #define JAP_DIR "JAP" | ||
| 46 | |||
| 47 | // Subdirs in the User dir returned by GetUserPath(D_USER_IDX) | ||
| 48 | #define CONFIG_DIR "config" | ||
| 49 | #define GAMECONFIG_DIR "game_config" | ||
| 50 | #define MAPS_DIR "maps" | ||
| 51 | #define CACHE_DIR "cache" | ||
| 52 | #define SHADERCACHE_DIR "shader_cache" | ||
| 53 | #define STATESAVES_DIR "state_saves" | ||
| 54 | #define SCREENSHOTS_DIR "screenShots" | ||
| 55 | #define DUMP_DIR "dump" | ||
| 56 | #define DUMP_TEXTURES_DIR "textures" | ||
| 57 | #define DUMP_FRAMES_DIR "frames" | ||
| 58 | #define DUMP_AUDIO_DIR "audio" | ||
| 59 | #define LOGS_DIR "logs" | ||
| 60 | #define SHADERS_DIR "shaders" | ||
| 61 | |||
| 62 | // Filenames | ||
| 63 | // Files in the directory returned by GetUserPath(D_CONFIG_IDX) | ||
| 64 | #define AKIRU_CONFIG "akiru.ini" | ||
| 65 | #define DEBUGGER_CONFIG "debugger.ini" | ||
| 66 | #define LOGGER_CONFIG "logger.ini" | ||
| 67 | |||
| 68 | // Files in the directory returned by GetUserPath(D_LOGS_IDX) | ||
| 69 | #define MAIN_LOG "akiru.log" | ||
| 70 | |||
| 71 | #endif // _COMMON_PATHS_H_ | ||
diff --git a/src/common/src/common_types.h b/src/common/src/common_types.h new file mode 100644 index 000000000..5cb402131 --- /dev/null +++ b/src/common/src/common_types.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | // This header contains type definitions that are shared between the Dolphin core and | ||
| 7 | // other parts of the code. Any definitions that are only used by the core should be | ||
| 8 | // placed in "common.h" instead. | ||
| 9 | |||
| 10 | #ifndef _COMMONTYPES_H_ | ||
| 11 | #define _COMMONTYPES_H_ | ||
| 12 | |||
| 13 | #ifdef _WIN32 | ||
| 14 | |||
| 15 | #include <tchar.h> | ||
| 16 | |||
| 17 | typedef unsigned __int8 u8; | ||
| 18 | typedef unsigned __int16 u16; | ||
| 19 | typedef unsigned __int32 u32; | ||
| 20 | typedef unsigned __int64 u64; | ||
| 21 | |||
| 22 | typedef signed __int8 s8; | ||
| 23 | typedef signed __int16 s16; | ||
| 24 | typedef signed __int32 s32; | ||
| 25 | typedef signed __int64 s64; | ||
| 26 | |||
| 27 | #else | ||
| 28 | |||
| 29 | #ifndef GEKKO | ||
| 30 | |||
| 31 | typedef unsigned char u8; | ||
| 32 | typedef unsigned short u16; | ||
| 33 | typedef unsigned int u32; | ||
| 34 | typedef unsigned long long u64; | ||
| 35 | |||
| 36 | typedef signed char s8; | ||
| 37 | typedef signed short s16; | ||
| 38 | typedef signed int s32; | ||
| 39 | typedef signed long long s64; | ||
| 40 | |||
| 41 | #endif | ||
| 42 | // For using windows lock code | ||
| 43 | #define TCHAR char | ||
| 44 | #define LONG int | ||
| 45 | |||
| 46 | #endif // _WIN32 | ||
| 47 | |||
| 48 | #endif // _COMMONTYPES_H_ | ||
diff --git a/src/common/src/console_listener.cpp b/src/common/src/console_listener.cpp new file mode 100644 index 000000000..84f57675d --- /dev/null +++ b/src/common/src/console_listener.cpp | |||
| @@ -0,0 +1,337 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> // min | ||
| 6 | #include <string> // System: To be able to add strings with "+" | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <math.h> | ||
| 9 | #ifdef _WIN32 | ||
| 10 | #include <windows.h> | ||
| 11 | #include <array> | ||
| 12 | #else | ||
| 13 | #include <stdarg.h> | ||
| 14 | #endif | ||
| 15 | |||
| 16 | #include "common.h" | ||
| 17 | #include "log_manager.h" // Common | ||
| 18 | #include "console_listener.h" // Common | ||
| 19 | |||
| 20 | ConsoleListener::ConsoleListener() | ||
| 21 | { | ||
| 22 | #ifdef _WIN32 | ||
| 23 | hConsole = NULL; | ||
| 24 | bUseColor = true; | ||
| 25 | #else | ||
| 26 | bUseColor = isatty(fileno(stdout)); | ||
| 27 | #endif | ||
| 28 | } | ||
| 29 | |||
| 30 | ConsoleListener::~ConsoleListener() | ||
| 31 | { | ||
| 32 | Close(); | ||
| 33 | } | ||
| 34 | |||
| 35 | // 100, 100, "Dolphin Log Console" | ||
| 36 | // Open console window - width and height is the size of console window | ||
| 37 | // Name is the window title | ||
| 38 | void ConsoleListener::Open(bool Hidden, int Width, int Height, const char *Title) | ||
| 39 | { | ||
| 40 | #ifdef _WIN32 | ||
| 41 | if (!GetConsoleWindow()) | ||
| 42 | { | ||
| 43 | // Open the console window and create the window handle for GetStdHandle() | ||
| 44 | AllocConsole(); | ||
| 45 | // Hide | ||
| 46 | if (Hidden) ShowWindow(GetConsoleWindow(), SW_HIDE); | ||
| 47 | // Save the window handle that AllocConsole() created | ||
| 48 | hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 49 | // Set the console window title | ||
| 50 | SetConsoleTitle(UTF8ToTStr(Title).c_str()); | ||
| 51 | // Set letter space | ||
| 52 | LetterSpace(80, 4000); | ||
| 53 | //MoveWindow(GetConsoleWindow(), 200,200, 800,800, true); | ||
| 54 | } | ||
| 55 | else | ||
| 56 | { | ||
| 57 | hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 58 | } | ||
| 59 | #endif | ||
| 60 | } | ||
| 61 | |||
| 62 | void ConsoleListener::UpdateHandle() | ||
| 63 | { | ||
| 64 | #ifdef _WIN32 | ||
| 65 | hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 66 | #endif | ||
| 67 | } | ||
| 68 | |||
| 69 | // Close the console window and close the eventual file handle | ||
| 70 | void ConsoleListener::Close() | ||
| 71 | { | ||
| 72 | #ifdef _WIN32 | ||
| 73 | if (hConsole == NULL) | ||
| 74 | return; | ||
| 75 | FreeConsole(); | ||
| 76 | hConsole = NULL; | ||
| 77 | #else | ||
| 78 | fflush(NULL); | ||
| 79 | #endif | ||
| 80 | } | ||
| 81 | |||
| 82 | bool ConsoleListener::IsOpen() | ||
| 83 | { | ||
| 84 | #ifdef _WIN32 | ||
| 85 | return (hConsole != NULL); | ||
| 86 | #else | ||
| 87 | return true; | ||
| 88 | #endif | ||
| 89 | } | ||
| 90 | |||
| 91 | /* | ||
| 92 | LetterSpace: SetConsoleScreenBufferSize and SetConsoleWindowInfo are | ||
| 93 | dependent on each other, that's the reason for the additional checks. | ||
| 94 | */ | ||
| 95 | void ConsoleListener::BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst) | ||
| 96 | { | ||
| 97 | #ifdef _WIN32 | ||
| 98 | BOOL SB, SW; | ||
| 99 | if (BufferFirst) | ||
| 100 | { | ||
| 101 | // Change screen buffer size | ||
| 102 | COORD Co = {BufferWidth, BufferHeight}; | ||
| 103 | SB = SetConsoleScreenBufferSize(hConsole, Co); | ||
| 104 | // Change the screen buffer window size | ||
| 105 | SMALL_RECT coo = {0,0,ScreenWidth, ScreenHeight}; // top, left, right, bottom | ||
| 106 | SW = SetConsoleWindowInfo(hConsole, TRUE, &coo); | ||
| 107 | } | ||
| 108 | else | ||
| 109 | { | ||
| 110 | // Change the screen buffer window size | ||
| 111 | SMALL_RECT coo = {0,0, ScreenWidth, ScreenHeight}; // top, left, right, bottom | ||
| 112 | SW = SetConsoleWindowInfo(hConsole, TRUE, &coo); | ||
| 113 | // Change screen buffer size | ||
| 114 | COORD Co = {BufferWidth, BufferHeight}; | ||
| 115 | SB = SetConsoleScreenBufferSize(hConsole, Co); | ||
| 116 | } | ||
| 117 | #endif | ||
| 118 | } | ||
| 119 | void ConsoleListener::LetterSpace(int Width, int Height) | ||
| 120 | { | ||
| 121 | #ifdef _WIN32 | ||
| 122 | // Get console info | ||
| 123 | CONSOLE_SCREEN_BUFFER_INFO ConInfo; | ||
| 124 | GetConsoleScreenBufferInfo(hConsole, &ConInfo); | ||
| 125 | |||
| 126 | // | ||
| 127 | int OldBufferWidth = ConInfo.dwSize.X; | ||
| 128 | int OldBufferHeight = ConInfo.dwSize.Y; | ||
| 129 | int OldScreenWidth = (ConInfo.srWindow.Right - ConInfo.srWindow.Left); | ||
| 130 | int OldScreenHeight = (ConInfo.srWindow.Bottom - ConInfo.srWindow.Top); | ||
| 131 | // | ||
| 132 | int NewBufferWidth = Width; | ||
| 133 | int NewBufferHeight = Height; | ||
| 134 | int NewScreenWidth = NewBufferWidth - 1; | ||
| 135 | int NewScreenHeight = OldScreenHeight; | ||
| 136 | |||
| 137 | // Width | ||
| 138 | BufferWidthHeight(NewBufferWidth, OldBufferHeight, NewScreenWidth, OldScreenHeight, (NewBufferWidth > OldScreenWidth-1)); | ||
| 139 | // Height | ||
| 140 | BufferWidthHeight(NewBufferWidth, NewBufferHeight, NewScreenWidth, NewScreenHeight, (NewBufferHeight > OldScreenHeight-1)); | ||
| 141 | |||
| 142 | // Resize the window too | ||
| 143 | //MoveWindow(GetConsoleWindow(), 200,200, (Width*8 + 50),(NewScreenHeight*12 + 200), true); | ||
| 144 | #endif | ||
| 145 | } | ||
| 146 | #ifdef _WIN32 | ||
| 147 | COORD ConsoleListener::GetCoordinates(int BytesRead, int BufferWidth) | ||
| 148 | { | ||
| 149 | COORD Ret = {0, 0}; | ||
| 150 | // Full rows | ||
| 151 | int Step = (int)floor((float)BytesRead / (float)BufferWidth); | ||
| 152 | Ret.Y += Step; | ||
| 153 | // Partial row | ||
| 154 | Ret.X = BytesRead - (BufferWidth * Step); | ||
| 155 | return Ret; | ||
| 156 | } | ||
| 157 | #endif | ||
| 158 | void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool Resize) | ||
| 159 | { | ||
| 160 | #ifdef _WIN32 | ||
| 161 | // Check size | ||
| 162 | if (Width < 8 || Height < 12) return; | ||
| 163 | |||
| 164 | bool DBef = true; | ||
| 165 | bool DAft = true; | ||
| 166 | std::string SLog = ""; | ||
| 167 | |||
| 168 | const HWND hWnd = GetConsoleWindow(); | ||
| 169 | const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 170 | |||
| 171 | // Get console info | ||
| 172 | CONSOLE_SCREEN_BUFFER_INFO ConInfo; | ||
| 173 | GetConsoleScreenBufferInfo(hConsole, &ConInfo); | ||
| 174 | DWORD BufferSize = ConInfo.dwSize.X * ConInfo.dwSize.Y; | ||
| 175 | |||
| 176 | // --------------------------------------------------------------------- | ||
| 177 | // Save the current text | ||
| 178 | // ------------------------ | ||
| 179 | DWORD cCharsRead = 0; | ||
| 180 | COORD coordScreen = { 0, 0 }; | ||
| 181 | |||
| 182 | static const int MAX_BYTES = 1024 * 16; | ||
| 183 | |||
| 184 | std::vector<std::array<TCHAR, MAX_BYTES>> Str; | ||
| 185 | std::vector<std::array<WORD, MAX_BYTES>> Attr; | ||
| 186 | |||
| 187 | // ReadConsoleOutputAttribute seems to have a limit at this level | ||
| 188 | static const int ReadBufferSize = MAX_BYTES - 32; | ||
| 189 | |||
| 190 | DWORD cAttrRead = ReadBufferSize; | ||
| 191 | DWORD BytesRead = 0; | ||
| 192 | while (BytesRead < BufferSize) | ||
| 193 | { | ||
| 194 | Str.resize(Str.size() + 1); | ||
| 195 | if (!ReadConsoleOutputCharacter(hConsole, Str.back().data(), ReadBufferSize, coordScreen, &cCharsRead)) | ||
| 196 | SLog += StringFromFormat("WriteConsoleOutputCharacter error"); | ||
| 197 | |||
| 198 | Attr.resize(Attr.size() + 1); | ||
| 199 | if (!ReadConsoleOutputAttribute(hConsole, Attr.back().data(), ReadBufferSize, coordScreen, &cAttrRead)) | ||
| 200 | SLog += StringFromFormat("WriteConsoleOutputAttribute error"); | ||
| 201 | |||
| 202 | // Break on error | ||
| 203 | if (cAttrRead == 0) break; | ||
| 204 | BytesRead += cAttrRead; | ||
| 205 | coordScreen = GetCoordinates(BytesRead, ConInfo.dwSize.X); | ||
| 206 | } | ||
| 207 | // Letter space | ||
| 208 | int LWidth = (int)(floor((float)Width / 8.0f) - 1.0f); | ||
| 209 | int LHeight = (int)(floor((float)Height / 12.0f) - 1.0f); | ||
| 210 | int LBufWidth = LWidth + 1; | ||
| 211 | int LBufHeight = (int)floor((float)BufferSize / (float)LBufWidth); | ||
| 212 | // Change screen buffer size | ||
| 213 | LetterSpace(LBufWidth, LBufHeight); | ||
| 214 | |||
| 215 | |||
| 216 | ClearScreen(true); | ||
| 217 | coordScreen.Y = 0; | ||
| 218 | coordScreen.X = 0; | ||
| 219 | DWORD cCharsWritten = 0; | ||
| 220 | |||
| 221 | int BytesWritten = 0; | ||
| 222 | DWORD cAttrWritten = 0; | ||
| 223 | for (size_t i = 0; i < Attr.size(); i++) | ||
| 224 | { | ||
| 225 | if (!WriteConsoleOutputCharacter(hConsole, Str[i].data(), ReadBufferSize, coordScreen, &cCharsWritten)) | ||
| 226 | SLog += StringFromFormat("WriteConsoleOutputCharacter error"); | ||
| 227 | if (!WriteConsoleOutputAttribute(hConsole, Attr[i].data(), ReadBufferSize, coordScreen, &cAttrWritten)) | ||
| 228 | SLog += StringFromFormat("WriteConsoleOutputAttribute error"); | ||
| 229 | |||
| 230 | BytesWritten += cAttrWritten; | ||
| 231 | coordScreen = GetCoordinates(BytesWritten, LBufWidth); | ||
| 232 | } | ||
| 233 | |||
| 234 | const int OldCursor = ConInfo.dwCursorPosition.Y * ConInfo.dwSize.X + ConInfo.dwCursorPosition.X; | ||
| 235 | COORD Coo = GetCoordinates(OldCursor, LBufWidth); | ||
| 236 | SetConsoleCursorPosition(hConsole, Coo); | ||
| 237 | |||
| 238 | if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str()); | ||
| 239 | |||
| 240 | // Resize the window too | ||
| 241 | if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true); | ||
| 242 | #endif | ||
| 243 | } | ||
| 244 | |||
| 245 | void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text) | ||
| 246 | { | ||
| 247 | #if defined(_WIN32) | ||
| 248 | /* | ||
| 249 | const int MAX_BYTES = 1024*10; | ||
| 250 | char Str[MAX_BYTES]; | ||
| 251 | va_list ArgPtr; | ||
| 252 | int Cnt; | ||
| 253 | va_start(ArgPtr, Text); | ||
| 254 | Cnt = vsnprintf(Str, MAX_BYTES, Text, ArgPtr); | ||
| 255 | va_end(ArgPtr); | ||
| 256 | */ | ||
| 257 | DWORD cCharsWritten; | ||
| 258 | WORD Color; | ||
| 259 | |||
| 260 | switch (Level) | ||
| 261 | { | ||
| 262 | case NOTICE_LEVEL: // light green | ||
| 263 | Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY; | ||
| 264 | break; | ||
| 265 | case ERROR_LEVEL: // light red | ||
| 266 | Color = FOREGROUND_RED | FOREGROUND_INTENSITY; | ||
| 267 | break; | ||
| 268 | case WARNING_LEVEL: // light yellow | ||
| 269 | Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; | ||
| 270 | break; | ||
| 271 | case INFO_LEVEL: // cyan | ||
| 272 | Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; | ||
| 273 | break; | ||
| 274 | case DEBUG_LEVEL: // gray | ||
| 275 | Color = FOREGROUND_INTENSITY; | ||
| 276 | break; | ||
| 277 | default: // off-white | ||
| 278 | Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; | ||
| 279 | break; | ||
| 280 | } | ||
| 281 | if (strlen(Text) > 10) | ||
| 282 | { | ||
| 283 | // First 10 chars white | ||
| 284 | SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); | ||
| 285 | WriteConsole(hConsole, Text, 10, &cCharsWritten, NULL); | ||
| 286 | Text += 10; | ||
| 287 | } | ||
| 288 | SetConsoleTextAttribute(hConsole, Color); | ||
| 289 | WriteConsole(hConsole, Text, (DWORD)strlen(Text), &cCharsWritten, NULL); | ||
| 290 | #else | ||
| 291 | char ColorAttr[16] = ""; | ||
| 292 | char ResetAttr[16] = ""; | ||
| 293 | |||
| 294 | if (bUseColor) | ||
| 295 | { | ||
| 296 | strcpy(ResetAttr, "\033[0m"); | ||
| 297 | switch (Level) | ||
| 298 | { | ||
| 299 | case NOTICE_LEVEL: // light green | ||
| 300 | strcpy(ColorAttr, "\033[92m"); | ||
| 301 | break; | ||
| 302 | case ERROR_LEVEL: // light red | ||
| 303 | strcpy(ColorAttr, "\033[91m"); | ||
| 304 | break; | ||
| 305 | case WARNING_LEVEL: // light yellow | ||
| 306 | strcpy(ColorAttr, "\033[93m"); | ||
| 307 | break; | ||
| 308 | default: | ||
| 309 | break; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr); | ||
| 313 | #endif | ||
| 314 | } | ||
| 315 | // Clear console screen | ||
| 316 | void ConsoleListener::ClearScreen(bool Cursor) | ||
| 317 | { | ||
| 318 | #if defined(_WIN32) | ||
| 319 | COORD coordScreen = { 0, 0 }; | ||
| 320 | DWORD cCharsWritten; | ||
| 321 | CONSOLE_SCREEN_BUFFER_INFO csbi; | ||
| 322 | DWORD dwConSize; | ||
| 323 | |||
| 324 | HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 325 | |||
| 326 | GetConsoleScreenBufferInfo(hConsole, &csbi); | ||
| 327 | dwConSize = csbi.dwSize.X * csbi.dwSize.Y; | ||
| 328 | // Write space to the entire console | ||
| 329 | FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten); | ||
| 330 | GetConsoleScreenBufferInfo(hConsole, &csbi); | ||
| 331 | FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten); | ||
| 332 | // Reset cursor | ||
| 333 | if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen); | ||
| 334 | #endif | ||
| 335 | } | ||
| 336 | |||
| 337 | |||
diff --git a/src/common/src/console_listener.h b/src/common/src/console_listener.h new file mode 100644 index 000000000..ab5c00980 --- /dev/null +++ b/src/common/src/console_listener.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _CONSOLELISTENER_H | ||
| 6 | #define _CONSOLELISTENER_H | ||
| 7 | |||
| 8 | #include "log_manager.h" | ||
| 9 | |||
| 10 | #ifdef _WIN32 | ||
| 11 | #include <windows.h> | ||
| 12 | #endif | ||
| 13 | |||
| 14 | class ConsoleListener : public LogListener | ||
| 15 | { | ||
| 16 | public: | ||
| 17 | ConsoleListener(); | ||
| 18 | ~ConsoleListener(); | ||
| 19 | |||
| 20 | void Open(bool Hidden = false, int Width = 100, int Height = 100, const char * Name = "Console"); | ||
| 21 | void UpdateHandle(); | ||
| 22 | void Close(); | ||
| 23 | bool IsOpen(); | ||
| 24 | void LetterSpace(int Width, int Height); | ||
| 25 | void BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst); | ||
| 26 | void PixelSpace(int Left, int Top, int Width, int Height, bool); | ||
| 27 | #ifdef _WIN32 | ||
| 28 | COORD GetCoordinates(int BytesRead, int BufferWidth); | ||
| 29 | #endif | ||
| 30 | void Log(LogTypes::LOG_LEVELS, const char *Text); | ||
| 31 | void ClearScreen(bool Cursor = true); | ||
| 32 | |||
| 33 | private: | ||
| 34 | #ifdef _WIN32 | ||
| 35 | HWND GetHwnd(void); | ||
| 36 | HANDLE hConsole; | ||
| 37 | #endif | ||
| 38 | bool bUseColor; | ||
| 39 | }; | ||
| 40 | |||
| 41 | #endif // _CONSOLELISTENER_H | ||
diff --git a/src/common/src/cpu_detect.h b/src/common/src/cpu_detect.h new file mode 100644 index 000000000..be6ce3498 --- /dev/null +++ b/src/common/src/cpu_detect.h | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | // Detect the cpu, so we'll know which optimizations to use | ||
| 7 | #ifndef _CPUDETECT_H_ | ||
| 8 | #define _CPUDETECT_H_ | ||
| 9 | |||
| 10 | #include <string> | ||
| 11 | |||
| 12 | enum CPUVendor | ||
| 13 | { | ||
| 14 | VENDOR_INTEL = 0, | ||
| 15 | VENDOR_AMD = 1, | ||
| 16 | VENDOR_ARM = 2, | ||
| 17 | VENDOR_OTHER = 3, | ||
| 18 | }; | ||
| 19 | |||
| 20 | struct CPUInfo | ||
| 21 | { | ||
| 22 | CPUVendor vendor; | ||
| 23 | |||
| 24 | char cpu_string[0x21]; | ||
| 25 | char brand_string[0x41]; | ||
| 26 | bool OS64bit; | ||
| 27 | bool CPU64bit; | ||
| 28 | bool Mode64bit; | ||
| 29 | |||
| 30 | bool HTT; | ||
| 31 | int num_cores; | ||
| 32 | int logical_cpu_count; | ||
| 33 | |||
| 34 | bool bSSE; | ||
| 35 | bool bSSE2; | ||
| 36 | bool bSSE3; | ||
| 37 | bool bSSSE3; | ||
| 38 | bool bPOPCNT; | ||
| 39 | bool bSSE4_1; | ||
| 40 | bool bSSE4_2; | ||
| 41 | bool bLZCNT; | ||
| 42 | bool bSSE4A; | ||
| 43 | bool bAVX; | ||
| 44 | bool bAES; | ||
| 45 | bool bLAHFSAHF64; | ||
| 46 | bool bLongMode; | ||
| 47 | |||
| 48 | // ARM specific CPUInfo | ||
| 49 | bool bSwp; | ||
| 50 | bool bHalf; | ||
| 51 | bool bThumb; | ||
| 52 | bool bFastMult; | ||
| 53 | bool bVFP; | ||
| 54 | bool bEDSP; | ||
| 55 | bool bThumbEE; | ||
| 56 | bool bNEON; | ||
| 57 | bool bVFPv3; | ||
| 58 | bool bTLS; | ||
| 59 | bool bVFPv4; | ||
| 60 | bool bIDIVa; | ||
| 61 | bool bIDIVt; | ||
| 62 | bool bArmV7; // enable MOVT, MOVW etc | ||
| 63 | |||
| 64 | // ARMv8 specific | ||
| 65 | bool bFP; | ||
| 66 | bool bASIMD; | ||
| 67 | |||
| 68 | // Call Detect() | ||
| 69 | explicit CPUInfo(); | ||
| 70 | |||
| 71 | // Turn the cpu info into a string we can show | ||
| 72 | std::string Summarize(); | ||
| 73 | |||
| 74 | private: | ||
| 75 | // Detects the various cpu features | ||
| 76 | void Detect(); | ||
| 77 | }; | ||
| 78 | |||
| 79 | extern CPUInfo cpu_info; | ||
| 80 | |||
| 81 | #endif // _CPUDETECT_H_ | ||
diff --git a/src/common/src/debug_interface.h b/src/common/src/debug_interface.h new file mode 100644 index 000000000..317cd0bb4 --- /dev/null +++ b/src/common/src/debug_interface.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #ifndef _DEBUGINTERFACE_H | ||
| 2 | #define _DEBUGINTERFACE_H | ||
| 3 | |||
| 4 | #include <string> | ||
| 5 | #include <string.h> | ||
| 6 | |||
| 7 | class DebugInterface | ||
| 8 | { | ||
| 9 | protected: | ||
| 10 | virtual ~DebugInterface() {} | ||
| 11 | |||
| 12 | public: | ||
| 13 | virtual void disasm(unsigned int /*address*/, char *dest, int /*max_size*/) {strcpy(dest, "NODEBUGGER");} | ||
| 14 | virtual void getRawMemoryString(int /*memory*/, unsigned int /*address*/, char *dest, int /*max_size*/) {strcpy(dest, "NODEBUGGER");} | ||
| 15 | virtual int getInstructionSize(int /*instruction*/) {return 1;} | ||
| 16 | virtual bool isAlive() {return true;} | ||
| 17 | virtual bool isBreakpoint(unsigned int /*address*/) {return false;} | ||
| 18 | virtual void setBreakpoint(unsigned int /*address*/){} | ||
| 19 | virtual void clearBreakpoint(unsigned int /*address*/){} | ||
| 20 | virtual void clearAllBreakpoints() {} | ||
| 21 | virtual void toggleBreakpoint(unsigned int /*address*/){} | ||
| 22 | virtual bool isMemCheck(unsigned int /*address*/) {return false;} | ||
| 23 | virtual void toggleMemCheck(unsigned int /*address*/){} | ||
| 24 | virtual unsigned int readMemory(unsigned int /*address*/){return 0;} | ||
| 25 | virtual void writeExtraMemory(int /*memory*/, unsigned int /*value*/, unsigned int /*address*/) {} | ||
| 26 | virtual unsigned int readExtraMemory(int /*memory*/, unsigned int /*address*/){return 0;} | ||
| 27 | virtual unsigned int readInstruction(unsigned int /*address*/){return 0;} | ||
| 28 | virtual unsigned int getPC() {return 0;} | ||
| 29 | virtual void setPC(unsigned int /*address*/) {} | ||
| 30 | virtual void step() {} | ||
| 31 | virtual void runToBreakpoint() {} | ||
| 32 | virtual void breakNow() {} | ||
| 33 | virtual void insertBLR(unsigned int /*address*/, unsigned int /*value*/) {} | ||
| 34 | virtual void showJitResults(unsigned int /*address*/) {}; | ||
| 35 | virtual int getColor(unsigned int /*address*/){return 0xFFFFFFFF;} | ||
| 36 | virtual std::string getDescription(unsigned int /*address*/) = 0; | ||
| 37 | }; | ||
| 38 | |||
| 39 | #endif | ||
diff --git a/src/common/src/extended_trace.cpp b/src/common/src/extended_trace.cpp new file mode 100644 index 000000000..44815343d --- /dev/null +++ b/src/common/src/extended_trace.cpp | |||
| @@ -0,0 +1,433 @@ | |||
| 1 | // -------------------------------------------------------------------------------------- | ||
| 2 | // | ||
| 3 | // Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com | ||
| 4 | // For companies(Austin,TX): If you would like to get my resume, send an email. | ||
| 5 | // | ||
| 6 | // The source is free, but if you want to use it, mention my name and e-mail address | ||
| 7 | // | ||
| 8 | // History: | ||
| 9 | // 1.0 Initial version Zoltan Csizmadia | ||
| 10 | // 1.1 WhineCube version Masken | ||
| 11 | // 1.2 Dolphin version Masken | ||
| 12 | // | ||
| 13 | // -------------------------------------------------------------------------------------- | ||
| 14 | |||
| 15 | #if defined(WIN32) | ||
| 16 | |||
| 17 | #include <windows.h> | ||
| 18 | #include <stdio.h> | ||
| 19 | #include "extended_trace.h" | ||
| 20 | #include "string_util.h" | ||
| 21 | using namespace std; | ||
| 22 | |||
| 23 | #include <tchar.h> | ||
| 24 | #include <ImageHlp.h> | ||
| 25 | |||
| 26 | #define BUFFERSIZE 0x200 | ||
| 27 | #pragma warning(disable:4996) | ||
| 28 | |||
| 29 | // Unicode safe char* -> TCHAR* conversion | ||
| 30 | void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut ) | ||
| 31 | { | ||
| 32 | #if defined(UNICODE)||defined(_UNICODE) | ||
| 33 | ULONG index = 0; | ||
| 34 | PCSTR lpAct = lpszIn; | ||
| 35 | |||
| 36 | for( ; ; lpAct++ ) | ||
| 37 | { | ||
| 38 | lpszOut[index++] = (TCHAR)(*lpAct); | ||
| 39 | if ( *lpAct == 0 ) | ||
| 40 | break; | ||
| 41 | } | ||
| 42 | #else | ||
| 43 | // This is trivial :) | ||
| 44 | strcpy( lpszOut, lpszIn ); | ||
| 45 | #endif | ||
| 46 | } | ||
| 47 | |||
| 48 | // Let's figure out the path for the symbol files | ||
| 49 | // Search path= ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + lpszIniPath | ||
| 50 | // Note: There is no size check for lpszSymbolPath! | ||
| 51 | static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath ) | ||
| 52 | { | ||
| 53 | CHAR lpszPath[BUFFERSIZE]; | ||
| 54 | |||
| 55 | // Creating the default path | ||
| 56 | // ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" | ||
| 57 | strcpy( lpszSymbolPath, "." ); | ||
| 58 | |||
| 59 | // environment variable _NT_SYMBOL_PATH | ||
| 60 | if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE ) ) | ||
| 61 | { | ||
| 62 | strcat( lpszSymbolPath, ";" ); | ||
| 63 | strcat( lpszSymbolPath, lpszPath ); | ||
| 64 | } | ||
| 65 | |||
| 66 | // environment variable _NT_ALTERNATE_SYMBOL_PATH | ||
| 67 | if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", lpszPath, BUFFERSIZE ) ) | ||
| 68 | { | ||
| 69 | strcat( lpszSymbolPath, ";" ); | ||
| 70 | strcat( lpszSymbolPath, lpszPath ); | ||
| 71 | } | ||
| 72 | |||
| 73 | // environment variable SYSTEMROOT | ||
| 74 | if ( GetEnvironmentVariableA( "SYSTEMROOT", lpszPath, BUFFERSIZE ) ) | ||
| 75 | { | ||
| 76 | strcat( lpszSymbolPath, ";" ); | ||
| 77 | strcat( lpszSymbolPath, lpszPath ); | ||
| 78 | strcat( lpszSymbolPath, ";" ); | ||
| 79 | |||
| 80 | // SYSTEMROOT\System32 | ||
| 81 | strcat( lpszSymbolPath, lpszPath ); | ||
| 82 | strcat( lpszSymbolPath, "\\System32" ); | ||
| 83 | } | ||
| 84 | |||
| 85 | // Add user defined path | ||
| 86 | if ( lpszIniPath != NULL ) | ||
| 87 | if ( lpszIniPath[0] != '\0' ) | ||
| 88 | { | ||
| 89 | strcat( lpszSymbolPath, ";" ); | ||
| 90 | strcat( lpszSymbolPath, lpszIniPath ); | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | // Uninitialize the loaded symbol files | ||
| 95 | BOOL UninitSymInfo() { | ||
| 96 | return SymCleanup( GetCurrentProcess() ); | ||
| 97 | } | ||
| 98 | |||
| 99 | // Initializes the symbol files | ||
| 100 | BOOL InitSymInfo( PCSTR lpszInitialSymbolPath ) | ||
| 101 | { | ||
| 102 | CHAR lpszSymbolPath[BUFFERSIZE]; | ||
| 103 | DWORD symOptions = SymGetOptions(); | ||
| 104 | |||
| 105 | symOptions |= SYMOPT_LOAD_LINES; | ||
| 106 | symOptions &= ~SYMOPT_UNDNAME; | ||
| 107 | SymSetOptions( symOptions ); | ||
| 108 | InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath ); | ||
| 109 | |||
| 110 | return SymInitialize( GetCurrentProcess(), lpszSymbolPath, TRUE); | ||
| 111 | } | ||
| 112 | |||
| 113 | // Get the module name from a given address | ||
| 114 | static BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule ) | ||
| 115 | { | ||
| 116 | BOOL ret = FALSE; | ||
| 117 | IMAGEHLP_MODULE moduleInfo; | ||
| 118 | |||
| 119 | ::ZeroMemory( &moduleInfo, sizeof(moduleInfo) ); | ||
| 120 | moduleInfo.SizeOfStruct = sizeof(moduleInfo); | ||
| 121 | |||
| 122 | if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) ) | ||
| 123 | { | ||
| 124 | // Got it! | ||
| 125 | PCSTR2LPTSTR( moduleInfo.ModuleName, lpszModule ); | ||
| 126 | ret = TRUE; | ||
| 127 | } | ||
| 128 | else | ||
| 129 | // Not found :( | ||
| 130 | _tcscpy( lpszModule, _T("?") ); | ||
| 131 | |||
| 132 | return ret; | ||
| 133 | } | ||
| 134 | |||
| 135 | // Get function prototype and parameter info from ip address and stack address | ||
| 136 | static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol ) | ||
| 137 | { | ||
| 138 | BOOL ret = FALSE; | ||
| 139 | DWORD dwSymSize = 10000; | ||
| 140 | TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?"); | ||
| 141 | CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; | ||
| 142 | LPTSTR lpszParamSep = NULL; | ||
| 143 | LPTSTR lpszParsed = lpszUnDSymbol; | ||
| 144 | PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize ); | ||
| 145 | |||
| 146 | ::ZeroMemory( pSym, dwSymSize ); | ||
| 147 | pSym->SizeOfStruct = dwSymSize; | ||
| 148 | pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL); | ||
| 149 | |||
| 150 | // Set the default to unknown | ||
| 151 | _tcscpy( lpszSymbol, _T("?") ); | ||
| 152 | |||
| 153 | // Get symbol info for IP | ||
| 154 | #ifndef _M_X64 | ||
| 155 | DWORD dwDisp = 0; | ||
| 156 | if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) ) | ||
| 157 | #else | ||
| 158 | //makes it compile but hell im not sure if this works... | ||
| 159 | DWORD64 dwDisp = 0; | ||
| 160 | if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) ) | ||
| 161 | #endif | ||
| 162 | { | ||
| 163 | // Make the symbol readable for humans | ||
| 164 | UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, | ||
| 165 | UNDNAME_COMPLETE | | ||
| 166 | UNDNAME_NO_THISTYPE | | ||
| 167 | UNDNAME_NO_SPECIAL_SYMS | | ||
| 168 | UNDNAME_NO_MEMBER_TYPE | | ||
| 169 | UNDNAME_NO_MS_KEYWORDS | | ||
| 170 | UNDNAME_NO_ACCESS_SPECIFIERS ); | ||
| 171 | |||
| 172 | // Symbol information is ANSI string | ||
| 173 | PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol ); | ||
| 174 | |||
| 175 | // I am just smarter than the symbol file :) | ||
| 176 | if ( _tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0 ) | ||
| 177 | _tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)")); | ||
| 178 | else | ||
| 179 | if ( _tcscmp(lpszUnDSymbol, _T("_main")) == 0 ) | ||
| 180 | _tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)")); | ||
| 181 | else | ||
| 182 | if ( _tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0 ) | ||
| 183 | _tcscpy(lpszUnDSymbol, _T("mainCRTStartup()")); | ||
| 184 | else | ||
| 185 | if ( _tcscmp(lpszUnDSymbol, _T("_wmain")) == 0 ) | ||
| 186 | _tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)")); | ||
| 187 | else | ||
| 188 | if ( _tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0 ) | ||
| 189 | _tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()")); | ||
| 190 | |||
| 191 | lpszSymbol[0] = _T('\0'); | ||
| 192 | |||
| 193 | // Let's go through the stack, and modify the function prototype, and insert the actual | ||
| 194 | // parameter values from the stack | ||
| 195 | if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL) | ||
| 196 | { | ||
| 197 | ULONG index = 0; | ||
| 198 | for( ; ; index++ ) | ||
| 199 | { | ||
| 200 | lpszParamSep = _tcschr( lpszParsed, _T(',') ); | ||
| 201 | if ( lpszParamSep == NULL ) | ||
| 202 | break; | ||
| 203 | |||
| 204 | *lpszParamSep = _T('\0'); | ||
| 205 | |||
| 206 | _tcscat( lpszSymbol, lpszParsed ); | ||
| 207 | _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) ); | ||
| 208 | |||
| 209 | lpszParsed = lpszParamSep + 1; | ||
| 210 | } | ||
| 211 | |||
| 212 | lpszParamSep = _tcschr( lpszParsed, _T(')') ); | ||
| 213 | if ( lpszParamSep != NULL ) | ||
| 214 | { | ||
| 215 | *lpszParamSep = _T('\0'); | ||
| 216 | |||
| 217 | _tcscat( lpszSymbol, lpszParsed ); | ||
| 218 | _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) ); | ||
| 219 | |||
| 220 | lpszParsed = lpszParamSep + 1; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | _tcscat( lpszSymbol, lpszParsed ); | ||
| 225 | |||
| 226 | ret = TRUE; | ||
| 227 | } | ||
| 228 | GlobalFree( pSym ); | ||
| 229 | |||
| 230 | return ret; | ||
| 231 | } | ||
| 232 | |||
| 233 | // Get source file name and line number from IP address | ||
| 234 | // The output format is: "sourcefile(linenumber)" or | ||
| 235 | // "modulename!address" or | ||
| 236 | // "address" | ||
| 237 | static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo ) | ||
| 238 | { | ||
| 239 | BOOL ret = FALSE; | ||
| 240 | IMAGEHLP_LINE lineInfo; | ||
| 241 | DWORD dwDisp; | ||
| 242 | TCHAR lpszFileName[BUFFERSIZE] = _T(""); | ||
| 243 | TCHAR lpModuleInfo[BUFFERSIZE] = _T(""); | ||
| 244 | |||
| 245 | _tcscpy( lpszSourceInfo, _T("?(?)") ); | ||
| 246 | |||
| 247 | ::ZeroMemory( &lineInfo, sizeof( lineInfo ) ); | ||
| 248 | lineInfo.SizeOfStruct = sizeof( lineInfo ); | ||
| 249 | |||
| 250 | if ( SymGetLineFromAddr( GetCurrentProcess(), address, &dwDisp, &lineInfo ) ) | ||
| 251 | { | ||
| 252 | // Got it. Let's use "sourcefile(linenumber)" format | ||
| 253 | PCSTR2LPTSTR( lineInfo.FileName, lpszFileName ); | ||
| 254 | TCHAR fname[_MAX_FNAME]; | ||
| 255 | TCHAR ext[_MAX_EXT]; | ||
| 256 | _tsplitpath(lpszFileName, NULL, NULL, fname, ext); | ||
| 257 | _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber ); | ||
| 258 | ret = TRUE; | ||
| 259 | } | ||
| 260 | else | ||
| 261 | { | ||
| 262 | // There is no source file information. :( | ||
| 263 | // Let's use the "modulename!address" format | ||
| 264 | GetModuleNameFromAddress( address, lpModuleInfo ); | ||
| 265 | |||
| 266 | if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0')) | ||
| 267 | // There is no modulename information. :(( | ||
| 268 | // Let's use the "address" format | ||
| 269 | _stprintf( lpszSourceInfo, _T("0x%08X"), address ); | ||
| 270 | else | ||
| 271 | _stprintf( lpszSourceInfo, _T("%s!0x%08X"), lpModuleInfo, address ); | ||
| 272 | |||
| 273 | ret = FALSE; | ||
| 274 | } | ||
| 275 | |||
| 276 | return ret; | ||
| 277 | } | ||
| 278 | |||
| 279 | void PrintFunctionAndSourceInfo(FILE* file, const STACKFRAME& callstack) | ||
| 280 | { | ||
| 281 | TCHAR symInfo[BUFFERSIZE] = _T("?"); | ||
| 282 | TCHAR srcInfo[BUFFERSIZE] = _T("?"); | ||
| 283 | |||
| 284 | GetFunctionInfoFromAddresses((ULONG)callstack.AddrPC.Offset, (ULONG)callstack.AddrFrame.Offset, symInfo); | ||
| 285 | GetSourceInfoFromAddress((ULONG)callstack.AddrPC.Offset, srcInfo); | ||
| 286 | etfprint(file, " " + TStrToUTF8(srcInfo) + " : " + TStrToUTF8(symInfo) + "\n"); | ||
| 287 | } | ||
| 288 | |||
| 289 | void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file ) | ||
| 290 | { | ||
| 291 | STACKFRAME callStack; | ||
| 292 | BOOL bResult; | ||
| 293 | CONTEXT context; | ||
| 294 | HANDLE hProcess = GetCurrentProcess(); | ||
| 295 | |||
| 296 | // If it's not this thread, let's suspend it, and resume it at the end | ||
| 297 | if ( hThread != GetCurrentThread() ) | ||
| 298 | if ( SuspendThread( hThread ) == -1 ) | ||
| 299 | { | ||
| 300 | // whaaat ?! | ||
| 301 | etfprint(file, "Call stack info failed\n"); | ||
| 302 | return; | ||
| 303 | } | ||
| 304 | |||
| 305 | ::ZeroMemory( &context, sizeof(context) ); | ||
| 306 | context.ContextFlags = CONTEXT_FULL; | ||
| 307 | |||
| 308 | if ( !GetThreadContext( hThread, &context ) ) | ||
| 309 | { | ||
| 310 | etfprint(file, "Call stack info failed\n"); | ||
| 311 | return; | ||
| 312 | } | ||
| 313 | |||
| 314 | ::ZeroMemory( &callStack, sizeof(callStack) ); | ||
| 315 | #ifndef _M_X64 | ||
| 316 | callStack.AddrPC.Offset = context.Eip; | ||
| 317 | callStack.AddrStack.Offset = context.Esp; | ||
| 318 | callStack.AddrFrame.Offset = context.Ebp; | ||
| 319 | #else | ||
| 320 | callStack.AddrPC.Offset = context.Rip; | ||
| 321 | callStack.AddrStack.Offset = context.Rsp; | ||
| 322 | callStack.AddrFrame.Offset = context.Rbp; | ||
| 323 | #endif | ||
| 324 | callStack.AddrPC.Mode = AddrModeFlat; | ||
| 325 | callStack.AddrStack.Mode = AddrModeFlat; | ||
| 326 | callStack.AddrFrame.Mode = AddrModeFlat; | ||
| 327 | |||
| 328 | etfprint(file, "Call stack info: \n"); | ||
| 329 | etfprint(file, lpszMessage); | ||
| 330 | |||
| 331 | PrintFunctionAndSourceInfo(file, callStack); | ||
| 332 | |||
| 333 | for( ULONG index = 0; ; index++ ) | ||
| 334 | { | ||
| 335 | bResult = StackWalk( | ||
| 336 | IMAGE_FILE_MACHINE_I386, | ||
| 337 | hProcess, | ||
| 338 | hThread, | ||
| 339 | &callStack, | ||
| 340 | NULL, | ||
| 341 | NULL, | ||
| 342 | SymFunctionTableAccess, | ||
| 343 | SymGetModuleBase, | ||
| 344 | NULL); | ||
| 345 | |||
| 346 | if ( index == 0 ) | ||
| 347 | continue; | ||
| 348 | |||
| 349 | if( !bResult || callStack.AddrFrame.Offset == 0 ) | ||
| 350 | break; | ||
| 351 | |||
| 352 | PrintFunctionAndSourceInfo(file, callStack); | ||
| 353 | |||
| 354 | } | ||
| 355 | |||
| 356 | if ( hThread != GetCurrentThread() ) | ||
| 357 | ResumeThread( hThread ); | ||
| 358 | } | ||
| 359 | |||
| 360 | void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip, DWORD esp, DWORD ebp ) | ||
| 361 | { | ||
| 362 | STACKFRAME callStack; | ||
| 363 | BOOL bResult; | ||
| 364 | TCHAR symInfo[BUFFERSIZE] = _T("?"); | ||
| 365 | TCHAR srcInfo[BUFFERSIZE] = _T("?"); | ||
| 366 | HANDLE hProcess = GetCurrentProcess(); | ||
| 367 | |||
| 368 | // If it's not this thread, let's suspend it, and resume it at the end | ||
| 369 | if ( hThread != GetCurrentThread() ) | ||
| 370 | if ( SuspendThread( hThread ) == -1 ) | ||
| 371 | { | ||
| 372 | // whaaat ?! | ||
| 373 | etfprint(file, "Call stack info failed\n"); | ||
| 374 | return; | ||
| 375 | } | ||
| 376 | |||
| 377 | ::ZeroMemory( &callStack, sizeof(callStack) ); | ||
| 378 | callStack.AddrPC.Offset = eip; | ||
| 379 | callStack.AddrStack.Offset = esp; | ||
| 380 | callStack.AddrFrame.Offset = ebp; | ||
| 381 | callStack.AddrPC.Mode = AddrModeFlat; | ||
| 382 | callStack.AddrStack.Mode = AddrModeFlat; | ||
| 383 | callStack.AddrFrame.Mode = AddrModeFlat; | ||
| 384 | |||
| 385 | etfprint(file, "Call stack info: \n"); | ||
| 386 | etfprint(file, lpszMessage); | ||
| 387 | |||
| 388 | PrintFunctionAndSourceInfo(file, callStack); | ||
| 389 | |||
| 390 | for( ULONG index = 0; ; index++ ) | ||
| 391 | { | ||
| 392 | bResult = StackWalk( | ||
| 393 | IMAGE_FILE_MACHINE_I386, | ||
| 394 | hProcess, | ||
| 395 | hThread, | ||
| 396 | &callStack, | ||
| 397 | NULL, | ||
| 398 | NULL, | ||
| 399 | SymFunctionTableAccess, | ||
| 400 | SymGetModuleBase, | ||
| 401 | NULL); | ||
| 402 | |||
| 403 | if ( index == 0 ) | ||
| 404 | continue; | ||
| 405 | |||
| 406 | if( !bResult || callStack.AddrFrame.Offset == 0 ) | ||
| 407 | break; | ||
| 408 | |||
| 409 | PrintFunctionAndSourceInfo(file, callStack); | ||
| 410 | } | ||
| 411 | |||
| 412 | if ( hThread != GetCurrentThread() ) | ||
| 413 | ResumeThread( hThread ); | ||
| 414 | } | ||
| 415 | |||
| 416 | char g_uefbuf[2048]; | ||
| 417 | |||
| 418 | void etfprintf(FILE *file, const char *format, ...) | ||
| 419 | { | ||
| 420 | va_list ap; | ||
| 421 | va_start(ap, format); | ||
| 422 | int len = vsprintf(g_uefbuf, format, ap); | ||
| 423 | fwrite(g_uefbuf, 1, len, file); | ||
| 424 | va_end(ap); | ||
| 425 | } | ||
| 426 | |||
| 427 | void etfprint(FILE *file, const std::string &text) | ||
| 428 | { | ||
| 429 | size_t len = text.length(); | ||
| 430 | fwrite(text.data(), 1, len, file); | ||
| 431 | } | ||
| 432 | |||
| 433 | #endif //WIN32 | ||
diff --git a/src/common/src/extended_trace.h b/src/common/src/extended_trace.h new file mode 100644 index 000000000..6d33eb632 --- /dev/null +++ b/src/common/src/extended_trace.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // ----------------------------------------------------------------------------------------- | ||
| 2 | // | ||
| 3 | // Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com | ||
| 4 | // For companies(Austin,TX): If you would like to get my resume, send an email. | ||
| 5 | // | ||
| 6 | // The source is free, but if you want to use it, mention my name and e-mail address | ||
| 7 | // | ||
| 8 | // History: | ||
| 9 | // 1.0 Initial version Zoltan Csizmadia | ||
| 10 | // 1.1 WhineCube version Masken | ||
| 11 | // 1.2 Dolphin version Masken | ||
| 12 | // | ||
| 13 | // ---------------------------------------------------------------------------------------- | ||
| 14 | |||
| 15 | #ifndef _EXTENDEDTRACE_H_INCLUDED_ | ||
| 16 | #define _EXTENDEDTRACE_H_INCLUDED_ | ||
| 17 | |||
| 18 | #if defined(WIN32) | ||
| 19 | |||
| 20 | #include <windows.h> | ||
| 21 | #include <tchar.h> | ||
| 22 | |||
| 23 | #include <string> | ||
| 24 | |||
| 25 | #pragma comment( lib, "imagehlp.lib" ) | ||
| 26 | |||
| 27 | #define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) InitSymInfo( IniSymbolPath ) | ||
| 28 | #define EXTENDEDTRACEUNINITIALIZE() UninitSymInfo() | ||
| 29 | #define STACKTRACE(file) StackTrace( GetCurrentThread(), "", file) | ||
| 30 | #define STACKTRACE2(file, eip, esp, ebp) StackTrace(GetCurrentThread(), "", file, eip, esp, ebp) | ||
| 31 | // class File; | ||
| 32 | |||
| 33 | BOOL InitSymInfo( PCSTR ); | ||
| 34 | BOOL UninitSymInfo(); | ||
| 35 | void StackTrace(HANDLE, char const* msg, FILE *file); | ||
| 36 | void StackTrace(HANDLE, char const* msg, FILE *file, DWORD eip, DWORD esp, DWORD ebp); | ||
| 37 | |||
| 38 | // functions by Masken | ||
| 39 | void etfprintf(FILE *file, const char *format, ...); | ||
| 40 | void etfprint(FILE *file, const std::string &text); | ||
| 41 | #define UEFBUFSIZE 2048 | ||
| 42 | extern char g_uefbuf[UEFBUFSIZE]; | ||
| 43 | |||
| 44 | #else // not WIN32 | ||
| 45 | |||
| 46 | #define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) ((void)0) | ||
| 47 | #define EXTENDEDTRACEUNINITIALIZE() ((void)0) | ||
| 48 | #define STACKTRACE(file) ((void)0) | ||
| 49 | #define STACKTRACE2(file, eip, esp, ebp) ((void)0) | ||
| 50 | |||
| 51 | #endif // WIN32 | ||
| 52 | |||
| 53 | #endif // _EXTENDEDTRACE_H_INCLUDED_ | ||
diff --git a/src/common/src/fifo_queue.h b/src/common/src/fifo_queue.h new file mode 100644 index 000000000..4f5ca5706 --- /dev/null +++ b/src/common/src/fifo_queue.h | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | |||
| 2 | #ifndef _FIFO_QUEUE_H_ | ||
| 3 | #define _FIFO_QUEUE_H_ | ||
| 4 | |||
| 5 | // a simple lockless thread-safe, | ||
| 6 | // single reader, single writer queue | ||
| 7 | |||
| 8 | #include "atomic.h" | ||
| 9 | |||
| 10 | namespace Common | ||
| 11 | { | ||
| 12 | |||
| 13 | template <typename T> | ||
| 14 | class FifoQueue | ||
| 15 | { | ||
| 16 | public: | ||
| 17 | FifoQueue() : m_size(0) | ||
| 18 | { | ||
| 19 | m_write_ptr = m_read_ptr = new ElementPtr(); | ||
| 20 | } | ||
| 21 | |||
| 22 | ~FifoQueue() | ||
| 23 | { | ||
| 24 | // this will empty out the whole queue | ||
| 25 | delete m_read_ptr; | ||
| 26 | } | ||
| 27 | |||
| 28 | u32 Size() const | ||
| 29 | { | ||
| 30 | return m_size; | ||
| 31 | } | ||
| 32 | |||
| 33 | bool Empty() const | ||
| 34 | { | ||
| 35 | //return (m_read_ptr == m_write_ptr); | ||
| 36 | return (0 == m_size); | ||
| 37 | } | ||
| 38 | |||
| 39 | T& Front() const | ||
| 40 | { | ||
| 41 | return *m_read_ptr->current; | ||
| 42 | } | ||
| 43 | |||
| 44 | template <typename Arg> | ||
| 45 | void Push(Arg&& t) | ||
| 46 | { | ||
| 47 | // create the element, add it to the queue | ||
| 48 | m_write_ptr->current = new T(std::forward<Arg>(t)); | ||
| 49 | // set the next pointer to a new element ptr | ||
| 50 | // then advance the write pointer | ||
| 51 | m_write_ptr = m_write_ptr->next = new ElementPtr(); | ||
| 52 | Common::AtomicIncrement(m_size); | ||
| 53 | } | ||
| 54 | |||
| 55 | void Pop() | ||
| 56 | { | ||
| 57 | Common::AtomicDecrement(m_size); | ||
| 58 | ElementPtr *const tmpptr = m_read_ptr; | ||
| 59 | // advance the read pointer | ||
| 60 | m_read_ptr = m_read_ptr->next; | ||
| 61 | // set the next element to NULL to stop the recursive deletion | ||
| 62 | tmpptr->next = NULL; | ||
| 63 | delete tmpptr; // this also deletes the element | ||
| 64 | } | ||
| 65 | |||
| 66 | bool Pop(T& t) | ||
| 67 | { | ||
| 68 | if (Empty()) | ||
| 69 | return false; | ||
| 70 | |||
| 71 | t = std::move(Front()); | ||
| 72 | Pop(); | ||
| 73 | |||
| 74 | return true; | ||
| 75 | } | ||
| 76 | |||
| 77 | // not thread-safe | ||
| 78 | void Clear() | ||
| 79 | { | ||
| 80 | m_size = 0; | ||
| 81 | delete m_read_ptr; | ||
| 82 | m_write_ptr = m_read_ptr = new ElementPtr(); | ||
| 83 | } | ||
| 84 | |||
| 85 | private: | ||
| 86 | // stores a pointer to element | ||
| 87 | // and a pointer to the next ElementPtr | ||
| 88 | class ElementPtr | ||
| 89 | { | ||
| 90 | public: | ||
| 91 | ElementPtr() : current(NULL), next(NULL) {} | ||
| 92 | |||
| 93 | ~ElementPtr() | ||
| 94 | { | ||
| 95 | if (current) | ||
| 96 | { | ||
| 97 | delete current; | ||
| 98 | // recusion ftw | ||
| 99 | if (next) | ||
| 100 | delete next; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | T *volatile current; | ||
| 105 | ElementPtr *volatile next; | ||
| 106 | }; | ||
| 107 | |||
| 108 | ElementPtr *volatile m_write_ptr; | ||
| 109 | ElementPtr *volatile m_read_ptr; | ||
| 110 | volatile u32 m_size; | ||
| 111 | }; | ||
| 112 | |||
| 113 | } | ||
| 114 | |||
| 115 | #endif | ||
diff --git a/src/common/src/file_search.cpp b/src/common/src/file_search.cpp new file mode 100644 index 000000000..ba140ec12 --- /dev/null +++ b/src/common/src/file_search.cpp | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #include "common.h" | ||
| 7 | #include "common_paths.h" | ||
| 8 | #ifndef _WIN32 | ||
| 9 | #include <sys/types.h> | ||
| 10 | #include <dirent.h> | ||
| 11 | #else | ||
| 12 | #include <windows.h> | ||
| 13 | #endif | ||
| 14 | |||
| 15 | #include <string> | ||
| 16 | #include <algorithm> | ||
| 17 | |||
| 18 | #include "file_search.h" | ||
| 19 | |||
| 20 | #include "string_util.h" | ||
| 21 | |||
| 22 | |||
| 23 | CFileSearch::CFileSearch(const CFileSearch::XStringVector& _rSearchStrings, const CFileSearch::XStringVector& _rDirectories) | ||
| 24 | { | ||
| 25 | // Reverse the loop order for speed? | ||
| 26 | for (size_t j = 0; j < _rSearchStrings.size(); j++) | ||
| 27 | { | ||
| 28 | for (size_t i = 0; i < _rDirectories.size(); i++) | ||
| 29 | { | ||
| 30 | FindFiles(_rSearchStrings[j], _rDirectories[i]); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | |||
| 36 | void CFileSearch::FindFiles(const std::string& _searchString, const std::string& _strPath) | ||
| 37 | { | ||
| 38 | std::string GCMSearchPath; | ||
| 39 | BuildCompleteFilename(GCMSearchPath, _strPath, _searchString); | ||
| 40 | #ifdef _WIN32 | ||
| 41 | WIN32_FIND_DATA findData; | ||
| 42 | HANDLE FindFirst = FindFirstFile(UTF8ToTStr(GCMSearchPath).c_str(), &findData); | ||
| 43 | |||
| 44 | if (FindFirst != INVALID_HANDLE_VALUE) | ||
| 45 | { | ||
| 46 | bool bkeepLooping = true; | ||
| 47 | |||
| 48 | while (bkeepLooping) | ||
| 49 | { | ||
| 50 | if (findData.cFileName[0] != '.') | ||
| 51 | { | ||
| 52 | std::string strFilename; | ||
| 53 | BuildCompleteFilename(strFilename, _strPath, TStrToUTF8(findData.cFileName)); | ||
| 54 | m_FileNames.push_back(strFilename); | ||
| 55 | } | ||
| 56 | |||
| 57 | bkeepLooping = FindNextFile(FindFirst, &findData) ? true : false; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | FindClose(FindFirst); | ||
| 61 | |||
| 62 | |||
| 63 | #else | ||
| 64 | // TODO: super lame/broken | ||
| 65 | |||
| 66 | auto end_match(_searchString); | ||
| 67 | |||
| 68 | // assuming we have a "*.blah"-like pattern | ||
| 69 | if (!end_match.empty() && end_match[0] == '*') | ||
| 70 | end_match.erase(0, 1); | ||
| 71 | |||
| 72 | // ugly | ||
| 73 | if (end_match == ".*") | ||
| 74 | end_match.clear(); | ||
| 75 | |||
| 76 | DIR* dir = opendir(_strPath.c_str()); | ||
| 77 | |||
| 78 | if (!dir) | ||
| 79 | return; | ||
| 80 | |||
| 81 | while (auto const dp = readdir(dir)) | ||
| 82 | { | ||
| 83 | std::string found(dp->d_name); | ||
| 84 | |||
| 85 | if ((found != ".") && (found != "..") | ||
| 86 | && (found.size() >= end_match.size()) | ||
| 87 | && std::equal(end_match.rbegin(), end_match.rend(), found.rbegin())) | ||
| 88 | { | ||
| 89 | std::string full_name; | ||
| 90 | if (_strPath.c_str()[_strPath.size()-1] == DIR_SEP_CHR) | ||
| 91 | full_name = _strPath + found; | ||
| 92 | else | ||
| 93 | full_name = _strPath + DIR_SEP + found; | ||
| 94 | |||
| 95 | m_FileNames.push_back(full_name); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | closedir(dir); | ||
| 100 | #endif | ||
| 101 | } | ||
| 102 | |||
| 103 | const CFileSearch::XStringVector& CFileSearch::GetFileNames() const | ||
| 104 | { | ||
| 105 | return m_FileNames; | ||
| 106 | } | ||
diff --git a/src/common/src/file_search.h b/src/common/src/file_search.h new file mode 100644 index 000000000..55aaf4ebe --- /dev/null +++ b/src/common/src/file_search.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #ifndef _FILESEARCH_H_ | ||
| 7 | #define _FILESEARCH_H_ | ||
| 8 | |||
| 9 | #include <string> | ||
| 10 | #include <vector> | ||
| 11 | |||
| 12 | class CFileSearch | ||
| 13 | { | ||
| 14 | public: | ||
| 15 | typedef std::vector<std::string>XStringVector; | ||
| 16 | |||
| 17 | CFileSearch(const XStringVector& _rSearchStrings, const XStringVector& _rDirectories); | ||
| 18 | const XStringVector& GetFileNames() const; | ||
| 19 | |||
| 20 | private: | ||
| 21 | |||
| 22 | void FindFiles(const std::string& _searchString, const std::string& _strPath); | ||
| 23 | |||
| 24 | XStringVector m_FileNames; | ||
| 25 | }; | ||
| 26 | |||
| 27 | #endif // _FILESEARCH_H_ | ||
| 28 | |||
diff --git a/src/common/src/file_util.cpp b/src/common/src/file_util.cpp new file mode 100644 index 000000000..f86414bf3 --- /dev/null +++ b/src/common/src/file_util.cpp | |||
| @@ -0,0 +1,922 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #include "common.h" | ||
| 7 | #include "common_paths.h" | ||
| 8 | #include "file_util.h" | ||
| 9 | #include "string_util.h" | ||
| 10 | |||
| 11 | #ifdef _WIN32 | ||
| 12 | #include <windows.h> | ||
| 13 | #include <shlobj.h> // for SHGetFolderPath | ||
| 14 | #include <shellapi.h> | ||
| 15 | #include <commdlg.h> // for GetSaveFileName | ||
| 16 | #include <io.h> | ||
| 17 | #include <direct.h> // getcwd | ||
| 18 | #else | ||
| 19 | #include <sys/param.h> | ||
| 20 | #include <sys/types.h> | ||
| 21 | #include <dirent.h> | ||
| 22 | #include <errno.h> | ||
| 23 | #include <stdlib.h> | ||
| 24 | #endif | ||
| 25 | |||
| 26 | #if defined(__APPLE__) | ||
| 27 | #include <CoreFoundation/CFString.h> | ||
| 28 | #include <CoreFoundation/CFURL.h> | ||
| 29 | #include <CoreFoundation/CFBundle.h> | ||
| 30 | #endif | ||
| 31 | |||
| 32 | #include <algorithm> | ||
| 33 | #include <sys/stat.h> | ||
| 34 | |||
| 35 | #include "string_util.h" | ||
| 36 | |||
| 37 | #ifndef S_ISDIR | ||
| 38 | #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) | ||
| 39 | #endif | ||
| 40 | |||
| 41 | #ifdef BSD4_4 | ||
| 42 | #define stat64 stat | ||
| 43 | #define fstat64 fstat | ||
| 44 | #endif | ||
| 45 | |||
| 46 | // This namespace has various generic functions related to files and paths. | ||
| 47 | // The code still needs a ton of cleanup. | ||
| 48 | // REMEMBER: strdup considered harmful! | ||
| 49 | namespace File | ||
| 50 | { | ||
| 51 | |||
| 52 | // Remove any ending forward slashes from directory paths | ||
| 53 | // Modifies argument. | ||
| 54 | static void StripTailDirSlashes(std::string &fname) | ||
| 55 | { | ||
| 56 | if (fname.length() > 1) | ||
| 57 | { | ||
| 58 | size_t i = fname.length() - 1; | ||
| 59 | while (fname[i] == DIR_SEP_CHR) | ||
| 60 | fname[i--] = '\0'; | ||
| 61 | } | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | |||
| 65 | // Returns true if file filename exists | ||
| 66 | bool Exists(const std::string &filename) | ||
| 67 | { | ||
| 68 | struct stat64 file_info; | ||
| 69 | |||
| 70 | std::string copy(filename); | ||
| 71 | StripTailDirSlashes(copy); | ||
| 72 | |||
| 73 | #ifdef _WIN32 | ||
| 74 | int result = _tstat64(UTF8ToTStr(copy).c_str(), &file_info); | ||
| 75 | #else | ||
| 76 | int result = stat64(copy.c_str(), &file_info); | ||
| 77 | #endif | ||
| 78 | |||
| 79 | return (result == 0); | ||
| 80 | } | ||
| 81 | |||
| 82 | // Returns true if filename is a directory | ||
| 83 | bool IsDirectory(const std::string &filename) | ||
| 84 | { | ||
| 85 | struct stat64 file_info; | ||
| 86 | |||
| 87 | std::string copy(filename); | ||
| 88 | StripTailDirSlashes(copy); | ||
| 89 | |||
| 90 | #ifdef _WIN32 | ||
| 91 | int result = _tstat64(UTF8ToTStr(copy).c_str(), &file_info); | ||
| 92 | #else | ||
| 93 | int result = stat64(copy.c_str(), &file_info); | ||
| 94 | #endif | ||
| 95 | |||
| 96 | if (result < 0) { | ||
| 97 | WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s", | ||
| 98 | filename.c_str(), GetLastErrorMsg()); | ||
| 99 | return false; | ||
| 100 | } | ||
| 101 | |||
| 102 | return S_ISDIR(file_info.st_mode); | ||
| 103 | } | ||
| 104 | |||
| 105 | // Deletes a given filename, return true on success | ||
| 106 | // Doesn't supports deleting a directory | ||
| 107 | bool Delete(const std::string &filename) | ||
| 108 | { | ||
| 109 | INFO_LOG(COMMON, "Delete: file %s", filename.c_str()); | ||
| 110 | |||
| 111 | // Return true because we care about the file no | ||
| 112 | // being there, not the actual delete. | ||
| 113 | if (!Exists(filename)) | ||
| 114 | { | ||
| 115 | WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str()); | ||
| 116 | return true; | ||
| 117 | } | ||
| 118 | |||
| 119 | // We can't delete a directory | ||
| 120 | if (IsDirectory(filename)) | ||
| 121 | { | ||
| 122 | WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str()); | ||
| 123 | return false; | ||
| 124 | } | ||
| 125 | |||
| 126 | #ifdef _WIN32 | ||
| 127 | if (!DeleteFile(UTF8ToTStr(filename).c_str())) | ||
| 128 | { | ||
| 129 | WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s", | ||
| 130 | filename.c_str(), GetLastErrorMsg()); | ||
| 131 | return false; | ||
| 132 | } | ||
| 133 | #else | ||
| 134 | if (unlink(filename.c_str()) == -1) { | ||
| 135 | WARN_LOG(COMMON, "Delete: unlink failed on %s: %s", | ||
| 136 | filename.c_str(), GetLastErrorMsg()); | ||
| 137 | return false; | ||
| 138 | } | ||
| 139 | #endif | ||
| 140 | |||
| 141 | return true; | ||
| 142 | } | ||
| 143 | |||
| 144 | // Returns true if successful, or path already exists. | ||
| 145 | bool CreateDir(const std::string &path) | ||
| 146 | { | ||
| 147 | INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str()); | ||
| 148 | #ifdef _WIN32 | ||
| 149 | if (::CreateDirectory(UTF8ToTStr(path).c_str(), NULL)) | ||
| 150 | return true; | ||
| 151 | DWORD error = GetLastError(); | ||
| 152 | if (error == ERROR_ALREADY_EXISTS) | ||
| 153 | { | ||
| 154 | WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str()); | ||
| 155 | return true; | ||
| 156 | } | ||
| 157 | ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error); | ||
| 158 | return false; | ||
| 159 | #else | ||
| 160 | if (mkdir(path.c_str(), 0755) == 0) | ||
| 161 | return true; | ||
| 162 | |||
| 163 | int err = errno; | ||
| 164 | |||
| 165 | if (err == EEXIST) | ||
| 166 | { | ||
| 167 | WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str()); | ||
| 168 | return true; | ||
| 169 | } | ||
| 170 | |||
| 171 | ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err)); | ||
| 172 | return false; | ||
| 173 | #endif | ||
| 174 | } | ||
| 175 | |||
| 176 | // Creates the full path of fullPath returns true on success | ||
| 177 | bool CreateFullPath(const std::string &fullPath) | ||
| 178 | { | ||
| 179 | int panicCounter = 100; | ||
| 180 | INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str()); | ||
| 181 | |||
| 182 | if (File::Exists(fullPath)) | ||
| 183 | { | ||
| 184 | INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str()); | ||
| 185 | return true; | ||
| 186 | } | ||
| 187 | |||
| 188 | size_t position = 0; | ||
| 189 | while (true) | ||
| 190 | { | ||
| 191 | // Find next sub path | ||
| 192 | position = fullPath.find(DIR_SEP_CHR, position); | ||
| 193 | |||
| 194 | // we're done, yay! | ||
| 195 | if (position == fullPath.npos) | ||
| 196 | return true; | ||
| 197 | |||
| 198 | // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") | ||
| 199 | std::string const subPath(fullPath.substr(0, position + 1)); | ||
| 200 | if (!File::IsDirectory(subPath)) | ||
| 201 | File::CreateDir(subPath); | ||
| 202 | |||
| 203 | // A safety check | ||
| 204 | panicCounter--; | ||
| 205 | if (panicCounter <= 0) | ||
| 206 | { | ||
| 207 | ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep"); | ||
| 208 | return false; | ||
| 209 | } | ||
| 210 | position++; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | |||
| 215 | // Deletes a directory filename, returns true on success | ||
| 216 | bool DeleteDir(const std::string &filename) | ||
| 217 | { | ||
| 218 | INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str()); | ||
| 219 | |||
| 220 | // check if a directory | ||
| 221 | if (!File::IsDirectory(filename)) | ||
| 222 | { | ||
| 223 | ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str()); | ||
| 224 | return false; | ||
| 225 | } | ||
| 226 | |||
| 227 | #ifdef _WIN32 | ||
| 228 | if (::RemoveDirectory(UTF8ToTStr(filename).c_str())) | ||
| 229 | return true; | ||
| 230 | #else | ||
| 231 | if (rmdir(filename.c_str()) == 0) | ||
| 232 | return true; | ||
| 233 | #endif | ||
| 234 | ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg()); | ||
| 235 | |||
| 236 | return false; | ||
| 237 | } | ||
| 238 | |||
| 239 | // renames file srcFilename to destFilename, returns true on success | ||
| 240 | bool Rename(const std::string &srcFilename, const std::string &destFilename) | ||
| 241 | { | ||
| 242 | INFO_LOG(COMMON, "Rename: %s --> %s", | ||
| 243 | srcFilename.c_str(), destFilename.c_str()); | ||
| 244 | if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) | ||
| 245 | return true; | ||
| 246 | ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s", | ||
| 247 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 248 | return false; | ||
| 249 | } | ||
| 250 | |||
| 251 | // copies file srcFilename to destFilename, returns true on success | ||
| 252 | bool Copy(const std::string &srcFilename, const std::string &destFilename) | ||
| 253 | { | ||
| 254 | INFO_LOG(COMMON, "Copy: %s --> %s", | ||
| 255 | srcFilename.c_str(), destFilename.c_str()); | ||
| 256 | #ifdef _WIN32 | ||
| 257 | if (CopyFile(UTF8ToTStr(srcFilename).c_str(), UTF8ToTStr(destFilename).c_str(), FALSE)) | ||
| 258 | return true; | ||
| 259 | |||
| 260 | ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s", | ||
| 261 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 262 | return false; | ||
| 263 | #else | ||
| 264 | |||
| 265 | // buffer size | ||
| 266 | #define BSIZE 1024 | ||
| 267 | |||
| 268 | char buffer[BSIZE]; | ||
| 269 | |||
| 270 | // Open input file | ||
| 271 | FILE *input = fopen(srcFilename.c_str(), "rb"); | ||
| 272 | if (!input) | ||
| 273 | { | ||
| 274 | ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s", | ||
| 275 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 276 | return false; | ||
| 277 | } | ||
| 278 | |||
| 279 | // open output file | ||
| 280 | FILE *output = fopen(destFilename.c_str(), "wb"); | ||
| 281 | if (!output) | ||
| 282 | { | ||
| 283 | fclose(input); | ||
| 284 | ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s", | ||
| 285 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 286 | return false; | ||
| 287 | } | ||
| 288 | |||
| 289 | // copy loop | ||
| 290 | while (!feof(input)) | ||
| 291 | { | ||
| 292 | // read input | ||
| 293 | int rnum = fread(buffer, sizeof(char), BSIZE, input); | ||
| 294 | if (rnum != BSIZE) | ||
| 295 | { | ||
| 296 | if (ferror(input) != 0) | ||
| 297 | { | ||
| 298 | ERROR_LOG(COMMON, | ||
| 299 | "Copy: failed reading from source, %s --> %s: %s", | ||
| 300 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 301 | goto bail; | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | // write output | ||
| 306 | int wnum = fwrite(buffer, sizeof(char), rnum, output); | ||
| 307 | if (wnum != rnum) | ||
| 308 | { | ||
| 309 | ERROR_LOG(COMMON, | ||
| 310 | "Copy: failed writing to output, %s --> %s: %s", | ||
| 311 | srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); | ||
| 312 | goto bail; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | // close files | ||
| 316 | fclose(input); | ||
| 317 | fclose(output); | ||
| 318 | return true; | ||
| 319 | bail: | ||
| 320 | if (input) | ||
| 321 | fclose(input); | ||
| 322 | if (output) | ||
| 323 | fclose(output); | ||
| 324 | return false; | ||
| 325 | #endif | ||
| 326 | } | ||
| 327 | |||
| 328 | // Returns the size of filename (64bit) | ||
| 329 | u64 GetSize(const std::string &filename) | ||
| 330 | { | ||
| 331 | if (!Exists(filename)) | ||
| 332 | { | ||
| 333 | WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str()); | ||
| 334 | return 0; | ||
| 335 | } | ||
| 336 | |||
| 337 | if (IsDirectory(filename)) | ||
| 338 | { | ||
| 339 | WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str()); | ||
| 340 | return 0; | ||
| 341 | } | ||
| 342 | |||
| 343 | struct stat64 buf; | ||
| 344 | #ifdef _WIN32 | ||
| 345 | if (_tstat64(UTF8ToTStr(filename).c_str(), &buf) == 0) | ||
| 346 | #else | ||
| 347 | if (stat64(filename.c_str(), &buf) == 0) | ||
| 348 | #endif | ||
| 349 | { | ||
| 350 | DEBUG_LOG(COMMON, "GetSize: %s: %lld", | ||
| 351 | filename.c_str(), (long long)buf.st_size); | ||
| 352 | return buf.st_size; | ||
| 353 | } | ||
| 354 | |||
| 355 | ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s", | ||
| 356 | filename.c_str(), GetLastErrorMsg()); | ||
| 357 | return 0; | ||
| 358 | } | ||
| 359 | |||
| 360 | // Overloaded GetSize, accepts file descriptor | ||
| 361 | u64 GetSize(const int fd) | ||
| 362 | { | ||
| 363 | struct stat64 buf; | ||
| 364 | if (fstat64(fd, &buf) != 0) { | ||
| 365 | ERROR_LOG(COMMON, "GetSize: stat failed %i: %s", | ||
| 366 | fd, GetLastErrorMsg()); | ||
| 367 | return 0; | ||
| 368 | } | ||
| 369 | return buf.st_size; | ||
| 370 | } | ||
| 371 | |||
| 372 | // Overloaded GetSize, accepts FILE* | ||
| 373 | u64 GetSize(FILE *f) | ||
| 374 | { | ||
| 375 | // can't use off_t here because it can be 32-bit | ||
| 376 | u64 pos = ftello(f); | ||
| 377 | if (fseeko(f, 0, SEEK_END) != 0) { | ||
| 378 | ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", | ||
| 379 | f, GetLastErrorMsg()); | ||
| 380 | return 0; | ||
| 381 | } | ||
| 382 | u64 size = ftello(f); | ||
| 383 | if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { | ||
| 384 | ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", | ||
| 385 | f, GetLastErrorMsg()); | ||
| 386 | return 0; | ||
| 387 | } | ||
| 388 | return size; | ||
| 389 | } | ||
| 390 | |||
| 391 | // creates an empty file filename, returns true on success | ||
| 392 | bool CreateEmptyFile(const std::string &filename) | ||
| 393 | { | ||
| 394 | INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str()); | ||
| 395 | |||
| 396 | if (!File::IOFile(filename, "wb")) | ||
| 397 | { | ||
| 398 | ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s", | ||
| 399 | filename.c_str(), GetLastErrorMsg()); | ||
| 400 | return false; | ||
| 401 | } | ||
| 402 | |||
| 403 | return true; | ||
| 404 | } | ||
| 405 | |||
| 406 | |||
| 407 | // Scans the directory tree gets, starting from _Directory and adds the | ||
| 408 | // results into parentEntry. Returns the number of files+directories found | ||
| 409 | u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) | ||
| 410 | { | ||
| 411 | INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str()); | ||
| 412 | // How many files + directories we found | ||
| 413 | u32 foundEntries = 0; | ||
| 414 | #ifdef _WIN32 | ||
| 415 | // Find the first file in the directory. | ||
| 416 | WIN32_FIND_DATA ffd; | ||
| 417 | |||
| 418 | HANDLE hFind = FindFirstFile(UTF8ToTStr(directory + "\\*").c_str(), &ffd); | ||
| 419 | if (hFind == INVALID_HANDLE_VALUE) | ||
| 420 | { | ||
| 421 | FindClose(hFind); | ||
| 422 | return foundEntries; | ||
| 423 | } | ||
| 424 | // windows loop | ||
| 425 | do | ||
| 426 | { | ||
| 427 | FSTEntry entry; | ||
| 428 | const std::string virtualName(TStrToUTF8(ffd.cFileName)); | ||
| 429 | #else | ||
| 430 | struct dirent dirent, *result = NULL; | ||
| 431 | |||
| 432 | DIR *dirp = opendir(directory.c_str()); | ||
| 433 | if (!dirp) | ||
| 434 | return 0; | ||
| 435 | |||
| 436 | // non windows loop | ||
| 437 | while (!readdir_r(dirp, &dirent, &result) && result) | ||
| 438 | { | ||
| 439 | FSTEntry entry; | ||
| 440 | const std::string virtualName(result->d_name); | ||
| 441 | #endif | ||
| 442 | // check for "." and ".." | ||
| 443 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || | ||
| 444 | ((virtualName[0] == '.') && (virtualName[1] == '.') && | ||
| 445 | (virtualName[2] == '\0'))) | ||
| 446 | continue; | ||
| 447 | entry.virtualName = virtualName; | ||
| 448 | entry.physicalName = directory; | ||
| 449 | entry.physicalName += DIR_SEP + entry.virtualName; | ||
| 450 | |||
| 451 | if (IsDirectory(entry.physicalName.c_str())) | ||
| 452 | { | ||
| 453 | entry.isDirectory = true; | ||
| 454 | // is a directory, lets go inside | ||
| 455 | entry.size = ScanDirectoryTree(entry.physicalName, entry); | ||
| 456 | foundEntries += (u32)entry.size; | ||
| 457 | } | ||
| 458 | else | ||
| 459 | { // is a file | ||
| 460 | entry.isDirectory = false; | ||
| 461 | entry.size = GetSize(entry.physicalName.c_str()); | ||
| 462 | } | ||
| 463 | ++foundEntries; | ||
| 464 | // Push into the tree | ||
| 465 | parentEntry.children.push_back(entry); | ||
| 466 | #ifdef _WIN32 | ||
| 467 | } while (FindNextFile(hFind, &ffd) != 0); | ||
| 468 | FindClose(hFind); | ||
| 469 | #else | ||
| 470 | } | ||
| 471 | closedir(dirp); | ||
| 472 | #endif | ||
| 473 | // Return number of entries found. | ||
| 474 | return foundEntries; | ||
| 475 | } | ||
| 476 | |||
| 477 | |||
| 478 | // Deletes the given directory and anything under it. Returns true on success. | ||
| 479 | bool DeleteDirRecursively(const std::string &directory) | ||
| 480 | { | ||
| 481 | INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str()); | ||
| 482 | #ifdef _WIN32 | ||
| 483 | // Find the first file in the directory. | ||
| 484 | WIN32_FIND_DATA ffd; | ||
| 485 | HANDLE hFind = FindFirstFile(UTF8ToTStr(directory + "\\*").c_str(), &ffd); | ||
| 486 | |||
| 487 | if (hFind == INVALID_HANDLE_VALUE) | ||
| 488 | { | ||
| 489 | FindClose(hFind); | ||
| 490 | return false; | ||
| 491 | } | ||
| 492 | |||
| 493 | // windows loop | ||
| 494 | do | ||
| 495 | { | ||
| 496 | const std::string virtualName(TStrToUTF8(ffd.cFileName)); | ||
| 497 | #else | ||
| 498 | struct dirent dirent, *result = NULL; | ||
| 499 | DIR *dirp = opendir(directory.c_str()); | ||
| 500 | if (!dirp) | ||
| 501 | return false; | ||
| 502 | |||
| 503 | // non windows loop | ||
| 504 | while (!readdir_r(dirp, &dirent, &result) && result) | ||
| 505 | { | ||
| 506 | const std::string virtualName = result->d_name; | ||
| 507 | #endif | ||
| 508 | |||
| 509 | // check for "." and ".." | ||
| 510 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || | ||
| 511 | ((virtualName[0] == '.') && (virtualName[1] == '.') && | ||
| 512 | (virtualName[2] == '\0'))) | ||
| 513 | continue; | ||
| 514 | |||
| 515 | std::string newPath = directory + DIR_SEP_CHR + virtualName; | ||
| 516 | if (IsDirectory(newPath)) | ||
| 517 | { | ||
| 518 | if (!DeleteDirRecursively(newPath)) | ||
| 519 | { | ||
| 520 | #ifndef _WIN32 | ||
| 521 | closedir(dirp); | ||
| 522 | #endif | ||
| 523 | |||
| 524 | return false; | ||
| 525 | } | ||
| 526 | } | ||
| 527 | else | ||
| 528 | { | ||
| 529 | if (!File::Delete(newPath)) | ||
| 530 | { | ||
| 531 | #ifndef _WIN32 | ||
| 532 | closedir(dirp); | ||
| 533 | #endif | ||
| 534 | |||
| 535 | return false; | ||
| 536 | } | ||
| 537 | } | ||
| 538 | |||
| 539 | #ifdef _WIN32 | ||
| 540 | } while (FindNextFile(hFind, &ffd) != 0); | ||
| 541 | FindClose(hFind); | ||
| 542 | #else | ||
| 543 | } | ||
| 544 | closedir(dirp); | ||
| 545 | #endif | ||
| 546 | File::DeleteDir(directory); | ||
| 547 | |||
| 548 | return true; | ||
| 549 | } | ||
| 550 | |||
| 551 | // Create directory and copy contents (does not overwrite existing files) | ||
| 552 | void CopyDir(const std::string &source_path, const std::string &dest_path) | ||
| 553 | { | ||
| 554 | #ifndef _WIN32 | ||
| 555 | if (source_path == dest_path) return; | ||
| 556 | if (!File::Exists(source_path)) return; | ||
| 557 | if (!File::Exists(dest_path)) File::CreateFullPath(dest_path); | ||
| 558 | |||
| 559 | struct dirent dirent, *result = NULL; | ||
| 560 | DIR *dirp = opendir(source_path.c_str()); | ||
| 561 | if (!dirp) return; | ||
| 562 | |||
| 563 | while (!readdir_r(dirp, &dirent, &result) && result) | ||
| 564 | { | ||
| 565 | const std::string virtualName(result->d_name); | ||
| 566 | // check for "." and ".." | ||
| 567 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || | ||
| 568 | ((virtualName[0] == '.') && (virtualName[1] == '.') && | ||
| 569 | (virtualName[2] == '\0'))) | ||
| 570 | continue; | ||
| 571 | |||
| 572 | std::string source, dest; | ||
| 573 | source = source_path + virtualName; | ||
| 574 | dest = dest_path + virtualName; | ||
| 575 | if (IsDirectory(source)) | ||
| 576 | { | ||
| 577 | source += '/'; | ||
| 578 | dest += '/'; | ||
| 579 | if (!File::Exists(dest)) File::CreateFullPath(dest); | ||
| 580 | CopyDir(source, dest); | ||
| 581 | } | ||
| 582 | else if (!File::Exists(dest)) File::Copy(source, dest); | ||
| 583 | } | ||
| 584 | closedir(dirp); | ||
| 585 | #endif | ||
| 586 | } | ||
| 587 | |||
| 588 | // Returns the current directory | ||
| 589 | std::string GetCurrentDir() | ||
| 590 | { | ||
| 591 | char *dir; | ||
| 592 | // Get the current working directory (getcwd uses malloc) | ||
| 593 | if (!(dir = __getcwd(NULL, 0))) { | ||
| 594 | |||
| 595 | ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s", | ||
| 596 | GetLastErrorMsg()); | ||
| 597 | return NULL; | ||
| 598 | } | ||
| 599 | std::string strDir = dir; | ||
| 600 | free(dir); | ||
| 601 | return strDir; | ||
| 602 | } | ||
| 603 | |||
| 604 | // Sets the current directory to the given directory | ||
| 605 | bool SetCurrentDir(const std::string &directory) | ||
| 606 | { | ||
| 607 | return __chdir(directory.c_str()) == 0; | ||
| 608 | } | ||
| 609 | |||
| 610 | #if defined(__APPLE__) | ||
| 611 | std::string GetBundleDirectory() | ||
| 612 | { | ||
| 613 | CFURLRef BundleRef; | ||
| 614 | char AppBundlePath[MAXPATHLEN]; | ||
| 615 | // Get the main bundle for the app | ||
| 616 | BundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle()); | ||
| 617 | CFStringRef BundlePath = CFURLCopyFileSystemPath(BundleRef, kCFURLPOSIXPathStyle); | ||
| 618 | CFStringGetFileSystemRepresentation(BundlePath, AppBundlePath, sizeof(AppBundlePath)); | ||
| 619 | CFRelease(BundleRef); | ||
| 620 | CFRelease(BundlePath); | ||
| 621 | |||
| 622 | return AppBundlePath; | ||
| 623 | } | ||
| 624 | #endif | ||
| 625 | |||
| 626 | #ifdef _WIN32 | ||
| 627 | std::string& GetExeDirectory() | ||
| 628 | { | ||
| 629 | static std::string DolphinPath; | ||
| 630 | if (DolphinPath.empty()) | ||
| 631 | { | ||
| 632 | TCHAR Dolphin_exe_Path[2048]; | ||
| 633 | GetModuleFileName(NULL, Dolphin_exe_Path, 2048); | ||
| 634 | DolphinPath = TStrToUTF8(Dolphin_exe_Path); | ||
| 635 | DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\')); | ||
| 636 | } | ||
| 637 | return DolphinPath; | ||
| 638 | } | ||
| 639 | #endif | ||
| 640 | |||
| 641 | std::string GetSysDirectory() | ||
| 642 | { | ||
| 643 | std::string sysDir; | ||
| 644 | |||
| 645 | #if defined (__APPLE__) | ||
| 646 | sysDir = GetBundleDirectory(); | ||
| 647 | sysDir += DIR_SEP; | ||
| 648 | sysDir += SYSDATA_DIR; | ||
| 649 | #else | ||
| 650 | sysDir = SYSDATA_DIR; | ||
| 651 | #endif | ||
| 652 | sysDir += DIR_SEP; | ||
| 653 | |||
| 654 | INFO_LOG(COMMON, "GetSysDirectory: Setting to %s:", sysDir.c_str()); | ||
| 655 | return sysDir; | ||
| 656 | } | ||
| 657 | |||
| 658 | // Returns a string with a Dolphin data dir or file in the user's home | ||
| 659 | // directory. To be used in "multi-user" mode (that is, installed). | ||
| 660 | //const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath) | ||
| 661 | //{ | ||
| 662 | // static std::string paths[NUM_PATH_INDICES]; | ||
| 663 | // | ||
| 664 | // // Set up all paths and files on the first run | ||
| 665 | // if (paths[D_USER_IDX].empty()) | ||
| 666 | // { | ||
| 667 | //#ifdef _WIN32 | ||
| 668 | // paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; | ||
| 669 | //#else | ||
| 670 | // if (File::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) | ||
| 671 | // paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; | ||
| 672 | // else | ||
| 673 | // paths[D_USER_IDX] = std::string(getenv("HOME") ? | ||
| 674 | // getenv("HOME") : getenv("PWD") ? | ||
| 675 | // getenv("PWD") : "") + DIR_SEP DOLPHIN_DATA_DIR DIR_SEP; | ||
| 676 | //#endif | ||
| 677 | // | ||
| 678 | // paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; | ||
| 679 | // paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; | ||
| 680 | // paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | ||
| 681 | // paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | ||
| 682 | // paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | ||
| 683 | // paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | ||
| 684 | // paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | ||
| 685 | // paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP; | ||
| 686 | // paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP; | ||
| 687 | // paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; | ||
| 688 | // paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; | ||
| 689 | // paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; | ||
| 690 | // paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP; | ||
| 691 | // paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; | ||
| 692 | // paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; | ||
| 693 | // paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; | ||
| 694 | // } | ||
| 695 | // | ||
| 696 | // if (!newPath.empty()) | ||
| 697 | // { | ||
| 698 | // if (!File::IsDirectory(newPath)) | ||
| 699 | // { | ||
| 700 | // WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str()); | ||
| 701 | // return paths[DirIDX]; | ||
| 702 | // } | ||
| 703 | // else | ||
| 704 | // { | ||
| 705 | // paths[DirIDX] = newPath; | ||
| 706 | // } | ||
| 707 | // | ||
| 708 | // switch (DirIDX) | ||
| 709 | // { | ||
| 710 | // case D_WIIROOT_IDX: | ||
| 711 | // paths[D_WIIUSER_IDX] = paths[D_WIIROOT_IDX] + DIR_SEP; | ||
| 712 | // paths[D_WIISYSCONF_IDX] = paths[D_WIIUSER_IDX] + WII_SYSCONF_DIR + DIR_SEP; | ||
| 713 | // paths[F_WIISYSCONF_IDX] = paths[D_WIISYSCONF_IDX] + WII_SYSCONF; | ||
| 714 | // break; | ||
| 715 | // | ||
| 716 | // case D_USER_IDX: | ||
| 717 | // paths[D_GCUSER_IDX] = paths[D_USER_IDX] + GC_USER_DIR DIR_SEP; | ||
| 718 | // paths[D_WIIROOT_IDX] = paths[D_USER_IDX] + WII_USER_DIR; | ||
| 719 | // paths[D_WIIUSER_IDX] = paths[D_WIIROOT_IDX] + DIR_SEP; | ||
| 720 | // paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; | ||
| 721 | // paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; | ||
| 722 | // paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; | ||
| 723 | // paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; | ||
| 724 | // paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; | ||
| 725 | // paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; | ||
| 726 | // paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; | ||
| 727 | // paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP; | ||
| 728 | // paths[D_OPENCL_IDX] = paths[D_USER_IDX] + OPENCL_DIR DIR_SEP; | ||
| 729 | // paths[D_HIRESTEXTURES_IDX] = paths[D_USER_IDX] + HIRES_TEXTURES_DIR DIR_SEP; | ||
| 730 | // paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP; | ||
| 731 | // paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; | ||
| 732 | // paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; | ||
| 733 | // paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; | ||
| 734 | // paths[D_DUMPDSP_IDX] = paths[D_DUMP_IDX] + DUMP_DSP_DIR DIR_SEP; | ||
| 735 | // paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP; | ||
| 736 | // paths[D_MAILLOGS_IDX] = paths[D_LOGS_IDX] + MAIL_LOGS_DIR DIR_SEP; | ||
| 737 | // paths[D_WIISYSCONF_IDX] = paths[D_WIIUSER_IDX] + WII_SYSCONF_DIR DIR_SEP; | ||
| 738 | // paths[D_THEMES_IDX] = paths[D_USER_IDX] + THEMES_DIR DIR_SEP; | ||
| 739 | // paths[F_DOLPHINCONFIG_IDX] = paths[D_CONFIG_IDX] + DOLPHIN_CONFIG; | ||
| 740 | // paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; | ||
| 741 | // paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; | ||
| 742 | // paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; | ||
| 743 | // paths[F_WIISYSCONF_IDX] = paths[D_WIISYSCONF_IDX] + WII_SYSCONF; | ||
| 744 | // paths[F_RAMDUMP_IDX] = paths[D_DUMP_IDX] + RAM_DUMP; | ||
| 745 | // paths[F_ARAMDUMP_IDX] = paths[D_DUMP_IDX] + ARAM_DUMP; | ||
| 746 | // paths[F_FAKEVMEMDUMP_IDX] = paths[D_DUMP_IDX] + FAKEVMEM_DUMP; | ||
| 747 | // paths[F_GCSRAM_IDX] = paths[D_GCUSER_IDX] + GC_SRAM; | ||
| 748 | // break; | ||
| 749 | // | ||
| 750 | // case D_CONFIG_IDX: | ||
| 751 | // paths[F_DOLPHINCONFIG_IDX] = paths[D_CONFIG_IDX] + DOLPHIN_CONFIG; | ||
| 752 | // paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; | ||
| 753 | // paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; | ||
| 754 | // break; | ||
| 755 | // | ||
| 756 | // case D_DUMP_IDX: | ||
| 757 | // paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; | ||
| 758 | // paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; | ||
| 759 | // paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; | ||
| 760 | // break; | ||
| 761 | // | ||
| 762 | // case D_LOGS_IDX: | ||
| 763 | // paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; | ||
| 764 | // } | ||
| 765 | // } | ||
| 766 | // | ||
| 767 | // return paths[DirIDX]; | ||
| 768 | //} | ||
| 769 | |||
| 770 | std::string GetThemeDir(const std::string& theme_name) | ||
| 771 | { | ||
| 772 | std::string dir = File::GetUserPath(D_THEMES_IDX) + theme_name + "/"; | ||
| 773 | |||
| 774 | #if !defined(_WIN32) | ||
| 775 | // If theme does not exist in user's dir load from shared directory | ||
| 776 | if (!File::Exists(dir)) | ||
| 777 | dir = SHARED_USER_DIR THEMES_DIR "/" + theme_name + "/"; | ||
| 778 | #endif | ||
| 779 | |||
| 780 | return dir; | ||
| 781 | } | ||
| 782 | |||
| 783 | bool WriteStringToFile(bool text_file, const std::string &str, const char *filename) | ||
| 784 | { | ||
| 785 | return File::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); | ||
| 786 | } | ||
| 787 | |||
| 788 | bool ReadFileToString(bool text_file, const char *filename, std::string &str) | ||
| 789 | { | ||
| 790 | File::IOFile file(filename, text_file ? "r" : "rb"); | ||
| 791 | auto const f = file.GetHandle(); | ||
| 792 | |||
| 793 | if (!f) | ||
| 794 | return false; | ||
| 795 | |||
| 796 | str.resize(GetSize(f)); | ||
| 797 | return file.ReadArray(&str[0], str.size()); | ||
| 798 | } | ||
| 799 | |||
| 800 | IOFile::IOFile() | ||
| 801 | : m_file(NULL), m_good(true) | ||
| 802 | {} | ||
| 803 | |||
| 804 | IOFile::IOFile(std::FILE* file) | ||
| 805 | : m_file(file), m_good(true) | ||
| 806 | {} | ||
| 807 | |||
| 808 | IOFile::IOFile(const std::string& filename, const char openmode[]) | ||
| 809 | : m_file(NULL), m_good(true) | ||
| 810 | { | ||
| 811 | Open(filename, openmode); | ||
| 812 | } | ||
| 813 | |||
| 814 | IOFile::~IOFile() | ||
| 815 | { | ||
| 816 | Close(); | ||
| 817 | } | ||
| 818 | |||
| 819 | IOFile::IOFile(IOFile&& other) | ||
| 820 | : m_file(NULL), m_good(true) | ||
| 821 | { | ||
| 822 | Swap(other); | ||
| 823 | } | ||
| 824 | |||
| 825 | IOFile& IOFile::operator=(IOFile&& other) | ||
| 826 | { | ||
| 827 | Swap(other); | ||
| 828 | return *this; | ||
| 829 | } | ||
| 830 | |||
| 831 | void IOFile::Swap(IOFile& other) | ||
| 832 | { | ||
| 833 | std::swap(m_file, other.m_file); | ||
| 834 | std::swap(m_good, other.m_good); | ||
| 835 | } | ||
| 836 | |||
| 837 | bool IOFile::Open(const std::string& filename, const char openmode[]) | ||
| 838 | { | ||
| 839 | Close(); | ||
| 840 | #ifdef _WIN32 | ||
| 841 | _tfopen_s(&m_file, UTF8ToTStr(filename).c_str(), UTF8ToTStr(openmode).c_str()); | ||
| 842 | #else | ||
| 843 | m_file = fopen(filename.c_str(), openmode); | ||
| 844 | #endif | ||
| 845 | |||
| 846 | m_good = IsOpen(); | ||
| 847 | return m_good; | ||
| 848 | } | ||
| 849 | |||
| 850 | bool IOFile::Close() | ||
| 851 | { | ||
| 852 | if (!IsOpen() || 0 != std::fclose(m_file)) | ||
| 853 | m_good = false; | ||
| 854 | |||
| 855 | m_file = NULL; | ||
| 856 | return m_good; | ||
| 857 | } | ||
| 858 | |||
| 859 | std::FILE* IOFile::ReleaseHandle() | ||
| 860 | { | ||
| 861 | std::FILE* const ret = m_file; | ||
| 862 | m_file = NULL; | ||
| 863 | return ret; | ||
| 864 | } | ||
| 865 | |||
| 866 | void IOFile::SetHandle(std::FILE* file) | ||
| 867 | { | ||
| 868 | Close(); | ||
| 869 | Clear(); | ||
| 870 | m_file = file; | ||
| 871 | } | ||
| 872 | |||
| 873 | u64 IOFile::GetSize() | ||
| 874 | { | ||
| 875 | if (IsOpen()) | ||
| 876 | return File::GetSize(m_file); | ||
| 877 | else | ||
| 878 | return 0; | ||
| 879 | } | ||
| 880 | |||
| 881 | bool IOFile::Seek(s64 off, int origin) | ||
| 882 | { | ||
| 883 | if (!IsOpen() || 0 != fseeko(m_file, off, origin)) | ||
| 884 | m_good = false; | ||
| 885 | |||
| 886 | return m_good; | ||
| 887 | } | ||
| 888 | |||
| 889 | u64 IOFile::Tell() | ||
| 890 | { | ||
| 891 | if (IsOpen()) | ||
| 892 | return ftello(m_file); | ||
| 893 | else | ||
| 894 | return -1; | ||
| 895 | } | ||
| 896 | |||
| 897 | bool IOFile::Flush() | ||
| 898 | { | ||
| 899 | if (!IsOpen() || 0 != std::fflush(m_file)) | ||
| 900 | m_good = false; | ||
| 901 | |||
| 902 | return m_good; | ||
| 903 | } | ||
| 904 | |||
| 905 | bool IOFile::Resize(u64 size) | ||
| 906 | { | ||
| 907 | if (!IsOpen() || 0 != | ||
| 908 | #ifdef _WIN32 | ||
| 909 | // ector: _chsize sucks, not 64-bit safe | ||
| 910 | // F|RES: changed to _chsize_s. i think it is 64-bit safe | ||
| 911 | _chsize_s(_fileno(m_file), size) | ||
| 912 | #else | ||
| 913 | // TODO: handle 64bit and growing | ||
| 914 | ftruncate(fileno(m_file), size) | ||
| 915 | #endif | ||
| 916 | ) | ||
| 917 | m_good = false; | ||
| 918 | |||
| 919 | return m_good; | ||
| 920 | } | ||
| 921 | |||
| 922 | } // namespace | ||
diff --git a/src/common/src/file_util.h b/src/common/src/file_util.h new file mode 100644 index 000000000..a6bd085ab --- /dev/null +++ b/src/common/src/file_util.h | |||
| @@ -0,0 +1,232 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #ifndef _FILEUTIL_H_ | ||
| 7 | #define _FILEUTIL_H_ | ||
| 8 | |||
| 9 | #include <fstream> | ||
| 10 | #include <cstdio> | ||
| 11 | #include <string> | ||
| 12 | #include <vector> | ||
| 13 | #include <string.h> | ||
| 14 | |||
| 15 | #include "common.h" | ||
| 16 | #include "string_util.h" | ||
| 17 | |||
| 18 | // User directory indices for GetUserPath | ||
| 19 | enum { | ||
| 20 | D_USER_IDX, | ||
| 21 | D_GCUSER_IDX, | ||
| 22 | D_WIIROOT_IDX, | ||
| 23 | D_WIIUSER_IDX, | ||
| 24 | D_CONFIG_IDX, | ||
| 25 | D_GAMECONFIG_IDX, | ||
| 26 | D_MAPS_IDX, | ||
| 27 | D_CACHE_IDX, | ||
| 28 | D_SHADERCACHE_IDX, | ||
| 29 | D_SHADERS_IDX, | ||
| 30 | D_STATESAVES_IDX, | ||
| 31 | D_SCREENSHOTS_IDX, | ||
| 32 | D_OPENCL_IDX, | ||
| 33 | D_HIRESTEXTURES_IDX, | ||
| 34 | D_DUMP_IDX, | ||
| 35 | D_DUMPFRAMES_IDX, | ||
| 36 | D_DUMPAUDIO_IDX, | ||
| 37 | D_DUMPTEXTURES_IDX, | ||
| 38 | D_DUMPDSP_IDX, | ||
| 39 | D_LOGS_IDX, | ||
| 40 | D_MAILLOGS_IDX, | ||
| 41 | D_WIISYSCONF_IDX, | ||
| 42 | D_WIIWC24_IDX, | ||
| 43 | D_THEMES_IDX, | ||
| 44 | F_DOLPHINCONFIG_IDX, | ||
| 45 | F_DEBUGGERCONFIG_IDX, | ||
| 46 | F_LOGGERCONFIG_IDX, | ||
| 47 | F_MAINLOG_IDX, | ||
| 48 | F_WIISYSCONF_IDX, | ||
| 49 | F_RAMDUMP_IDX, | ||
| 50 | F_ARAMDUMP_IDX, | ||
| 51 | F_FAKEVMEMDUMP_IDX, | ||
| 52 | F_GCSRAM_IDX, | ||
| 53 | NUM_PATH_INDICES | ||
| 54 | }; | ||
| 55 | |||
| 56 | namespace File | ||
| 57 | { | ||
| 58 | |||
| 59 | // FileSystem tree node/ | ||
| 60 | struct FSTEntry | ||
| 61 | { | ||
| 62 | bool isDirectory; | ||
| 63 | u64 size; // file length or number of entries from children | ||
| 64 | std::string physicalName; // name on disk | ||
| 65 | std::string virtualName; // name in FST names table | ||
| 66 | std::vector<FSTEntry> children; | ||
| 67 | }; | ||
| 68 | |||
| 69 | // Returns true if file filename exists | ||
| 70 | bool Exists(const std::string &filename); | ||
| 71 | |||
| 72 | // Returns true if filename is a directory | ||
| 73 | bool IsDirectory(const std::string &filename); | ||
| 74 | |||
| 75 | // Returns the size of filename (64bit) | ||
| 76 | u64 GetSize(const std::string &filename); | ||
| 77 | |||
| 78 | // Overloaded GetSize, accepts file descriptor | ||
| 79 | u64 GetSize(const int fd); | ||
| 80 | |||
| 81 | // Overloaded GetSize, accepts FILE* | ||
| 82 | u64 GetSize(FILE *f); | ||
| 83 | |||
| 84 | // Returns true if successful, or path already exists. | ||
| 85 | bool CreateDir(const std::string &filename); | ||
| 86 | |||
| 87 | // Creates the full path of fullPath returns true on success | ||
| 88 | bool CreateFullPath(const std::string &fullPath); | ||
| 89 | |||
| 90 | // Deletes a given filename, return true on success | ||
| 91 | // Doesn't supports deleting a directory | ||
| 92 | bool Delete(const std::string &filename); | ||
| 93 | |||
| 94 | // Deletes a directory filename, returns true on success | ||
| 95 | bool DeleteDir(const std::string &filename); | ||
| 96 | |||
| 97 | // renames file srcFilename to destFilename, returns true on success | ||
| 98 | bool Rename(const std::string &srcFilename, const std::string &destFilename); | ||
| 99 | |||
| 100 | // copies file srcFilename to destFilename, returns true on success | ||
| 101 | bool Copy(const std::string &srcFilename, const std::string &destFilename); | ||
| 102 | |||
| 103 | // creates an empty file filename, returns true on success | ||
| 104 | bool CreateEmptyFile(const std::string &filename); | ||
| 105 | |||
| 106 | // Scans the directory tree gets, starting from _Directory and adds the | ||
| 107 | // results into parentEntry. Returns the number of files+directories found | ||
| 108 | u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry); | ||
| 109 | |||
| 110 | // deletes the given directory and anything under it. Returns true on success. | ||
| 111 | bool DeleteDirRecursively(const std::string &directory); | ||
| 112 | |||
| 113 | // Returns the current directory | ||
| 114 | std::string GetCurrentDir(); | ||
| 115 | |||
| 116 | // Create directory and copy contents (does not overwrite existing files) | ||
| 117 | void CopyDir(const std::string &source_path, const std::string &dest_path); | ||
| 118 | |||
| 119 | // Set the current directory to given directory | ||
| 120 | bool SetCurrentDir(const std::string &directory); | ||
| 121 | |||
| 122 | // Returns a pointer to a string with a Dolphin data dir in the user's home | ||
| 123 | // directory. To be used in "multi-user" mode (that is, installed). | ||
| 124 | const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath=""); | ||
| 125 | |||
| 126 | // probably doesn't belong here | ||
| 127 | std::string GetThemeDir(const std::string& theme_name); | ||
| 128 | |||
| 129 | // Returns the path to where the sys file are | ||
| 130 | std::string GetSysDirectory(); | ||
| 131 | |||
| 132 | #ifdef __APPLE__ | ||
| 133 | std::string GetBundleDirectory(); | ||
| 134 | #endif | ||
| 135 | |||
| 136 | #ifdef _WIN32 | ||
| 137 | std::string &GetExeDirectory(); | ||
| 138 | #endif | ||
| 139 | |||
| 140 | bool WriteStringToFile(bool text_file, const std::string &str, const char *filename); | ||
| 141 | bool ReadFileToString(bool text_file, const char *filename, std::string &str); | ||
| 142 | |||
| 143 | // simple wrapper for cstdlib file functions to | ||
| 144 | // hopefully will make error checking easier | ||
| 145 | // and make forgetting an fclose() harder | ||
| 146 | class IOFile : public NonCopyable | ||
| 147 | { | ||
| 148 | public: | ||
| 149 | IOFile(); | ||
| 150 | IOFile(std::FILE* file); | ||
| 151 | IOFile(const std::string& filename, const char openmode[]); | ||
| 152 | |||
| 153 | ~IOFile(); | ||
| 154 | |||
| 155 | IOFile(IOFile&& other); | ||
| 156 | IOFile& operator=(IOFile&& other); | ||
| 157 | |||
| 158 | void Swap(IOFile& other); | ||
| 159 | |||
| 160 | bool Open(const std::string& filename, const char openmode[]); | ||
| 161 | bool Close(); | ||
| 162 | |||
| 163 | template <typename T> | ||
| 164 | bool ReadArray(T* data, size_t length) | ||
| 165 | { | ||
| 166 | if (!IsOpen() || length != std::fread(data, sizeof(T), length, m_file)) | ||
| 167 | m_good = false; | ||
| 168 | |||
| 169 | return m_good; | ||
| 170 | } | ||
| 171 | |||
| 172 | template <typename T> | ||
| 173 | bool WriteArray(const T* data, size_t length) | ||
| 174 | { | ||
| 175 | if (!IsOpen() || length != std::fwrite(data, sizeof(T), length, m_file)) | ||
| 176 | m_good = false; | ||
| 177 | |||
| 178 | return m_good; | ||
| 179 | } | ||
| 180 | |||
| 181 | bool ReadBytes(void* data, size_t length) | ||
| 182 | { | ||
| 183 | return ReadArray(reinterpret_cast<char*>(data), length); | ||
| 184 | } | ||
| 185 | |||
| 186 | bool WriteBytes(const void* data, size_t length) | ||
| 187 | { | ||
| 188 | return WriteArray(reinterpret_cast<const char*>(data), length); | ||
| 189 | } | ||
| 190 | |||
| 191 | bool IsOpen() { return NULL != m_file; } | ||
| 192 | |||
| 193 | // m_good is set to false when a read, write or other function fails | ||
| 194 | bool IsGood() { return m_good; } | ||
| 195 | operator void*() { return m_good ? m_file : NULL; } | ||
| 196 | |||
| 197 | std::FILE* ReleaseHandle(); | ||
| 198 | |||
| 199 | std::FILE* GetHandle() { return m_file; } | ||
| 200 | |||
| 201 | void SetHandle(std::FILE* file); | ||
| 202 | |||
| 203 | bool Seek(s64 off, int origin); | ||
| 204 | u64 Tell(); | ||
| 205 | u64 GetSize(); | ||
| 206 | bool Resize(u64 size); | ||
| 207 | bool Flush(); | ||
| 208 | |||
| 209 | // clear error state | ||
| 210 | void Clear() { m_good = true; std::clearerr(m_file); } | ||
| 211 | |||
| 212 | std::FILE* m_file; | ||
| 213 | bool m_good; | ||
| 214 | private: | ||
| 215 | IOFile(IOFile&); | ||
| 216 | IOFile& operator=(IOFile& other); | ||
| 217 | }; | ||
| 218 | |||
| 219 | } // namespace | ||
| 220 | |||
| 221 | // To deal with Windows being dumb at unicode: | ||
| 222 | template <typename T> | ||
| 223 | void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) | ||
| 224 | { | ||
| 225 | #ifdef _WIN32 | ||
| 226 | fstream.open(UTF8ToTStr(filename).c_str(), openmode); | ||
| 227 | #else | ||
| 228 | fstream.open(filename.c_str(), openmode); | ||
| 229 | #endif | ||
| 230 | } | ||
| 231 | |||
| 232 | #endif | ||
diff --git a/src/common/src/fixed_size_queue.h b/src/common/src/fixed_size_queue.h new file mode 100644 index 000000000..4045dfa33 --- /dev/null +++ b/src/common/src/fixed_size_queue.h | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #ifndef _FIXED_SIZE_QUEUE_H_ | ||
| 7 | #define _FIXED_SIZE_QUEUE_H_ | ||
| 8 | |||
| 9 | // STL-look-a-like interface, but name is mixed case to distinguish it clearly from the | ||
| 10 | // real STL classes. | ||
| 11 | |||
| 12 | // Not fully featured, no safety checking yet. Add features as needed. | ||
| 13 | |||
| 14 | // TODO: "inline" storage? | ||
| 15 | |||
| 16 | template <class T, int N> | ||
| 17 | class fixed_size_queue.h | ||
| 18 | { | ||
| 19 | T *storage; | ||
| 20 | int head; | ||
| 21 | int tail; | ||
| 22 | int count; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future. | ||
| 23 | |||
| 24 | // Make copy constructor private for now. | ||
| 25 | fixed_size_queue.h(fixed_size_queue.h &other) { } | ||
| 26 | |||
| 27 | public: | ||
| 28 | fixed_size_queue.h() | ||
| 29 | { | ||
| 30 | storage = new T[N]; | ||
| 31 | clear(); | ||
| 32 | } | ||
| 33 | |||
| 34 | ~fixed_size_queue.h() | ||
| 35 | { | ||
| 36 | delete [] storage; | ||
| 37 | } | ||
| 38 | |||
| 39 | void clear() { | ||
| 40 | head = 0; | ||
| 41 | tail = 0; | ||
| 42 | count = 0; | ||
| 43 | } | ||
| 44 | |||
| 45 | void push(T t) { | ||
| 46 | storage[tail] = t; | ||
| 47 | tail++; | ||
| 48 | if (tail == N) | ||
| 49 | tail = 0; | ||
| 50 | count++; | ||
| 51 | } | ||
| 52 | |||
| 53 | void pop() { | ||
| 54 | head++; | ||
| 55 | if (head == N) | ||
| 56 | head = 0; | ||
| 57 | count--; | ||
| 58 | } | ||
| 59 | |||
| 60 | T pop_front() { | ||
| 61 | const T &temp = storage[head]; | ||
| 62 | pop(); | ||
| 63 | return temp; | ||
| 64 | } | ||
| 65 | |||
| 66 | T &front() { return storage[head]; } | ||
| 67 | const T &front() const { return storage[head]; } | ||
| 68 | |||
| 69 | size_t size() const { | ||
| 70 | return count; | ||
| 71 | } | ||
| 72 | }; | ||
| 73 | |||
| 74 | #endif // _FIXED_SIZE_QUEUE_H_ | ||
| 75 | |||
diff --git a/src/common/src/hash.cpp b/src/common/src/hash.cpp new file mode 100644 index 000000000..39c636419 --- /dev/null +++ b/src/common/src/hash.cpp | |||
| @@ -0,0 +1,520 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #include "hash.h" | ||
| 7 | #if _M_SSE >= 0x402 | ||
| 8 | #include "cpu_detect.h" | ||
| 9 | #include <nmmintrin.h> | ||
| 10 | #endif | ||
| 11 | |||
| 12 | static u64 (*ptrHashFunction)(const u8 *src, int len, u32 samples) = &GetMurmurHash3; | ||
| 13 | |||
| 14 | // uint32_t | ||
| 15 | // WARNING - may read one more byte! | ||
| 16 | // Implementation from Wikipedia. | ||
| 17 | u32 HashFletcher(const u8* data_u8, size_t length) | ||
| 18 | { | ||
| 19 | const u16* data = (const u16*)data_u8; /* Pointer to the data to be summed */ | ||
| 20 | size_t len = (length + 1) / 2; /* Length in 16-bit words */ | ||
| 21 | u32 sum1 = 0xffff, sum2 = 0xffff; | ||
| 22 | |||
| 23 | while (len) | ||
| 24 | { | ||
| 25 | size_t tlen = len > 360 ? 360 : len; | ||
| 26 | len -= tlen; | ||
| 27 | |||
| 28 | do { | ||
| 29 | sum1 += *data++; | ||
| 30 | sum2 += sum1; | ||
| 31 | } | ||
| 32 | while (--tlen); | ||
| 33 | |||
| 34 | sum1 = (sum1 & 0xffff) + (sum1 >> 16); | ||
| 35 | sum2 = (sum2 & 0xffff) + (sum2 >> 16); | ||
| 36 | } | ||
| 37 | |||
| 38 | // Second reduction step to reduce sums to 16 bits | ||
| 39 | sum1 = (sum1 & 0xffff) + (sum1 >> 16); | ||
| 40 | sum2 = (sum2 & 0xffff) + (sum2 >> 16); | ||
| 41 | return(sum2 << 16 | sum1); | ||
| 42 | } | ||
| 43 | |||
| 44 | |||
| 45 | // Implementation from Wikipedia | ||
| 46 | // Slightly slower than Fletcher above, but slightly more reliable. | ||
| 47 | #define MOD_ADLER 65521 | ||
| 48 | // data: Pointer to the data to be summed; len is in bytes | ||
| 49 | u32 HashAdler32(const u8* data, size_t len) | ||
| 50 | { | ||
| 51 | u32 a = 1, b = 0; | ||
| 52 | |||
| 53 | while (len) | ||
| 54 | { | ||
| 55 | size_t tlen = len > 5550 ? 5550 : len; | ||
| 56 | len -= tlen; | ||
| 57 | |||
| 58 | do | ||
| 59 | { | ||
| 60 | a += *data++; | ||
| 61 | b += a; | ||
| 62 | } | ||
| 63 | while (--tlen); | ||
| 64 | |||
| 65 | a = (a & 0xffff) + (a >> 16) * (65536 - MOD_ADLER); | ||
| 66 | b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); | ||
| 67 | } | ||
| 68 | |||
| 69 | // It can be shown that a <= 0x1013a here, so a single subtract will do. | ||
| 70 | if (a >= MOD_ADLER) | ||
| 71 | { | ||
| 72 | a -= MOD_ADLER; | ||
| 73 | } | ||
| 74 | |||
| 75 | // It can be shown that b can reach 0xfff87 here. | ||
| 76 | b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); | ||
| 77 | |||
| 78 | if (b >= MOD_ADLER) | ||
| 79 | { | ||
| 80 | b -= MOD_ADLER; | ||
| 81 | } | ||
| 82 | |||
| 83 | return((b << 16) | a); | ||
| 84 | } | ||
| 85 | |||
| 86 | // Stupid hash - but can't go back now :) | ||
| 87 | // Don't use for new things. At least it's reasonably fast. | ||
| 88 | u32 HashEctor(const u8* ptr, int length) | ||
| 89 | { | ||
| 90 | u32 crc = 0; | ||
| 91 | |||
| 92 | for (int i = 0; i < length; i++) | ||
| 93 | { | ||
| 94 | crc ^= ptr[i]; | ||
| 95 | crc = (crc << 3) | (crc >> 29); | ||
| 96 | } | ||
| 97 | |||
| 98 | return(crc); | ||
| 99 | } | ||
| 100 | |||
| 101 | |||
| 102 | #ifdef _M_X64 | ||
| 103 | |||
| 104 | //----------------------------------------------------------------------------- | ||
| 105 | // Block read - if your platform needs to do endian-swapping or can only | ||
| 106 | // handle aligned reads, do the conversion here | ||
| 107 | |||
| 108 | inline u64 getblock(const u64 * p, int i) | ||
| 109 | { | ||
| 110 | return p[i]; | ||
| 111 | } | ||
| 112 | |||
| 113 | //---------- | ||
| 114 | // Block mix - combine the key bits with the hash bits and scramble everything | ||
| 115 | |||
| 116 | inline void bmix64(u64 & h1, u64 & h2, u64 & k1, u64 & k2, u64 & c1, u64 & c2) | ||
| 117 | { | ||
| 118 | k1 *= c1; | ||
| 119 | k1 = _rotl64(k1,23); | ||
| 120 | k1 *= c2; | ||
| 121 | h1 ^= k1; | ||
| 122 | h1 += h2; | ||
| 123 | |||
| 124 | h2 = _rotl64(h2,41); | ||
| 125 | |||
| 126 | k2 *= c2; | ||
| 127 | k2 = _rotl64(k2,23); | ||
| 128 | k2 *= c1; | ||
| 129 | h2 ^= k2; | ||
| 130 | h2 += h1; | ||
| 131 | |||
| 132 | h1 = h1*3+0x52dce729; | ||
| 133 | h2 = h2*3+0x38495ab5; | ||
| 134 | |||
| 135 | c1 = c1*5+0x7b7d159c; | ||
| 136 | c2 = c2*5+0x6bce6396; | ||
| 137 | } | ||
| 138 | |||
| 139 | //---------- | ||
| 140 | // Finalization mix - avalanches all bits to within 0.05% bias | ||
| 141 | |||
| 142 | inline u64 fmix64(u64 k) | ||
| 143 | { | ||
| 144 | k ^= k >> 33; | ||
| 145 | k *= 0xff51afd7ed558ccd; | ||
| 146 | k ^= k >> 33; | ||
| 147 | k *= 0xc4ceb9fe1a85ec53; | ||
| 148 | k ^= k >> 33; | ||
| 149 | |||
| 150 | return k; | ||
| 151 | } | ||
| 152 | |||
| 153 | u64 GetMurmurHash3(const u8 *src, int len, u32 samples) | ||
| 154 | { | ||
| 155 | const u8 * data = (const u8*)src; | ||
| 156 | const int nblocks = len / 16; | ||
| 157 | u32 Step = (len / 8); | ||
| 158 | if(samples == 0) samples = max(Step, 1u); | ||
| 159 | Step = Step / samples; | ||
| 160 | if(Step < 1) Step = 1; | ||
| 161 | |||
| 162 | u64 h1 = 0x9368e53c2f6af274; | ||
| 163 | u64 h2 = 0x586dcd208f7cd3fd; | ||
| 164 | |||
| 165 | u64 c1 = 0x87c37b91114253d5; | ||
| 166 | u64 c2 = 0x4cf5ad432745937f; | ||
| 167 | |||
| 168 | |||
| 169 | //---------- | ||
| 170 | // body | ||
| 171 | |||
| 172 | const u64 * blocks = (const u64 *)(data); | ||
| 173 | |||
| 174 | for(int i = 0; i < nblocks; i+=Step) | ||
| 175 | { | ||
| 176 | u64 k1 = getblock(blocks,i*2+0); | ||
| 177 | u64 k2 = getblock(blocks,i*2+1); | ||
| 178 | |||
| 179 | bmix64(h1,h2,k1,k2,c1,c2); | ||
| 180 | } | ||
| 181 | |||
| 182 | //---------- | ||
| 183 | // tail | ||
| 184 | |||
| 185 | const u8 * tail = (const u8*)(data + nblocks*16); | ||
| 186 | |||
| 187 | u64 k1 = 0; | ||
| 188 | u64 k2 = 0; | ||
| 189 | |||
| 190 | switch(len & 15) | ||
| 191 | { | ||
| 192 | case 15: k2 ^= u64(tail[14]) << 48; | ||
| 193 | case 14: k2 ^= u64(tail[13]) << 40; | ||
| 194 | case 13: k2 ^= u64(tail[12]) << 32; | ||
| 195 | case 12: k2 ^= u64(tail[11]) << 24; | ||
| 196 | case 11: k2 ^= u64(tail[10]) << 16; | ||
| 197 | case 10: k2 ^= u64(tail[ 9]) << 8; | ||
| 198 | case 9: k2 ^= u64(tail[ 8]) << 0; | ||
| 199 | |||
| 200 | case 8: k1 ^= u64(tail[ 7]) << 56; | ||
| 201 | case 7: k1 ^= u64(tail[ 6]) << 48; | ||
| 202 | case 6: k1 ^= u64(tail[ 5]) << 40; | ||
| 203 | case 5: k1 ^= u64(tail[ 4]) << 32; | ||
| 204 | case 4: k1 ^= u64(tail[ 3]) << 24; | ||
| 205 | case 3: k1 ^= u64(tail[ 2]) << 16; | ||
| 206 | case 2: k1 ^= u64(tail[ 1]) << 8; | ||
| 207 | case 1: k1 ^= u64(tail[ 0]) << 0; | ||
| 208 | bmix64(h1,h2,k1,k2,c1,c2); | ||
| 209 | }; | ||
| 210 | |||
| 211 | //---------- | ||
| 212 | // finalization | ||
| 213 | |||
| 214 | h2 ^= len; | ||
| 215 | |||
| 216 | h1 += h2; | ||
| 217 | h2 += h1; | ||
| 218 | |||
| 219 | h1 = fmix64(h1); | ||
| 220 | h2 = fmix64(h2); | ||
| 221 | |||
| 222 | h1 += h2; | ||
| 223 | |||
| 224 | return h1; | ||
| 225 | } | ||
| 226 | |||
| 227 | |||
| 228 | // CRC32 hash using the SSE4.2 instruction | ||
| 229 | u64 GetCRC32(const u8 *src, int len, u32 samples) | ||
| 230 | { | ||
| 231 | #if _M_SSE >= 0x402 | ||
| 232 | u64 h = len; | ||
| 233 | u32 Step = (len / 8); | ||
| 234 | const u64 *data = (const u64 *)src; | ||
| 235 | const u64 *end = data + Step; | ||
| 236 | if(samples == 0) samples = max(Step, 1u); | ||
| 237 | Step = Step / samples; | ||
| 238 | if(Step < 1) Step = 1; | ||
| 239 | while(data < end) | ||
| 240 | { | ||
| 241 | h = _mm_crc32_u64(h, data[0]); | ||
| 242 | data += Step; | ||
| 243 | } | ||
| 244 | |||
| 245 | const u8 *data2 = (const u8*)end; | ||
| 246 | return _mm_crc32_u64(h, u64(data2[0])); | ||
| 247 | #else | ||
| 248 | return 0; | ||
| 249 | #endif | ||
| 250 | } | ||
| 251 | |||
| 252 | |||
| 253 | /* | ||
| 254 | * NOTE: This hash function is used for custom texture loading/dumping, so | ||
| 255 | * it should not be changed, which would require all custom textures to be | ||
| 256 | * recalculated for their new hash values. If the hashing function is | ||
| 257 | * changed, make sure this one is still used when the legacy parameter is | ||
| 258 | * true. | ||
| 259 | */ | ||
| 260 | u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) | ||
| 261 | { | ||
| 262 | const u64 m = 0xc6a4a7935bd1e995; | ||
| 263 | u64 h = len * m; | ||
| 264 | const int r = 47; | ||
| 265 | u32 Step = (len / 8); | ||
| 266 | const u64 *data = (const u64 *)src; | ||
| 267 | const u64 *end = data + Step; | ||
| 268 | if(samples == 0) samples = max(Step, 1u); | ||
| 269 | Step = Step / samples; | ||
| 270 | if(Step < 1) Step = 1; | ||
| 271 | while(data < end) | ||
| 272 | { | ||
| 273 | u64 k = data[0]; | ||
| 274 | data+=Step; | ||
| 275 | k *= m; | ||
| 276 | k ^= k >> r; | ||
| 277 | k *= m; | ||
| 278 | h ^= k; | ||
| 279 | h *= m; | ||
| 280 | } | ||
| 281 | |||
| 282 | const u8 * data2 = (const u8*)end; | ||
| 283 | |||
| 284 | switch(len & 7) | ||
| 285 | { | ||
| 286 | case 7: h ^= u64(data2[6]) << 48; | ||
| 287 | case 6: h ^= u64(data2[5]) << 40; | ||
| 288 | case 5: h ^= u64(data2[4]) << 32; | ||
| 289 | case 4: h ^= u64(data2[3]) << 24; | ||
| 290 | case 3: h ^= u64(data2[2]) << 16; | ||
| 291 | case 2: h ^= u64(data2[1]) << 8; | ||
| 292 | case 1: h ^= u64(data2[0]); | ||
| 293 | h *= m; | ||
| 294 | }; | ||
| 295 | |||
| 296 | h ^= h >> r; | ||
| 297 | h *= m; | ||
| 298 | h ^= h >> r; | ||
| 299 | |||
| 300 | return h; | ||
| 301 | } | ||
| 302 | #else | ||
| 303 | // CRC32 hash using the SSE4.2 instruction | ||
| 304 | u64 GetCRC32(const u8 *src, int len, u32 samples) | ||
| 305 | { | ||
| 306 | #if _M_SSE >= 0x402 | ||
| 307 | u32 h = len; | ||
| 308 | u32 Step = (len/4); | ||
| 309 | const u32 *data = (const u32 *)src; | ||
| 310 | const u32 *end = data + Step; | ||
| 311 | if(samples == 0) samples = max(Step, 1u); | ||
| 312 | Step = Step / samples; | ||
| 313 | if(Step < 1) Step = 1; | ||
| 314 | while(data < end) | ||
| 315 | { | ||
| 316 | h = _mm_crc32_u32(h, data[0]); | ||
| 317 | data += Step; | ||
| 318 | } | ||
| 319 | |||
| 320 | const u8 *data2 = (const u8*)end; | ||
| 321 | return (u64)_mm_crc32_u32(h, u32(data2[0])); | ||
| 322 | #else | ||
| 323 | return 0; | ||
| 324 | #endif | ||
| 325 | } | ||
| 326 | |||
| 327 | //----------------------------------------------------------------------------- | ||
| 328 | // Block read - if your platform needs to do endian-swapping or can only | ||
| 329 | // handle aligned reads, do the conversion here | ||
| 330 | |||
| 331 | inline u32 getblock(const u32 * p, int i) | ||
| 332 | { | ||
| 333 | return p[i]; | ||
| 334 | } | ||
| 335 | |||
| 336 | //---------- | ||
| 337 | // Finalization mix - force all bits of a hash block to avalanche | ||
| 338 | |||
| 339 | // avalanches all bits to within 0.25% bias | ||
| 340 | |||
| 341 | inline u32 fmix32(u32 h) | ||
| 342 | { | ||
| 343 | h ^= h >> 16; | ||
| 344 | h *= 0x85ebca6b; | ||
| 345 | h ^= h >> 13; | ||
| 346 | h *= 0xc2b2ae35; | ||
| 347 | h ^= h >> 16; | ||
| 348 | |||
| 349 | return h; | ||
| 350 | } | ||
| 351 | |||
| 352 | inline void bmix32(u32 & h1, u32 & h2, u32 & k1, u32 & k2, u32 & c1, u32 & c2) | ||
| 353 | { | ||
| 354 | k1 *= c1; | ||
| 355 | k1 = _rotl(k1,11); | ||
| 356 | k1 *= c2; | ||
| 357 | h1 ^= k1; | ||
| 358 | h1 += h2; | ||
| 359 | |||
| 360 | h2 = _rotl(h2,17); | ||
| 361 | |||
| 362 | k2 *= c2; | ||
| 363 | k2 = _rotl(k2,11); | ||
| 364 | k2 *= c1; | ||
| 365 | h2 ^= k2; | ||
| 366 | h2 += h1; | ||
| 367 | |||
| 368 | h1 = h1*3+0x52dce729; | ||
| 369 | h2 = h2*3+0x38495ab5; | ||
| 370 | |||
| 371 | c1 = c1*5+0x7b7d159c; | ||
| 372 | c2 = c2*5+0x6bce6396; | ||
| 373 | } | ||
| 374 | |||
| 375 | //---------- | ||
| 376 | |||
| 377 | u64 GetMurmurHash3(const u8* src, int len, u32 samples) | ||
| 378 | { | ||
| 379 | const u8 * data = (const u8*)src; | ||
| 380 | u32 out[2]; | ||
| 381 | const int nblocks = len / 8; | ||
| 382 | u32 Step = (len / 4); | ||
| 383 | if(samples == 0) samples = max(Step, 1u); | ||
| 384 | Step = Step / samples; | ||
| 385 | if(Step < 1) Step = 1; | ||
| 386 | |||
| 387 | u32 h1 = 0x8de1c3ac; | ||
| 388 | u32 h2 = 0xbab98226; | ||
| 389 | |||
| 390 | u32 c1 = 0x95543787; | ||
| 391 | u32 c2 = 0x2ad7eb25; | ||
| 392 | |||
| 393 | //---------- | ||
| 394 | // body | ||
| 395 | |||
| 396 | const u32 * blocks = (const u32 *)(data + nblocks*8); | ||
| 397 | |||
| 398 | for(int i = -nblocks; i < 0; i+=Step) | ||
| 399 | { | ||
| 400 | u32 k1 = getblock(blocks,i*2+0); | ||
| 401 | u32 k2 = getblock(blocks,i*2+1); | ||
| 402 | |||
| 403 | bmix32(h1,h2,k1,k2,c1,c2); | ||
| 404 | } | ||
| 405 | |||
| 406 | //---------- | ||
| 407 | // tail | ||
| 408 | |||
| 409 | const u8 * tail = (const u8*)(data + nblocks*8); | ||
| 410 | |||
| 411 | u32 k1 = 0; | ||
| 412 | u32 k2 = 0; | ||
| 413 | |||
| 414 | switch(len & 7) | ||
| 415 | { | ||
| 416 | case 7: k2 ^= tail[6] << 16; | ||
| 417 | case 6: k2 ^= tail[5] << 8; | ||
| 418 | case 5: k2 ^= tail[4] << 0; | ||
| 419 | case 4: k1 ^= tail[3] << 24; | ||
| 420 | case 3: k1 ^= tail[2] << 16; | ||
| 421 | case 2: k1 ^= tail[1] << 8; | ||
| 422 | case 1: k1 ^= tail[0] << 0; | ||
| 423 | bmix32(h1,h2,k1,k2,c1,c2); | ||
| 424 | }; | ||
| 425 | |||
| 426 | //---------- | ||
| 427 | // finalization | ||
| 428 | |||
| 429 | h2 ^= len; | ||
| 430 | |||
| 431 | h1 += h2; | ||
| 432 | h2 += h1; | ||
| 433 | |||
| 434 | h1 = fmix32(h1); | ||
| 435 | h2 = fmix32(h2); | ||
| 436 | |||
| 437 | h1 += h2; | ||
| 438 | h2 += h1; | ||
| 439 | |||
| 440 | out[0] = h1; | ||
| 441 | out[1] = h2; | ||
| 442 | |||
| 443 | return *((u64 *)&out); | ||
| 444 | } | ||
| 445 | |||
| 446 | /* | ||
| 447 | * FIXME: The old 32-bit version of this hash made different hashes than the | ||
| 448 | * 64-bit version. Until someone can make a new version of the 32-bit one that | ||
| 449 | * makes identical hashes, this is just a c/p of the 64-bit one. | ||
| 450 | */ | ||
| 451 | u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) | ||
| 452 | { | ||
| 453 | const u64 m = 0xc6a4a7935bd1e995ULL; | ||
| 454 | u64 h = len * m; | ||
| 455 | const int r = 47; | ||
| 456 | u32 Step = (len / 8); | ||
| 457 | const u64 *data = (const u64 *)src; | ||
| 458 | const u64 *end = data + Step; | ||
| 459 | if(samples == 0) samples = max(Step, 1u); | ||
| 460 | Step = Step / samples; | ||
| 461 | if(Step < 1) Step = 1; | ||
| 462 | while(data < end) | ||
| 463 | { | ||
| 464 | u64 k = data[0]; | ||
| 465 | data+=Step; | ||
| 466 | k *= m; | ||
| 467 | k ^= k >> r; | ||
| 468 | k *= m; | ||
| 469 | h ^= k; | ||
| 470 | h *= m; | ||
| 471 | } | ||
| 472 | |||
| 473 | const u8 * data2 = (const u8*)end; | ||
| 474 | |||
| 475 | switch(len & 7) | ||
| 476 | { | ||
| 477 | case 7: h ^= u64(data2[6]) << 48; | ||
| 478 | case 6: h ^= u64(data2[5]) << 40; | ||
| 479 | case 5: h ^= u64(data2[4]) << 32; | ||
| 480 | case 4: h ^= u64(data2[3]) << 24; | ||
| 481 | case 3: h ^= u64(data2[2]) << 16; | ||
| 482 | case 2: h ^= u64(data2[1]) << 8; | ||
| 483 | case 1: h ^= u64(data2[0]); | ||
| 484 | h *= m; | ||
| 485 | }; | ||
| 486 | |||
| 487 | h ^= h >> r; | ||
| 488 | h *= m; | ||
| 489 | h ^= h >> r; | ||
| 490 | |||
| 491 | return h; | ||
| 492 | } | ||
| 493 | #endif | ||
| 494 | |||
| 495 | u64 GetHash64(const u8 *src, int len, u32 samples) | ||
| 496 | { | ||
| 497 | return ptrHashFunction(src, len, samples); | ||
| 498 | } | ||
| 499 | |||
| 500 | // sets the hash function used for the texture cache | ||
| 501 | void SetHash64Function(bool useHiresTextures) | ||
| 502 | { | ||
| 503 | if (useHiresTextures) | ||
| 504 | { | ||
| 505 | ptrHashFunction = &GetHashHiresTexture; | ||
| 506 | } | ||
| 507 | #if _M_SSE >= 0x402 | ||
| 508 | else if (cpu_info.bSSE4_2 && !useHiresTextures) // sse crc32 version | ||
| 509 | { | ||
| 510 | ptrHashFunction = &GetCRC32; | ||
| 511 | } | ||
| 512 | #endif | ||
| 513 | else | ||
| 514 | { | ||
| 515 | ptrHashFunction = &GetMurmurHash3; | ||
| 516 | } | ||
| 517 | } | ||
| 518 | |||
| 519 | |||
| 520 | |||
diff --git a/src/common/src/hash.h b/src/common/src/hash.h new file mode 100644 index 000000000..addfa4b5f --- /dev/null +++ b/src/common/src/hash.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #ifndef _HASH_H_ | ||
| 7 | #define _HASH_H_ | ||
| 8 | |||
| 9 | #include "common.h" | ||
| 10 | |||
| 11 | u32 HashFletcher(const u8* data_u8, size_t length); // FAST. Length & 1 == 0. | ||
| 12 | u32 HashAdler32(const u8* data, size_t len); // Fairly accurate, slightly slower | ||
| 13 | u32 HashFNV(const u8* ptr, int length); // Another fast and decent hash | ||
| 14 | u32 HashEctor(const u8* ptr, int length); // JUNK. DO NOT USE FOR NEW THINGS | ||
| 15 | u64 GetCRC32(const u8 *src, int len, u32 samples); // SSE4.2 version of CRC32 | ||
| 16 | u64 GetHashHiresTexture(const u8 *src, int len, u32 samples); | ||
| 17 | u64 GetMurmurHash3(const u8 *src, int len, u32 samples); | ||
| 18 | u64 GetHash64(const u8 *src, int len, u32 samples); | ||
| 19 | void SetHash64Function(bool useHiresTextures); | ||
| 20 | #endif // _HASH_H_ | ||
diff --git a/src/common/src/linear_disk_cache.h b/src/common/src/linear_disk_cache.h new file mode 100644 index 000000000..d33ee7820 --- /dev/null +++ b/src/common/src/linear_disk_cache.h | |||
| @@ -0,0 +1,191 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #ifndef _LINEAR_DISKCACHE | ||
| 7 | #define _LINEAR_DISKCACHE | ||
| 8 | |||
| 9 | #include "common.h" | ||
| 10 | #include <fstream> | ||
| 11 | |||
| 12 | // defined in Version.cpp | ||
| 13 | extern const char *scm_rev_git_str; | ||
| 14 | |||
| 15 | // On disk format: | ||
| 16 | //header{ | ||
| 17 | // u32 'DCAC'; | ||
| 18 | // u32 version; // svn_rev | ||
| 19 | // u16 sizeof(key_type); | ||
| 20 | // u16 sizeof(value_type); | ||
| 21 | //} | ||
| 22 | |||
| 23 | //key_value_pair{ | ||
| 24 | // u32 value_size; | ||
| 25 | // key_type key; | ||
| 26 | // value_type[value_size] value; | ||
| 27 | //} | ||
| 28 | |||
| 29 | template <typename K, typename V> | ||
| 30 | class LinearDiskCacheReader | ||
| 31 | { | ||
| 32 | public: | ||
| 33 | virtual void Read(const K &key, const V *value, u32 value_size) = 0; | ||
| 34 | }; | ||
| 35 | |||
| 36 | // Dead simple unsorted key-value store with append functionality. | ||
| 37 | // No random read functionality, all reading is done in OpenAndRead. | ||
| 38 | // Keys and values can contain any characters, including \0. | ||
| 39 | // | ||
| 40 | // Suitable for caching generated shader bytecode between executions. | ||
| 41 | // Not tuned for extreme performance but should be reasonably fast. | ||
| 42 | // Does not support keys or values larger than 2GB, which should be reasonable. | ||
| 43 | // Keys must have non-zero length; values can have zero length. | ||
| 44 | |||
| 45 | // K and V are some POD type | ||
| 46 | // K : the key type | ||
| 47 | // V : value array type | ||
| 48 | template <typename K, typename V> | ||
| 49 | class LinearDiskCache | ||
| 50 | { | ||
| 51 | public: | ||
| 52 | // return number of read entries | ||
| 53 | u32 OpenAndRead(const char *filename, LinearDiskCacheReader<K, V> &reader) | ||
| 54 | { | ||
| 55 | using std::ios_base; | ||
| 56 | |||
| 57 | // close any currently opened file | ||
| 58 | Close(); | ||
| 59 | m_num_entries = 0; | ||
| 60 | |||
| 61 | // try opening for reading/writing | ||
| 62 | OpenFStream(m_file, filename, ios_base::in | ios_base::out | ios_base::binary); | ||
| 63 | |||
| 64 | m_file.seekg(0, std::ios::end); | ||
| 65 | std::fstream::pos_type end_pos = m_file.tellg(); | ||
| 66 | m_file.seekg(0, std::ios::beg); | ||
| 67 | std::fstream::pos_type start_pos = m_file.tellg(); | ||
| 68 | std::streamoff file_size = end_pos - start_pos; | ||
| 69 | |||
| 70 | if (m_file.is_open() && ValidateHeader()) | ||
| 71 | { | ||
| 72 | // good header, read some key/value pairs | ||
| 73 | K key; | ||
| 74 | |||
| 75 | V *value = NULL; | ||
| 76 | u32 value_size; | ||
| 77 | u32 entry_number; | ||
| 78 | |||
| 79 | std::fstream::pos_type last_pos = m_file.tellg(); | ||
| 80 | |||
| 81 | while (Read(&value_size)) | ||
| 82 | { | ||
| 83 | std::streamoff next_extent = (last_pos - start_pos) + sizeof(value_size) + value_size; | ||
| 84 | if (next_extent > file_size) | ||
| 85 | break; | ||
| 86 | |||
| 87 | delete[] value; | ||
| 88 | value = new V[value_size]; | ||
| 89 | |||
| 90 | // read key/value and pass to reader | ||
| 91 | if (Read(&key) && | ||
| 92 | Read(value, value_size) && | ||
| 93 | Read(&entry_number) && | ||
| 94 | entry_number == m_num_entries+1) | ||
| 95 | { | ||
| 96 | reader.Read(key, value, value_size); | ||
| 97 | } | ||
| 98 | else | ||
| 99 | { | ||
| 100 | break; | ||
| 101 | } | ||
| 102 | |||
| 103 | m_num_entries++; | ||
| 104 | last_pos = m_file.tellg(); | ||
| 105 | } | ||
| 106 | m_file.seekp(last_pos); | ||
| 107 | m_file.clear(); | ||
| 108 | |||
| 109 | delete[] value; | ||
| 110 | return m_num_entries; | ||
| 111 | } | ||
| 112 | |||
| 113 | // failed to open file for reading or bad header | ||
| 114 | // close and recreate file | ||
| 115 | Close(); | ||
| 116 | m_file.open(filename, ios_base::out | ios_base::trunc | ios_base::binary); | ||
| 117 | WriteHeader(); | ||
| 118 | return 0; | ||
| 119 | } | ||
| 120 | |||
| 121 | void Sync() | ||
| 122 | { | ||
| 123 | m_file.flush(); | ||
| 124 | } | ||
| 125 | |||
| 126 | void Close() | ||
| 127 | { | ||
| 128 | if (m_file.is_open()) | ||
| 129 | m_file.close(); | ||
| 130 | // clear any error flags | ||
| 131 | m_file.clear(); | ||
| 132 | } | ||
| 133 | |||
| 134 | // Appends a key-value pair to the store. | ||
| 135 | void Append(const K &key, const V *value, u32 value_size) | ||
| 136 | { | ||
| 137 | // TODO: Should do a check that we don't already have "key"? (I think each caller does that already.) | ||
| 138 | Write(&value_size); | ||
| 139 | Write(&key); | ||
| 140 | Write(value, value_size); | ||
| 141 | m_num_entries++; | ||
| 142 | Write(&m_num_entries); | ||
| 143 | } | ||
| 144 | |||
| 145 | private: | ||
| 146 | void WriteHeader() | ||
| 147 | { | ||
| 148 | Write(&m_header); | ||
| 149 | } | ||
| 150 | |||
| 151 | bool ValidateHeader() | ||
| 152 | { | ||
| 153 | char file_header[sizeof(Header)]; | ||
| 154 | |||
| 155 | return (Read(file_header, sizeof(Header)) | ||
| 156 | && !memcmp((const char*)&m_header, file_header, sizeof(Header))); | ||
| 157 | } | ||
| 158 | |||
| 159 | template <typename D> | ||
| 160 | bool Write(const D *data, u32 count = 1) | ||
| 161 | { | ||
| 162 | return m_file.write((const char*)data, count * sizeof(D)).good(); | ||
| 163 | } | ||
| 164 | |||
| 165 | template <typename D> | ||
| 166 | bool Read(const D *data, u32 count = 1) | ||
| 167 | { | ||
| 168 | return m_file.read((char*)data, count * sizeof(D)).good(); | ||
| 169 | } | ||
| 170 | |||
| 171 | struct Header | ||
| 172 | { | ||
| 173 | Header() | ||
| 174 | : id(*(u32*)"DCAC") | ||
| 175 | , key_t_size(sizeof(K)) | ||
| 176 | , value_t_size(sizeof(V)) | ||
| 177 | { | ||
| 178 | memcpy(ver, scm_rev_git_str, 40); | ||
| 179 | } | ||
| 180 | |||
| 181 | const u32 id; | ||
| 182 | const u16 key_t_size, value_t_size; | ||
| 183 | char ver[40]; | ||
| 184 | |||
| 185 | } m_header; | ||
| 186 | |||
| 187 | std::fstream m_file; | ||
| 188 | u32 m_num_entries; | ||
| 189 | }; | ||
| 190 | |||
| 191 | #endif // _LINEAR_DISKCACHE | ||
diff --git a/src/common/src/log.h b/src/common/src/log.h new file mode 100644 index 000000000..8691a825f --- /dev/null +++ b/src/common/src/log.h | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _LOG_H_ | ||
| 6 | #define _LOG_H_ | ||
| 7 | |||
| 8 | #define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and OSReports. | ||
| 9 | #define ERROR_LEVEL 2 // Critical errors | ||
| 10 | #define WARNING_LEVEL 3 // Something is suspicious. | ||
| 11 | #define INFO_LEVEL 4 // General information. | ||
| 12 | #define DEBUG_LEVEL 5 // Detailed debugging - might make things slow. | ||
| 13 | |||
| 14 | namespace LogTypes | ||
| 15 | { | ||
| 16 | |||
| 17 | enum LOG_TYPE { | ||
| 18 | ACTIONREPLAY, | ||
| 19 | AUDIO, | ||
| 20 | AUDIO_INTERFACE, | ||
| 21 | BOOT, | ||
| 22 | COMMANDPROCESSOR, | ||
| 23 | COMMON, | ||
| 24 | CONSOLE, | ||
| 25 | DISCIO, | ||
| 26 | FILEMON, | ||
| 27 | DSPHLE, | ||
| 28 | DSPLLE, | ||
| 29 | DSP_MAIL, | ||
| 30 | DSPINTERFACE, | ||
| 31 | DVDINTERFACE, | ||
| 32 | DYNA_REC, | ||
| 33 | EXPANSIONINTERFACE, | ||
| 34 | GDB_STUB, | ||
| 35 | POWERPC, | ||
| 36 | GPFIFO, | ||
| 37 | OSHLE, | ||
| 38 | MASTER_LOG, | ||
| 39 | MEMMAP, | ||
| 40 | MEMCARD_MANAGER, | ||
| 41 | OSREPORT, | ||
| 42 | PAD, | ||
| 43 | PROCESSORINTERFACE, | ||
| 44 | PIXELENGINE, | ||
| 45 | SERIALINTERFACE, | ||
| 46 | SP1, | ||
| 47 | STREAMINGINTERFACE, | ||
| 48 | VIDEO, | ||
| 49 | VIDEOINTERFACE, | ||
| 50 | WII_IOB, | ||
| 51 | WII_IPC, | ||
| 52 | WII_IPC_DVD, | ||
| 53 | WII_IPC_ES, | ||
| 54 | WII_IPC_FILEIO, | ||
| 55 | WII_IPC_HID, | ||
| 56 | WII_IPC_HLE, | ||
| 57 | WII_IPC_NET, | ||
| 58 | WII_IPC_WC24, | ||
| 59 | WII_IPC_SSL, | ||
| 60 | WII_IPC_SD, | ||
| 61 | WII_IPC_STM, | ||
| 62 | WII_IPC_WIIMOTE, | ||
| 63 | WIIMOTE, | ||
| 64 | NETPLAY, | ||
| 65 | |||
| 66 | NUMBER_OF_LOGS // Must be last | ||
| 67 | }; | ||
| 68 | |||
| 69 | // FIXME: should this be removed? | ||
| 70 | enum LOG_LEVELS { | ||
| 71 | LNOTICE = NOTICE_LEVEL, | ||
| 72 | LERROR = ERROR_LEVEL, | ||
| 73 | LWARNING = WARNING_LEVEL, | ||
| 74 | LINFO = INFO_LEVEL, | ||
| 75 | LDEBUG = DEBUG_LEVEL, | ||
| 76 | }; | ||
| 77 | |||
| 78 | #define LOGTYPES_LEVELS LogTypes::LOG_LEVELS | ||
| 79 | #define LOGTYPES_TYPE LogTypes::LOG_TYPE | ||
| 80 | |||
| 81 | } // namespace | ||
| 82 | |||
| 83 | void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, | ||
| 84 | const char *file, int line, const char *fmt, ...) | ||
| 85 | #ifdef __GNUC__ | ||
| 86 | __attribute__((format(printf, 5, 6))) | ||
| 87 | #endif | ||
| 88 | ; | ||
| 89 | |||
| 90 | #if defined LOGGING || defined _DEBUG || defined DEBUGFAST | ||
| 91 | #define MAX_LOGLEVEL DEBUG_LEVEL | ||
| 92 | #else | ||
| 93 | #ifndef MAX_LOGLEVEL | ||
| 94 | #define MAX_LOGLEVEL WARNING_LEVEL | ||
| 95 | #endif // loglevel | ||
| 96 | #endif // logging | ||
| 97 | |||
| 98 | #ifdef GEKKO | ||
| 99 | #define GENERIC_LOG(t, v, ...) | ||
| 100 | #else | ||
| 101 | // Let the compiler optimize this out | ||
| 102 | #define GENERIC_LOG(t, v, ...) { \ | ||
| 103 | if (v <= MAX_LOGLEVEL) \ | ||
| 104 | GenericLog(v, t, __FILE__, __LINE__, __VA_ARGS__); \ | ||
| 105 | } | ||
| 106 | #endif | ||
| 107 | |||
| 108 | #define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0) | ||
| 109 | #define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0) | ||
| 110 | #define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0) | ||
| 111 | #define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0) | ||
| 112 | #define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0) | ||
| 113 | |||
| 114 | #if MAX_LOGLEVEL >= DEBUG_LEVEL | ||
| 115 | #define _dbg_assert_(_t_, _a_) \ | ||
| 116 | if (!(_a_)) {\ | ||
| 117 | ERROR_LOG(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \ | ||
| 118 | __LINE__, __FILE__, __TIME__); \ | ||
| 119 | if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \ | ||
| 120 | } | ||
| 121 | #define _dbg_assert_msg_(_t_, _a_, ...)\ | ||
| 122 | if (!(_a_)) {\ | ||
| 123 | ERROR_LOG(_t_, __VA_ARGS__); \ | ||
| 124 | if (!PanicYesNo(__VA_ARGS__)) {Crash();} \ | ||
| 125 | } | ||
| 126 | #define _dbg_update_() Host_UpdateLogDisplay(); | ||
| 127 | |||
| 128 | #else // not debug | ||
| 129 | #define _dbg_update_() ; | ||
| 130 | |||
| 131 | #ifndef _dbg_assert_ | ||
| 132 | #define _dbg_assert_(_t_, _a_) {} | ||
| 133 | #define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {} | ||
| 134 | #endif // dbg_assert | ||
| 135 | #endif // MAX_LOGLEVEL DEBUG | ||
| 136 | |||
| 137 | #define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_) | ||
| 138 | |||
| 139 | #ifndef GEKKO | ||
| 140 | #ifdef _WIN32 | ||
| 141 | #define _assert_msg_(_t_, _a_, _fmt_, ...) \ | ||
| 142 | if (!(_a_)) {\ | ||
| 143 | if (!PanicYesNo(_fmt_, __VA_ARGS__)) {Crash();} \ | ||
| 144 | } | ||
| 145 | #else // not win32 | ||
| 146 | #define _assert_msg_(_t_, _a_, _fmt_, ...) \ | ||
| 147 | if (!(_a_)) {\ | ||
| 148 | if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \ | ||
| 149 | } | ||
| 150 | #endif // WIN32 | ||
| 151 | #else // GEKKO | ||
| 152 | #define _assert_msg_(_t_, _a_, _fmt_, ...) | ||
| 153 | #endif | ||
| 154 | |||
| 155 | #endif // _LOG_H_ | ||
diff --git a/src/common/src/log_manager.cpp b/src/common/src/log_manager.cpp new file mode 100644 index 000000000..c19f728f8 --- /dev/null +++ b/src/common/src/log_manager.cpp | |||
| @@ -0,0 +1,199 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | |||
| 7 | #ifdef ANDROID | ||
| 8 | #include "Host.h" | ||
| 9 | #endif | ||
| 10 | #include "log_manager.h" | ||
| 11 | #include "console_listener.h" | ||
| 12 | #include "timer.h" | ||
| 13 | #include "thread.h" | ||
| 14 | #include "file_util.h" | ||
| 15 | |||
| 16 | void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, | ||
| 17 | const char *file, int line, const char* fmt, ...) | ||
| 18 | { | ||
| 19 | va_list args; | ||
| 20 | va_start(args, fmt); | ||
| 21 | if (LogManager::GetInstance()) | ||
| 22 | LogManager::GetInstance()->Log(level, type, | ||
| 23 | file, line, fmt, args); | ||
| 24 | va_end(args); | ||
| 25 | } | ||
| 26 | |||
| 27 | LogManager *LogManager::m_logManager = NULL; | ||
| 28 | |||
| 29 | LogManager::LogManager() | ||
| 30 | { | ||
| 31 | // create log files | ||
| 32 | m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log"); | ||
| 33 | m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot"); | ||
| 34 | m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common"); | ||
| 35 | m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO"); | ||
| 36 | m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor"); | ||
| 37 | m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad"); | ||
| 38 | m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine"); | ||
| 39 | m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc"); | ||
| 40 | m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "VideoInt"); | ||
| 41 | m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "SerialInt"); | ||
| 42 | m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt"); | ||
| 43 | m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap"); | ||
| 44 | m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1"); | ||
| 45 | m_Log[LogTypes::STREAMINGINTERFACE] = new LogContainer("Stream", "StreamingInt"); | ||
| 46 | m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface"); | ||
| 47 | m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface"); | ||
| 48 | m_Log[LogTypes::GPFIFO] = new LogContainer("GP", "GPFifo"); | ||
| 49 | m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt"); | ||
| 50 | m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub"); | ||
| 51 | m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt"); | ||
| 52 | m_Log[LogTypes::POWERPC] = new LogContainer("PowerPC", "IBM CPU"); | ||
| 53 | m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE"); | ||
| 54 | m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE"); | ||
| 55 | m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE"); | ||
| 56 | m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails"); | ||
| 57 | m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend"); | ||
| 58 | m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator"); | ||
| 59 | m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "Dynamic Recompiler"); | ||
| 60 | m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console"); | ||
| 61 | m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport"); | ||
| 62 | m_Log[LogTypes::WIIMOTE] = new LogContainer("Wiimote", "Wiimote"); | ||
| 63 | m_Log[LogTypes::WII_IOB] = new LogContainer("WII_IOB", "WII IO Bridge"); | ||
| 64 | m_Log[LogTypes::WII_IPC] = new LogContainer("WII_IPC", "WII IPC"); | ||
| 65 | m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID"); | ||
| 66 | m_Log[LogTypes::WII_IPC_HLE] = new LogContainer("WII_IPC_HLE", "WII IPC HLE"); | ||
| 67 | m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD"); | ||
| 68 | m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES"); | ||
| 69 | m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO","WII IPC FILEIO"); | ||
| 70 | m_Log[LogTypes::WII_IPC_SD] = new LogContainer("WII_IPC_SD", "WII IPC SD"); | ||
| 71 | m_Log[LogTypes::WII_IPC_STM] = new LogContainer("WII_IPC_STM", "WII IPC STM"); | ||
| 72 | m_Log[LogTypes::WII_IPC_NET] = new LogContainer("WII_IPC_NET", "WII IPC NET"); | ||
| 73 | m_Log[LogTypes::WII_IPC_WC24] = new LogContainer("WII_IPC_WC24", "WII IPC WC24"); | ||
| 74 | m_Log[LogTypes::WII_IPC_SSL] = new LogContainer("WII_IPC_SSL", "WII IPC SSL"); | ||
| 75 | m_Log[LogTypes::WII_IPC_WIIMOTE] = new LogContainer("WII_IPC_WIIMOTE","WII IPC WIIMOTE"); | ||
| 76 | m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay"); | ||
| 77 | m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager"); | ||
| 78 | m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay"); | ||
| 79 | |||
| 80 | m_fileLog = new FileLogListener(File::GetUserPath(F_MAINLOG_IDX).c_str()); | ||
| 81 | m_consoleLog = new ConsoleListener(); | ||
| 82 | m_debuggerLog = new DebuggerLogListener(); | ||
| 83 | |||
| 84 | for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) | ||
| 85 | { | ||
| 86 | m_Log[i]->SetEnable(true); | ||
| 87 | m_Log[i]->AddListener(m_fileLog); | ||
| 88 | m_Log[i]->AddListener(m_consoleLog); | ||
| 89 | #ifdef _MSC_VER | ||
| 90 | if (IsDebuggerPresent()) | ||
| 91 | m_Log[i]->AddListener(m_debuggerLog); | ||
| 92 | #endif | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | LogManager::~LogManager() | ||
| 97 | { | ||
| 98 | for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) | ||
| 99 | { | ||
| 100 | m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog); | ||
| 101 | m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog); | ||
| 102 | m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog); | ||
| 103 | } | ||
| 104 | |||
| 105 | for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i) | ||
| 106 | delete m_Log[i]; | ||
| 107 | |||
| 108 | delete m_fileLog; | ||
| 109 | delete m_consoleLog; | ||
| 110 | delete m_debuggerLog; | ||
| 111 | } | ||
| 112 | |||
| 113 | void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, | ||
| 114 | const char *file, int line, const char *format, va_list args) | ||
| 115 | { | ||
| 116 | char temp[MAX_MSGLEN]; | ||
| 117 | char msg[MAX_MSGLEN * 2]; | ||
| 118 | LogContainer *log = m_Log[type]; | ||
| 119 | |||
| 120 | if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners()) | ||
| 121 | return; | ||
| 122 | |||
| 123 | CharArrayFromFormatV(temp, MAX_MSGLEN, format, args); | ||
| 124 | |||
| 125 | static const char level_to_char[7] = "-NEWID"; | ||
| 126 | sprintf(msg, "%s %s:%u %c[%s]: %s\n", | ||
| 127 | Common::Timer::GetTimeFormatted().c_str(), | ||
| 128 | file, line, level_to_char[(int)level], | ||
| 129 | log->GetShortName(), temp); | ||
| 130 | #ifdef ANDROID | ||
| 131 | Host_SysMessage(msg); | ||
| 132 | #endif | ||
| 133 | log->Trigger(level, msg); | ||
| 134 | } | ||
| 135 | |||
| 136 | void LogManager::Init() | ||
| 137 | { | ||
| 138 | m_logManager = new LogManager(); | ||
| 139 | } | ||
| 140 | |||
| 141 | void LogManager::Shutdown() | ||
| 142 | { | ||
| 143 | delete m_logManager; | ||
| 144 | m_logManager = NULL; | ||
| 145 | } | ||
| 146 | |||
| 147 | LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable) | ||
| 148 | : m_enable(enable) | ||
| 149 | { | ||
| 150 | strncpy(m_fullName, fullName, 128); | ||
| 151 | strncpy(m_shortName, shortName, 32); | ||
| 152 | m_level = LogTypes::LWARNING; | ||
| 153 | } | ||
| 154 | |||
| 155 | // LogContainer | ||
| 156 | void LogContainer::AddListener(LogListener *listener) | ||
| 157 | { | ||
| 158 | std::lock_guard<std::mutex> lk(m_listeners_lock); | ||
| 159 | m_listeners.insert(listener); | ||
| 160 | } | ||
| 161 | |||
| 162 | void LogContainer::RemoveListener(LogListener *listener) | ||
| 163 | { | ||
| 164 | std::lock_guard<std::mutex> lk(m_listeners_lock); | ||
| 165 | m_listeners.erase(listener); | ||
| 166 | } | ||
| 167 | |||
| 168 | void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg) | ||
| 169 | { | ||
| 170 | std::lock_guard<std::mutex> lk(m_listeners_lock); | ||
| 171 | |||
| 172 | std::set<LogListener*>::const_iterator i; | ||
| 173 | for (i = m_listeners.begin(); i != m_listeners.end(); ++i) | ||
| 174 | { | ||
| 175 | (*i)->Log(level, msg); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | FileLogListener::FileLogListener(const char *filename) | ||
| 180 | { | ||
| 181 | OpenFStream(m_logfile, filename, std::ios::app); | ||
| 182 | SetEnable(true); | ||
| 183 | } | ||
| 184 | |||
| 185 | void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) | ||
| 186 | { | ||
| 187 | if (!IsEnabled() || !IsValid()) | ||
| 188 | return; | ||
| 189 | |||
| 190 | std::lock_guard<std::mutex> lk(m_log_lock); | ||
| 191 | m_logfile << msg << std::flush; | ||
| 192 | } | ||
| 193 | |||
| 194 | void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) | ||
| 195 | { | ||
| 196 | #if _MSC_VER | ||
| 197 | ::OutputDebugStringA(msg); | ||
| 198 | #endif | ||
| 199 | } | ||
diff --git a/src/common/src/log_manager.h b/src/common/src/log_manager.h new file mode 100644 index 000000000..59078849b --- /dev/null +++ b/src/common/src/log_manager.h | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _LOGMANAGER_H_ | ||
| 6 | #define _LOGMANAGER_H_ | ||
| 7 | |||
| 8 | #include "log.h" | ||
| 9 | #include "string_util.h" | ||
| 10 | #include "thread.h" | ||
| 11 | #include "file_util.h" | ||
| 12 | |||
| 13 | #include <set> | ||
| 14 | #include <string.h> | ||
| 15 | |||
| 16 | #define MAX_MESSAGES 8000 | ||
| 17 | #define MAX_MSGLEN 1024 | ||
| 18 | |||
| 19 | |||
| 20 | // pure virtual interface | ||
| 21 | class LogListener | ||
| 22 | { | ||
| 23 | public: | ||
| 24 | virtual ~LogListener() {} | ||
| 25 | |||
| 26 | virtual void Log(LogTypes::LOG_LEVELS, const char *msg) = 0; | ||
| 27 | }; | ||
| 28 | |||
| 29 | class FileLogListener : public LogListener | ||
| 30 | { | ||
| 31 | public: | ||
| 32 | FileLogListener(const char *filename); | ||
| 33 | |||
| 34 | void Log(LogTypes::LOG_LEVELS, const char *msg); | ||
| 35 | |||
| 36 | bool IsValid() { return !m_logfile.fail(); } | ||
| 37 | bool IsEnabled() const { return m_enable; } | ||
| 38 | void SetEnable(bool enable) { m_enable = enable; } | ||
| 39 | |||
| 40 | const char* GetName() const { return "file"; } | ||
| 41 | |||
| 42 | private: | ||
| 43 | std::mutex m_log_lock; | ||
| 44 | std::ofstream m_logfile; | ||
| 45 | bool m_enable; | ||
| 46 | }; | ||
| 47 | |||
| 48 | class DebuggerLogListener : public LogListener | ||
| 49 | { | ||
| 50 | public: | ||
| 51 | void Log(LogTypes::LOG_LEVELS, const char *msg); | ||
| 52 | }; | ||
| 53 | |||
| 54 | class LogContainer | ||
| 55 | { | ||
| 56 | public: | ||
| 57 | LogContainer(const char* shortName, const char* fullName, bool enable = false); | ||
| 58 | |||
| 59 | const char* GetShortName() const { return m_shortName; } | ||
| 60 | const char* GetFullName() const { return m_fullName; } | ||
| 61 | |||
| 62 | void AddListener(LogListener* listener); | ||
| 63 | void RemoveListener(LogListener* listener); | ||
| 64 | |||
| 65 | void Trigger(LogTypes::LOG_LEVELS, const char *msg); | ||
| 66 | |||
| 67 | bool IsEnabled() const { return m_enable; } | ||
| 68 | void SetEnable(bool enable) { m_enable = enable; } | ||
| 69 | |||
| 70 | LogTypes::LOG_LEVELS GetLevel() const { return m_level; } | ||
| 71 | |||
| 72 | void SetLevel(LogTypes::LOG_LEVELS level) { m_level = level; } | ||
| 73 | |||
| 74 | bool HasListeners() const { return !m_listeners.empty(); } | ||
| 75 | |||
| 76 | private: | ||
| 77 | char m_fullName[128]; | ||
| 78 | char m_shortName[32]; | ||
| 79 | bool m_enable; | ||
| 80 | LogTypes::LOG_LEVELS m_level; | ||
| 81 | std::mutex m_listeners_lock; | ||
| 82 | std::set<LogListener*> m_listeners; | ||
| 83 | }; | ||
| 84 | |||
| 85 | class ConsoleListener; | ||
| 86 | |||
| 87 | class LogManager : NonCopyable | ||
| 88 | { | ||
| 89 | private: | ||
| 90 | LogContainer* m_Log[LogTypes::NUMBER_OF_LOGS]; | ||
| 91 | FileLogListener *m_fileLog; | ||
| 92 | ConsoleListener *m_consoleLog; | ||
| 93 | DebuggerLogListener *m_debuggerLog; | ||
| 94 | static LogManager *m_logManager; // Singleton. Ugh. | ||
| 95 | |||
| 96 | LogManager(); | ||
| 97 | ~LogManager(); | ||
| 98 | public: | ||
| 99 | |||
| 100 | static u32 GetMaxLevel() { return MAX_LOGLEVEL; } | ||
| 101 | |||
| 102 | void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, | ||
| 103 | const char *file, int line, const char *fmt, va_list args); | ||
| 104 | |||
| 105 | void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level) | ||
| 106 | { | ||
| 107 | m_Log[type]->SetLevel(level); | ||
| 108 | } | ||
| 109 | |||
| 110 | void SetEnable(LogTypes::LOG_TYPE type, bool enable) | ||
| 111 | { | ||
| 112 | m_Log[type]->SetEnable(enable); | ||
| 113 | } | ||
| 114 | |||
| 115 | bool IsEnabled(LogTypes::LOG_TYPE type) const | ||
| 116 | { | ||
| 117 | return m_Log[type]->IsEnabled(); | ||
| 118 | } | ||
| 119 | |||
| 120 | const char* GetShortName(LogTypes::LOG_TYPE type) const | ||
| 121 | { | ||
| 122 | return m_Log[type]->GetShortName(); | ||
| 123 | } | ||
| 124 | |||
| 125 | const char* GetFullName(LogTypes::LOG_TYPE type) const | ||
| 126 | { | ||
| 127 | return m_Log[type]->GetFullName(); | ||
| 128 | } | ||
| 129 | |||
| 130 | void AddListener(LogTypes::LOG_TYPE type, LogListener *listener) | ||
| 131 | { | ||
| 132 | m_Log[type]->AddListener(listener); | ||
| 133 | } | ||
| 134 | |||
| 135 | void RemoveListener(LogTypes::LOG_TYPE type, LogListener *listener) | ||
| 136 | { | ||
| 137 | m_Log[type]->RemoveListener(listener); | ||
| 138 | } | ||
| 139 | |||
| 140 | FileLogListener *GetFileListener() const | ||
| 141 | { | ||
| 142 | return m_fileLog; | ||
| 143 | } | ||
| 144 | |||
| 145 | ConsoleListener *GetConsoleListener() const | ||
| 146 | { | ||
| 147 | return m_consoleLog; | ||
| 148 | } | ||
| 149 | |||
| 150 | DebuggerLogListener *GetDebuggerListener() const | ||
| 151 | { | ||
| 152 | return m_debuggerLog; | ||
| 153 | } | ||
| 154 | |||
| 155 | static LogManager* GetInstance() | ||
| 156 | { | ||
| 157 | return m_logManager; | ||
| 158 | } | ||
| 159 | |||
| 160 | static void SetInstance(LogManager *logManager) | ||
| 161 | { | ||
| 162 | m_logManager = logManager; | ||
| 163 | } | ||
| 164 | |||
| 165 | static void Init(); | ||
| 166 | static void Shutdown(); | ||
| 167 | }; | ||
| 168 | |||
| 169 | #endif // _LOGMANAGER_H_ | ||
diff --git a/src/common/src/math_util.cpp b/src/common/src/math_util.cpp new file mode 100644 index 000000000..6667e078c --- /dev/null +++ b/src/common/src/math_util.cpp | |||
| @@ -0,0 +1,212 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #include "common.h" | ||
| 7 | #include "math_util.h" | ||
| 8 | |||
| 9 | #include <cmath> | ||
| 10 | #include <numeric> | ||
| 11 | |||
| 12 | namespace MathUtil | ||
| 13 | { | ||
| 14 | |||
| 15 | u32 ClassifyDouble(double dvalue) | ||
| 16 | { | ||
| 17 | // TODO: Optimize the below to be as fast as possible. | ||
| 18 | IntDouble value; | ||
| 19 | value.d = dvalue; | ||
| 20 | u64 sign = value.i & DOUBLE_SIGN; | ||
| 21 | u64 exp = value.i & DOUBLE_EXP; | ||
| 22 | if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP) | ||
| 23 | { | ||
| 24 | // Nice normalized number. | ||
| 25 | return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN; | ||
| 26 | } | ||
| 27 | else | ||
| 28 | { | ||
| 29 | u64 mantissa = value.i & DOUBLE_FRAC; | ||
| 30 | if (mantissa) | ||
| 31 | { | ||
| 32 | if (exp) | ||
| 33 | { | ||
| 34 | return PPC_FPCLASS_QNAN; | ||
| 35 | } | ||
| 36 | else | ||
| 37 | { | ||
| 38 | // Denormalized number. | ||
| 39 | return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | else if (exp) | ||
| 43 | { | ||
| 44 | //Infinite | ||
| 45 | return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF; | ||
| 46 | } | ||
| 47 | else | ||
| 48 | { | ||
| 49 | //Zero | ||
| 50 | return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | u32 ClassifyFloat(float fvalue) | ||
| 56 | { | ||
| 57 | // TODO: Optimize the below to be as fast as possible. | ||
| 58 | IntFloat value; | ||
| 59 | value.f = fvalue; | ||
| 60 | u32 sign = value.i & FLOAT_SIGN; | ||
| 61 | u32 exp = value.i & FLOAT_EXP; | ||
| 62 | if (exp > FLOAT_ZERO && exp < FLOAT_EXP) | ||
| 63 | { | ||
| 64 | // Nice normalized number. | ||
| 65 | return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN; | ||
| 66 | } | ||
| 67 | else | ||
| 68 | { | ||
| 69 | u32 mantissa = value.i & FLOAT_FRAC; | ||
| 70 | if (mantissa) | ||
| 71 | { | ||
| 72 | if (exp) | ||
| 73 | { | ||
| 74 | return PPC_FPCLASS_QNAN; // Quiet NAN | ||
| 75 | } | ||
| 76 | else | ||
| 77 | { | ||
| 78 | // Denormalized number. | ||
| 79 | return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | else if (exp) | ||
| 83 | { | ||
| 84 | // Infinite | ||
| 85 | return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF; | ||
| 86 | } | ||
| 87 | else | ||
| 88 | { | ||
| 89 | //Zero | ||
| 90 | return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | |||
| 96 | } // namespace | ||
| 97 | |||
| 98 | inline void MatrixMul(int n, const float *a, const float *b, float *result) | ||
| 99 | { | ||
| 100 | for (int i = 0; i < n; ++i) | ||
| 101 | { | ||
| 102 | for (int j = 0; j < n; ++j) | ||
| 103 | { | ||
| 104 | float temp = 0; | ||
| 105 | for (int k = 0; k < n; ++k) | ||
| 106 | { | ||
| 107 | temp += a[i * n + k] * b[k * n + j]; | ||
| 108 | } | ||
| 109 | result[i * n + j] = temp; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | // Calculate sum of a float list | ||
| 115 | float MathFloatVectorSum(const std::vector<float>& Vec) | ||
| 116 | { | ||
| 117 | return std::accumulate(Vec.begin(), Vec.end(), 0.0f); | ||
| 118 | } | ||
| 119 | |||
| 120 | void Matrix33::LoadIdentity(Matrix33 &mtx) | ||
| 121 | { | ||
| 122 | memset(mtx.data, 0, sizeof(mtx.data)); | ||
| 123 | mtx.data[0] = 1.0f; | ||
| 124 | mtx.data[4] = 1.0f; | ||
| 125 | mtx.data[8] = 1.0f; | ||
| 126 | } | ||
| 127 | |||
| 128 | void Matrix33::RotateX(Matrix33 &mtx, float rad) | ||
| 129 | { | ||
| 130 | float s = sin(rad); | ||
| 131 | float c = cos(rad); | ||
| 132 | memset(mtx.data, 0, sizeof(mtx.data)); | ||
| 133 | mtx.data[0] = 1; | ||
| 134 | mtx.data[4] = c; | ||
| 135 | mtx.data[5] = -s; | ||
| 136 | mtx.data[7] = s; | ||
| 137 | mtx.data[8] = c; | ||
| 138 | } | ||
| 139 | void Matrix33::RotateY(Matrix33 &mtx, float rad) | ||
| 140 | { | ||
| 141 | float s = sin(rad); | ||
| 142 | float c = cos(rad); | ||
| 143 | memset(mtx.data, 0, sizeof(mtx.data)); | ||
| 144 | mtx.data[0] = c; | ||
| 145 | mtx.data[2] = s; | ||
| 146 | mtx.data[4] = 1; | ||
| 147 | mtx.data[6] = -s; | ||
| 148 | mtx.data[8] = c; | ||
| 149 | } | ||
| 150 | |||
| 151 | void Matrix33::Multiply(const Matrix33 &a, const Matrix33 &b, Matrix33 &result) | ||
| 152 | { | ||
| 153 | MatrixMul(3, a.data, b.data, result.data); | ||
| 154 | } | ||
| 155 | |||
| 156 | void Matrix33::Multiply(const Matrix33 &a, const float vec[3], float result[3]) | ||
| 157 | { | ||
| 158 | for (int i = 0; i < 3; ++i) { | ||
| 159 | result[i] = 0; | ||
| 160 | for (int k = 0; k < 3; ++k) { | ||
| 161 | result[i] += a.data[i * 3 + k] * vec[k]; | ||
| 162 | } | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | void Matrix44::LoadIdentity(Matrix44 &mtx) | ||
| 167 | { | ||
| 168 | memset(mtx.data, 0, sizeof(mtx.data)); | ||
| 169 | mtx.data[0] = 1.0f; | ||
| 170 | mtx.data[5] = 1.0f; | ||
| 171 | mtx.data[10] = 1.0f; | ||
| 172 | mtx.data[15] = 1.0f; | ||
| 173 | } | ||
| 174 | |||
| 175 | void Matrix44::LoadMatrix33(Matrix44 &mtx, const Matrix33 &m33) | ||
| 176 | { | ||
| 177 | for (int i = 0; i < 3; ++i) | ||
| 178 | { | ||
| 179 | for (int j = 0; j < 3; ++j) | ||
| 180 | { | ||
| 181 | mtx.data[i * 4 + j] = m33.data[i * 3 + j]; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | for (int i = 0; i < 3; ++i) | ||
| 186 | { | ||
| 187 | mtx.data[i * 4 + 3] = 0; | ||
| 188 | mtx.data[i + 12] = 0; | ||
| 189 | } | ||
| 190 | mtx.data[15] = 1.0f; | ||
| 191 | } | ||
| 192 | |||
| 193 | void Matrix44::Set(Matrix44 &mtx, const float mtxArray[16]) | ||
| 194 | { | ||
| 195 | for(int i = 0; i < 16; ++i) { | ||
| 196 | mtx.data[i] = mtxArray[i]; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | void Matrix44::Translate(Matrix44 &mtx, const float vec[3]) | ||
| 201 | { | ||
| 202 | LoadIdentity(mtx); | ||
| 203 | mtx.data[3] = vec[0]; | ||
| 204 | mtx.data[7] = vec[1]; | ||
| 205 | mtx.data[11] = vec[2]; | ||
| 206 | } | ||
| 207 | |||
| 208 | void Matrix44::Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result) | ||
| 209 | { | ||
| 210 | MatrixMul(4, a.data, b.data, result.data); | ||
| 211 | } | ||
| 212 | |||
diff --git a/src/common/src/math_util.h b/src/common/src/math_util.h new file mode 100644 index 000000000..4dd12c309 --- /dev/null +++ b/src/common/src/math_util.h | |||
| @@ -0,0 +1,200 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #ifndef _MATH_UTIL_H_ | ||
| 7 | #define _MATH_UTIL_H_ | ||
| 8 | |||
| 9 | #include "common.h" | ||
| 10 | |||
| 11 | #include <vector> | ||
| 12 | |||
| 13 | namespace MathUtil | ||
| 14 | { | ||
| 15 | |||
| 16 | static const u64 DOUBLE_SIGN = 0x8000000000000000ULL, | ||
| 17 | DOUBLE_EXP = 0x7FF0000000000000ULL, | ||
| 18 | DOUBLE_FRAC = 0x000FFFFFFFFFFFFFULL, | ||
| 19 | DOUBLE_ZERO = 0x0000000000000000ULL; | ||
| 20 | |||
| 21 | static const u32 FLOAT_SIGN = 0x80000000, | ||
| 22 | FLOAT_EXP = 0x7F800000, | ||
| 23 | FLOAT_FRAC = 0x007FFFFF, | ||
| 24 | FLOAT_ZERO = 0x00000000; | ||
| 25 | |||
| 26 | union IntDouble { | ||
| 27 | double d; | ||
| 28 | u64 i; | ||
| 29 | }; | ||
| 30 | union IntFloat { | ||
| 31 | float f; | ||
| 32 | u32 i; | ||
| 33 | }; | ||
| 34 | |||
| 35 | inline bool IsNAN(double d) | ||
| 36 | { | ||
| 37 | IntDouble x; x.d = d; | ||
| 38 | return ( ((x.i & DOUBLE_EXP) == DOUBLE_EXP) && | ||
| 39 | ((x.i & DOUBLE_FRAC) != DOUBLE_ZERO) ); | ||
| 40 | } | ||
| 41 | |||
| 42 | inline bool IsQNAN(double d) | ||
| 43 | { | ||
| 44 | IntDouble x; x.d = d; | ||
| 45 | return ( ((x.i & DOUBLE_EXP) == DOUBLE_EXP) && | ||
| 46 | ((x.i & 0x0007fffffffffffULL) == 0x000000000000000ULL) && | ||
| 47 | ((x.i & 0x000800000000000ULL) == 0x000800000000000ULL) ); | ||
| 48 | } | ||
| 49 | |||
| 50 | inline bool IsSNAN(double d) | ||
| 51 | { | ||
| 52 | IntDouble x; x.d = d; | ||
| 53 | return( ((x.i & DOUBLE_EXP) == DOUBLE_EXP) && | ||
| 54 | ((x.i & DOUBLE_FRAC) != DOUBLE_ZERO) && | ||
| 55 | ((x.i & 0x0008000000000000ULL) == DOUBLE_ZERO) ); | ||
| 56 | } | ||
| 57 | |||
| 58 | inline float FlushToZero(float f) | ||
| 59 | { | ||
| 60 | IntFloat x; x.f = f; | ||
| 61 | if ((x.i & FLOAT_EXP) == 0) | ||
| 62 | x.i &= FLOAT_SIGN; // turn into signed zero | ||
| 63 | return x.f; | ||
| 64 | } | ||
| 65 | |||
| 66 | inline double FlushToZeroAsFloat(double d) | ||
| 67 | { | ||
| 68 | IntDouble x; x.d = d; | ||
| 69 | if ((x.i & DOUBLE_EXP) < 0x3800000000000000ULL) | ||
| 70 | x.i &= DOUBLE_SIGN; // turn into signed zero | ||
| 71 | return x.d; | ||
| 72 | } | ||
| 73 | |||
| 74 | enum PPCFpClass | ||
| 75 | { | ||
| 76 | PPC_FPCLASS_QNAN = 0x11, | ||
| 77 | PPC_FPCLASS_NINF = 0x9, | ||
| 78 | PPC_FPCLASS_NN = 0x8, | ||
| 79 | PPC_FPCLASS_ND = 0x18, | ||
| 80 | PPC_FPCLASS_NZ = 0x12, | ||
| 81 | PPC_FPCLASS_PZ = 0x2, | ||
| 82 | PPC_FPCLASS_PD = 0x14, | ||
| 83 | PPC_FPCLASS_PN = 0x4, | ||
| 84 | PPC_FPCLASS_PINF = 0x5, | ||
| 85 | }; | ||
| 86 | |||
| 87 | // Uses PowerPC conventions for the return value, so it can be easily | ||
| 88 | // used directly in CPU emulation. | ||
| 89 | u32 ClassifyDouble(double dvalue); | ||
| 90 | // More efficient float version. | ||
| 91 | u32 ClassifyFloat(float fvalue); | ||
| 92 | |||
| 93 | template<class T> | ||
| 94 | struct Rectangle | ||
| 95 | { | ||
| 96 | T left; | ||
| 97 | T top; | ||
| 98 | T right; | ||
| 99 | T bottom; | ||
| 100 | |||
| 101 | Rectangle() | ||
| 102 | { } | ||
| 103 | |||
| 104 | Rectangle(T theLeft, T theTop, T theRight, T theBottom) | ||
| 105 | : left(theLeft), top(theTop), right(theRight), bottom(theBottom) | ||
| 106 | { } | ||
| 107 | |||
| 108 | bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; } | ||
| 109 | |||
| 110 | T GetWidth() const { return abs(right - left); } | ||
| 111 | T GetHeight() const { return abs(bottom - top); } | ||
| 112 | |||
| 113 | // If the rectangle is in a coordinate system with a lower-left origin, use | ||
| 114 | // this Clamp. | ||
| 115 | void ClampLL(T x1, T y1, T x2, T y2) | ||
| 116 | { | ||
| 117 | if (left < x1) left = x1; | ||
| 118 | if (right > x2) right = x2; | ||
| 119 | if (top > y1) top = y1; | ||
| 120 | if (bottom < y2) bottom = y2; | ||
| 121 | } | ||
| 122 | |||
| 123 | // If the rectangle is in a coordinate system with an upper-left origin, | ||
| 124 | // use this Clamp. | ||
| 125 | void ClampUL(T x1, T y1, T x2, T y2) | ||
| 126 | { | ||
| 127 | if (left < x1) left = x1; | ||
| 128 | if (right > x2) right = x2; | ||
| 129 | if (top < y1) top = y1; | ||
| 130 | if (bottom > y2) bottom = y2; | ||
| 131 | } | ||
| 132 | }; | ||
| 133 | |||
| 134 | } // namespace MathUtil | ||
| 135 | |||
| 136 | inline float pow2f(float x) {return x * x;} | ||
| 137 | inline double pow2(double x) {return x * x;} | ||
| 138 | |||
| 139 | float MathFloatVectorSum(const std::vector<float>&); | ||
| 140 | |||
| 141 | #define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) | ||
| 142 | #define ROUND_DOWN(x, a) ((x) & ~((a) - 1)) | ||
| 143 | |||
| 144 | // Rounds down. 0 -> undefined | ||
| 145 | inline u64 Log2(u64 val) | ||
| 146 | { | ||
| 147 | #if defined(__GNUC__) | ||
| 148 | return 63 - __builtin_clzll(val); | ||
| 149 | |||
| 150 | #elif defined(_MSC_VER) && defined(_M_X64) | ||
| 151 | unsigned long result = -1; | ||
| 152 | _BitScanReverse64(&result, val); | ||
| 153 | return result; | ||
| 154 | |||
| 155 | #else | ||
| 156 | u64 result = -1; | ||
| 157 | while (val != 0) | ||
| 158 | { | ||
| 159 | val >>= 1; | ||
| 160 | ++result; | ||
| 161 | } | ||
| 162 | return result; | ||
| 163 | #endif | ||
| 164 | } | ||
| 165 | |||
| 166 | // Tiny matrix/vector library. | ||
| 167 | // Used for things like Free-Look in the gfx backend. | ||
| 168 | |||
| 169 | class Matrix33 | ||
| 170 | { | ||
| 171 | public: | ||
| 172 | static void LoadIdentity(Matrix33 &mtx); | ||
| 173 | |||
| 174 | // set mtx to be a rotation matrix around the x axis | ||
| 175 | static void RotateX(Matrix33 &mtx, float rad); | ||
| 176 | // set mtx to be a rotation matrix around the y axis | ||
| 177 | static void RotateY(Matrix33 &mtx, float rad); | ||
| 178 | |||
| 179 | // set result = a x b | ||
| 180 | static void Multiply(const Matrix33 &a, const Matrix33 &b, Matrix33 &result); | ||
| 181 | static void Multiply(const Matrix33 &a, const float vec[3], float result[3]); | ||
| 182 | |||
| 183 | float data[9]; | ||
| 184 | }; | ||
| 185 | |||
| 186 | class Matrix44 | ||
| 187 | { | ||
| 188 | public: | ||
| 189 | static void LoadIdentity(Matrix44 &mtx); | ||
| 190 | static void LoadMatrix33(Matrix44 &mtx, const Matrix33 &m33); | ||
| 191 | static void Set(Matrix44 &mtx, const float mtxArray[16]); | ||
| 192 | |||
| 193 | static void Translate(Matrix44 &mtx, const float vec[3]); | ||
| 194 | |||
| 195 | static void Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result); | ||
| 196 | |||
| 197 | float data[16]; | ||
| 198 | }; | ||
| 199 | |||
| 200 | #endif // _MATH_UTIL_H_ | ||
diff --git a/src/common/src/mem_arena.cpp b/src/common/src/mem_arena.cpp new file mode 100644 index 000000000..13cd00fd3 --- /dev/null +++ b/src/common/src/mem_arena.cpp | |||
| @@ -0,0 +1,304 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #include "common.h" | ||
| 7 | #include "memory_util.h" | ||
| 8 | #include "mem_arena.h" | ||
| 9 | |||
| 10 | #ifdef _WIN32 | ||
| 11 | #include <windows.h> | ||
| 12 | #else | ||
| 13 | #include <sys/stat.h> | ||
| 14 | #include <fcntl.h> | ||
| 15 | #include <unistd.h> | ||
| 16 | #include <cerrno> | ||
| 17 | #include <cstring> | ||
| 18 | #ifdef ANDROID | ||
| 19 | #include <sys/ioctl.h> | ||
| 20 | #include <linux/ashmem.h> | ||
| 21 | #endif | ||
| 22 | #endif | ||
| 23 | #include <set> | ||
| 24 | |||
| 25 | #if defined(__APPLE__) | ||
| 26 | static const char* ram_temp_file = "/tmp/gc_mem.tmp"; | ||
| 27 | #elif !defined(_WIN32) // non OSX unixes | ||
| 28 | static const char* ram_temp_file = "/dev/shm/gc_mem.tmp"; | ||
| 29 | #endif | ||
| 30 | #ifdef ANDROID | ||
| 31 | #define ASHMEM_DEVICE "/dev/ashmem" | ||
| 32 | |||
| 33 | int AshmemCreateFileMapping(const char *name, size_t size) | ||
| 34 | { | ||
| 35 | int fd, ret; | ||
| 36 | fd = open(ASHMEM_DEVICE, O_RDWR); | ||
| 37 | if (fd < 0) | ||
| 38 | return fd; | ||
| 39 | |||
| 40 | // We don't really care if we can't set the name, it is optional | ||
| 41 | ret = ioctl(fd, ASHMEM_SET_NAME, name); | ||
| 42 | |||
| 43 | ret = ioctl(fd, ASHMEM_SET_SIZE, size); | ||
| 44 | if (ret < 0) | ||
| 45 | { | ||
| 46 | close(fd); | ||
| 47 | NOTICE_LOG(MEMMAP, "Ashmem returned error: 0x%08x", ret); | ||
| 48 | return ret; | ||
| 49 | } | ||
| 50 | return fd; | ||
| 51 | } | ||
| 52 | #endif | ||
| 53 | |||
| 54 | void MemArena::GrabLowMemSpace(size_t size) | ||
| 55 | { | ||
| 56 | #ifdef _WIN32 | ||
| 57 | hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL); | ||
| 58 | #elif defined(ANDROID) | ||
| 59 | fd = AshmemCreateFileMapping("Dolphin-emu", size); | ||
| 60 | if (fd < 0) | ||
| 61 | { | ||
| 62 | NOTICE_LOG(MEMMAP, "Ashmem allocation failed"); | ||
| 63 | return; | ||
| 64 | } | ||
| 65 | #else | ||
| 66 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; | ||
| 67 | fd = open(ram_temp_file, O_RDWR | O_CREAT, mode); | ||
| 68 | unlink(ram_temp_file); | ||
| 69 | if (ftruncate(fd, size) < 0) | ||
| 70 | ERROR_LOG(MEMMAP, "Failed to allocate low memory space"); | ||
| 71 | return; | ||
| 72 | #endif | ||
| 73 | } | ||
| 74 | |||
| 75 | |||
| 76 | void MemArena::ReleaseSpace() | ||
| 77 | { | ||
| 78 | #ifdef _WIN32 | ||
| 79 | CloseHandle(hMemoryMapping); | ||
| 80 | hMemoryMapping = 0; | ||
| 81 | #else | ||
| 82 | close(fd); | ||
| 83 | #endif | ||
| 84 | } | ||
| 85 | |||
| 86 | |||
| 87 | void *MemArena::CreateView(s64 offset, size_t size, void *base) | ||
| 88 | { | ||
| 89 | #ifdef _WIN32 | ||
| 90 | return MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base); | ||
| 91 | #else | ||
| 92 | void *retval = mmap( | ||
| 93 | base, size, | ||
| 94 | PROT_READ | PROT_WRITE, | ||
| 95 | MAP_SHARED | ((base == nullptr) ? 0 : MAP_FIXED), | ||
| 96 | fd, offset); | ||
| 97 | |||
| 98 | if (retval == MAP_FAILED) | ||
| 99 | { | ||
| 100 | NOTICE_LOG(MEMMAP, "mmap on %s failed", ram_temp_file); | ||
| 101 | return nullptr; | ||
| 102 | } | ||
| 103 | else | ||
| 104 | { | ||
| 105 | return retval; | ||
| 106 | } | ||
| 107 | #endif | ||
| 108 | } | ||
| 109 | |||
| 110 | |||
| 111 | void MemArena::ReleaseView(void* view, size_t size) | ||
| 112 | { | ||
| 113 | #ifdef _WIN32 | ||
| 114 | UnmapViewOfFile(view); | ||
| 115 | #else | ||
| 116 | munmap(view, size); | ||
| 117 | #endif | ||
| 118 | } | ||
| 119 | |||
| 120 | |||
| 121 | u8* MemArena::Find4GBBase() | ||
| 122 | { | ||
| 123 | #ifdef _M_X64 | ||
| 124 | #ifdef _WIN32 | ||
| 125 | // 64 bit | ||
| 126 | u8* base = (u8*)VirtualAlloc(0, 0xE1000000, MEM_RESERVE, PAGE_READWRITE); | ||
| 127 | VirtualFree(base, 0, MEM_RELEASE); | ||
| 128 | return base; | ||
| 129 | #else | ||
| 130 | // Very precarious - mmap cannot return an error when trying to map already used pages. | ||
| 131 | // This makes the Windows approach above unusable on Linux, so we will simply pray... | ||
| 132 | return reinterpret_cast<u8*>(0x2300000000ULL); | ||
| 133 | #endif | ||
| 134 | |||
| 135 | #else | ||
| 136 | // 32 bit | ||
| 137 | #ifdef _WIN32 | ||
| 138 | // The highest thing in any 1GB section of memory space is the locked cache. We only need to fit it. | ||
| 139 | u8* base = (u8*)VirtualAlloc(0, 0x31000000, MEM_RESERVE, PAGE_READWRITE); | ||
| 140 | if (base) { | ||
| 141 | VirtualFree(base, 0, MEM_RELEASE); | ||
| 142 | } | ||
| 143 | return base; | ||
| 144 | #else | ||
| 145 | #ifdef ANDROID | ||
| 146 | // Android 4.3 changed how mmap works. | ||
| 147 | // if we map it private and then munmap it, we can't use the base returned. | ||
| 148 | // This may be due to changes in them support a full SELinux implementation. | ||
| 149 | const int flags = MAP_ANON; | ||
| 150 | #else | ||
| 151 | const int flags = MAP_ANON | MAP_PRIVATE; | ||
| 152 | #endif | ||
| 153 | const u32 MemSize = 0x31000000; | ||
| 154 | void* base = mmap(0, MemSize, PROT_NONE, flags, -1, 0); | ||
| 155 | if (base == MAP_FAILED) { | ||
| 156 | PanicAlert("Failed to map 1 GB of memory space: %s", strerror(errno)); | ||
| 157 | return 0; | ||
| 158 | } | ||
| 159 | munmap(base, MemSize); | ||
| 160 | return static_cast<u8*>(base); | ||
| 161 | #endif | ||
| 162 | #endif | ||
| 163 | } | ||
| 164 | |||
| 165 | |||
| 166 | // yeah, this could also be done in like two bitwise ops... | ||
| 167 | #define SKIP(a_flags, b_flags) \ | ||
| 168 | if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY)) \ | ||
| 169 | continue; \ | ||
| 170 | if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM)) \ | ||
| 171 | continue; \ | ||
| 172 | |||
| 173 | |||
| 174 | static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 flags, MemArena *arena) { | ||
| 175 | // OK, we know where to find free space. Now grab it! | ||
| 176 | // We just mimic the popular BAT setup. | ||
| 177 | u32 position = 0; | ||
| 178 | u32 last_position = 0; | ||
| 179 | |||
| 180 | // Zero all the pointers to be sure. | ||
| 181 | for (int i = 0; i < num_views; i++) | ||
| 182 | { | ||
| 183 | if (views[i].out_ptr_low) | ||
| 184 | *views[i].out_ptr_low = 0; | ||
| 185 | if (views[i].out_ptr) | ||
| 186 | *views[i].out_ptr = 0; | ||
| 187 | } | ||
| 188 | |||
| 189 | int i; | ||
| 190 | for (i = 0; i < num_views; i++) | ||
| 191 | { | ||
| 192 | SKIP(flags, views[i].flags); | ||
| 193 | if (views[i].flags & MV_MIRROR_PREVIOUS) { | ||
| 194 | position = last_position; | ||
| 195 | } else { | ||
| 196 | *(views[i].out_ptr_low) = (u8*)arena->CreateView(position, views[i].size); | ||
| 197 | if (!*views[i].out_ptr_low) | ||
| 198 | goto bail; | ||
| 199 | } | ||
| 200 | #ifdef _M_X64 | ||
| 201 | *views[i].out_ptr = (u8*)arena->CreateView( | ||
| 202 | position, views[i].size, base + views[i].virtual_address); | ||
| 203 | #else | ||
| 204 | if (views[i].flags & MV_MIRROR_PREVIOUS) { | ||
| 205 | // No need to create multiple identical views. | ||
| 206 | *views[i].out_ptr = *views[i - 1].out_ptr; | ||
| 207 | } else { | ||
| 208 | *views[i].out_ptr = (u8*)arena->CreateView( | ||
| 209 | position, views[i].size, base + (views[i].virtual_address & 0x3FFFFFFF)); | ||
| 210 | if (!*views[i].out_ptr) | ||
| 211 | goto bail; | ||
| 212 | } | ||
| 213 | #endif | ||
| 214 | last_position = position; | ||
| 215 | position += views[i].size; | ||
| 216 | } | ||
| 217 | |||
| 218 | return true; | ||
| 219 | |||
| 220 | bail: | ||
| 221 | // Argh! ERROR! Free what we grabbed so far so we can try again. | ||
| 222 | MemoryMap_Shutdown(views, i+1, flags, arena); | ||
| 223 | return false; | ||
| 224 | } | ||
| 225 | |||
| 226 | u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena *arena) | ||
| 227 | { | ||
| 228 | u32 total_mem = 0; | ||
| 229 | int base_attempts = 0; | ||
| 230 | |||
| 231 | for (int i = 0; i < num_views; i++) | ||
| 232 | { | ||
| 233 | SKIP(flags, views[i].flags); | ||
| 234 | if ((views[i].flags & MV_MIRROR_PREVIOUS) == 0) | ||
| 235 | total_mem += views[i].size; | ||
| 236 | } | ||
| 237 | // Grab some pagefile backed memory out of the void ... | ||
| 238 | arena->GrabLowMemSpace(total_mem); | ||
| 239 | |||
| 240 | // Now, create views in high memory where there's plenty of space. | ||
| 241 | #ifdef _M_X64 | ||
| 242 | u8 *base = MemArena::Find4GBBase(); | ||
| 243 | // This really shouldn't fail - in 64-bit, there will always be enough | ||
| 244 | // address space. | ||
| 245 | if (!Memory_TryBase(base, views, num_views, flags, arena)) | ||
| 246 | { | ||
| 247 | PanicAlert("MemoryMap_Setup: Failed finding a memory base."); | ||
| 248 | exit(0); | ||
| 249 | return 0; | ||
| 250 | } | ||
| 251 | #else | ||
| 252 | #ifdef _WIN32 | ||
| 253 | // Try a whole range of possible bases. Return once we got a valid one. | ||
| 254 | u32 max_base_addr = 0x7FFF0000 - 0x31000000; | ||
| 255 | u8 *base = NULL; | ||
| 256 | |||
| 257 | for (u32 base_addr = 0x40000; base_addr < max_base_addr; base_addr += 0x40000) | ||
| 258 | { | ||
| 259 | base_attempts++; | ||
| 260 | base = (u8 *)base_addr; | ||
| 261 | if (Memory_TryBase(base, views, num_views, flags, arena)) | ||
| 262 | { | ||
| 263 | INFO_LOG(MEMMAP, "Found valid memory base at %p after %i tries.", base, base_attempts); | ||
| 264 | base_attempts = 0; | ||
| 265 | break; | ||
| 266 | } | ||
| 267 | |||
| 268 | } | ||
| 269 | #else | ||
| 270 | // Linux32 is fine with the x64 method, although limited to 32-bit with no automirrors. | ||
| 271 | u8 *base = MemArena::Find4GBBase(); | ||
| 272 | if (!Memory_TryBase(base, views, num_views, flags, arena)) | ||
| 273 | { | ||
| 274 | PanicAlert("MemoryMap_Setup: Failed finding a memory base."); | ||
| 275 | exit(0); | ||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | #endif | ||
| 279 | |||
| 280 | #endif | ||
| 281 | if (base_attempts) | ||
| 282 | PanicAlert("No possible memory base pointer found!"); | ||
| 283 | return base; | ||
| 284 | } | ||
| 285 | |||
| 286 | void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena) | ||
| 287 | { | ||
| 288 | std::set<void*> freeset; | ||
| 289 | for (int i = 0; i < num_views; i++) | ||
| 290 | { | ||
| 291 | const MemoryView* view = &views[i]; | ||
| 292 | u8** outptrs[2] = {view->out_ptr_low, view->out_ptr}; | ||
| 293 | for (int j = 0; j < 2; j++) | ||
| 294 | { | ||
| 295 | u8** outptr = outptrs[j]; | ||
| 296 | if (outptr && *outptr && !freeset.count(*outptr)) | ||
| 297 | { | ||
| 298 | arena->ReleaseView(*outptr, view->size); | ||
| 299 | freeset.insert(*outptr); | ||
| 300 | *outptr = NULL; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
| 304 | } | ||
diff --git a/src/common/src/mem_arena.h b/src/common/src/mem_arena.h new file mode 100644 index 000000000..4c3ded0df --- /dev/null +++ b/src/common/src/mem_arena.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #ifndef _MEMARENA_H_ | ||
| 7 | #define _MEMARENA_H_ | ||
| 8 | |||
| 9 | #ifdef _WIN32 | ||
| 10 | #include <windows.h> | ||
| 11 | #endif | ||
| 12 | |||
| 13 | #include "common.h" | ||
| 14 | |||
| 15 | // This class lets you create a block of anonymous RAM, and then arbitrarily map views into it. | ||
| 16 | // Multiple views can mirror the same section of the block, which makes it very convenient for emulating | ||
| 17 | // memory mirrors. | ||
| 18 | |||
| 19 | class MemArena | ||
| 20 | { | ||
| 21 | public: | ||
| 22 | void GrabLowMemSpace(size_t size); | ||
| 23 | void ReleaseSpace(); | ||
| 24 | void *CreateView(s64 offset, size_t size, void *base = nullptr); | ||
| 25 | void ReleaseView(void *view, size_t size); | ||
| 26 | |||
| 27 | // This only finds 1 GB in 32-bit | ||
| 28 | static u8 *Find4GBBase(); | ||
| 29 | private: | ||
| 30 | |||
| 31 | #ifdef _WIN32 | ||
| 32 | HANDLE hMemoryMapping; | ||
| 33 | #else | ||
| 34 | int fd; | ||
| 35 | #endif | ||
| 36 | }; | ||
| 37 | |||
| 38 | enum { | ||
| 39 | MV_MIRROR_PREVIOUS = 1, | ||
| 40 | MV_FAKE_VMEM = 2, | ||
| 41 | MV_WII_ONLY = 4, | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct MemoryView | ||
| 45 | { | ||
| 46 | u8 **out_ptr_low; | ||
| 47 | u8 **out_ptr; | ||
| 48 | u32 virtual_address; | ||
| 49 | u32 size; | ||
| 50 | u32 flags; | ||
| 51 | }; | ||
| 52 | |||
| 53 | // Uses a memory arena to set up an emulator-friendly memory map according to | ||
| 54 | // a passed-in list of MemoryView structures. | ||
| 55 | u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena *arena); | ||
| 56 | void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena); | ||
| 57 | |||
| 58 | #endif // _MEMARENA_H_ | ||
diff --git a/src/common/src/memory_util.cpp b/src/common/src/memory_util.cpp new file mode 100644 index 000000000..346d2e525 --- /dev/null +++ b/src/common/src/memory_util.cpp | |||
| @@ -0,0 +1,197 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #include "common.h" | ||
| 7 | #include "memory_util.h" | ||
| 8 | #include "string_util.h" | ||
| 9 | |||
| 10 | #ifdef _WIN32 | ||
| 11 | #include <windows.h> | ||
| 12 | #include <psapi.h> | ||
| 13 | #else | ||
| 14 | #include <errno.h> | ||
| 15 | #include <stdio.h> | ||
| 16 | #endif | ||
| 17 | |||
| 18 | #if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) | ||
| 19 | #include <unistd.h> | ||
| 20 | #define PAGE_MASK (getpagesize() - 1) | ||
| 21 | #define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK)) | ||
| 22 | #endif | ||
| 23 | |||
| 24 | // This is purposely not a full wrapper for virtualalloc/mmap, but it | ||
| 25 | // provides exactly the primitive operations that Dolphin needs. | ||
| 26 | |||
| 27 | void* AllocateExecutableMemory(size_t size, bool low) | ||
| 28 | { | ||
| 29 | #if defined(_WIN32) | ||
| 30 | void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
| 31 | #else | ||
| 32 | static char *map_hint = 0; | ||
| 33 | #if defined(__x86_64__) && !defined(MAP_32BIT) | ||
| 34 | // This OS has no flag to enforce allocation below the 4 GB boundary, | ||
| 35 | // but if we hint that we want a low address it is very likely we will | ||
| 36 | // get one. | ||
| 37 | // An older version of this code used MAP_FIXED, but that has the side | ||
| 38 | // effect of discarding already mapped pages that happen to be in the | ||
| 39 | // requested virtual memory range (such as the emulated RAM, sometimes). | ||
| 40 | if (low && (!map_hint)) | ||
| 41 | map_hint = (char*)round_page(512*1024*1024); /* 0.5 GB rounded up to the next page */ | ||
| 42 | #endif | ||
| 43 | void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, | ||
| 44 | MAP_ANON | MAP_PRIVATE | ||
| 45 | #if defined(__x86_64__) && defined(MAP_32BIT) | ||
| 46 | | (low ? MAP_32BIT : 0) | ||
| 47 | #endif | ||
| 48 | , -1, 0); | ||
| 49 | #endif /* defined(_WIN32) */ | ||
| 50 | |||
| 51 | // printf("Mapped executable memory at %p (size %ld)\n", ptr, | ||
| 52 | // (unsigned long)size); | ||
| 53 | |||
| 54 | #if defined(__FreeBSD__) | ||
| 55 | if (ptr == MAP_FAILED) | ||
| 56 | { | ||
| 57 | ptr = NULL; | ||
| 58 | #else | ||
| 59 | if (ptr == NULL) | ||
| 60 | { | ||
| 61 | #endif | ||
| 62 | PanicAlert("Failed to allocate executable memory"); | ||
| 63 | } | ||
| 64 | #if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) | ||
| 65 | else | ||
| 66 | { | ||
| 67 | if (low) | ||
| 68 | { | ||
| 69 | map_hint += size; | ||
| 70 | map_hint = (char*)round_page(map_hint); /* round up to the next page */ | ||
| 71 | // printf("Next map will (hopefully) be at %p\n", map_hint); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | #endif | ||
| 75 | |||
| 76 | #if defined(_M_X64) | ||
| 77 | if ((u64)ptr >= 0x80000000 && low == true) | ||
| 78 | PanicAlert("Executable memory ended up above 2GB!"); | ||
| 79 | #endif | ||
| 80 | |||
| 81 | return ptr; | ||
| 82 | } | ||
| 83 | |||
| 84 | void* AllocateMemoryPages(size_t size) | ||
| 85 | { | ||
| 86 | #ifdef _WIN32 | ||
| 87 | void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); | ||
| 88 | #else | ||
| 89 | void* ptr = mmap(0, size, PROT_READ | PROT_WRITE, | ||
| 90 | MAP_ANON | MAP_PRIVATE, -1, 0); | ||
| 91 | #endif | ||
| 92 | |||
| 93 | // printf("Mapped memory at %p (size %ld)\n", ptr, | ||
| 94 | // (unsigned long)size); | ||
| 95 | |||
| 96 | if (ptr == NULL) | ||
| 97 | PanicAlert("Failed to allocate raw memory"); | ||
| 98 | |||
| 99 | return ptr; | ||
| 100 | } | ||
| 101 | |||
| 102 | void* AllocateAlignedMemory(size_t size,size_t alignment) | ||
| 103 | { | ||
| 104 | #ifdef _WIN32 | ||
| 105 | void* ptr = _aligned_malloc(size,alignment); | ||
| 106 | #else | ||
| 107 | void* ptr = NULL; | ||
| 108 | #ifdef ANDROID | ||
| 109 | ptr = memalign(alignment, size); | ||
| 110 | #else | ||
| 111 | if (posix_memalign(&ptr, alignment, size) != 0) | ||
| 112 | ERROR_LOG(MEMMAP, "Failed to allocate aligned memory"); | ||
| 113 | #endif | ||
| 114 | #endif | ||
| 115 | |||
| 116 | // printf("Mapped memory at %p (size %ld)\n", ptr, | ||
| 117 | // (unsigned long)size); | ||
| 118 | |||
| 119 | if (ptr == NULL) | ||
| 120 | PanicAlert("Failed to allocate aligned memory"); | ||
| 121 | |||
| 122 | return ptr; | ||
| 123 | } | ||
| 124 | |||
| 125 | void FreeMemoryPages(void* ptr, size_t size) | ||
| 126 | { | ||
| 127 | if (ptr) | ||
| 128 | { | ||
| 129 | #ifdef _WIN32 | ||
| 130 | |||
| 131 | if (!VirtualFree(ptr, 0, MEM_RELEASE)) | ||
| 132 | PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg()); | ||
| 133 | ptr = NULL; // Is this our responsibility? | ||
| 134 | |||
| 135 | #else | ||
| 136 | munmap(ptr, size); | ||
| 137 | #endif | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | void FreeAlignedMemory(void* ptr) | ||
| 142 | { | ||
| 143 | if (ptr) | ||
| 144 | { | ||
| 145 | #ifdef _WIN32 | ||
| 146 | _aligned_free(ptr); | ||
| 147 | #else | ||
| 148 | free(ptr); | ||
| 149 | #endif | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) | ||
| 154 | { | ||
| 155 | #ifdef _WIN32 | ||
| 156 | DWORD oldValue; | ||
| 157 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) | ||
| 158 | PanicAlert("WriteProtectMemory failed!\n%s", GetLastErrorMsg()); | ||
| 159 | #else | ||
| 160 | mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ); | ||
| 161 | #endif | ||
| 162 | } | ||
| 163 | |||
| 164 | void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) | ||
| 165 | { | ||
| 166 | #ifdef _WIN32 | ||
| 167 | DWORD oldValue; | ||
| 168 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue)) | ||
| 169 | PanicAlert("UnWriteProtectMemory failed!\n%s", GetLastErrorMsg()); | ||
| 170 | #else | ||
| 171 | mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ); | ||
| 172 | #endif | ||
| 173 | } | ||
| 174 | |||
| 175 | std::string MemUsage() | ||
| 176 | { | ||
| 177 | #ifdef _WIN32 | ||
| 178 | #pragma comment(lib, "psapi") | ||
| 179 | DWORD processID = GetCurrentProcessId(); | ||
| 180 | HANDLE hProcess; | ||
| 181 | PROCESS_MEMORY_COUNTERS pmc; | ||
| 182 | std::string Ret; | ||
| 183 | |||
| 184 | // Print information about the memory usage of the process. | ||
| 185 | |||
| 186 | hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); | ||
| 187 | if (NULL == hProcess) return "MemUsage Error"; | ||
| 188 | |||
| 189 | if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) | ||
| 190 | Ret = StringFromFormat("%s K", ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str()); | ||
| 191 | |||
| 192 | CloseHandle(hProcess); | ||
| 193 | return Ret; | ||
| 194 | #else | ||
| 195 | return ""; | ||
| 196 | #endif | ||
| 197 | } | ||
diff --git a/src/common/src/memory_util.h b/src/common/src/memory_util.h new file mode 100644 index 000000000..49b2589da --- /dev/null +++ b/src/common/src/memory_util.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | |||
| 6 | #ifndef _MEMORYUTIL_H | ||
| 7 | #define _MEMORYUTIL_H | ||
| 8 | |||
| 9 | #ifndef _WIN32 | ||
| 10 | #include <sys/mman.h> | ||
| 11 | #endif | ||
| 12 | #include <string> | ||
| 13 | |||
| 14 | void* AllocateExecutableMemory(size_t size, bool low = true); | ||
| 15 | void* AllocateMemoryPages(size_t size); | ||
| 16 | void FreeMemoryPages(void* ptr, size_t size); | ||
| 17 | void* AllocateAlignedMemory(size_t size,size_t alignment); | ||
| 18 | void FreeAlignedMemory(void* ptr); | ||
| 19 | void WriteProtectMemory(void* ptr, size_t size, bool executable = false); | ||
| 20 | void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); | ||
| 21 | std::string MemUsage(); | ||
| 22 | |||
| 23 | inline int GetPageSize() { return 4096; } | ||
| 24 | |||
| 25 | #endif | ||
diff --git a/src/common/src/misc.cpp b/src/common/src/misc.cpp new file mode 100644 index 000000000..719a98cb9 --- /dev/null +++ b/src/common/src/misc.cpp | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common.h" | ||
| 6 | |||
| 7 | // Neither Android nor OS X support TLS | ||
| 8 | #if defined(__APPLE__) || (ANDROID && __clang__) | ||
| 9 | #define __thread | ||
| 10 | #endif | ||
| 11 | |||
| 12 | // Generic function to get last error message. | ||
| 13 | // Call directly after the command or use the error num. | ||
| 14 | // This function might change the error code. | ||
| 15 | //const char* GetLastErrorMsg() | ||
| 16 | //{ | ||
| 17 | // static const size_t buff_size = 255; | ||
| 18 | // | ||
| 19 | //#ifdef _WIN32 | ||
| 20 | // static __declspec(thread) char err_str[buff_size] = {}; | ||
| 21 | // | ||
| 22 | // FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), | ||
| 23 | // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | ||
| 24 | // err_str, buff_size, NULL); | ||
| 25 | //#else | ||
| 26 | // static __thread char err_str[buff_size] = {}; | ||
| 27 | // | ||
| 28 | // // Thread safe (XSI-compliant) | ||
| 29 | // strerror_r(errno, err_str, buff_size); | ||
| 30 | //#endif | ||
| 31 | // | ||
| 32 | // return err_str; | ||
| 33 | //} | ||
diff --git a/src/common/src/msg_handler.cpp b/src/common/src/msg_handler.cpp new file mode 100644 index 000000000..62cbe0aa7 --- /dev/null +++ b/src/common/src/msg_handler.cpp | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <stdio.h> // System | ||
| 6 | |||
| 7 | #include "common.h" // Local | ||
| 8 | #include "string_util.h" | ||
| 9 | |||
| 10 | bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style); | ||
| 11 | static MsgAlertHandler msg_handler = DefaultMsgHandler; | ||
| 12 | static bool AlertEnabled = true; | ||
| 13 | |||
| 14 | std::string DefaultStringTranslator(const char* text); | ||
| 15 | static StringTranslator str_translator = DefaultStringTranslator; | ||
| 16 | |||
| 17 | // Select which of these functions that are used for message boxes. If | ||
| 18 | // wxWidgets is enabled we will use wxMsgAlert() that is defined in Main.cpp | ||
| 19 | void RegisterMsgAlertHandler(MsgAlertHandler handler) | ||
| 20 | { | ||
| 21 | msg_handler = handler; | ||
| 22 | } | ||
| 23 | |||
| 24 | // Select translation function. For wxWidgets use wxStringTranslator in Main.cpp | ||
| 25 | void RegisterStringTranslator(StringTranslator translator) | ||
| 26 | { | ||
| 27 | str_translator = translator; | ||
| 28 | } | ||
| 29 | |||
| 30 | // enable/disable the alert handler | ||
| 31 | void SetEnableAlert(bool enable) | ||
| 32 | { | ||
| 33 | AlertEnabled = enable; | ||
| 34 | } | ||
| 35 | |||
| 36 | // This is the first stop for gui alerts where the log is updated and the | ||
| 37 | // correct window is shown | ||
| 38 | bool MsgAlert(bool yes_no, int Style, const char* format, ...) | ||
| 39 | { | ||
| 40 | // Read message and write it to the log | ||
| 41 | std::string caption; | ||
| 42 | char buffer[2048]; | ||
| 43 | |||
| 44 | static std::string info_caption; | ||
| 45 | static std::string warn_caption; | ||
| 46 | static std::string ques_caption; | ||
| 47 | static std::string crit_caption; | ||
| 48 | |||
| 49 | if (!info_caption.length()) | ||
| 50 | { | ||
| 51 | info_caption = str_translator(_trans("Information")); | ||
| 52 | ques_caption = str_translator(_trans("Question")); | ||
| 53 | warn_caption = str_translator(_trans("Warning")); | ||
| 54 | crit_caption = str_translator(_trans("Critical")); | ||
| 55 | } | ||
| 56 | |||
| 57 | switch(Style) | ||
| 58 | { | ||
| 59 | case INFORMATION: | ||
| 60 | caption = info_caption; | ||
| 61 | break; | ||
| 62 | case QUESTION: | ||
| 63 | caption = ques_caption; | ||
| 64 | break; | ||
| 65 | case WARNING: | ||
| 66 | caption = warn_caption; | ||
| 67 | break; | ||
| 68 | case CRITICAL: | ||
| 69 | caption = crit_caption; | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | |||
| 73 | va_list args; | ||
| 74 | va_start(args, format); | ||
| 75 | CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args); | ||
| 76 | va_end(args); | ||
| 77 | |||
| 78 | ERROR_LOG(MASTER_LOG, "%s: %s", caption.c_str(), buffer); | ||
| 79 | |||
| 80 | // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored | ||
| 81 | if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL)) | ||
| 82 | return msg_handler(caption.c_str(), buffer, yes_no, Style); | ||
| 83 | |||
| 84 | return true; | ||
| 85 | } | ||
| 86 | |||
| 87 | // Default non library dependent panic alert | ||
| 88 | bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style) | ||
| 89 | { | ||
| 90 | //#ifdef _WIN32 | ||
| 91 | // int STYLE = MB_ICONINFORMATION; | ||
| 92 | // if (Style == QUESTION) STYLE = MB_ICONQUESTION; | ||
| 93 | // if (Style == WARNING) STYLE = MB_ICONWARNING; | ||
| 94 | // | ||
| 95 | // return IDYES == MessageBox(0, UTF8ToTStr(text).c_str(), UTF8ToTStr(caption).c_str(), STYLE | (yes_no ? MB_YESNO : MB_OK)); | ||
| 96 | //#else | ||
| 97 | printf("%s\n", text); | ||
| 98 | return true; | ||
| 99 | //#endif | ||
| 100 | } | ||
| 101 | |||
| 102 | // Default (non) translator | ||
| 103 | std::string DefaultStringTranslator(const char* text) | ||
| 104 | { | ||
| 105 | return text; | ||
| 106 | } | ||
| 107 | |||
diff --git a/src/common/src/msg_handler.h b/src/common/src/msg_handler.h new file mode 100644 index 000000000..7de10c7b0 --- /dev/null +++ b/src/common/src/msg_handler.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _MSGHANDLER_H_ | ||
| 6 | #define _MSGHANDLER_H_ | ||
| 7 | |||
| 8 | #include <string> | ||
| 9 | |||
| 10 | // Message alerts | ||
| 11 | enum MSG_TYPE | ||
| 12 | { | ||
| 13 | INFORMATION, | ||
| 14 | QUESTION, | ||
| 15 | WARNING, | ||
| 16 | CRITICAL | ||
| 17 | }; | ||
| 18 | |||
| 19 | typedef bool (*MsgAlertHandler)(const char* caption, const char* text, | ||
| 20 | bool yes_no, int Style); | ||
| 21 | typedef std::string (*StringTranslator)(const char* text); | ||
| 22 | |||
| 23 | void RegisterMsgAlertHandler(MsgAlertHandler handler); | ||
| 24 | void RegisterStringTranslator(StringTranslator translator); | ||
| 25 | |||
| 26 | extern bool MsgAlert(bool yes_no, int Style, const char* format, ...) | ||
| 27 | #ifdef __GNUC__ | ||
| 28 | __attribute__((format(printf, 3, 4))) | ||
| 29 | #endif | ||
| 30 | ; | ||
| 31 | void SetEnableAlert(bool enable); | ||
| 32 | |||
| 33 | #ifndef GEKKO | ||
| 34 | #ifdef _WIN32 | ||
| 35 | #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) | ||
| 36 | #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) | ||
| 37 | #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) | ||
| 38 | #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) | ||
| 39 | #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) | ||
| 40 | // Use these macros (that do the same thing) if the message should be translated. | ||
| 41 | #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) | ||
| 42 | #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) | ||
| 43 | #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) | ||
| 44 | #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) | ||
| 45 | #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) | ||
| 46 | #else | ||
| 47 | #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) | ||
| 48 | #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) | ||
| 49 | #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) | ||
| 50 | #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) | ||
| 51 | #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) | ||
| 52 | // Use these macros (that do the same thing) if the message should be translated. | ||
| 53 | #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) | ||
| 54 | #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) | ||
| 55 | #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) | ||
| 56 | #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) | ||
| 57 | #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) | ||
| 58 | #endif | ||
| 59 | #else | ||
| 60 | // GEKKO | ||
| 61 | #define SuccessAlert(format, ...) ; | ||
| 62 | #define PanicAlert(format, ...) ; | ||
| 63 | #define PanicYesNo(format, ...) ; | ||
| 64 | #define AskYesNo(format, ...) ; | ||
| 65 | #define CriticalAlert(format, ...) ; | ||
| 66 | #define SuccessAlertT(format, ...) ; | ||
| 67 | #define PanicAlertT(format, ...) ; | ||
| 68 | #define PanicYesNoT(format, ...) ; | ||
| 69 | #define AskYesNoT(format, ...) ; | ||
| 70 | #define CriticalAlertT(format, ...) ; | ||
| 71 | #endif | ||
| 72 | |||
| 73 | #endif // _MSGHANDLER_H_ | ||
diff --git a/src/common/src/scm_rev.h b/src/common/src/scm_rev.h new file mode 100644 index 000000000..cb4eccfd0 --- /dev/null +++ b/src/common/src/scm_rev.h | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | #define SCM_REV_STR "7d11f8cedd7c135d96880f19ecbd3ff87a60a11f" | ||
| 2 | #define SCM_DESC_STR "3.5-254-dirty" | ||
| 3 | #define SCM_BRANCH_STR "master" | ||
| 4 | #define SCM_IS_MASTER 1 | ||
diff --git a/src/common/src/std_condition_variable.h b/src/common/src/std_condition_variable.h new file mode 100644 index 000000000..d44545e61 --- /dev/null +++ b/src/common/src/std_condition_variable.h | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | |||
| 2 | #ifndef CONDITION_VARIABLE_H_ | ||
| 3 | #define CONDITION_VARIABLE_H_ | ||
| 4 | |||
| 5 | #define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z)) | ||
| 6 | #define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) | ||
| 7 | |||
| 8 | #ifndef __has_include | ||
| 9 | #define __has_include(s) 0 | ||
| 10 | #endif | ||
| 11 | |||
| 12 | #if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ | ||
| 13 | |||
| 14 | // GCC 4.4 provides <condition_variable> | ||
| 15 | #include <condition_variable> | ||
| 16 | |||
| 17 | #elif __has_include(<condition_variable>) && !ANDROID | ||
| 18 | |||
| 19 | // clang and libc++ provide <condition_variable> on OSX. However, the version | ||
| 20 | // of libc++ bundled with OSX 10.7 and 10.8 is buggy: it uses _ as a variable. | ||
| 21 | // | ||
| 22 | // We work around this issue by undefining and redefining _. | ||
| 23 | |||
| 24 | #undef _ | ||
| 25 | #include <condition_variable> | ||
| 26 | #define _(s) wxGetTranslation((s)) | ||
| 27 | |||
| 28 | #else | ||
| 29 | |||
| 30 | // partial std::condition_variable implementation for win32/pthread | ||
| 31 | |||
| 32 | #include "std_mutex.h" | ||
| 33 | |||
| 34 | #if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__) | ||
| 35 | #define USE_RVALUE_REFERENCES | ||
| 36 | #endif | ||
| 37 | |||
| 38 | #if defined(_WIN32) && defined(_M_X64) | ||
| 39 | #define USE_CONDITION_VARIABLES | ||
| 40 | #elif defined(_WIN32) | ||
| 41 | #define USE_EVENTS | ||
| 42 | #endif | ||
| 43 | |||
| 44 | namespace std | ||
| 45 | { | ||
| 46 | |||
| 47 | class condition_variable | ||
| 48 | { | ||
| 49 | #if defined(_WIN32) && defined(USE_CONDITION_VARIABLES) | ||
| 50 | typedef CONDITION_VARIABLE native_type; | ||
| 51 | #elif defined(_WIN32) | ||
| 52 | typedef HANDLE native_type; | ||
| 53 | #else | ||
| 54 | typedef pthread_cond_t native_type; | ||
| 55 | #endif | ||
| 56 | |||
| 57 | public: | ||
| 58 | |||
| 59 | #ifdef USE_EVENTS | ||
| 60 | typedef native_type native_handle_type; | ||
| 61 | #else | ||
| 62 | typedef native_type* native_handle_type; | ||
| 63 | #endif | ||
| 64 | |||
| 65 | condition_variable() | ||
| 66 | { | ||
| 67 | #if defined(_WIN32) && defined(USE_CONDITION_VARIABLES) | ||
| 68 | InitializeConditionVariable(&m_handle); | ||
| 69 | #elif defined(_WIN32) | ||
| 70 | m_handle = CreateEvent(NULL, false, false, NULL); | ||
| 71 | #else | ||
| 72 | pthread_cond_init(&m_handle, NULL); | ||
| 73 | #endif | ||
| 74 | } | ||
| 75 | |||
| 76 | ~condition_variable() | ||
| 77 | { | ||
| 78 | #if defined(_WIN32) && !defined(USE_CONDITION_VARIABLES) | ||
| 79 | CloseHandle(m_handle); | ||
| 80 | #elif !defined(_WIN32) | ||
| 81 | pthread_cond_destroy(&m_handle); | ||
| 82 | #endif | ||
| 83 | } | ||
| 84 | |||
| 85 | condition_variable(const condition_variable&) /*= delete*/; | ||
| 86 | condition_variable& operator=(const condition_variable&) /*= delete*/; | ||
| 87 | |||
| 88 | void notify_one() | ||
| 89 | { | ||
| 90 | #if defined(_WIN32) && defined(USE_CONDITION_VARIABLES) | ||
| 91 | WakeConditionVariable(&m_handle); | ||
| 92 | #elif defined(_WIN32) | ||
| 93 | SetEvent(m_handle); | ||
| 94 | #else | ||
| 95 | pthread_cond_signal(&m_handle); | ||
| 96 | #endif | ||
| 97 | } | ||
| 98 | |||
| 99 | void notify_all() | ||
| 100 | { | ||
| 101 | #if defined(_WIN32) && defined(USE_CONDITION_VARIABLES) | ||
| 102 | WakeAllConditionVariable(&m_handle); | ||
| 103 | #elif defined(_WIN32) | ||
| 104 | // TODO: broken | ||
| 105 | SetEvent(m_handle); | ||
| 106 | #else | ||
| 107 | pthread_cond_broadcast(&m_handle); | ||
| 108 | #endif | ||
| 109 | } | ||
| 110 | |||
| 111 | void wait(unique_lock<mutex>& lock) | ||
| 112 | { | ||
| 113 | #ifdef _WIN32 | ||
| 114 | #ifdef USE_SRWLOCKS | ||
| 115 | SleepConditionVariableSRW(&m_handle, lock.mutex()->native_handle(), INFINITE, 0); | ||
| 116 | #elif defined(USE_CONDITION_VARIABLES) | ||
| 117 | SleepConditionVariableCS(&m_handle, lock.mutex()->native_handle(), INFINITE); | ||
| 118 | #else | ||
| 119 | // TODO: broken, the unlock and wait need to be atomic | ||
| 120 | lock.unlock(); | ||
| 121 | WaitForSingleObject(m_handle, INFINITE); | ||
| 122 | lock.lock(); | ||
| 123 | #endif | ||
| 124 | #else | ||
| 125 | pthread_cond_wait(&m_handle, lock.mutex()->native_handle()); | ||
| 126 | #endif | ||
| 127 | } | ||
| 128 | |||
| 129 | template <class Predicate> | ||
| 130 | void wait(unique_lock<mutex>& lock, Predicate pred) | ||
| 131 | { | ||
| 132 | while (!pred()) | ||
| 133 | wait(lock); | ||
| 134 | } | ||
| 135 | |||
| 136 | //template <class Clock, class Duration> | ||
| 137 | //cv_status wait_until(unique_lock<mutex>& lock, | ||
| 138 | // const chrono::time_point<Clock, Duration>& abs_time); | ||
| 139 | |||
| 140 | //template <class Clock, class Duration, class Predicate> | ||
| 141 | // bool wait_until(unique_lock<mutex>& lock, | ||
| 142 | // const chrono::time_point<Clock, Duration>& abs_time, | ||
| 143 | // Predicate pred); | ||
| 144 | |||
| 145 | //template <class Rep, class Period> | ||
| 146 | //cv_status wait_for(unique_lock<mutex>& lock, | ||
| 147 | // const chrono::duration<Rep, Period>& rel_time); | ||
| 148 | |||
| 149 | //template <class Rep, class Period, class Predicate> | ||
| 150 | // bool wait_for(unique_lock<mutex>& lock, | ||
| 151 | // const chrono::duration<Rep, Period>& rel_time, | ||
| 152 | // Predicate pred); | ||
| 153 | |||
| 154 | native_handle_type native_handle() | ||
| 155 | { | ||
| 156 | #ifdef USE_EVENTS | ||
| 157 | return m_handle; | ||
| 158 | #else | ||
| 159 | return &m_handle; | ||
| 160 | #endif | ||
| 161 | } | ||
| 162 | |||
| 163 | private: | ||
| 164 | native_type m_handle; | ||
| 165 | }; | ||
| 166 | |||
| 167 | } | ||
| 168 | |||
| 169 | #endif | ||
| 170 | #endif | ||
diff --git a/src/common/src/std_mutex.h b/src/common/src/std_mutex.h new file mode 100644 index 000000000..ce46a2f59 --- /dev/null +++ b/src/common/src/std_mutex.h | |||
| @@ -0,0 +1,365 @@ | |||
| 1 | |||
| 2 | #ifndef MUTEX_H_ | ||
| 3 | #define MUTEX_H_ | ||
| 4 | |||
| 5 | #define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z)) | ||
| 6 | #define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) | ||
| 7 | |||
| 8 | #ifndef __has_include | ||
| 9 | #define __has_include(s) 0 | ||
| 10 | #endif | ||
| 11 | |||
| 12 | #if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ | ||
| 13 | // GCC 4.4 provides <mutex> | ||
| 14 | #include <mutex> | ||
| 15 | #elif __has_include(<mutex>) && !ANDROID | ||
| 16 | // Clang + libc++ | ||
| 17 | #include <mutex> | ||
| 18 | #else | ||
| 19 | |||
| 20 | // partial <mutex> implementation for win32/pthread | ||
| 21 | |||
| 22 | #include <algorithm> | ||
| 23 | |||
| 24 | #if defined(_WIN32) | ||
| 25 | // WIN32 | ||
| 26 | #define WIN32_LEAN_AND_MEAN | ||
| 27 | #include <Windows.h> | ||
| 28 | |||
| 29 | #else | ||
| 30 | // POSIX | ||
| 31 | #include <pthread.h> | ||
| 32 | |||
| 33 | #endif | ||
| 34 | |||
| 35 | #if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__) | ||
| 36 | #define USE_RVALUE_REFERENCES | ||
| 37 | #endif | ||
| 38 | |||
| 39 | #if defined(_WIN32) && defined(_M_X64) | ||
| 40 | #define USE_SRWLOCKS | ||
| 41 | #endif | ||
| 42 | |||
| 43 | namespace std | ||
| 44 | { | ||
| 45 | |||
| 46 | class recursive_mutex | ||
| 47 | { | ||
| 48 | #ifdef _WIN32 | ||
| 49 | typedef CRITICAL_SECTION native_type; | ||
| 50 | #else | ||
| 51 | typedef pthread_mutex_t native_type; | ||
| 52 | #endif | ||
| 53 | |||
| 54 | public: | ||
| 55 | typedef native_type* native_handle_type; | ||
| 56 | |||
| 57 | recursive_mutex(const recursive_mutex&) /*= delete*/; | ||
| 58 | recursive_mutex& operator=(const recursive_mutex&) /*= delete*/; | ||
| 59 | |||
| 60 | recursive_mutex() | ||
| 61 | { | ||
| 62 | #ifdef _WIN32 | ||
| 63 | InitializeCriticalSection(&m_handle); | ||
| 64 | #else | ||
| 65 | pthread_mutexattr_t attr; | ||
| 66 | pthread_mutexattr_init(&attr); | ||
| 67 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); | ||
| 68 | pthread_mutex_init(&m_handle, &attr); | ||
| 69 | #endif | ||
| 70 | } | ||
| 71 | |||
| 72 | ~recursive_mutex() | ||
| 73 | { | ||
| 74 | #ifdef _WIN32 | ||
| 75 | DeleteCriticalSection(&m_handle); | ||
| 76 | #else | ||
| 77 | pthread_mutex_destroy(&m_handle); | ||
| 78 | #endif | ||
| 79 | } | ||
| 80 | |||
| 81 | void lock() | ||
| 82 | { | ||
| 83 | #ifdef _WIN32 | ||
| 84 | EnterCriticalSection(&m_handle); | ||
| 85 | #else | ||
| 86 | pthread_mutex_lock(&m_handle); | ||
| 87 | #endif | ||
| 88 | } | ||
| 89 | |||
| 90 | void unlock() | ||
| 91 | { | ||
| 92 | #ifdef _WIN32 | ||
| 93 | LeaveCriticalSection(&m_handle); | ||
| 94 | #else | ||
| 95 | pthread_mutex_unlock(&m_handle); | ||
| 96 | #endif | ||
| 97 | } | ||
| 98 | |||
| 99 | bool try_lock() | ||
| 100 | { | ||
| 101 | #ifdef _WIN32 | ||
| 102 | return (0 != TryEnterCriticalSection(&m_handle)); | ||
| 103 | #else | ||
| 104 | return !pthread_mutex_trylock(&m_handle); | ||
| 105 | #endif | ||
| 106 | } | ||
| 107 | |||
| 108 | native_handle_type native_handle() | ||
| 109 | { | ||
| 110 | return &m_handle; | ||
| 111 | } | ||
| 112 | |||
| 113 | private: | ||
| 114 | native_type m_handle; | ||
| 115 | }; | ||
| 116 | |||
| 117 | #if !defined(_WIN32) || defined(USE_SRWLOCKS) | ||
| 118 | |||
| 119 | class mutex | ||
| 120 | { | ||
| 121 | #ifdef _WIN32 | ||
| 122 | typedef SRWLOCK native_type; | ||
| 123 | #else | ||
| 124 | typedef pthread_mutex_t native_type; | ||
| 125 | #endif | ||
| 126 | |||
| 127 | public: | ||
| 128 | typedef native_type* native_handle_type; | ||
| 129 | |||
| 130 | mutex(const mutex&) /*= delete*/; | ||
| 131 | mutex& operator=(const mutex&) /*= delete*/; | ||
| 132 | |||
| 133 | mutex() | ||
| 134 | { | ||
| 135 | #ifdef _WIN32 | ||
| 136 | InitializeSRWLock(&m_handle); | ||
| 137 | #else | ||
| 138 | pthread_mutex_init(&m_handle, NULL); | ||
| 139 | #endif | ||
| 140 | } | ||
| 141 | |||
| 142 | ~mutex() | ||
| 143 | { | ||
| 144 | #ifdef _WIN32 | ||
| 145 | #else | ||
| 146 | pthread_mutex_destroy(&m_handle); | ||
| 147 | #endif | ||
| 148 | } | ||
| 149 | |||
| 150 | void lock() | ||
| 151 | { | ||
| 152 | #ifdef _WIN32 | ||
| 153 | AcquireSRWLockExclusive(&m_handle); | ||
| 154 | #else | ||
| 155 | pthread_mutex_lock(&m_handle); | ||
| 156 | #endif | ||
| 157 | } | ||
| 158 | |||
| 159 | void unlock() | ||
| 160 | { | ||
| 161 | #ifdef _WIN32 | ||
| 162 | ReleaseSRWLockExclusive(&m_handle); | ||
| 163 | #else | ||
| 164 | pthread_mutex_unlock(&m_handle); | ||
| 165 | #endif | ||
| 166 | } | ||
| 167 | |||
| 168 | bool try_lock() | ||
| 169 | { | ||
| 170 | #ifdef _WIN32 | ||
| 171 | // XXX TryAcquireSRWLockExclusive requires Windows 7! | ||
| 172 | // return (0 != TryAcquireSRWLockExclusive(&m_handle)); | ||
| 173 | return false; | ||
| 174 | #else | ||
| 175 | return !pthread_mutex_trylock(&m_handle); | ||
| 176 | #endif | ||
| 177 | } | ||
| 178 | |||
| 179 | native_handle_type native_handle() | ||
| 180 | { | ||
| 181 | return &m_handle; | ||
| 182 | } | ||
| 183 | |||
| 184 | private: | ||
| 185 | native_type m_handle; | ||
| 186 | }; | ||
| 187 | |||
| 188 | #else | ||
| 189 | typedef recursive_mutex mutex; // just use CriticalSections | ||
| 190 | |||
| 191 | #endif | ||
| 192 | |||
| 193 | enum defer_lock_t { defer_lock }; | ||
| 194 | enum try_to_lock_t { try_to_lock }; | ||
| 195 | enum adopt_lock_t { adopt_lock }; | ||
| 196 | |||
| 197 | template <class Mutex> | ||
| 198 | class lock_guard | ||
| 199 | { | ||
| 200 | public: | ||
| 201 | typedef Mutex mutex_type; | ||
| 202 | |||
| 203 | explicit lock_guard(mutex_type& m) | ||
| 204 | : pm(m) | ||
| 205 | { | ||
| 206 | m.lock(); | ||
| 207 | } | ||
| 208 | |||
| 209 | lock_guard(mutex_type& m, adopt_lock_t) | ||
| 210 | : pm(m) | ||
| 211 | { | ||
| 212 | } | ||
| 213 | |||
| 214 | ~lock_guard() | ||
| 215 | { | ||
| 216 | pm.unlock(); | ||
| 217 | } | ||
| 218 | |||
| 219 | lock_guard(lock_guard const&) /*= delete*/; | ||
| 220 | lock_guard& operator=(lock_guard const&) /*= delete*/; | ||
| 221 | |||
| 222 | private: | ||
| 223 | mutex_type& pm; | ||
| 224 | }; | ||
| 225 | |||
| 226 | template <class Mutex> | ||
| 227 | class unique_lock | ||
| 228 | { | ||
| 229 | public: | ||
| 230 | typedef Mutex mutex_type; | ||
| 231 | |||
| 232 | unique_lock() | ||
| 233 | : pm(NULL), owns(false) | ||
| 234 | {} | ||
| 235 | |||
| 236 | /*explicit*/ unique_lock(mutex_type& m) | ||
| 237 | : pm(&m), owns(true) | ||
| 238 | { | ||
| 239 | m.lock(); | ||
| 240 | } | ||
| 241 | |||
| 242 | unique_lock(mutex_type& m, defer_lock_t) | ||
| 243 | : pm(&m), owns(false) | ||
| 244 | {} | ||
| 245 | |||
| 246 | unique_lock(mutex_type& m, try_to_lock_t) | ||
| 247 | : pm(&m), owns(m.try_lock()) | ||
| 248 | {} | ||
| 249 | |||
| 250 | unique_lock(mutex_type& m, adopt_lock_t) | ||
| 251 | : pm(&m), owns(true) | ||
| 252 | {} | ||
| 253 | |||
| 254 | //template <class Clock, class Duration> | ||
| 255 | //unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time); | ||
| 256 | |||
| 257 | //template <class Rep, class Period> | ||
| 258 | //unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time); | ||
| 259 | |||
| 260 | ~unique_lock() | ||
| 261 | { | ||
| 262 | if (owns_lock()) | ||
| 263 | mutex()->unlock(); | ||
| 264 | } | ||
| 265 | |||
| 266 | #ifdef USE_RVALUE_REFERENCES | ||
| 267 | unique_lock& operator=(const unique_lock&) /*= delete*/; | ||
| 268 | |||
| 269 | unique_lock& operator=(unique_lock&& other) | ||
| 270 | { | ||
| 271 | #else | ||
| 272 | unique_lock& operator=(const unique_lock& u) | ||
| 273 | { | ||
| 274 | // ugly const_cast to get around lack of rvalue references | ||
| 275 | unique_lock& other = const_cast<unique_lock&>(u); | ||
| 276 | #endif | ||
| 277 | swap(other); | ||
| 278 | return *this; | ||
| 279 | } | ||
| 280 | |||
| 281 | #ifdef USE_RVALUE_REFERENCES | ||
| 282 | unique_lock(const unique_lock&) /*= delete*/; | ||
| 283 | |||
| 284 | unique_lock(unique_lock&& other) | ||
| 285 | : pm(NULL), owns(false) | ||
| 286 | { | ||
| 287 | #else | ||
| 288 | unique_lock(const unique_lock& u) | ||
| 289 | : pm(NULL), owns(false) | ||
| 290 | { | ||
| 291 | // ugly const_cast to get around lack of rvalue references | ||
| 292 | unique_lock& other = const_cast<unique_lock&>(u); | ||
| 293 | #endif | ||
| 294 | swap(other); | ||
| 295 | } | ||
| 296 | |||
| 297 | void lock() | ||
| 298 | { | ||
| 299 | mutex()->lock(); | ||
| 300 | owns = true; | ||
| 301 | } | ||
| 302 | |||
| 303 | bool try_lock() | ||
| 304 | { | ||
| 305 | owns = mutex()->try_lock(); | ||
| 306 | return owns; | ||
| 307 | } | ||
| 308 | |||
| 309 | //template <class Rep, class Period> | ||
| 310 | //bool try_lock_for(const chrono::duration<Rep, Period>& rel_time); | ||
| 311 | //template <class Clock, class Duration> | ||
| 312 | //bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time); | ||
| 313 | |||
| 314 | void unlock() | ||
| 315 | { | ||
| 316 | mutex()->unlock(); | ||
| 317 | owns = false; | ||
| 318 | } | ||
| 319 | |||
| 320 | void swap(unique_lock& u) | ||
| 321 | { | ||
| 322 | std::swap(pm, u.pm); | ||
| 323 | std::swap(owns, u.owns); | ||
| 324 | } | ||
| 325 | |||
| 326 | mutex_type* release() | ||
| 327 | { | ||
| 328 | auto const ret = mutex(); | ||
| 329 | |||
| 330 | pm = NULL; | ||
| 331 | owns = false; | ||
| 332 | |||
| 333 | return ret; | ||
| 334 | } | ||
| 335 | |||
| 336 | bool owns_lock() const | ||
| 337 | { | ||
| 338 | return owns; | ||
| 339 | } | ||
| 340 | |||
| 341 | //explicit operator bool () const | ||
| 342 | //{ | ||
| 343 | // return owns_lock(); | ||
| 344 | //} | ||
| 345 | |||
| 346 | mutex_type* mutex() const | ||
| 347 | { | ||
| 348 | return pm; | ||
| 349 | } | ||
| 350 | |||
| 351 | private: | ||
| 352 | mutex_type* pm; | ||
| 353 | bool owns; | ||
| 354 | }; | ||
| 355 | |||
| 356 | template <class Mutex> | ||
| 357 | void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) | ||
| 358 | { | ||
| 359 | x.swap(y); | ||
| 360 | } | ||
| 361 | |||
| 362 | } | ||
| 363 | |||
| 364 | #endif | ||
| 365 | #endif | ||
diff --git a/src/common/src/std_thread.h b/src/common/src/std_thread.h new file mode 100644 index 000000000..e43d28344 --- /dev/null +++ b/src/common/src/std_thread.h | |||
| @@ -0,0 +1,317 @@ | |||
| 1 | |||
| 2 | #ifndef STD_THREAD_H_ | ||
| 3 | #define STD_THREAD_H_ | ||
| 4 | |||
| 5 | #define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z)) | ||
| 6 | #define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) | ||
| 7 | |||
| 8 | #ifndef __has_include | ||
| 9 | #define __has_include(s) 0 | ||
| 10 | #endif | ||
| 11 | |||
| 12 | #if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ | ||
| 13 | // GCC 4.4 provides <thread> | ||
| 14 | #ifndef _GLIBCXX_USE_SCHED_YIELD | ||
| 15 | #define _GLIBCXX_USE_SCHED_YIELD | ||
| 16 | #endif | ||
| 17 | #include <thread> | ||
| 18 | #elif __has_include(<thread>) && !ANDROID | ||
| 19 | // Clang + libc++ | ||
| 20 | #include <thread> | ||
| 21 | #else | ||
| 22 | |||
| 23 | // partial std::thread implementation for win32/pthread | ||
| 24 | |||
| 25 | #include <algorithm> | ||
| 26 | |||
| 27 | #if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__) | ||
| 28 | #define USE_RVALUE_REFERENCES | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #ifdef __APPLE__ | ||
| 32 | #import <Foundation/NSAutoreleasePool.h> | ||
| 33 | #endif | ||
| 34 | |||
| 35 | #if defined(_WIN32) | ||
| 36 | // WIN32 | ||
| 37 | |||
| 38 | #define WIN32_LEAN_AND_MEAN | ||
| 39 | #include <Windows.h> | ||
| 40 | |||
| 41 | #if defined(_MSC_VER) && defined(_MT) | ||
| 42 | // When linking with LIBCMT (the multithreaded C library), Microsoft recommends | ||
| 43 | // using _beginthreadex instead of CreateThread. | ||
| 44 | #define USE_BEGINTHREADEX | ||
| 45 | #include <process.h> | ||
| 46 | #endif | ||
| 47 | |||
| 48 | #ifdef USE_BEGINTHREADEX | ||
| 49 | #define THREAD_ID unsigned | ||
| 50 | #define THREAD_RETURN unsigned __stdcall | ||
| 51 | #else | ||
| 52 | #define THREAD_ID DWORD | ||
| 53 | #define THREAD_RETURN DWORD WINAPI | ||
| 54 | #endif | ||
| 55 | #define THREAD_HANDLE HANDLE | ||
| 56 | |||
| 57 | #else | ||
| 58 | // PTHREAD | ||
| 59 | |||
| 60 | #include <unistd.h> | ||
| 61 | |||
| 62 | #ifndef _POSIX_THREADS | ||
| 63 | #error unsupported platform (no pthreads?) | ||
| 64 | #endif | ||
| 65 | |||
| 66 | #include <pthread.h> | ||
| 67 | |||
| 68 | #define THREAD_ID pthread_t | ||
| 69 | #define THREAD_HANDLE pthread_t | ||
| 70 | #define THREAD_RETURN void* | ||
| 71 | |||
| 72 | #endif | ||
| 73 | |||
| 74 | namespace std | ||
| 75 | { | ||
| 76 | |||
| 77 | class thread | ||
| 78 | { | ||
| 79 | public: | ||
| 80 | typedef THREAD_HANDLE native_handle_type; | ||
| 81 | |||
| 82 | class id | ||
| 83 | { | ||
| 84 | friend class thread; | ||
| 85 | public: | ||
| 86 | id() : m_thread(0) {} | ||
| 87 | id(THREAD_ID _id) : m_thread(_id) {} | ||
| 88 | |||
| 89 | bool operator==(const id& rhs) const | ||
| 90 | { | ||
| 91 | return m_thread == rhs.m_thread; | ||
| 92 | } | ||
| 93 | |||
| 94 | bool operator!=(const id& rhs) const | ||
| 95 | { | ||
| 96 | return !(*this == rhs); | ||
| 97 | } | ||
| 98 | |||
| 99 | bool operator<(const id& rhs) const | ||
| 100 | { | ||
| 101 | return m_thread < rhs.m_thread; | ||
| 102 | } | ||
| 103 | |||
| 104 | private: | ||
| 105 | THREAD_ID m_thread; | ||
| 106 | }; | ||
| 107 | |||
| 108 | // no variadic template support in msvc | ||
| 109 | //template <typename C, typename... A> | ||
| 110 | //thread(C&& func, A&&... args); | ||
| 111 | |||
| 112 | template <typename C> | ||
| 113 | thread(C func) | ||
| 114 | { | ||
| 115 | StartThread(new Func<C>(func)); | ||
| 116 | } | ||
| 117 | |||
| 118 | template <typename C, typename A> | ||
| 119 | thread(C func, A arg) | ||
| 120 | { | ||
| 121 | StartThread(new FuncArg<C, A>(func, arg)); | ||
| 122 | } | ||
| 123 | |||
| 124 | thread() /*= default;*/ {} | ||
| 125 | |||
| 126 | #ifdef USE_RVALUE_REFERENCES | ||
| 127 | thread(const thread&) /*= delete*/; | ||
| 128 | |||
| 129 | thread(thread&& other) | ||
| 130 | { | ||
| 131 | #else | ||
| 132 | thread(const thread& t) | ||
| 133 | { | ||
| 134 | // ugly const_cast to get around lack of rvalue references | ||
| 135 | thread& other = const_cast<thread&>(t); | ||
| 136 | #endif | ||
| 137 | swap(other); | ||
| 138 | } | ||
| 139 | |||
| 140 | #ifdef USE_RVALUE_REFERENCES | ||
| 141 | thread& operator=(const thread&) /*= delete*/; | ||
| 142 | |||
| 143 | thread& operator=(thread&& other) | ||
| 144 | { | ||
| 145 | #else | ||
| 146 | thread& operator=(const thread& t) | ||
| 147 | { | ||
| 148 | // ugly const_cast to get around lack of rvalue references | ||
| 149 | thread& other = const_cast<thread&>(t); | ||
| 150 | #endif | ||
| 151 | if (joinable()) | ||
| 152 | detach(); | ||
| 153 | swap(other); | ||
| 154 | return *this; | ||
| 155 | } | ||
| 156 | |||
| 157 | ~thread() | ||
| 158 | { | ||
| 159 | if (joinable()) | ||
| 160 | detach(); | ||
| 161 | } | ||
| 162 | |||
| 163 | bool joinable() const | ||
| 164 | { | ||
| 165 | return m_id != id(); | ||
| 166 | } | ||
| 167 | |||
| 168 | id get_id() const | ||
| 169 | { | ||
| 170 | return m_id; | ||
| 171 | } | ||
| 172 | |||
| 173 | native_handle_type native_handle() | ||
| 174 | { | ||
| 175 | #ifdef _WIN32 | ||
| 176 | return m_handle; | ||
| 177 | #else | ||
| 178 | return m_id.m_thread; | ||
| 179 | #endif | ||
| 180 | } | ||
| 181 | |||
| 182 | void join() | ||
| 183 | { | ||
| 184 | #ifdef _WIN32 | ||
| 185 | WaitForSingleObject(m_handle, INFINITE); | ||
| 186 | detach(); | ||
| 187 | #else | ||
| 188 | pthread_join(m_id.m_thread, NULL); | ||
| 189 | m_id = id(); | ||
| 190 | #endif | ||
| 191 | } | ||
| 192 | |||
| 193 | void detach() | ||
| 194 | { | ||
| 195 | #ifdef _WIN32 | ||
| 196 | CloseHandle(m_handle); | ||
| 197 | #else | ||
| 198 | pthread_detach(m_id.m_thread); | ||
| 199 | #endif | ||
| 200 | m_id = id(); | ||
| 201 | } | ||
| 202 | |||
| 203 | void swap(thread& other) | ||
| 204 | { | ||
| 205 | std::swap(m_id, other.m_id); | ||
| 206 | #ifdef _WIN32 | ||
| 207 | std::swap(m_handle, other.m_handle); | ||
| 208 | #endif | ||
| 209 | } | ||
| 210 | |||
| 211 | static unsigned hardware_concurrency() | ||
| 212 | { | ||
| 213 | #ifdef _WIN32 | ||
| 214 | SYSTEM_INFO sysinfo; | ||
| 215 | GetSystemInfo(&sysinfo); | ||
| 216 | return static_cast<unsigned>(sysinfo.dwNumberOfProcessors); | ||
| 217 | #else | ||
| 218 | return 0; | ||
| 219 | #endif | ||
| 220 | } | ||
| 221 | |||
| 222 | private: | ||
| 223 | id m_id; | ||
| 224 | |||
| 225 | #ifdef _WIN32 | ||
| 226 | native_handle_type m_handle; | ||
| 227 | #endif | ||
| 228 | |||
| 229 | template <typename F> | ||
| 230 | void StartThread(F* param) | ||
| 231 | { | ||
| 232 | #ifdef USE_BEGINTHREADEX | ||
| 233 | m_handle = (HANDLE)_beginthreadex(NULL, 0, &RunAndDelete<F>, param, 0, &m_id.m_thread); | ||
| 234 | #elif defined(_WIN32) | ||
| 235 | m_handle = CreateThread(NULL, 0, &RunAndDelete<F>, param, 0, &m_id.m_thread); | ||
| 236 | #else | ||
| 237 | pthread_attr_t attr; | ||
| 238 | pthread_attr_init(&attr); | ||
| 239 | pthread_attr_setstacksize(&attr, 1024 * 1024); | ||
| 240 | if (pthread_create(&m_id.m_thread, &attr, &RunAndDelete<F>, param)) | ||
| 241 | m_id = id(); | ||
| 242 | #endif | ||
| 243 | } | ||
| 244 | |||
| 245 | template <typename C> | ||
| 246 | class Func | ||
| 247 | { | ||
| 248 | public: | ||
| 249 | Func(C _func) : func(_func) {} | ||
| 250 | |||
| 251 | void Run() { func(); } | ||
| 252 | |||
| 253 | private: | ||
| 254 | C const func; | ||
| 255 | }; | ||
| 256 | |||
| 257 | template <typename C, typename A> | ||
| 258 | class FuncArg | ||
| 259 | { | ||
| 260 | public: | ||
| 261 | FuncArg(C _func, A _arg) : func(_func), arg(_arg) {} | ||
| 262 | |||
| 263 | void Run() { func(arg); } | ||
| 264 | |||
| 265 | private: | ||
| 266 | C const func; | ||
| 267 | A arg; | ||
| 268 | }; | ||
| 269 | |||
| 270 | template <typename F> | ||
| 271 | static THREAD_RETURN RunAndDelete(void* param) | ||
| 272 | { | ||
| 273 | #ifdef __APPLE__ | ||
| 274 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | ||
| 275 | #endif | ||
| 276 | static_cast<F*>(param)->Run(); | ||
| 277 | delete static_cast<F*>(param); | ||
| 278 | #ifdef __APPLE__ | ||
| 279 | [pool release]; | ||
| 280 | #endif | ||
| 281 | return 0; | ||
| 282 | } | ||
| 283 | }; | ||
| 284 | |||
| 285 | namespace this_thread | ||
| 286 | { | ||
| 287 | |||
| 288 | inline void yield() | ||
| 289 | { | ||
| 290 | #ifdef _WIN32 | ||
| 291 | SwitchToThread(); | ||
| 292 | #else | ||
| 293 | sleep(0); | ||
| 294 | #endif | ||
| 295 | } | ||
| 296 | |||
| 297 | inline thread::id get_id() | ||
| 298 | { | ||
| 299 | #ifdef _WIN32 | ||
| 300 | return GetCurrentThreadId(); | ||
| 301 | #else | ||
| 302 | return pthread_self(); | ||
| 303 | #endif | ||
| 304 | } | ||
| 305 | |||
| 306 | } // namespace this_thread | ||
| 307 | |||
| 308 | } // namespace std | ||
| 309 | |||
| 310 | #undef USE_RVALUE_REFERENCES | ||
| 311 | #undef USE_BEGINTHREADEX | ||
| 312 | #undef THREAD_ID | ||
| 313 | #undef THREAD_RETURN | ||
| 314 | #undef THREAD_HANDLE | ||
| 315 | |||
| 316 | #endif | ||
| 317 | #endif | ||
diff --git a/src/common/src/string_util.cpp b/src/common/src/string_util.cpp new file mode 100644 index 000000000..ff4c5dbe0 --- /dev/null +++ b/src/common/src/string_util.cpp | |||
| @@ -0,0 +1,531 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <stdlib.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <algorithm> | ||
| 8 | |||
| 9 | #include "common.h" | ||
| 10 | #include "common_paths.h" | ||
| 11 | #include "string_util.h" | ||
| 12 | |||
| 13 | #ifdef _WIN32 | ||
| 14 | #include <Windows.h> | ||
| 15 | #else | ||
| 16 | #include <iconv.h> | ||
| 17 | #include <errno.h> | ||
| 18 | #endif | ||
| 19 | |||
| 20 | // faster than sscanf | ||
| 21 | bool AsciiToHex(const char* _szValue, u32& result) | ||
| 22 | { | ||
| 23 | char *endptr = NULL; | ||
| 24 | const u32 value = strtoul(_szValue, &endptr, 16); | ||
| 25 | |||
| 26 | if (!endptr || *endptr) | ||
| 27 | return false; | ||
| 28 | |||
| 29 | result = value; | ||
| 30 | return true; | ||
| 31 | } | ||
| 32 | |||
| 33 | bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args) | ||
| 34 | { | ||
| 35 | int writtenCount; | ||
| 36 | |||
| 37 | #ifdef _WIN32 | ||
| 38 | // You would think *printf are simple, right? Iterate on each character, | ||
| 39 | // if it's a format specifier handle it properly, etc. | ||
| 40 | // | ||
| 41 | // Nooooo. Not according to the C standard. | ||
| 42 | // | ||
| 43 | // According to the C99 standard (7.19.6.1 "The fprintf function") | ||
| 44 | // The format shall be a multibyte character sequence | ||
| 45 | // | ||
| 46 | // Because some character encodings might have '%' signs in the middle of | ||
| 47 | // a multibyte sequence (SJIS for example only specifies that the first | ||
| 48 | // byte of a 2 byte sequence is "high", the second byte can be anything), | ||
| 49 | // printf functions have to decode the multibyte sequences and try their | ||
| 50 | // best to not screw up. | ||
| 51 | // | ||
| 52 | // Unfortunately, on Windows, the locale for most languages is not UTF-8 | ||
| 53 | // as we would need. Notably, for zh_TW, Windows chooses EUC-CN as the | ||
| 54 | // locale, and completely fails when trying to decode UTF-8 as EUC-CN. | ||
| 55 | // | ||
| 56 | // On the other hand, the fix is simple: because we use UTF-8, no such | ||
| 57 | // multibyte handling is required as we can simply assume that no '%' char | ||
| 58 | // will be present in the middle of a multibyte sequence. | ||
| 59 | // | ||
| 60 | // This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l. | ||
| 61 | static locale_t c_locale = NULL; | ||
| 62 | if (!c_locale) | ||
| 63 | c_locale = _create_locale(LC_ALL, ".1252"); | ||
| 64 | writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args); | ||
| 65 | #else | ||
| 66 | writtenCount = vsnprintf(out, outsize, format, args); | ||
| 67 | #endif | ||
| 68 | |||
| 69 | if (writtenCount > 0 && writtenCount < outsize) | ||
| 70 | { | ||
| 71 | out[writtenCount] = '\0'; | ||
| 72 | return true; | ||
| 73 | } | ||
| 74 | else | ||
| 75 | { | ||
| 76 | out[outsize - 1] = '\0'; | ||
| 77 | return false; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | std::string StringFromFormat(const char* format, ...) | ||
| 82 | { | ||
| 83 | va_list args; | ||
| 84 | char *buf = NULL; | ||
| 85 | #ifdef _WIN32 | ||
| 86 | int required = 0; | ||
| 87 | |||
| 88 | va_start(args, format); | ||
| 89 | required = _vscprintf(format, args); | ||
| 90 | buf = new char[required + 1]; | ||
| 91 | CharArrayFromFormatV(buf, required + 1, format, args); | ||
| 92 | va_end(args); | ||
| 93 | |||
| 94 | std::string temp = buf; | ||
| 95 | delete[] buf; | ||
| 96 | #else | ||
| 97 | va_start(args, format); | ||
| 98 | if (vasprintf(&buf, format, args) < 0) | ||
| 99 | ERROR_LOG(COMMON, "Unable to allocate memory for string"); | ||
| 100 | va_end(args); | ||
| 101 | |||
| 102 | std::string temp = buf; | ||
| 103 | free(buf); | ||
| 104 | #endif | ||
| 105 | return temp; | ||
| 106 | } | ||
| 107 | |||
| 108 | // For Debugging. Read out an u8 array. | ||
| 109 | std::string ArrayToString(const u8 *data, u32 size, int line_len, bool spaces) | ||
| 110 | { | ||
| 111 | std::ostringstream oss; | ||
| 112 | oss << std::setfill('0') << std::hex; | ||
| 113 | |||
| 114 | for (int line = 0; size; ++data, --size) | ||
| 115 | { | ||
| 116 | oss << std::setw(2) << (int)*data; | ||
| 117 | |||
| 118 | if (line_len == ++line) | ||
| 119 | { | ||
| 120 | oss << '\n'; | ||
| 121 | line = 0; | ||
| 122 | } | ||
| 123 | else if (spaces) | ||
| 124 | oss << ' '; | ||
| 125 | } | ||
| 126 | |||
| 127 | return oss.str(); | ||
| 128 | } | ||
| 129 | |||
| 130 | // Turns " hej " into "hej". Also handles tabs. | ||
| 131 | std::string StripSpaces(const std::string &str) | ||
| 132 | { | ||
| 133 | const size_t s = str.find_first_not_of(" \t\r\n"); | ||
| 134 | |||
| 135 | if (str.npos != s) | ||
| 136 | return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1); | ||
| 137 | else | ||
| 138 | return ""; | ||
| 139 | } | ||
| 140 | |||
| 141 | // "\"hello\"" is turned to "hello" | ||
| 142 | // This one assumes that the string has already been space stripped in both | ||
| 143 | // ends, as done by StripSpaces above, for example. | ||
| 144 | std::string StripQuotes(const std::string& s) | ||
| 145 | { | ||
| 146 | if (s.size() && '\"' == s[0] && '\"' == *s.rbegin()) | ||
| 147 | return s.substr(1, s.size() - 2); | ||
| 148 | else | ||
| 149 | return s; | ||
| 150 | } | ||
| 151 | |||
| 152 | bool TryParse(const std::string &str, u32 *const output) | ||
| 153 | { | ||
| 154 | char *endptr = NULL; | ||
| 155 | |||
| 156 | // Reset errno to a value other than ERANGE | ||
| 157 | errno = 0; | ||
| 158 | |||
| 159 | unsigned long value = strtoul(str.c_str(), &endptr, 0); | ||
| 160 | |||
| 161 | if (!endptr || *endptr) | ||
| 162 | return false; | ||
| 163 | |||
| 164 | if (errno == ERANGE) | ||
| 165 | return false; | ||
| 166 | |||
| 167 | #if ULONG_MAX > UINT_MAX | ||
| 168 | if (value >= 0x100000000ull | ||
| 169 | && value <= 0xFFFFFFFF00000000ull) | ||
| 170 | return false; | ||
| 171 | #endif | ||
| 172 | |||
| 173 | *output = static_cast<u32>(value); | ||
| 174 | return true; | ||
| 175 | } | ||
| 176 | |||
| 177 | bool TryParse(const std::string &str, bool *const output) | ||
| 178 | { | ||
| 179 | if ("1" == str || !strcasecmp("true", str.c_str())) | ||
| 180 | *output = true; | ||
| 181 | else if ("0" == str || !strcasecmp("false", str.c_str())) | ||
| 182 | *output = false; | ||
| 183 | else | ||
| 184 | return false; | ||
| 185 | |||
| 186 | return true; | ||
| 187 | } | ||
| 188 | |||
| 189 | std::string StringFromInt(int value) | ||
| 190 | { | ||
| 191 | char temp[16]; | ||
| 192 | sprintf(temp, "%i", value); | ||
| 193 | return temp; | ||
| 194 | } | ||
| 195 | |||
| 196 | std::string StringFromBool(bool value) | ||
| 197 | { | ||
| 198 | return value ? "True" : "False"; | ||
| 199 | } | ||
| 200 | |||
| 201 | bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension) | ||
| 202 | { | ||
| 203 | if (full_path.empty()) | ||
| 204 | return false; | ||
| 205 | |||
| 206 | size_t dir_end = full_path.find_last_of("/" | ||
| 207 | // windows needs the : included for something like just "C:" to be considered a directory | ||
| 208 | #ifdef _WIN32 | ||
| 209 | ":" | ||
| 210 | #endif | ||
| 211 | ); | ||
| 212 | if (std::string::npos == dir_end) | ||
| 213 | dir_end = 0; | ||
| 214 | else | ||
| 215 | dir_end += 1; | ||
| 216 | |||
| 217 | size_t fname_end = full_path.rfind('.'); | ||
| 218 | if (fname_end < dir_end || std::string::npos == fname_end) | ||
| 219 | fname_end = full_path.size(); | ||
| 220 | |||
| 221 | if (_pPath) | ||
| 222 | *_pPath = full_path.substr(0, dir_end); | ||
| 223 | |||
| 224 | if (_pFilename) | ||
| 225 | *_pFilename = full_path.substr(dir_end, fname_end - dir_end); | ||
| 226 | |||
| 227 | if (_pExtension) | ||
| 228 | *_pExtension = full_path.substr(fname_end); | ||
| 229 | |||
| 230 | return true; | ||
| 231 | } | ||
| 232 | |||
| 233 | void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename) | ||
| 234 | { | ||
| 235 | _CompleteFilename = _Path; | ||
| 236 | |||
| 237 | // check for seperator | ||
| 238 | if (DIR_SEP_CHR != *_CompleteFilename.rbegin()) | ||
| 239 | _CompleteFilename += DIR_SEP_CHR; | ||
| 240 | |||
| 241 | // add the filename | ||
| 242 | _CompleteFilename += _Filename; | ||
| 243 | } | ||
| 244 | |||
| 245 | void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) | ||
| 246 | { | ||
| 247 | std::istringstream iss(str); | ||
| 248 | output.resize(1); | ||
| 249 | |||
| 250 | while (std::getline(iss, *output.rbegin(), delim)) | ||
| 251 | output.push_back(""); | ||
| 252 | |||
| 253 | output.pop_back(); | ||
| 254 | } | ||
| 255 | |||
| 256 | std::string TabsToSpaces(int tab_size, const std::string &in) | ||
| 257 | { | ||
| 258 | const std::string spaces(tab_size, ' '); | ||
| 259 | std::string out(in); | ||
| 260 | |||
| 261 | size_t i = 0; | ||
| 262 | while (out.npos != (i = out.find('\t'))) | ||
| 263 | out.replace(i, 1, spaces); | ||
| 264 | |||
| 265 | return out; | ||
| 266 | } | ||
| 267 | |||
| 268 | std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) | ||
| 269 | { | ||
| 270 | while(1) | ||
| 271 | { | ||
| 272 | size_t pos = result.find(src); | ||
| 273 | if (pos == std::string::npos) break; | ||
| 274 | result.replace(pos, src.size(), dest); | ||
| 275 | } | ||
| 276 | return result; | ||
| 277 | } | ||
| 278 | |||
| 279 | // UriDecode and UriEncode are from http://www.codeguru.com/cpp/cpp/string/conversions/print.php/c12759 | ||
| 280 | // by jinq0123 (November 2, 2006) | ||
| 281 | |||
| 282 | // Uri encode and decode. | ||
| 283 | // RFC1630, RFC1738, RFC2396 | ||
| 284 | |||
| 285 | //#include <string> | ||
| 286 | //#include <assert.h> | ||
| 287 | |||
| 288 | const char HEX2DEC[256] = | ||
| 289 | { | ||
| 290 | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ | ||
| 291 | /* 0 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 292 | /* 1 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 293 | /* 2 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 294 | /* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,16,16, 16,16,16,16, | ||
| 295 | |||
| 296 | /* 4 */ 16,10,11,12, 13,14,15,16, 16,16,16,16, 16,16,16,16, | ||
| 297 | /* 5 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 298 | /* 6 */ 16,10,11,12, 13,14,15,16, 16,16,16,16, 16,16,16,16, | ||
| 299 | /* 7 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 300 | |||
| 301 | /* 8 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 302 | /* 9 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 303 | /* A */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 304 | /* B */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 305 | |||
| 306 | /* C */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 307 | /* D */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 308 | /* E */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16, | ||
| 309 | /* F */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16 | ||
| 310 | }; | ||
| 311 | |||
| 312 | std::string UriDecode(const std::string & sSrc) | ||
| 313 | { | ||
| 314 | // Note from RFC1630: "Sequences which start with a percent sign | ||
| 315 | // but are not followed by two hexadecimal characters (0-9, A-F) are reserved | ||
| 316 | // for future extension" | ||
| 317 | |||
| 318 | const unsigned char * pSrc = (const unsigned char *)sSrc.c_str(); | ||
| 319 | const size_t SRC_LEN = sSrc.length(); | ||
| 320 | const unsigned char * const SRC_END = pSrc + SRC_LEN; | ||
| 321 | const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%' | ||
| 322 | |||
| 323 | char * const pStart = new char[SRC_LEN]; | ||
| 324 | char * pEnd = pStart; | ||
| 325 | |||
| 326 | while (pSrc < SRC_LAST_DEC) | ||
| 327 | { | ||
| 328 | if (*pSrc == '%') | ||
| 329 | { | ||
| 330 | char dec1, dec2; | ||
| 331 | if (16 != (dec1 = HEX2DEC[*(pSrc + 1)]) | ||
| 332 | && 16 != (dec2 = HEX2DEC[*(pSrc + 2)])) | ||
| 333 | { | ||
| 334 | *pEnd++ = (dec1 << 4) + dec2; | ||
| 335 | pSrc += 3; | ||
| 336 | continue; | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | *pEnd++ = *pSrc++; | ||
| 341 | } | ||
| 342 | |||
| 343 | // the last 2- chars | ||
| 344 | while (pSrc < SRC_END) | ||
| 345 | *pEnd++ = *pSrc++; | ||
| 346 | |||
| 347 | std::string sResult(pStart, pEnd); | ||
| 348 | delete [] pStart; | ||
| 349 | return sResult; | ||
| 350 | } | ||
| 351 | |||
| 352 | // Only alphanum is safe. | ||
| 353 | const char SAFE[256] = | ||
| 354 | { | ||
| 355 | /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ | ||
| 356 | /* 0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, | ||
| 357 | /* 1 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, | ||
| 358 | /* 2 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, | ||
| 359 | /* 3 */ 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0, | ||
| 360 | |||
| 361 | /* 4 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, | ||
| 362 | /* 5 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0, | ||
| 363 | /* 6 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, | ||
| 364 | /* 7 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0, | ||
| 365 | |||
| 366 | /* 8 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, | ||
| 367 | /* 9 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, | ||
| 368 | /* A */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, | ||
| 369 | /* B */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, | ||
| 370 | |||
| 371 | /* C */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, | ||
| 372 | /* D */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, | ||
| 373 | /* E */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, | ||
| 374 | /* F */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 | ||
| 375 | }; | ||
| 376 | |||
| 377 | std::string UriEncode(const std::string & sSrc) | ||
| 378 | { | ||
| 379 | const char DEC2HEX[16 + 1] = "0123456789ABCDEF"; | ||
| 380 | const unsigned char * pSrc = (const unsigned char *)sSrc.c_str(); | ||
| 381 | const size_t SRC_LEN = sSrc.length(); | ||
| 382 | unsigned char * const pStart = new unsigned char[SRC_LEN * 3]; | ||
| 383 | unsigned char * pEnd = pStart; | ||
| 384 | const unsigned char * const SRC_END = pSrc + SRC_LEN; | ||
| 385 | |||
| 386 | for (; pSrc < SRC_END; ++pSrc) | ||
| 387 | { | ||
| 388 | if (SAFE[*pSrc]) | ||
| 389 | *pEnd++ = *pSrc; | ||
| 390 | else | ||
| 391 | { | ||
| 392 | // escape this char | ||
| 393 | *pEnd++ = '%'; | ||
| 394 | *pEnd++ = DEC2HEX[*pSrc >> 4]; | ||
| 395 | *pEnd++ = DEC2HEX[*pSrc & 0x0F]; | ||
| 396 | } | ||
| 397 | } | ||
| 398 | |||
| 399 | std::string sResult((char *)pStart, (char *)pEnd); | ||
| 400 | delete [] pStart; | ||
| 401 | return sResult; | ||
| 402 | } | ||
| 403 | |||
| 404 | #ifdef _WIN32 | ||
| 405 | |||
| 406 | std::string UTF16ToUTF8(const std::wstring& input) | ||
| 407 | { | ||
| 408 | auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), nullptr, 0, nullptr, nullptr); | ||
| 409 | |||
| 410 | std::string output; | ||
| 411 | output.resize(size); | ||
| 412 | |||
| 413 | if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), &output[0], output.size(), nullptr, nullptr)) | ||
| 414 | output.clear(); | ||
| 415 | |||
| 416 | return output; | ||
| 417 | } | ||
| 418 | |||
| 419 | std::wstring CPToUTF16(u32 code_page, const std::string& input) | ||
| 420 | { | ||
| 421 | auto const size = MultiByteToWideChar(code_page, 0, input.data(), input.size(), nullptr, 0); | ||
| 422 | |||
| 423 | std::wstring output; | ||
| 424 | output.resize(size); | ||
| 425 | |||
| 426 | if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), input.size(), &output[0], output.size())) | ||
| 427 | output.clear(); | ||
| 428 | |||
| 429 | return output; | ||
| 430 | } | ||
| 431 | |||
| 432 | std::wstring UTF8ToUTF16(const std::string& input) | ||
| 433 | { | ||
| 434 | return CPToUTF16(CP_UTF8, input); | ||
| 435 | } | ||
| 436 | |||
| 437 | std::string SHIFTJISToUTF8(const std::string& input) | ||
| 438 | { | ||
| 439 | return UTF16ToUTF8(CPToUTF16(932, input)); | ||
| 440 | } | ||
| 441 | |||
| 442 | std::string CP1252ToUTF8(const std::string& input) | ||
| 443 | { | ||
| 444 | return UTF16ToUTF8(CPToUTF16(1252, input)); | ||
| 445 | } | ||
| 446 | |||
| 447 | #else | ||
| 448 | |||
| 449 | template <typename T> | ||
| 450 | std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) | ||
| 451 | { | ||
| 452 | std::string result; | ||
| 453 | |||
| 454 | iconv_t const conv_desc = iconv_open("UTF-8", fromcode); | ||
| 455 | if ((iconv_t)-1 == conv_desc) | ||
| 456 | { | ||
| 457 | ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno)); | ||
| 458 | } | ||
| 459 | else | ||
| 460 | { | ||
| 461 | size_t const in_bytes = sizeof(T) * input.size(); | ||
| 462 | size_t const out_buffer_size = 4 * in_bytes; | ||
| 463 | |||
| 464 | std::string out_buffer; | ||
| 465 | out_buffer.resize(out_buffer_size); | ||
| 466 | |||
| 467 | auto src_buffer = &input[0]; | ||
| 468 | size_t src_bytes = in_bytes; | ||
| 469 | auto dst_buffer = &out_buffer[0]; | ||
| 470 | size_t dst_bytes = out_buffer.size(); | ||
| 471 | |||
| 472 | while (src_bytes != 0) | ||
| 473 | { | ||
| 474 | size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes, | ||
| 475 | &dst_buffer, &dst_bytes); | ||
| 476 | |||
| 477 | if ((size_t)-1 == iconv_result) | ||
| 478 | { | ||
| 479 | if (EILSEQ == errno || EINVAL == errno) | ||
| 480 | { | ||
| 481 | // Try to skip the bad character | ||
| 482 | if (src_bytes != 0) | ||
| 483 | { | ||
| 484 | --src_bytes; | ||
| 485 | ++src_buffer; | ||
| 486 | } | ||
| 487 | } | ||
| 488 | else | ||
| 489 | { | ||
| 490 | ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno)); | ||
| 491 | break; | ||
| 492 | } | ||
| 493 | } | ||
| 494 | } | ||
| 495 | |||
| 496 | out_buffer.resize(out_buffer_size - dst_bytes); | ||
| 497 | out_buffer.swap(result); | ||
| 498 | |||
| 499 | iconv_close(conv_desc); | ||
| 500 | } | ||
| 501 | |||
| 502 | return result; | ||
| 503 | } | ||
| 504 | |||
| 505 | std::string CP1252ToUTF8(const std::string& input) | ||
| 506 | { | ||
| 507 | //return CodeToUTF8("CP1252//TRANSLIT", input); | ||
| 508 | //return CodeToUTF8("CP1252//IGNORE", input); | ||
| 509 | return CodeToUTF8("CP1252", input); | ||
| 510 | } | ||
| 511 | |||
| 512 | std::string SHIFTJISToUTF8(const std::string& input) | ||
| 513 | { | ||
| 514 | //return CodeToUTF8("CP932", input); | ||
| 515 | return CodeToUTF8("SJIS", input); | ||
| 516 | } | ||
| 517 | |||
| 518 | std::string UTF16ToUTF8(const std::wstring& input) | ||
| 519 | { | ||
| 520 | std::string result = | ||
| 521 | // CodeToUTF8("UCS-2", input); | ||
| 522 | // CodeToUTF8("UCS-2LE", input); | ||
| 523 | // CodeToUTF8("UTF-16", input); | ||
| 524 | CodeToUTF8("UTF-16LE", input); | ||
| 525 | |||
| 526 | // TODO: why is this needed? | ||
| 527 | result.erase(std::remove(result.begin(), result.end(), 0x00), result.end()); | ||
| 528 | return result; | ||
| 529 | } | ||
| 530 | |||
| 531 | #endif | ||
diff --git a/src/common/src/string_util.h b/src/common/src/string_util.h new file mode 100644 index 000000000..31eaeb246 --- /dev/null +++ b/src/common/src/string_util.h | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _STRINGUTIL_H_ | ||
| 6 | #define _STRINGUTIL_H_ | ||
| 7 | |||
| 8 | #include <stdarg.h> | ||
| 9 | |||
| 10 | #include <vector> | ||
| 11 | #include <string> | ||
| 12 | #include <sstream> | ||
| 13 | #include <iomanip> | ||
| 14 | |||
| 15 | #include "common.h" | ||
| 16 | |||
| 17 | std::string StringFromFormat(const char* format, ...); | ||
| 18 | // Cheap! | ||
| 19 | bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args); | ||
| 20 | |||
| 21 | template<size_t Count> | ||
| 22 | inline void CharArrayFromFormat(char (& out)[Count], const char* format, ...) | ||
| 23 | { | ||
| 24 | va_list args; | ||
| 25 | va_start(args, format); | ||
| 26 | CharArrayFromFormatV(out, Count, format, args); | ||
| 27 | va_end(args); | ||
| 28 | } | ||
| 29 | |||
| 30 | // Good | ||
| 31 | std::string ArrayToString(const u8 *data, u32 size, int line_len = 20, bool spaces = true); | ||
| 32 | |||
| 33 | std::string StripSpaces(const std::string &s); | ||
| 34 | std::string StripQuotes(const std::string &s); | ||
| 35 | |||
| 36 | // Thousand separator. Turns 12345678 into 12,345,678 | ||
| 37 | template <typename I> | ||
| 38 | std::string ThousandSeparate(I value, int spaces = 0) | ||
| 39 | { | ||
| 40 | std::ostringstream oss; | ||
| 41 | |||
| 42 | // std::locale("") seems to be broken on many platforms | ||
| 43 | #if defined _WIN32 || (defined __linux__ && !defined __clang__) | ||
| 44 | oss.imbue(std::locale("")); | ||
| 45 | #endif | ||
| 46 | oss << std::setw(spaces) << value; | ||
| 47 | |||
| 48 | return oss.str(); | ||
| 49 | } | ||
| 50 | |||
| 51 | std::string StringFromInt(int value); | ||
| 52 | std::string StringFromBool(bool value); | ||
| 53 | |||
| 54 | bool TryParse(const std::string &str, bool *output); | ||
| 55 | bool TryParse(const std::string &str, u32 *output); | ||
| 56 | |||
| 57 | template <typename N> | ||
| 58 | static bool TryParse(const std::string &str, N *const output) | ||
| 59 | { | ||
| 60 | std::istringstream iss(str); | ||
| 61 | |||
| 62 | N tmp = 0; | ||
| 63 | if (iss >> tmp) | ||
| 64 | { | ||
| 65 | *output = tmp; | ||
| 66 | return true; | ||
| 67 | } | ||
| 68 | else | ||
| 69 | return false; | ||
| 70 | } | ||
| 71 | |||
| 72 | // TODO: kill this | ||
| 73 | bool AsciiToHex(const char* _szValue, u32& result); | ||
| 74 | |||
| 75 | std::string TabsToSpaces(int tab_size, const std::string &in); | ||
| 76 | |||
| 77 | void SplitString(const std::string& str, char delim, std::vector<std::string>& output); | ||
| 78 | |||
| 79 | // "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe" | ||
| 80 | bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension); | ||
| 81 | |||
| 82 | void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename); | ||
| 83 | std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest); | ||
| 84 | std::string UriDecode(const std::string & sSrc); | ||
| 85 | std::string UriEncode(const std::string & sSrc); | ||
| 86 | |||
| 87 | std::string CP1252ToUTF8(const std::string& str); | ||
| 88 | std::string SHIFTJISToUTF8(const std::string& str); | ||
| 89 | std::string UTF16ToUTF8(const std::wstring& str); | ||
| 90 | |||
| 91 | #ifdef _WIN32 | ||
| 92 | |||
| 93 | std::wstring UTF8ToUTF16(const std::string& str); | ||
| 94 | |||
| 95 | #ifdef _UNICODE | ||
| 96 | inline std::string TStrToUTF8(const std::wstring& str) | ||
| 97 | { return UTF16ToUTF8(str); } | ||
| 98 | |||
| 99 | inline std::wstring UTF8ToTStr(const std::string& str) | ||
| 100 | { return UTF8ToUTF16(str); } | ||
| 101 | #else | ||
| 102 | inline std::string TStrToUTF8(const std::string& str) | ||
| 103 | { return str; } | ||
| 104 | |||
| 105 | inline std::string UTF8ToTStr(const std::string& str) | ||
| 106 | { return str; } | ||
| 107 | #endif | ||
| 108 | |||
| 109 | #endif | ||
| 110 | |||
| 111 | #endif // _STRINGUTIL_H_ | ||
diff --git a/src/common/src/thread.cpp b/src/common/src/thread.cpp new file mode 100644 index 000000000..e75dfffeb --- /dev/null +++ b/src/common/src/thread.cpp | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "thread.h" | ||
| 6 | #include "common.h" | ||
| 7 | |||
| 8 | #ifdef __APPLE__ | ||
| 9 | #include <mach/mach.h> | ||
| 10 | #elif defined BSD4_4 | ||
| 11 | #include <pthread_np.h> | ||
| 12 | #endif | ||
| 13 | |||
| 14 | #ifdef USE_BEGINTHREADEX | ||
| 15 | #include <process.h> | ||
| 16 | #endif | ||
| 17 | |||
| 18 | namespace Common | ||
| 19 | { | ||
| 20 | |||
| 21 | int CurrentThreadId() | ||
| 22 | { | ||
| 23 | #ifdef _WIN32 | ||
| 24 | return GetCurrentThreadId(); | ||
| 25 | #elif defined __APPLE__ | ||
| 26 | return mach_thread_self(); | ||
| 27 | #else | ||
| 28 | return 0; | ||
| 29 | #endif | ||
| 30 | } | ||
| 31 | |||
| 32 | #ifdef _WIN32 | ||
| 33 | |||
| 34 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) | ||
| 35 | { | ||
| 36 | SetThreadAffinityMask(thread, mask); | ||
| 37 | } | ||
| 38 | |||
| 39 | void SetCurrentThreadAffinity(u32 mask) | ||
| 40 | { | ||
| 41 | SetThreadAffinityMask(GetCurrentThread(), mask); | ||
| 42 | } | ||
| 43 | |||
| 44 | // Supporting functions | ||
| 45 | void SleepCurrentThread(int ms) | ||
| 46 | { | ||
| 47 | Sleep(ms); | ||
| 48 | } | ||
| 49 | |||
| 50 | void SwitchCurrentThread() | ||
| 51 | { | ||
| 52 | SwitchToThread(); | ||
| 53 | } | ||
| 54 | |||
| 55 | // Sets the debugger-visible name of the current thread. | ||
| 56 | // Uses undocumented (actually, it is now documented) trick. | ||
| 57 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp | ||
| 58 | |||
| 59 | // This is implemented much nicer in upcoming msvc++, see: | ||
| 60 | // http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx | ||
| 61 | void SetCurrentThreadName(const char* szThreadName) | ||
| 62 | { | ||
| 63 | static const DWORD MS_VC_EXCEPTION = 0x406D1388; | ||
| 64 | |||
| 65 | #pragma pack(push,8) | ||
| 66 | struct THREADNAME_INFO | ||
| 67 | { | ||
| 68 | DWORD dwType; // must be 0x1000 | ||
| 69 | LPCSTR szName; // pointer to name (in user addr space) | ||
| 70 | DWORD dwThreadID; // thread ID (-1=caller thread) | ||
| 71 | DWORD dwFlags; // reserved for future use, must be zero | ||
| 72 | } info; | ||
| 73 | #pragma pack(pop) | ||
| 74 | |||
| 75 | info.dwType = 0x1000; | ||
| 76 | info.szName = szThreadName; | ||
| 77 | info.dwThreadID = -1; //dwThreadID; | ||
| 78 | info.dwFlags = 0; | ||
| 79 | |||
| 80 | __try | ||
| 81 | { | ||
| 82 | RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info); | ||
| 83 | } | ||
| 84 | __except(EXCEPTION_CONTINUE_EXECUTION) | ||
| 85 | {} | ||
| 86 | } | ||
| 87 | |||
| 88 | #else // !WIN32, so must be POSIX threads | ||
| 89 | |||
| 90 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) | ||
| 91 | { | ||
| 92 | #ifdef __APPLE__ | ||
| 93 | thread_policy_set(pthread_mach_thread_np(thread), | ||
| 94 | THREAD_AFFINITY_POLICY, (integer_t *)&mask, 1); | ||
| 95 | #elif (defined __linux__ || defined BSD4_4) && !(defined ANDROID) | ||
| 96 | cpu_set_t cpu_set; | ||
| 97 | CPU_ZERO(&cpu_set); | ||
| 98 | |||
| 99 | for (int i = 0; i != sizeof(mask) * 8; ++i) | ||
| 100 | if ((mask >> i) & 1) | ||
| 101 | CPU_SET(i, &cpu_set); | ||
| 102 | |||
| 103 | pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set); | ||
| 104 | #endif | ||
| 105 | } | ||
| 106 | |||
| 107 | void SetCurrentThreadAffinity(u32 mask) | ||
| 108 | { | ||
| 109 | SetThreadAffinity(pthread_self(), mask); | ||
| 110 | } | ||
| 111 | |||
| 112 | void SleepCurrentThread(int ms) | ||
| 113 | { | ||
| 114 | usleep(1000 * ms); | ||
| 115 | } | ||
| 116 | |||
| 117 | void SwitchCurrentThread() | ||
| 118 | { | ||
| 119 | usleep(1000 * 1); | ||
| 120 | } | ||
| 121 | |||
| 122 | void SetCurrentThreadName(const char* szThreadName) | ||
| 123 | { | ||
| 124 | #ifdef __APPLE__ | ||
| 125 | pthread_setname_np(szThreadName); | ||
| 126 | #else | ||
| 127 | pthread_setname_np(pthread_self(), szThreadName); | ||
| 128 | #endif | ||
| 129 | } | ||
| 130 | |||
| 131 | #endif | ||
| 132 | |||
| 133 | } // namespace Common | ||
diff --git a/src/common/src/thread.h b/src/common/src/thread.h new file mode 100644 index 000000000..3178c40f7 --- /dev/null +++ b/src/common/src/thread.h | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _THREAD_H_ | ||
| 6 | #define _THREAD_H_ | ||
| 7 | |||
| 8 | #include "std_condition_variable.h" | ||
| 9 | #include "std_mutex.h" | ||
| 10 | #include "std_thread.h" | ||
| 11 | |||
| 12 | // Don't include common.h here as it will break LogManager | ||
| 13 | #include "common_types.h" | ||
| 14 | #include <stdio.h> | ||
| 15 | #include <string.h> | ||
| 16 | |||
| 17 | // This may not be defined outside _WIN32 | ||
| 18 | #ifndef _WIN32 | ||
| 19 | #ifndef INFINITE | ||
| 20 | #define INFINITE 0xffffffff | ||
| 21 | #endif | ||
| 22 | |||
| 23 | //for gettimeofday and struct time(spec|val) | ||
| 24 | #include <time.h> | ||
| 25 | #include <sys/time.h> | ||
| 26 | #endif | ||
| 27 | |||
| 28 | namespace Common | ||
| 29 | { | ||
| 30 | |||
| 31 | int CurrentThreadId(); | ||
| 32 | |||
| 33 | void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); | ||
| 34 | void SetCurrentThreadAffinity(u32 mask); | ||
| 35 | |||
| 36 | class Event | ||
| 37 | { | ||
| 38 | public: | ||
| 39 | Event() | ||
| 40 | : is_set(false) | ||
| 41 | {}; | ||
| 42 | |||
| 43 | void Set() | ||
| 44 | { | ||
| 45 | std::lock_guard<std::mutex> lk(m_mutex); | ||
| 46 | if (!is_set) | ||
| 47 | { | ||
| 48 | is_set = true; | ||
| 49 | m_condvar.notify_one(); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | void Wait() | ||
| 54 | { | ||
| 55 | std::unique_lock<std::mutex> lk(m_mutex); | ||
| 56 | m_condvar.wait(lk, IsSet(this)); | ||
| 57 | is_set = false; | ||
| 58 | } | ||
| 59 | |||
| 60 | void Reset() | ||
| 61 | { | ||
| 62 | std::unique_lock<std::mutex> lk(m_mutex); | ||
| 63 | // no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration | ||
| 64 | is_set = false; | ||
| 65 | } | ||
| 66 | |||
| 67 | private: | ||
| 68 | class IsSet | ||
| 69 | { | ||
| 70 | public: | ||
| 71 | IsSet(const Event* ev) | ||
| 72 | : m_event(ev) | ||
| 73 | {} | ||
| 74 | |||
| 75 | bool operator()() | ||
| 76 | { | ||
| 77 | return m_event->is_set; | ||
| 78 | } | ||
| 79 | |||
| 80 | private: | ||
| 81 | const Event* const m_event; | ||
| 82 | }; | ||
| 83 | |||
| 84 | volatile bool is_set; | ||
| 85 | std::condition_variable m_condvar; | ||
| 86 | std::mutex m_mutex; | ||
| 87 | }; | ||
| 88 | |||
| 89 | // TODO: doesn't work on windows with (count > 2) | ||
| 90 | class Barrier | ||
| 91 | { | ||
| 92 | public: | ||
| 93 | Barrier(size_t count) | ||
| 94 | : m_count(count), m_waiting(0) | ||
| 95 | {} | ||
| 96 | |||
| 97 | // block until "count" threads call Sync() | ||
| 98 | bool Sync() | ||
| 99 | { | ||
| 100 | std::unique_lock<std::mutex> lk(m_mutex); | ||
| 101 | |||
| 102 | // TODO: broken when next round of Sync()s | ||
| 103 | // is entered before all waiting threads return from the notify_all | ||
| 104 | |||
| 105 | if (m_count == ++m_waiting) | ||
| 106 | { | ||
| 107 | m_waiting = 0; | ||
| 108 | m_condvar.notify_all(); | ||
| 109 | return true; | ||
| 110 | } | ||
| 111 | else | ||
| 112 | { | ||
| 113 | m_condvar.wait(lk, IsDoneWating(this)); | ||
| 114 | return false; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | private: | ||
| 119 | class IsDoneWating | ||
| 120 | { | ||
| 121 | public: | ||
| 122 | IsDoneWating(const Barrier* bar) | ||
| 123 | : m_bar(bar) | ||
| 124 | {} | ||
| 125 | |||
| 126 | bool operator()() | ||
| 127 | { | ||
| 128 | return (0 == m_bar->m_waiting); | ||
| 129 | } | ||
| 130 | |||
| 131 | private: | ||
| 132 | const Barrier* const m_bar; | ||
| 133 | }; | ||
| 134 | |||
| 135 | std::condition_variable m_condvar; | ||
| 136 | std::mutex m_mutex; | ||
| 137 | const size_t m_count; | ||
| 138 | volatile size_t m_waiting; | ||
| 139 | }; | ||
| 140 | |||
| 141 | void SleepCurrentThread(int ms); | ||
| 142 | void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms | ||
| 143 | |||
| 144 | // Use this function during a spin-wait to make the current thread | ||
| 145 | // relax while another thread is working. This may be more efficient | ||
| 146 | // than using events because event functions use kernel calls. | ||
| 147 | inline void YieldCPU() | ||
| 148 | { | ||
| 149 | std::this_thread::yield(); | ||
| 150 | } | ||
| 151 | |||
| 152 | void SetCurrentThreadName(const char *name); | ||
| 153 | |||
| 154 | } // namespace Common | ||
| 155 | |||
| 156 | #endif // _THREAD_H_ | ||
diff --git a/src/common/src/thunk.h b/src/common/src/thunk.h new file mode 100644 index 000000000..4b64a9b0e --- /dev/null +++ b/src/common/src/thunk.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _THUNK_H_ | ||
| 6 | #define _THUNK_H_ | ||
| 7 | |||
| 8 | #include <map> | ||
| 9 | |||
| 10 | #include "common.h" | ||
| 11 | #include "x64Emitter.h" | ||
| 12 | |||
| 13 | // This simple class creates a wrapper around a C/C++ function that saves all fp state | ||
| 14 | // before entering it, and restores it upon exit. This is required to be able to selectively | ||
| 15 | // call functions from generated code, without inflicting the performance hit and increase | ||
| 16 | // of complexity that it means to protect the generated code from this problem. | ||
| 17 | |||
| 18 | // This process is called thunking. | ||
| 19 | |||
| 20 | // There will only ever be one level of thunking on the stack, plus, | ||
| 21 | // we don't want to pollute the stack, so we store away regs somewhere global. | ||
| 22 | // NOT THREAD SAFE. This may only be used from the CPU thread. | ||
| 23 | // Any other thread using this stuff will be FATAL. | ||
| 24 | |||
| 25 | class ThunkManager : public Gen::XCodeBlock | ||
| 26 | { | ||
| 27 | std::map<void *, const u8 *> thunks; | ||
| 28 | |||
| 29 | const u8 *save_regs; | ||
| 30 | const u8 *load_regs; | ||
| 31 | |||
| 32 | public: | ||
| 33 | ThunkManager() { | ||
| 34 | Init(); | ||
| 35 | } | ||
| 36 | ~ThunkManager() { | ||
| 37 | Shutdown(); | ||
| 38 | } | ||
| 39 | void *ProtectFunction(void *function, int num_params); | ||
| 40 | private: | ||
| 41 | void Init(); | ||
| 42 | void Shutdown(); | ||
| 43 | void Reset(); | ||
| 44 | }; | ||
| 45 | |||
| 46 | #endif // _THUNK_H_ | ||
diff --git a/src/common/src/timer.cpp b/src/common/src/timer.cpp new file mode 100644 index 000000000..0300133c5 --- /dev/null +++ b/src/common/src/timer.cpp | |||
| @@ -0,0 +1,226 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <time.h> | ||
| 6 | |||
| 7 | #ifdef _WIN32 | ||
| 8 | #include <Windows.h> | ||
| 9 | #include <mmsystem.h> | ||
| 10 | #include <sys/timeb.h> | ||
| 11 | #else | ||
| 12 | #include <sys/time.h> | ||
| 13 | #endif | ||
| 14 | |||
| 15 | #include "common.h" | ||
| 16 | #include "timer.h" | ||
| 17 | #include "string_util.h" | ||
| 18 | |||
| 19 | namespace Common | ||
| 20 | { | ||
| 21 | |||
| 22 | u32 Timer::GetTimeMs() | ||
| 23 | { | ||
| 24 | #ifdef _WIN32 | ||
| 25 | return timeGetTime(); | ||
| 26 | #else | ||
| 27 | struct timeval t; | ||
| 28 | (void)gettimeofday(&t, NULL); | ||
| 29 | return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000)); | ||
| 30 | #endif | ||
| 31 | } | ||
| 32 | |||
| 33 | // -------------------------------------------- | ||
| 34 | // Initiate, Start, Stop, and Update the time | ||
| 35 | // -------------------------------------------- | ||
| 36 | |||
| 37 | // Set initial values for the class | ||
| 38 | Timer::Timer() | ||
| 39 | : m_LastTime(0), m_StartTime(0), m_Running(false) | ||
| 40 | { | ||
| 41 | Update(); | ||
| 42 | } | ||
| 43 | |||
| 44 | // Write the starting time | ||
| 45 | void Timer::Start() | ||
| 46 | { | ||
| 47 | m_StartTime = GetTimeMs(); | ||
| 48 | m_Running = true; | ||
| 49 | } | ||
| 50 | |||
| 51 | // Stop the timer | ||
| 52 | void Timer::Stop() | ||
| 53 | { | ||
| 54 | // Write the final time | ||
| 55 | m_LastTime = GetTimeMs(); | ||
| 56 | m_Running = false; | ||
| 57 | } | ||
| 58 | |||
| 59 | // Update the last time variable | ||
| 60 | void Timer::Update() | ||
| 61 | { | ||
| 62 | m_LastTime = GetTimeMs(); | ||
| 63 | //TODO(ector) - QPF | ||
| 64 | } | ||
| 65 | |||
| 66 | // ------------------------------------- | ||
| 67 | // Get time difference and elapsed time | ||
| 68 | // ------------------------------------- | ||
| 69 | |||
| 70 | // Get the number of milliseconds since the last Update() | ||
| 71 | u64 Timer::GetTimeDifference() | ||
| 72 | { | ||
| 73 | return GetTimeMs() - m_LastTime; | ||
| 74 | } | ||
| 75 | |||
| 76 | // Add the time difference since the last Update() to the starting time. | ||
| 77 | // This is used to compensate for a paused game. | ||
| 78 | void Timer::AddTimeDifference() | ||
| 79 | { | ||
| 80 | m_StartTime += GetTimeDifference(); | ||
| 81 | } | ||
| 82 | |||
| 83 | // Get the time elapsed since the Start() | ||
| 84 | u64 Timer::GetTimeElapsed() | ||
| 85 | { | ||
| 86 | // If we have not started yet, return 1 (because then I don't | ||
| 87 | // have to change the FPS calculation in CoreRerecording.cpp . | ||
| 88 | if (m_StartTime == 0) return 1; | ||
| 89 | |||
| 90 | // Return the final timer time if the timer is stopped | ||
| 91 | if (!m_Running) return (m_LastTime - m_StartTime); | ||
| 92 | |||
| 93 | return (GetTimeMs() - m_StartTime); | ||
| 94 | } | ||
| 95 | |||
| 96 | // Get the formatted time elapsed since the Start() | ||
| 97 | std::string Timer::GetTimeElapsedFormatted() const | ||
| 98 | { | ||
| 99 | // If we have not started yet, return zero | ||
| 100 | if (m_StartTime == 0) | ||
| 101 | return "00:00:00:000"; | ||
| 102 | |||
| 103 | // The number of milliseconds since the start. | ||
| 104 | // Use a different value if the timer is stopped. | ||
| 105 | u64 Milliseconds; | ||
| 106 | if (m_Running) | ||
| 107 | Milliseconds = GetTimeMs() - m_StartTime; | ||
| 108 | else | ||
| 109 | Milliseconds = m_LastTime - m_StartTime; | ||
| 110 | // Seconds | ||
| 111 | u32 Seconds = (u32)(Milliseconds / 1000); | ||
| 112 | // Minutes | ||
| 113 | u32 Minutes = Seconds / 60; | ||
| 114 | // Hours | ||
| 115 | u32 Hours = Minutes / 60; | ||
| 116 | |||
| 117 | std::string TmpStr = StringFromFormat("%02i:%02i:%02i:%03i", | ||
| 118 | Hours, Minutes % 60, Seconds % 60, Milliseconds % 1000); | ||
| 119 | return TmpStr; | ||
| 120 | } | ||
| 121 | |||
| 122 | // Get current time | ||
| 123 | void Timer::IncreaseResolution() | ||
| 124 | { | ||
| 125 | #ifdef _WIN32 | ||
| 126 | timeBeginPeriod(1); | ||
| 127 | #endif | ||
| 128 | } | ||
| 129 | |||
| 130 | void Timer::RestoreResolution() | ||
| 131 | { | ||
| 132 | #ifdef _WIN32 | ||
| 133 | timeEndPeriod(1); | ||
| 134 | #endif | ||
| 135 | } | ||
| 136 | |||
| 137 | // Get the number of seconds since January 1 1970 | ||
| 138 | u64 Timer::GetTimeSinceJan1970() | ||
| 139 | { | ||
| 140 | time_t ltime; | ||
| 141 | time(<ime); | ||
| 142 | return((u64)ltime); | ||
| 143 | } | ||
| 144 | |||
| 145 | u64 Timer::GetLocalTimeSinceJan1970() | ||
| 146 | { | ||
| 147 | time_t sysTime, tzDiff, tzDST; | ||
| 148 | struct tm * gmTime; | ||
| 149 | |||
| 150 | time(&sysTime); | ||
| 151 | |||
| 152 | // Account for DST where needed | ||
| 153 | gmTime = localtime(&sysTime); | ||
| 154 | if(gmTime->tm_isdst == 1) | ||
| 155 | tzDST = 3600; | ||
| 156 | else | ||
| 157 | tzDST = 0; | ||
| 158 | |||
| 159 | // Lazy way to get local time in sec | ||
| 160 | gmTime = gmtime(&sysTime); | ||
| 161 | tzDiff = sysTime - mktime(gmTime); | ||
| 162 | |||
| 163 | return (u64)(sysTime + tzDiff + tzDST); | ||
| 164 | } | ||
| 165 | |||
| 166 | // Return the current time formatted as Minutes:Seconds:Milliseconds | ||
| 167 | // in the form 00:00:000. | ||
| 168 | std::string Timer::GetTimeFormatted() | ||
| 169 | { | ||
| 170 | time_t sysTime; | ||
| 171 | struct tm * gmTime; | ||
| 172 | char formattedTime[13]; | ||
| 173 | char tmp[13]; | ||
| 174 | |||
| 175 | time(&sysTime); | ||
| 176 | gmTime = localtime(&sysTime); | ||
| 177 | |||
| 178 | strftime(tmp, 6, "%M:%S", gmTime); | ||
| 179 | |||
| 180 | // Now tack on the milliseconds | ||
| 181 | #ifdef _WIN32 | ||
| 182 | struct timeb tp; | ||
| 183 | (void)::ftime(&tp); | ||
| 184 | sprintf(formattedTime, "%s:%03i", tmp, tp.millitm); | ||
| 185 | #else | ||
| 186 | struct timeval t; | ||
| 187 | (void)gettimeofday(&t, NULL); | ||
| 188 | sprintf(formattedTime, "%s:%03d", tmp, (int)(t.tv_usec / 1000)); | ||
| 189 | #endif | ||
| 190 | |||
| 191 | return std::string(formattedTime); | ||
| 192 | } | ||
| 193 | |||
| 194 | // Returns a timestamp with decimals for precise time comparisons | ||
| 195 | // ---------------- | ||
| 196 | double Timer::GetDoubleTime() | ||
| 197 | { | ||
| 198 | #ifdef _WIN32 | ||
| 199 | struct timeb tp; | ||
| 200 | (void)::ftime(&tp); | ||
| 201 | #else | ||
| 202 | struct timeval t; | ||
| 203 | (void)gettimeofday(&t, NULL); | ||
| 204 | #endif | ||
| 205 | // Get continuous timestamp | ||
| 206 | u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970(); | ||
| 207 | |||
| 208 | // Remove a few years. We only really want enough seconds to make | ||
| 209 | // sure that we are detecting actual actions, perhaps 60 seconds is | ||
| 210 | // enough really, but I leave a year of seconds anyway, in case the | ||
| 211 | // user's clock is incorrect or something like that. | ||
| 212 | TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60); | ||
| 213 | |||
| 214 | // Make a smaller integer that fits in the double | ||
| 215 | u32 Seconds = (u32)TmpSeconds; | ||
| 216 | #ifdef _WIN32 | ||
| 217 | double ms = tp.millitm / 1000.0 / 1000.0; | ||
| 218 | #else | ||
| 219 | double ms = t.tv_usec / 1000000.0; | ||
| 220 | #endif | ||
| 221 | double TmpTime = Seconds + ms; | ||
| 222 | |||
| 223 | return TmpTime; | ||
| 224 | } | ||
| 225 | |||
| 226 | } // Namespace Common | ||
diff --git a/src/common/src/timer.h b/src/common/src/timer.h new file mode 100644 index 000000000..9ee5719a3 --- /dev/null +++ b/src/common/src/timer.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #ifndef _TIMER_H_ | ||
| 6 | #define _TIMER_H_ | ||
| 7 | |||
| 8 | #include "common.h" | ||
| 9 | #include <string> | ||
| 10 | |||
| 11 | namespace Common | ||
| 12 | { | ||
| 13 | class Timer | ||
| 14 | { | ||
| 15 | public: | ||
| 16 | Timer(); | ||
| 17 | |||
| 18 | void Start(); | ||
| 19 | void Stop(); | ||
| 20 | void Update(); | ||
| 21 | |||
| 22 | // The time difference is always returned in milliseconds, regardless of alternative internal representation | ||
| 23 | u64 GetTimeDifference(); | ||
| 24 | void AddTimeDifference(); | ||
| 25 | |||
| 26 | static void IncreaseResolution(); | ||
| 27 | static void RestoreResolution(); | ||
| 28 | static u64 GetTimeSinceJan1970(); | ||
| 29 | static u64 GetLocalTimeSinceJan1970(); | ||
| 30 | static double GetDoubleTime(); | ||
| 31 | |||
| 32 | static std::string GetTimeFormatted(); | ||
| 33 | std::string GetTimeElapsedFormatted() const; | ||
| 34 | u64 GetTimeElapsed(); | ||
| 35 | |||
| 36 | static u32 GetTimeMs(); | ||
| 37 | |||
| 38 | private: | ||
| 39 | u64 m_LastTime; | ||
| 40 | u64 m_StartTime; | ||
| 41 | bool m_Running; | ||
| 42 | }; | ||
| 43 | |||
| 44 | } // Namespace Common | ||
| 45 | |||
| 46 | #endif // _TIMER_H_ | ||
diff --git a/src/common/src/version.cpp b/src/common/src/version.cpp new file mode 100644 index 000000000..47d7b52ad --- /dev/null +++ b/src/common/src/version.cpp | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project | ||
| 2 | // Licensed under GPLv2 | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common.h" | ||
| 6 | #include "scm_rev.h" | ||
| 7 | |||
| 8 | #ifdef _DEBUG | ||
| 9 | #define BUILD_TYPE_STR "Debug " | ||
| 10 | #elif defined DEBUGFAST | ||
| 11 | #define BUILD_TYPE_STR "DebugFast " | ||
| 12 | #else | ||
| 13 | #define BUILD_TYPE_STR "" | ||
| 14 | #endif | ||
| 15 | |||
| 16 | const char *scm_rev_str = "Dolphin " | ||
| 17 | #if !SCM_IS_MASTER | ||
| 18 | "[" SCM_BRANCH_STR "] " | ||
| 19 | #endif | ||
| 20 | |||
| 21 | #ifdef __INTEL_COMPILER | ||
| 22 | BUILD_TYPE_STR SCM_DESC_STR "-ICC"; | ||
| 23 | #else | ||
| 24 | BUILD_TYPE_STR SCM_DESC_STR; | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #ifdef _M_X64 | ||
| 28 | #define NP_ARCH "x64" | ||
| 29 | #else | ||
| 30 | #ifdef _M_ARM | ||
| 31 | #define NP_ARCH "ARM" | ||
| 32 | #else | ||
| 33 | #define NP_ARCH "x86" | ||
| 34 | #endif | ||
| 35 | #endif | ||
| 36 | |||
| 37 | #ifdef _WIN32 | ||
| 38 | const char *netplay_dolphin_ver = SCM_DESC_STR " W" NP_ARCH; | ||
| 39 | #elif __APPLE__ | ||
| 40 | const char *netplay_dolphin_ver = SCM_DESC_STR " M" NP_ARCH; | ||
| 41 | #else | ||
| 42 | const char *netplay_dolphin_ver = SCM_DESC_STR " L" NP_ARCH; | ||
| 43 | #endif | ||
| 44 | |||
| 45 | const char *scm_rev_git_str = SCM_REV_STR; | ||
diff --git a/vsprops/CodeGen_Debug.props b/vsprops/code_generation_debug.props index b138f38e7..b138f38e7 100644 --- a/vsprops/CodeGen_Debug.props +++ b/vsprops/code_generation_debug.props | |||
diff --git a/vsprops/CodeGen_Release.props b/vsprops/code_generation_release.props index 0a74151ce..0a74151ce 100644 --- a/vsprops/CodeGen_Release.props +++ b/vsprops/code_generation_release.props | |||