aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar ShizZy <shizzy@6bit.net>2013-09-04 20:17:46 -0400
committerGravatar ShizZy <shizzy@6bit.net>2013-09-04 20:17:46 -0400
commit7564d28faf780bc0967ab3f6ff11b29b44e4b1aa (patch)
tree48c87985296d0bc8d62785b8f3209f4b727d0cc0 /src
parent72325bef1d6d1e70f308e64bf7d764f9fd2abbc4 (diff)
replaced common code with dolphin common
Diffstat (limited to 'src')
-rw-r--r--src/common/common.vcxproj185
-rw-r--r--src/common/common.vcxproj.filters68
-rw-r--r--src/common/src/atomic.h19
-rw-r--r--src/common/src/atomic_gcc.h113
-rw-r--r--src/common/src/atomic_win32.h72
-rw-r--r--src/common/src/break_points.cpp203
-rw-r--r--src/common/src/break_points.h102
-rw-r--r--src/common/src/chunk_file.h384
-rw-r--r--src/common/src/common.h164
-rw-r--r--src/common/src/common_funcs.h243
-rw-r--r--src/common/src/common_paths.h71
-rw-r--r--src/common/src/common_types.h48
-rw-r--r--src/common/src/console_listener.cpp337
-rw-r--r--src/common/src/console_listener.h41
-rw-r--r--src/common/src/cpu_detect.h81
-rw-r--r--src/common/src/debug_interface.h39
-rw-r--r--src/common/src/extended_trace.cpp433
-rw-r--r--src/common/src/extended_trace.h53
-rw-r--r--src/common/src/fifo_queue.h115
-rw-r--r--src/common/src/file_search.cpp106
-rw-r--r--src/common/src/file_search.h28
-rw-r--r--src/common/src/file_util.cpp922
-rw-r--r--src/common/src/file_util.h232
-rw-r--r--src/common/src/fixed_size_queue.h75
-rw-r--r--src/common/src/hash.cpp520
-rw-r--r--src/common/src/hash.h20
-rw-r--r--src/common/src/linear_disk_cache.h191
-rw-r--r--src/common/src/log.h155
-rw-r--r--src/common/src/log_manager.cpp199
-rw-r--r--src/common/src/log_manager.h169
-rw-r--r--src/common/src/math_util.cpp212
-rw-r--r--src/common/src/math_util.h200
-rw-r--r--src/common/src/mem_arena.cpp304
-rw-r--r--src/common/src/mem_arena.h58
-rw-r--r--src/common/src/memory_util.cpp197
-rw-r--r--src/common/src/memory_util.h25
-rw-r--r--src/common/src/misc.cpp33
-rw-r--r--src/common/src/msg_handler.cpp107
-rw-r--r--src/common/src/msg_handler.h73
-rw-r--r--src/common/src/scm_rev.h4
-rw-r--r--src/common/src/std_condition_variable.h170
-rw-r--r--src/common/src/std_mutex.h365
-rw-r--r--src/common/src/std_thread.h317
-rw-r--r--src/common/src/string_util.cpp531
-rw-r--r--src/common/src/string_util.h111
-rw-r--r--src/common/src/thread.cpp133
-rw-r--r--src/common/src/thread.h156
-rw-r--r--src/common/src/thunk.h46
-rw-r--r--src/common/src/timer.cpp226
-rw-r--r--src/common/src/timer.h46
-rw-r--r--src/common/src/version.cpp45
51 files changed, 8640 insertions, 107 deletions
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index c61ba6d9..1cfe0bb3 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -1,6 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="DebugFast|Win32">
+ <Configuration>DebugFast</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="DebugFast|x64">
+ <Configuration>DebugFast</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
@@ -20,102 +28,82 @@
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{DFE335FC-755D-4BAA-8452-94434F8A1EDB}</ProjectGuid>
- <RootNamespace>common</RootNamespace>
+ <RootNamespace>Common</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
<ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
- <ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- <Import Project="..\..\vsprops\Base.props" />
- <Import Project="..\..\vsprops\CodeGen_Debug.props" />
- <Import Project="..\..\vsprops\Optimization_Debug.props" />
- <Import Project="..\..\vsprops\Externals.props" />
+ <Import Project="..\..\vsprops\base.props" />
+ <Import Project="..\..\vsprops\code_generation_debug.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- <Import Project="..\..\vsprops\Base.props" />
- <Import Project="..\..\vsprops\CodeGen_Debug.props" />
- <Import Project="..\..\vsprops\Optimization_Debug.props" />
- <Import Project="..\..\vsprops\Externals.props" />
+ <Import Project="..\..\vsprops\base.props" />
+ <Import Project="..\..\vsprops\code_generation_debug.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- <Import Project="..\..\vsprops\Base.props" />
- <Import Project="..\..\vsprops\CodeGen_Release.props" />
- <Import Project="..\..\vsprops\Optimization_Release.props" />
- <Import Project="..\..\vsprops\Externals.props" />
+ <Import Project="..\..\vsprops\base.props" />
+ <Import Project="..\..\vsprops\code_generation_release.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
- <Import Project="..\..\vsprops\Base.props" />
- <Import Project="..\..\vsprops\CodeGen_Release.props" />
- <Import Project="..\..\vsprops\Optimization_Release.props" />
- <Import Project="..\..\vsprops\Externals.props" />
+ <Import Project="..\..\vsprops\base.props" />
+ <Import Project="..\..\vsprops\code_generation_release.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
- <CustomBuildBeforeTargets>
- </CustomBuildBeforeTargets>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
- <CustomBuildBeforeTargets />
- </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
- <CustomBuildBeforeTargets>
- </CustomBuildBeforeTargets>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
- <CustomBuildBeforeTargets />
- </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile />
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
- <CustomBuildStep>
- <Command>
- </Command>
- <Message>
- </Message>
- <Outputs>
- </Outputs>
- </CustomBuildStep>
+ <Lib />
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile />
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
- <CustomBuildStep>
- <Command>
- </Command>
- <Message>
- </Message>
- <Outputs>
- </Outputs>
- </CustomBuildStep>
+ <Lib />
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile />
@@ -124,15 +112,16 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
- <CustomBuildStep>
- <Command>
- </Command>
- <Message>
- </Message>
- <Outputs>
- </Outputs>
- </CustomBuildStep>
+ <Lib />
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
<ClCompile />
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ <Lib />
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile />
@@ -141,46 +130,72 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
- <CustomBuildStep>
- <Command>
- </Command>
- <Message>
- </Message>
- <Outputs>
- </Outputs>
- </CustomBuildStep>
+ <Lib />
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
+ <ClCompile />
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ <Lib />
</ItemDefinitionGroup>
- <ItemGroup>
- <ClCompile Include="src\config.cpp" />
- <ClCompile Include="src\crc.cpp" />
- <ClCompile Include="src\file_utils.cpp" />
- <ClCompile Include="src\hash.cpp" />
- <ClCompile Include="src\log.cpp" />
- <ClCompile Include="src\misc_utils.cpp" />
- <ClCompile Include="src\timer.cpp" />
- <ClCompile Include="src\x86_utils.cpp" />
- <ClCompile Include="src\xml.cpp" />
- </ItemGroup>
<ItemGroup>
<ClInclude Include="src\atomic.h" />
<ClInclude Include="src\atomic_gcc.h" />
<ClInclude Include="src\atomic_win32.h" />
+ <ClInclude Include="src\break_points.h" />
+ <ClInclude Include="src\chunk_file.h" />
<ClInclude Include="src\common.h" />
- <ClInclude Include="src\config.h" />
- <ClInclude Include="src\crc.h" />
- <ClInclude Include="src\file_utils.h" />
+ <ClInclude Include="src\common_funcs.h" />
+ <ClInclude Include="src\common_paths.h" />
+ <ClInclude Include="src\common_types.h" />
+ <ClInclude Include="src\console_listener.h" />
+ <ClInclude Include="src\cpu_detect.h" />
+ <ClInclude Include="src\debug_interface.h" />
+ <ClInclude Include="src\extended_trace.h" />
+ <ClInclude Include="src\fifo_queue.h" />
+ <ClInclude Include="src\file_search.h" />
+ <ClInclude Include="src\file_util.h" />
+ <ClInclude Include="src\fixed_size_queue.h" />
<ClInclude Include="src\hash.h" />
- <ClInclude Include="src\hash_container.h" />
+ <ClInclude Include="src\linear_disk_cache.h" />
<ClInclude Include="src\log.h" />
- <ClInclude Include="src\misc_utils.h" />
- <ClInclude Include="src\platform.h" />
+ <ClInclude Include="src\log_manager.h" />
+ <ClInclude Include="src\math_util.h" />
+ <ClInclude Include="src\memory_util.h" />
+ <ClInclude Include="src\mem_arena.h" />
+ <ClInclude Include="src\msg_handler.h" />
+ <ClInclude Include="src\scm_rev.h" />
<ClInclude Include="src\std_condition_variable.h" />
<ClInclude Include="src\std_mutex.h" />
<ClInclude Include="src\std_thread.h" />
+ <ClInclude Include="src\string_util.h" />
+ <ClInclude Include="src\thread.h" />
+ <ClInclude Include="src\thunk.h" />
<ClInclude Include="src\timer.h" />
- <ClInclude Include="src\types.h" />
- <ClInclude Include="src\x86_utils.h" />
- <ClInclude Include="src\xml.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="src\break_points.cpp" />
+ <ClCompile Include="src\console_listener.cpp" />
+ <ClCompile Include="src\extended_trace.cpp" />
+ <ClCompile Include="src\file_search.cpp" />
+ <ClCompile Include="src\file_util.cpp" />
+ <ClCompile Include="src\hash.cpp" />
+ <ClCompile Include="src\log_manager.cpp" />
+ <ClCompile Include="src\math_util.cpp" />
+ <ClCompile Include="src\memory_util.cpp" />
+ <ClCompile Include="src\mem_arena.cpp" />
+ <ClCompile Include="src\misc.cpp" />
+ <ClCompile Include="src\msg_handler.cpp" />
+ <ClCompile Include="src\string_util.cpp" />
+ <ClCompile Include="src\thread.cpp" />
+ <ClCompile Include="src\timer.cpp" />
+ <ClCompile Include="src\version.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="CMakeLists.txt" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
index 9d2b5c51..ab8e5d12 100644
--- a/src/common/common.vcxproj.filters
+++ b/src/common/common.vcxproj.filters
@@ -1,35 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
- <ClCompile Include="src\crc.cpp" />
- <ClCompile Include="src\log.cpp" />
- <ClCompile Include="src\timer.cpp" />
- <ClCompile Include="src\xml.cpp" />
- <ClCompile Include="src\config.cpp" />
- <ClCompile Include="src\misc_utils.cpp" />
+ <ClCompile Include="src\break_points.cpp" />
+ <ClCompile Include="src\console_listener.cpp" />
+ <ClCompile Include="src\extended_trace.cpp" />
+ <ClCompile Include="src\file_search.cpp" />
+ <ClCompile Include="src\file_util.cpp" />
<ClCompile Include="src\hash.cpp" />
- <ClCompile Include="src\x86_utils.cpp" />
- <ClCompile Include="src\file_utils.cpp" />
+ <ClCompile Include="src\log_manager.cpp" />
+ <ClCompile Include="src\math_util.cpp" />
+ <ClCompile Include="src\memory_util.cpp" />
+ <ClCompile Include="src\mem_arena.cpp" />
+ <ClCompile Include="src\misc.cpp" />
+ <ClCompile Include="src\msg_handler.cpp" />
+ <ClCompile Include="src\string_util.cpp" />
+ <ClCompile Include="src\thread.cpp" />
+ <ClCompile Include="src\timer.cpp" />
+ <ClCompile Include="src\version.cpp" />
</ItemGroup>
<ItemGroup>
- <ClInclude Include="src\crc.h" />
- <ClInclude Include="src\platform.h" />
+ <ClInclude Include="src\atomic.h" />
+ <ClInclude Include="src\break_points.h" />
+ <ClInclude Include="src\chunk_file.h" />
<ClInclude Include="src\common.h" />
- <ClInclude Include="src\types.h" />
+ <ClInclude Include="src\common_funcs.h" />
+ <ClInclude Include="src\common_paths.h" />
+ <ClInclude Include="src\common_types.h" />
+ <ClInclude Include="src\console_listener.h" />
+ <ClInclude Include="src\cpu_detect.h" />
+ <ClInclude Include="src\debug_interface.h" />
+ <ClInclude Include="src\extended_trace.h" />
+ <ClInclude Include="src\fifo_queue.h" />
+ <ClInclude Include="src\file_search.h" />
+ <ClInclude Include="src\file_util.h" />
+ <ClInclude Include="src\fixed_size_queue.h" />
+ <ClInclude Include="src\hash.h" />
+ <ClInclude Include="src\linear_disk_cache.h" />
<ClInclude Include="src\log.h" />
- <ClInclude Include="src\timer.h" />
- <ClInclude Include="src\x86_utils.h" />
- <ClInclude Include="src\config.h" />
- <ClInclude Include="src\xml.h" />
- <ClInclude Include="src\misc_utils.h" />
- <ClInclude Include="src\atomic.h" />
- <ClInclude Include="src\atomic_win32.h" />
- <ClInclude Include="src\atomic_gcc.h" />
+ <ClInclude Include="src\log_manager.h" />
+ <ClInclude Include="src\math_util.h" />
+ <ClInclude Include="src\memory_util.h" />
+ <ClInclude Include="src\mem_arena.h" />
+ <ClInclude Include="src\msg_handler.h" />
+ <ClInclude Include="src\scm_rev.h" />
<ClInclude Include="src\std_condition_variable.h" />
<ClInclude Include="src\std_mutex.h" />
<ClInclude Include="src\std_thread.h" />
- <ClInclude Include="src\hash_container.h" />
- <ClInclude Include="src\hash.h" />
- <ClInclude Include="src\file_utils.h" />
+ <ClInclude Include="src\string_util.h" />
+ <ClInclude Include="src\thread.h" />
+ <ClInclude Include="src\thunk.h" />
+ <ClInclude Include="src\timer.h" />
+ <ClInclude Include="src\atomic_gcc.h" />
+ <ClInclude Include="src\atomic_win32.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="CMakeLists.txt" />
</ItemGroup>
</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 00000000..883bc14f
--- /dev/null
+++ b/src/common/src/atomic.h
@@ -0,0 +1,19 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _ATOMIC_H_
+#define _ATOMIC_H_
+
+#ifdef _WIN32
+
+#include "atomic_win32.h"
+
+#else
+
+// GCC-compatible compiler assumed!
+#include "atomic_gcc.h"
+
+#endif
+
+#endif
diff --git a/src/common/src/atomic_gcc.h b/src/common/src/atomic_gcc.h
new file mode 100644
index 00000000..0f820f4f
--- /dev/null
+++ b/src/common/src/atomic_gcc.h
@@ -0,0 +1,113 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _ATOMIC_GCC_H_
+#define _ATOMIC_GCC_H_
+
+#include "common.h"
+
+// Atomic operations are performed in a single step by the CPU. It is
+// impossible for other threads to see the operation "half-done."
+//
+// Some atomic operations can be combined with different types of memory
+// barriers called "Acquire semantics" and "Release semantics", defined below.
+//
+// Acquire semantics: Future memory accesses cannot be relocated to before the
+// operation.
+//
+// Release semantics: Past memory accesses cannot be relocated to after the
+// operation.
+//
+// These barriers affect not only the compiler, but also the CPU.
+
+namespace Common
+{
+
+inline void AtomicAdd(volatile u32& target, u32 value) {
+ __sync_add_and_fetch(&target, value);
+}
+
+inline void AtomicAnd(volatile u32& target, u32 value) {
+ __sync_and_and_fetch(&target, value);
+}
+
+inline void AtomicDecrement(volatile u32& target) {
+ __sync_add_and_fetch(&target, -1);
+}
+
+inline void AtomicIncrement(volatile u32& target) {
+ __sync_add_and_fetch(&target, 1);
+}
+
+inline u32 AtomicLoad(volatile u32& src) {
+ return src; // 32-bit reads are always atomic.
+}
+inline u32 AtomicLoadAcquire(volatile u32& src) {
+ //keep the compiler from caching any memory references
+ u32 result = src; // 32-bit reads are always atomic.
+ //__sync_synchronize(); // TODO: May not be necessary.
+ // Compiler instruction only. x86 loads always have acquire semantics.
+ __asm__ __volatile__ ( "":::"memory" );
+ return result;
+}
+
+inline void AtomicOr(volatile u32& target, u32 value) {
+ __sync_or_and_fetch(&target, value);
+}
+
+inline void AtomicStore(volatile u32& dest, u32 value) {
+ dest = value; // 32-bit writes are always atomic.
+}
+inline void AtomicStoreRelease(volatile u32& dest, u32 value) {
+ __sync_lock_test_and_set(&dest, value); // TODO: Wrong! This function is has acquire semantics.
+}
+
+}
+
+// Old code kept here for reference in case we need the parts with __asm__ __volatile__.
+#if 0
+LONG SyncInterlockedIncrement(LONG *Dest)
+{
+#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
+ return __sync_add_and_fetch(Dest, 1);
+#else
+ register int result;
+ __asm__ __volatile__("lock; xadd %0,%1"
+ : "=r" (result), "=m" (*Dest)
+ : "0" (1), "m" (*Dest)
+ : "memory");
+ return result;
+#endif
+}
+
+LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val)
+{
+#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
+ return __sync_add_and_fetch(Dest, Val);
+#else
+ register int result;
+ __asm__ __volatile__("lock; xadd %0,%1"
+ : "=r" (result), "=m" (*Dest)
+ : "0" (Val), "m" (*Dest)
+ : "memory");
+ return result;
+#endif
+}
+
+LONG SyncInterlockedExchange(LONG *Dest, LONG Val)
+{
+#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
+ return __sync_lock_test_and_set(Dest, Val);
+#else
+ register int result;
+ __asm__ __volatile__("lock; xchg %0,%1"
+ : "=r" (result), "=m" (*Dest)
+ : "0" (Val), "m" (*Dest)
+ : "memory");
+ return result;
+#endif
+}
+#endif
+
+#endif
diff --git a/src/common/src/atomic_win32.h b/src/common/src/atomic_win32.h
new file mode 100644
index 00000000..31ee0b78
--- /dev/null
+++ b/src/common/src/atomic_win32.h
@@ -0,0 +1,72 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _ATOMIC_WIN32_H_
+#define _ATOMIC_WIN32_H_
+
+#include "common.h"
+#include <intrin.h>
+#include <Windows.h>
+
+// Atomic operations are performed in a single step by the CPU. It is
+// impossible for other threads to see the operation "half-done."
+//
+// Some atomic operations can be combined with different types of memory
+// barriers called "Acquire semantics" and "Release semantics", defined below.
+//
+// Acquire semantics: Future memory accesses cannot be relocated to before the
+// operation.
+//
+// Release semantics: Past memory accesses cannot be relocated to after the
+// operation.
+//
+// These barriers affect not only the compiler, but also the CPU.
+//
+// NOTE: Acquire and Release are not differentiated right now. They perform a
+// full memory barrier instead of a "one-way" memory barrier. The newest
+// Windows SDK has Acquire and Release versions of some Interlocked* functions.
+
+namespace Common
+{
+
+inline void AtomicAdd(volatile u32& target, u32 value) {
+ InterlockedExchangeAdd((volatile LONG*)&target, (LONG)value);
+}
+
+inline void AtomicAnd(volatile u32& target, u32 value) {
+ _InterlockedAnd((volatile LONG*)&target, (LONG)value);
+}
+
+inline void AtomicIncrement(volatile u32& target) {
+ InterlockedIncrement((volatile LONG*)&target);
+}
+
+inline void AtomicDecrement(volatile u32& target) {
+ InterlockedDecrement((volatile LONG*)&target);
+}
+
+inline u32 AtomicLoad(volatile u32& src) {
+ return src; // 32-bit reads are always atomic.
+}
+inline u32 AtomicLoadAcquire(volatile u32& src) {
+ u32 result = src; // 32-bit reads are always atomic.
+ _ReadBarrier(); // Compiler instruction only. x86 loads always have acquire semantics.
+ return result;
+}
+
+inline void AtomicOr(volatile u32& target, u32 value) {
+ _InterlockedOr((volatile LONG*)&target, (LONG)value);
+}
+
+inline void AtomicStore(volatile u32& dest, u32 value) {
+ dest = value; // 32-bit writes are always atomic.
+}
+inline void AtomicStoreRelease(volatile u32& dest, u32 value) {
+ _WriteBarrier(); // Compiler instruction only. x86 stores always have release semantics.
+ dest = value; // 32-bit writes are always atomic.
+}
+
+}
+
+#endif
diff --git a/src/common/src/break_points.cpp b/src/common/src/break_points.cpp
new file mode 100644
index 00000000..9e5c6515
--- /dev/null
+++ b/src/common/src/break_points.cpp
@@ -0,0 +1,203 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common.h"
+#include "debug_interface.h"
+#include "break_points.h"
+
+#include <sstream>
+#include <algorithm>
+
+bool BreakPoints::IsAddressBreakPoint(u32 _iAddress)
+{
+ for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i)
+ if (i->iAddress == _iAddress)
+ return true;
+ return false;
+}
+
+bool BreakPoints::IsTempBreakPoint(u32 _iAddress)
+{
+ for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i)
+ if (i->iAddress == _iAddress && i->bTemporary)
+ return true;
+ return false;
+}
+
+BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const
+{
+ TBreakPointsStr bps;
+ for (TBreakPoints::const_iterator i = m_BreakPoints.begin();
+ i != m_BreakPoints.end(); ++i)
+ {
+ if (!i->bTemporary)
+ {
+ std::stringstream bp;
+ bp << std::hex << i->iAddress << " " << (i->bOn ? "n" : "");
+ bps.push_back(bp.str());
+ }
+ }
+
+ return bps;
+}
+
+void BreakPoints::AddFromStrings(const TBreakPointsStr& bps)
+{
+ for (TBreakPointsStr::const_iterator i = bps.begin(); i != bps.end(); ++i)
+ {
+ TBreakPoint bp;
+ std::stringstream bpstr;
+ bpstr << std::hex << *i;
+ bpstr >> bp.iAddress;
+ bp.bOn = i->find("n") != i->npos;
+ bp.bTemporary = false;
+ Add(bp);
+ }
+}
+
+void BreakPoints::Add(const TBreakPoint& bp)
+{
+ if (!IsAddressBreakPoint(bp.iAddress))
+ {
+ m_BreakPoints.push_back(bp);
+ //if (jit)
+ // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
+ }
+}
+
+void BreakPoints::Add(u32 em_address, bool temp)
+{
+ if (!IsAddressBreakPoint(em_address)) // only add new addresses
+ {
+ TBreakPoint pt; // breakpoint settings
+ pt.bOn = true;
+ pt.bTemporary = temp;
+ pt.iAddress = em_address;
+
+ m_BreakPoints.push_back(pt);
+
+ //if (jit)
+ // jit->GetBlockCache()->InvalidateICache(em_address, 4);
+ }
+}
+
+void BreakPoints::Remove(u32 em_address)
+{
+ for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i)
+ {
+ if (i->iAddress == em_address)
+ {
+ m_BreakPoints.erase(i);
+ //if (jit)
+ // jit->GetBlockCache()->InvalidateICache(em_address, 4);
+ return;
+ }
+ }
+}
+
+void BreakPoints::Clear()
+{
+ //if (jit)
+ //{
+ // std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
+ // [](const TBreakPoint& bp)
+ // {
+ // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
+ // }
+ // );
+ //}
+
+ m_BreakPoints.clear();
+}
+
+MemChecks::TMemChecksStr MemChecks::GetStrings() const
+{
+ TMemChecksStr mcs;
+ for (TMemChecks::const_iterator i = m_MemChecks.begin();
+ i != m_MemChecks.end(); ++i)
+ {
+ std::stringstream mc;
+ mc << std::hex << i->StartAddress;
+ mc << " " << (i->bRange ? i->EndAddress : i->StartAddress) << " " <<
+ (i->bRange ? "n" : "") << (i->OnRead ? "r" : "") <<
+ (i->OnWrite ? "w" : "") << (i->Log ? "l" : "") << (i->Break ? "p" : "");
+ mcs.push_back(mc.str());
+ }
+
+ return mcs;
+}
+
+void MemChecks::AddFromStrings(const TMemChecksStr& mcs)
+{
+ for (TMemChecksStr::const_iterator i = mcs.begin(); i != mcs.end(); ++i)
+ {
+ TMemCheck mc;
+ std::stringstream mcstr;
+ mcstr << std::hex << *i;
+ mcstr >> mc.StartAddress;
+ mc.bRange = i->find("n") != i->npos;
+ mc.OnRead = i->find("r") != i->npos;
+ mc.OnWrite = i->find("w") != i->npos;
+ mc.Log = i->find("l") != i->npos;
+ mc.Break = i->find("p") != i->npos;
+ if (mc.bRange)
+ mcstr >> mc.EndAddress;
+ else
+ mc.EndAddress = mc.StartAddress;
+ Add(mc);
+ }
+}
+
+void MemChecks::Add(const TMemCheck& _rMemoryCheck)
+{
+ if (GetMemCheck(_rMemoryCheck.StartAddress) == 0)
+ m_MemChecks.push_back(_rMemoryCheck);
+}
+
+void MemChecks::Remove(u32 _Address)
+{
+ for (TMemChecks::iterator i = m_MemChecks.begin(); i != m_MemChecks.end(); ++i)
+ {
+ if (i->StartAddress == _Address)
+ {
+ m_MemChecks.erase(i);
+ return;
+ }
+ }
+}
+
+TMemCheck *MemChecks::GetMemCheck(u32 address)
+{
+ for (TMemChecks::iterator i = m_MemChecks.begin(); i != m_MemChecks.end(); ++i)
+ {
+ if (i->bRange)
+ {
+ if (address >= i->StartAddress && address <= i->EndAddress)
+ return &(*i);
+ }
+ else if (i->StartAddress == address)
+ return &(*i);
+ }
+
+ // none found
+ return 0;
+}
+
+void TMemCheck::Action(DebugInterface *debug_interface, u32 iValue, u32 addr,
+ bool write, int size, u32 pc)
+{
+ if ((write && OnWrite) || (!write && OnRead))
+ {
+ if (Log)
+ {
+ INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)",
+ pc, debug_interface->getDescription(pc).c_str(),
+ write ? "Write" : "Read", size*8, size*2, iValue, addr,
+ debug_interface->getDescription(addr).c_str()
+ );
+ }
+ if (Break)
+ debug_interface->breakNow();
+ }
+}
diff --git a/src/common/src/break_points.h b/src/common/src/break_points.h
new file mode 100644
index 00000000..281de100
--- /dev/null
+++ b/src/common/src/break_points.h
@@ -0,0 +1,102 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _DEBUGGER_BREAKPOINTS_H
+#define _DEBUGGER_BREAKPOINTS_H
+
+#include <vector>
+#include <string>
+
+#include "common.h"
+
+class DebugInterface;
+
+struct TBreakPoint
+{
+ u32 iAddress;
+ bool bOn;
+ bool bTemporary;
+};
+
+struct TMemCheck
+{
+ TMemCheck() {
+ numHits = 0;
+ StartAddress = EndAddress = 0;
+ bRange = OnRead = OnWrite = Log = Break = false;
+ }
+ u32 StartAddress;
+ u32 EndAddress;
+
+ bool bRange;
+
+ bool OnRead;
+ bool OnWrite;
+
+ bool Log;
+ bool Break;
+
+ u32 numHits;
+
+ void Action(DebugInterface *dbg_interface, u32 _iValue, u32 addr,
+ bool write, int size, u32 pc);
+};
+
+// Code breakpoints.
+class BreakPoints
+{
+public:
+ typedef std::vector<TBreakPoint> TBreakPoints;
+ typedef std::vector<std::string> TBreakPointsStr;
+
+ const TBreakPoints& GetBreakPoints() { return m_BreakPoints; }
+
+ TBreakPointsStr GetStrings() const;
+ void AddFromStrings(const TBreakPointsStr& bps);
+
+ // is address breakpoint
+ bool IsAddressBreakPoint(u32 _iAddress);
+ bool IsTempBreakPoint(u32 _iAddress);
+
+ // Add BreakPoint
+ void Add(u32 em_address, bool temp=false);
+ void Add(const TBreakPoint& bp);
+
+ // Remove Breakpoint
+ void Remove(u32 _iAddress);
+ void Clear();
+
+ void DeleteByAddress(u32 _Address);
+
+private:
+ TBreakPoints m_BreakPoints;
+ u32 m_iBreakOnCount;
+};
+
+
+// Memory breakpoints
+class MemChecks
+{
+public:
+ typedef std::vector<TMemCheck> TMemChecks;
+ typedef std::vector<std::string> TMemChecksStr;
+
+ TMemChecks m_MemChecks;
+
+ const TMemChecks& GetMemChecks() { return m_MemChecks; }
+
+ TMemChecksStr GetStrings() const;
+ void AddFromStrings(const TMemChecksStr& mcs);
+
+ void Add(const TMemCheck& _rMemoryCheck);
+
+ // memory breakpoint
+ TMemCheck *GetMemCheck(u32 address);
+ void Remove(u32 _Address);
+
+ void Clear() { m_MemChecks.clear(); };
+};
+
+#endif
+
diff --git a/src/common/src/chunk_file.h b/src/common/src/chunk_file.h
new file mode 100644
index 00000000..0c333b09
--- /dev/null
+++ b/src/common/src/chunk_file.h
@@ -0,0 +1,384 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#ifndef _POINTERWRAP_H_
+#define _POINTERWRAP_H_
+
+// Extremely simple serialization framework.
+
+// (mis)-features:
+// + Super fast
+// + Very simple
+// + Same code is used for serialization and deserializaition (in most cases)
+// - Zero backwards/forwards compatibility
+// - Serialization code for anything complex has to be manually written.
+
+#include <map>
+#include <vector>
+#include <list>
+#include <deque>
+#include <string>
+#include <type_traits>
+
+#include "common.h"
+#include "file_util.h"
+
+template <class T>
+struct LinkedListItem : public T
+{
+ LinkedListItem<T> *next;
+};
+
+// Wrapper class
+class PointerWrap
+{
+public:
+ enum Mode
+ {
+ MODE_READ = 1, // load
+ MODE_WRITE, // save
+ MODE_MEASURE, // calculate size
+ MODE_VERIFY, // compare
+ };
+
+ u8 **ptr;
+ Mode mode;
+
+public:
+ PointerWrap(u8 **ptr_, Mode mode_) : ptr(ptr_), mode(mode_) {}
+
+ void SetMode(Mode mode_) { mode = mode_; }
+ Mode GetMode() const { return mode; }
+ u8** GetPPtr() { return ptr; }
+
+ template <typename K, class V>
+ void Do(std::map<K, V>& x)
+ {
+ u32 count = (u32)x.size();
+ Do(count);
+
+ switch (mode)
+ {
+ case MODE_READ:
+ for (x.clear(); count != 0; --count)
+ {
+ std::pair<K, V> pair;
+ Do(pair.first);
+ Do(pair.second);
+ x.insert(pair);
+ }
+ break;
+
+ case MODE_WRITE:
+ case MODE_MEASURE:
+ case MODE_VERIFY:
+ for (auto itr = x.begin(); itr != x.end(); ++itr)
+ {
+ Do(itr->first);
+ Do(itr->second);
+ }
+ break;
+ }
+ }
+
+ template <typename T>
+ void DoContainer(T& x)
+ {
+ u32 size = (u32)x.size();
+ Do(size);
+ x.resize(size);
+
+ for (auto itr = x.begin(); itr != x.end(); ++itr)
+ Do(*itr);
+ }
+
+ template <typename T>
+ void Do(std::vector<T>& x)
+ {
+ DoContainer(x);
+ }
+
+ template <typename T>
+ void Do(std::list<T>& x)
+ {
+ DoContainer(x);
+ }
+
+ template <typename T>
+ void Do(std::deque<T>& x)
+ {
+ DoContainer(x);
+ }
+
+ template <typename T>
+ void Do(std::basic_string<T>& x)
+ {
+ DoContainer(x);
+ }
+
+ template <typename T>
+ void DoArray(T* x, u32 count)
+ {
+ for (u32 i = 0; i != count; ++i)
+ Do(x[i]);
+ }
+
+ template <typename T>
+ void Do(T& x)
+ {
+ // Ideally this would be std::is_trivially_copyable, but not enough support yet
+ static_assert(std::is_pod<T>::value, "Only sane for POD types");
+
+ DoVoid((void*)&x, sizeof(x));
+ }
+
+ template <typename T>
+ void DoPOD(T& x)
+ {
+ DoVoid((void*)&x, sizeof(x));
+ }
+
+ template <typename T>
+ void DoPointer(T*& x, T* const base)
+ {
+ // pointers can be more than 2^31 apart, but you're using this function wrong if you need that much range
+ s32 offset = x - base;
+ Do(offset);
+ if (mode == MODE_READ)
+ x = base + offset;
+ }
+
+ // Let's pretend std::list doesn't exist!
+ template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*), void (*TDo)(PointerWrap&, T*)>
+ void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end=0)
+ {
+ LinkedListItem<T>* list_cur = list_start;
+ LinkedListItem<T>* prev = 0;
+
+ while (true)
+ {
+ u8 shouldExist = (list_cur ? 1 : 0);
+ Do(shouldExist);
+ if (shouldExist == 1)
+ {
+ LinkedListItem<T>* cur = list_cur ? list_cur : TNew();
+ TDo(*this, (T*)cur);
+ if (!list_cur)
+ {
+ if (mode == MODE_READ)
+ {
+ cur->next = 0;
+ list_cur = cur;
+ if (prev)
+ prev->next = cur;
+ else
+ list_start = cur;
+ }
+ else
+ {
+ TFree(cur);
+ continue;
+ }
+ }
+ }
+ else
+ {
+ if (mode == MODE_READ)
+ {
+ if (prev)
+ prev->next = 0;
+ if (list_end)
+ *list_end = prev;
+ if (list_cur)
+ {
+ if (list_start == list_cur)
+ list_start = 0;
+ do
+ {
+ LinkedListItem<T>* next = list_cur->next;
+ TFree(list_cur);
+ list_cur = next;
+ }
+ while (list_cur);
+ }
+ }
+ break;
+ }
+ prev = list_cur;
+ list_cur = list_cur->next;
+ }
+ }
+
+ void DoMarker(const char* prevName, u32 arbitraryNumber = 0x42)
+ {
+ u32 cookie = arbitraryNumber;
+ Do(cookie);
+
+ if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber)
+ {
+ PanicAlertT("Error: After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). Aborting savestate load...",
+ prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
+ mode = PointerWrap::MODE_MEASURE;
+ }
+ }
+
+private:
+ __forceinline void DoByte(u8& x)
+ {
+ switch (mode)
+ {
+ case MODE_READ:
+ x = **ptr;
+ break;
+
+ case MODE_WRITE:
+ **ptr = x;
+ break;
+
+ case MODE_MEASURE:
+ break;
+
+ case MODE_VERIFY:
+ _dbg_assert_msg_(COMMON, (x == **ptr),
+ "Savestate verification failure: %d (0x%X) (at %p) != %d (0x%X) (at %p).\n",
+ x, x, &x, **ptr, **ptr, *ptr);
+ break;
+
+ default:
+ break;
+ }
+
+ ++(*ptr);
+ }
+
+ void DoVoid(void *data, u32 size)
+ {
+ for(u32 i = 0; i != size; ++i)
+ DoByte(reinterpret_cast<u8*>(data)[i]);
+ }
+};
+
+class CChunkFileReader
+{
+public:
+ // Load file template
+ template<class T>
+ static bool Load(const std::string& _rFilename, u32 _Revision, T& _class)
+ {
+ INFO_LOG(COMMON, "ChunkReader: Loading %s" , _rFilename.c_str());
+
+ if (!File::Exists(_rFilename))
+ return false;
+
+ // Check file size
+ const u64 fileSize = File::GetSize(_rFilename);
+ static const u64 headerSize = sizeof(SChunkHeader);
+ if (fileSize < headerSize)
+ {
+ ERROR_LOG(COMMON,"ChunkReader: File too small");
+ return false;
+ }
+
+ File::IOFile pFile(_rFilename, "rb");
+ if (!pFile)
+ {
+ ERROR_LOG(COMMON,"ChunkReader: Can't open file for reading");
+ return false;
+ }
+
+ // read the header
+ SChunkHeader header;
+ if (!pFile.ReadArray(&header, 1))
+ {
+ ERROR_LOG(COMMON,"ChunkReader: Bad header size");
+ return false;
+ }
+
+ // Check revision
+ if (header.Revision != _Revision)
+ {
+ ERROR_LOG(COMMON,"ChunkReader: Wrong file revision, got %d expected %d",
+ header.Revision, _Revision);
+ return false;
+ }
+
+ // get size
+ const u32 sz = (u32)(fileSize - headerSize);
+ if (header.ExpectedSize != sz)
+ {
+ ERROR_LOG(COMMON,"ChunkReader: Bad file size, got %d expected %d",
+ sz, header.ExpectedSize);
+ return false;
+ }
+
+ // read the state
+ std::vector<u8> buffer(sz);
+ if (!pFile.ReadArray(&buffer[0], sz))
+ {
+ ERROR_LOG(COMMON,"ChunkReader: Error reading file");
+ return false;
+ }
+
+ u8* ptr = &buffer[0];
+ PointerWrap p(&ptr, PointerWrap::MODE_READ);
+ _class.DoState(p);
+
+ INFO_LOG(COMMON, "ChunkReader: Done loading %s" , _rFilename.c_str());
+ return true;
+ }
+
+ // Save file template
+ template<class T>
+ static bool Save(const std::string& _rFilename, u32 _Revision, T& _class)
+ {
+ INFO_LOG(COMMON, "ChunkReader: Writing %s" , _rFilename.c_str());
+ File::IOFile pFile(_rFilename, "wb");
+ if (!pFile)
+ {
+ ERROR_LOG(COMMON,"ChunkReader: Error opening file for write");
+ return false;
+ }
+
+ // Get data
+ u8 *ptr = 0;
+ PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
+ _class.DoState(p);
+ size_t const sz = (size_t)ptr;
+ std::vector<u8> buffer(sz);
+ ptr = &buffer[0];
+ p.SetMode(PointerWrap::MODE_WRITE);
+ _class.DoState(p);
+
+ // Create header
+ SChunkHeader header;
+ header.Revision = _Revision;
+ header.ExpectedSize = (u32)sz;
+
+ // Write to file
+ if (!pFile.WriteArray(&header, 1))
+ {
+ ERROR_LOG(COMMON,"ChunkReader: Failed writing header");
+ return false;
+ }
+
+ if (!pFile.WriteArray(&buffer[0], sz))
+ {
+ ERROR_LOG(COMMON,"ChunkReader: Failed writing data");
+ return false;
+ }
+
+ INFO_LOG(COMMON,"ChunkReader: Done writing %s", _rFilename.c_str());
+ return true;
+ }
+
+private:
+ struct SChunkHeader
+ {
+ u32 Revision;
+ u32 ExpectedSize;
+ };
+};
+
+#endif // _POINTERWRAP_H_
diff --git a/src/common/src/common.h b/src/common/src/common.h
new file mode 100644
index 00000000..f95d5807
--- /dev/null
+++ b/src/common/src/common.h
@@ -0,0 +1,164 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+// DO NOT EVER INCLUDE <windows.h> directly _or indirectly_ from this file
+// since it slows down the build a lot.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+// SVN version number
+extern const char *scm_rev_str;
+extern const char *netplay_dolphin_ver;
+
+// Force enable logging in the right modes. For some reason, something had changed
+// so that debugfast no longer logged.
+#if defined(_DEBUG) || defined(DEBUGFAST)
+#undef LOGGING
+#define LOGGING 1
+#endif
+
+#define STACKALIGN
+
+#if __cplusplus >= 201103 || defined(_MSC_VER) || defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define HAVE_CXX11_SYNTAX 1
+#endif
+
+#if HAVE_CXX11_SYNTAX
+// An inheritable class to disallow the copy constructor and operator= functions
+class NonCopyable
+{
+protected:
+ NonCopyable() {}
+ NonCopyable(const NonCopyable&&) {}
+ void operator=(const NonCopyable&&) {}
+private:
+ NonCopyable(NonCopyable&);
+ NonCopyable& operator=(NonCopyable& other);
+};
+#endif
+
+#include "log.h"
+#include "common_types.h"
+#include "msg_handler.h"
+#include "common_funcs.h"
+
+#ifdef __APPLE__
+// The Darwin ABI requires that stack frames be aligned to 16-byte boundaries.
+// This is only needed on i386 gcc - x86_64 already aligns to 16 bytes.
+#if defined __i386__ && defined __GNUC__
+#undef STACKALIGN
+#define STACKALIGN __attribute__((__force_align_arg_pointer__))
+#endif
+
+#elif defined _WIN32
+
+// Check MSC ver
+ #if !defined _MSC_VER || _MSC_VER <= 1000
+ #error needs at least version 1000 of MSC
+ #endif
+
+ #define NOMINMAX
+
+// Memory leak checks
+ #define CHECK_HEAP_INTEGRITY()
+
+// Alignment
+ #define GC_ALIGNED16(x) __declspec(align(16)) x
+ #define GC_ALIGNED32(x) __declspec(align(32)) x
+ #define GC_ALIGNED64(x) __declspec(align(64)) x
+ #define GC_ALIGNED128(x) __declspec(align(128)) x
+ #define GC_ALIGNED16_DECL(x) __declspec(align(16)) x
+ #define GC_ALIGNED64_DECL(x) __declspec(align(64)) x
+
+// Since they are always around on windows
+ #define HAVE_WX 1
+ #define HAVE_OPENAL 1
+
+ #define HAVE_PORTAUDIO 1
+
+// Debug definitions
+ #if defined(_DEBUG)
+ #include <crtdbg.h>
+ #undef CHECK_HEAP_INTEGRITY
+ #define CHECK_HEAP_INTEGRITY() {if (!_CrtCheckMemory()) PanicAlert("memory corruption detected. see log.");}
+ // If you want to see how much a pain in the ass singletons are, for example:
+ // {614} normal block at 0x030C5310, 188 bytes long.
+ // Data: <Master Log > 4D 61 73 74 65 72 20 4C 6F 67 00 00 00 00 00 00
+ struct CrtDebugBreak { CrtDebugBreak(int spot) { _CrtSetBreakAlloc(spot); } };
+ //CrtDebugBreak breakAt(614);
+ #endif // end DEBUG/FAST
+
+#endif
+
+// Windows compatibility
+#ifndef _WIN32
+#include <limits.h>
+#define MAX_PATH PATH_MAX
+#ifdef _LP64
+#define _M_X64 1
+#else
+#define _M_IX86 1
+#endif
+#define __forceinline inline __attribute__((always_inline))
+#define GC_ALIGNED16(x) __attribute__((aligned(16))) x
+#define GC_ALIGNED32(x) __attribute__((aligned(32))) x
+#define GC_ALIGNED64(x) __attribute__((aligned(64))) x
+#define GC_ALIGNED128(x) __attribute__((aligned(128))) x
+#define GC_ALIGNED16_DECL(x) __attribute__((aligned(16))) x
+#define GC_ALIGNED64_DECL(x) __attribute__((aligned(64))) x
+#endif
+
+#ifdef _MSC_VER
+#define __strdup _strdup
+#define __getcwd _getcwd
+#define __chdir _chdir
+#else
+#define __strdup strdup
+#define __getcwd getcwd
+#define __chdir chdir
+#endif
+
+// Dummy macro for marking translatable strings that can not be immediately translated.
+// wxWidgets does not have a true dummy macro for this.
+#define _trans(a) a
+
+#if defined _M_GENERIC
+# define _M_SSE 0x0
+#elif defined __GNUC__
+# if defined __SSE4_2__
+# define _M_SSE 0x402
+# elif defined __SSE4_1__
+# define _M_SSE 0x401
+# elif defined __SSSE3__
+# define _M_SSE 0x301
+# elif defined __SSE3__
+# define _M_SSE 0x300
+# endif
+#elif (_MSC_VER >= 1500) || __INTEL_COMPILER // Visual Studio 2008
+# define _M_SSE 0x402
+#endif
+
+// Host communication.
+enum HOST_COMM
+{
+ // Begin at 10 in case there is already messages with wParam = 0, 1, 2 and so on
+ WM_USER_STOP = 10,
+ WM_USER_CREATE,
+ WM_USER_SETCURSOR,
+};
+
+// Used for notification on emulation state
+enum EMUSTATE_CHANGE
+{
+ EMUSTATE_CHANGE_PLAY = 1,
+ EMUSTATE_CHANGE_PAUSE,
+ EMUSTATE_CHANGE_STOP
+};
+
+#endif // _COMMON_H_
diff --git a/src/common/src/common_funcs.h b/src/common/src/common_funcs.h
new file mode 100644
index 00000000..73320a3a
--- /dev/null
+++ b/src/common/src/common_funcs.h
@@ -0,0 +1,243 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _COMMONFUNCS_H_
+#define _COMMONFUNCS_H_
+
+#ifdef _WIN32
+#define SLEEP(x) Sleep(x)
+#else
+#include <unistd.h>
+#define SLEEP(x) usleep(x*1000)
+#endif
+
+template <bool> struct CompileTimeAssert;
+template<> struct CompileTimeAssert<true> {};
+
+#define b2(x) ( (x) | ( (x) >> 1) )
+#define b4(x) ( b2(x) | ( b2(x) >> 2) )
+#define b8(x) ( b4(x) | ( b4(x) >> 4) )
+#define b16(x) ( b8(x) | ( b8(x) >> 8) )
+#define b32(x) (b16(x) | (b16(x) >>16) )
+#define ROUND_UP_POW2(x) (b32(x - 1) + 1)
+
+#if defined __GNUC__ && !defined __SSSE3__ && !defined _M_GENERIC
+#include <emmintrin.h>
+static __inline __m128i __attribute__((__always_inline__))
+_mm_shuffle_epi8(__m128i a, __m128i mask)
+{
+ __m128i result;
+ __asm__("pshufb %1, %0"
+ : "=x" (result)
+ : "xm" (mask), "0" (a));
+ return result;
+}
+#endif
+
+#ifndef _WIN32
+
+#include <errno.h>
+#ifdef __linux__
+#include <byteswap.h>
+#elif defined __FreeBSD__
+#include <sys/endian.h>
+#endif
+
+// go to debugger mode
+ #ifdef GEKKO
+ #define Crash()
+ #elif defined _M_GENERIC
+ #define Crash() { exit(1); }
+ #else
+ #define Crash() {asm ("int $3");}
+ #endif
+ #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+// GCC 4.8 defines all the rotate functions now
+// Small issue with GCC's lrotl/lrotr intrinsics is they are still 32bit while we require 64bit
+#ifndef _rotl
+inline u32 _rotl(u32 x, int shift) {
+ shift &= 31;
+ if (!shift) return x;
+ return (x << shift) | (x >> (32 - shift));
+}
+
+inline u32 _rotr(u32 x, int shift) {
+ shift &= 31;
+ if (!shift) return x;
+ return (x >> shift) | (x << (32 - shift));
+}
+#endif
+
+inline u64 _rotl64(u64 x, unsigned int shift){
+ unsigned int n = shift % 64;
+ return (x << n) | (x >> (64 - n));
+}
+
+inline u64 _rotr64(u64 x, unsigned int shift){
+ unsigned int n = shift % 64;
+ return (x >> n) | (x << (64 - n));
+}
+
+#else // WIN32
+// Function Cross-Compatibility
+ #define strcasecmp _stricmp
+ #define strncasecmp _strnicmp
+ #define unlink _unlink
+ #define snprintf _snprintf
+ #define vscprintf _vscprintf
+
+// Locale Cross-Compatibility
+ #define locale_t _locale_t
+ #define freelocale _free_locale
+ #define newlocale(mask, locale, base) _create_locale(mask, locale)
+
+ #define LC_GLOBAL_LOCALE ((locale_t)-1)
+ #define LC_ALL_MASK LC_ALL
+ #define LC_COLLATE_MASK LC_COLLATE
+ #define LC_CTYPE_MASK LC_CTYPE
+ #define LC_MONETARY_MASK LC_MONETARY
+ #define LC_NUMERIC_MASK LC_NUMERIC
+ #define LC_TIME_MASK LC_TIME
+
+ inline locale_t uselocale(locale_t new_locale)
+ {
+ // Retrieve the current per thread locale setting
+ bool bIsPerThread = (_configthreadlocale(0) == _ENABLE_PER_THREAD_LOCALE);
+
+ // Retrieve the current thread-specific locale
+ locale_t old_locale = bIsPerThread ? _get_current_locale() : LC_GLOBAL_LOCALE;
+
+ if(new_locale == LC_GLOBAL_LOCALE)
+ {
+ // Restore the global locale
+ _configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
+ }
+ else if(new_locale != NULL)
+ {
+ // Configure the thread to set the locale only for this thread
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+
+ // Set all locale categories
+ for(int i = LC_MIN; i <= LC_MAX; i++)
+ setlocale(i, new_locale->locinfo->lc_category[i].locale);
+ }
+
+ return old_locale;
+ }
+
+// 64 bit offsets for windows
+ #define fseeko _fseeki64
+ #define ftello _ftelli64
+ #define atoll _atoi64
+ #define stat64 _stat64
+ #define fstat64 _fstat64
+ #define fileno _fileno
+
+ #if _M_IX86
+ #define Crash() {__asm int 3}
+ #else
+extern "C" {
+ __declspec(dllimport) void __stdcall DebugBreak(void);
+}
+ #define Crash() {DebugBreak();}
+ #endif // M_IX86
+#endif // WIN32 ndef
+
+// Dolphin's min and max functions
+#undef min
+#undef max
+
+template<class T>
+inline T min(const T& a, const T& b) {return a > b ? b : a;}
+template<class T>
+inline T max(const T& a, const T& b) {return a > b ? a : b;}
+
+// Generic function to get last error message.
+// Call directly after the command or use the error num.
+// This function might change the error code.
+// Defined in Misc.cpp.
+const char* GetLastErrorMsg();
+
+namespace Common
+{
+inline u8 swap8(u8 _data) {return _data;}
+inline u32 swap24(const u8* _data) {return (_data[0] << 16) | (_data[1] << 8) | _data[2];}
+
+#ifdef ANDROID
+#undef swap16
+#undef swap32
+#undef swap64
+#endif
+
+#ifdef _WIN32
+inline u16 swap16(u16 _data) {return _byteswap_ushort(_data);}
+inline u32 swap32(u32 _data) {return _byteswap_ulong (_data);}
+inline u64 swap64(u64 _data) {return _byteswap_uint64(_data);}
+#elif _M_ARM
+inline u16 swap16 (u16 _data) { u32 data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (u16)data;}
+inline u32 swap32 (u32 _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;}
+inline u64 swap64(u64 _data) {return ((u64)swap32(_data) << 32) | swap32(_data >> 32);}
+#elif __linux__
+inline u16 swap16(u16 _data) {return bswap_16(_data);}
+inline u32 swap32(u32 _data) {return bswap_32(_data);}
+inline u64 swap64(u64 _data) {return bswap_64(_data);}
+#elif __APPLE__
+inline __attribute__((always_inline)) u16 swap16(u16 _data)
+ {return (_data >> 8) | (_data << 8);}
+inline __attribute__((always_inline)) u32 swap32(u32 _data)
+ {return __builtin_bswap32(_data);}
+inline __attribute__((always_inline)) u64 swap64(u64 _data)
+ {return __builtin_bswap64(_data);}
+#elif __FreeBSD__
+inline u16 swap16(u16 _data) {return bswap16(_data);}
+inline u32 swap32(u32 _data) {return bswap32(_data);}
+inline u64 swap64(u64 _data) {return bswap64(_data);}
+#else
+// Slow generic implementation.
+inline u16 swap16(u16 data) {return (data >> 8) | (data << 8);}
+inline u32 swap32(u32 data) {return (swap16(data) << 16) | swap16(data >> 16);}
+inline u64 swap64(u64 data) {return ((u64)swap32(data) << 32) | swap32(data >> 32);}
+#endif
+
+inline u16 swap16(const u8* _pData) {return swap16(*(const u16*)_pData);}
+inline u32 swap32(const u8* _pData) {return swap32(*(const u32*)_pData);}
+inline u64 swap64(const u8* _pData) {return swap64(*(const u64*)_pData);}
+
+template <int count>
+void swap(u8*);
+
+template <>
+inline void swap<1>(u8* data)
+{}
+
+template <>
+inline void swap<2>(u8* data)
+{
+ *reinterpret_cast<u16*>(data) = swap16(data);
+}
+
+template <>
+inline void swap<4>(u8* data)
+{
+ *reinterpret_cast<u32*>(data) = swap32(data);
+}
+
+template <>
+inline void swap<8>(u8* data)
+{
+ *reinterpret_cast<u64*>(data) = swap64(data);
+}
+
+template <typename T>
+inline T FromBigEndian(T data)
+{
+ //static_assert(std::is_arithmetic<T>::value, "function only makes sense with arithmetic types");
+
+ swap<sizeof(data)>(reinterpret_cast<u8*>(&data));
+ return data;
+}
+
+} // Namespace Common
+
+#endif // _COMMONFUNCS_H_
diff --git a/src/common/src/common_paths.h b/src/common/src/common_paths.h
new file mode 100644
index 00000000..9af93ab7
--- /dev/null
+++ b/src/common/src/common_paths.h
@@ -0,0 +1,71 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _COMMON_PATHS_H_
+#define _COMMON_PATHS_H_
+
+// Make sure we pick up USER_DIR if set in config.h
+#include "common.h"
+
+// Directory seperators, do we need this?
+#define DIR_SEP "/"
+#define DIR_SEP_CHR '/'
+
+// The user data dir
+#define ROOT_DIR "."
+#ifdef _WIN32
+ #define USERDATA_DIR "user"
+ #define DOLPHIN_DATA_DIR "akiru"
+#else
+ #define USERDATA_DIR "user"
+ #ifdef USER_DIR
+ #define DOLPHIN_DATA_DIR USER_DIR
+ #else
+ #define DOLPHIN_DATA_DIR ".akiru"
+ #endif
+#endif
+
+// Shared data dirs (Sys and shared User for linux)
+#ifdef _WIN32
+ #define SYSDATA_DIR "sys"
+#else
+ #ifdef DATA_DIR
+ #define SYSDATA_DIR DATA_DIR "sys"
+ #define SHARED_USER_DIR DATA_DIR USERDATA_DIR DIR_SEP
+ #else
+ #define SYSDATA_DIR "sys"
+ #define SHARED_USER_DIR ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP
+ #endif
+#endif
+
+// Dirs in both User and Sys
+#define EUR_DIR "EUR"
+#define USA_DIR "USA"
+#define JAP_DIR "JAP"
+
+// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
+#define CONFIG_DIR "config"
+#define GAMECONFIG_DIR "game_config"
+#define MAPS_DIR "maps"
+#define CACHE_DIR "cache"
+#define SHADERCACHE_DIR "shader_cache"
+#define STATESAVES_DIR "state_saves"
+#define SCREENSHOTS_DIR "screenShots"
+#define DUMP_DIR "dump"
+#define DUMP_TEXTURES_DIR "textures"
+#define DUMP_FRAMES_DIR "frames"
+#define DUMP_AUDIO_DIR "audio"
+#define LOGS_DIR "logs"
+#define SHADERS_DIR "shaders"
+
+// Filenames
+// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
+#define AKIRU_CONFIG "akiru.ini"
+#define DEBUGGER_CONFIG "debugger.ini"
+#define LOGGER_CONFIG "logger.ini"
+
+// Files in the directory returned by GetUserPath(D_LOGS_IDX)
+#define MAIN_LOG "akiru.log"
+
+#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 00000000..5cb40213
--- /dev/null
+++ b/src/common/src/common_types.h
@@ -0,0 +1,48 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+// This header contains type definitions that are shared between the Dolphin core and
+// other parts of the code. Any definitions that are only used by the core should be
+// placed in "common.h" instead.
+
+#ifndef _COMMONTYPES_H_
+#define _COMMONTYPES_H_
+
+#ifdef _WIN32
+
+#include <tchar.h>
+
+typedef unsigned __int8 u8;
+typedef unsigned __int16 u16;
+typedef unsigned __int32 u32;
+typedef unsigned __int64 u64;
+
+typedef signed __int8 s8;
+typedef signed __int16 s16;
+typedef signed __int32 s32;
+typedef signed __int64 s64;
+
+#else
+
+#ifndef GEKKO
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+
+typedef signed char s8;
+typedef signed short s16;
+typedef signed int s32;
+typedef signed long long s64;
+
+#endif
+// For using windows lock code
+#define TCHAR char
+#define LONG int
+
+#endif // _WIN32
+
+#endif // _COMMONTYPES_H_
diff --git a/src/common/src/console_listener.cpp b/src/common/src/console_listener.cpp
new file mode 100644
index 00000000..84f57675
--- /dev/null
+++ b/src/common/src/console_listener.cpp
@@ -0,0 +1,337 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <algorithm> // min
+#include <string> // System: To be able to add strings with "+"
+#include <stdio.h>
+#include <math.h>
+#ifdef _WIN32
+#include <windows.h>
+#include <array>
+#else
+#include <stdarg.h>
+#endif
+
+#include "common.h"
+#include "log_manager.h" // Common
+#include "console_listener.h" // Common
+
+ConsoleListener::ConsoleListener()
+{
+#ifdef _WIN32
+ hConsole = NULL;
+ bUseColor = true;
+#else
+ bUseColor = isatty(fileno(stdout));
+#endif
+}
+
+ConsoleListener::~ConsoleListener()
+{
+ Close();
+}
+
+// 100, 100, "Dolphin Log Console"
+// Open console window - width and height is the size of console window
+// Name is the window title
+void ConsoleListener::Open(bool Hidden, int Width, int Height, const char *Title)
+{
+#ifdef _WIN32
+ if (!GetConsoleWindow())
+ {
+ // Open the console window and create the window handle for GetStdHandle()
+ AllocConsole();
+ // Hide
+ if (Hidden) ShowWindow(GetConsoleWindow(), SW_HIDE);
+ // Save the window handle that AllocConsole() created
+ hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+ // Set the console window title
+ SetConsoleTitle(UTF8ToTStr(Title).c_str());
+ // Set letter space
+ LetterSpace(80, 4000);
+ //MoveWindow(GetConsoleWindow(), 200,200, 800,800, true);
+ }
+ else
+ {
+ hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+ }
+#endif
+}
+
+void ConsoleListener::UpdateHandle()
+{
+#ifdef _WIN32
+ hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+#endif
+}
+
+// Close the console window and close the eventual file handle
+void ConsoleListener::Close()
+{
+#ifdef _WIN32
+ if (hConsole == NULL)
+ return;
+ FreeConsole();
+ hConsole = NULL;
+#else
+ fflush(NULL);
+#endif
+}
+
+bool ConsoleListener::IsOpen()
+{
+#ifdef _WIN32
+ return (hConsole != NULL);
+#else
+ return true;
+#endif
+}
+
+/*
+ LetterSpace: SetConsoleScreenBufferSize and SetConsoleWindowInfo are
+ dependent on each other, that's the reason for the additional checks.
+*/
+void ConsoleListener::BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst)
+{
+#ifdef _WIN32
+ BOOL SB, SW;
+ if (BufferFirst)
+ {
+ // Change screen buffer size
+ COORD Co = {BufferWidth, BufferHeight};
+ SB = SetConsoleScreenBufferSize(hConsole, Co);
+ // Change the screen buffer window size
+ SMALL_RECT coo = {0,0,ScreenWidth, ScreenHeight}; // top, left, right, bottom
+ SW = SetConsoleWindowInfo(hConsole, TRUE, &coo);
+ }
+ else
+ {
+ // Change the screen buffer window size
+ SMALL_RECT coo = {0,0, ScreenWidth, ScreenHeight}; // top, left, right, bottom
+ SW = SetConsoleWindowInfo(hConsole, TRUE, &coo);
+ // Change screen buffer size
+ COORD Co = {BufferWidth, BufferHeight};
+ SB = SetConsoleScreenBufferSize(hConsole, Co);
+ }
+#endif
+}
+void ConsoleListener::LetterSpace(int Width, int Height)
+{
+#ifdef _WIN32
+ // Get console info
+ CONSOLE_SCREEN_BUFFER_INFO ConInfo;
+ GetConsoleScreenBufferInfo(hConsole, &ConInfo);
+
+ //
+ int OldBufferWidth = ConInfo.dwSize.X;
+ int OldBufferHeight = ConInfo.dwSize.Y;
+ int OldScreenWidth = (ConInfo.srWindow.Right - ConInfo.srWindow.Left);
+ int OldScreenHeight = (ConInfo.srWindow.Bottom - ConInfo.srWindow.Top);
+ //
+ int NewBufferWidth = Width;
+ int NewBufferHeight = Height;
+ int NewScreenWidth = NewBufferWidth - 1;
+ int NewScreenHeight = OldScreenHeight;
+
+ // Width
+ BufferWidthHeight(NewBufferWidth, OldBufferHeight, NewScreenWidth, OldScreenHeight, (NewBufferWidth > OldScreenWidth-1));
+ // Height
+ BufferWidthHeight(NewBufferWidth, NewBufferHeight, NewScreenWidth, NewScreenHeight, (NewBufferHeight > OldScreenHeight-1));
+
+ // Resize the window too
+ //MoveWindow(GetConsoleWindow(), 200,200, (Width*8 + 50),(NewScreenHeight*12 + 200), true);
+#endif
+}
+#ifdef _WIN32
+COORD ConsoleListener::GetCoordinates(int BytesRead, int BufferWidth)
+{
+ COORD Ret = {0, 0};
+ // Full rows
+ int Step = (int)floor((float)BytesRead / (float)BufferWidth);
+ Ret.Y += Step;
+ // Partial row
+ Ret.X = BytesRead - (BufferWidth * Step);
+ return Ret;
+}
+#endif
+void ConsoleListener::PixelSpace(int Left, int Top, int Width, int Height, bool Resize)
+{
+#ifdef _WIN32
+ // Check size
+ if (Width < 8 || Height < 12) return;
+
+ bool DBef = true;
+ bool DAft = true;
+ std::string SLog = "";
+
+ const HWND hWnd = GetConsoleWindow();
+ const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ // Get console info
+ CONSOLE_SCREEN_BUFFER_INFO ConInfo;
+ GetConsoleScreenBufferInfo(hConsole, &ConInfo);
+ DWORD BufferSize = ConInfo.dwSize.X * ConInfo.dwSize.Y;
+
+ // ---------------------------------------------------------------------
+ // Save the current text
+ // ------------------------
+ DWORD cCharsRead = 0;
+ COORD coordScreen = { 0, 0 };
+
+ static const int MAX_BYTES = 1024 * 16;
+
+ std::vector<std::array<TCHAR, MAX_BYTES>> Str;
+ std::vector<std::array<WORD, MAX_BYTES>> Attr;
+
+ // ReadConsoleOutputAttribute seems to have a limit at this level
+ static const int ReadBufferSize = MAX_BYTES - 32;
+
+ DWORD cAttrRead = ReadBufferSize;
+ DWORD BytesRead = 0;
+ while (BytesRead < BufferSize)
+ {
+ Str.resize(Str.size() + 1);
+ if (!ReadConsoleOutputCharacter(hConsole, Str.back().data(), ReadBufferSize, coordScreen, &cCharsRead))
+ SLog += StringFromFormat("WriteConsoleOutputCharacter error");
+
+ Attr.resize(Attr.size() + 1);
+ if (!ReadConsoleOutputAttribute(hConsole, Attr.back().data(), ReadBufferSize, coordScreen, &cAttrRead))
+ SLog += StringFromFormat("WriteConsoleOutputAttribute error");
+
+ // Break on error
+ if (cAttrRead == 0) break;
+ BytesRead += cAttrRead;
+ coordScreen = GetCoordinates(BytesRead, ConInfo.dwSize.X);
+ }
+ // Letter space
+ int LWidth = (int)(floor((float)Width / 8.0f) - 1.0f);
+ int LHeight = (int)(floor((float)Height / 12.0f) - 1.0f);
+ int LBufWidth = LWidth + 1;
+ int LBufHeight = (int)floor((float)BufferSize / (float)LBufWidth);
+ // Change screen buffer size
+ LetterSpace(LBufWidth, LBufHeight);
+
+
+ ClearScreen(true);
+ coordScreen.Y = 0;
+ coordScreen.X = 0;
+ DWORD cCharsWritten = 0;
+
+ int BytesWritten = 0;
+ DWORD cAttrWritten = 0;
+ for (size_t i = 0; i < Attr.size(); i++)
+ {
+ if (!WriteConsoleOutputCharacter(hConsole, Str[i].data(), ReadBufferSize, coordScreen, &cCharsWritten))
+ SLog += StringFromFormat("WriteConsoleOutputCharacter error");
+ if (!WriteConsoleOutputAttribute(hConsole, Attr[i].data(), ReadBufferSize, coordScreen, &cAttrWritten))
+ SLog += StringFromFormat("WriteConsoleOutputAttribute error");
+
+ BytesWritten += cAttrWritten;
+ coordScreen = GetCoordinates(BytesWritten, LBufWidth);
+ }
+
+ const int OldCursor = ConInfo.dwCursorPosition.Y * ConInfo.dwSize.X + ConInfo.dwCursorPosition.X;
+ COORD Coo = GetCoordinates(OldCursor, LBufWidth);
+ SetConsoleCursorPosition(hConsole, Coo);
+
+ if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str());
+
+ // Resize the window too
+ if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true);
+#endif
+}
+
+void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
+{
+#if defined(_WIN32)
+ /*
+ const int MAX_BYTES = 1024*10;
+ char Str[MAX_BYTES];
+ va_list ArgPtr;
+ int Cnt;
+ va_start(ArgPtr, Text);
+ Cnt = vsnprintf(Str, MAX_BYTES, Text, ArgPtr);
+ va_end(ArgPtr);
+ */
+ DWORD cCharsWritten;
+ WORD Color;
+
+ switch (Level)
+ {
+ case NOTICE_LEVEL: // light green
+ Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+ break;
+ case ERROR_LEVEL: // light red
+ Color = FOREGROUND_RED | FOREGROUND_INTENSITY;
+ break;
+ case WARNING_LEVEL: // light yellow
+ Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+ break;
+ case INFO_LEVEL: // cyan
+ Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
+ break;
+ case DEBUG_LEVEL: // gray
+ Color = FOREGROUND_INTENSITY;
+ break;
+ default: // off-white
+ Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+ break;
+ }
+ if (strlen(Text) > 10)
+ {
+ // First 10 chars white
+ SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+ WriteConsole(hConsole, Text, 10, &cCharsWritten, NULL);
+ Text += 10;
+ }
+ SetConsoleTextAttribute(hConsole, Color);
+ WriteConsole(hConsole, Text, (DWORD)strlen(Text), &cCharsWritten, NULL);
+#else
+ char ColorAttr[16] = "";
+ char ResetAttr[16] = "";
+
+ if (bUseColor)
+ {
+ strcpy(ResetAttr, "\033[0m");
+ switch (Level)
+ {
+ case NOTICE_LEVEL: // light green
+ strcpy(ColorAttr, "\033[92m");
+ break;
+ case ERROR_LEVEL: // light red
+ strcpy(ColorAttr, "\033[91m");
+ break;
+ case WARNING_LEVEL: // light yellow
+ strcpy(ColorAttr, "\033[93m");
+ break;
+ default:
+ break;
+ }
+ }
+ fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr);
+#endif
+}
+// Clear console screen
+void ConsoleListener::ClearScreen(bool Cursor)
+{
+#if defined(_WIN32)
+ COORD coordScreen = { 0, 0 };
+ DWORD cCharsWritten;
+ CONSOLE_SCREEN_BUFFER_INFO csbi;
+ DWORD dwConSize;
+
+ HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ GetConsoleScreenBufferInfo(hConsole, &csbi);
+ dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
+ // Write space to the entire console
+ FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
+ GetConsoleScreenBufferInfo(hConsole, &csbi);
+ FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
+ // Reset cursor
+ if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen);
+#endif
+}
+
+
diff --git a/src/common/src/console_listener.h b/src/common/src/console_listener.h
new file mode 100644
index 00000000..ab5c0098
--- /dev/null
+++ b/src/common/src/console_listener.h
@@ -0,0 +1,41 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _CONSOLELISTENER_H
+#define _CONSOLELISTENER_H
+
+#include "log_manager.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+class ConsoleListener : public LogListener
+{
+public:
+ ConsoleListener();
+ ~ConsoleListener();
+
+ void Open(bool Hidden = false, int Width = 100, int Height = 100, const char * Name = "Console");
+ void UpdateHandle();
+ void Close();
+ bool IsOpen();
+ void LetterSpace(int Width, int Height);
+ void BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst);
+ void PixelSpace(int Left, int Top, int Width, int Height, bool);
+#ifdef _WIN32
+ COORD GetCoordinates(int BytesRead, int BufferWidth);
+#endif
+ void Log(LogTypes::LOG_LEVELS, const char *Text);
+ void ClearScreen(bool Cursor = true);
+
+private:
+#ifdef _WIN32
+ HWND GetHwnd(void);
+ HANDLE hConsole;
+#endif
+ bool bUseColor;
+};
+
+#endif // _CONSOLELISTENER_H
diff --git a/src/common/src/cpu_detect.h b/src/common/src/cpu_detect.h
new file mode 100644
index 00000000..be6ce349
--- /dev/null
+++ b/src/common/src/cpu_detect.h
@@ -0,0 +1,81 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+// Detect the cpu, so we'll know which optimizations to use
+#ifndef _CPUDETECT_H_
+#define _CPUDETECT_H_
+
+#include <string>
+
+enum CPUVendor
+{
+ VENDOR_INTEL = 0,
+ VENDOR_AMD = 1,
+ VENDOR_ARM = 2,
+ VENDOR_OTHER = 3,
+};
+
+struct CPUInfo
+{
+ CPUVendor vendor;
+
+ char cpu_string[0x21];
+ char brand_string[0x41];
+ bool OS64bit;
+ bool CPU64bit;
+ bool Mode64bit;
+
+ bool HTT;
+ int num_cores;
+ int logical_cpu_count;
+
+ bool bSSE;
+ bool bSSE2;
+ bool bSSE3;
+ bool bSSSE3;
+ bool bPOPCNT;
+ bool bSSE4_1;
+ bool bSSE4_2;
+ bool bLZCNT;
+ bool bSSE4A;
+ bool bAVX;
+ bool bAES;
+ bool bLAHFSAHF64;
+ bool bLongMode;
+
+ // ARM specific CPUInfo
+ bool bSwp;
+ bool bHalf;
+ bool bThumb;
+ bool bFastMult;
+ bool bVFP;
+ bool bEDSP;
+ bool bThumbEE;
+ bool bNEON;
+ bool bVFPv3;
+ bool bTLS;
+ bool bVFPv4;
+ bool bIDIVa;
+ bool bIDIVt;
+ bool bArmV7; // enable MOVT, MOVW etc
+
+ // ARMv8 specific
+ bool bFP;
+ bool bASIMD;
+
+ // Call Detect()
+ explicit CPUInfo();
+
+ // Turn the cpu info into a string we can show
+ std::string Summarize();
+
+private:
+ // Detects the various cpu features
+ void Detect();
+};
+
+extern CPUInfo cpu_info;
+
+#endif // _CPUDETECT_H_
diff --git a/src/common/src/debug_interface.h b/src/common/src/debug_interface.h
new file mode 100644
index 00000000..317cd0bb
--- /dev/null
+++ b/src/common/src/debug_interface.h
@@ -0,0 +1,39 @@
+#ifndef _DEBUGINTERFACE_H
+#define _DEBUGINTERFACE_H
+
+#include <string>
+#include <string.h>
+
+class DebugInterface
+{
+protected:
+ virtual ~DebugInterface() {}
+
+public:
+ virtual void disasm(unsigned int /*address*/, char *dest, int /*max_size*/) {strcpy(dest, "NODEBUGGER");}
+ virtual void getRawMemoryString(int /*memory*/, unsigned int /*address*/, char *dest, int /*max_size*/) {strcpy(dest, "NODEBUGGER");}
+ virtual int getInstructionSize(int /*instruction*/) {return 1;}
+ virtual bool isAlive() {return true;}
+ virtual bool isBreakpoint(unsigned int /*address*/) {return false;}
+ virtual void setBreakpoint(unsigned int /*address*/){}
+ virtual void clearBreakpoint(unsigned int /*address*/){}
+ virtual void clearAllBreakpoints() {}
+ virtual void toggleBreakpoint(unsigned int /*address*/){}
+ virtual bool isMemCheck(unsigned int /*address*/) {return false;}
+ virtual void toggleMemCheck(unsigned int /*address*/){}
+ virtual unsigned int readMemory(unsigned int /*address*/){return 0;}
+ virtual void writeExtraMemory(int /*memory*/, unsigned int /*value*/, unsigned int /*address*/) {}
+ virtual unsigned int readExtraMemory(int /*memory*/, unsigned int /*address*/){return 0;}
+ virtual unsigned int readInstruction(unsigned int /*address*/){return 0;}
+ virtual unsigned int getPC() {return 0;}
+ virtual void setPC(unsigned int /*address*/) {}
+ virtual void step() {}
+ virtual void runToBreakpoint() {}
+ virtual void breakNow() {}
+ virtual void insertBLR(unsigned int /*address*/, unsigned int /*value*/) {}
+ virtual void showJitResults(unsigned int /*address*/) {};
+ virtual int getColor(unsigned int /*address*/){return 0xFFFFFFFF;}
+ virtual std::string getDescription(unsigned int /*address*/) = 0;
+};
+
+#endif
diff --git a/src/common/src/extended_trace.cpp b/src/common/src/extended_trace.cpp
new file mode 100644
index 00000000..44815343
--- /dev/null
+++ b/src/common/src/extended_trace.cpp
@@ -0,0 +1,433 @@
+// --------------------------------------------------------------------------------------
+//
+// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com
+// For companies(Austin,TX): If you would like to get my resume, send an email.
+//
+// The source is free, but if you want to use it, mention my name and e-mail address
+//
+// History:
+// 1.0 Initial version Zoltan Csizmadia
+// 1.1 WhineCube version Masken
+// 1.2 Dolphin version Masken
+//
+// --------------------------------------------------------------------------------------
+
+#if defined(WIN32)
+
+#include <windows.h>
+#include <stdio.h>
+#include "extended_trace.h"
+#include "string_util.h"
+using namespace std;
+
+#include <tchar.h>
+#include <ImageHlp.h>
+
+#define BUFFERSIZE 0x200
+#pragma warning(disable:4996)
+
+// Unicode safe char* -> TCHAR* conversion
+void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut )
+{
+#if defined(UNICODE)||defined(_UNICODE)
+ ULONG index = 0;
+ PCSTR lpAct = lpszIn;
+
+ for( ; ; lpAct++ )
+ {
+ lpszOut[index++] = (TCHAR)(*lpAct);
+ if ( *lpAct == 0 )
+ break;
+ }
+#else
+ // This is trivial :)
+ strcpy( lpszOut, lpszIn );
+#endif
+}
+
+// Let's figure out the path for the symbol files
+// Search path= ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + lpszIniPath
+// Note: There is no size check for lpszSymbolPath!
+static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath )
+{
+ CHAR lpszPath[BUFFERSIZE];
+
+ // Creating the default path
+ // ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;"
+ strcpy( lpszSymbolPath, "." );
+
+ // environment variable _NT_SYMBOL_PATH
+ if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE ) )
+ {
+ strcat( lpszSymbolPath, ";" );
+ strcat( lpszSymbolPath, lpszPath );
+ }
+
+ // environment variable _NT_ALTERNATE_SYMBOL_PATH
+ if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", lpszPath, BUFFERSIZE ) )
+ {
+ strcat( lpszSymbolPath, ";" );
+ strcat( lpszSymbolPath, lpszPath );
+ }
+
+ // environment variable SYSTEMROOT
+ if ( GetEnvironmentVariableA( "SYSTEMROOT", lpszPath, BUFFERSIZE ) )
+ {
+ strcat( lpszSymbolPath, ";" );
+ strcat( lpszSymbolPath, lpszPath );
+ strcat( lpszSymbolPath, ";" );
+
+ // SYSTEMROOT\System32
+ strcat( lpszSymbolPath, lpszPath );
+ strcat( lpszSymbolPath, "\\System32" );
+ }
+
+ // Add user defined path
+ if ( lpszIniPath != NULL )
+ if ( lpszIniPath[0] != '\0' )
+ {
+ strcat( lpszSymbolPath, ";" );
+ strcat( lpszSymbolPath, lpszIniPath );
+ }
+}
+
+// Uninitialize the loaded symbol files
+BOOL UninitSymInfo() {
+ return SymCleanup( GetCurrentProcess() );
+}
+
+// Initializes the symbol files
+BOOL InitSymInfo( PCSTR lpszInitialSymbolPath )
+{
+ CHAR lpszSymbolPath[BUFFERSIZE];
+ DWORD symOptions = SymGetOptions();
+
+ symOptions |= SYMOPT_LOAD_LINES;
+ symOptions &= ~SYMOPT_UNDNAME;
+ SymSetOptions( symOptions );
+ InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath );
+
+ return SymInitialize( GetCurrentProcess(), lpszSymbolPath, TRUE);
+}
+
+// Get the module name from a given address
+static BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule )
+{
+ BOOL ret = FALSE;
+ IMAGEHLP_MODULE moduleInfo;
+
+ ::ZeroMemory( &moduleInfo, sizeof(moduleInfo) );
+ moduleInfo.SizeOfStruct = sizeof(moduleInfo);
+
+ if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) )
+ {
+ // Got it!
+ PCSTR2LPTSTR( moduleInfo.ModuleName, lpszModule );
+ ret = TRUE;
+ }
+ else
+ // Not found :(
+ _tcscpy( lpszModule, _T("?") );
+
+ return ret;
+}
+
+// Get function prototype and parameter info from ip address and stack address
+static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol )
+{
+ BOOL ret = FALSE;
+ DWORD dwSymSize = 10000;
+ TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?");
+ CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?";
+ LPTSTR lpszParamSep = NULL;
+ LPTSTR lpszParsed = lpszUnDSymbol;
+ PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize );
+
+ ::ZeroMemory( pSym, dwSymSize );
+ pSym->SizeOfStruct = dwSymSize;
+ pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL);
+
+ // Set the default to unknown
+ _tcscpy( lpszSymbol, _T("?") );
+
+ // Get symbol info for IP
+#ifndef _M_X64
+ DWORD dwDisp = 0;
+ if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) )
+#else
+ //makes it compile but hell im not sure if this works...
+ DWORD64 dwDisp = 0;
+ if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) )
+#endif
+ {
+ // Make the symbol readable for humans
+ UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE,
+ UNDNAME_COMPLETE |
+ UNDNAME_NO_THISTYPE |
+ UNDNAME_NO_SPECIAL_SYMS |
+ UNDNAME_NO_MEMBER_TYPE |
+ UNDNAME_NO_MS_KEYWORDS |
+ UNDNAME_NO_ACCESS_SPECIFIERS );
+
+ // Symbol information is ANSI string
+ PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol );
+
+ // I am just smarter than the symbol file :)
+ if ( _tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0 )
+ _tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)"));
+ else
+ if ( _tcscmp(lpszUnDSymbol, _T("_main")) == 0 )
+ _tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)"));
+ else
+ if ( _tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0 )
+ _tcscpy(lpszUnDSymbol, _T("mainCRTStartup()"));
+ else
+ if ( _tcscmp(lpszUnDSymbol, _T("_wmain")) == 0 )
+ _tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)"));
+ else
+ if ( _tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0 )
+ _tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()"));
+
+ lpszSymbol[0] = _T('\0');
+
+ // Let's go through the stack, and modify the function prototype, and insert the actual
+ // parameter values from the stack
+ if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL)
+ {
+ ULONG index = 0;
+ for( ; ; index++ )
+ {
+ lpszParamSep = _tcschr( lpszParsed, _T(',') );
+ if ( lpszParamSep == NULL )
+ break;
+
+ *lpszParamSep = _T('\0');
+
+ _tcscat( lpszSymbol, lpszParsed );
+ _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) );
+
+ lpszParsed = lpszParamSep + 1;
+ }
+
+ lpszParamSep = _tcschr( lpszParsed, _T(')') );
+ if ( lpszParamSep != NULL )
+ {
+ *lpszParamSep = _T('\0');
+
+ _tcscat( lpszSymbol, lpszParsed );
+ _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) );
+
+ lpszParsed = lpszParamSep + 1;
+ }
+ }
+
+ _tcscat( lpszSymbol, lpszParsed );
+
+ ret = TRUE;
+ }
+ GlobalFree( pSym );
+
+ return ret;
+}
+
+// Get source file name and line number from IP address
+// The output format is: "sourcefile(linenumber)" or
+// "modulename!address" or
+// "address"
+static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo )
+{
+ BOOL ret = FALSE;
+ IMAGEHLP_LINE lineInfo;
+ DWORD dwDisp;
+ TCHAR lpszFileName[BUFFERSIZE] = _T("");
+ TCHAR lpModuleInfo[BUFFERSIZE] = _T("");
+
+ _tcscpy( lpszSourceInfo, _T("?(?)") );
+
+ ::ZeroMemory( &lineInfo, sizeof( lineInfo ) );
+ lineInfo.SizeOfStruct = sizeof( lineInfo );
+
+ if ( SymGetLineFromAddr( GetCurrentProcess(), address, &dwDisp, &lineInfo ) )
+ {
+ // Got it. Let's use "sourcefile(linenumber)" format
+ PCSTR2LPTSTR( lineInfo.FileName, lpszFileName );
+ TCHAR fname[_MAX_FNAME];
+ TCHAR ext[_MAX_EXT];
+ _tsplitpath(lpszFileName, NULL, NULL, fname, ext);
+ _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber );
+ ret = TRUE;
+ }
+ else
+ {
+ // There is no source file information. :(
+ // Let's use the "modulename!address" format
+ GetModuleNameFromAddress( address, lpModuleInfo );
+
+ if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0'))
+ // There is no modulename information. :((
+ // Let's use the "address" format
+ _stprintf( lpszSourceInfo, _T("0x%08X"), address );
+ else
+ _stprintf( lpszSourceInfo, _T("%s!0x%08X"), lpModuleInfo, address );
+
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+void PrintFunctionAndSourceInfo(FILE* file, const STACKFRAME& callstack)
+{
+ TCHAR symInfo[BUFFERSIZE] = _T("?");
+ TCHAR srcInfo[BUFFERSIZE] = _T("?");
+
+ GetFunctionInfoFromAddresses((ULONG)callstack.AddrPC.Offset, (ULONG)callstack.AddrFrame.Offset, symInfo);
+ GetSourceInfoFromAddress((ULONG)callstack.AddrPC.Offset, srcInfo);
+ etfprint(file, " " + TStrToUTF8(srcInfo) + " : " + TStrToUTF8(symInfo) + "\n");
+}
+
+void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file )
+{
+ STACKFRAME callStack;
+ BOOL bResult;
+ CONTEXT context;
+ HANDLE hProcess = GetCurrentProcess();
+
+ // If it's not this thread, let's suspend it, and resume it at the end
+ if ( hThread != GetCurrentThread() )
+ if ( SuspendThread( hThread ) == -1 )
+ {
+ // whaaat ?!
+ etfprint(file, "Call stack info failed\n");
+ return;
+ }
+
+ ::ZeroMemory( &context, sizeof(context) );
+ context.ContextFlags = CONTEXT_FULL;
+
+ if ( !GetThreadContext( hThread, &context ) )
+ {
+ etfprint(file, "Call stack info failed\n");
+ return;
+ }
+
+ ::ZeroMemory( &callStack, sizeof(callStack) );
+#ifndef _M_X64
+ callStack.AddrPC.Offset = context.Eip;
+ callStack.AddrStack.Offset = context.Esp;
+ callStack.AddrFrame.Offset = context.Ebp;
+#else
+ callStack.AddrPC.Offset = context.Rip;
+ callStack.AddrStack.Offset = context.Rsp;
+ callStack.AddrFrame.Offset = context.Rbp;
+#endif
+ callStack.AddrPC.Mode = AddrModeFlat;
+ callStack.AddrStack.Mode = AddrModeFlat;
+ callStack.AddrFrame.Mode = AddrModeFlat;
+
+ etfprint(file, "Call stack info: \n");
+ etfprint(file, lpszMessage);
+
+ PrintFunctionAndSourceInfo(file, callStack);
+
+ for( ULONG index = 0; ; index++ )
+ {
+ bResult = StackWalk(
+ IMAGE_FILE_MACHINE_I386,
+ hProcess,
+ hThread,
+ &callStack,
+ NULL,
+ NULL,
+ SymFunctionTableAccess,
+ SymGetModuleBase,
+ NULL);
+
+ if ( index == 0 )
+ continue;
+
+ if( !bResult || callStack.AddrFrame.Offset == 0 )
+ break;
+
+ PrintFunctionAndSourceInfo(file, callStack);
+
+ }
+
+ if ( hThread != GetCurrentThread() )
+ ResumeThread( hThread );
+}
+
+void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip, DWORD esp, DWORD ebp )
+{
+ STACKFRAME callStack;
+ BOOL bResult;
+ TCHAR symInfo[BUFFERSIZE] = _T("?");
+ TCHAR srcInfo[BUFFERSIZE] = _T("?");
+ HANDLE hProcess = GetCurrentProcess();
+
+ // If it's not this thread, let's suspend it, and resume it at the end
+ if ( hThread != GetCurrentThread() )
+ if ( SuspendThread( hThread ) == -1 )
+ {
+ // whaaat ?!
+ etfprint(file, "Call stack info failed\n");
+ return;
+ }
+
+ ::ZeroMemory( &callStack, sizeof(callStack) );
+ callStack.AddrPC.Offset = eip;
+ callStack.AddrStack.Offset = esp;
+ callStack.AddrFrame.Offset = ebp;
+ callStack.AddrPC.Mode = AddrModeFlat;
+ callStack.AddrStack.Mode = AddrModeFlat;
+ callStack.AddrFrame.Mode = AddrModeFlat;
+
+ etfprint(file, "Call stack info: \n");
+ etfprint(file, lpszMessage);
+
+ PrintFunctionAndSourceInfo(file, callStack);
+
+ for( ULONG index = 0; ; index++ )
+ {
+ bResult = StackWalk(
+ IMAGE_FILE_MACHINE_I386,
+ hProcess,
+ hThread,
+ &callStack,
+ NULL,
+ NULL,
+ SymFunctionTableAccess,
+ SymGetModuleBase,
+ NULL);
+
+ if ( index == 0 )
+ continue;
+
+ if( !bResult || callStack.AddrFrame.Offset == 0 )
+ break;
+
+ PrintFunctionAndSourceInfo(file, callStack);
+ }
+
+ if ( hThread != GetCurrentThread() )
+ ResumeThread( hThread );
+}
+
+char g_uefbuf[2048];
+
+void etfprintf(FILE *file, const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ int len = vsprintf(g_uefbuf, format, ap);
+ fwrite(g_uefbuf, 1, len, file);
+ va_end(ap);
+}
+
+void etfprint(FILE *file, const std::string &text)
+{
+ size_t len = text.length();
+ fwrite(text.data(), 1, len, file);
+}
+
+#endif //WIN32
diff --git a/src/common/src/extended_trace.h b/src/common/src/extended_trace.h
new file mode 100644
index 00000000..6d33eb63
--- /dev/null
+++ b/src/common/src/extended_trace.h
@@ -0,0 +1,53 @@
+// -----------------------------------------------------------------------------------------
+//
+// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com
+// For companies(Austin,TX): If you would like to get my resume, send an email.
+//
+// The source is free, but if you want to use it, mention my name and e-mail address
+//
+// History:
+// 1.0 Initial version Zoltan Csizmadia
+// 1.1 WhineCube version Masken
+// 1.2 Dolphin version Masken
+//
+// ----------------------------------------------------------------------------------------
+
+#ifndef _EXTENDEDTRACE_H_INCLUDED_
+#define _EXTENDEDTRACE_H_INCLUDED_
+
+#if defined(WIN32)
+
+#include <windows.h>
+#include <tchar.h>
+
+#include <string>
+
+#pragma comment( lib, "imagehlp.lib" )
+
+#define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) InitSymInfo( IniSymbolPath )
+#define EXTENDEDTRACEUNINITIALIZE() UninitSymInfo()
+#define STACKTRACE(file) StackTrace( GetCurrentThread(), "", file)
+#define STACKTRACE2(file, eip, esp, ebp) StackTrace(GetCurrentThread(), "", file, eip, esp, ebp)
+// class File;
+
+BOOL InitSymInfo( PCSTR );
+BOOL UninitSymInfo();
+void StackTrace(HANDLE, char const* msg, FILE *file);
+void StackTrace(HANDLE, char const* msg, FILE *file, DWORD eip, DWORD esp, DWORD ebp);
+
+// functions by Masken
+void etfprintf(FILE *file, const char *format, ...);
+void etfprint(FILE *file, const std::string &text);
+#define UEFBUFSIZE 2048
+extern char g_uefbuf[UEFBUFSIZE];
+
+#else // not WIN32
+
+#define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) ((void)0)
+#define EXTENDEDTRACEUNINITIALIZE() ((void)0)
+#define STACKTRACE(file) ((void)0)
+#define STACKTRACE2(file, eip, esp, ebp) ((void)0)
+
+#endif // WIN32
+
+#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 00000000..4f5ca570
--- /dev/null
+++ b/src/common/src/fifo_queue.h
@@ -0,0 +1,115 @@
+
+#ifndef _FIFO_QUEUE_H_
+#define _FIFO_QUEUE_H_
+
+// a simple lockless thread-safe,
+// single reader, single writer queue
+
+#include "atomic.h"
+
+namespace Common
+{
+
+template <typename T>
+class FifoQueue
+{
+public:
+ FifoQueue() : m_size(0)
+ {
+ m_write_ptr = m_read_ptr = new ElementPtr();
+ }
+
+ ~FifoQueue()
+ {
+ // this will empty out the whole queue
+ delete m_read_ptr;
+ }
+
+ u32 Size() const
+ {
+ return m_size;
+ }
+
+ bool Empty() const
+ {
+ //return (m_read_ptr == m_write_ptr);
+ return (0 == m_size);
+ }
+
+ T& Front() const
+ {
+ return *m_read_ptr->current;
+ }
+
+ template <typename Arg>
+ void Push(Arg&& t)
+ {
+ // create the element, add it to the queue
+ m_write_ptr->current = new T(std::forward<Arg>(t));
+ // set the next pointer to a new element ptr
+ // then advance the write pointer
+ m_write_ptr = m_write_ptr->next = new ElementPtr();
+ Common::AtomicIncrement(m_size);
+ }
+
+ void Pop()
+ {
+ Common::AtomicDecrement(m_size);
+ ElementPtr *const tmpptr = m_read_ptr;
+ // advance the read pointer
+ m_read_ptr = m_read_ptr->next;
+ // set the next element to NULL to stop the recursive deletion
+ tmpptr->next = NULL;
+ delete tmpptr; // this also deletes the element
+ }
+
+ bool Pop(T& t)
+ {
+ if (Empty())
+ return false;
+
+ t = std::move(Front());
+ Pop();
+
+ return true;
+ }
+
+ // not thread-safe
+ void Clear()
+ {
+ m_size = 0;
+ delete m_read_ptr;
+ m_write_ptr = m_read_ptr = new ElementPtr();
+ }
+
+private:
+ // stores a pointer to element
+ // and a pointer to the next ElementPtr
+ class ElementPtr
+ {
+ public:
+ ElementPtr() : current(NULL), next(NULL) {}
+
+ ~ElementPtr()
+ {
+ if (current)
+ {
+ delete current;
+ // recusion ftw
+ if (next)
+ delete next;
+ }
+ }
+
+ T *volatile current;
+ ElementPtr *volatile next;
+ };
+
+ ElementPtr *volatile m_write_ptr;
+ ElementPtr *volatile m_read_ptr;
+ volatile u32 m_size;
+};
+
+}
+
+#endif
diff --git a/src/common/src/file_search.cpp b/src/common/src/file_search.cpp
new file mode 100644
index 00000000..ba140ec1
--- /dev/null
+++ b/src/common/src/file_search.cpp
@@ -0,0 +1,106 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#include "common.h"
+#include "common_paths.h"
+#ifndef _WIN32
+#include <sys/types.h>
+#include <dirent.h>
+#else
+#include <windows.h>
+#endif
+
+#include <string>
+#include <algorithm>
+
+#include "file_search.h"
+
+#include "string_util.h"
+
+
+CFileSearch::CFileSearch(const CFileSearch::XStringVector& _rSearchStrings, const CFileSearch::XStringVector& _rDirectories)
+{
+ // Reverse the loop order for speed?
+ for (size_t j = 0; j < _rSearchStrings.size(); j++)
+ {
+ for (size_t i = 0; i < _rDirectories.size(); i++)
+ {
+ FindFiles(_rSearchStrings[j], _rDirectories[i]);
+ }
+ }
+}
+
+
+void CFileSearch::FindFiles(const std::string& _searchString, const std::string& _strPath)
+{
+ std::string GCMSearchPath;
+ BuildCompleteFilename(GCMSearchPath, _strPath, _searchString);
+#ifdef _WIN32
+ WIN32_FIND_DATA findData;
+ HANDLE FindFirst = FindFirstFile(UTF8ToTStr(GCMSearchPath).c_str(), &findData);
+
+ if (FindFirst != INVALID_HANDLE_VALUE)
+ {
+ bool bkeepLooping = true;
+
+ while (bkeepLooping)
+ {
+ if (findData.cFileName[0] != '.')
+ {
+ std::string strFilename;
+ BuildCompleteFilename(strFilename, _strPath, TStrToUTF8(findData.cFileName));
+ m_FileNames.push_back(strFilename);
+ }
+
+ bkeepLooping = FindNextFile(FindFirst, &findData) ? true : false;
+ }
+ }
+ FindClose(FindFirst);
+
+
+#else
+ // TODO: super lame/broken
+
+ auto end_match(_searchString);
+
+ // assuming we have a "*.blah"-like pattern
+ if (!end_match.empty() && end_match[0] == '*')
+ end_match.erase(0, 1);
+
+ // ugly
+ if (end_match == ".*")
+ end_match.clear();
+
+ DIR* dir = opendir(_strPath.c_str());
+
+ if (!dir)
+ return;
+
+ while (auto const dp = readdir(dir))
+ {
+ std::string found(dp->d_name);
+
+ if ((found != ".") && (found != "..")
+ && (found.size() >= end_match.size())
+ && std::equal(end_match.rbegin(), end_match.rend(), found.rbegin()))
+ {
+ std::string full_name;
+ if (_strPath.c_str()[_strPath.size()-1] == DIR_SEP_CHR)
+ full_name = _strPath + found;
+ else
+ full_name = _strPath + DIR_SEP + found;
+
+ m_FileNames.push_back(full_name);
+ }
+ }
+
+ closedir(dir);
+#endif
+}
+
+const CFileSearch::XStringVector& CFileSearch::GetFileNames() const
+{
+ return m_FileNames;
+}
diff --git a/src/common/src/file_search.h b/src/common/src/file_search.h
new file mode 100644
index 00000000..55aaf4eb
--- /dev/null
+++ b/src/common/src/file_search.h
@@ -0,0 +1,28 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#ifndef _FILESEARCH_H_
+#define _FILESEARCH_H_
+
+#include <string>
+#include <vector>
+
+class CFileSearch
+{
+public:
+ typedef std::vector<std::string>XStringVector;
+
+ CFileSearch(const XStringVector& _rSearchStrings, const XStringVector& _rDirectories);
+ const XStringVector& GetFileNames() const;
+
+private:
+
+ void FindFiles(const std::string& _searchString, const std::string& _strPath);
+
+ XStringVector m_FileNames;
+};
+
+#endif // _FILESEARCH_H_
+
diff --git a/src/common/src/file_util.cpp b/src/common/src/file_util.cpp
new file mode 100644
index 00000000..f86414bf
--- /dev/null
+++ b/src/common/src/file_util.cpp
@@ -0,0 +1,922 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#include "common.h"
+#include "common_paths.h"
+#include "file_util.h"
+#include "string_util.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <shlobj.h> // for SHGetFolderPath
+#include <shellapi.h>
+#include <commdlg.h> // for GetSaveFileName
+#include <io.h>
+#include <direct.h> // getcwd
+#else
+#include <sys/param.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+#endif
+
+#if defined(__APPLE__)
+#include <CoreFoundation/CFString.h>
+#include <CoreFoundation/CFURL.h>
+#include <CoreFoundation/CFBundle.h>
+#endif
+
+#include <algorithm>
+#include <sys/stat.h>
+
+#include "string_util.h"
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
+#endif
+
+#ifdef BSD4_4
+#define stat64 stat
+#define fstat64 fstat
+#endif
+
+// This namespace has various generic functions related to files and paths.
+// The code still needs a ton of cleanup.
+// REMEMBER: strdup considered harmful!
+namespace File
+{
+
+// Remove any ending forward slashes from directory paths
+// Modifies argument.
+static void StripTailDirSlashes(std::string &fname)
+{
+ if (fname.length() > 1)
+ {
+ size_t i = fname.length() - 1;
+ while (fname[i] == DIR_SEP_CHR)
+ fname[i--] = '\0';
+ }
+ return;
+}
+
+// Returns true if file filename exists
+bool Exists(const std::string &filename)
+{
+ struct stat64 file_info;
+
+ std::string copy(filename);
+ StripTailDirSlashes(copy);
+
+#ifdef _WIN32
+ int result = _tstat64(UTF8ToTStr(copy).c_str(), &file_info);
+#else
+ int result = stat64(copy.c_str(), &file_info);
+#endif
+
+ return (result == 0);
+}
+
+// Returns true if filename is a directory
+bool IsDirectory(const std::string &filename)
+{
+ struct stat64 file_info;
+
+ std::string copy(filename);
+ StripTailDirSlashes(copy);
+
+#ifdef _WIN32
+ int result = _tstat64(UTF8ToTStr(copy).c_str(), &file_info);
+#else
+ int result = stat64(copy.c_str(), &file_info);
+#endif
+
+ if (result < 0) {
+ WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s",
+ filename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+
+ return S_ISDIR(file_info.st_mode);
+}
+
+// Deletes a given filename, return true on success
+// Doesn't supports deleting a directory
+bool Delete(const std::string &filename)
+{
+ INFO_LOG(COMMON, "Delete: file %s", filename.c_str());
+
+ // Return true because we care about the file no
+ // being there, not the actual delete.
+ if (!Exists(filename))
+ {
+ WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str());
+ return true;
+ }
+
+ // We can't delete a directory
+ if (IsDirectory(filename))
+ {
+ WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str());
+ return false;
+ }
+
+#ifdef _WIN32
+ if (!DeleteFile(UTF8ToTStr(filename).c_str()))
+ {
+ WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s",
+ filename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+#else
+ if (unlink(filename.c_str()) == -1) {
+ WARN_LOG(COMMON, "Delete: unlink failed on %s: %s",
+ filename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+// Returns true if successful, or path already exists.
+bool CreateDir(const std::string &path)
+{
+ INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str());
+#ifdef _WIN32
+ if (::CreateDirectory(UTF8ToTStr(path).c_str(), NULL))
+ return true;
+ DWORD error = GetLastError();
+ if (error == ERROR_ALREADY_EXISTS)
+ {
+ WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str());
+ return true;
+ }
+ ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error);
+ return false;
+#else
+ if (mkdir(path.c_str(), 0755) == 0)
+ return true;
+
+ int err = errno;
+
+ if (err == EEXIST)
+ {
+ WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str());
+ return true;
+ }
+
+ ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err));
+ return false;
+#endif
+}
+
+// Creates the full path of fullPath returns true on success
+bool CreateFullPath(const std::string &fullPath)
+{
+ int panicCounter = 100;
+ INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str());
+
+ if (File::Exists(fullPath))
+ {
+ INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str());
+ return true;
+ }
+
+ size_t position = 0;
+ while (true)
+ {
+ // Find next sub path
+ position = fullPath.find(DIR_SEP_CHR, position);
+
+ // we're done, yay!
+ if (position == fullPath.npos)
+ return true;
+
+ // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
+ std::string const subPath(fullPath.substr(0, position + 1));
+ if (!File::IsDirectory(subPath))
+ File::CreateDir(subPath);
+
+ // A safety check
+ panicCounter--;
+ if (panicCounter <= 0)
+ {
+ ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep");
+ return false;
+ }
+ position++;
+ }
+}
+
+
+// Deletes a directory filename, returns true on success
+bool DeleteDir(const std::string &filename)
+{
+ INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str());
+
+ // check if a directory
+ if (!File::IsDirectory(filename))
+ {
+ ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str());
+ return false;
+ }
+
+#ifdef _WIN32
+ if (::RemoveDirectory(UTF8ToTStr(filename).c_str()))
+ return true;
+#else
+ if (rmdir(filename.c_str()) == 0)
+ return true;
+#endif
+ ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg());
+
+ return false;
+}
+
+// renames file srcFilename to destFilename, returns true on success
+bool Rename(const std::string &srcFilename, const std::string &destFilename)
+{
+ INFO_LOG(COMMON, "Rename: %s --> %s",
+ srcFilename.c_str(), destFilename.c_str());
+ if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
+ return true;
+ ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ return false;
+}
+
+// copies file srcFilename to destFilename, returns true on success
+bool Copy(const std::string &srcFilename, const std::string &destFilename)
+{
+ INFO_LOG(COMMON, "Copy: %s --> %s",
+ srcFilename.c_str(), destFilename.c_str());
+#ifdef _WIN32
+ if (CopyFile(UTF8ToTStr(srcFilename).c_str(), UTF8ToTStr(destFilename).c_str(), FALSE))
+ return true;
+
+ ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ return false;
+#else
+
+ // buffer size
+#define BSIZE 1024
+
+ char buffer[BSIZE];
+
+ // Open input file
+ FILE *input = fopen(srcFilename.c_str(), "rb");
+ if (!input)
+ {
+ ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+
+ // open output file
+ FILE *output = fopen(destFilename.c_str(), "wb");
+ if (!output)
+ {
+ fclose(input);
+ ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+
+ // copy loop
+ while (!feof(input))
+ {
+ // read input
+ int rnum = fread(buffer, sizeof(char), BSIZE, input);
+ if (rnum != BSIZE)
+ {
+ if (ferror(input) != 0)
+ {
+ ERROR_LOG(COMMON,
+ "Copy: failed reading from source, %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ goto bail;
+ }
+ }
+
+ // write output
+ int wnum = fwrite(buffer, sizeof(char), rnum, output);
+ if (wnum != rnum)
+ {
+ ERROR_LOG(COMMON,
+ "Copy: failed writing to output, %s --> %s: %s",
+ srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
+ goto bail;
+ }
+ }
+ // close files
+ fclose(input);
+ fclose(output);
+ return true;
+bail:
+ if (input)
+ fclose(input);
+ if (output)
+ fclose(output);
+ return false;
+#endif
+}
+
+// Returns the size of filename (64bit)
+u64 GetSize(const std::string &filename)
+{
+ if (!Exists(filename))
+ {
+ WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str());
+ return 0;
+ }
+
+ if (IsDirectory(filename))
+ {
+ WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str());
+ return 0;
+ }
+
+ struct stat64 buf;
+#ifdef _WIN32
+ if (_tstat64(UTF8ToTStr(filename).c_str(), &buf) == 0)
+#else
+ if (stat64(filename.c_str(), &buf) == 0)
+#endif
+ {
+ DEBUG_LOG(COMMON, "GetSize: %s: %lld",
+ filename.c_str(), (long long)buf.st_size);
+ return buf.st_size;
+ }
+
+ ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s",
+ filename.c_str(), GetLastErrorMsg());
+ return 0;
+}
+
+// Overloaded GetSize, accepts file descriptor
+u64 GetSize(const int fd)
+{
+ struct stat64 buf;
+ if (fstat64(fd, &buf) != 0) {
+ ERROR_LOG(COMMON, "GetSize: stat failed %i: %s",
+ fd, GetLastErrorMsg());
+ return 0;
+ }
+ return buf.st_size;
+}
+
+// Overloaded GetSize, accepts FILE*
+u64 GetSize(FILE *f)
+{
+ // can't use off_t here because it can be 32-bit
+ u64 pos = ftello(f);
+ if (fseeko(f, 0, SEEK_END) != 0) {
+ ERROR_LOG(COMMON, "GetSize: seek failed %p: %s",
+ f, GetLastErrorMsg());
+ return 0;
+ }
+ u64 size = ftello(f);
+ if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) {
+ ERROR_LOG(COMMON, "GetSize: seek failed %p: %s",
+ f, GetLastErrorMsg());
+ return 0;
+ }
+ return size;
+}
+
+// creates an empty file filename, returns true on success
+bool CreateEmptyFile(const std::string &filename)
+{
+ INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str());
+
+ if (!File::IOFile(filename, "wb"))
+ {
+ ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s",
+ filename.c_str(), GetLastErrorMsg());
+ return false;
+ }
+
+ return true;
+}
+
+
+// Scans the directory tree gets, starting from _Directory and adds the
+// results into parentEntry. Returns the number of files+directories found
+u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry)
+{
+ INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str());
+ // How many files + directories we found
+ u32 foundEntries = 0;
+#ifdef _WIN32
+ // Find the first file in the directory.
+ WIN32_FIND_DATA ffd;
+
+ HANDLE hFind = FindFirstFile(UTF8ToTStr(directory + "\\*").c_str(), &ffd);
+ if (hFind == INVALID_HANDLE_VALUE)
+ {
+ FindClose(hFind);
+ return foundEntries;
+ }
+ // windows loop
+ do
+ {
+ FSTEntry entry;
+ const std::string virtualName(TStrToUTF8(ffd.cFileName));
+#else
+ struct dirent dirent, *result = NULL;
+
+ DIR *dirp = opendir(directory.c_str());
+ if (!dirp)
+ return 0;
+
+ // non windows loop
+ while (!readdir_r(dirp, &dirent, &result) && result)
+ {
+ FSTEntry entry;
+ const std::string virtualName(result->d_name);
+#endif
+ // check for "." and ".."
+ if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
+ ((virtualName[0] == '.') && (virtualName[1] == '.') &&
+ (virtualName[2] == '\0')))
+ continue;
+ entry.virtualName = virtualName;
+ entry.physicalName = directory;
+ entry.physicalName += DIR_SEP + entry.virtualName;
+
+ if (IsDirectory(entry.physicalName.c_str()))
+ {
+ entry.isDirectory = true;
+ // is a directory, lets go inside
+ entry.size = ScanDirectoryTree(entry.physicalName, entry);
+ foundEntries += (u32)entry.size;
+ }
+ else
+ { // is a file
+ entry.isDirectory = false;
+ entry.size = GetSize(entry.physicalName.c_str());
+ }
+ ++foundEntries;
+ // Push into the tree
+ parentEntry.children.push_back(entry);
+#ifdef _WIN32
+ } while (FindNextFile(hFind, &ffd) != 0);
+ FindClose(hFind);
+#else
+ }
+ closedir(dirp);
+#endif
+ // Return number of entries found.
+ return foundEntries;
+}
+
+
+// Deletes the given directory and anything under it. Returns true on success.
+bool DeleteDirRecursively(const std::string &directory)
+{
+ INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str());
+#ifdef _WIN32
+ // Find the first file in the directory.
+ WIN32_FIND_DATA ffd;
+ HANDLE hFind = FindFirstFile(UTF8ToTStr(directory + "\\*").c_str(), &ffd);
+
+ if (hFind == INVALID_HANDLE_VALUE)
+ {
+ FindClose(hFind);
+ return false;
+ }
+
+ // windows loop
+ do
+ {
+ const std::string virtualName(TStrToUTF8(ffd.cFileName));
+#else
+ struct dirent dirent, *result = NULL;
+ DIR *dirp = opendir(directory.c_str());
+ if (!dirp)
+ return false;
+
+ // non windows loop
+ while (!readdir_r(dirp, &dirent, &result) && result)
+ {
+ const std::string virtualName = result->d_name;
+#endif
+
+ // check for "." and ".."
+ if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
+ ((virtualName[0] == '.') && (virtualName[1] == '.') &&
+ (virtualName[2] == '\0')))
+ continue;
+
+ std::string newPath = directory + DIR_SEP_CHR + virtualName;
+ if (IsDirectory(newPath))
+ {
+ if (!DeleteDirRecursively(newPath))
+ {
+ #ifndef _WIN32
+ closedir(dirp);
+ #endif
+
+ return false;
+ }
+ }
+ else
+ {
+ if (!File::Delete(newPath))
+ {
+ #ifndef _WIN32
+ closedir(dirp);
+ #endif
+
+ return false;
+ }
+ }
+
+#ifdef _WIN32
+ } while (FindNextFile(hFind, &ffd) != 0);
+ FindClose(hFind);
+#else
+ }
+ closedir(dirp);
+#endif
+ File::DeleteDir(directory);
+
+ return true;
+}
+
+// Create directory and copy contents (does not overwrite existing files)
+void CopyDir(const std::string &source_path, const std::string &dest_path)
+{
+#ifndef _WIN32
+ if (source_path == dest_path) return;
+ if (!File::Exists(source_path)) return;
+ if (!File::Exists(dest_path)) File::CreateFullPath(dest_path);
+
+ struct dirent dirent, *result = NULL;
+ DIR *dirp = opendir(source_path.c_str());
+ if (!dirp) return;
+
+ while (!readdir_r(dirp, &dirent, &result) && result)
+ {
+ const std::string virtualName(result->d_name);
+ // check for "." and ".."
+ if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
+ ((virtualName[0] == '.') && (virtualName[1] == '.') &&
+ (virtualName[2] == '\0')))
+ continue;
+
+ std::string source, dest;
+ source = source_path + virtualName;
+ dest = dest_path + virtualName;
+ if (IsDirectory(source))
+ {
+ source += '/';
+ dest += '/';
+ if (!File::Exists(dest)) File::CreateFullPath(dest);
+ CopyDir(source, dest);
+ }
+ else if (!File::Exists(dest)) File::Copy(source, dest);
+ }
+ closedir(dirp);
+#endif
+}
+
+// Returns the current directory
+std::string GetCurrentDir()
+{
+ char *dir;
+ // Get the current working directory (getcwd uses malloc)
+ if (!(dir = __getcwd(NULL, 0))) {
+
+ ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s",
+ GetLastErrorMsg());
+ return NULL;
+ }
+ std::string strDir = dir;
+ free(dir);
+ return strDir;
+}
+
+// Sets the current directory to the given directory
+bool SetCurrentDir(const std::string &directory)
+{
+ return __chdir(directory.c_str()) == 0;
+}
+
+#if defined(__APPLE__)
+std::string GetBundleDirectory()
+{
+ CFURLRef BundleRef;
+ char AppBundlePath[MAXPATHLEN];
+ // Get the main bundle for the app
+ BundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+ CFStringRef BundlePath = CFURLCopyFileSystemPath(BundleRef, kCFURLPOSIXPathStyle);
+ CFStringGetFileSystemRepresentation(BundlePath, AppBundlePath, sizeof(AppBundlePath));
+ CFRelease(BundleRef);
+ CFRelease(BundlePath);
+
+ return AppBundlePath;
+}
+#endif
+
+#ifdef _WIN32
+std::string& GetExeDirectory()
+{
+ static std::string DolphinPath;
+ if (DolphinPath.empty())
+ {
+ TCHAR Dolphin_exe_Path[2048];
+ GetModuleFileName(NULL, Dolphin_exe_Path, 2048);
+ DolphinPath = TStrToUTF8(Dolphin_exe_Path);
+ DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\'));
+ }
+ return DolphinPath;
+}
+#endif
+
+std::string GetSysDirectory()
+{
+ std::string sysDir;
+
+#if defined (__APPLE__)
+ sysDir = GetBundleDirectory();
+ sysDir += DIR_SEP;
+ sysDir += SYSDATA_DIR;
+#else
+ sysDir = SYSDATA_DIR;
+#endif
+ sysDir += DIR_SEP;
+
+ INFO_LOG(COMMON, "GetSysDirectory: Setting to %s:", sysDir.c_str());
+ return sysDir;
+}
+
+// Returns a string with a Dolphin data dir or file in the user's home
+// directory. To be used in "multi-user" mode (that is, installed).
+//const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath)
+//{
+// static std::string paths[NUM_PATH_INDICES];
+//
+// // Set up all paths and files on the first run
+// if (paths[D_USER_IDX].empty())
+// {
+//#ifdef _WIN32
+// paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
+//#else
+// if (File::Exists(ROOT_DIR DIR_SEP USERDATA_DIR))
+// paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
+// else
+// paths[D_USER_IDX] = std::string(getenv("HOME") ?
+// getenv("HOME") : getenv("PWD") ?
+// getenv("PWD") : "") + DIR_SEP DOLPHIN_DATA_DIR DIR_SEP;
+//#endif
+//
+// paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
+// paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
+// paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
+// paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
+// paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
+// paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
+// paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
+// paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
+// paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
+// paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
+// paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
+// paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
+// paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
+// paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
+// paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
+// paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
+// }
+//
+// if (!newPath.empty())
+// {
+// if (!File::IsDirectory(newPath))
+// {
+// WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str());
+// return paths[DirIDX];
+// }
+// else
+// {
+// paths[DirIDX] = newPath;
+// }
+//
+// switch (DirIDX)
+// {
+// case D_WIIROOT_IDX:
+// paths[D_WIIUSER_IDX] = paths[D_WIIROOT_IDX] + DIR_SEP;
+// paths[D_WIISYSCONF_IDX] = paths[D_WIIUSER_IDX] + WII_SYSCONF_DIR + DIR_SEP;
+// paths[F_WIISYSCONF_IDX] = paths[D_WIISYSCONF_IDX] + WII_SYSCONF;
+// break;
+//
+// case D_USER_IDX:
+// paths[D_GCUSER_IDX] = paths[D_USER_IDX] + GC_USER_DIR DIR_SEP;
+// paths[D_WIIROOT_IDX] = paths[D_USER_IDX] + WII_USER_DIR;
+// paths[D_WIIUSER_IDX] = paths[D_WIIROOT_IDX] + DIR_SEP;
+// paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
+// paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP;
+// paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
+// paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
+// paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP;
+// paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
+// paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
+// paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
+// paths[D_OPENCL_IDX] = paths[D_USER_IDX] + OPENCL_DIR DIR_SEP;
+// paths[D_HIRESTEXTURES_IDX] = paths[D_USER_IDX] + HIRES_TEXTURES_DIR DIR_SEP;
+// paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
+// paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
+// paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
+// paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
+// paths[D_DUMPDSP_IDX] = paths[D_DUMP_IDX] + DUMP_DSP_DIR DIR_SEP;
+// paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
+// paths[D_MAILLOGS_IDX] = paths[D_LOGS_IDX] + MAIL_LOGS_DIR DIR_SEP;
+// paths[D_WIISYSCONF_IDX] = paths[D_WIIUSER_IDX] + WII_SYSCONF_DIR DIR_SEP;
+// paths[D_THEMES_IDX] = paths[D_USER_IDX] + THEMES_DIR DIR_SEP;
+// paths[F_DOLPHINCONFIG_IDX] = paths[D_CONFIG_IDX] + DOLPHIN_CONFIG;
+// paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
+// paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
+// paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
+// paths[F_WIISYSCONF_IDX] = paths[D_WIISYSCONF_IDX] + WII_SYSCONF;
+// paths[F_RAMDUMP_IDX] = paths[D_DUMP_IDX] + RAM_DUMP;
+// paths[F_ARAMDUMP_IDX] = paths[D_DUMP_IDX] + ARAM_DUMP;
+// paths[F_FAKEVMEMDUMP_IDX] = paths[D_DUMP_IDX] + FAKEVMEM_DUMP;
+// paths[F_GCSRAM_IDX] = paths[D_GCUSER_IDX] + GC_SRAM;
+// break;
+//
+// case D_CONFIG_IDX:
+// paths[F_DOLPHINCONFIG_IDX] = paths[D_CONFIG_IDX] + DOLPHIN_CONFIG;
+// paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG;
+// paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG;
+// break;
+//
+// case D_DUMP_IDX:
+// paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
+// paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP;
+// paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP;
+// break;
+//
+// case D_LOGS_IDX:
+// paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG;
+// }
+// }
+//
+// return paths[DirIDX];
+//}
+
+std::string GetThemeDir(const std::string& theme_name)
+{
+ std::string dir = File::GetUserPath(D_THEMES_IDX) + theme_name + "/";
+
+#if !defined(_WIN32)
+ // If theme does not exist in user's dir load from shared directory
+ if (!File::Exists(dir))
+ dir = SHARED_USER_DIR THEMES_DIR "/" + theme_name + "/";
+#endif
+
+ return dir;
+}
+
+bool WriteStringToFile(bool text_file, const std::string &str, const char *filename)
+{
+ return File::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
+}
+
+bool ReadFileToString(bool text_file, const char *filename, std::string &str)
+{
+ File::IOFile file(filename, text_file ? "r" : "rb");
+ auto const f = file.GetHandle();
+
+ if (!f)
+ return false;
+
+ str.resize(GetSize(f));
+ return file.ReadArray(&str[0], str.size());
+}
+
+IOFile::IOFile()
+ : m_file(NULL), m_good(true)
+{}
+
+IOFile::IOFile(std::FILE* file)
+ : m_file(file), m_good(true)
+{}
+
+IOFile::IOFile(const std::string& filename, const char openmode[])
+ : m_file(NULL), m_good(true)
+{
+ Open(filename, openmode);
+}
+
+IOFile::~IOFile()
+{
+ Close();
+}
+
+IOFile::IOFile(IOFile&& other)
+ : m_file(NULL), m_good(true)
+{
+ Swap(other);
+}
+
+IOFile& IOFile::operator=(IOFile&& other)
+{
+ Swap(other);
+ return *this;
+}
+
+void IOFile::Swap(IOFile& other)
+{
+ std::swap(m_file, other.m_file);
+ std::swap(m_good, other.m_good);
+}
+
+bool IOFile::Open(const std::string& filename, const char openmode[])
+{
+ Close();
+#ifdef _WIN32
+ _tfopen_s(&m_file, UTF8ToTStr(filename).c_str(), UTF8ToTStr(openmode).c_str());
+#else
+ m_file = fopen(filename.c_str(), openmode);
+#endif
+
+ m_good = IsOpen();
+ return m_good;
+}
+
+bool IOFile::Close()
+{
+ if (!IsOpen() || 0 != std::fclose(m_file))
+ m_good = false;
+
+ m_file = NULL;
+ return m_good;
+}
+
+std::FILE* IOFile::ReleaseHandle()
+{
+ std::FILE* const ret = m_file;
+ m_file = NULL;
+ return ret;
+}
+
+void IOFile::SetHandle(std::FILE* file)
+{
+ Close();
+ Clear();
+ m_file = file;
+}
+
+u64 IOFile::GetSize()
+{
+ if (IsOpen())
+ return File::GetSize(m_file);
+ else
+ return 0;
+}
+
+bool IOFile::Seek(s64 off, int origin)
+{
+ if (!IsOpen() || 0 != fseeko(m_file, off, origin))
+ m_good = false;
+
+ return m_good;
+}
+
+u64 IOFile::Tell()
+{
+ if (IsOpen())
+ return ftello(m_file);
+ else
+ return -1;
+}
+
+bool IOFile::Flush()
+{
+ if (!IsOpen() || 0 != std::fflush(m_file))
+ m_good = false;
+
+ return m_good;
+}
+
+bool IOFile::Resize(u64 size)
+{
+ if (!IsOpen() || 0 !=
+#ifdef _WIN32
+ // ector: _chsize sucks, not 64-bit safe
+ // F|RES: changed to _chsize_s. i think it is 64-bit safe
+ _chsize_s(_fileno(m_file), size)
+#else
+ // TODO: handle 64bit and growing
+ ftruncate(fileno(m_file), size)
+#endif
+ )
+ m_good = false;
+
+ return m_good;
+}
+
+} // namespace
diff --git a/src/common/src/file_util.h b/src/common/src/file_util.h
new file mode 100644
index 00000000..a6bd085a
--- /dev/null
+++ b/src/common/src/file_util.h
@@ -0,0 +1,232 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#ifndef _FILEUTIL_H_
+#define _FILEUTIL_H_
+
+#include <fstream>
+#include <cstdio>
+#include <string>
+#include <vector>
+#include <string.h>
+
+#include "common.h"
+#include "string_util.h"
+
+// User directory indices for GetUserPath
+enum {
+ D_USER_IDX,
+ D_GCUSER_IDX,
+ D_WIIROOT_IDX,
+ D_WIIUSER_IDX,
+ D_CONFIG_IDX,
+ D_GAMECONFIG_IDX,
+ D_MAPS_IDX,
+ D_CACHE_IDX,
+ D_SHADERCACHE_IDX,
+ D_SHADERS_IDX,
+ D_STATESAVES_IDX,
+ D_SCREENSHOTS_IDX,
+ D_OPENCL_IDX,
+ D_HIRESTEXTURES_IDX,
+ D_DUMP_IDX,
+ D_DUMPFRAMES_IDX,
+ D_DUMPAUDIO_IDX,
+ D_DUMPTEXTURES_IDX,
+ D_DUMPDSP_IDX,
+ D_LOGS_IDX,
+ D_MAILLOGS_IDX,
+ D_WIISYSCONF_IDX,
+ D_WIIWC24_IDX,
+ D_THEMES_IDX,
+ F_DOLPHINCONFIG_IDX,
+ F_DEBUGGERCONFIG_IDX,
+ F_LOGGERCONFIG_IDX,
+ F_MAINLOG_IDX,
+ F_WIISYSCONF_IDX,
+ F_RAMDUMP_IDX,
+ F_ARAMDUMP_IDX,
+ F_FAKEVMEMDUMP_IDX,
+ F_GCSRAM_IDX,
+ NUM_PATH_INDICES
+};
+
+namespace File
+{
+
+// FileSystem tree node/
+struct FSTEntry
+{
+ bool isDirectory;
+ u64 size; // file length or number of entries from children
+ std::string physicalName; // name on disk
+ std::string virtualName; // name in FST names table
+ std::vector<FSTEntry> children;
+};
+
+// Returns true if file filename exists
+bool Exists(const std::string &filename);
+
+// Returns true if filename is a directory
+bool IsDirectory(const std::string &filename);
+
+// Returns the size of filename (64bit)
+u64 GetSize(const std::string &filename);
+
+// Overloaded GetSize, accepts file descriptor
+u64 GetSize(const int fd);
+
+// Overloaded GetSize, accepts FILE*
+u64 GetSize(FILE *f);
+
+// Returns true if successful, or path already exists.
+bool CreateDir(const std::string &filename);
+
+// Creates the full path of fullPath returns true on success
+bool CreateFullPath(const std::string &fullPath);
+
+// Deletes a given filename, return true on success
+// Doesn't supports deleting a directory
+bool Delete(const std::string &filename);
+
+// Deletes a directory filename, returns true on success
+bool DeleteDir(const std::string &filename);
+
+// renames file srcFilename to destFilename, returns true on success
+bool Rename(const std::string &srcFilename, const std::string &destFilename);
+
+// copies file srcFilename to destFilename, returns true on success
+bool Copy(const std::string &srcFilename, const std::string &destFilename);
+
+// creates an empty file filename, returns true on success
+bool CreateEmptyFile(const std::string &filename);
+
+// Scans the directory tree gets, starting from _Directory and adds the
+// results into parentEntry. Returns the number of files+directories found
+u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry);
+
+// deletes the given directory and anything under it. Returns true on success.
+bool DeleteDirRecursively(const std::string &directory);
+
+// Returns the current directory
+std::string GetCurrentDir();
+
+// Create directory and copy contents (does not overwrite existing files)
+void CopyDir(const std::string &source_path, const std::string &dest_path);
+
+// Set the current directory to given directory
+bool SetCurrentDir(const std::string &directory);
+
+// Returns a pointer to a string with a Dolphin data dir in the user's home
+// directory. To be used in "multi-user" mode (that is, installed).
+const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath="");
+
+// probably doesn't belong here
+std::string GetThemeDir(const std::string& theme_name);
+
+// Returns the path to where the sys file are
+std::string GetSysDirectory();
+
+#ifdef __APPLE__
+std::string GetBundleDirectory();
+#endif
+
+#ifdef _WIN32
+std::string &GetExeDirectory();
+#endif
+
+bool WriteStringToFile(bool text_file, const std::string &str, const char *filename);
+bool ReadFileToString(bool text_file, const char *filename, std::string &str);
+
+// simple wrapper for cstdlib file functions to
+// hopefully will make error checking easier
+// and make forgetting an fclose() harder
+class IOFile : public NonCopyable
+{
+public:
+ IOFile();
+ IOFile(std::FILE* file);
+ IOFile(const std::string& filename, const char openmode[]);
+
+ ~IOFile();
+
+ IOFile(IOFile&& other);
+ IOFile& operator=(IOFile&& other);
+
+ void Swap(IOFile& other);
+
+ bool Open(const std::string& filename, const char openmode[]);
+ bool Close();
+
+ template <typename T>
+ bool ReadArray(T* data, size_t length)
+ {
+ if (!IsOpen() || length != std::fread(data, sizeof(T), length, m_file))
+ m_good = false;
+
+ return m_good;
+ }
+
+ template <typename T>
+ bool WriteArray(const T* data, size_t length)
+ {
+ if (!IsOpen() || length != std::fwrite(data, sizeof(T), length, m_file))
+ m_good = false;
+
+ return m_good;
+ }
+
+ bool ReadBytes(void* data, size_t length)
+ {
+ return ReadArray(reinterpret_cast<char*>(data), length);
+ }
+
+ bool WriteBytes(const void* data, size_t length)
+ {
+ return WriteArray(reinterpret_cast<const char*>(data), length);
+ }
+
+ bool IsOpen() { return NULL != m_file; }
+
+ // m_good is set to false when a read, write or other function fails
+ bool IsGood() { return m_good; }
+ operator void*() { return m_good ? m_file : NULL; }
+
+ std::FILE* ReleaseHandle();
+
+ std::FILE* GetHandle() { return m_file; }
+
+ void SetHandle(std::FILE* file);
+
+ bool Seek(s64 off, int origin);
+ u64 Tell();
+ u64 GetSize();
+ bool Resize(u64 size);
+ bool Flush();
+
+ // clear error state
+ void Clear() { m_good = true; std::clearerr(m_file); }
+
+ std::FILE* m_file;
+ bool m_good;
+private:
+ IOFile(IOFile&);
+ IOFile& operator=(IOFile& other);
+};
+
+} // namespace
+
+// To deal with Windows being dumb at unicode:
+template <typename T>
+void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode)
+{
+#ifdef _WIN32
+ fstream.open(UTF8ToTStr(filename).c_str(), openmode);
+#else
+ fstream.open(filename.c_str(), openmode);
+#endif
+}
+
+#endif
diff --git a/src/common/src/fixed_size_queue.h b/src/common/src/fixed_size_queue.h
new file mode 100644
index 00000000..4045dfa3
--- /dev/null
+++ b/src/common/src/fixed_size_queue.h
@@ -0,0 +1,75 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#ifndef _FIXED_SIZE_QUEUE_H_
+#define _FIXED_SIZE_QUEUE_H_
+
+// STL-look-a-like interface, but name is mixed case to distinguish it clearly from the
+// real STL classes.
+
+// Not fully featured, no safety checking yet. Add features as needed.
+
+// TODO: "inline" storage?
+
+template <class T, int N>
+class fixed_size_queue.h
+{
+ T *storage;
+ int head;
+ int tail;
+ int count; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future.
+
+ // Make copy constructor private for now.
+ fixed_size_queue.h(fixed_size_queue.h &other) { }
+
+public:
+ fixed_size_queue.h()
+ {
+ storage = new T[N];
+ clear();
+ }
+
+ ~fixed_size_queue.h()
+ {
+ delete [] storage;
+ }
+
+ void clear() {
+ head = 0;
+ tail = 0;
+ count = 0;
+ }
+
+ void push(T t) {
+ storage[tail] = t;
+ tail++;
+ if (tail == N)
+ tail = 0;
+ count++;
+ }
+
+ void pop() {
+ head++;
+ if (head == N)
+ head = 0;
+ count--;
+ }
+
+ T pop_front() {
+ const T &temp = storage[head];
+ pop();
+ return temp;
+ }
+
+ T &front() { return storage[head]; }
+ const T &front() const { return storage[head]; }
+
+ size_t size() const {
+ return count;
+ }
+};
+
+#endif // _FIXED_SIZE_QUEUE_H_
+
diff --git a/src/common/src/hash.cpp b/src/common/src/hash.cpp
new file mode 100644
index 00000000..39c63641
--- /dev/null
+++ b/src/common/src/hash.cpp
@@ -0,0 +1,520 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#include "hash.h"
+#if _M_SSE >= 0x402
+#include "cpu_detect.h"
+#include <nmmintrin.h>
+#endif
+
+static u64 (*ptrHashFunction)(const u8 *src, int len, u32 samples) = &GetMurmurHash3;
+
+// uint32_t
+// WARNING - may read one more byte!
+// Implementation from Wikipedia.
+u32 HashFletcher(const u8* data_u8, size_t length)
+{
+ const u16* data = (const u16*)data_u8; /* Pointer to the data to be summed */
+ size_t len = (length + 1) / 2; /* Length in 16-bit words */
+ u32 sum1 = 0xffff, sum2 = 0xffff;
+
+ while (len)
+ {
+ size_t tlen = len > 360 ? 360 : len;
+ len -= tlen;
+
+ do {
+ sum1 += *data++;
+ sum2 += sum1;
+ }
+ while (--tlen);
+
+ sum1 = (sum1 & 0xffff) + (sum1 >> 16);
+ sum2 = (sum2 & 0xffff) + (sum2 >> 16);
+ }
+
+ // Second reduction step to reduce sums to 16 bits
+ sum1 = (sum1 & 0xffff) + (sum1 >> 16);
+ sum2 = (sum2 & 0xffff) + (sum2 >> 16);
+ return(sum2 << 16 | sum1);
+}
+
+
+// Implementation from Wikipedia
+// Slightly slower than Fletcher above, but slightly more reliable.
+#define MOD_ADLER 65521
+// data: Pointer to the data to be summed; len is in bytes
+u32 HashAdler32(const u8* data, size_t len)
+{
+ u32 a = 1, b = 0;
+
+ while (len)
+ {
+ size_t tlen = len > 5550 ? 5550 : len;
+ len -= tlen;
+
+ do
+ {
+ a += *data++;
+ b += a;
+ }
+ while (--tlen);
+
+ a = (a & 0xffff) + (a >> 16) * (65536 - MOD_ADLER);
+ b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER);
+ }
+
+ // It can be shown that a <= 0x1013a here, so a single subtract will do.
+ if (a >= MOD_ADLER)
+ {
+ a -= MOD_ADLER;
+ }
+
+ // It can be shown that b can reach 0xfff87 here.
+ b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER);
+
+ if (b >= MOD_ADLER)
+ {
+ b -= MOD_ADLER;
+ }
+
+ return((b << 16) | a);
+}
+
+// Stupid hash - but can't go back now :)
+// Don't use for new things. At least it's reasonably fast.
+u32 HashEctor(const u8* ptr, int length)
+{
+ u32 crc = 0;
+
+ for (int i = 0; i < length; i++)
+ {
+ crc ^= ptr[i];
+ crc = (crc << 3) | (crc >> 29);
+ }
+
+ return(crc);
+}
+
+
+#ifdef _M_X64
+
+//-----------------------------------------------------------------------------
+// Block read - if your platform needs to do endian-swapping or can only
+// handle aligned reads, do the conversion here
+
+inline u64 getblock(const u64 * p, int i)
+{
+ return p[i];
+}
+
+//----------
+// Block mix - combine the key bits with the hash bits and scramble everything
+
+inline void bmix64(u64 & h1, u64 & h2, u64 & k1, u64 & k2, u64 & c1, u64 & c2)
+{
+ k1 *= c1;
+ k1 = _rotl64(k1,23);
+ k1 *= c2;
+ h1 ^= k1;
+ h1 += h2;
+
+ h2 = _rotl64(h2,41);
+
+ k2 *= c2;
+ k2 = _rotl64(k2,23);
+ k2 *= c1;
+ h2 ^= k2;
+ h2 += h1;
+
+ h1 = h1*3+0x52dce729;
+ h2 = h2*3+0x38495ab5;
+
+ c1 = c1*5+0x7b7d159c;
+ c2 = c2*5+0x6bce6396;
+}
+
+//----------
+// Finalization mix - avalanches all bits to within 0.05% bias
+
+inline u64 fmix64(u64 k)
+{
+ k ^= k >> 33;
+ k *= 0xff51afd7ed558ccd;
+ k ^= k >> 33;
+ k *= 0xc4ceb9fe1a85ec53;
+ k ^= k >> 33;
+
+ return k;
+}
+
+u64 GetMurmurHash3(const u8 *src, int len, u32 samples)
+{
+ const u8 * data = (const u8*)src;
+ const int nblocks = len / 16;
+ u32 Step = (len / 8);
+ if(samples == 0) samples = max(Step, 1u);
+ Step = Step / samples;
+ if(Step < 1) Step = 1;
+
+ u64 h1 = 0x9368e53c2f6af274;
+ u64 h2 = 0x586dcd208f7cd3fd;
+
+ u64 c1 = 0x87c37b91114253d5;
+ u64 c2 = 0x4cf5ad432745937f;
+
+
+ //----------
+ // body
+
+ const u64 * blocks = (const u64 *)(data);
+
+ for(int i = 0; i < nblocks; i+=Step)
+ {
+ u64 k1 = getblock(blocks,i*2+0);
+ u64 k2 = getblock(blocks,i*2+1);
+
+ bmix64(h1,h2,k1,k2,c1,c2);
+ }
+
+ //----------
+ // tail
+
+ const u8 * tail = (const u8*)(data + nblocks*16);
+
+ u64 k1 = 0;
+ u64 k2 = 0;
+
+ switch(len & 15)
+ {
+ case 15: k2 ^= u64(tail[14]) << 48;
+ case 14: k2 ^= u64(tail[13]) << 40;
+ case 13: k2 ^= u64(tail[12]) << 32;
+ case 12: k2 ^= u64(tail[11]) << 24;
+ case 11: k2 ^= u64(tail[10]) << 16;
+ case 10: k2 ^= u64(tail[ 9]) << 8;
+ case 9: k2 ^= u64(tail[ 8]) << 0;
+
+ case 8: k1 ^= u64(tail[ 7]) << 56;
+ case 7: k1 ^= u64(tail[ 6]) << 48;
+ case 6: k1 ^= u64(tail[ 5]) << 40;
+ case 5: k1 ^= u64(tail[ 4]) << 32;
+ case 4: k1 ^= u64(tail[ 3]) << 24;
+ case 3: k1 ^= u64(tail[ 2]) << 16;
+ case 2: k1 ^= u64(tail[ 1]) << 8;
+ case 1: k1 ^= u64(tail[ 0]) << 0;
+ bmix64(h1,h2,k1,k2,c1,c2);
+ };
+
+ //----------
+ // finalization
+
+ h2 ^= len;
+
+ h1 += h2;
+ h2 += h1;
+
+ h1 = fmix64(h1);
+ h2 = fmix64(h2);
+
+ h1 += h2;
+
+ return h1;
+}
+
+
+// CRC32 hash using the SSE4.2 instruction
+u64 GetCRC32(const u8 *src, int len, u32 samples)
+{
+#if _M_SSE >= 0x402
+ u64 h = len;
+ u32 Step = (len / 8);
+ const u64 *data = (const u64 *)src;
+ const u64 *end = data + Step;
+ if(samples == 0) samples = max(Step, 1u);
+ Step = Step / samples;
+ if(Step < 1) Step = 1;
+ while(data < end)
+ {
+ h = _mm_crc32_u64(h, data[0]);
+ data += Step;
+ }
+
+ const u8 *data2 = (const u8*)end;
+ return _mm_crc32_u64(h, u64(data2[0]));
+#else
+ return 0;
+#endif
+}
+
+
+/*
+ * NOTE: This hash function is used for custom texture loading/dumping, so
+ * it should not be changed, which would require all custom textures to be
+ * recalculated for their new hash values. If the hashing function is
+ * changed, make sure this one is still used when the legacy parameter is
+ * true.
+ */
+u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
+{
+ const u64 m = 0xc6a4a7935bd1e995;
+ u64 h = len * m;
+ const int r = 47;
+ u32 Step = (len / 8);
+ const u64 *data = (const u64 *)src;
+ const u64 *end = data + Step;
+ if(samples == 0) samples = max(Step, 1u);
+ Step = Step / samples;
+ if(Step < 1) Step = 1;
+ while(data < end)
+ {
+ u64 k = data[0];
+ data+=Step;
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h ^= k;
+ h *= m;
+ }
+
+ const u8 * data2 = (const u8*)end;
+
+ switch(len & 7)
+ {
+ case 7: h ^= u64(data2[6]) << 48;
+ case 6: h ^= u64(data2[5]) << 40;
+ case 5: h ^= u64(data2[4]) << 32;
+ case 4: h ^= u64(data2[3]) << 24;
+ case 3: h ^= u64(data2[2]) << 16;
+ case 2: h ^= u64(data2[1]) << 8;
+ case 1: h ^= u64(data2[0]);
+ h *= m;
+ };
+
+ h ^= h >> r;
+ h *= m;
+ h ^= h >> r;
+
+ return h;
+}
+#else
+// CRC32 hash using the SSE4.2 instruction
+u64 GetCRC32(const u8 *src, int len, u32 samples)
+{
+#if _M_SSE >= 0x402
+ u32 h = len;
+ u32 Step = (len/4);
+ const u32 *data = (const u32 *)src;
+ const u32 *end = data + Step;
+ if(samples == 0) samples = max(Step, 1u);
+ Step = Step / samples;
+ if(Step < 1) Step = 1;
+ while(data < end)
+ {
+ h = _mm_crc32_u32(h, data[0]);
+ data += Step;
+ }
+
+ const u8 *data2 = (const u8*)end;
+ return (u64)_mm_crc32_u32(h, u32(data2[0]));
+#else
+ return 0;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Block read - if your platform needs to do endian-swapping or can only
+// handle aligned reads, do the conversion here
+
+inline u32 getblock(const u32 * p, int i)
+{
+ return p[i];
+}
+
+//----------
+// Finalization mix - force all bits of a hash block to avalanche
+
+// avalanches all bits to within 0.25% bias
+
+inline u32 fmix32(u32 h)
+{
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+
+ return h;
+}
+
+inline void bmix32(u32 & h1, u32 & h2, u32 & k1, u32 & k2, u32 & c1, u32 & c2)
+{
+ k1 *= c1;
+ k1 = _rotl(k1,11);
+ k1 *= c2;
+ h1 ^= k1;
+ h1 += h2;
+
+ h2 = _rotl(h2,17);
+
+ k2 *= c2;
+ k2 = _rotl(k2,11);
+ k2 *= c1;
+ h2 ^= k2;
+ h2 += h1;
+
+ h1 = h1*3+0x52dce729;
+ h2 = h2*3+0x38495ab5;
+
+ c1 = c1*5+0x7b7d159c;
+ c2 = c2*5+0x6bce6396;
+}
+
+//----------
+
+u64 GetMurmurHash3(const u8* src, int len, u32 samples)
+{
+ const u8 * data = (const u8*)src;
+ u32 out[2];
+ const int nblocks = len / 8;
+ u32 Step = (len / 4);
+ if(samples == 0) samples = max(Step, 1u);
+ Step = Step / samples;
+ if(Step < 1) Step = 1;
+
+ u32 h1 = 0x8de1c3ac;
+ u32 h2 = 0xbab98226;
+
+ u32 c1 = 0x95543787;
+ u32 c2 = 0x2ad7eb25;
+
+ //----------
+ // body
+
+ const u32 * blocks = (const u32 *)(data + nblocks*8);
+
+ for(int i = -nblocks; i < 0; i+=Step)
+ {
+ u32 k1 = getblock(blocks,i*2+0);
+ u32 k2 = getblock(blocks,i*2+1);
+
+ bmix32(h1,h2,k1,k2,c1,c2);
+ }
+
+ //----------
+ // tail
+
+ const u8 * tail = (const u8*)(data + nblocks*8);
+
+ u32 k1 = 0;
+ u32 k2 = 0;
+
+ switch(len & 7)
+ {
+ case 7: k2 ^= tail[6] << 16;
+ case 6: k2 ^= tail[5] << 8;
+ case 5: k2 ^= tail[4] << 0;
+ case 4: k1 ^= tail[3] << 24;
+ case 3: k1 ^= tail[2] << 16;
+ case 2: k1 ^= tail[1] << 8;
+ case 1: k1 ^= tail[0] << 0;
+ bmix32(h1,h2,k1,k2,c1,c2);
+ };
+
+ //----------
+ // finalization
+
+ h2 ^= len;
+
+ h1 += h2;
+ h2 += h1;
+
+ h1 = fmix32(h1);
+ h2 = fmix32(h2);
+
+ h1 += h2;
+ h2 += h1;
+
+ out[0] = h1;
+ out[1] = h2;
+
+ return *((u64 *)&out);
+}
+
+/*
+ * FIXME: The old 32-bit version of this hash made different hashes than the
+ * 64-bit version. Until someone can make a new version of the 32-bit one that
+ * makes identical hashes, this is just a c/p of the 64-bit one.
+ */
+u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
+{
+ const u64 m = 0xc6a4a7935bd1e995ULL;
+ u64 h = len * m;
+ const int r = 47;
+ u32 Step = (len / 8);
+ const u64 *data = (const u64 *)src;
+ const u64 *end = data + Step;
+ if(samples == 0) samples = max(Step, 1u);
+ Step = Step / samples;
+ if(Step < 1) Step = 1;
+ while(data < end)
+ {
+ u64 k = data[0];
+ data+=Step;
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h ^= k;
+ h *= m;
+ }
+
+ const u8 * data2 = (const u8*)end;
+
+ switch(len & 7)
+ {
+ case 7: h ^= u64(data2[6]) << 48;
+ case 6: h ^= u64(data2[5]) << 40;
+ case 5: h ^= u64(data2[4]) << 32;
+ case 4: h ^= u64(data2[3]) << 24;
+ case 3: h ^= u64(data2[2]) << 16;
+ case 2: h ^= u64(data2[1]) << 8;
+ case 1: h ^= u64(data2[0]);
+ h *= m;
+ };
+
+ h ^= h >> r;
+ h *= m;
+ h ^= h >> r;
+
+ return h;
+}
+#endif
+
+u64 GetHash64(const u8 *src, int len, u32 samples)
+{
+ return ptrHashFunction(src, len, samples);
+}
+
+// sets the hash function used for the texture cache
+void SetHash64Function(bool useHiresTextures)
+{
+ if (useHiresTextures)
+ {
+ ptrHashFunction = &GetHashHiresTexture;
+ }
+#if _M_SSE >= 0x402
+ else if (cpu_info.bSSE4_2 && !useHiresTextures) // sse crc32 version
+ {
+ ptrHashFunction = &GetCRC32;
+ }
+#endif
+ else
+ {
+ ptrHashFunction = &GetMurmurHash3;
+ }
+}
+
+
+
diff --git a/src/common/src/hash.h b/src/common/src/hash.h
new file mode 100644
index 00000000..addfa4b5
--- /dev/null
+++ b/src/common/src/hash.h
@@ -0,0 +1,20 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#ifndef _HASH_H_
+#define _HASH_H_
+
+#include "common.h"
+
+u32 HashFletcher(const u8* data_u8, size_t length); // FAST. Length & 1 == 0.
+u32 HashAdler32(const u8* data, size_t len); // Fairly accurate, slightly slower
+u32 HashFNV(const u8* ptr, int length); // Another fast and decent hash
+u32 HashEctor(const u8* ptr, int length); // JUNK. DO NOT USE FOR NEW THINGS
+u64 GetCRC32(const u8 *src, int len, u32 samples); // SSE4.2 version of CRC32
+u64 GetHashHiresTexture(const u8 *src, int len, u32 samples);
+u64 GetMurmurHash3(const u8 *src, int len, u32 samples);
+u64 GetHash64(const u8 *src, int len, u32 samples);
+void SetHash64Function(bool useHiresTextures);
+#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 00000000..d33ee782
--- /dev/null
+++ b/src/common/src/linear_disk_cache.h
@@ -0,0 +1,191 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#ifndef _LINEAR_DISKCACHE
+#define _LINEAR_DISKCACHE
+
+#include "common.h"
+#include <fstream>
+
+// defined in Version.cpp
+extern const char *scm_rev_git_str;
+
+// On disk format:
+//header{
+// u32 'DCAC';
+// u32 version; // svn_rev
+// u16 sizeof(key_type);
+// u16 sizeof(value_type);
+//}
+
+//key_value_pair{
+// u32 value_size;
+// key_type key;
+// value_type[value_size] value;
+//}
+
+template <typename K, typename V>
+class LinearDiskCacheReader
+{
+public:
+ virtual void Read(const K &key, const V *value, u32 value_size) = 0;
+};
+
+// Dead simple unsorted key-value store with append functionality.
+// No random read functionality, all reading is done in OpenAndRead.
+// Keys and values can contain any characters, including \0.
+//
+// Suitable for caching generated shader bytecode between executions.
+// Not tuned for extreme performance but should be reasonably fast.
+// Does not support keys or values larger than 2GB, which should be reasonable.
+// Keys must have non-zero length; values can have zero length.
+
+// K and V are some POD type
+// K : the key type
+// V : value array type
+template <typename K, typename V>
+class LinearDiskCache
+{
+public:
+ // return number of read entries
+ u32 OpenAndRead(const char *filename, LinearDiskCacheReader<K, V> &reader)
+ {
+ using std::ios_base;
+
+ // close any currently opened file
+ Close();
+ m_num_entries = 0;
+
+ // try opening for reading/writing
+ OpenFStream(m_file, filename, ios_base::in | ios_base::out | ios_base::binary);
+
+ m_file.seekg(0, std::ios::end);
+ std::fstream::pos_type end_pos = m_file.tellg();
+ m_file.seekg(0, std::ios::beg);
+ std::fstream::pos_type start_pos = m_file.tellg();
+ std::streamoff file_size = end_pos - start_pos;
+
+ if (m_file.is_open() && ValidateHeader())
+ {
+ // good header, read some key/value pairs
+ K key;
+
+ V *value = NULL;
+ u32 value_size;
+ u32 entry_number;
+
+ std::fstream::pos_type last_pos = m_file.tellg();
+
+ while (Read(&value_size))
+ {
+ std::streamoff next_extent = (last_pos - start_pos) + sizeof(value_size) + value_size;
+ if (next_extent > file_size)
+ break;
+
+ delete[] value;
+ value = new V[value_size];
+
+ // read key/value and pass to reader
+ if (Read(&key) &&
+ Read(value, value_size) &&
+ Read(&entry_number) &&
+ entry_number == m_num_entries+1)
+ {
+ reader.Read(key, value, value_size);
+ }
+ else
+ {
+ break;
+ }
+
+ m_num_entries++;
+ last_pos = m_file.tellg();
+ }
+ m_file.seekp(last_pos);
+ m_file.clear();
+
+ delete[] value;
+ return m_num_entries;
+ }
+
+ // failed to open file for reading or bad header
+ // close and recreate file
+ Close();
+ m_file.open(filename, ios_base::out | ios_base::trunc | ios_base::binary);
+ WriteHeader();
+ return 0;
+ }
+
+ void Sync()
+ {
+ m_file.flush();
+ }
+
+ void Close()
+ {
+ if (m_file.is_open())
+ m_file.close();
+ // clear any error flags
+ m_file.clear();
+ }
+
+ // Appends a key-value pair to the store.
+ void Append(const K &key, const V *value, u32 value_size)
+ {
+ // TODO: Should do a check that we don't already have "key"? (I think each caller does that already.)
+ Write(&value_size);
+ Write(&key);
+ Write(value, value_size);
+ m_num_entries++;
+ Write(&m_num_entries);
+ }
+
+private:
+ void WriteHeader()
+ {
+ Write(&m_header);
+ }
+
+ bool ValidateHeader()
+ {
+ char file_header[sizeof(Header)];
+
+ return (Read(file_header, sizeof(Header))
+ && !memcmp((const char*)&m_header, file_header, sizeof(Header)));
+ }
+
+ template <typename D>
+ bool Write(const D *data, u32 count = 1)
+ {
+ return m_file.write((const char*)data, count * sizeof(D)).good();
+ }
+
+ template <typename D>
+ bool Read(const D *data, u32 count = 1)
+ {
+ return m_file.read((char*)data, count * sizeof(D)).good();
+ }
+
+ struct Header
+ {
+ Header()
+ : id(*(u32*)"DCAC")
+ , key_t_size(sizeof(K))
+ , value_t_size(sizeof(V))
+ {
+ memcpy(ver, scm_rev_git_str, 40);
+ }
+
+ const u32 id;
+ const u16 key_t_size, value_t_size;
+ char ver[40];
+
+ } m_header;
+
+ std::fstream m_file;
+ u32 m_num_entries;
+};
+
+#endif // _LINEAR_DISKCACHE
diff --git a/src/common/src/log.h b/src/common/src/log.h
new file mode 100644
index 00000000..8691a825
--- /dev/null
+++ b/src/common/src/log.h
@@ -0,0 +1,155 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and OSReports.
+#define ERROR_LEVEL 2 // Critical errors
+#define WARNING_LEVEL 3 // Something is suspicious.
+#define INFO_LEVEL 4 // General information.
+#define DEBUG_LEVEL 5 // Detailed debugging - might make things slow.
+
+namespace LogTypes
+{
+
+enum LOG_TYPE {
+ ACTIONREPLAY,
+ AUDIO,
+ AUDIO_INTERFACE,
+ BOOT,
+ COMMANDPROCESSOR,
+ COMMON,
+ CONSOLE,
+ DISCIO,
+ FILEMON,
+ DSPHLE,
+ DSPLLE,
+ DSP_MAIL,
+ DSPINTERFACE,
+ DVDINTERFACE,
+ DYNA_REC,
+ EXPANSIONINTERFACE,
+ GDB_STUB,
+ POWERPC,
+ GPFIFO,
+ OSHLE,
+ MASTER_LOG,
+ MEMMAP,
+ MEMCARD_MANAGER,
+ OSREPORT,
+ PAD,
+ PROCESSORINTERFACE,
+ PIXELENGINE,
+ SERIALINTERFACE,
+ SP1,
+ STREAMINGINTERFACE,
+ VIDEO,
+ VIDEOINTERFACE,
+ WII_IOB,
+ WII_IPC,
+ WII_IPC_DVD,
+ WII_IPC_ES,
+ WII_IPC_FILEIO,
+ WII_IPC_HID,
+ WII_IPC_HLE,
+ WII_IPC_NET,
+ WII_IPC_WC24,
+ WII_IPC_SSL,
+ WII_IPC_SD,
+ WII_IPC_STM,
+ WII_IPC_WIIMOTE,
+ WIIMOTE,
+ NETPLAY,
+
+ NUMBER_OF_LOGS // Must be last
+};
+
+// FIXME: should this be removed?
+enum LOG_LEVELS {
+ LNOTICE = NOTICE_LEVEL,
+ LERROR = ERROR_LEVEL,
+ LWARNING = WARNING_LEVEL,
+ LINFO = INFO_LEVEL,
+ LDEBUG = DEBUG_LEVEL,
+};
+
+#define LOGTYPES_LEVELS LogTypes::LOG_LEVELS
+#define LOGTYPES_TYPE LogTypes::LOG_TYPE
+
+} // namespace
+
+void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type,
+ const char *file, int line, const char *fmt, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 5, 6)))
+#endif
+ ;
+
+#if defined LOGGING || defined _DEBUG || defined DEBUGFAST
+#define MAX_LOGLEVEL DEBUG_LEVEL
+#else
+#ifndef MAX_LOGLEVEL
+#define MAX_LOGLEVEL WARNING_LEVEL
+#endif // loglevel
+#endif // logging
+
+#ifdef GEKKO
+#define GENERIC_LOG(t, v, ...)
+#else
+// Let the compiler optimize this out
+#define GENERIC_LOG(t, v, ...) { \
+ if (v <= MAX_LOGLEVEL) \
+ GenericLog(v, t, __FILE__, __LINE__, __VA_ARGS__); \
+ }
+#endif
+
+#define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0)
+#define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0)
+#define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0)
+#define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0)
+#define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0)
+
+#if MAX_LOGLEVEL >= DEBUG_LEVEL
+#define _dbg_assert_(_t_, _a_) \
+ if (!(_a_)) {\
+ ERROR_LOG(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \
+ __LINE__, __FILE__, __TIME__); \
+ if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \
+ }
+#define _dbg_assert_msg_(_t_, _a_, ...)\
+ if (!(_a_)) {\
+ ERROR_LOG(_t_, __VA_ARGS__); \
+ if (!PanicYesNo(__VA_ARGS__)) {Crash();} \
+ }
+#define _dbg_update_() Host_UpdateLogDisplay();
+
+#else // not debug
+#define _dbg_update_() ;
+
+#ifndef _dbg_assert_
+#define _dbg_assert_(_t_, _a_) {}
+#define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {}
+#endif // dbg_assert
+#endif // MAX_LOGLEVEL DEBUG
+
+#define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_)
+
+#ifndef GEKKO
+#ifdef _WIN32
+#define _assert_msg_(_t_, _a_, _fmt_, ...) \
+ if (!(_a_)) {\
+ if (!PanicYesNo(_fmt_, __VA_ARGS__)) {Crash();} \
+ }
+#else // not win32
+#define _assert_msg_(_t_, _a_, _fmt_, ...) \
+ if (!(_a_)) {\
+ if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \
+ }
+#endif // WIN32
+#else // GEKKO
+#define _assert_msg_(_t_, _a_, _fmt_, ...)
+#endif
+
+#endif // _LOG_H_
diff --git a/src/common/src/log_manager.cpp b/src/common/src/log_manager.cpp
new file mode 100644
index 00000000..c19f728f
--- /dev/null
+++ b/src/common/src/log_manager.cpp
@@ -0,0 +1,199 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#ifdef ANDROID
+#include "Host.h"
+#endif
+#include "log_manager.h"
+#include "console_listener.h"
+#include "timer.h"
+#include "thread.h"
+#include "file_util.h"
+
+void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
+ const char *file, int line, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ if (LogManager::GetInstance())
+ LogManager::GetInstance()->Log(level, type,
+ file, line, fmt, args);
+ va_end(args);
+}
+
+LogManager *LogManager::m_logManager = NULL;
+
+LogManager::LogManager()
+{
+ // create log files
+ m_Log[LogTypes::MASTER_LOG] = new LogContainer("*", "Master Log");
+ m_Log[LogTypes::BOOT] = new LogContainer("BOOT", "Boot");
+ m_Log[LogTypes::COMMON] = new LogContainer("COMMON", "Common");
+ m_Log[LogTypes::DISCIO] = new LogContainer("DIO", "Disc IO");
+ m_Log[LogTypes::FILEMON] = new LogContainer("FileMon", "File Monitor");
+ m_Log[LogTypes::PAD] = new LogContainer("PAD", "Pad");
+ m_Log[LogTypes::PIXELENGINE] = new LogContainer("PE", "PixelEngine");
+ m_Log[LogTypes::COMMANDPROCESSOR] = new LogContainer("CP", "CommandProc");
+ m_Log[LogTypes::VIDEOINTERFACE] = new LogContainer("VI", "VideoInt");
+ m_Log[LogTypes::SERIALINTERFACE] = new LogContainer("SI", "SerialInt");
+ m_Log[LogTypes::PROCESSORINTERFACE] = new LogContainer("PI", "ProcessorInt");
+ m_Log[LogTypes::MEMMAP] = new LogContainer("MI", "MI & memmap");
+ m_Log[LogTypes::SP1] = new LogContainer("SP1", "Serial Port 1");
+ m_Log[LogTypes::STREAMINGINTERFACE] = new LogContainer("Stream", "StreamingInt");
+ m_Log[LogTypes::DSPINTERFACE] = new LogContainer("DSP", "DSPInterface");
+ m_Log[LogTypes::DVDINTERFACE] = new LogContainer("DVD", "DVDInterface");
+ m_Log[LogTypes::GPFIFO] = new LogContainer("GP", "GPFifo");
+ m_Log[LogTypes::EXPANSIONINTERFACE] = new LogContainer("EXI", "ExpansionInt");
+ m_Log[LogTypes::GDB_STUB] = new LogContainer("GDB_STUB", "GDB Stub");
+ m_Log[LogTypes::AUDIO_INTERFACE] = new LogContainer("AI", "AudioInt");
+ m_Log[LogTypes::POWERPC] = new LogContainer("PowerPC", "IBM CPU");
+ m_Log[LogTypes::OSHLE] = new LogContainer("HLE", "HLE");
+ m_Log[LogTypes::DSPHLE] = new LogContainer("DSPHLE", "DSP HLE");
+ m_Log[LogTypes::DSPLLE] = new LogContainer("DSPLLE", "DSP LLE");
+ m_Log[LogTypes::DSP_MAIL] = new LogContainer("DSPMails", "DSP Mails");
+ m_Log[LogTypes::VIDEO] = new LogContainer("Video", "Video Backend");
+ m_Log[LogTypes::AUDIO] = new LogContainer("Audio", "Audio Emulator");
+ m_Log[LogTypes::DYNA_REC] = new LogContainer("JIT", "Dynamic Recompiler");
+ m_Log[LogTypes::CONSOLE] = new LogContainer("CONSOLE", "Dolphin Console");
+ m_Log[LogTypes::OSREPORT] = new LogContainer("OSREPORT", "OSReport");
+ m_Log[LogTypes::WIIMOTE] = new LogContainer("Wiimote", "Wiimote");
+ m_Log[LogTypes::WII_IOB] = new LogContainer("WII_IOB", "WII IO Bridge");
+ m_Log[LogTypes::WII_IPC] = new LogContainer("WII_IPC", "WII IPC");
+ m_Log[LogTypes::WII_IPC_HID] = new LogContainer("WII_IPC_HID", "WII IPC HID");
+ m_Log[LogTypes::WII_IPC_HLE] = new LogContainer("WII_IPC_HLE", "WII IPC HLE");
+ m_Log[LogTypes::WII_IPC_DVD] = new LogContainer("WII_IPC_DVD", "WII IPC DVD");
+ m_Log[LogTypes::WII_IPC_ES] = new LogContainer("WII_IPC_ES", "WII IPC ES");
+ m_Log[LogTypes::WII_IPC_FILEIO] = new LogContainer("WII_IPC_FILEIO","WII IPC FILEIO");
+ m_Log[LogTypes::WII_IPC_SD] = new LogContainer("WII_IPC_SD", "WII IPC SD");
+ m_Log[LogTypes::WII_IPC_STM] = new LogContainer("WII_IPC_STM", "WII IPC STM");
+ m_Log[LogTypes::WII_IPC_NET] = new LogContainer("WII_IPC_NET", "WII IPC NET");
+ m_Log[LogTypes::WII_IPC_WC24] = new LogContainer("WII_IPC_WC24", "WII IPC WC24");
+ m_Log[LogTypes::WII_IPC_SSL] = new LogContainer("WII_IPC_SSL", "WII IPC SSL");
+ m_Log[LogTypes::WII_IPC_WIIMOTE] = new LogContainer("WII_IPC_WIIMOTE","WII IPC WIIMOTE");
+ m_Log[LogTypes::ACTIONREPLAY] = new LogContainer("ActionReplay", "ActionReplay");
+ m_Log[LogTypes::MEMCARD_MANAGER] = new LogContainer("MemCard Manager", "MemCard Manager");
+ m_Log[LogTypes::NETPLAY] = new LogContainer("NETPLAY", "Netplay");
+
+ m_fileLog = new FileLogListener(File::GetUserPath(F_MAINLOG_IDX).c_str());
+ m_consoleLog = new ConsoleListener();
+ m_debuggerLog = new DebuggerLogListener();
+
+ for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
+ {
+ m_Log[i]->SetEnable(true);
+ m_Log[i]->AddListener(m_fileLog);
+ m_Log[i]->AddListener(m_consoleLog);
+#ifdef _MSC_VER
+ if (IsDebuggerPresent())
+ m_Log[i]->AddListener(m_debuggerLog);
+#endif
+ }
+}
+
+LogManager::~LogManager()
+{
+ for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
+ {
+ m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog);
+ m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog);
+ m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog);
+ }
+
+ for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
+ delete m_Log[i];
+
+ delete m_fileLog;
+ delete m_consoleLog;
+ delete m_debuggerLog;
+}
+
+void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
+ const char *file, int line, const char *format, va_list args)
+{
+ char temp[MAX_MSGLEN];
+ char msg[MAX_MSGLEN * 2];
+ LogContainer *log = m_Log[type];
+
+ if (!log->IsEnabled() || level > log->GetLevel() || ! log->HasListeners())
+ return;
+
+ CharArrayFromFormatV(temp, MAX_MSGLEN, format, args);
+
+ static const char level_to_char[7] = "-NEWID";
+ sprintf(msg, "%s %s:%u %c[%s]: %s\n",
+ Common::Timer::GetTimeFormatted().c_str(),
+ file, line, level_to_char[(int)level],
+ log->GetShortName(), temp);
+#ifdef ANDROID
+ Host_SysMessage(msg);
+#endif
+ log->Trigger(level, msg);
+}
+
+void LogManager::Init()
+{
+ m_logManager = new LogManager();
+}
+
+void LogManager::Shutdown()
+{
+ delete m_logManager;
+ m_logManager = NULL;
+}
+
+LogContainer::LogContainer(const char* shortName, const char* fullName, bool enable)
+ : m_enable(enable)
+{
+ strncpy(m_fullName, fullName, 128);
+ strncpy(m_shortName, shortName, 32);
+ m_level = LogTypes::LWARNING;
+}
+
+// LogContainer
+void LogContainer::AddListener(LogListener *listener)
+{
+ std::lock_guard<std::mutex> lk(m_listeners_lock);
+ m_listeners.insert(listener);
+}
+
+void LogContainer::RemoveListener(LogListener *listener)
+{
+ std::lock_guard<std::mutex> lk(m_listeners_lock);
+ m_listeners.erase(listener);
+}
+
+void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg)
+{
+ std::lock_guard<std::mutex> lk(m_listeners_lock);
+
+ std::set<LogListener*>::const_iterator i;
+ for (i = m_listeners.begin(); i != m_listeners.end(); ++i)
+ {
+ (*i)->Log(level, msg);
+ }
+}
+
+FileLogListener::FileLogListener(const char *filename)
+{
+ OpenFStream(m_logfile, filename, std::ios::app);
+ SetEnable(true);
+}
+
+void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
+{
+ if (!IsEnabled() || !IsValid())
+ return;
+
+ std::lock_guard<std::mutex> lk(m_log_lock);
+ m_logfile << msg << std::flush;
+}
+
+void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
+{
+#if _MSC_VER
+ ::OutputDebugStringA(msg);
+#endif
+}
diff --git a/src/common/src/log_manager.h b/src/common/src/log_manager.h
new file mode 100644
index 00000000..59078849
--- /dev/null
+++ b/src/common/src/log_manager.h
@@ -0,0 +1,169 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _LOGMANAGER_H_
+#define _LOGMANAGER_H_
+
+#include "log.h"
+#include "string_util.h"
+#include "thread.h"
+#include "file_util.h"
+
+#include <set>
+#include <string.h>
+
+#define MAX_MESSAGES 8000
+#define MAX_MSGLEN 1024
+
+
+// pure virtual interface
+class LogListener
+{
+public:
+ virtual ~LogListener() {}
+
+ virtual void Log(LogTypes::LOG_LEVELS, const char *msg) = 0;
+};
+
+class FileLogListener : public LogListener
+{
+public:
+ FileLogListener(const char *filename);
+
+ void Log(LogTypes::LOG_LEVELS, const char *msg);
+
+ bool IsValid() { return !m_logfile.fail(); }
+ bool IsEnabled() const { return m_enable; }
+ void SetEnable(bool enable) { m_enable = enable; }
+
+ const char* GetName() const { return "file"; }
+
+private:
+ std::mutex m_log_lock;
+ std::ofstream m_logfile;
+ bool m_enable;
+};
+
+class DebuggerLogListener : public LogListener
+{
+public:
+ void Log(LogTypes::LOG_LEVELS, const char *msg);
+};
+
+class LogContainer
+{
+public:
+ LogContainer(const char* shortName, const char* fullName, bool enable = false);
+
+ const char* GetShortName() const { return m_shortName; }
+ const char* GetFullName() const { return m_fullName; }
+
+ void AddListener(LogListener* listener);
+ void RemoveListener(LogListener* listener);
+
+ void Trigger(LogTypes::LOG_LEVELS, const char *msg);
+
+ bool IsEnabled() const { return m_enable; }
+ void SetEnable(bool enable) { m_enable = enable; }
+
+ LogTypes::LOG_LEVELS GetLevel() const { return m_level; }
+
+ void SetLevel(LogTypes::LOG_LEVELS level) { m_level = level; }
+
+ bool HasListeners() const { return !m_listeners.empty(); }
+
+private:
+ char m_fullName[128];
+ char m_shortName[32];
+ bool m_enable;
+ LogTypes::LOG_LEVELS m_level;
+ std::mutex m_listeners_lock;
+ std::set<LogListener*> m_listeners;
+};
+
+class ConsoleListener;
+
+class LogManager : NonCopyable
+{
+private:
+ LogContainer* m_Log[LogTypes::NUMBER_OF_LOGS];
+ FileLogListener *m_fileLog;
+ ConsoleListener *m_consoleLog;
+ DebuggerLogListener *m_debuggerLog;
+ static LogManager *m_logManager; // Singleton. Ugh.
+
+ LogManager();
+ ~LogManager();
+public:
+
+ static u32 GetMaxLevel() { return MAX_LOGLEVEL; }
+
+ void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
+ const char *file, int line, const char *fmt, va_list args);
+
+ void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level)
+ {
+ m_Log[type]->SetLevel(level);
+ }
+
+ void SetEnable(LogTypes::LOG_TYPE type, bool enable)
+ {
+ m_Log[type]->SetEnable(enable);
+ }
+
+ bool IsEnabled(LogTypes::LOG_TYPE type) const
+ {
+ return m_Log[type]->IsEnabled();
+ }
+
+ const char* GetShortName(LogTypes::LOG_TYPE type) const
+ {
+ return m_Log[type]->GetShortName();
+ }
+
+ const char* GetFullName(LogTypes::LOG_TYPE type) const
+ {
+ return m_Log[type]->GetFullName();
+ }
+
+ void AddListener(LogTypes::LOG_TYPE type, LogListener *listener)
+ {
+ m_Log[type]->AddListener(listener);
+ }
+
+ void RemoveListener(LogTypes::LOG_TYPE type, LogListener *listener)
+ {
+ m_Log[type]->RemoveListener(listener);
+ }
+
+ FileLogListener *GetFileListener() const
+ {
+ return m_fileLog;
+ }
+
+ ConsoleListener *GetConsoleListener() const
+ {
+ return m_consoleLog;
+ }
+
+ DebuggerLogListener *GetDebuggerListener() const
+ {
+ return m_debuggerLog;
+ }
+
+ static LogManager* GetInstance()
+ {
+ return m_logManager;
+ }
+
+ static void SetInstance(LogManager *logManager)
+ {
+ m_logManager = logManager;
+ }
+
+ static void Init();
+ static void Shutdown();
+};
+
+#endif // _LOGMANAGER_H_
diff --git a/src/common/src/math_util.cpp b/src/common/src/math_util.cpp
new file mode 100644
index 00000000..6667e078
--- /dev/null
+++ b/src/common/src/math_util.cpp
@@ -0,0 +1,212 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#include "common.h"
+#include "math_util.h"
+
+#include <cmath>
+#include <numeric>
+
+namespace MathUtil
+{
+
+u32 ClassifyDouble(double dvalue)
+{
+ // TODO: Optimize the below to be as fast as possible.
+ IntDouble value;
+ value.d = dvalue;
+ u64 sign = value.i & DOUBLE_SIGN;
+ u64 exp = value.i & DOUBLE_EXP;
+ if (exp > DOUBLE_ZERO && exp < DOUBLE_EXP)
+ {
+ // Nice normalized number.
+ return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
+ }
+ else
+ {
+ u64 mantissa = value.i & DOUBLE_FRAC;
+ if (mantissa)
+ {
+ if (exp)
+ {
+ return PPC_FPCLASS_QNAN;
+ }
+ else
+ {
+ // Denormalized number.
+ return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD;
+ }
+ }
+ else if (exp)
+ {
+ //Infinite
+ return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF;
+ }
+ else
+ {
+ //Zero
+ return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ;
+ }
+ }
+}
+
+u32 ClassifyFloat(float fvalue)
+{
+ // TODO: Optimize the below to be as fast as possible.
+ IntFloat value;
+ value.f = fvalue;
+ u32 sign = value.i & FLOAT_SIGN;
+ u32 exp = value.i & FLOAT_EXP;
+ if (exp > FLOAT_ZERO && exp < FLOAT_EXP)
+ {
+ // Nice normalized number.
+ return sign ? PPC_FPCLASS_NN : PPC_FPCLASS_PN;
+ }
+ else
+ {
+ u32 mantissa = value.i & FLOAT_FRAC;
+ if (mantissa)
+ {
+ if (exp)
+ {
+ return PPC_FPCLASS_QNAN; // Quiet NAN
+ }
+ else
+ {
+ // Denormalized number.
+ return sign ? PPC_FPCLASS_ND : PPC_FPCLASS_PD;
+ }
+ }
+ else if (exp)
+ {
+ // Infinite
+ return sign ? PPC_FPCLASS_NINF : PPC_FPCLASS_PINF;
+ }
+ else
+ {
+ //Zero
+ return sign ? PPC_FPCLASS_NZ : PPC_FPCLASS_PZ;
+ }
+ }
+}
+
+
+} // namespace
+
+inline void MatrixMul(int n, const float *a, const float *b, float *result)
+{
+ for (int i = 0; i < n; ++i)
+ {
+ for (int j = 0; j < n; ++j)
+ {
+ float temp = 0;
+ for (int k = 0; k < n; ++k)
+ {
+ temp += a[i * n + k] * b[k * n + j];
+ }
+ result[i * n + j] = temp;
+ }
+ }
+}
+
+// Calculate sum of a float list
+float MathFloatVectorSum(const std::vector<float>& Vec)
+{
+ return std::accumulate(Vec.begin(), Vec.end(), 0.0f);
+}
+
+void Matrix33::LoadIdentity(Matrix33 &mtx)
+{
+ memset(mtx.data, 0, sizeof(mtx.data));
+ mtx.data[0] = 1.0f;
+ mtx.data[4] = 1.0f;
+ mtx.data[8] = 1.0f;
+}
+
+void Matrix33::RotateX(Matrix33 &mtx, float rad)
+{
+ float s = sin(rad);
+ float c = cos(rad);
+ memset(mtx.data, 0, sizeof(mtx.data));
+ mtx.data[0] = 1;
+ mtx.data[4] = c;
+ mtx.data[5] = -s;
+ mtx.data[7] = s;
+ mtx.data[8] = c;
+}
+void Matrix33::RotateY(Matrix33 &mtx, float rad)
+{
+ float s = sin(rad);
+ float c = cos(rad);
+ memset(mtx.data, 0, sizeof(mtx.data));
+ mtx.data[0] = c;
+ mtx.data[2] = s;
+ mtx.data[4] = 1;
+ mtx.data[6] = -s;
+ mtx.data[8] = c;
+}
+
+void Matrix33::Multiply(const Matrix33 &a, const Matrix33 &b, Matrix33 &result)
+{
+ MatrixMul(3, a.data, b.data, result.data);
+}
+
+void Matrix33::Multiply(const Matrix33 &a, const float vec[3], float result[3])
+{
+ for (int i = 0; i < 3; ++i) {
+ result[i] = 0;
+ for (int k = 0; k < 3; ++k) {
+ result[i] += a.data[i * 3 + k] * vec[k];
+ }
+ }
+}
+
+void Matrix44::LoadIdentity(Matrix44 &mtx)
+{
+ memset(mtx.data, 0, sizeof(mtx.data));
+ mtx.data[0] = 1.0f;
+ mtx.data[5] = 1.0f;
+ mtx.data[10] = 1.0f;
+ mtx.data[15] = 1.0f;
+}
+
+void Matrix44::LoadMatrix33(Matrix44 &mtx, const Matrix33 &m33)
+{
+ for (int i = 0; i < 3; ++i)
+ {
+ for (int j = 0; j < 3; ++j)
+ {
+ mtx.data[i * 4 + j] = m33.data[i * 3 + j];
+ }
+ }
+
+ for (int i = 0; i < 3; ++i)
+ {
+ mtx.data[i * 4 + 3] = 0;
+ mtx.data[i + 12] = 0;
+ }
+ mtx.data[15] = 1.0f;
+}
+
+void Matrix44::Set(Matrix44 &mtx, const float mtxArray[16])
+{
+ for(int i = 0; i < 16; ++i) {
+ mtx.data[i] = mtxArray[i];
+ }
+}
+
+void Matrix44::Translate(Matrix44 &mtx, const float vec[3])
+{
+ LoadIdentity(mtx);
+ mtx.data[3] = vec[0];
+ mtx.data[7] = vec[1];
+ mtx.data[11] = vec[2];
+}
+
+void Matrix44::Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result)
+{
+ MatrixMul(4, a.data, b.data, result.data);
+}
+
diff --git a/src/common/src/math_util.h b/src/common/src/math_util.h
new file mode 100644
index 00000000..4dd12c30
--- /dev/null
+++ b/src/common/src/math_util.h
@@ -0,0 +1,200 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#ifndef _MATH_UTIL_H_
+#define _MATH_UTIL_H_
+
+#include "common.h"
+
+#include <vector>
+
+namespace MathUtil
+{
+
+static const u64 DOUBLE_SIGN = 0x8000000000000000ULL,
+ DOUBLE_EXP = 0x7FF0000000000000ULL,
+ DOUBLE_FRAC = 0x000FFFFFFFFFFFFFULL,
+ DOUBLE_ZERO = 0x0000000000000000ULL;
+
+static const u32 FLOAT_SIGN = 0x80000000,
+ FLOAT_EXP = 0x7F800000,
+ FLOAT_FRAC = 0x007FFFFF,
+ FLOAT_ZERO = 0x00000000;
+
+union IntDouble {
+ double d;
+ u64 i;
+};
+union IntFloat {
+ float f;
+ u32 i;
+};
+
+inline bool IsNAN(double d)
+{
+ IntDouble x; x.d = d;
+ return ( ((x.i & DOUBLE_EXP) == DOUBLE_EXP) &&
+ ((x.i & DOUBLE_FRAC) != DOUBLE_ZERO) );
+}
+
+inline bool IsQNAN(double d)
+{
+ IntDouble x; x.d = d;
+ return ( ((x.i & DOUBLE_EXP) == DOUBLE_EXP) &&
+ ((x.i & 0x0007fffffffffffULL) == 0x000000000000000ULL) &&
+ ((x.i & 0x000800000000000ULL) == 0x000800000000000ULL) );
+}
+
+inline bool IsSNAN(double d)
+{
+ IntDouble x; x.d = d;
+ return( ((x.i & DOUBLE_EXP) == DOUBLE_EXP) &&
+ ((x.i & DOUBLE_FRAC) != DOUBLE_ZERO) &&
+ ((x.i & 0x0008000000000000ULL) == DOUBLE_ZERO) );
+}
+
+inline float FlushToZero(float f)
+{
+ IntFloat x; x.f = f;
+ if ((x.i & FLOAT_EXP) == 0)
+ x.i &= FLOAT_SIGN; // turn into signed zero
+ return x.f;
+}
+
+inline double FlushToZeroAsFloat(double d)
+{
+ IntDouble x; x.d = d;
+ if ((x.i & DOUBLE_EXP) < 0x3800000000000000ULL)
+ x.i &= DOUBLE_SIGN; // turn into signed zero
+ return x.d;
+}
+
+enum PPCFpClass
+{
+ PPC_FPCLASS_QNAN = 0x11,
+ PPC_FPCLASS_NINF = 0x9,
+ PPC_FPCLASS_NN = 0x8,
+ PPC_FPCLASS_ND = 0x18,
+ PPC_FPCLASS_NZ = 0x12,
+ PPC_FPCLASS_PZ = 0x2,
+ PPC_FPCLASS_PD = 0x14,
+ PPC_FPCLASS_PN = 0x4,
+ PPC_FPCLASS_PINF = 0x5,
+};
+
+// Uses PowerPC conventions for the return value, so it can be easily
+// used directly in CPU emulation.
+u32 ClassifyDouble(double dvalue);
+// More efficient float version.
+u32 ClassifyFloat(float fvalue);
+
+template<class T>
+struct Rectangle
+{
+ T left;
+ T top;
+ T right;
+ T bottom;
+
+ Rectangle()
+ { }
+
+ Rectangle(T theLeft, T theTop, T theRight, T theBottom)
+ : left(theLeft), top(theTop), right(theRight), bottom(theBottom)
+ { }
+
+ bool operator==(const Rectangle& r) { return left==r.left && top==r.top && right==r.right && bottom==r.bottom; }
+
+ T GetWidth() const { return abs(right - left); }
+ T GetHeight() const { return abs(bottom - top); }
+
+ // If the rectangle is in a coordinate system with a lower-left origin, use
+ // this Clamp.
+ void ClampLL(T x1, T y1, T x2, T y2)
+ {
+ if (left < x1) left = x1;
+ if (right > x2) right = x2;
+ if (top > y1) top = y1;
+ if (bottom < y2) bottom = y2;
+ }
+
+ // If the rectangle is in a coordinate system with an upper-left origin,
+ // use this Clamp.
+ void ClampUL(T x1, T y1, T x2, T y2)
+ {
+ if (left < x1) left = x1;
+ if (right > x2) right = x2;
+ if (top < y1) top = y1;
+ if (bottom > y2) bottom = y2;
+ }
+};
+
+} // namespace MathUtil
+
+inline float pow2f(float x) {return x * x;}
+inline double pow2(double x) {return x * x;}
+
+float MathFloatVectorSum(const std::vector<float>&);
+
+#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define ROUND_DOWN(x, a) ((x) & ~((a) - 1))
+
+// Rounds down. 0 -> undefined
+inline u64 Log2(u64 val)
+{
+#if defined(__GNUC__)
+ return 63 - __builtin_clzll(val);
+
+#elif defined(_MSC_VER) && defined(_M_X64)
+ unsigned long result = -1;
+ _BitScanReverse64(&result, val);
+ return result;
+
+#else
+ u64 result = -1;
+ while (val != 0)
+ {
+ val >>= 1;
+ ++result;
+ }
+ return result;
+#endif
+}
+
+// Tiny matrix/vector library.
+// Used for things like Free-Look in the gfx backend.
+
+class Matrix33
+{
+public:
+ static void LoadIdentity(Matrix33 &mtx);
+
+ // set mtx to be a rotation matrix around the x axis
+ static void RotateX(Matrix33 &mtx, float rad);
+ // set mtx to be a rotation matrix around the y axis
+ static void RotateY(Matrix33 &mtx, float rad);
+
+ // set result = a x b
+ static void Multiply(const Matrix33 &a, const Matrix33 &b, Matrix33 &result);
+ static void Multiply(const Matrix33 &a, const float vec[3], float result[3]);
+
+ float data[9];
+};
+
+class Matrix44
+{
+public:
+ static void LoadIdentity(Matrix44 &mtx);
+ static void LoadMatrix33(Matrix44 &mtx, const Matrix33 &m33);
+ static void Set(Matrix44 &mtx, const float mtxArray[16]);
+
+ static void Translate(Matrix44 &mtx, const float vec[3]);
+
+ static void Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result);
+
+ float data[16];
+};
+
+#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 00000000..13cd00fd
--- /dev/null
+++ b/src/common/src/mem_arena.cpp
@@ -0,0 +1,304 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#include "common.h"
+#include "memory_util.h"
+#include "mem_arena.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <cerrno>
+#include <cstring>
+#ifdef ANDROID
+#include <sys/ioctl.h>
+#include <linux/ashmem.h>
+#endif
+#endif
+#include <set>
+
+#if defined(__APPLE__)
+static const char* ram_temp_file = "/tmp/gc_mem.tmp";
+#elif !defined(_WIN32) // non OSX unixes
+static const char* ram_temp_file = "/dev/shm/gc_mem.tmp";
+#endif
+#ifdef ANDROID
+#define ASHMEM_DEVICE "/dev/ashmem"
+
+int AshmemCreateFileMapping(const char *name, size_t size)
+{
+ int fd, ret;
+ fd = open(ASHMEM_DEVICE, O_RDWR);
+ if (fd < 0)
+ return fd;
+
+ // We don't really care if we can't set the name, it is optional
+ ret = ioctl(fd, ASHMEM_SET_NAME, name);
+
+ ret = ioctl(fd, ASHMEM_SET_SIZE, size);
+ if (ret < 0)
+ {
+ close(fd);
+ NOTICE_LOG(MEMMAP, "Ashmem returned error: 0x%08x", ret);
+ return ret;
+ }
+ return fd;
+}
+#endif
+
+void MemArena::GrabLowMemSpace(size_t size)
+{
+#ifdef _WIN32
+ hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL);
+#elif defined(ANDROID)
+ fd = AshmemCreateFileMapping("Dolphin-emu", size);
+ if (fd < 0)
+ {
+ NOTICE_LOG(MEMMAP, "Ashmem allocation failed");
+ return;
+ }
+#else
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ fd = open(ram_temp_file, O_RDWR | O_CREAT, mode);
+ unlink(ram_temp_file);
+ if (ftruncate(fd, size) < 0)
+ ERROR_LOG(MEMMAP, "Failed to allocate low memory space");
+ return;
+#endif
+}
+
+
+void MemArena::ReleaseSpace()
+{
+#ifdef _WIN32
+ CloseHandle(hMemoryMapping);
+ hMemoryMapping = 0;
+#else
+ close(fd);
+#endif
+}
+
+
+void *MemArena::CreateView(s64 offset, size_t size, void *base)
+{
+#ifdef _WIN32
+ return MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base);
+#else
+ void *retval = mmap(
+ base, size,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED | ((base == nullptr) ? 0 : MAP_FIXED),
+ fd, offset);
+
+ if (retval == MAP_FAILED)
+ {
+ NOTICE_LOG(MEMMAP, "mmap on %s failed", ram_temp_file);
+ return nullptr;
+ }
+ else
+ {
+ return retval;
+ }
+#endif
+}
+
+
+void MemArena::ReleaseView(void* view, size_t size)
+{
+#ifdef _WIN32
+ UnmapViewOfFile(view);
+#else
+ munmap(view, size);
+#endif
+}
+
+
+u8* MemArena::Find4GBBase()
+{
+#ifdef _M_X64
+#ifdef _WIN32
+ // 64 bit
+ u8* base = (u8*)VirtualAlloc(0, 0xE1000000, MEM_RESERVE, PAGE_READWRITE);
+ VirtualFree(base, 0, MEM_RELEASE);
+ return base;
+#else
+ // Very precarious - mmap cannot return an error when trying to map already used pages.
+ // This makes the Windows approach above unusable on Linux, so we will simply pray...
+ return reinterpret_cast<u8*>(0x2300000000ULL);
+#endif
+
+#else
+ // 32 bit
+#ifdef _WIN32
+ // The highest thing in any 1GB section of memory space is the locked cache. We only need to fit it.
+ u8* base = (u8*)VirtualAlloc(0, 0x31000000, MEM_RESERVE, PAGE_READWRITE);
+ if (base) {
+ VirtualFree(base, 0, MEM_RELEASE);
+ }
+ return base;
+#else
+#ifdef ANDROID
+ // Android 4.3 changed how mmap works.
+ // if we map it private and then munmap it, we can't use the base returned.
+ // This may be due to changes in them support a full SELinux implementation.
+ const int flags = MAP_ANON;
+#else
+ const int flags = MAP_ANON | MAP_PRIVATE;
+#endif
+ const u32 MemSize = 0x31000000;
+ void* base = mmap(0, MemSize, PROT_NONE, flags, -1, 0);
+ if (base == MAP_FAILED) {
+ PanicAlert("Failed to map 1 GB of memory space: %s", strerror(errno));
+ return 0;
+ }
+ munmap(base, MemSize);
+ return static_cast<u8*>(base);
+#endif
+#endif
+}
+
+
+// yeah, this could also be done in like two bitwise ops...
+#define SKIP(a_flags, b_flags) \
+ if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY)) \
+ continue; \
+ if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM)) \
+ continue; \
+
+
+static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 flags, MemArena *arena) {
+ // OK, we know where to find free space. Now grab it!
+ // We just mimic the popular BAT setup.
+ u32 position = 0;
+ u32 last_position = 0;
+
+ // Zero all the pointers to be sure.
+ for (int i = 0; i < num_views; i++)
+ {
+ if (views[i].out_ptr_low)
+ *views[i].out_ptr_low = 0;
+ if (views[i].out_ptr)
+ *views[i].out_ptr = 0;
+ }
+
+ int i;
+ for (i = 0; i < num_views; i++)
+ {
+ SKIP(flags, views[i].flags);
+ if (views[i].flags & MV_MIRROR_PREVIOUS) {
+ position = last_position;
+ } else {
+ *(views[i].out_ptr_low) = (u8*)arena->CreateView(position, views[i].size);
+ if (!*views[i].out_ptr_low)
+ goto bail;
+ }
+#ifdef _M_X64
+ *views[i].out_ptr = (u8*)arena->CreateView(
+ position, views[i].size, base + views[i].virtual_address);
+#else
+ if (views[i].flags & MV_MIRROR_PREVIOUS) {
+ // No need to create multiple identical views.
+ *views[i].out_ptr = *views[i - 1].out_ptr;
+ } else {
+ *views[i].out_ptr = (u8*)arena->CreateView(
+ position, views[i].size, base + (views[i].virtual_address & 0x3FFFFFFF));
+ if (!*views[i].out_ptr)
+ goto bail;
+ }
+#endif
+ last_position = position;
+ position += views[i].size;
+ }
+
+ return true;
+
+bail:
+ // Argh! ERROR! Free what we grabbed so far so we can try again.
+ MemoryMap_Shutdown(views, i+1, flags, arena);
+ return false;
+}
+
+u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena *arena)
+{
+ u32 total_mem = 0;
+ int base_attempts = 0;
+
+ for (int i = 0; i < num_views; i++)
+ {
+ SKIP(flags, views[i].flags);
+ if ((views[i].flags & MV_MIRROR_PREVIOUS) == 0)
+ total_mem += views[i].size;
+ }
+ // Grab some pagefile backed memory out of the void ...
+ arena->GrabLowMemSpace(total_mem);
+
+ // Now, create views in high memory where there's plenty of space.
+#ifdef _M_X64
+ u8 *base = MemArena::Find4GBBase();
+ // This really shouldn't fail - in 64-bit, there will always be enough
+ // address space.
+ if (!Memory_TryBase(base, views, num_views, flags, arena))
+ {
+ PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
+ exit(0);
+ return 0;
+ }
+#else
+#ifdef _WIN32
+ // Try a whole range of possible bases. Return once we got a valid one.
+ u32 max_base_addr = 0x7FFF0000 - 0x31000000;
+ u8 *base = NULL;
+
+ for (u32 base_addr = 0x40000; base_addr < max_base_addr; base_addr += 0x40000)
+ {
+ base_attempts++;
+ base = (u8 *)base_addr;
+ if (Memory_TryBase(base, views, num_views, flags, arena))
+ {
+ INFO_LOG(MEMMAP, "Found valid memory base at %p after %i tries.", base, base_attempts);
+ base_attempts = 0;
+ break;
+ }
+
+ }
+#else
+ // Linux32 is fine with the x64 method, although limited to 32-bit with no automirrors.
+ u8 *base = MemArena::Find4GBBase();
+ if (!Memory_TryBase(base, views, num_views, flags, arena))
+ {
+ PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
+ exit(0);
+ return 0;
+ }
+#endif
+
+#endif
+ if (base_attempts)
+ PanicAlert("No possible memory base pointer found!");
+ return base;
+}
+
+void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena)
+{
+ std::set<void*> freeset;
+ for (int i = 0; i < num_views; i++)
+ {
+ const MemoryView* view = &views[i];
+ u8** outptrs[2] = {view->out_ptr_low, view->out_ptr};
+ for (int j = 0; j < 2; j++)
+ {
+ u8** outptr = outptrs[j];
+ if (outptr && *outptr && !freeset.count(*outptr))
+ {
+ arena->ReleaseView(*outptr, view->size);
+ freeset.insert(*outptr);
+ *outptr = NULL;
+ }
+ }
+ }
+}
diff --git a/src/common/src/mem_arena.h b/src/common/src/mem_arena.h
new file mode 100644
index 00000000..4c3ded0d
--- /dev/null
+++ b/src/common/src/mem_arena.h
@@ -0,0 +1,58 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#ifndef _MEMARENA_H_
+#define _MEMARENA_H_
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include "common.h"
+
+// This class lets you create a block of anonymous RAM, and then arbitrarily map views into it.
+// Multiple views can mirror the same section of the block, which makes it very convenient for emulating
+// memory mirrors.
+
+class MemArena
+{
+public:
+ void GrabLowMemSpace(size_t size);
+ void ReleaseSpace();
+ void *CreateView(s64 offset, size_t size, void *base = nullptr);
+ void ReleaseView(void *view, size_t size);
+
+ // This only finds 1 GB in 32-bit
+ static u8 *Find4GBBase();
+private:
+
+#ifdef _WIN32
+ HANDLE hMemoryMapping;
+#else
+ int fd;
+#endif
+};
+
+enum {
+ MV_MIRROR_PREVIOUS = 1,
+ MV_FAKE_VMEM = 2,
+ MV_WII_ONLY = 4,
+};
+
+struct MemoryView
+{
+ u8 **out_ptr_low;
+ u8 **out_ptr;
+ u32 virtual_address;
+ u32 size;
+ u32 flags;
+};
+
+// Uses a memory arena to set up an emulator-friendly memory map according to
+// a passed-in list of MemoryView structures.
+u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena *arena);
+void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena);
+
+#endif // _MEMARENA_H_
diff --git a/src/common/src/memory_util.cpp b/src/common/src/memory_util.cpp
new file mode 100644
index 00000000..346d2e52
--- /dev/null
+++ b/src/common/src/memory_util.cpp
@@ -0,0 +1,197 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#include "common.h"
+#include "memory_util.h"
+#include "string_util.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#include <psapi.h>
+#else
+#include <errno.h>
+#include <stdio.h>
+#endif
+
+#if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT)
+#include <unistd.h>
+#define PAGE_MASK (getpagesize() - 1)
+#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
+#endif
+
+// This is purposely not a full wrapper for virtualalloc/mmap, but it
+// provides exactly the primitive operations that Dolphin needs.
+
+void* AllocateExecutableMemory(size_t size, bool low)
+{
+#if defined(_WIN32)
+ void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+#else
+ static char *map_hint = 0;
+#if defined(__x86_64__) && !defined(MAP_32BIT)
+ // This OS has no flag to enforce allocation below the 4 GB boundary,
+ // but if we hint that we want a low address it is very likely we will
+ // get one.
+ // An older version of this code used MAP_FIXED, but that has the side
+ // effect of discarding already mapped pages that happen to be in the
+ // requested virtual memory range (such as the emulated RAM, sometimes).
+ if (low && (!map_hint))
+ map_hint = (char*)round_page(512*1024*1024); /* 0.5 GB rounded up to the next page */
+#endif
+ void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_ANON | MAP_PRIVATE
+#if defined(__x86_64__) && defined(MAP_32BIT)
+ | (low ? MAP_32BIT : 0)
+#endif
+ , -1, 0);
+#endif /* defined(_WIN32) */
+
+ // printf("Mapped executable memory at %p (size %ld)\n", ptr,
+ // (unsigned long)size);
+
+#if defined(__FreeBSD__)
+ if (ptr == MAP_FAILED)
+ {
+ ptr = NULL;
+#else
+ if (ptr == NULL)
+ {
+#endif
+ PanicAlert("Failed to allocate executable memory");
+ }
+#if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT)
+ else
+ {
+ if (low)
+ {
+ map_hint += size;
+ map_hint = (char*)round_page(map_hint); /* round up to the next page */
+ // printf("Next map will (hopefully) be at %p\n", map_hint);
+ }
+ }
+#endif
+
+#if defined(_M_X64)
+ if ((u64)ptr >= 0x80000000 && low == true)
+ PanicAlert("Executable memory ended up above 2GB!");
+#endif
+
+ return ptr;
+}
+
+void* AllocateMemoryPages(size_t size)
+{
+#ifdef _WIN32
+ void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
+#else
+ void* ptr = mmap(0, size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_PRIVATE, -1, 0);
+#endif
+
+ // printf("Mapped memory at %p (size %ld)\n", ptr,
+ // (unsigned long)size);
+
+ if (ptr == NULL)
+ PanicAlert("Failed to allocate raw memory");
+
+ return ptr;
+}
+
+void* AllocateAlignedMemory(size_t size,size_t alignment)
+{
+#ifdef _WIN32
+ void* ptr = _aligned_malloc(size,alignment);
+#else
+ void* ptr = NULL;
+#ifdef ANDROID
+ ptr = memalign(alignment, size);
+#else
+ if (posix_memalign(&ptr, alignment, size) != 0)
+ ERROR_LOG(MEMMAP, "Failed to allocate aligned memory");
+#endif
+#endif
+
+ // printf("Mapped memory at %p (size %ld)\n", ptr,
+ // (unsigned long)size);
+
+ if (ptr == NULL)
+ PanicAlert("Failed to allocate aligned memory");
+
+ return ptr;
+}
+
+void FreeMemoryPages(void* ptr, size_t size)
+{
+ if (ptr)
+ {
+#ifdef _WIN32
+
+ if (!VirtualFree(ptr, 0, MEM_RELEASE))
+ PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg());
+ ptr = NULL; // Is this our responsibility?
+
+#else
+ munmap(ptr, size);
+#endif
+ }
+}
+
+void FreeAlignedMemory(void* ptr)
+{
+ if (ptr)
+ {
+#ifdef _WIN32
+ _aligned_free(ptr);
+#else
+ free(ptr);
+#endif
+ }
+}
+
+void WriteProtectMemory(void* ptr, size_t size, bool allowExecute)
+{
+#ifdef _WIN32
+ DWORD oldValue;
+ if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
+ PanicAlert("WriteProtectMemory failed!\n%s", GetLastErrorMsg());
+#else
+ mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
+#endif
+}
+
+void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute)
+{
+#ifdef _WIN32
+ DWORD oldValue;
+ if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue))
+ PanicAlert("UnWriteProtectMemory failed!\n%s", GetLastErrorMsg());
+#else
+ mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
+#endif
+}
+
+std::string MemUsage()
+{
+#ifdef _WIN32
+#pragma comment(lib, "psapi")
+ DWORD processID = GetCurrentProcessId();
+ HANDLE hProcess;
+ PROCESS_MEMORY_COUNTERS pmc;
+ std::string Ret;
+
+ // Print information about the memory usage of the process.
+
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
+ if (NULL == hProcess) return "MemUsage Error";
+
+ if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
+ Ret = StringFromFormat("%s K", ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str());
+
+ CloseHandle(hProcess);
+ return Ret;
+#else
+ return "";
+#endif
+}
diff --git a/src/common/src/memory_util.h b/src/common/src/memory_util.h
new file mode 100644
index 00000000..49b2589d
--- /dev/null
+++ b/src/common/src/memory_util.h
@@ -0,0 +1,25 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+
+#ifndef _MEMORYUTIL_H
+#define _MEMORYUTIL_H
+
+#ifndef _WIN32
+#include <sys/mman.h>
+#endif
+#include <string>
+
+void* AllocateExecutableMemory(size_t size, bool low = true);
+void* AllocateMemoryPages(size_t size);
+void FreeMemoryPages(void* ptr, size_t size);
+void* AllocateAlignedMemory(size_t size,size_t alignment);
+void FreeAlignedMemory(void* ptr);
+void WriteProtectMemory(void* ptr, size_t size, bool executable = false);
+void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
+std::string MemUsage();
+
+inline int GetPageSize() { return 4096; }
+
+#endif
diff --git a/src/common/src/misc.cpp b/src/common/src/misc.cpp
new file mode 100644
index 00000000..719a98cb
--- /dev/null
+++ b/src/common/src/misc.cpp
@@ -0,0 +1,33 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common.h"
+
+// Neither Android nor OS X support TLS
+#if defined(__APPLE__) || (ANDROID && __clang__)
+#define __thread
+#endif
+
+// Generic function to get last error message.
+// Call directly after the command or use the error num.
+// This function might change the error code.
+//const char* GetLastErrorMsg()
+//{
+// static const size_t buff_size = 255;
+//
+//#ifdef _WIN32
+// static __declspec(thread) char err_str[buff_size] = {};
+//
+// FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
+// MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+// err_str, buff_size, NULL);
+//#else
+// static __thread char err_str[buff_size] = {};
+//
+// // Thread safe (XSI-compliant)
+// strerror_r(errno, err_str, buff_size);
+//#endif
+//
+// return err_str;
+//}
diff --git a/src/common/src/msg_handler.cpp b/src/common/src/msg_handler.cpp
new file mode 100644
index 00000000..62cbe0aa
--- /dev/null
+++ b/src/common/src/msg_handler.cpp
@@ -0,0 +1,107 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <stdio.h> // System
+
+#include "common.h" // Local
+#include "string_util.h"
+
+bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style);
+static MsgAlertHandler msg_handler = DefaultMsgHandler;
+static bool AlertEnabled = true;
+
+std::string DefaultStringTranslator(const char* text);
+static StringTranslator str_translator = DefaultStringTranslator;
+
+// Select which of these functions that are used for message boxes. If
+// wxWidgets is enabled we will use wxMsgAlert() that is defined in Main.cpp
+void RegisterMsgAlertHandler(MsgAlertHandler handler)
+{
+ msg_handler = handler;
+}
+
+// Select translation function. For wxWidgets use wxStringTranslator in Main.cpp
+void RegisterStringTranslator(StringTranslator translator)
+{
+ str_translator = translator;
+}
+
+// enable/disable the alert handler
+void SetEnableAlert(bool enable)
+{
+ AlertEnabled = enable;
+}
+
+// This is the first stop for gui alerts where the log is updated and the
+// correct window is shown
+bool MsgAlert(bool yes_no, int Style, const char* format, ...)
+{
+ // Read message and write it to the log
+ std::string caption;
+ char buffer[2048];
+
+ static std::string info_caption;
+ static std::string warn_caption;
+ static std::string ques_caption;
+ static std::string crit_caption;
+
+ if (!info_caption.length())
+ {
+ info_caption = str_translator(_trans("Information"));
+ ques_caption = str_translator(_trans("Question"));
+ warn_caption = str_translator(_trans("Warning"));
+ crit_caption = str_translator(_trans("Critical"));
+ }
+
+ switch(Style)
+ {
+ case INFORMATION:
+ caption = info_caption;
+ break;
+ case QUESTION:
+ caption = ques_caption;
+ break;
+ case WARNING:
+ caption = warn_caption;
+ break;
+ case CRITICAL:
+ caption = crit_caption;
+ break;
+ }
+
+ va_list args;
+ va_start(args, format);
+ CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args);
+ va_end(args);
+
+ ERROR_LOG(MASTER_LOG, "%s: %s", caption.c_str(), buffer);
+
+ // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored
+ if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL))
+ return msg_handler(caption.c_str(), buffer, yes_no, Style);
+
+ return true;
+}
+
+// Default non library dependent panic alert
+bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style)
+{
+//#ifdef _WIN32
+// int STYLE = MB_ICONINFORMATION;
+// if (Style == QUESTION) STYLE = MB_ICONQUESTION;
+// if (Style == WARNING) STYLE = MB_ICONWARNING;
+//
+// return IDYES == MessageBox(0, UTF8ToTStr(text).c_str(), UTF8ToTStr(caption).c_str(), STYLE | (yes_no ? MB_YESNO : MB_OK));
+//#else
+ printf("%s\n", text);
+ return true;
+//#endif
+}
+
+// Default (non) translator
+std::string DefaultStringTranslator(const char* text)
+{
+ return text;
+}
+
diff --git a/src/common/src/msg_handler.h b/src/common/src/msg_handler.h
new file mode 100644
index 00000000..7de10c7b
--- /dev/null
+++ b/src/common/src/msg_handler.h
@@ -0,0 +1,73 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _MSGHANDLER_H_
+#define _MSGHANDLER_H_
+
+#include <string>
+
+// Message alerts
+enum MSG_TYPE
+{
+ INFORMATION,
+ QUESTION,
+ WARNING,
+ CRITICAL
+};
+
+typedef bool (*MsgAlertHandler)(const char* caption, const char* text,
+ bool yes_no, int Style);
+typedef std::string (*StringTranslator)(const char* text);
+
+void RegisterMsgAlertHandler(MsgAlertHandler handler);
+void RegisterStringTranslator(StringTranslator translator);
+
+extern bool MsgAlert(bool yes_no, int Style, const char* format, ...)
+#ifdef __GNUC__
+ __attribute__((format(printf, 3, 4)))
+#endif
+ ;
+void SetEnableAlert(bool enable);
+
+#ifndef GEKKO
+#ifdef _WIN32
+ #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__)
+ #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__)
+ #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__)
+ #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__)
+ #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__)
+ // Use these macros (that do the same thing) if the message should be translated.
+ #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__)
+ #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__)
+ #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__)
+ #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__)
+ #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__)
+#else
+ #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__)
+ #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__)
+ #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__)
+ #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__)
+ #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__)
+ // Use these macros (that do the same thing) if the message should be translated.
+ #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__)
+ #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__)
+ #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__)
+ #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__)
+ #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__)
+#endif
+#else
+// GEKKO
+ #define SuccessAlert(format, ...) ;
+ #define PanicAlert(format, ...) ;
+ #define PanicYesNo(format, ...) ;
+ #define AskYesNo(format, ...) ;
+ #define CriticalAlert(format, ...) ;
+ #define SuccessAlertT(format, ...) ;
+ #define PanicAlertT(format, ...) ;
+ #define PanicYesNoT(format, ...) ;
+ #define AskYesNoT(format, ...) ;
+ #define CriticalAlertT(format, ...) ;
+#endif
+
+#endif // _MSGHANDLER_H_
diff --git a/src/common/src/scm_rev.h b/src/common/src/scm_rev.h
new file mode 100644
index 00000000..cb4eccfd
--- /dev/null
+++ b/src/common/src/scm_rev.h
@@ -0,0 +1,4 @@
+#define SCM_REV_STR "7d11f8cedd7c135d96880f19ecbd3ff87a60a11f"
+#define SCM_DESC_STR "3.5-254-dirty"
+#define SCM_BRANCH_STR "master"
+#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 00000000..d44545e6
--- /dev/null
+++ b/src/common/src/std_condition_variable.h
@@ -0,0 +1,170 @@
+
+#ifndef CONDITION_VARIABLE_H_
+#define CONDITION_VARIABLE_H_
+
+#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
+#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+
+#ifndef __has_include
+#define __has_include(s) 0
+#endif
+
+#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__
+
+// GCC 4.4 provides <condition_variable>
+#include <condition_variable>
+
+#elif __has_include(<condition_variable>) && !ANDROID
+
+// clang and libc++ provide <condition_variable> on OSX. However, the version
+// of libc++ bundled with OSX 10.7 and 10.8 is buggy: it uses _ as a variable.
+//
+// We work around this issue by undefining and redefining _.
+
+#undef _
+#include <condition_variable>
+#define _(s) wxGetTranslation((s))
+
+#else
+
+// partial std::condition_variable implementation for win32/pthread
+
+#include "std_mutex.h"
+
+#if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__)
+#define USE_RVALUE_REFERENCES
+#endif
+
+#if defined(_WIN32) && defined(_M_X64)
+#define USE_CONDITION_VARIABLES
+#elif defined(_WIN32)
+#define USE_EVENTS
+#endif
+
+namespace std
+{
+
+class condition_variable
+{
+#if defined(_WIN32) && defined(USE_CONDITION_VARIABLES)
+ typedef CONDITION_VARIABLE native_type;
+#elif defined(_WIN32)
+ typedef HANDLE native_type;
+#else
+ typedef pthread_cond_t native_type;
+#endif
+
+public:
+
+#ifdef USE_EVENTS
+ typedef native_type native_handle_type;
+#else
+ typedef native_type* native_handle_type;
+#endif
+
+ condition_variable()
+ {
+#if defined(_WIN32) && defined(USE_CONDITION_VARIABLES)
+ InitializeConditionVariable(&m_handle);
+#elif defined(_WIN32)
+ m_handle = CreateEvent(NULL, false, false, NULL);
+#else
+ pthread_cond_init(&m_handle, NULL);
+#endif
+ }
+
+ ~condition_variable()
+ {
+#if defined(_WIN32) && !defined(USE_CONDITION_VARIABLES)
+ CloseHandle(m_handle);
+#elif !defined(_WIN32)
+ pthread_cond_destroy(&m_handle);
+#endif
+ }
+
+ condition_variable(const condition_variable&) /*= delete*/;
+ condition_variable& operator=(const condition_variable&) /*= delete*/;
+
+ void notify_one()
+ {
+#if defined(_WIN32) && defined(USE_CONDITION_VARIABLES)
+ WakeConditionVariable(&m_handle);
+#elif defined(_WIN32)
+ SetEvent(m_handle);
+#else
+ pthread_cond_signal(&m_handle);
+#endif
+ }
+
+ void notify_all()
+ {
+#if defined(_WIN32) && defined(USE_CONDITION_VARIABLES)
+ WakeAllConditionVariable(&m_handle);
+#elif defined(_WIN32)
+ // TODO: broken
+ SetEvent(m_handle);
+#else
+ pthread_cond_broadcast(&m_handle);
+#endif
+ }
+
+ void wait(unique_lock<mutex>& lock)
+ {
+#ifdef _WIN32
+ #ifdef USE_SRWLOCKS
+ SleepConditionVariableSRW(&m_handle, lock.mutex()->native_handle(), INFINITE, 0);
+ #elif defined(USE_CONDITION_VARIABLES)
+ SleepConditionVariableCS(&m_handle, lock.mutex()->native_handle(), INFINITE);
+ #else
+ // TODO: broken, the unlock and wait need to be atomic
+ lock.unlock();
+ WaitForSingleObject(m_handle, INFINITE);
+ lock.lock();
+ #endif
+#else
+ pthread_cond_wait(&m_handle, lock.mutex()->native_handle());
+#endif
+ }
+
+ template <class Predicate>
+ void wait(unique_lock<mutex>& lock, Predicate pred)
+ {
+ while (!pred())
+ wait(lock);
+ }
+
+ //template <class Clock, class Duration>
+ //cv_status wait_until(unique_lock<mutex>& lock,
+ // const chrono::time_point<Clock, Duration>& abs_time);
+
+ //template <class Clock, class Duration, class Predicate>
+ // bool wait_until(unique_lock<mutex>& lock,
+ // const chrono::time_point<Clock, Duration>& abs_time,
+ // Predicate pred);
+
+ //template <class Rep, class Period>
+ //cv_status wait_for(unique_lock<mutex>& lock,
+ // const chrono::duration<Rep, Period>& rel_time);
+
+ //template <class Rep, class Period, class Predicate>
+ // bool wait_for(unique_lock<mutex>& lock,
+ // const chrono::duration<Rep, Period>& rel_time,
+ // Predicate pred);
+
+ native_handle_type native_handle()
+ {
+#ifdef USE_EVENTS
+ return m_handle;
+#else
+ return &m_handle;
+#endif
+ }
+
+private:
+ native_type m_handle;
+};
+
+}
+
+#endif
+#endif
diff --git a/src/common/src/std_mutex.h b/src/common/src/std_mutex.h
new file mode 100644
index 00000000..ce46a2f5
--- /dev/null
+++ b/src/common/src/std_mutex.h
@@ -0,0 +1,365 @@
+
+#ifndef MUTEX_H_
+#define MUTEX_H_
+
+#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
+#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+
+#ifndef __has_include
+#define __has_include(s) 0
+#endif
+
+#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__
+// GCC 4.4 provides <mutex>
+#include <mutex>
+#elif __has_include(<mutex>) && !ANDROID
+// Clang + libc++
+#include <mutex>
+#else
+
+// partial <mutex> implementation for win32/pthread
+
+#include <algorithm>
+
+#if defined(_WIN32)
+// WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#else
+// POSIX
+#include <pthread.h>
+
+#endif
+
+#if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__)
+#define USE_RVALUE_REFERENCES
+#endif
+
+#if defined(_WIN32) && defined(_M_X64)
+#define USE_SRWLOCKS
+#endif
+
+namespace std
+{
+
+class recursive_mutex
+{
+#ifdef _WIN32
+ typedef CRITICAL_SECTION native_type;
+#else
+ typedef pthread_mutex_t native_type;
+#endif
+
+public:
+ typedef native_type* native_handle_type;
+
+ recursive_mutex(const recursive_mutex&) /*= delete*/;
+ recursive_mutex& operator=(const recursive_mutex&) /*= delete*/;
+
+ recursive_mutex()
+ {
+#ifdef _WIN32
+ InitializeCriticalSection(&m_handle);
+#else
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&m_handle, &attr);
+#endif
+ }
+
+ ~recursive_mutex()
+ {
+#ifdef _WIN32
+ DeleteCriticalSection(&m_handle);
+#else
+ pthread_mutex_destroy(&m_handle);
+#endif
+ }
+
+ void lock()
+ {
+#ifdef _WIN32
+ EnterCriticalSection(&m_handle);
+#else
+ pthread_mutex_lock(&m_handle);
+#endif
+ }
+
+ void unlock()
+ {
+#ifdef _WIN32
+ LeaveCriticalSection(&m_handle);
+#else
+ pthread_mutex_unlock(&m_handle);
+#endif
+ }
+
+ bool try_lock()
+ {
+#ifdef _WIN32
+ return (0 != TryEnterCriticalSection(&m_handle));
+#else
+ return !pthread_mutex_trylock(&m_handle);
+#endif
+ }
+
+ native_handle_type native_handle()
+ {
+ return &m_handle;
+ }
+
+private:
+ native_type m_handle;
+};
+
+#if !defined(_WIN32) || defined(USE_SRWLOCKS)
+
+class mutex
+{
+#ifdef _WIN32
+ typedef SRWLOCK native_type;
+#else
+ typedef pthread_mutex_t native_type;
+#endif
+
+public:
+ typedef native_type* native_handle_type;
+
+ mutex(const mutex&) /*= delete*/;
+ mutex& operator=(const mutex&) /*= delete*/;
+
+ mutex()
+ {
+#ifdef _WIN32
+ InitializeSRWLock(&m_handle);
+#else
+ pthread_mutex_init(&m_handle, NULL);
+#endif
+ }
+
+ ~mutex()
+ {
+#ifdef _WIN32
+#else
+ pthread_mutex_destroy(&m_handle);
+#endif
+ }
+
+ void lock()
+ {
+#ifdef _WIN32
+ AcquireSRWLockExclusive(&m_handle);
+#else
+ pthread_mutex_lock(&m_handle);
+#endif
+ }
+
+ void unlock()
+ {
+#ifdef _WIN32
+ ReleaseSRWLockExclusive(&m_handle);
+#else
+ pthread_mutex_unlock(&m_handle);
+#endif
+ }
+
+ bool try_lock()
+ {
+#ifdef _WIN32
+ // XXX TryAcquireSRWLockExclusive requires Windows 7!
+ // return (0 != TryAcquireSRWLockExclusive(&m_handle));
+ return false;
+#else
+ return !pthread_mutex_trylock(&m_handle);
+#endif
+ }
+
+ native_handle_type native_handle()
+ {
+ return &m_handle;
+ }
+
+private:
+ native_type m_handle;
+};
+
+#else
+typedef recursive_mutex mutex; // just use CriticalSections
+
+#endif
+
+enum defer_lock_t { defer_lock };
+enum try_to_lock_t { try_to_lock };
+enum adopt_lock_t { adopt_lock };
+
+template <class Mutex>
+class lock_guard
+{
+public:
+ typedef Mutex mutex_type;
+
+ explicit lock_guard(mutex_type& m)
+ : pm(m)
+ {
+ m.lock();
+ }
+
+ lock_guard(mutex_type& m, adopt_lock_t)
+ : pm(m)
+ {
+ }
+
+ ~lock_guard()
+ {
+ pm.unlock();
+ }
+
+ lock_guard(lock_guard const&) /*= delete*/;
+ lock_guard& operator=(lock_guard const&) /*= delete*/;
+
+private:
+ mutex_type& pm;
+};
+
+template <class Mutex>
+class unique_lock
+{
+public:
+ typedef Mutex mutex_type;
+
+ unique_lock()
+ : pm(NULL), owns(false)
+ {}
+
+ /*explicit*/ unique_lock(mutex_type& m)
+ : pm(&m), owns(true)
+ {
+ m.lock();
+ }
+
+ unique_lock(mutex_type& m, defer_lock_t)
+ : pm(&m), owns(false)
+ {}
+
+ unique_lock(mutex_type& m, try_to_lock_t)
+ : pm(&m), owns(m.try_lock())
+ {}
+
+ unique_lock(mutex_type& m, adopt_lock_t)
+ : pm(&m), owns(true)
+ {}
+
+ //template <class Clock, class Duration>
+ //unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);
+
+ //template <class Rep, class Period>
+ //unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);
+
+ ~unique_lock()
+ {
+ if (owns_lock())
+ mutex()->unlock();
+ }
+
+#ifdef USE_RVALUE_REFERENCES
+ unique_lock& operator=(const unique_lock&) /*= delete*/;
+
+ unique_lock& operator=(unique_lock&& other)
+ {
+#else
+ unique_lock& operator=(const unique_lock& u)
+ {
+ // ugly const_cast to get around lack of rvalue references
+ unique_lock& other = const_cast<unique_lock&>(u);
+#endif
+ swap(other);
+ return *this;
+ }
+
+#ifdef USE_RVALUE_REFERENCES
+ unique_lock(const unique_lock&) /*= delete*/;
+
+ unique_lock(unique_lock&& other)
+ : pm(NULL), owns(false)
+ {
+#else
+ unique_lock(const unique_lock& u)
+ : pm(NULL), owns(false)
+ {
+ // ugly const_cast to get around lack of rvalue references
+ unique_lock& other = const_cast<unique_lock&>(u);
+#endif
+ swap(other);
+ }
+
+ void lock()
+ {
+ mutex()->lock();
+ owns = true;
+ }
+
+ bool try_lock()
+ {
+ owns = mutex()->try_lock();
+ return owns;
+ }
+
+ //template <class Rep, class Period>
+ //bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
+ //template <class Clock, class Duration>
+ //bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
+
+ void unlock()
+ {
+ mutex()->unlock();
+ owns = false;
+ }
+
+ void swap(unique_lock& u)
+ {
+ std::swap(pm, u.pm);
+ std::swap(owns, u.owns);
+ }
+
+ mutex_type* release()
+ {
+ auto const ret = mutex();
+
+ pm = NULL;
+ owns = false;
+
+ return ret;
+ }
+
+ bool owns_lock() const
+ {
+ return owns;
+ }
+
+ //explicit operator bool () const
+ //{
+ // return owns_lock();
+ //}
+
+ mutex_type* mutex() const
+ {
+ return pm;
+ }
+
+private:
+ mutex_type* pm;
+ bool owns;
+};
+
+template <class Mutex>
+void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y)
+{
+ x.swap(y);
+}
+
+}
+
+#endif
+#endif
diff --git a/src/common/src/std_thread.h b/src/common/src/std_thread.h
new file mode 100644
index 00000000..e43d2834
--- /dev/null
+++ b/src/common/src/std_thread.h
@@ -0,0 +1,317 @@
+
+#ifndef STD_THREAD_H_
+#define STD_THREAD_H_
+
+#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
+#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+
+#ifndef __has_include
+#define __has_include(s) 0
+#endif
+
+#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__
+// GCC 4.4 provides <thread>
+#ifndef _GLIBCXX_USE_SCHED_YIELD
+#define _GLIBCXX_USE_SCHED_YIELD
+#endif
+#include <thread>
+#elif __has_include(<thread>) && !ANDROID
+// Clang + libc++
+#include <thread>
+#else
+
+// partial std::thread implementation for win32/pthread
+
+#include <algorithm>
+
+#if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__)
+#define USE_RVALUE_REFERENCES
+#endif
+
+#ifdef __APPLE__
+#import <Foundation/NSAutoreleasePool.h>
+#endif
+
+#if defined(_WIN32)
+// WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#if defined(_MSC_VER) && defined(_MT)
+// When linking with LIBCMT (the multithreaded C library), Microsoft recommends
+// using _beginthreadex instead of CreateThread.
+#define USE_BEGINTHREADEX
+#include <process.h>
+#endif
+
+#ifdef USE_BEGINTHREADEX
+#define THREAD_ID unsigned
+#define THREAD_RETURN unsigned __stdcall
+#else
+#define THREAD_ID DWORD
+#define THREAD_RETURN DWORD WINAPI
+#endif
+#define THREAD_HANDLE HANDLE
+
+#else
+// PTHREAD
+
+#include <unistd.h>
+
+#ifndef _POSIX_THREADS
+#error unsupported platform (no pthreads?)
+#endif
+
+#include <pthread.h>
+
+#define THREAD_ID pthread_t
+#define THREAD_HANDLE pthread_t
+#define THREAD_RETURN void*
+
+#endif
+
+namespace std
+{
+
+class thread
+{
+public:
+ typedef THREAD_HANDLE native_handle_type;
+
+ class id
+ {
+ friend class thread;
+ public:
+ id() : m_thread(0) {}
+ id(THREAD_ID _id) : m_thread(_id) {}
+
+ bool operator==(const id& rhs) const
+ {
+ return m_thread == rhs.m_thread;
+ }
+
+ bool operator!=(const id& rhs) const
+ {
+ return !(*this == rhs);
+ }
+
+ bool operator<(const id& rhs) const
+ {
+ return m_thread < rhs.m_thread;
+ }
+
+ private:
+ THREAD_ID m_thread;
+ };
+
+ // no variadic template support in msvc
+ //template <typename C, typename... A>
+ //thread(C&& func, A&&... args);
+
+ template <typename C>
+ thread(C func)
+ {
+ StartThread(new Func<C>(func));
+ }
+
+ template <typename C, typename A>
+ thread(C func, A arg)
+ {
+ StartThread(new FuncArg<C, A>(func, arg));
+ }
+
+ thread() /*= default;*/ {}
+
+#ifdef USE_RVALUE_REFERENCES
+ thread(const thread&) /*= delete*/;
+
+ thread(thread&& other)
+ {
+#else
+ thread(const thread& t)
+ {
+ // ugly const_cast to get around lack of rvalue references
+ thread& other = const_cast<thread&>(t);
+#endif
+ swap(other);
+ }
+
+#ifdef USE_RVALUE_REFERENCES
+ thread& operator=(const thread&) /*= delete*/;
+
+ thread& operator=(thread&& other)
+ {
+#else
+ thread& operator=(const thread& t)
+ {
+ // ugly const_cast to get around lack of rvalue references
+ thread& other = const_cast<thread&>(t);
+#endif
+ if (joinable())
+ detach();
+ swap(other);
+ return *this;
+ }
+
+ ~thread()
+ {
+ if (joinable())
+ detach();
+ }
+
+ bool joinable() const
+ {
+ return m_id != id();
+ }
+
+ id get_id() const
+ {
+ return m_id;
+ }
+
+ native_handle_type native_handle()
+ {
+#ifdef _WIN32
+ return m_handle;
+#else
+ return m_id.m_thread;
+#endif
+ }
+
+ void join()
+ {
+#ifdef _WIN32
+ WaitForSingleObject(m_handle, INFINITE);
+ detach();
+#else
+ pthread_join(m_id.m_thread, NULL);
+ m_id = id();
+#endif
+ }
+
+ void detach()
+ {
+#ifdef _WIN32
+ CloseHandle(m_handle);
+#else
+ pthread_detach(m_id.m_thread);
+#endif
+ m_id = id();
+ }
+
+ void swap(thread& other)
+ {
+ std::swap(m_id, other.m_id);
+#ifdef _WIN32
+ std::swap(m_handle, other.m_handle);
+#endif
+ }
+
+ static unsigned hardware_concurrency()
+ {
+#ifdef _WIN32
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+ return static_cast<unsigned>(sysinfo.dwNumberOfProcessors);
+#else
+ return 0;
+#endif
+ }
+
+private:
+ id m_id;
+
+#ifdef _WIN32
+ native_handle_type m_handle;
+#endif
+
+ template <typename F>
+ void StartThread(F* param)
+ {
+#ifdef USE_BEGINTHREADEX
+ m_handle = (HANDLE)_beginthreadex(NULL, 0, &RunAndDelete<F>, param, 0, &m_id.m_thread);
+#elif defined(_WIN32)
+ m_handle = CreateThread(NULL, 0, &RunAndDelete<F>, param, 0, &m_id.m_thread);
+#else
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 1024 * 1024);
+ if (pthread_create(&m_id.m_thread, &attr, &RunAndDelete<F>, param))
+ m_id = id();
+#endif
+ }
+
+ template <typename C>
+ class Func
+ {
+ public:
+ Func(C _func) : func(_func) {}
+
+ void Run() { func(); }
+
+ private:
+ C const func;
+ };
+
+ template <typename C, typename A>
+ class FuncArg
+ {
+ public:
+ FuncArg(C _func, A _arg) : func(_func), arg(_arg) {}
+
+ void Run() { func(arg); }
+
+ private:
+ C const func;
+ A arg;
+ };
+
+ template <typename F>
+ static THREAD_RETURN RunAndDelete(void* param)
+ {
+#ifdef __APPLE__
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+#endif
+ static_cast<F*>(param)->Run();
+ delete static_cast<F*>(param);
+#ifdef __APPLE__
+ [pool release];
+#endif
+ return 0;
+ }
+};
+
+namespace this_thread
+{
+
+inline void yield()
+{
+#ifdef _WIN32
+ SwitchToThread();
+#else
+ sleep(0);
+#endif
+}
+
+inline thread::id get_id()
+{
+#ifdef _WIN32
+ return GetCurrentThreadId();
+#else
+ return pthread_self();
+#endif
+}
+
+} // namespace this_thread
+
+} // namespace std
+
+#undef USE_RVALUE_REFERENCES
+#undef USE_BEGINTHREADEX
+#undef THREAD_ID
+#undef THREAD_RETURN
+#undef THREAD_HANDLE
+
+#endif
+#endif
diff --git a/src/common/src/string_util.cpp b/src/common/src/string_util.cpp
new file mode 100644
index 00000000..ff4c5dbe
--- /dev/null
+++ b/src/common/src/string_util.cpp
@@ -0,0 +1,531 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <algorithm>
+
+#include "common.h"
+#include "common_paths.h"
+#include "string_util.h"
+
+#ifdef _WIN32
+ #include <Windows.h>
+#else
+ #include <iconv.h>
+ #include <errno.h>
+#endif
+
+// faster than sscanf
+bool AsciiToHex(const char* _szValue, u32& result)
+{
+ char *endptr = NULL;
+ const u32 value = strtoul(_szValue, &endptr, 16);
+
+ if (!endptr || *endptr)
+ return false;
+
+ result = value;
+ return true;
+}
+
+bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args)
+{
+ int writtenCount;
+
+#ifdef _WIN32
+ // You would think *printf are simple, right? Iterate on each character,
+ // if it's a format specifier handle it properly, etc.
+ //
+ // Nooooo. Not according to the C standard.
+ //
+ // According to the C99 standard (7.19.6.1 "The fprintf function")
+ // The format shall be a multibyte character sequence
+ //
+ // Because some character encodings might have '%' signs in the middle of
+ // a multibyte sequence (SJIS for example only specifies that the first
+ // byte of a 2 byte sequence is "high", the second byte can be anything),
+ // printf functions have to decode the multibyte sequences and try their
+ // best to not screw up.
+ //
+ // Unfortunately, on Windows, the locale for most languages is not UTF-8
+ // as we would need. Notably, for zh_TW, Windows chooses EUC-CN as the
+ // locale, and completely fails when trying to decode UTF-8 as EUC-CN.
+ //
+ // On the other hand, the fix is simple: because we use UTF-8, no such
+ // multibyte handling is required as we can simply assume that no '%' char
+ // will be present in the middle of a multibyte sequence.
+ //
+ // This is why we lookup an ANSI (cp1252) locale here and use _vsnprintf_l.
+ static locale_t c_locale = NULL;
+ if (!c_locale)
+ c_locale = _create_locale(LC_ALL, ".1252");
+ writtenCount = _vsnprintf_l(out, outsize, format, c_locale, args);
+#else
+ writtenCount = vsnprintf(out, outsize, format, args);
+#endif
+
+ if (writtenCount > 0 && writtenCount < outsize)
+ {
+ out[writtenCount] = '\0';
+ return true;
+ }
+ else
+ {
+ out[outsize - 1] = '\0';
+ return false;
+ }
+}
+
+std::string StringFromFormat(const char* format, ...)
+{
+ va_list args;
+ char *buf = NULL;
+#ifdef _WIN32
+ int required = 0;
+
+ va_start(args, format);
+ required = _vscprintf(format, args);
+ buf = new char[required + 1];
+ CharArrayFromFormatV(buf, required + 1, format, args);
+ va_end(args);
+
+ std::string temp = buf;
+ delete[] buf;
+#else
+ va_start(args, format);
+ if (vasprintf(&buf, format, args) < 0)
+ ERROR_LOG(COMMON, "Unable to allocate memory for string");
+ va_end(args);
+
+ std::string temp = buf;
+ free(buf);
+#endif
+ return temp;
+}
+
+// For Debugging. Read out an u8 array.
+std::string ArrayToString(const u8 *data, u32 size, int line_len, bool spaces)
+{
+ std::ostringstream oss;
+ oss << std::setfill('0') << std::hex;
+
+ for (int line = 0; size; ++data, --size)
+ {
+ oss << std::setw(2) << (int)*data;
+
+ if (line_len == ++line)
+ {
+ oss << '\n';
+ line = 0;
+ }
+ else if (spaces)
+ oss << ' ';
+ }
+
+ return oss.str();
+}
+
+// Turns " hej " into "hej". Also handles tabs.
+std::string StripSpaces(const std::string &str)
+{
+ const size_t s = str.find_first_not_of(" \t\r\n");
+
+ if (str.npos != s)
+ return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1);
+ else
+ return "";
+}
+
+// "\"hello\"" is turned to "hello"
+// This one assumes that the string has already been space stripped in both
+// ends, as done by StripSpaces above, for example.
+std::string StripQuotes(const std::string& s)
+{
+ if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
+ return s.substr(1, s.size() - 2);
+ else
+ return s;
+}
+
+bool TryParse(const std::string &str, u32 *const output)
+{
+ char *endptr = NULL;
+
+ // Reset errno to a value other than ERANGE
+ errno = 0;
+
+ unsigned long value = strtoul(str.c_str(), &endptr, 0);
+
+ if (!endptr || *endptr)
+ return false;
+
+ if (errno == ERANGE)
+ return false;
+
+#if ULONG_MAX > UINT_MAX
+ if (value >= 0x100000000ull
+ && value <= 0xFFFFFFFF00000000ull)
+ return false;
+#endif
+
+ *output = static_cast<u32>(value);
+ return true;
+}
+
+bool TryParse(const std::string &str, bool *const output)
+{
+ if ("1" == str || !strcasecmp("true", str.c_str()))
+ *output = true;
+ else if ("0" == str || !strcasecmp("false", str.c_str()))
+ *output = false;
+ else
+ return false;
+
+ return true;
+}
+
+std::string StringFromInt(int value)
+{
+ char temp[16];
+ sprintf(temp, "%i", value);
+ return temp;
+}
+
+std::string StringFromBool(bool value)
+{
+ return value ? "True" : "False";
+}
+
+bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension)
+{
+ if (full_path.empty())
+ return false;
+
+ size_t dir_end = full_path.find_last_of("/"
+ // windows needs the : included for something like just "C:" to be considered a directory
+#ifdef _WIN32
+ ":"
+#endif
+ );
+ if (std::string::npos == dir_end)
+ dir_end = 0;
+ else
+ dir_end += 1;
+
+ size_t fname_end = full_path.rfind('.');
+ if (fname_end < dir_end || std::string::npos == fname_end)
+ fname_end = full_path.size();
+
+ if (_pPath)
+ *_pPath = full_path.substr(0, dir_end);
+
+ if (_pFilename)
+ *_pFilename = full_path.substr(dir_end, fname_end - dir_end);
+
+ if (_pExtension)
+ *_pExtension = full_path.substr(fname_end);
+
+ return true;
+}
+
+void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename)
+{
+ _CompleteFilename = _Path;
+
+ // check for seperator
+ if (DIR_SEP_CHR != *_CompleteFilename.rbegin())
+ _CompleteFilename += DIR_SEP_CHR;
+
+ // add the filename
+ _CompleteFilename += _Filename;
+}
+
+void SplitString(const std::string& str, const char delim, std::vector<std::string>& output)
+{
+ std::istringstream iss(str);
+ output.resize(1);
+
+ while (std::getline(iss, *output.rbegin(), delim))
+ output.push_back("");
+
+ output.pop_back();
+}
+
+std::string TabsToSpaces(int tab_size, const std::string &in)
+{
+ const std::string spaces(tab_size, ' ');
+ std::string out(in);
+
+ size_t i = 0;
+ while (out.npos != (i = out.find('\t')))
+ out.replace(i, 1, spaces);
+
+ return out;
+}
+
+std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest)
+{
+ while(1)
+ {
+ size_t pos = result.find(src);
+ if (pos == std::string::npos) break;
+ result.replace(pos, src.size(), dest);
+ }
+ return result;
+}
+
+// UriDecode and UriEncode are from http://www.codeguru.com/cpp/cpp/string/conversions/print.php/c12759
+// by jinq0123 (November 2, 2006)
+
+// Uri encode and decode.
+// RFC1630, RFC1738, RFC2396
+
+//#include <string>
+//#include <assert.h>
+
+const char HEX2DEC[256] =
+{
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ /* 0 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+ /* 1 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+ /* 2 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+ /* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,16,16, 16,16,16,16,
+
+ /* 4 */ 16,10,11,12, 13,14,15,16, 16,16,16,16, 16,16,16,16,
+ /* 5 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+ /* 6 */ 16,10,11,12, 13,14,15,16, 16,16,16,16, 16,16,16,16,
+ /* 7 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+
+ /* 8 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+ /* 9 */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+ /* A */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+ /* B */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+
+ /* C */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+ /* D */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+ /* E */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16,
+ /* F */ 16,16,16,16, 16,16,16,16, 16,16,16,16, 16,16,16,16
+};
+
+std::string UriDecode(const std::string & sSrc)
+{
+ // Note from RFC1630: "Sequences which start with a percent sign
+ // but are not followed by two hexadecimal characters (0-9, A-F) are reserved
+ // for future extension"
+
+ const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
+ const size_t SRC_LEN = sSrc.length();
+ const unsigned char * const SRC_END = pSrc + SRC_LEN;
+ const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%'
+
+ char * const pStart = new char[SRC_LEN];
+ char * pEnd = pStart;
+
+ while (pSrc < SRC_LAST_DEC)
+ {
+ if (*pSrc == '%')
+ {
+ char dec1, dec2;
+ if (16 != (dec1 = HEX2DEC[*(pSrc + 1)])
+ && 16 != (dec2 = HEX2DEC[*(pSrc + 2)]))
+ {
+ *pEnd++ = (dec1 << 4) + dec2;
+ pSrc += 3;
+ continue;
+ }
+ }
+
+ *pEnd++ = *pSrc++;
+ }
+
+ // the last 2- chars
+ while (pSrc < SRC_END)
+ *pEnd++ = *pSrc++;
+
+ std::string sResult(pStart, pEnd);
+ delete [] pStart;
+ return sResult;
+}
+
+// Only alphanum is safe.
+const char SAFE[256] =
+{
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ /* 0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ /* 1 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ /* 2 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ /* 3 */ 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0,
+
+ /* 4 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
+ /* 5 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
+ /* 6 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
+ /* 7 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
+
+ /* 8 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ /* 9 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ /* A */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ /* B */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+
+ /* C */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ /* D */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ /* E */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
+ /* F */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
+};
+
+std::string UriEncode(const std::string & sSrc)
+{
+ const char DEC2HEX[16 + 1] = "0123456789ABCDEF";
+ const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
+ const size_t SRC_LEN = sSrc.length();
+ unsigned char * const pStart = new unsigned char[SRC_LEN * 3];
+ unsigned char * pEnd = pStart;
+ const unsigned char * const SRC_END = pSrc + SRC_LEN;
+
+ for (; pSrc < SRC_END; ++pSrc)
+ {
+ if (SAFE[*pSrc])
+ *pEnd++ = *pSrc;
+ else
+ {
+ // escape this char
+ *pEnd++ = '%';
+ *pEnd++ = DEC2HEX[*pSrc >> 4];
+ *pEnd++ = DEC2HEX[*pSrc & 0x0F];
+ }
+ }
+
+ std::string sResult((char *)pStart, (char *)pEnd);
+ delete [] pStart;
+ return sResult;
+}
+
+#ifdef _WIN32
+
+std::string UTF16ToUTF8(const std::wstring& input)
+{
+ auto const size = WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), nullptr, 0, nullptr, nullptr);
+
+ std::string output;
+ output.resize(size);
+
+ if (size == 0 || size != WideCharToMultiByte(CP_UTF8, 0, input.data(), input.size(), &output[0], output.size(), nullptr, nullptr))
+ output.clear();
+
+ return output;
+}
+
+std::wstring CPToUTF16(u32 code_page, const std::string& input)
+{
+ auto const size = MultiByteToWideChar(code_page, 0, input.data(), input.size(), nullptr, 0);
+
+ std::wstring output;
+ output.resize(size);
+
+ if (size == 0 || size != MultiByteToWideChar(code_page, 0, input.data(), input.size(), &output[0], output.size()))
+ output.clear();
+
+ return output;
+}
+
+std::wstring UTF8ToUTF16(const std::string& input)
+{
+ return CPToUTF16(CP_UTF8, input);
+}
+
+std::string SHIFTJISToUTF8(const std::string& input)
+{
+ return UTF16ToUTF8(CPToUTF16(932, input));
+}
+
+std::string CP1252ToUTF8(const std::string& input)
+{
+ return UTF16ToUTF8(CPToUTF16(1252, input));
+}
+
+#else
+
+template <typename T>
+std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input)
+{
+ std::string result;
+
+ iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
+ if ((iconv_t)-1 == conv_desc)
+ {
+ ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno));
+ }
+ else
+ {
+ size_t const in_bytes = sizeof(T) * input.size();
+ size_t const out_buffer_size = 4 * in_bytes;
+
+ std::string out_buffer;
+ out_buffer.resize(out_buffer_size);
+
+ auto src_buffer = &input[0];
+ size_t src_bytes = in_bytes;
+ auto dst_buffer = &out_buffer[0];
+ size_t dst_bytes = out_buffer.size();
+
+ while (src_bytes != 0)
+ {
+ size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes,
+ &dst_buffer, &dst_bytes);
+
+ if ((size_t)-1 == iconv_result)
+ {
+ if (EILSEQ == errno || EINVAL == errno)
+ {
+ // Try to skip the bad character
+ if (src_bytes != 0)
+ {
+ --src_bytes;
+ ++src_buffer;
+ }
+ }
+ else
+ {
+ ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno));
+ break;
+ }
+ }
+ }
+
+ out_buffer.resize(out_buffer_size - dst_bytes);
+ out_buffer.swap(result);
+
+ iconv_close(conv_desc);
+ }
+
+ return result;
+}
+
+std::string CP1252ToUTF8(const std::string& input)
+{
+ //return CodeToUTF8("CP1252//TRANSLIT", input);
+ //return CodeToUTF8("CP1252//IGNORE", input);
+ return CodeToUTF8("CP1252", input);
+}
+
+std::string SHIFTJISToUTF8(const std::string& input)
+{
+ //return CodeToUTF8("CP932", input);
+ return CodeToUTF8("SJIS", input);
+}
+
+std::string UTF16ToUTF8(const std::wstring& input)
+{
+ std::string result =
+ // CodeToUTF8("UCS-2", input);
+ // CodeToUTF8("UCS-2LE", input);
+ // CodeToUTF8("UTF-16", input);
+ CodeToUTF8("UTF-16LE", input);
+
+ // TODO: why is this needed?
+ result.erase(std::remove(result.begin(), result.end(), 0x00), result.end());
+ return result;
+}
+
+#endif
diff --git a/src/common/src/string_util.h b/src/common/src/string_util.h
new file mode 100644
index 00000000..31eaeb24
--- /dev/null
+++ b/src/common/src/string_util.h
@@ -0,0 +1,111 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _STRINGUTIL_H_
+#define _STRINGUTIL_H_
+
+#include <stdarg.h>
+
+#include <vector>
+#include <string>
+#include <sstream>
+#include <iomanip>
+
+#include "common.h"
+
+std::string StringFromFormat(const char* format, ...);
+// Cheap!
+bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args);
+
+template<size_t Count>
+inline void CharArrayFromFormat(char (& out)[Count], const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ CharArrayFromFormatV(out, Count, format, args);
+ va_end(args);
+}
+
+// Good
+std::string ArrayToString(const u8 *data, u32 size, int line_len = 20, bool spaces = true);
+
+std::string StripSpaces(const std::string &s);
+std::string StripQuotes(const std::string &s);
+
+// Thousand separator. Turns 12345678 into 12,345,678
+template <typename I>
+std::string ThousandSeparate(I value, int spaces = 0)
+{
+ std::ostringstream oss;
+
+// std::locale("") seems to be broken on many platforms
+#if defined _WIN32 || (defined __linux__ && !defined __clang__)
+ oss.imbue(std::locale(""));
+#endif
+ oss << std::setw(spaces) << value;
+
+ return oss.str();
+}
+
+std::string StringFromInt(int value);
+std::string StringFromBool(bool value);
+
+bool TryParse(const std::string &str, bool *output);
+bool TryParse(const std::string &str, u32 *output);
+
+template <typename N>
+static bool TryParse(const std::string &str, N *const output)
+{
+ std::istringstream iss(str);
+
+ N tmp = 0;
+ if (iss >> tmp)
+ {
+ *output = tmp;
+ return true;
+ }
+ else
+ return false;
+}
+
+// TODO: kill this
+bool AsciiToHex(const char* _szValue, u32& result);
+
+std::string TabsToSpaces(int tab_size, const std::string &in);
+
+void SplitString(const std::string& str, char delim, std::vector<std::string>& output);
+
+// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
+bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension);
+
+void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename);
+std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest);
+std::string UriDecode(const std::string & sSrc);
+std::string UriEncode(const std::string & sSrc);
+
+std::string CP1252ToUTF8(const std::string& str);
+std::string SHIFTJISToUTF8(const std::string& str);
+std::string UTF16ToUTF8(const std::wstring& str);
+
+#ifdef _WIN32
+
+std::wstring UTF8ToUTF16(const std::string& str);
+
+#ifdef _UNICODE
+inline std::string TStrToUTF8(const std::wstring& str)
+{ return UTF16ToUTF8(str); }
+
+inline std::wstring UTF8ToTStr(const std::string& str)
+{ return UTF8ToUTF16(str); }
+#else
+inline std::string TStrToUTF8(const std::string& str)
+{ return str; }
+
+inline std::string UTF8ToTStr(const std::string& str)
+{ return str; }
+#endif
+
+#endif
+
+#endif // _STRINGUTIL_H_
diff --git a/src/common/src/thread.cpp b/src/common/src/thread.cpp
new file mode 100644
index 00000000..e75dfffe
--- /dev/null
+++ b/src/common/src/thread.cpp
@@ -0,0 +1,133 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "thread.h"
+#include "common.h"
+
+#ifdef __APPLE__
+#include <mach/mach.h>
+#elif defined BSD4_4
+#include <pthread_np.h>
+#endif
+
+#ifdef USE_BEGINTHREADEX
+#include <process.h>
+#endif
+
+namespace Common
+{
+
+int CurrentThreadId()
+{
+#ifdef _WIN32
+ return GetCurrentThreadId();
+#elif defined __APPLE__
+ return mach_thread_self();
+#else
+ return 0;
+#endif
+}
+
+#ifdef _WIN32
+
+void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask)
+{
+ SetThreadAffinityMask(thread, mask);
+}
+
+void SetCurrentThreadAffinity(u32 mask)
+{
+ SetThreadAffinityMask(GetCurrentThread(), mask);
+}
+
+// Supporting functions
+void SleepCurrentThread(int ms)
+{
+ Sleep(ms);
+}
+
+void SwitchCurrentThread()
+{
+ SwitchToThread();
+}
+
+// Sets the debugger-visible name of the current thread.
+// Uses undocumented (actually, it is now documented) trick.
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
+
+// This is implemented much nicer in upcoming msvc++, see:
+// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
+void SetCurrentThreadName(const char* szThreadName)
+{
+ static const DWORD MS_VC_EXCEPTION = 0x406D1388;
+
+ #pragma pack(push,8)
+ struct THREADNAME_INFO
+ {
+ DWORD dwType; // must be 0x1000
+ LPCSTR szName; // pointer to name (in user addr space)
+ DWORD dwThreadID; // thread ID (-1=caller thread)
+ DWORD dwFlags; // reserved for future use, must be zero
+ } info;
+ #pragma pack(pop)
+
+ info.dwType = 0x1000;
+ info.szName = szThreadName;
+ info.dwThreadID = -1; //dwThreadID;
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {}
+}
+
+#else // !WIN32, so must be POSIX threads
+
+void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask)
+{
+#ifdef __APPLE__
+ thread_policy_set(pthread_mach_thread_np(thread),
+ THREAD_AFFINITY_POLICY, (integer_t *)&mask, 1);
+#elif (defined __linux__ || defined BSD4_4) && !(defined ANDROID)
+ cpu_set_t cpu_set;
+ CPU_ZERO(&cpu_set);
+
+ for (int i = 0; i != sizeof(mask) * 8; ++i)
+ if ((mask >> i) & 1)
+ CPU_SET(i, &cpu_set);
+
+ pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set);
+#endif
+}
+
+void SetCurrentThreadAffinity(u32 mask)
+{
+ SetThreadAffinity(pthread_self(), mask);
+}
+
+void SleepCurrentThread(int ms)
+{
+ usleep(1000 * ms);
+}
+
+void SwitchCurrentThread()
+{
+ usleep(1000 * 1);
+}
+
+void SetCurrentThreadName(const char* szThreadName)
+{
+#ifdef __APPLE__
+ pthread_setname_np(szThreadName);
+#else
+ pthread_setname_np(pthread_self(), szThreadName);
+#endif
+}
+
+#endif
+
+} // namespace Common
diff --git a/src/common/src/thread.h b/src/common/src/thread.h
new file mode 100644
index 00000000..3178c40f
--- /dev/null
+++ b/src/common/src/thread.h
@@ -0,0 +1,156 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _THREAD_H_
+#define _THREAD_H_
+
+#include "std_condition_variable.h"
+#include "std_mutex.h"
+#include "std_thread.h"
+
+// Don't include common.h here as it will break LogManager
+#include "common_types.h"
+#include <stdio.h>
+#include <string.h>
+
+// This may not be defined outside _WIN32
+#ifndef _WIN32
+#ifndef INFINITE
+#define INFINITE 0xffffffff
+#endif
+
+//for gettimeofday and struct time(spec|val)
+#include <time.h>
+#include <sys/time.h>
+#endif
+
+namespace Common
+{
+
+int CurrentThreadId();
+
+void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask);
+void SetCurrentThreadAffinity(u32 mask);
+
+class Event
+{
+public:
+ Event()
+ : is_set(false)
+ {};
+
+ void Set()
+ {
+ std::lock_guard<std::mutex> lk(m_mutex);
+ if (!is_set)
+ {
+ is_set = true;
+ m_condvar.notify_one();
+ }
+ }
+
+ void Wait()
+ {
+ std::unique_lock<std::mutex> lk(m_mutex);
+ m_condvar.wait(lk, IsSet(this));
+ is_set = false;
+ }
+
+ void Reset()
+ {
+ std::unique_lock<std::mutex> lk(m_mutex);
+ // no other action required, since wait loops on the predicate and any lingering signal will get cleared on the first iteration
+ is_set = false;
+ }
+
+private:
+ class IsSet
+ {
+ public:
+ IsSet(const Event* ev)
+ : m_event(ev)
+ {}
+
+ bool operator()()
+ {
+ return m_event->is_set;
+ }
+
+ private:
+ const Event* const m_event;
+ };
+
+ volatile bool is_set;
+ std::condition_variable m_condvar;
+ std::mutex m_mutex;
+};
+
+// TODO: doesn't work on windows with (count > 2)
+class Barrier
+{
+public:
+ Barrier(size_t count)
+ : m_count(count), m_waiting(0)
+ {}
+
+ // block until "count" threads call Sync()
+ bool Sync()
+ {
+ std::unique_lock<std::mutex> lk(m_mutex);
+
+ // TODO: broken when next round of Sync()s
+ // is entered before all waiting threads return from the notify_all
+
+ if (m_count == ++m_waiting)
+ {
+ m_waiting = 0;
+ m_condvar.notify_all();
+ return true;
+ }
+ else
+ {
+ m_condvar.wait(lk, IsDoneWating(this));
+ return false;
+ }
+ }
+
+private:
+ class IsDoneWating
+ {
+ public:
+ IsDoneWating(const Barrier* bar)
+ : m_bar(bar)
+ {}
+
+ bool operator()()
+ {
+ return (0 == m_bar->m_waiting);
+ }
+
+ private:
+ const Barrier* const m_bar;
+ };
+
+ std::condition_variable m_condvar;
+ std::mutex m_mutex;
+ const size_t m_count;
+ volatile size_t m_waiting;
+};
+
+void SleepCurrentThread(int ms);
+void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
+
+// Use this function during a spin-wait to make the current thread
+// relax while another thread is working. This may be more efficient
+// than using events because event functions use kernel calls.
+inline void YieldCPU()
+{
+ std::this_thread::yield();
+}
+
+void SetCurrentThreadName(const char *name);
+
+} // namespace Common
+
+#endif // _THREAD_H_
diff --git a/src/common/src/thunk.h b/src/common/src/thunk.h
new file mode 100644
index 00000000..4b64a9b0
--- /dev/null
+++ b/src/common/src/thunk.h
@@ -0,0 +1,46 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _THUNK_H_
+#define _THUNK_H_
+
+#include <map>
+
+#include "common.h"
+#include "x64Emitter.h"
+
+// This simple class creates a wrapper around a C/C++ function that saves all fp state
+// before entering it, and restores it upon exit. This is required to be able to selectively
+// call functions from generated code, without inflicting the performance hit and increase
+// of complexity that it means to protect the generated code from this problem.
+
+// This process is called thunking.
+
+// There will only ever be one level of thunking on the stack, plus,
+// we don't want to pollute the stack, so we store away regs somewhere global.
+// NOT THREAD SAFE. This may only be used from the CPU thread.
+// Any other thread using this stuff will be FATAL.
+
+class ThunkManager : public Gen::XCodeBlock
+{
+ std::map<void *, const u8 *> thunks;
+
+ const u8 *save_regs;
+ const u8 *load_regs;
+
+public:
+ ThunkManager() {
+ Init();
+ }
+ ~ThunkManager() {
+ Shutdown();
+ }
+ void *ProtectFunction(void *function, int num_params);
+private:
+ void Init();
+ void Shutdown();
+ void Reset();
+};
+
+#endif // _THUNK_H_
diff --git a/src/common/src/timer.cpp b/src/common/src/timer.cpp
new file mode 100644
index 00000000..0300133c
--- /dev/null
+++ b/src/common/src/timer.cpp
@@ -0,0 +1,226 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <time.h>
+
+#ifdef _WIN32
+#include <Windows.h>
+#include <mmsystem.h>
+#include <sys/timeb.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include "common.h"
+#include "timer.h"
+#include "string_util.h"
+
+namespace Common
+{
+
+u32 Timer::GetTimeMs()
+{
+#ifdef _WIN32
+ return timeGetTime();
+#else
+ struct timeval t;
+ (void)gettimeofday(&t, NULL);
+ return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000));
+#endif
+}
+
+// --------------------------------------------
+// Initiate, Start, Stop, and Update the time
+// --------------------------------------------
+
+// Set initial values for the class
+Timer::Timer()
+ : m_LastTime(0), m_StartTime(0), m_Running(false)
+{
+ Update();
+}
+
+// Write the starting time
+void Timer::Start()
+{
+ m_StartTime = GetTimeMs();
+ m_Running = true;
+}
+
+// Stop the timer
+void Timer::Stop()
+{
+ // Write the final time
+ m_LastTime = GetTimeMs();
+ m_Running = false;
+}
+
+// Update the last time variable
+void Timer::Update()
+{
+ m_LastTime = GetTimeMs();
+ //TODO(ector) - QPF
+}
+
+// -------------------------------------
+// Get time difference and elapsed time
+// -------------------------------------
+
+// Get the number of milliseconds since the last Update()
+u64 Timer::GetTimeDifference()
+{
+ return GetTimeMs() - m_LastTime;
+}
+
+// Add the time difference since the last Update() to the starting time.
+// This is used to compensate for a paused game.
+void Timer::AddTimeDifference()
+{
+ m_StartTime += GetTimeDifference();
+}
+
+// Get the time elapsed since the Start()
+u64 Timer::GetTimeElapsed()
+{
+ // If we have not started yet, return 1 (because then I don't
+ // have to change the FPS calculation in CoreRerecording.cpp .
+ if (m_StartTime == 0) return 1;
+
+ // Return the final timer time if the timer is stopped
+ if (!m_Running) return (m_LastTime - m_StartTime);
+
+ return (GetTimeMs() - m_StartTime);
+}
+
+// Get the formatted time elapsed since the Start()
+std::string Timer::GetTimeElapsedFormatted() const
+{
+ // If we have not started yet, return zero
+ if (m_StartTime == 0)
+ return "00:00:00:000";
+
+ // The number of milliseconds since the start.
+ // Use a different value if the timer is stopped.
+ u64 Milliseconds;
+ if (m_Running)
+ Milliseconds = GetTimeMs() - m_StartTime;
+ else
+ Milliseconds = m_LastTime - m_StartTime;
+ // Seconds
+ u32 Seconds = (u32)(Milliseconds / 1000);
+ // Minutes
+ u32 Minutes = Seconds / 60;
+ // Hours
+ u32 Hours = Minutes / 60;
+
+ std::string TmpStr = StringFromFormat("%02i:%02i:%02i:%03i",
+ Hours, Minutes % 60, Seconds % 60, Milliseconds % 1000);
+ return TmpStr;
+}
+
+// Get current time
+void Timer::IncreaseResolution()
+{
+#ifdef _WIN32
+ timeBeginPeriod(1);
+#endif
+}
+
+void Timer::RestoreResolution()
+{
+#ifdef _WIN32
+ timeEndPeriod(1);
+#endif
+}
+
+// Get the number of seconds since January 1 1970
+u64 Timer::GetTimeSinceJan1970()
+{
+ time_t ltime;
+ time(&ltime);
+ return((u64)ltime);
+}
+
+u64 Timer::GetLocalTimeSinceJan1970()
+{
+ time_t sysTime, tzDiff, tzDST;
+ struct tm * gmTime;
+
+ time(&sysTime);
+
+ // Account for DST where needed
+ gmTime = localtime(&sysTime);
+ if(gmTime->tm_isdst == 1)
+ tzDST = 3600;
+ else
+ tzDST = 0;
+
+ // Lazy way to get local time in sec
+ gmTime = gmtime(&sysTime);
+ tzDiff = sysTime - mktime(gmTime);
+
+ return (u64)(sysTime + tzDiff + tzDST);
+}
+
+// Return the current time formatted as Minutes:Seconds:Milliseconds
+// in the form 00:00:000.
+std::string Timer::GetTimeFormatted()
+{
+ time_t sysTime;
+ struct tm * gmTime;
+ char formattedTime[13];
+ char tmp[13];
+
+ time(&sysTime);
+ gmTime = localtime(&sysTime);
+
+ strftime(tmp, 6, "%M:%S", gmTime);
+
+ // Now tack on the milliseconds
+#ifdef _WIN32
+ struct timeb tp;
+ (void)::ftime(&tp);
+ sprintf(formattedTime, "%s:%03i", tmp, tp.millitm);
+#else
+ struct timeval t;
+ (void)gettimeofday(&t, NULL);
+ sprintf(formattedTime, "%s:%03d", tmp, (int)(t.tv_usec / 1000));
+#endif
+
+ return std::string(formattedTime);
+}
+
+// Returns a timestamp with decimals for precise time comparisons
+// ----------------
+double Timer::GetDoubleTime()
+{
+#ifdef _WIN32
+ struct timeb tp;
+ (void)::ftime(&tp);
+#else
+ struct timeval t;
+ (void)gettimeofday(&t, NULL);
+#endif
+ // Get continuous timestamp
+ u64 TmpSeconds = Common::Timer::GetTimeSinceJan1970();
+
+ // Remove a few years. We only really want enough seconds to make
+ // sure that we are detecting actual actions, perhaps 60 seconds is
+ // enough really, but I leave a year of seconds anyway, in case the
+ // user's clock is incorrect or something like that.
+ TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60);
+
+ // Make a smaller integer that fits in the double
+ u32 Seconds = (u32)TmpSeconds;
+#ifdef _WIN32
+ double ms = tp.millitm / 1000.0 / 1000.0;
+#else
+ double ms = t.tv_usec / 1000000.0;
+#endif
+ double TmpTime = Seconds + ms;
+
+ return TmpTime;
+}
+
+} // Namespace Common
diff --git a/src/common/src/timer.h b/src/common/src/timer.h
new file mode 100644
index 00000000..9ee5719a
--- /dev/null
+++ b/src/common/src/timer.h
@@ -0,0 +1,46 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#ifndef _TIMER_H_
+#define _TIMER_H_
+
+#include "common.h"
+#include <string>
+
+namespace Common
+{
+class Timer
+{
+public:
+ Timer();
+
+ void Start();
+ void Stop();
+ void Update();
+
+ // The time difference is always returned in milliseconds, regardless of alternative internal representation
+ u64 GetTimeDifference();
+ void AddTimeDifference();
+
+ static void IncreaseResolution();
+ static void RestoreResolution();
+ static u64 GetTimeSinceJan1970();
+ static u64 GetLocalTimeSinceJan1970();
+ static double GetDoubleTime();
+
+ static std::string GetTimeFormatted();
+ std::string GetTimeElapsedFormatted() const;
+ u64 GetTimeElapsed();
+
+ static u32 GetTimeMs();
+
+private:
+ u64 m_LastTime;
+ u64 m_StartTime;
+ bool m_Running;
+};
+
+} // Namespace Common
+
+#endif // _TIMER_H_
diff --git a/src/common/src/version.cpp b/src/common/src/version.cpp
new file mode 100644
index 00000000..47d7b52a
--- /dev/null
+++ b/src/common/src/version.cpp
@@ -0,0 +1,45 @@
+// Copyright 2013 Dolphin Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include "common.h"
+#include "scm_rev.h"
+
+#ifdef _DEBUG
+ #define BUILD_TYPE_STR "Debug "
+#elif defined DEBUGFAST
+ #define BUILD_TYPE_STR "DebugFast "
+#else
+ #define BUILD_TYPE_STR ""
+#endif
+
+const char *scm_rev_str = "Dolphin "
+#if !SCM_IS_MASTER
+ "[" SCM_BRANCH_STR "] "
+#endif
+
+#ifdef __INTEL_COMPILER
+ BUILD_TYPE_STR SCM_DESC_STR "-ICC";
+#else
+ BUILD_TYPE_STR SCM_DESC_STR;
+#endif
+
+#ifdef _M_X64
+#define NP_ARCH "x64"
+#else
+#ifdef _M_ARM
+#define NP_ARCH "ARM"
+#else
+#define NP_ARCH "x86"
+#endif
+#endif
+
+#ifdef _WIN32
+const char *netplay_dolphin_ver = SCM_DESC_STR " W" NP_ARCH;
+#elif __APPLE__
+const char *netplay_dolphin_ver = SCM_DESC_STR " M" NP_ARCH;
+#else
+const char *netplay_dolphin_ver = SCM_DESC_STR " L" NP_ARCH;
+#endif
+
+const char *scm_rev_git_str = SCM_REV_STR;