54 changed files with 8640 additions and 163 deletions
-
56akiru.sln
-
185src/common/common.vcxproj
-
68src/common/common.vcxproj.filters
-
19src/common/src/atomic.h
-
113src/common/src/atomic_gcc.h
-
72src/common/src/atomic_win32.h
-
203src/common/src/break_points.cpp
-
102src/common/src/break_points.h
-
384src/common/src/chunk_file.h
-
164src/common/src/common.h
-
243src/common/src/common_funcs.h
-
71src/common/src/common_paths.h
-
48src/common/src/common_types.h
-
337src/common/src/console_listener.cpp
-
41src/common/src/console_listener.h
-
81src/common/src/cpu_detect.h
-
39src/common/src/debug_interface.h
-
433src/common/src/extended_trace.cpp
-
53src/common/src/extended_trace.h
-
115src/common/src/fifo_queue.h
-
106src/common/src/file_search.cpp
-
28src/common/src/file_search.h
-
922src/common/src/file_util.cpp
-
232src/common/src/file_util.h
-
75src/common/src/fixed_size_queue.h
-
520src/common/src/hash.cpp
-
20src/common/src/hash.h
-
191src/common/src/linear_disk_cache.h
-
155src/common/src/log.h
-
199src/common/src/log_manager.cpp
-
169src/common/src/log_manager.h
-
212src/common/src/math_util.cpp
-
200src/common/src/math_util.h
-
304src/common/src/mem_arena.cpp
-
58src/common/src/mem_arena.h
-
197src/common/src/memory_util.cpp
-
25src/common/src/memory_util.h
-
33src/common/src/misc.cpp
-
107src/common/src/msg_handler.cpp
-
73src/common/src/msg_handler.h
-
4src/common/src/scm_rev.h
-
170src/common/src/std_condition_variable.h
-
365src/common/src/std_mutex.h
-
317src/common/src/std_thread.h
-
531src/common/src/string_util.cpp
-
111src/common/src/string_util.h
-
133src/common/src/thread.cpp
-
156src/common/src/thread.h
-
46src/common/src/thunk.h
-
226src/common/src/timer.cpp
-
46src/common/src/timer.h
-
45src/common/src/version.cpp
-
0vsprops/code_generation_debug.props
-
0vsprops/code_generation_release.props
@ -1,35 +1,59 @@ |
|||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
<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\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> |
||||
<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\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\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_condition_variable.h" /> |
||||
<ClInclude Include="src\std_mutex.h" /> |
<ClInclude Include="src\std_mutex.h" /> |
||||
<ClInclude Include="src\std_thread.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> |
</ItemGroup> |
||||
</Project> |
</Project> |
||||
@ -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 |
||||
@ -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 |
||||
@ -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 |
||||
@ -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(); |
||||
|
} |
||||
|
} |
||||
@ -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 |
||||
|
|
||||
@ -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_ |
||||
@ -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_ |
||||
@ -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_ |
||||
@ -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_ |
||||
@ -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_ |
||||
@ -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
|
||||
|
} |
||||
|
|
||||
|
|
||||
@ -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 |
||||
@ -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_ |
||||
@ -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 |
||||
@ -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
|
||||
@ -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_ |
||||
@ -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 |
||||
@ -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; |
||||
|
} |
||||
@ -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_ |
||||
|
|
||||
@ -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
|
||||
@ -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 |
||||
@ -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_ |
||||
|
|
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
@ -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_ |
||||
@ -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 |
||||
@ -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_ |
||||
@ -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
|
||||
|
} |
||||
@ -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_ |
||||
@ -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); |
||||
|
} |
||||
|
|
||||
@ -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_ |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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_ |
||||
@ -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
|
||||
|
} |
||||
@ -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 |
||||
@ -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;
|
||||
|
//}
|
||||
@ -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; |
||||
|
} |
||||
|
|
||||
@ -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_ |
||||
@ -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 |
||||
@ -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 |
||||
@ -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 |
||||
@ -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 |
||||
@ -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
|
||||
@ -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_ |
||||
@ -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
|
||||
@ -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_ |
||||
@ -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_ |
||||
@ -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(<ime); |
||||
|
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
|
||||
@ -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_ |
||||
@ -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; |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue