diff --git a/akiru.sln b/akiru.sln index 47fbc7bf96..c8bf300004 100644 --- a/akiru.sln +++ b/akiru.sln @@ -1,23 +1,7 @@ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "akiru_qt", "src\akiru_qt\akiru_qt.vcxproj", "{A587F714-490F-407A-9E36-7AB7FA0D7BAB}" - ProjectSection(ProjectDependencies) = postProject - {8AEA7F29-3466-4786-A10D-6A4BD0610977} = {8AEA7F29-3466-4786-A10D-6A4BD0610977} - {6678D1A3-33A6-48A9-878B-48E5D2903D27} = {6678D1A3-33A6-48A9-878B-48E5D2903D27} - {E34D07D0-7DE6-4C46-A00D-C20C691789E4} = {E34D07D0-7DE6-4C46-A00D-C20C691789E4} - {DFE335FC-755D-4BAA-8452-94434F8A1EDB} = {DFE335FC-755D-4BAA-8452-94434F8A1EDB} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core", "src\core\core.vcxproj", "{8AEA7F29-3466-4786-A10D-6A4BD0610977}" - ProjectSection(ProjectDependencies) = postProject - {6678D1A3-33A6-48A9-878B-48E5D2903D27} = {6678D1A3-33A6-48A9-878B-48E5D2903D27} - {E34D07D0-7DE6-4C46-A00D-C20C691789E4} = {E34D07D0-7DE6-4C46-A00D-C20C691789E4} - EndProjectSection -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "src\common\common.vcxproj", "{DFE335FC-755D-4BAA-8452-94434F8A1EDB}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "akiru", "src\akiru\akiru.vcxproj", "{CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -26,22 +10,6 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Debug|Win32.ActiveCfg = Debug|Win32 - {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Debug|Win32.Build.0 = Debug|Win32 - {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Debug|x64.ActiveCfg = Debug|x64 - {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Debug|x64.Build.0 = Debug|x64 - {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Release|Win32.ActiveCfg = Release|Win32 - {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Release|Win32.Build.0 = Release|Win32 - {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Release|x64.ActiveCfg = Release|x64 - {A587F714-490F-407A-9E36-7AB7FA0D7BAB}.Release|x64.Build.0 = Release|x64 - {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Debug|Win32.ActiveCfg = Debug|Win32 - {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Debug|Win32.Build.0 = Debug|Win32 - {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Debug|x64.ActiveCfg = Debug|x64 - {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Debug|x64.Build.0 = Debug|x64 - {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Release|Win32.ActiveCfg = Release|Win32 - {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Release|Win32.Build.0 = Release|Win32 - {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Release|x64.ActiveCfg = Release|x64 - {8AEA7F29-3466-4786-A10D-6A4BD0610977}.Release|x64.Build.0 = Release|x64 {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Debug|Win32.ActiveCfg = Debug|Win32 {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Debug|Win32.Build.0 = Debug|Win32 {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Debug|x64.ActiveCfg = Debug|x64 @@ -50,30 +18,6 @@ Global {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Release|Win32.Build.0 = Release|Win32 {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Release|x64.ActiveCfg = Release|x64 {DFE335FC-755D-4BAA-8452-94434F8A1EDB}.Release|x64.Build.0 = Release|x64 - {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Debug|Win32.ActiveCfg = Debug|Win32 - {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Debug|Win32.Build.0 = Debug|Win32 - {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Debug|x64.ActiveCfg = Debug|x64 - {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Debug|x64.Build.0 = Debug|x64 - {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Release|Win32.ActiveCfg = Release|Win32 - {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Release|Win32.Build.0 = Release|Win32 - {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Release|x64.ActiveCfg = Release|x64 - {E34D07D0-7DE6-4C46-A00D-C20C691789E4}.Release|x64.Build.0 = Release|x64 - {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Debug|Win32.ActiveCfg = Debug|Win32 - {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Debug|Win32.Build.0 = Debug|Win32 - {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Debug|x64.ActiveCfg = Debug|x64 - {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Debug|x64.Build.0 = Debug|x64 - {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Release|Win32.ActiveCfg = Release|Win32 - {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Release|Win32.Build.0 = Release|Win32 - {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Release|x64.ActiveCfg = Release|x64 - {6678D1A3-33A6-48A9-878B-48E5D2903D27}.Release|x64.Build.0 = Release|x64 - {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Debug|Win32.ActiveCfg = Debug|Win32 - {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Debug|Win32.Build.0 = Debug|Win32 - {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Debug|x64.ActiveCfg = Debug|x64 - {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Debug|x64.Build.0 = Debug|x64 - {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Release|Win32.ActiveCfg = Release|Win32 - {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Release|Win32.Build.0 = Release|Win32 - {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Release|x64.ActiveCfg = Release|x64 - {CE7D2C07-21CE-4590-81AB-2ADA88A2B85F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj index c61ba6d9d0..1cfe0bb37d 100644 --- a/src/common/common.vcxproj +++ b/src/common/common.vcxproj @@ -1,6 +1,14 @@  + + DebugFast + Win32 + + + DebugFast + x64 + Debug Win32 @@ -20,102 +28,82 @@ {DFE335FC-755D-4BAA-8452-94434F8A1EDB} - common + Common true StaticLibrary + MultiByte - StaticLibrary true + StaticLibrary + MultiByte + false + StaticLibrary + Unicode + + StaticLibrary false + MultiByte + false + StaticLibrary + Unicode + + StaticLibrary false + MultiByte - - - - + + - - - - + + - - - - + + - - - - + + - - - - - - - - + + - - - - - - - + true - - - - - - - - + true - - - - - - - - + @@ -124,15 +112,16 @@ true true - - - - - - - - + + + + + true + true + true + + @@ -141,46 +130,72 @@ true true - - - - - - - - + + + + + + true + true + true + + - - - - - - - - - - - + + - - - + + + + + + + + + + + - + - - + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters index 9d2b5c5131..ab8e5d12ec 100644 --- a/src/common/common.vcxproj.filters +++ b/src/common/common.vcxproj.filters @@ -1,35 +1,59 @@  - - - - - - + + + + + - - + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + \ No newline at end of file diff --git a/src/common/src/atomic.h b/src/common/src/atomic.h new file mode 100644 index 0000000000..883bc14fb6 --- /dev/null +++ b/src/common/src/atomic.h @@ -0,0 +1,19 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _ATOMIC_H_ +#define _ATOMIC_H_ + +#ifdef _WIN32 + +#include "atomic_win32.h" + +#else + +// GCC-compatible compiler assumed! +#include "atomic_gcc.h" + +#endif + +#endif diff --git a/src/common/src/atomic_gcc.h b/src/common/src/atomic_gcc.h new file mode 100644 index 0000000000..0f820f4fa3 --- /dev/null +++ b/src/common/src/atomic_gcc.h @@ -0,0 +1,113 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _ATOMIC_GCC_H_ +#define _ATOMIC_GCC_H_ + +#include "common.h" + +// Atomic operations are performed in a single step by the CPU. It is +// impossible for other threads to see the operation "half-done." +// +// Some atomic operations can be combined with different types of memory +// barriers called "Acquire semantics" and "Release semantics", defined below. +// +// Acquire semantics: Future memory accesses cannot be relocated to before the +// operation. +// +// Release semantics: Past memory accesses cannot be relocated to after the +// operation. +// +// These barriers affect not only the compiler, but also the CPU. + +namespace Common +{ + +inline void AtomicAdd(volatile u32& target, u32 value) { + __sync_add_and_fetch(&target, value); +} + +inline void AtomicAnd(volatile u32& target, u32 value) { + __sync_and_and_fetch(&target, value); +} + +inline void AtomicDecrement(volatile u32& target) { + __sync_add_and_fetch(&target, -1); +} + +inline void AtomicIncrement(volatile u32& target) { + __sync_add_and_fetch(&target, 1); +} + +inline u32 AtomicLoad(volatile u32& src) { + return src; // 32-bit reads are always atomic. +} +inline u32 AtomicLoadAcquire(volatile u32& src) { + //keep the compiler from caching any memory references + u32 result = src; // 32-bit reads are always atomic. + //__sync_synchronize(); // TODO: May not be necessary. + // Compiler instruction only. x86 loads always have acquire semantics. + __asm__ __volatile__ ( "":::"memory" ); + return result; +} + +inline void AtomicOr(volatile u32& target, u32 value) { + __sync_or_and_fetch(&target, value); +} + +inline void AtomicStore(volatile u32& dest, u32 value) { + dest = value; // 32-bit writes are always atomic. +} +inline void AtomicStoreRelease(volatile u32& dest, u32 value) { + __sync_lock_test_and_set(&dest, value); // TODO: Wrong! This function is has acquire semantics. +} + +} + +// Old code kept here for reference in case we need the parts with __asm__ __volatile__. +#if 0 +LONG SyncInterlockedIncrement(LONG *Dest) +{ +#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) + return __sync_add_and_fetch(Dest, 1); +#else + register int result; + __asm__ __volatile__("lock; xadd %0,%1" + : "=r" (result), "=m" (*Dest) + : "0" (1), "m" (*Dest) + : "memory"); + return result; +#endif +} + +LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val) +{ +#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) + return __sync_add_and_fetch(Dest, Val); +#else + register int result; + __asm__ __volatile__("lock; xadd %0,%1" + : "=r" (result), "=m" (*Dest) + : "0" (Val), "m" (*Dest) + : "memory"); + return result; +#endif +} + +LONG SyncInterlockedExchange(LONG *Dest, LONG Val) +{ +#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) + return __sync_lock_test_and_set(Dest, Val); +#else + register int result; + __asm__ __volatile__("lock; xchg %0,%1" + : "=r" (result), "=m" (*Dest) + : "0" (Val), "m" (*Dest) + : "memory"); + return result; +#endif +} +#endif + +#endif diff --git a/src/common/src/atomic_win32.h b/src/common/src/atomic_win32.h new file mode 100644 index 0000000000..31ee0b784c --- /dev/null +++ b/src/common/src/atomic_win32.h @@ -0,0 +1,72 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _ATOMIC_WIN32_H_ +#define _ATOMIC_WIN32_H_ + +#include "common.h" +#include +#include + +// Atomic operations are performed in a single step by the CPU. It is +// impossible for other threads to see the operation "half-done." +// +// Some atomic operations can be combined with different types of memory +// barriers called "Acquire semantics" and "Release semantics", defined below. +// +// Acquire semantics: Future memory accesses cannot be relocated to before the +// operation. +// +// Release semantics: Past memory accesses cannot be relocated to after the +// operation. +// +// These barriers affect not only the compiler, but also the CPU. +// +// NOTE: Acquire and Release are not differentiated right now. They perform a +// full memory barrier instead of a "one-way" memory barrier. The newest +// Windows SDK has Acquire and Release versions of some Interlocked* functions. + +namespace Common +{ + +inline void AtomicAdd(volatile u32& target, u32 value) { + InterlockedExchangeAdd((volatile LONG*)&target, (LONG)value); +} + +inline void AtomicAnd(volatile u32& target, u32 value) { + _InterlockedAnd((volatile LONG*)&target, (LONG)value); +} + +inline void AtomicIncrement(volatile u32& target) { + InterlockedIncrement((volatile LONG*)&target); +} + +inline void AtomicDecrement(volatile u32& target) { + InterlockedDecrement((volatile LONG*)&target); +} + +inline u32 AtomicLoad(volatile u32& src) { + return src; // 32-bit reads are always atomic. +} +inline u32 AtomicLoadAcquire(volatile u32& src) { + u32 result = src; // 32-bit reads are always atomic. + _ReadBarrier(); // Compiler instruction only. x86 loads always have acquire semantics. + return result; +} + +inline void AtomicOr(volatile u32& target, u32 value) { + _InterlockedOr((volatile LONG*)&target, (LONG)value); +} + +inline void AtomicStore(volatile u32& dest, u32 value) { + dest = value; // 32-bit writes are always atomic. +} +inline void AtomicStoreRelease(volatile u32& dest, u32 value) { + _WriteBarrier(); // Compiler instruction only. x86 stores always have release semantics. + dest = value; // 32-bit writes are always atomic. +} + +} + +#endif diff --git a/src/common/src/break_points.cpp b/src/common/src/break_points.cpp new file mode 100644 index 0000000000..9e5c651539 --- /dev/null +++ b/src/common/src/break_points.cpp @@ -0,0 +1,203 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "common.h" +#include "debug_interface.h" +#include "break_points.h" + +#include +#include + +bool BreakPoints::IsAddressBreakPoint(u32 _iAddress) +{ + for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i) + if (i->iAddress == _iAddress) + return true; + return false; +} + +bool BreakPoints::IsTempBreakPoint(u32 _iAddress) +{ + for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i) + if (i->iAddress == _iAddress && i->bTemporary) + return true; + return false; +} + +BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const +{ + TBreakPointsStr bps; + for (TBreakPoints::const_iterator i = m_BreakPoints.begin(); + i != m_BreakPoints.end(); ++i) + { + if (!i->bTemporary) + { + std::stringstream bp; + bp << std::hex << i->iAddress << " " << (i->bOn ? "n" : ""); + bps.push_back(bp.str()); + } + } + + return bps; +} + +void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) +{ + for (TBreakPointsStr::const_iterator i = bps.begin(); i != bps.end(); ++i) + { + TBreakPoint bp; + std::stringstream bpstr; + bpstr << std::hex << *i; + bpstr >> bp.iAddress; + bp.bOn = i->find("n") != i->npos; + bp.bTemporary = false; + Add(bp); + } +} + +void BreakPoints::Add(const TBreakPoint& bp) +{ + if (!IsAddressBreakPoint(bp.iAddress)) + { + m_BreakPoints.push_back(bp); + //if (jit) + // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4); + } +} + +void BreakPoints::Add(u32 em_address, bool temp) +{ + if (!IsAddressBreakPoint(em_address)) // only add new addresses + { + TBreakPoint pt; // breakpoint settings + pt.bOn = true; + pt.bTemporary = temp; + pt.iAddress = em_address; + + m_BreakPoints.push_back(pt); + + //if (jit) + // jit->GetBlockCache()->InvalidateICache(em_address, 4); + } +} + +void BreakPoints::Remove(u32 em_address) +{ + for (TBreakPoints::iterator i = m_BreakPoints.begin(); i != m_BreakPoints.end(); ++i) + { + if (i->iAddress == em_address) + { + m_BreakPoints.erase(i); + //if (jit) + // jit->GetBlockCache()->InvalidateICache(em_address, 4); + return; + } + } +} + +void BreakPoints::Clear() +{ + //if (jit) + //{ + // std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(), + // [](const TBreakPoint& bp) + // { + // jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4); + // } + // ); + //} + + m_BreakPoints.clear(); +} + +MemChecks::TMemChecksStr MemChecks::GetStrings() const +{ + TMemChecksStr mcs; + for (TMemChecks::const_iterator i = m_MemChecks.begin(); + i != m_MemChecks.end(); ++i) + { + std::stringstream mc; + mc << std::hex << i->StartAddress; + mc << " " << (i->bRange ? i->EndAddress : i->StartAddress) << " " << + (i->bRange ? "n" : "") << (i->OnRead ? "r" : "") << + (i->OnWrite ? "w" : "") << (i->Log ? "l" : "") << (i->Break ? "p" : ""); + mcs.push_back(mc.str()); + } + + return mcs; +} + +void MemChecks::AddFromStrings(const TMemChecksStr& mcs) +{ + for (TMemChecksStr::const_iterator i = mcs.begin(); i != mcs.end(); ++i) + { + TMemCheck mc; + std::stringstream mcstr; + mcstr << std::hex << *i; + mcstr >> mc.StartAddress; + mc.bRange = i->find("n") != i->npos; + mc.OnRead = i->find("r") != i->npos; + mc.OnWrite = i->find("w") != i->npos; + mc.Log = i->find("l") != i->npos; + mc.Break = i->find("p") != i->npos; + if (mc.bRange) + mcstr >> mc.EndAddress; + else + mc.EndAddress = mc.StartAddress; + Add(mc); + } +} + +void MemChecks::Add(const TMemCheck& _rMemoryCheck) +{ + if (GetMemCheck(_rMemoryCheck.StartAddress) == 0) + m_MemChecks.push_back(_rMemoryCheck); +} + +void MemChecks::Remove(u32 _Address) +{ + for (TMemChecks::iterator i = m_MemChecks.begin(); i != m_MemChecks.end(); ++i) + { + if (i->StartAddress == _Address) + { + m_MemChecks.erase(i); + return; + } + } +} + +TMemCheck *MemChecks::GetMemCheck(u32 address) +{ + for (TMemChecks::iterator i = m_MemChecks.begin(); i != m_MemChecks.end(); ++i) + { + if (i->bRange) + { + if (address >= i->StartAddress && address <= i->EndAddress) + return &(*i); + } + else if (i->StartAddress == address) + return &(*i); + } + + // none found + return 0; +} + +void TMemCheck::Action(DebugInterface *debug_interface, u32 iValue, u32 addr, + bool write, int size, u32 pc) +{ + if ((write && OnWrite) || (!write && OnRead)) + { + if (Log) + { + INFO_LOG(MEMMAP, "CHK %08x (%s) %s%i %0*x at %08x (%s)", + pc, debug_interface->getDescription(pc).c_str(), + write ? "Write" : "Read", size*8, size*2, iValue, addr, + debug_interface->getDescription(addr).c_str() + ); + } + if (Break) + debug_interface->breakNow(); + } +} diff --git a/src/common/src/break_points.h b/src/common/src/break_points.h new file mode 100644 index 0000000000..281de1004a --- /dev/null +++ b/src/common/src/break_points.h @@ -0,0 +1,102 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _DEBUGGER_BREAKPOINTS_H +#define _DEBUGGER_BREAKPOINTS_H + +#include +#include + +#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 TBreakPoints; + typedef std::vector 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 TMemChecks; + typedef std::vector TMemChecksStr; + + TMemChecks m_MemChecks; + + const TMemChecks& GetMemChecks() { return m_MemChecks; } + + TMemChecksStr GetStrings() const; + void AddFromStrings(const TMemChecksStr& mcs); + + void Add(const TMemCheck& _rMemoryCheck); + + // memory breakpoint + TMemCheck *GetMemCheck(u32 address); + void Remove(u32 _Address); + + void Clear() { m_MemChecks.clear(); }; +}; + +#endif + diff --git a/src/common/src/chunk_file.h b/src/common/src/chunk_file.h new file mode 100644 index 0000000000..0c333b094b --- /dev/null +++ b/src/common/src/chunk_file.h @@ -0,0 +1,384 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#ifndef _POINTERWRAP_H_ +#define _POINTERWRAP_H_ + +// Extremely simple serialization framework. + +// (mis)-features: +// + Super fast +// + Very simple +// + Same code is used for serialization and deserializaition (in most cases) +// - Zero backwards/forwards compatibility +// - Serialization code for anything complex has to be manually written. + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "file_util.h" + +template +struct LinkedListItem : public T +{ + LinkedListItem *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 + void Do(std::map& x) + { + u32 count = (u32)x.size(); + Do(count); + + switch (mode) + { + case MODE_READ: + for (x.clear(); count != 0; --count) + { + std::pair 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 + 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 + void Do(std::vector& x) + { + DoContainer(x); + } + + template + void Do(std::list& x) + { + DoContainer(x); + } + + template + void Do(std::deque& x) + { + DoContainer(x); + } + + template + void Do(std::basic_string& x) + { + DoContainer(x); + } + + template + void DoArray(T* x, u32 count) + { + for (u32 i = 0; i != count; ++i) + Do(x[i]); + } + + template + void Do(T& x) + { + // Ideally this would be std::is_trivially_copyable, but not enough support yet + static_assert(std::is_pod::value, "Only sane for POD types"); + + DoVoid((void*)&x, sizeof(x)); + } + + template + void DoPOD(T& x) + { + DoVoid((void*)&x, sizeof(x)); + } + + template + 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 * (*TNew)(), void (*TFree)(LinkedListItem*), void (*TDo)(PointerWrap&, T*)> + void DoLinkedList(LinkedListItem*& list_start, LinkedListItem** list_end=0) + { + LinkedListItem* list_cur = list_start; + LinkedListItem* prev = 0; + + while (true) + { + u8 shouldExist = (list_cur ? 1 : 0); + Do(shouldExist); + if (shouldExist == 1) + { + LinkedListItem* 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* 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(data)[i]); + } +}; + +class CChunkFileReader +{ +public: + // Load file template + template + 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 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 + 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 buffer(sz); + ptr = &buffer[0]; + p.SetMode(PointerWrap::MODE_WRITE); + _class.DoState(p); + + // Create header + SChunkHeader header; + header.Revision = _Revision; + header.ExpectedSize = (u32)sz; + + // Write to file + if (!pFile.WriteArray(&header, 1)) + { + ERROR_LOG(COMMON,"ChunkReader: Failed writing header"); + return false; + } + + if (!pFile.WriteArray(&buffer[0], sz)) + { + ERROR_LOG(COMMON,"ChunkReader: Failed writing data"); + return false; + } + + INFO_LOG(COMMON,"ChunkReader: Done writing %s", _rFilename.c_str()); + return true; + } + +private: + struct SChunkHeader + { + u32 Revision; + u32 ExpectedSize; + }; +}; + +#endif // _POINTERWRAP_H_ diff --git a/src/common/src/common.h b/src/common/src/common.h new file mode 100644 index 0000000000..f95d580746 --- /dev/null +++ b/src/common/src/common.h @@ -0,0 +1,164 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +// DO NOT EVER INCLUDE directly _or indirectly_ from this file +// since it slows down the build a lot. + +#include +#include +#include + +// 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 + #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: 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 +#define MAX_PATH PATH_MAX +#ifdef _LP64 +#define _M_X64 1 +#else +#define _M_IX86 1 +#endif +#define __forceinline inline __attribute__((always_inline)) +#define GC_ALIGNED16(x) __attribute__((aligned(16))) x +#define GC_ALIGNED32(x) __attribute__((aligned(32))) x +#define GC_ALIGNED64(x) __attribute__((aligned(64))) x +#define GC_ALIGNED128(x) __attribute__((aligned(128))) x +#define GC_ALIGNED16_DECL(x) __attribute__((aligned(16))) x +#define GC_ALIGNED64_DECL(x) __attribute__((aligned(64))) x +#endif + +#ifdef _MSC_VER +#define __strdup _strdup +#define __getcwd _getcwd +#define __chdir _chdir +#else +#define __strdup strdup +#define __getcwd getcwd +#define __chdir chdir +#endif + +// Dummy macro for marking translatable strings that can not be immediately translated. +// wxWidgets does not have a true dummy macro for this. +#define _trans(a) a + +#if defined _M_GENERIC +# define _M_SSE 0x0 +#elif defined __GNUC__ +# if defined __SSE4_2__ +# define _M_SSE 0x402 +# elif defined __SSE4_1__ +# define _M_SSE 0x401 +# elif defined __SSSE3__ +# define _M_SSE 0x301 +# elif defined __SSE3__ +# define _M_SSE 0x300 +# endif +#elif (_MSC_VER >= 1500) || __INTEL_COMPILER // Visual Studio 2008 +# define _M_SSE 0x402 +#endif + +// Host communication. +enum HOST_COMM +{ + // Begin at 10 in case there is already messages with wParam = 0, 1, 2 and so on + WM_USER_STOP = 10, + WM_USER_CREATE, + WM_USER_SETCURSOR, +}; + +// Used for notification on emulation state +enum EMUSTATE_CHANGE +{ + EMUSTATE_CHANGE_PLAY = 1, + EMUSTATE_CHANGE_PAUSE, + EMUSTATE_CHANGE_STOP +}; + +#endif // _COMMON_H_ diff --git a/src/common/src/common_funcs.h b/src/common/src/common_funcs.h new file mode 100644 index 0000000000..73320a3ac2 --- /dev/null +++ b/src/common/src/common_funcs.h @@ -0,0 +1,243 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _COMMONFUNCS_H_ +#define _COMMONFUNCS_H_ + +#ifdef _WIN32 +#define SLEEP(x) Sleep(x) +#else +#include +#define SLEEP(x) usleep(x*1000) +#endif + +template struct CompileTimeAssert; +template<> struct CompileTimeAssert {}; + +#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 +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 +#ifdef __linux__ +#include +#elif defined __FreeBSD__ +#include +#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 +inline T min(const T& a, const T& b) {return a > b ? b : a;} +template +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 +void swap(u8*); + +template <> +inline void swap<1>(u8* data) +{} + +template <> +inline void swap<2>(u8* data) +{ + *reinterpret_cast(data) = swap16(data); +} + +template <> +inline void swap<4>(u8* data) +{ + *reinterpret_cast(data) = swap32(data); +} + +template <> +inline void swap<8>(u8* data) +{ + *reinterpret_cast(data) = swap64(data); +} + +template +inline T FromBigEndian(T data) +{ + //static_assert(std::is_arithmetic::value, "function only makes sense with arithmetic types"); + + swap(reinterpret_cast(&data)); + return data; +} + +} // Namespace Common + +#endif // _COMMONFUNCS_H_ diff --git a/src/common/src/common_paths.h b/src/common/src/common_paths.h new file mode 100644 index 0000000000..9af93ab74a --- /dev/null +++ b/src/common/src/common_paths.h @@ -0,0 +1,71 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _COMMON_PATHS_H_ +#define _COMMON_PATHS_H_ + +// Make sure we pick up USER_DIR if set in config.h +#include "common.h" + +// Directory seperators, do we need this? +#define DIR_SEP "/" +#define DIR_SEP_CHR '/' + +// The user data dir +#define ROOT_DIR "." +#ifdef _WIN32 + #define USERDATA_DIR "user" + #define DOLPHIN_DATA_DIR "akiru" +#else + #define USERDATA_DIR "user" + #ifdef USER_DIR + #define DOLPHIN_DATA_DIR USER_DIR + #else + #define DOLPHIN_DATA_DIR ".akiru" + #endif +#endif + +// Shared data dirs (Sys and shared User for linux) +#ifdef _WIN32 + #define SYSDATA_DIR "sys" +#else + #ifdef DATA_DIR + #define SYSDATA_DIR DATA_DIR "sys" + #define SHARED_USER_DIR DATA_DIR USERDATA_DIR DIR_SEP + #else + #define SYSDATA_DIR "sys" + #define SHARED_USER_DIR ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP + #endif +#endif + +// Dirs in both User and Sys +#define EUR_DIR "EUR" +#define USA_DIR "USA" +#define JAP_DIR "JAP" + +// Subdirs in the User dir returned by GetUserPath(D_USER_IDX) +#define CONFIG_DIR "config" +#define GAMECONFIG_DIR "game_config" +#define MAPS_DIR "maps" +#define CACHE_DIR "cache" +#define SHADERCACHE_DIR "shader_cache" +#define STATESAVES_DIR "state_saves" +#define SCREENSHOTS_DIR "screenShots" +#define DUMP_DIR "dump" +#define DUMP_TEXTURES_DIR "textures" +#define DUMP_FRAMES_DIR "frames" +#define DUMP_AUDIO_DIR "audio" +#define LOGS_DIR "logs" +#define SHADERS_DIR "shaders" + +// Filenames +// Files in the directory returned by GetUserPath(D_CONFIG_IDX) +#define AKIRU_CONFIG "akiru.ini" +#define DEBUGGER_CONFIG "debugger.ini" +#define LOGGER_CONFIG "logger.ini" + +// Files in the directory returned by GetUserPath(D_LOGS_IDX) +#define MAIN_LOG "akiru.log" + +#endif // _COMMON_PATHS_H_ diff --git a/src/common/src/common_types.h b/src/common/src/common_types.h new file mode 100644 index 0000000000..5cb4021313 --- /dev/null +++ b/src/common/src/common_types.h @@ -0,0 +1,48 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +// This header contains type definitions that are shared between the Dolphin core and +// other parts of the code. Any definitions that are only used by the core should be +// placed in "common.h" instead. + +#ifndef _COMMONTYPES_H_ +#define _COMMONTYPES_H_ + +#ifdef _WIN32 + +#include + +typedef unsigned __int8 u8; +typedef unsigned __int16 u16; +typedef unsigned __int32 u32; +typedef unsigned __int64 u64; + +typedef signed __int8 s8; +typedef signed __int16 s16; +typedef signed __int32 s32; +typedef signed __int64 s64; + +#else + +#ifndef GEKKO + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; + +#endif +// For using windows lock code +#define TCHAR char +#define LONG int + +#endif // _WIN32 + +#endif // _COMMONTYPES_H_ diff --git a/src/common/src/console_listener.cpp b/src/common/src/console_listener.cpp new file mode 100644 index 0000000000..84f57675d1 --- /dev/null +++ b/src/common/src/console_listener.cpp @@ -0,0 +1,337 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include // min +#include // System: To be able to add strings with "+" +#include +#include +#ifdef _WIN32 +#include +#include +#else +#include +#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> Str; + std::vector> Attr; + + // ReadConsoleOutputAttribute seems to have a limit at this level + static const int ReadBufferSize = MAX_BYTES - 32; + + DWORD cAttrRead = ReadBufferSize; + DWORD BytesRead = 0; + while (BytesRead < BufferSize) + { + Str.resize(Str.size() + 1); + if (!ReadConsoleOutputCharacter(hConsole, Str.back().data(), ReadBufferSize, coordScreen, &cCharsRead)) + SLog += StringFromFormat("WriteConsoleOutputCharacter error"); + + Attr.resize(Attr.size() + 1); + if (!ReadConsoleOutputAttribute(hConsole, Attr.back().data(), ReadBufferSize, coordScreen, &cAttrRead)) + SLog += StringFromFormat("WriteConsoleOutputAttribute error"); + + // Break on error + if (cAttrRead == 0) break; + BytesRead += cAttrRead; + coordScreen = GetCoordinates(BytesRead, ConInfo.dwSize.X); + } + // Letter space + int LWidth = (int)(floor((float)Width / 8.0f) - 1.0f); + int LHeight = (int)(floor((float)Height / 12.0f) - 1.0f); + int LBufWidth = LWidth + 1; + int LBufHeight = (int)floor((float)BufferSize / (float)LBufWidth); + // Change screen buffer size + LetterSpace(LBufWidth, LBufHeight); + + + ClearScreen(true); + coordScreen.Y = 0; + coordScreen.X = 0; + DWORD cCharsWritten = 0; + + int BytesWritten = 0; + DWORD cAttrWritten = 0; + for (size_t i = 0; i < Attr.size(); i++) + { + if (!WriteConsoleOutputCharacter(hConsole, Str[i].data(), ReadBufferSize, coordScreen, &cCharsWritten)) + SLog += StringFromFormat("WriteConsoleOutputCharacter error"); + if (!WriteConsoleOutputAttribute(hConsole, Attr[i].data(), ReadBufferSize, coordScreen, &cAttrWritten)) + SLog += StringFromFormat("WriteConsoleOutputAttribute error"); + + BytesWritten += cAttrWritten; + coordScreen = GetCoordinates(BytesWritten, LBufWidth); + } + + const int OldCursor = ConInfo.dwCursorPosition.Y * ConInfo.dwSize.X + ConInfo.dwCursorPosition.X; + COORD Coo = GetCoordinates(OldCursor, LBufWidth); + SetConsoleCursorPosition(hConsole, Coo); + + if (SLog.length() > 0) Log(LogTypes::LNOTICE, SLog.c_str()); + + // Resize the window too + if (Resize) MoveWindow(GetConsoleWindow(), Left,Top, (Width + 100),Height, true); +#endif +} + +void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text) +{ +#if defined(_WIN32) + /* + const int MAX_BYTES = 1024*10; + char Str[MAX_BYTES]; + va_list ArgPtr; + int Cnt; + va_start(ArgPtr, Text); + Cnt = vsnprintf(Str, MAX_BYTES, Text, ArgPtr); + va_end(ArgPtr); + */ + DWORD cCharsWritten; + WORD Color; + + switch (Level) + { + case NOTICE_LEVEL: // light green + Color = FOREGROUND_GREEN | FOREGROUND_INTENSITY; + break; + case ERROR_LEVEL: // light red + Color = FOREGROUND_RED | FOREGROUND_INTENSITY; + break; + case WARNING_LEVEL: // light yellow + Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + break; + case INFO_LEVEL: // cyan + Color = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; + break; + case DEBUG_LEVEL: // gray + Color = FOREGROUND_INTENSITY; + break; + default: // off-white + Color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; + break; + } + if (strlen(Text) > 10) + { + // First 10 chars white + SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + WriteConsole(hConsole, Text, 10, &cCharsWritten, NULL); + Text += 10; + } + SetConsoleTextAttribute(hConsole, Color); + WriteConsole(hConsole, Text, (DWORD)strlen(Text), &cCharsWritten, NULL); +#else + char ColorAttr[16] = ""; + char ResetAttr[16] = ""; + + if (bUseColor) + { + strcpy(ResetAttr, "\033[0m"); + switch (Level) + { + case NOTICE_LEVEL: // light green + strcpy(ColorAttr, "\033[92m"); + break; + case ERROR_LEVEL: // light red + strcpy(ColorAttr, "\033[91m"); + break; + case WARNING_LEVEL: // light yellow + strcpy(ColorAttr, "\033[93m"); + break; + default: + break; + } + } + fprintf(stderr, "%s%s%s", ColorAttr, Text, ResetAttr); +#endif +} +// Clear console screen +void ConsoleListener::ClearScreen(bool Cursor) +{ +#if defined(_WIN32) + COORD coordScreen = { 0, 0 }; + DWORD cCharsWritten; + CONSOLE_SCREEN_BUFFER_INFO csbi; + DWORD dwConSize; + + HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + + GetConsoleScreenBufferInfo(hConsole, &csbi); + dwConSize = csbi.dwSize.X * csbi.dwSize.Y; + // Write space to the entire console + FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten); + GetConsoleScreenBufferInfo(hConsole, &csbi); + FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten); + // Reset cursor + if (Cursor) SetConsoleCursorPosition(hConsole, coordScreen); +#endif +} + + diff --git a/src/common/src/console_listener.h b/src/common/src/console_listener.h new file mode 100644 index 0000000000..ab5c00980b --- /dev/null +++ b/src/common/src/console_listener.h @@ -0,0 +1,41 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _CONSOLELISTENER_H +#define _CONSOLELISTENER_H + +#include "log_manager.h" + +#ifdef _WIN32 +#include +#endif + +class ConsoleListener : public LogListener +{ +public: + ConsoleListener(); + ~ConsoleListener(); + + void Open(bool Hidden = false, int Width = 100, int Height = 100, const char * Name = "Console"); + void UpdateHandle(); + void Close(); + bool IsOpen(); + void LetterSpace(int Width, int Height); + void BufferWidthHeight(int BufferWidth, int BufferHeight, int ScreenWidth, int ScreenHeight, bool BufferFirst); + void PixelSpace(int Left, int Top, int Width, int Height, bool); +#ifdef _WIN32 + COORD GetCoordinates(int BytesRead, int BufferWidth); +#endif + void Log(LogTypes::LOG_LEVELS, const char *Text); + void ClearScreen(bool Cursor = true); + +private: +#ifdef _WIN32 + HWND GetHwnd(void); + HANDLE hConsole; +#endif + bool bUseColor; +}; + +#endif // _CONSOLELISTENER_H diff --git a/src/common/src/cpu_detect.h b/src/common/src/cpu_detect.h new file mode 100644 index 0000000000..be6ce34985 --- /dev/null +++ b/src/common/src/cpu_detect.h @@ -0,0 +1,81 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +// Detect the cpu, so we'll know which optimizations to use +#ifndef _CPUDETECT_H_ +#define _CPUDETECT_H_ + +#include + +enum CPUVendor +{ + VENDOR_INTEL = 0, + VENDOR_AMD = 1, + VENDOR_ARM = 2, + VENDOR_OTHER = 3, +}; + +struct CPUInfo +{ + CPUVendor vendor; + + char cpu_string[0x21]; + char brand_string[0x41]; + bool OS64bit; + bool CPU64bit; + bool Mode64bit; + + bool HTT; + int num_cores; + int logical_cpu_count; + + bool bSSE; + bool bSSE2; + bool bSSE3; + bool bSSSE3; + bool bPOPCNT; + bool bSSE4_1; + bool bSSE4_2; + bool bLZCNT; + bool bSSE4A; + bool bAVX; + bool bAES; + bool bLAHFSAHF64; + bool bLongMode; + + // ARM specific CPUInfo + bool bSwp; + bool bHalf; + bool bThumb; + bool bFastMult; + bool bVFP; + bool bEDSP; + bool bThumbEE; + bool bNEON; + bool bVFPv3; + bool bTLS; + bool bVFPv4; + bool bIDIVa; + bool bIDIVt; + bool bArmV7; // enable MOVT, MOVW etc + + // ARMv8 specific + bool bFP; + bool bASIMD; + + // Call Detect() + explicit CPUInfo(); + + // Turn the cpu info into a string we can show + std::string Summarize(); + +private: + // Detects the various cpu features + void Detect(); +}; + +extern CPUInfo cpu_info; + +#endif // _CPUDETECT_H_ diff --git a/src/common/src/debug_interface.h b/src/common/src/debug_interface.h new file mode 100644 index 0000000000..317cd0bb43 --- /dev/null +++ b/src/common/src/debug_interface.h @@ -0,0 +1,39 @@ +#ifndef _DEBUGINTERFACE_H +#define _DEBUGINTERFACE_H + +#include +#include + +class DebugInterface +{ +protected: + virtual ~DebugInterface() {} + +public: + virtual void disasm(unsigned int /*address*/, char *dest, int /*max_size*/) {strcpy(dest, "NODEBUGGER");} + virtual void getRawMemoryString(int /*memory*/, unsigned int /*address*/, char *dest, int /*max_size*/) {strcpy(dest, "NODEBUGGER");} + virtual int getInstructionSize(int /*instruction*/) {return 1;} + virtual bool isAlive() {return true;} + virtual bool isBreakpoint(unsigned int /*address*/) {return false;} + virtual void setBreakpoint(unsigned int /*address*/){} + virtual void clearBreakpoint(unsigned int /*address*/){} + virtual void clearAllBreakpoints() {} + virtual void toggleBreakpoint(unsigned int /*address*/){} + virtual bool isMemCheck(unsigned int /*address*/) {return false;} + virtual void toggleMemCheck(unsigned int /*address*/){} + virtual unsigned int readMemory(unsigned int /*address*/){return 0;} + virtual void writeExtraMemory(int /*memory*/, unsigned int /*value*/, unsigned int /*address*/) {} + virtual unsigned int readExtraMemory(int /*memory*/, unsigned int /*address*/){return 0;} + virtual unsigned int readInstruction(unsigned int /*address*/){return 0;} + virtual unsigned int getPC() {return 0;} + virtual void setPC(unsigned int /*address*/) {} + virtual void step() {} + virtual void runToBreakpoint() {} + virtual void breakNow() {} + virtual void insertBLR(unsigned int /*address*/, unsigned int /*value*/) {} + virtual void showJitResults(unsigned int /*address*/) {}; + virtual int getColor(unsigned int /*address*/){return 0xFFFFFFFF;} + virtual std::string getDescription(unsigned int /*address*/) = 0; +}; + +#endif diff --git a/src/common/src/extended_trace.cpp b/src/common/src/extended_trace.cpp new file mode 100644 index 0000000000..44815343d7 --- /dev/null +++ b/src/common/src/extended_trace.cpp @@ -0,0 +1,433 @@ +// -------------------------------------------------------------------------------------- +// +// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com +// For companies(Austin,TX): If you would like to get my resume, send an email. +// +// The source is free, but if you want to use it, mention my name and e-mail address +// +// History: +// 1.0 Initial version Zoltan Csizmadia +// 1.1 WhineCube version Masken +// 1.2 Dolphin version Masken +// +// -------------------------------------------------------------------------------------- + +#if defined(WIN32) + +#include +#include +#include "extended_trace.h" +#include "string_util.h" +using namespace std; + +#include +#include + +#define BUFFERSIZE 0x200 +#pragma warning(disable:4996) + +// Unicode safe char* -> TCHAR* conversion +void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut ) +{ +#if defined(UNICODE)||defined(_UNICODE) + ULONG index = 0; + PCSTR lpAct = lpszIn; + + for( ; ; lpAct++ ) + { + lpszOut[index++] = (TCHAR)(*lpAct); + if ( *lpAct == 0 ) + break; + } +#else + // This is trivial :) + strcpy( lpszOut, lpszIn ); +#endif +} + +// Let's figure out the path for the symbol files +// Search path= ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + lpszIniPath +// Note: There is no size check for lpszSymbolPath! +static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath ) +{ + CHAR lpszPath[BUFFERSIZE]; + + // Creating the default path + // ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + strcpy( lpszSymbolPath, "." ); + + // environment variable _NT_SYMBOL_PATH + if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE ) ) + { + strcat( lpszSymbolPath, ";" ); + strcat( lpszSymbolPath, lpszPath ); + } + + // environment variable _NT_ALTERNATE_SYMBOL_PATH + if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", lpszPath, BUFFERSIZE ) ) + { + strcat( lpszSymbolPath, ";" ); + strcat( lpszSymbolPath, lpszPath ); + } + + // environment variable SYSTEMROOT + if ( GetEnvironmentVariableA( "SYSTEMROOT", lpszPath, BUFFERSIZE ) ) + { + strcat( lpszSymbolPath, ";" ); + strcat( lpszSymbolPath, lpszPath ); + strcat( lpszSymbolPath, ";" ); + + // SYSTEMROOT\System32 + strcat( lpszSymbolPath, lpszPath ); + strcat( lpszSymbolPath, "\\System32" ); + } + + // Add user defined path + if ( lpszIniPath != NULL ) + if ( lpszIniPath[0] != '\0' ) + { + strcat( lpszSymbolPath, ";" ); + strcat( lpszSymbolPath, lpszIniPath ); + } +} + +// Uninitialize the loaded symbol files +BOOL UninitSymInfo() { + return SymCleanup( GetCurrentProcess() ); +} + +// Initializes the symbol files +BOOL InitSymInfo( PCSTR lpszInitialSymbolPath ) +{ + CHAR lpszSymbolPath[BUFFERSIZE]; + DWORD symOptions = SymGetOptions(); + + symOptions |= SYMOPT_LOAD_LINES; + symOptions &= ~SYMOPT_UNDNAME; + SymSetOptions( symOptions ); + InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath ); + + return SymInitialize( GetCurrentProcess(), lpszSymbolPath, TRUE); +} + +// Get the module name from a given address +static BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule ) +{ + BOOL ret = FALSE; + IMAGEHLP_MODULE moduleInfo; + + ::ZeroMemory( &moduleInfo, sizeof(moduleInfo) ); + moduleInfo.SizeOfStruct = sizeof(moduleInfo); + + if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) ) + { + // Got it! + PCSTR2LPTSTR( moduleInfo.ModuleName, lpszModule ); + ret = TRUE; + } + else + // Not found :( + _tcscpy( lpszModule, _T("?") ); + + return ret; +} + +// Get function prototype and parameter info from ip address and stack address +static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol ) +{ + BOOL ret = FALSE; + DWORD dwSymSize = 10000; + TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?"); + CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; + LPTSTR lpszParamSep = NULL; + LPTSTR lpszParsed = lpszUnDSymbol; + PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize ); + + ::ZeroMemory( pSym, dwSymSize ); + pSym->SizeOfStruct = dwSymSize; + pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL); + + // Set the default to unknown + _tcscpy( lpszSymbol, _T("?") ); + + // Get symbol info for IP +#ifndef _M_X64 + DWORD dwDisp = 0; + if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) ) +#else + //makes it compile but hell im not sure if this works... + DWORD64 dwDisp = 0; + if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) ) +#endif + { + // Make the symbol readable for humans + UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, + UNDNAME_COMPLETE | + UNDNAME_NO_THISTYPE | + UNDNAME_NO_SPECIAL_SYMS | + UNDNAME_NO_MEMBER_TYPE | + UNDNAME_NO_MS_KEYWORDS | + UNDNAME_NO_ACCESS_SPECIFIERS ); + + // Symbol information is ANSI string + PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol ); + + // I am just smarter than the symbol file :) + if ( _tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0 ) + _tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)")); + else + if ( _tcscmp(lpszUnDSymbol, _T("_main")) == 0 ) + _tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)")); + else + if ( _tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0 ) + _tcscpy(lpszUnDSymbol, _T("mainCRTStartup()")); + else + if ( _tcscmp(lpszUnDSymbol, _T("_wmain")) == 0 ) + _tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)")); + else + if ( _tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0 ) + _tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()")); + + lpszSymbol[0] = _T('\0'); + + // Let's go through the stack, and modify the function prototype, and insert the actual + // parameter values from the stack + if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL) + { + ULONG index = 0; + for( ; ; index++ ) + { + lpszParamSep = _tcschr( lpszParsed, _T(',') ); + if ( lpszParamSep == NULL ) + break; + + *lpszParamSep = _T('\0'); + + _tcscat( lpszSymbol, lpszParsed ); + _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) ); + + lpszParsed = lpszParamSep + 1; + } + + lpszParamSep = _tcschr( lpszParsed, _T(')') ); + if ( lpszParamSep != NULL ) + { + *lpszParamSep = _T('\0'); + + _tcscat( lpszSymbol, lpszParsed ); + _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) ); + + lpszParsed = lpszParamSep + 1; + } + } + + _tcscat( lpszSymbol, lpszParsed ); + + ret = TRUE; + } + GlobalFree( pSym ); + + return ret; +} + +// Get source file name and line number from IP address +// The output format is: "sourcefile(linenumber)" or +// "modulename!address" or +// "address" +static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo ) +{ + BOOL ret = FALSE; + IMAGEHLP_LINE lineInfo; + DWORD dwDisp; + TCHAR lpszFileName[BUFFERSIZE] = _T(""); + TCHAR lpModuleInfo[BUFFERSIZE] = _T(""); + + _tcscpy( lpszSourceInfo, _T("?(?)") ); + + ::ZeroMemory( &lineInfo, sizeof( lineInfo ) ); + lineInfo.SizeOfStruct = sizeof( lineInfo ); + + if ( SymGetLineFromAddr( GetCurrentProcess(), address, &dwDisp, &lineInfo ) ) + { + // Got it. Let's use "sourcefile(linenumber)" format + PCSTR2LPTSTR( lineInfo.FileName, lpszFileName ); + TCHAR fname[_MAX_FNAME]; + TCHAR ext[_MAX_EXT]; + _tsplitpath(lpszFileName, NULL, NULL, fname, ext); + _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber ); + ret = TRUE; + } + else + { + // There is no source file information. :( + // Let's use the "modulename!address" format + GetModuleNameFromAddress( address, lpModuleInfo ); + + if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0')) + // There is no modulename information. :(( + // Let's use the "address" format + _stprintf( lpszSourceInfo, _T("0x%08X"), address ); + else + _stprintf( lpszSourceInfo, _T("%s!0x%08X"), lpModuleInfo, address ); + + ret = FALSE; + } + + return ret; +} + +void PrintFunctionAndSourceInfo(FILE* file, const STACKFRAME& callstack) +{ + TCHAR symInfo[BUFFERSIZE] = _T("?"); + TCHAR srcInfo[BUFFERSIZE] = _T("?"); + + GetFunctionInfoFromAddresses((ULONG)callstack.AddrPC.Offset, (ULONG)callstack.AddrFrame.Offset, symInfo); + GetSourceInfoFromAddress((ULONG)callstack.AddrPC.Offset, srcInfo); + etfprint(file, " " + TStrToUTF8(srcInfo) + " : " + TStrToUTF8(symInfo) + "\n"); +} + +void StackTrace( HANDLE hThread, const char* lpszMessage, FILE *file ) +{ + STACKFRAME callStack; + BOOL bResult; + CONTEXT context; + HANDLE hProcess = GetCurrentProcess(); + + // If it's not this thread, let's suspend it, and resume it at the end + if ( hThread != GetCurrentThread() ) + if ( SuspendThread( hThread ) == -1 ) + { + // whaaat ?! + etfprint(file, "Call stack info failed\n"); + return; + } + + ::ZeroMemory( &context, sizeof(context) ); + context.ContextFlags = CONTEXT_FULL; + + if ( !GetThreadContext( hThread, &context ) ) + { + etfprint(file, "Call stack info failed\n"); + return; + } + + ::ZeroMemory( &callStack, sizeof(callStack) ); +#ifndef _M_X64 + callStack.AddrPC.Offset = context.Eip; + callStack.AddrStack.Offset = context.Esp; + callStack.AddrFrame.Offset = context.Ebp; +#else + callStack.AddrPC.Offset = context.Rip; + callStack.AddrStack.Offset = context.Rsp; + callStack.AddrFrame.Offset = context.Rbp; +#endif + callStack.AddrPC.Mode = AddrModeFlat; + callStack.AddrStack.Mode = AddrModeFlat; + callStack.AddrFrame.Mode = AddrModeFlat; + + etfprint(file, "Call stack info: \n"); + etfprint(file, lpszMessage); + + PrintFunctionAndSourceInfo(file, callStack); + + for( ULONG index = 0; ; index++ ) + { + bResult = StackWalk( + IMAGE_FILE_MACHINE_I386, + hProcess, + hThread, + &callStack, + NULL, + NULL, + SymFunctionTableAccess, + SymGetModuleBase, + NULL); + + if ( index == 0 ) + continue; + + if( !bResult || callStack.AddrFrame.Offset == 0 ) + break; + + PrintFunctionAndSourceInfo(file, callStack); + + } + + if ( hThread != GetCurrentThread() ) + ResumeThread( hThread ); +} + +void StackTrace(HANDLE hThread, const char* lpszMessage, FILE *file, DWORD eip, DWORD esp, DWORD ebp ) +{ + STACKFRAME callStack; + BOOL bResult; + TCHAR symInfo[BUFFERSIZE] = _T("?"); + TCHAR srcInfo[BUFFERSIZE] = _T("?"); + HANDLE hProcess = GetCurrentProcess(); + + // If it's not this thread, let's suspend it, and resume it at the end + if ( hThread != GetCurrentThread() ) + if ( SuspendThread( hThread ) == -1 ) + { + // whaaat ?! + etfprint(file, "Call stack info failed\n"); + return; + } + + ::ZeroMemory( &callStack, sizeof(callStack) ); + callStack.AddrPC.Offset = eip; + callStack.AddrStack.Offset = esp; + callStack.AddrFrame.Offset = ebp; + callStack.AddrPC.Mode = AddrModeFlat; + callStack.AddrStack.Mode = AddrModeFlat; + callStack.AddrFrame.Mode = AddrModeFlat; + + etfprint(file, "Call stack info: \n"); + etfprint(file, lpszMessage); + + PrintFunctionAndSourceInfo(file, callStack); + + for( ULONG index = 0; ; index++ ) + { + bResult = StackWalk( + IMAGE_FILE_MACHINE_I386, + hProcess, + hThread, + &callStack, + NULL, + NULL, + SymFunctionTableAccess, + SymGetModuleBase, + NULL); + + if ( index == 0 ) + continue; + + if( !bResult || callStack.AddrFrame.Offset == 0 ) + break; + + PrintFunctionAndSourceInfo(file, callStack); + } + + if ( hThread != GetCurrentThread() ) + ResumeThread( hThread ); +} + +char g_uefbuf[2048]; + +void etfprintf(FILE *file, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + int len = vsprintf(g_uefbuf, format, ap); + fwrite(g_uefbuf, 1, len, file); + va_end(ap); +} + +void etfprint(FILE *file, const std::string &text) +{ + size_t len = text.length(); + fwrite(text.data(), 1, len, file); +} + +#endif //WIN32 diff --git a/src/common/src/extended_trace.h b/src/common/src/extended_trace.h new file mode 100644 index 0000000000..6d33eb632d --- /dev/null +++ b/src/common/src/extended_trace.h @@ -0,0 +1,53 @@ +// ----------------------------------------------------------------------------------------- +// +// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com +// For companies(Austin,TX): If you would like to get my resume, send an email. +// +// The source is free, but if you want to use it, mention my name and e-mail address +// +// History: +// 1.0 Initial version Zoltan Csizmadia +// 1.1 WhineCube version Masken +// 1.2 Dolphin version Masken +// +// ---------------------------------------------------------------------------------------- + +#ifndef _EXTENDEDTRACE_H_INCLUDED_ +#define _EXTENDEDTRACE_H_INCLUDED_ + +#if defined(WIN32) + +#include +#include + +#include + +#pragma comment( lib, "imagehlp.lib" ) + +#define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) InitSymInfo( IniSymbolPath ) +#define EXTENDEDTRACEUNINITIALIZE() UninitSymInfo() +#define STACKTRACE(file) StackTrace( GetCurrentThread(), "", file) +#define STACKTRACE2(file, eip, esp, ebp) StackTrace(GetCurrentThread(), "", file, eip, esp, ebp) +// class File; + +BOOL InitSymInfo( PCSTR ); +BOOL UninitSymInfo(); +void StackTrace(HANDLE, char const* msg, FILE *file); +void StackTrace(HANDLE, char const* msg, FILE *file, DWORD eip, DWORD esp, DWORD ebp); + +// functions by Masken +void etfprintf(FILE *file, const char *format, ...); +void etfprint(FILE *file, const std::string &text); +#define UEFBUFSIZE 2048 +extern char g_uefbuf[UEFBUFSIZE]; + +#else // not WIN32 + +#define EXTENDEDTRACEINITIALIZE( IniSymbolPath ) ((void)0) +#define EXTENDEDTRACEUNINITIALIZE() ((void)0) +#define STACKTRACE(file) ((void)0) +#define STACKTRACE2(file, eip, esp, ebp) ((void)0) + +#endif // WIN32 + +#endif // _EXTENDEDTRACE_H_INCLUDED_ diff --git a/src/common/src/fifo_queue.h b/src/common/src/fifo_queue.h new file mode 100644 index 0000000000..4f5ca57065 --- /dev/null +++ b/src/common/src/fifo_queue.h @@ -0,0 +1,115 @@ + +#ifndef _FIFO_QUEUE_H_ +#define _FIFO_QUEUE_H_ + +// a simple lockless thread-safe, +// single reader, single writer queue + +#include "atomic.h" + +namespace Common +{ + +template +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 + void Push(Arg&& t) + { + // create the element, add it to the queue + m_write_ptr->current = new T(std::forward(t)); + // set the next pointer to a new element ptr + // then advance the write pointer + m_write_ptr = m_write_ptr->next = new ElementPtr(); + Common::AtomicIncrement(m_size); + } + + void Pop() + { + Common::AtomicDecrement(m_size); + ElementPtr *const tmpptr = m_read_ptr; + // advance the read pointer + m_read_ptr = m_read_ptr->next; + // set the next element to NULL to stop the recursive deletion + tmpptr->next = NULL; + delete tmpptr; // this also deletes the element + } + + bool Pop(T& t) + { + if (Empty()) + return false; + + t = std::move(Front()); + Pop(); + + return true; + } + + // not thread-safe + void Clear() + { + m_size = 0; + delete m_read_ptr; + m_write_ptr = m_read_ptr = new ElementPtr(); + } + +private: + // stores a pointer to element + // and a pointer to the next ElementPtr + class ElementPtr + { + public: + ElementPtr() : current(NULL), next(NULL) {} + + ~ElementPtr() + { + if (current) + { + delete current; + // recusion ftw + if (next) + delete next; + } + } + + T *volatile current; + ElementPtr *volatile next; + }; + + ElementPtr *volatile m_write_ptr; + ElementPtr *volatile m_read_ptr; + volatile u32 m_size; +}; + +} + +#endif diff --git a/src/common/src/file_search.cpp b/src/common/src/file_search.cpp new file mode 100644 index 0000000000..ba140ec12a --- /dev/null +++ b/src/common/src/file_search.cpp @@ -0,0 +1,106 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#include "common.h" +#include "common_paths.h" +#ifndef _WIN32 +#include +#include +#else +#include +#endif + +#include +#include + +#include "file_search.h" + +#include "string_util.h" + + +CFileSearch::CFileSearch(const CFileSearch::XStringVector& _rSearchStrings, const CFileSearch::XStringVector& _rDirectories) +{ + // Reverse the loop order for speed? + for (size_t j = 0; j < _rSearchStrings.size(); j++) + { + for (size_t i = 0; i < _rDirectories.size(); i++) + { + FindFiles(_rSearchStrings[j], _rDirectories[i]); + } + } +} + + +void CFileSearch::FindFiles(const std::string& _searchString, const std::string& _strPath) +{ + std::string GCMSearchPath; + BuildCompleteFilename(GCMSearchPath, _strPath, _searchString); +#ifdef _WIN32 + WIN32_FIND_DATA findData; + HANDLE FindFirst = FindFirstFile(UTF8ToTStr(GCMSearchPath).c_str(), &findData); + + if (FindFirst != INVALID_HANDLE_VALUE) + { + bool bkeepLooping = true; + + while (bkeepLooping) + { + if (findData.cFileName[0] != '.') + { + std::string strFilename; + BuildCompleteFilename(strFilename, _strPath, TStrToUTF8(findData.cFileName)); + m_FileNames.push_back(strFilename); + } + + bkeepLooping = FindNextFile(FindFirst, &findData) ? true : false; + } + } + FindClose(FindFirst); + + +#else + // TODO: super lame/broken + + auto end_match(_searchString); + + // assuming we have a "*.blah"-like pattern + if (!end_match.empty() && end_match[0] == '*') + end_match.erase(0, 1); + + // ugly + if (end_match == ".*") + end_match.clear(); + + DIR* dir = opendir(_strPath.c_str()); + + if (!dir) + return; + + while (auto const dp = readdir(dir)) + { + std::string found(dp->d_name); + + if ((found != ".") && (found != "..") + && (found.size() >= end_match.size()) + && std::equal(end_match.rbegin(), end_match.rend(), found.rbegin())) + { + std::string full_name; + if (_strPath.c_str()[_strPath.size()-1] == DIR_SEP_CHR) + full_name = _strPath + found; + else + full_name = _strPath + DIR_SEP + found; + + m_FileNames.push_back(full_name); + } + } + + closedir(dir); +#endif +} + +const CFileSearch::XStringVector& CFileSearch::GetFileNames() const +{ + return m_FileNames; +} diff --git a/src/common/src/file_search.h b/src/common/src/file_search.h new file mode 100644 index 0000000000..55aaf4ebe6 --- /dev/null +++ b/src/common/src/file_search.h @@ -0,0 +1,28 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#ifndef _FILESEARCH_H_ +#define _FILESEARCH_H_ + +#include +#include + +class CFileSearch +{ +public: + typedef std::vectorXStringVector; + + CFileSearch(const XStringVector& _rSearchStrings, const XStringVector& _rDirectories); + const XStringVector& GetFileNames() const; + +private: + + void FindFiles(const std::string& _searchString, const std::string& _strPath); + + XStringVector m_FileNames; +}; + +#endif // _FILESEARCH_H_ + diff --git a/src/common/src/file_util.cpp b/src/common/src/file_util.cpp new file mode 100644 index 0000000000..f86414bf36 --- /dev/null +++ b/src/common/src/file_util.cpp @@ -0,0 +1,922 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#include "common.h" +#include "common_paths.h" +#include "file_util.h" +#include "string_util.h" + +#ifdef _WIN32 +#include +#include // for SHGetFolderPath +#include +#include // for GetSaveFileName +#include +#include // getcwd +#else +#include +#include +#include +#include +#include +#endif + +#if defined(__APPLE__) +#include +#include +#include +#endif + +#include +#include + +#include "string_util.h" + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) +#endif + +#ifdef BSD4_4 +#define stat64 stat +#define fstat64 fstat +#endif + +// This namespace has various generic functions related to files and paths. +// The code still needs a ton of cleanup. +// REMEMBER: strdup considered harmful! +namespace File +{ + +// Remove any ending forward slashes from directory paths +// Modifies argument. +static void StripTailDirSlashes(std::string &fname) +{ + if (fname.length() > 1) + { + size_t i = fname.length() - 1; + while (fname[i] == DIR_SEP_CHR) + fname[i--] = '\0'; + } + return; +} + +// Returns true if file filename exists +bool Exists(const std::string &filename) +{ + struct stat64 file_info; + + std::string copy(filename); + StripTailDirSlashes(copy); + +#ifdef _WIN32 + int result = _tstat64(UTF8ToTStr(copy).c_str(), &file_info); +#else + int result = stat64(copy.c_str(), &file_info); +#endif + + return (result == 0); +} + +// Returns true if filename is a directory +bool IsDirectory(const std::string &filename) +{ + struct stat64 file_info; + + std::string copy(filename); + StripTailDirSlashes(copy); + +#ifdef _WIN32 + int result = _tstat64(UTF8ToTStr(copy).c_str(), &file_info); +#else + int result = stat64(copy.c_str(), &file_info); +#endif + + if (result < 0) { + WARN_LOG(COMMON, "IsDirectory: stat failed on %s: %s", + filename.c_str(), GetLastErrorMsg()); + return false; + } + + return S_ISDIR(file_info.st_mode); +} + +// Deletes a given filename, return true on success +// Doesn't supports deleting a directory +bool Delete(const std::string &filename) +{ + INFO_LOG(COMMON, "Delete: file %s", filename.c_str()); + + // Return true because we care about the file no + // being there, not the actual delete. + if (!Exists(filename)) + { + WARN_LOG(COMMON, "Delete: %s does not exist", filename.c_str()); + return true; + } + + // We can't delete a directory + if (IsDirectory(filename)) + { + WARN_LOG(COMMON, "Delete failed: %s is a directory", filename.c_str()); + return false; + } + +#ifdef _WIN32 + if (!DeleteFile(UTF8ToTStr(filename).c_str())) + { + WARN_LOG(COMMON, "Delete: DeleteFile failed on %s: %s", + filename.c_str(), GetLastErrorMsg()); + return false; + } +#else + if (unlink(filename.c_str()) == -1) { + WARN_LOG(COMMON, "Delete: unlink failed on %s: %s", + filename.c_str(), GetLastErrorMsg()); + return false; + } +#endif + + return true; +} + +// Returns true if successful, or path already exists. +bool CreateDir(const std::string &path) +{ + INFO_LOG(COMMON, "CreateDir: directory %s", path.c_str()); +#ifdef _WIN32 + if (::CreateDirectory(UTF8ToTStr(path).c_str(), NULL)) + return true; + DWORD error = GetLastError(); + if (error == ERROR_ALREADY_EXISTS) + { + WARN_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: already exists", path.c_str()); + return true; + } + ERROR_LOG(COMMON, "CreateDir: CreateDirectory failed on %s: %i", path.c_str(), error); + return false; +#else + if (mkdir(path.c_str(), 0755) == 0) + return true; + + int err = errno; + + if (err == EEXIST) + { + WARN_LOG(COMMON, "CreateDir: mkdir failed on %s: already exists", path.c_str()); + return true; + } + + ERROR_LOG(COMMON, "CreateDir: mkdir failed on %s: %s", path.c_str(), strerror(err)); + return false; +#endif +} + +// Creates the full path of fullPath returns true on success +bool CreateFullPath(const std::string &fullPath) +{ + int panicCounter = 100; + INFO_LOG(COMMON, "CreateFullPath: path %s", fullPath.c_str()); + + if (File::Exists(fullPath)) + { + INFO_LOG(COMMON, "CreateFullPath: path exists %s", fullPath.c_str()); + return true; + } + + size_t position = 0; + while (true) + { + // Find next sub path + position = fullPath.find(DIR_SEP_CHR, position); + + // we're done, yay! + if (position == fullPath.npos) + return true; + + // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") + std::string const subPath(fullPath.substr(0, position + 1)); + if (!File::IsDirectory(subPath)) + File::CreateDir(subPath); + + // A safety check + panicCounter--; + if (panicCounter <= 0) + { + ERROR_LOG(COMMON, "CreateFullPath: directory structure is too deep"); + return false; + } + position++; + } +} + + +// Deletes a directory filename, returns true on success +bool DeleteDir(const std::string &filename) +{ + INFO_LOG(COMMON, "DeleteDir: directory %s", filename.c_str()); + + // check if a directory + if (!File::IsDirectory(filename)) + { + ERROR_LOG(COMMON, "DeleteDir: Not a directory %s", filename.c_str()); + return false; + } + +#ifdef _WIN32 + if (::RemoveDirectory(UTF8ToTStr(filename).c_str())) + return true; +#else + if (rmdir(filename.c_str()) == 0) + return true; +#endif + ERROR_LOG(COMMON, "DeleteDir: %s: %s", filename.c_str(), GetLastErrorMsg()); + + return false; +} + +// renames file srcFilename to destFilename, returns true on success +bool Rename(const std::string &srcFilename, const std::string &destFilename) +{ + INFO_LOG(COMMON, "Rename: %s --> %s", + srcFilename.c_str(), destFilename.c_str()); + if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) + return true; + ERROR_LOG(COMMON, "Rename: failed %s --> %s: %s", + srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); + return false; +} + +// copies file srcFilename to destFilename, returns true on success +bool Copy(const std::string &srcFilename, const std::string &destFilename) +{ + INFO_LOG(COMMON, "Copy: %s --> %s", + srcFilename.c_str(), destFilename.c_str()); +#ifdef _WIN32 + if (CopyFile(UTF8ToTStr(srcFilename).c_str(), UTF8ToTStr(destFilename).c_str(), FALSE)) + return true; + + ERROR_LOG(COMMON, "Copy: failed %s --> %s: %s", + srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); + return false; +#else + + // buffer size +#define BSIZE 1024 + + char buffer[BSIZE]; + + // Open input file + FILE *input = fopen(srcFilename.c_str(), "rb"); + if (!input) + { + ERROR_LOG(COMMON, "Copy: input failed %s --> %s: %s", + srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); + return false; + } + + // open output file + FILE *output = fopen(destFilename.c_str(), "wb"); + if (!output) + { + fclose(input); + ERROR_LOG(COMMON, "Copy: output failed %s --> %s: %s", + srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); + return false; + } + + // copy loop + while (!feof(input)) + { + // read input + int rnum = fread(buffer, sizeof(char), BSIZE, input); + if (rnum != BSIZE) + { + if (ferror(input) != 0) + { + ERROR_LOG(COMMON, + "Copy: failed reading from source, %s --> %s: %s", + srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); + goto bail; + } + } + + // write output + int wnum = fwrite(buffer, sizeof(char), rnum, output); + if (wnum != rnum) + { + ERROR_LOG(COMMON, + "Copy: failed writing to output, %s --> %s: %s", + srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg()); + goto bail; + } + } + // close files + fclose(input); + fclose(output); + return true; +bail: + if (input) + fclose(input); + if (output) + fclose(output); + return false; +#endif +} + +// Returns the size of filename (64bit) +u64 GetSize(const std::string &filename) +{ + if (!Exists(filename)) + { + WARN_LOG(COMMON, "GetSize: failed %s: No such file", filename.c_str()); + return 0; + } + + if (IsDirectory(filename)) + { + WARN_LOG(COMMON, "GetSize: failed %s: is a directory", filename.c_str()); + return 0; + } + + struct stat64 buf; +#ifdef _WIN32 + if (_tstat64(UTF8ToTStr(filename).c_str(), &buf) == 0) +#else + if (stat64(filename.c_str(), &buf) == 0) +#endif + { + DEBUG_LOG(COMMON, "GetSize: %s: %lld", + filename.c_str(), (long long)buf.st_size); + return buf.st_size; + } + + ERROR_LOG(COMMON, "GetSize: Stat failed %s: %s", + filename.c_str(), GetLastErrorMsg()); + return 0; +} + +// Overloaded GetSize, accepts file descriptor +u64 GetSize(const int fd) +{ + struct stat64 buf; + if (fstat64(fd, &buf) != 0) { + ERROR_LOG(COMMON, "GetSize: stat failed %i: %s", + fd, GetLastErrorMsg()); + return 0; + } + return buf.st_size; +} + +// Overloaded GetSize, accepts FILE* +u64 GetSize(FILE *f) +{ + // can't use off_t here because it can be 32-bit + u64 pos = ftello(f); + if (fseeko(f, 0, SEEK_END) != 0) { + ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", + f, GetLastErrorMsg()); + return 0; + } + u64 size = ftello(f); + if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { + ERROR_LOG(COMMON, "GetSize: seek failed %p: %s", + f, GetLastErrorMsg()); + return 0; + } + return size; +} + +// creates an empty file filename, returns true on success +bool CreateEmptyFile(const std::string &filename) +{ + INFO_LOG(COMMON, "CreateEmptyFile: %s", filename.c_str()); + + if (!File::IOFile(filename, "wb")) + { + ERROR_LOG(COMMON, "CreateEmptyFile: failed %s: %s", + filename.c_str(), GetLastErrorMsg()); + return false; + } + + return true; +} + + +// Scans the directory tree gets, starting from _Directory and adds the +// results into parentEntry. Returns the number of files+directories found +u32 ScanDirectoryTree(const std::string &directory, FSTEntry& parentEntry) +{ + INFO_LOG(COMMON, "ScanDirectoryTree: directory %s", directory.c_str()); + // How many files + directories we found + u32 foundEntries = 0; +#ifdef _WIN32 + // Find the first file in the directory. + WIN32_FIND_DATA ffd; + + HANDLE hFind = FindFirstFile(UTF8ToTStr(directory + "\\*").c_str(), &ffd); + if (hFind == INVALID_HANDLE_VALUE) + { + FindClose(hFind); + return foundEntries; + } + // windows loop + do + { + FSTEntry entry; + const std::string virtualName(TStrToUTF8(ffd.cFileName)); +#else + struct dirent dirent, *result = NULL; + + DIR *dirp = opendir(directory.c_str()); + if (!dirp) + return 0; + + // non windows loop + while (!readdir_r(dirp, &dirent, &result) && result) + { + FSTEntry entry; + const std::string virtualName(result->d_name); +#endif + // check for "." and ".." + if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || + ((virtualName[0] == '.') && (virtualName[1] == '.') && + (virtualName[2] == '\0'))) + continue; + entry.virtualName = virtualName; + entry.physicalName = directory; + entry.physicalName += DIR_SEP + entry.virtualName; + + if (IsDirectory(entry.physicalName.c_str())) + { + entry.isDirectory = true; + // is a directory, lets go inside + entry.size = ScanDirectoryTree(entry.physicalName, entry); + foundEntries += (u32)entry.size; + } + else + { // is a file + entry.isDirectory = false; + entry.size = GetSize(entry.physicalName.c_str()); + } + ++foundEntries; + // Push into the tree + parentEntry.children.push_back(entry); +#ifdef _WIN32 + } while (FindNextFile(hFind, &ffd) != 0); + FindClose(hFind); +#else + } + closedir(dirp); +#endif + // Return number of entries found. + return foundEntries; +} + + +// Deletes the given directory and anything under it. Returns true on success. +bool DeleteDirRecursively(const std::string &directory) +{ + INFO_LOG(COMMON, "DeleteDirRecursively: %s", directory.c_str()); +#ifdef _WIN32 + // Find the first file in the directory. + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFile(UTF8ToTStr(directory + "\\*").c_str(), &ffd); + + if (hFind == INVALID_HANDLE_VALUE) + { + FindClose(hFind); + return false; + } + + // windows loop + do + { + const std::string virtualName(TStrToUTF8(ffd.cFileName)); +#else + struct dirent dirent, *result = NULL; + DIR *dirp = opendir(directory.c_str()); + if (!dirp) + return false; + + // non windows loop + while (!readdir_r(dirp, &dirent, &result) && result) + { + const std::string virtualName = result->d_name; +#endif + + // check for "." and ".." + if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || + ((virtualName[0] == '.') && (virtualName[1] == '.') && + (virtualName[2] == '\0'))) + continue; + + std::string newPath = directory + DIR_SEP_CHR + virtualName; + if (IsDirectory(newPath)) + { + if (!DeleteDirRecursively(newPath)) + { + #ifndef _WIN32 + closedir(dirp); + #endif + + return false; + } + } + else + { + if (!File::Delete(newPath)) + { + #ifndef _WIN32 + closedir(dirp); + #endif + + return false; + } + } + +#ifdef _WIN32 + } while (FindNextFile(hFind, &ffd) != 0); + FindClose(hFind); +#else + } + closedir(dirp); +#endif + File::DeleteDir(directory); + + return true; +} + +// Create directory and copy contents (does not overwrite existing files) +void CopyDir(const std::string &source_path, const std::string &dest_path) +{ +#ifndef _WIN32 + if (source_path == dest_path) return; + if (!File::Exists(source_path)) return; + if (!File::Exists(dest_path)) File::CreateFullPath(dest_path); + + struct dirent dirent, *result = NULL; + DIR *dirp = opendir(source_path.c_str()); + if (!dirp) return; + + while (!readdir_r(dirp, &dirent, &result) && result) + { + const std::string virtualName(result->d_name); + // check for "." and ".." + if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || + ((virtualName[0] == '.') && (virtualName[1] == '.') && + (virtualName[2] == '\0'))) + continue; + + std::string source, dest; + source = source_path + virtualName; + dest = dest_path + virtualName; + if (IsDirectory(source)) + { + source += '/'; + dest += '/'; + if (!File::Exists(dest)) File::CreateFullPath(dest); + CopyDir(source, dest); + } + else if (!File::Exists(dest)) File::Copy(source, dest); + } + closedir(dirp); +#endif +} + +// Returns the current directory +std::string GetCurrentDir() +{ + char *dir; + // Get the current working directory (getcwd uses malloc) + if (!(dir = __getcwd(NULL, 0))) { + + ERROR_LOG(COMMON, "GetCurrentDirectory failed: %s", + GetLastErrorMsg()); + return NULL; + } + std::string strDir = dir; + free(dir); + return strDir; +} + +// Sets the current directory to the given directory +bool SetCurrentDir(const std::string &directory) +{ + return __chdir(directory.c_str()) == 0; +} + +#if defined(__APPLE__) +std::string GetBundleDirectory() +{ + CFURLRef BundleRef; + char AppBundlePath[MAXPATHLEN]; + // Get the main bundle for the app + BundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + CFStringRef BundlePath = CFURLCopyFileSystemPath(BundleRef, kCFURLPOSIXPathStyle); + CFStringGetFileSystemRepresentation(BundlePath, AppBundlePath, sizeof(AppBundlePath)); + CFRelease(BundleRef); + CFRelease(BundlePath); + + return AppBundlePath; +} +#endif + +#ifdef _WIN32 +std::string& GetExeDirectory() +{ + static std::string DolphinPath; + if (DolphinPath.empty()) + { + TCHAR Dolphin_exe_Path[2048]; + GetModuleFileName(NULL, Dolphin_exe_Path, 2048); + DolphinPath = TStrToUTF8(Dolphin_exe_Path); + DolphinPath = DolphinPath.substr(0, DolphinPath.find_last_of('\\')); + } + return DolphinPath; +} +#endif + +std::string GetSysDirectory() +{ + std::string sysDir; + +#if defined (__APPLE__) + sysDir = GetBundleDirectory(); + sysDir += DIR_SEP; + sysDir += SYSDATA_DIR; +#else + sysDir = SYSDATA_DIR; +#endif + sysDir += DIR_SEP; + + INFO_LOG(COMMON, "GetSysDirectory: Setting to %s:", sysDir.c_str()); + return sysDir; +} + +// Returns a string with a Dolphin data dir or file in the user's home +// directory. To be used in "multi-user" mode (that is, installed). +//const std::string& GetUserPath(const unsigned int DirIDX, const std::string &newPath) +//{ +// static std::string paths[NUM_PATH_INDICES]; +// +// // Set up all paths and files on the first run +// if (paths[D_USER_IDX].empty()) +// { +//#ifdef _WIN32 +// paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; +//#else +// if (File::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) +// paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; +// else +// paths[D_USER_IDX] = std::string(getenv("HOME") ? +// getenv("HOME") : getenv("PWD") ? +// getenv("PWD") : "") + DIR_SEP DOLPHIN_DATA_DIR DIR_SEP; +//#endif +// +// paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; +// paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; +// paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; +// paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; +// paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; +// paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; +// paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; +// paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP; +// paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP; +// paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; +// paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; +// paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; +// paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP; +// paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; +// paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; +// paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; +// } +// +// if (!newPath.empty()) +// { +// if (!File::IsDirectory(newPath)) +// { +// WARN_LOG(COMMON, "Invalid path specified %s", newPath.c_str()); +// return paths[DirIDX]; +// } +// else +// { +// paths[DirIDX] = newPath; +// } +// +// switch (DirIDX) +// { +// case D_WIIROOT_IDX: +// paths[D_WIIUSER_IDX] = paths[D_WIIROOT_IDX] + DIR_SEP; +// paths[D_WIISYSCONF_IDX] = paths[D_WIIUSER_IDX] + WII_SYSCONF_DIR + DIR_SEP; +// paths[F_WIISYSCONF_IDX] = paths[D_WIISYSCONF_IDX] + WII_SYSCONF; +// break; +// +// case D_USER_IDX: +// paths[D_GCUSER_IDX] = paths[D_USER_IDX] + GC_USER_DIR DIR_SEP; +// paths[D_WIIROOT_IDX] = paths[D_USER_IDX] + WII_USER_DIR; +// paths[D_WIIUSER_IDX] = paths[D_WIIROOT_IDX] + DIR_SEP; +// paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; +// paths[D_GAMECONFIG_IDX] = paths[D_USER_IDX] + GAMECONFIG_DIR DIR_SEP; +// paths[D_MAPS_IDX] = paths[D_USER_IDX] + MAPS_DIR DIR_SEP; +// paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; +// paths[D_SHADERCACHE_IDX] = paths[D_USER_IDX] + SHADERCACHE_DIR DIR_SEP; +// paths[D_SHADERS_IDX] = paths[D_USER_IDX] + SHADERS_DIR DIR_SEP; +// paths[D_STATESAVES_IDX] = paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP; +// paths[D_SCREENSHOTS_IDX] = paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP; +// paths[D_OPENCL_IDX] = paths[D_USER_IDX] + OPENCL_DIR DIR_SEP; +// paths[D_HIRESTEXTURES_IDX] = paths[D_USER_IDX] + HIRES_TEXTURES_DIR DIR_SEP; +// paths[D_DUMP_IDX] = paths[D_USER_IDX] + DUMP_DIR DIR_SEP; +// paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; +// paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; +// paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; +// paths[D_DUMPDSP_IDX] = paths[D_DUMP_IDX] + DUMP_DSP_DIR DIR_SEP; +// paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOGS_DIR DIR_SEP; +// paths[D_MAILLOGS_IDX] = paths[D_LOGS_IDX] + MAIL_LOGS_DIR DIR_SEP; +// paths[D_WIISYSCONF_IDX] = paths[D_WIIUSER_IDX] + WII_SYSCONF_DIR DIR_SEP; +// paths[D_THEMES_IDX] = paths[D_USER_IDX] + THEMES_DIR DIR_SEP; +// paths[F_DOLPHINCONFIG_IDX] = paths[D_CONFIG_IDX] + DOLPHIN_CONFIG; +// paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; +// paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; +// paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; +// paths[F_WIISYSCONF_IDX] = paths[D_WIISYSCONF_IDX] + WII_SYSCONF; +// paths[F_RAMDUMP_IDX] = paths[D_DUMP_IDX] + RAM_DUMP; +// paths[F_ARAMDUMP_IDX] = paths[D_DUMP_IDX] + ARAM_DUMP; +// paths[F_FAKEVMEMDUMP_IDX] = paths[D_DUMP_IDX] + FAKEVMEM_DUMP; +// paths[F_GCSRAM_IDX] = paths[D_GCUSER_IDX] + GC_SRAM; +// break; +// +// case D_CONFIG_IDX: +// paths[F_DOLPHINCONFIG_IDX] = paths[D_CONFIG_IDX] + DOLPHIN_CONFIG; +// paths[F_DEBUGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + DEBUGGER_CONFIG; +// paths[F_LOGGERCONFIG_IDX] = paths[D_CONFIG_IDX] + LOGGER_CONFIG; +// break; +// +// case D_DUMP_IDX: +// paths[D_DUMPFRAMES_IDX] = paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP; +// paths[D_DUMPAUDIO_IDX] = paths[D_DUMP_IDX] + DUMP_AUDIO_DIR DIR_SEP; +// paths[D_DUMPTEXTURES_IDX] = paths[D_DUMP_IDX] + DUMP_TEXTURES_DIR DIR_SEP; +// break; +// +// case D_LOGS_IDX: +// paths[F_MAINLOG_IDX] = paths[D_LOGS_IDX] + MAIN_LOG; +// } +// } +// +// return paths[DirIDX]; +//} + +std::string GetThemeDir(const std::string& theme_name) +{ + std::string dir = File::GetUserPath(D_THEMES_IDX) + theme_name + "/"; + +#if !defined(_WIN32) + // If theme does not exist in user's dir load from shared directory + if (!File::Exists(dir)) + dir = SHARED_USER_DIR THEMES_DIR "/" + theme_name + "/"; +#endif + + return dir; +} + +bool WriteStringToFile(bool text_file, const std::string &str, const char *filename) +{ + return File::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); +} + +bool ReadFileToString(bool text_file, const char *filename, std::string &str) +{ + File::IOFile file(filename, text_file ? "r" : "rb"); + auto const f = file.GetHandle(); + + if (!f) + return false; + + str.resize(GetSize(f)); + return file.ReadArray(&str[0], str.size()); +} + +IOFile::IOFile() + : m_file(NULL), m_good(true) +{} + +IOFile::IOFile(std::FILE* file) + : m_file(file), m_good(true) +{} + +IOFile::IOFile(const std::string& filename, const char openmode[]) + : m_file(NULL), m_good(true) +{ + Open(filename, openmode); +} + +IOFile::~IOFile() +{ + Close(); +} + +IOFile::IOFile(IOFile&& other) + : m_file(NULL), m_good(true) +{ + Swap(other); +} + +IOFile& IOFile::operator=(IOFile&& other) +{ + Swap(other); + return *this; +} + +void IOFile::Swap(IOFile& other) +{ + std::swap(m_file, other.m_file); + std::swap(m_good, other.m_good); +} + +bool IOFile::Open(const std::string& filename, const char openmode[]) +{ + Close(); +#ifdef _WIN32 + _tfopen_s(&m_file, UTF8ToTStr(filename).c_str(), UTF8ToTStr(openmode).c_str()); +#else + m_file = fopen(filename.c_str(), openmode); +#endif + + m_good = IsOpen(); + return m_good; +} + +bool IOFile::Close() +{ + if (!IsOpen() || 0 != std::fclose(m_file)) + m_good = false; + + m_file = NULL; + return m_good; +} + +std::FILE* IOFile::ReleaseHandle() +{ + std::FILE* const ret = m_file; + m_file = NULL; + return ret; +} + +void IOFile::SetHandle(std::FILE* file) +{ + Close(); + Clear(); + m_file = file; +} + +u64 IOFile::GetSize() +{ + if (IsOpen()) + return File::GetSize(m_file); + else + return 0; +} + +bool IOFile::Seek(s64 off, int origin) +{ + if (!IsOpen() || 0 != fseeko(m_file, off, origin)) + m_good = false; + + return m_good; +} + +u64 IOFile::Tell() +{ + if (IsOpen()) + return ftello(m_file); + else + return -1; +} + +bool IOFile::Flush() +{ + if (!IsOpen() || 0 != std::fflush(m_file)) + m_good = false; + + return m_good; +} + +bool IOFile::Resize(u64 size) +{ + if (!IsOpen() || 0 != +#ifdef _WIN32 + // ector: _chsize sucks, not 64-bit safe + // F|RES: changed to _chsize_s. i think it is 64-bit safe + _chsize_s(_fileno(m_file), size) +#else + // TODO: handle 64bit and growing + ftruncate(fileno(m_file), size) +#endif + ) + m_good = false; + + return m_good; +} + +} // namespace diff --git a/src/common/src/file_util.h b/src/common/src/file_util.h new file mode 100644 index 0000000000..a6bd085ab1 --- /dev/null +++ b/src/common/src/file_util.h @@ -0,0 +1,232 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#ifndef _FILEUTIL_H_ +#define _FILEUTIL_H_ + +#include +#include +#include +#include +#include + +#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 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 + 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 + 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(data), length); + } + + bool WriteBytes(const void* data, size_t length) + { + return WriteArray(reinterpret_cast(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 +void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) +{ +#ifdef _WIN32 + fstream.open(UTF8ToTStr(filename).c_str(), openmode); +#else + fstream.open(filename.c_str(), openmode); +#endif +} + +#endif diff --git a/src/common/src/fixed_size_queue.h b/src/common/src/fixed_size_queue.h new file mode 100644 index 0000000000..4045dfa339 --- /dev/null +++ b/src/common/src/fixed_size_queue.h @@ -0,0 +1,75 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#ifndef _FIXED_SIZE_QUEUE_H_ +#define _FIXED_SIZE_QUEUE_H_ + +// STL-look-a-like interface, but name is mixed case to distinguish it clearly from the +// real STL classes. + +// Not fully featured, no safety checking yet. Add features as needed. + +// TODO: "inline" storage? + +template +class fixed_size_queue.h +{ + T *storage; + int head; + int tail; + int count; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future. + + // Make copy constructor private for now. + fixed_size_queue.h(fixed_size_queue.h &other) { } + +public: + fixed_size_queue.h() + { + storage = new T[N]; + clear(); + } + + ~fixed_size_queue.h() + { + delete [] storage; + } + + void clear() { + head = 0; + tail = 0; + count = 0; + } + + void push(T t) { + storage[tail] = t; + tail++; + if (tail == N) + tail = 0; + count++; + } + + void pop() { + head++; + if (head == N) + head = 0; + count--; + } + + T pop_front() { + const T &temp = storage[head]; + pop(); + return temp; + } + + T &front() { return storage[head]; } + const T &front() const { return storage[head]; } + + size_t size() const { + return count; + } +}; + +#endif // _FIXED_SIZE_QUEUE_H_ + diff --git a/src/common/src/hash.cpp b/src/common/src/hash.cpp new file mode 100644 index 0000000000..39c636419c --- /dev/null +++ b/src/common/src/hash.cpp @@ -0,0 +1,520 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#include "hash.h" +#if _M_SSE >= 0x402 +#include "cpu_detect.h" +#include +#endif + +static u64 (*ptrHashFunction)(const u8 *src, int len, u32 samples) = &GetMurmurHash3; + +// uint32_t +// WARNING - may read one more byte! +// Implementation from Wikipedia. +u32 HashFletcher(const u8* data_u8, size_t length) +{ + const u16* data = (const u16*)data_u8; /* Pointer to the data to be summed */ + size_t len = (length + 1) / 2; /* Length in 16-bit words */ + u32 sum1 = 0xffff, sum2 = 0xffff; + + while (len) + { + size_t tlen = len > 360 ? 360 : len; + len -= tlen; + + do { + sum1 += *data++; + sum2 += sum1; + } + while (--tlen); + + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + } + + // Second reduction step to reduce sums to 16 bits + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + return(sum2 << 16 | sum1); +} + + +// Implementation from Wikipedia +// Slightly slower than Fletcher above, but slightly more reliable. +#define MOD_ADLER 65521 +// data: Pointer to the data to be summed; len is in bytes +u32 HashAdler32(const u8* data, size_t len) +{ + u32 a = 1, b = 0; + + while (len) + { + size_t tlen = len > 5550 ? 5550 : len; + len -= tlen; + + do + { + a += *data++; + b += a; + } + while (--tlen); + + a = (a & 0xffff) + (a >> 16) * (65536 - MOD_ADLER); + b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); + } + + // It can be shown that a <= 0x1013a here, so a single subtract will do. + if (a >= MOD_ADLER) + { + a -= MOD_ADLER; + } + + // It can be shown that b can reach 0xfff87 here. + b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); + + if (b >= MOD_ADLER) + { + b -= MOD_ADLER; + } + + return((b << 16) | a); +} + +// Stupid hash - but can't go back now :) +// Don't use for new things. At least it's reasonably fast. +u32 HashEctor(const u8* ptr, int length) +{ + u32 crc = 0; + + for (int i = 0; i < length; i++) + { + crc ^= ptr[i]; + crc = (crc << 3) | (crc >> 29); + } + + return(crc); +} + + +#ifdef _M_X64 + +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here + +inline u64 getblock(const u64 * p, int i) +{ + return p[i]; +} + +//---------- +// Block mix - combine the key bits with the hash bits and scramble everything + +inline void bmix64(u64 & h1, u64 & h2, u64 & k1, u64 & k2, u64 & c1, u64 & c2) +{ + k1 *= c1; + k1 = _rotl64(k1,23); + k1 *= c2; + h1 ^= k1; + h1 += h2; + + h2 = _rotl64(h2,41); + + k2 *= c2; + k2 = _rotl64(k2,23); + k2 *= c1; + h2 ^= k2; + h2 += h1; + + h1 = h1*3+0x52dce729; + h2 = h2*3+0x38495ab5; + + c1 = c1*5+0x7b7d159c; + c2 = c2*5+0x6bce6396; +} + +//---------- +// Finalization mix - avalanches all bits to within 0.05% bias + +inline u64 fmix64(u64 k) +{ + k ^= k >> 33; + k *= 0xff51afd7ed558ccd; + k ^= k >> 33; + k *= 0xc4ceb9fe1a85ec53; + k ^= k >> 33; + + return k; +} + +u64 GetMurmurHash3(const u8 *src, int len, u32 samples) +{ + const u8 * data = (const u8*)src; + const int nblocks = len / 16; + u32 Step = (len / 8); + if(samples == 0) samples = max(Step, 1u); + Step = Step / samples; + if(Step < 1) Step = 1; + + u64 h1 = 0x9368e53c2f6af274; + u64 h2 = 0x586dcd208f7cd3fd; + + u64 c1 = 0x87c37b91114253d5; + u64 c2 = 0x4cf5ad432745937f; + + + //---------- + // body + + const u64 * blocks = (const u64 *)(data); + + for(int i = 0; i < nblocks; i+=Step) + { + u64 k1 = getblock(blocks,i*2+0); + u64 k2 = getblock(blocks,i*2+1); + + bmix64(h1,h2,k1,k2,c1,c2); + } + + //---------- + // tail + + const u8 * tail = (const u8*)(data + nblocks*16); + + u64 k1 = 0; + u64 k2 = 0; + + switch(len & 15) + { + case 15: k2 ^= u64(tail[14]) << 48; + case 14: k2 ^= u64(tail[13]) << 40; + case 13: k2 ^= u64(tail[12]) << 32; + case 12: k2 ^= u64(tail[11]) << 24; + case 11: k2 ^= u64(tail[10]) << 16; + case 10: k2 ^= u64(tail[ 9]) << 8; + case 9: k2 ^= u64(tail[ 8]) << 0; + + case 8: k1 ^= u64(tail[ 7]) << 56; + case 7: k1 ^= u64(tail[ 6]) << 48; + case 6: k1 ^= u64(tail[ 5]) << 40; + case 5: k1 ^= u64(tail[ 4]) << 32; + case 4: k1 ^= u64(tail[ 3]) << 24; + case 3: k1 ^= u64(tail[ 2]) << 16; + case 2: k1 ^= u64(tail[ 1]) << 8; + case 1: k1 ^= u64(tail[ 0]) << 0; + bmix64(h1,h2,k1,k2,c1,c2); + }; + + //---------- + // finalization + + h2 ^= len; + + h1 += h2; + h2 += h1; + + h1 = fmix64(h1); + h2 = fmix64(h2); + + h1 += h2; + + return h1; +} + + +// CRC32 hash using the SSE4.2 instruction +u64 GetCRC32(const u8 *src, int len, u32 samples) +{ +#if _M_SSE >= 0x402 + u64 h = len; + u32 Step = (len / 8); + const u64 *data = (const u64 *)src; + const u64 *end = data + Step; + if(samples == 0) samples = max(Step, 1u); + Step = Step / samples; + if(Step < 1) Step = 1; + while(data < end) + { + h = _mm_crc32_u64(h, data[0]); + data += Step; + } + + const u8 *data2 = (const u8*)end; + return _mm_crc32_u64(h, u64(data2[0])); +#else + return 0; +#endif +} + + +/* + * NOTE: This hash function is used for custom texture loading/dumping, so + * it should not be changed, which would require all custom textures to be + * recalculated for their new hash values. If the hashing function is + * changed, make sure this one is still used when the legacy parameter is + * true. + */ +u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) +{ + const u64 m = 0xc6a4a7935bd1e995; + u64 h = len * m; + const int r = 47; + u32 Step = (len / 8); + const u64 *data = (const u64 *)src; + const u64 *end = data + Step; + if(samples == 0) samples = max(Step, 1u); + Step = Step / samples; + if(Step < 1) Step = 1; + while(data < end) + { + u64 k = data[0]; + data+=Step; + k *= m; + k ^= k >> r; + k *= m; + h ^= k; + h *= m; + } + + const u8 * data2 = (const u8*)end; + + switch(len & 7) + { + case 7: h ^= u64(data2[6]) << 48; + case 6: h ^= u64(data2[5]) << 40; + case 5: h ^= u64(data2[4]) << 32; + case 4: h ^= u64(data2[3]) << 24; + case 3: h ^= u64(data2[2]) << 16; + case 2: h ^= u64(data2[1]) << 8; + case 1: h ^= u64(data2[0]); + h *= m; + }; + + h ^= h >> r; + h *= m; + h ^= h >> r; + + return h; +} +#else +// CRC32 hash using the SSE4.2 instruction +u64 GetCRC32(const u8 *src, int len, u32 samples) +{ +#if _M_SSE >= 0x402 + u32 h = len; + u32 Step = (len/4); + const u32 *data = (const u32 *)src; + const u32 *end = data + Step; + if(samples == 0) samples = max(Step, 1u); + Step = Step / samples; + if(Step < 1) Step = 1; + while(data < end) + { + h = _mm_crc32_u32(h, data[0]); + data += Step; + } + + const u8 *data2 = (const u8*)end; + return (u64)_mm_crc32_u32(h, u32(data2[0])); +#else + return 0; +#endif +} + +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here + +inline u32 getblock(const u32 * p, int i) +{ + return p[i]; +} + +//---------- +// Finalization mix - force all bits of a hash block to avalanche + +// avalanches all bits to within 0.25% bias + +inline u32 fmix32(u32 h) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +inline void bmix32(u32 & h1, u32 & h2, u32 & k1, u32 & k2, u32 & c1, u32 & c2) +{ + k1 *= c1; + k1 = _rotl(k1,11); + k1 *= c2; + h1 ^= k1; + h1 += h2; + + h2 = _rotl(h2,17); + + k2 *= c2; + k2 = _rotl(k2,11); + k2 *= c1; + h2 ^= k2; + h2 += h1; + + h1 = h1*3+0x52dce729; + h2 = h2*3+0x38495ab5; + + c1 = c1*5+0x7b7d159c; + c2 = c2*5+0x6bce6396; +} + +//---------- + +u64 GetMurmurHash3(const u8* src, int len, u32 samples) +{ + const u8 * data = (const u8*)src; + u32 out[2]; + const int nblocks = len / 8; + u32 Step = (len / 4); + if(samples == 0) samples = max(Step, 1u); + Step = Step / samples; + if(Step < 1) Step = 1; + + u32 h1 = 0x8de1c3ac; + u32 h2 = 0xbab98226; + + u32 c1 = 0x95543787; + u32 c2 = 0x2ad7eb25; + + //---------- + // body + + const u32 * blocks = (const u32 *)(data + nblocks*8); + + for(int i = -nblocks; i < 0; i+=Step) + { + u32 k1 = getblock(blocks,i*2+0); + u32 k2 = getblock(blocks,i*2+1); + + bmix32(h1,h2,k1,k2,c1,c2); + } + + //---------- + // tail + + const u8 * tail = (const u8*)(data + nblocks*8); + + u32 k1 = 0; + u32 k2 = 0; + + switch(len & 7) + { + case 7: k2 ^= tail[6] << 16; + case 6: k2 ^= tail[5] << 8; + case 5: k2 ^= tail[4] << 0; + case 4: k1 ^= tail[3] << 24; + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0] << 0; + bmix32(h1,h2,k1,k2,c1,c2); + }; + + //---------- + // finalization + + h2 ^= len; + + h1 += h2; + h2 += h1; + + h1 = fmix32(h1); + h2 = fmix32(h2); + + h1 += h2; + h2 += h1; + + out[0] = h1; + out[1] = h2; + + return *((u64 *)&out); +} + +/* + * FIXME: The old 32-bit version of this hash made different hashes than the + * 64-bit version. Until someone can make a new version of the 32-bit one that + * makes identical hashes, this is just a c/p of the 64-bit one. + */ +u64 GetHashHiresTexture(const u8 *src, int len, u32 samples) +{ + const u64 m = 0xc6a4a7935bd1e995ULL; + u64 h = len * m; + const int r = 47; + u32 Step = (len / 8); + const u64 *data = (const u64 *)src; + const u64 *end = data + Step; + if(samples == 0) samples = max(Step, 1u); + Step = Step / samples; + if(Step < 1) Step = 1; + while(data < end) + { + u64 k = data[0]; + data+=Step; + k *= m; + k ^= k >> r; + k *= m; + h ^= k; + h *= m; + } + + const u8 * data2 = (const u8*)end; + + switch(len & 7) + { + case 7: h ^= u64(data2[6]) << 48; + case 6: h ^= u64(data2[5]) << 40; + case 5: h ^= u64(data2[4]) << 32; + case 4: h ^= u64(data2[3]) << 24; + case 3: h ^= u64(data2[2]) << 16; + case 2: h ^= u64(data2[1]) << 8; + case 1: h ^= u64(data2[0]); + h *= m; + }; + + h ^= h >> r; + h *= m; + h ^= h >> r; + + return h; +} +#endif + +u64 GetHash64(const u8 *src, int len, u32 samples) +{ + return ptrHashFunction(src, len, samples); +} + +// sets the hash function used for the texture cache +void SetHash64Function(bool useHiresTextures) +{ + if (useHiresTextures) + { + ptrHashFunction = &GetHashHiresTexture; + } +#if _M_SSE >= 0x402 + else if (cpu_info.bSSE4_2 && !useHiresTextures) // sse crc32 version + { + ptrHashFunction = &GetCRC32; + } +#endif + else + { + ptrHashFunction = &GetMurmurHash3; + } +} + + + diff --git a/src/common/src/hash.h b/src/common/src/hash.h new file mode 100644 index 0000000000..addfa4b5fb --- /dev/null +++ b/src/common/src/hash.h @@ -0,0 +1,20 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#ifndef _HASH_H_ +#define _HASH_H_ + +#include "common.h" + +u32 HashFletcher(const u8* data_u8, size_t length); // FAST. Length & 1 == 0. +u32 HashAdler32(const u8* data, size_t len); // Fairly accurate, slightly slower +u32 HashFNV(const u8* ptr, int length); // Another fast and decent hash +u32 HashEctor(const u8* ptr, int length); // JUNK. DO NOT USE FOR NEW THINGS +u64 GetCRC32(const u8 *src, int len, u32 samples); // SSE4.2 version of CRC32 +u64 GetHashHiresTexture(const u8 *src, int len, u32 samples); +u64 GetMurmurHash3(const u8 *src, int len, u32 samples); +u64 GetHash64(const u8 *src, int len, u32 samples); +void SetHash64Function(bool useHiresTextures); +#endif // _HASH_H_ diff --git a/src/common/src/linear_disk_cache.h b/src/common/src/linear_disk_cache.h new file mode 100644 index 0000000000..d33ee78206 --- /dev/null +++ b/src/common/src/linear_disk_cache.h @@ -0,0 +1,191 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#ifndef _LINEAR_DISKCACHE +#define _LINEAR_DISKCACHE + +#include "common.h" +#include + +// 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 +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 +class LinearDiskCache +{ +public: + // return number of read entries + u32 OpenAndRead(const char *filename, LinearDiskCacheReader &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 + bool Write(const D *data, u32 count = 1) + { + return m_file.write((const char*)data, count * sizeof(D)).good(); + } + + template + bool Read(const D *data, u32 count = 1) + { + return m_file.read((char*)data, count * sizeof(D)).good(); + } + + struct Header + { + Header() + : id(*(u32*)"DCAC") + , key_t_size(sizeof(K)) + , value_t_size(sizeof(V)) + { + memcpy(ver, scm_rev_git_str, 40); + } + + const u32 id; + const u16 key_t_size, value_t_size; + char ver[40]; + + } m_header; + + std::fstream m_file; + u32 m_num_entries; +}; + +#endif // _LINEAR_DISKCACHE diff --git a/src/common/src/log.h b/src/common/src/log.h new file mode 100644 index 0000000000..8691a825f8 --- /dev/null +++ b/src/common/src/log.h @@ -0,0 +1,155 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _LOG_H_ +#define _LOG_H_ + +#define NOTICE_LEVEL 1 // VERY important information that is NOT errors. Like startup and OSReports. +#define ERROR_LEVEL 2 // Critical errors +#define WARNING_LEVEL 3 // Something is suspicious. +#define INFO_LEVEL 4 // General information. +#define DEBUG_LEVEL 5 // Detailed debugging - might make things slow. + +namespace LogTypes +{ + +enum LOG_TYPE { + ACTIONREPLAY, + AUDIO, + AUDIO_INTERFACE, + BOOT, + COMMANDPROCESSOR, + COMMON, + CONSOLE, + DISCIO, + FILEMON, + DSPHLE, + DSPLLE, + DSP_MAIL, + DSPINTERFACE, + DVDINTERFACE, + DYNA_REC, + EXPANSIONINTERFACE, + GDB_STUB, + POWERPC, + GPFIFO, + OSHLE, + MASTER_LOG, + MEMMAP, + MEMCARD_MANAGER, + OSREPORT, + PAD, + PROCESSORINTERFACE, + PIXELENGINE, + SERIALINTERFACE, + SP1, + STREAMINGINTERFACE, + VIDEO, + VIDEOINTERFACE, + WII_IOB, + WII_IPC, + WII_IPC_DVD, + WII_IPC_ES, + WII_IPC_FILEIO, + WII_IPC_HID, + WII_IPC_HLE, + WII_IPC_NET, + WII_IPC_WC24, + WII_IPC_SSL, + WII_IPC_SD, + WII_IPC_STM, + WII_IPC_WIIMOTE, + WIIMOTE, + NETPLAY, + + NUMBER_OF_LOGS // Must be last +}; + +// FIXME: should this be removed? +enum LOG_LEVELS { + LNOTICE = NOTICE_LEVEL, + LERROR = ERROR_LEVEL, + LWARNING = WARNING_LEVEL, + LINFO = INFO_LEVEL, + LDEBUG = DEBUG_LEVEL, +}; + +#define LOGTYPES_LEVELS LogTypes::LOG_LEVELS +#define LOGTYPES_TYPE LogTypes::LOG_TYPE + +} // namespace + +void GenericLog(LOGTYPES_LEVELS level, LOGTYPES_TYPE type, + const char *file, int line, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 5, 6))) +#endif + ; + +#if defined LOGGING || defined _DEBUG || defined DEBUGFAST +#define MAX_LOGLEVEL DEBUG_LEVEL +#else +#ifndef MAX_LOGLEVEL +#define MAX_LOGLEVEL WARNING_LEVEL +#endif // loglevel +#endif // logging + +#ifdef GEKKO +#define GENERIC_LOG(t, v, ...) +#else +// Let the compiler optimize this out +#define GENERIC_LOG(t, v, ...) { \ + if (v <= MAX_LOGLEVEL) \ + GenericLog(v, t, __FILE__, __LINE__, __VA_ARGS__); \ + } +#endif + +#define ERROR_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LERROR, __VA_ARGS__) } while (0) +#define WARN_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LWARNING, __VA_ARGS__) } while (0) +#define NOTICE_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LNOTICE, __VA_ARGS__) } while (0) +#define INFO_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LINFO, __VA_ARGS__) } while (0) +#define DEBUG_LOG(t,...) do { GENERIC_LOG(LogTypes::t, LogTypes::LDEBUG, __VA_ARGS__) } while (0) + +#if MAX_LOGLEVEL >= DEBUG_LEVEL +#define _dbg_assert_(_t_, _a_) \ + if (!(_a_)) {\ + ERROR_LOG(_t_, "Error...\n\n Line: %d\n File: %s\n Time: %s\n\nIgnore and continue?", \ + __LINE__, __FILE__, __TIME__); \ + if (!PanicYesNo("*** Assertion (see log)***\n")) {Crash();} \ + } +#define _dbg_assert_msg_(_t_, _a_, ...)\ + if (!(_a_)) {\ + ERROR_LOG(_t_, __VA_ARGS__); \ + if (!PanicYesNo(__VA_ARGS__)) {Crash();} \ + } +#define _dbg_update_() Host_UpdateLogDisplay(); + +#else // not debug +#define _dbg_update_() ; + +#ifndef _dbg_assert_ +#define _dbg_assert_(_t_, _a_) {} +#define _dbg_assert_msg_(_t_, _a_, _desc_, ...) {} +#endif // dbg_assert +#endif // MAX_LOGLEVEL DEBUG + +#define _assert_(_a_) _dbg_assert_(MASTER_LOG, _a_) + +#ifndef GEKKO +#ifdef _WIN32 +#define _assert_msg_(_t_, _a_, _fmt_, ...) \ + if (!(_a_)) {\ + if (!PanicYesNo(_fmt_, __VA_ARGS__)) {Crash();} \ + } +#else // not win32 +#define _assert_msg_(_t_, _a_, _fmt_, ...) \ + if (!(_a_)) {\ + if (!PanicYesNo(_fmt_, ##__VA_ARGS__)) {Crash();} \ + } +#endif // WIN32 +#else // GEKKO +#define _assert_msg_(_t_, _a_, _fmt_, ...) +#endif + +#endif // _LOG_H_ diff --git a/src/common/src/log_manager.cpp b/src/common/src/log_manager.cpp new file mode 100644 index 0000000000..c19f728f86 --- /dev/null +++ b/src/common/src/log_manager.cpp @@ -0,0 +1,199 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include + +#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 lk(m_listeners_lock); + m_listeners.insert(listener); +} + +void LogContainer::RemoveListener(LogListener *listener) +{ + std::lock_guard lk(m_listeners_lock); + m_listeners.erase(listener); +} + +void LogContainer::Trigger(LogTypes::LOG_LEVELS level, const char *msg) +{ + std::lock_guard lk(m_listeners_lock); + + std::set::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 lk(m_log_lock); + m_logfile << msg << std::flush; +} + +void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg) +{ +#if _MSC_VER + ::OutputDebugStringA(msg); +#endif +} diff --git a/src/common/src/log_manager.h b/src/common/src/log_manager.h new file mode 100644 index 0000000000..59078849b9 --- /dev/null +++ b/src/common/src/log_manager.h @@ -0,0 +1,169 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _LOGMANAGER_H_ +#define _LOGMANAGER_H_ + +#include "log.h" +#include "string_util.h" +#include "thread.h" +#include "file_util.h" + +#include +#include + +#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 m_listeners; +}; + +class ConsoleListener; + +class LogManager : NonCopyable +{ +private: + LogContainer* m_Log[LogTypes::NUMBER_OF_LOGS]; + FileLogListener *m_fileLog; + ConsoleListener *m_consoleLog; + DebuggerLogListener *m_debuggerLog; + static LogManager *m_logManager; // Singleton. Ugh. + + LogManager(); + ~LogManager(); +public: + + static u32 GetMaxLevel() { return MAX_LOGLEVEL; } + + void Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, + const char *file, int line, const char *fmt, va_list args); + + void SetLogLevel(LogTypes::LOG_TYPE type, LogTypes::LOG_LEVELS level) + { + m_Log[type]->SetLevel(level); + } + + void SetEnable(LogTypes::LOG_TYPE type, bool enable) + { + m_Log[type]->SetEnable(enable); + } + + bool IsEnabled(LogTypes::LOG_TYPE type) const + { + return m_Log[type]->IsEnabled(); + } + + const char* GetShortName(LogTypes::LOG_TYPE type) const + { + return m_Log[type]->GetShortName(); + } + + const char* GetFullName(LogTypes::LOG_TYPE type) const + { + return m_Log[type]->GetFullName(); + } + + void AddListener(LogTypes::LOG_TYPE type, LogListener *listener) + { + m_Log[type]->AddListener(listener); + } + + void RemoveListener(LogTypes::LOG_TYPE type, LogListener *listener) + { + m_Log[type]->RemoveListener(listener); + } + + FileLogListener *GetFileListener() const + { + return m_fileLog; + } + + ConsoleListener *GetConsoleListener() const + { + return m_consoleLog; + } + + DebuggerLogListener *GetDebuggerListener() const + { + return m_debuggerLog; + } + + static LogManager* GetInstance() + { + return m_logManager; + } + + static void SetInstance(LogManager *logManager) + { + m_logManager = logManager; + } + + static void Init(); + static void Shutdown(); +}; + +#endif // _LOGMANAGER_H_ diff --git a/src/common/src/math_util.cpp b/src/common/src/math_util.cpp new file mode 100644 index 0000000000..6667e078cc --- /dev/null +++ b/src/common/src/math_util.cpp @@ -0,0 +1,212 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#include "common.h" +#include "math_util.h" + +#include +#include + +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& Vec) +{ + return std::accumulate(Vec.begin(), Vec.end(), 0.0f); +} + +void Matrix33::LoadIdentity(Matrix33 &mtx) +{ + memset(mtx.data, 0, sizeof(mtx.data)); + mtx.data[0] = 1.0f; + mtx.data[4] = 1.0f; + mtx.data[8] = 1.0f; +} + +void Matrix33::RotateX(Matrix33 &mtx, float rad) +{ + float s = sin(rad); + float c = cos(rad); + memset(mtx.data, 0, sizeof(mtx.data)); + mtx.data[0] = 1; + mtx.data[4] = c; + mtx.data[5] = -s; + mtx.data[7] = s; + mtx.data[8] = c; +} +void Matrix33::RotateY(Matrix33 &mtx, float rad) +{ + float s = sin(rad); + float c = cos(rad); + memset(mtx.data, 0, sizeof(mtx.data)); + mtx.data[0] = c; + mtx.data[2] = s; + mtx.data[4] = 1; + mtx.data[6] = -s; + mtx.data[8] = c; +} + +void Matrix33::Multiply(const Matrix33 &a, const Matrix33 &b, Matrix33 &result) +{ + MatrixMul(3, a.data, b.data, result.data); +} + +void Matrix33::Multiply(const Matrix33 &a, const float vec[3], float result[3]) +{ + for (int i = 0; i < 3; ++i) { + result[i] = 0; + for (int k = 0; k < 3; ++k) { + result[i] += a.data[i * 3 + k] * vec[k]; + } + } +} + +void Matrix44::LoadIdentity(Matrix44 &mtx) +{ + memset(mtx.data, 0, sizeof(mtx.data)); + mtx.data[0] = 1.0f; + mtx.data[5] = 1.0f; + mtx.data[10] = 1.0f; + mtx.data[15] = 1.0f; +} + +void Matrix44::LoadMatrix33(Matrix44 &mtx, const Matrix33 &m33) +{ + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + { + mtx.data[i * 4 + j] = m33.data[i * 3 + j]; + } + } + + for (int i = 0; i < 3; ++i) + { + mtx.data[i * 4 + 3] = 0; + mtx.data[i + 12] = 0; + } + mtx.data[15] = 1.0f; +} + +void Matrix44::Set(Matrix44 &mtx, const float mtxArray[16]) +{ + for(int i = 0; i < 16; ++i) { + mtx.data[i] = mtxArray[i]; + } +} + +void Matrix44::Translate(Matrix44 &mtx, const float vec[3]) +{ + LoadIdentity(mtx); + mtx.data[3] = vec[0]; + mtx.data[7] = vec[1]; + mtx.data[11] = vec[2]; +} + +void Matrix44::Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result) +{ + MatrixMul(4, a.data, b.data, result.data); +} + diff --git a/src/common/src/math_util.h b/src/common/src/math_util.h new file mode 100644 index 0000000000..4dd12c3096 --- /dev/null +++ b/src/common/src/math_util.h @@ -0,0 +1,200 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#ifndef _MATH_UTIL_H_ +#define _MATH_UTIL_H_ + +#include "common.h" + +#include + +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 +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&); + +#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define ROUND_DOWN(x, a) ((x) & ~((a) - 1)) + +// Rounds down. 0 -> undefined +inline u64 Log2(u64 val) +{ +#if defined(__GNUC__) + return 63 - __builtin_clzll(val); + +#elif defined(_MSC_VER) && defined(_M_X64) + unsigned long result = -1; + _BitScanReverse64(&result, val); + return result; + +#else + u64 result = -1; + while (val != 0) + { + val >>= 1; + ++result; + } + return result; +#endif +} + +// Tiny matrix/vector library. +// Used for things like Free-Look in the gfx backend. + +class Matrix33 +{ +public: + static void LoadIdentity(Matrix33 &mtx); + + // set mtx to be a rotation matrix around the x axis + static void RotateX(Matrix33 &mtx, float rad); + // set mtx to be a rotation matrix around the y axis + static void RotateY(Matrix33 &mtx, float rad); + + // set result = a x b + static void Multiply(const Matrix33 &a, const Matrix33 &b, Matrix33 &result); + static void Multiply(const Matrix33 &a, const float vec[3], float result[3]); + + float data[9]; +}; + +class Matrix44 +{ +public: + static void LoadIdentity(Matrix44 &mtx); + static void LoadMatrix33(Matrix44 &mtx, const Matrix33 &m33); + static void Set(Matrix44 &mtx, const float mtxArray[16]); + + static void Translate(Matrix44 &mtx, const float vec[3]); + + static void Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result); + + float data[16]; +}; + +#endif // _MATH_UTIL_H_ diff --git a/src/common/src/mem_arena.cpp b/src/common/src/mem_arena.cpp new file mode 100644 index 0000000000..13cd00fd3f --- /dev/null +++ b/src/common/src/mem_arena.cpp @@ -0,0 +1,304 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#include "common.h" +#include "memory_util.h" +#include "mem_arena.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#include +#ifdef ANDROID +#include +#include +#endif +#endif +#include + +#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(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(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 freeset; + for (int i = 0; i < num_views; i++) + { + const MemoryView* view = &views[i]; + u8** outptrs[2] = {view->out_ptr_low, view->out_ptr}; + for (int j = 0; j < 2; j++) + { + u8** outptr = outptrs[j]; + if (outptr && *outptr && !freeset.count(*outptr)) + { + arena->ReleaseView(*outptr, view->size); + freeset.insert(*outptr); + *outptr = NULL; + } + } + } +} diff --git a/src/common/src/mem_arena.h b/src/common/src/mem_arena.h new file mode 100644 index 0000000000..4c3ded0df6 --- /dev/null +++ b/src/common/src/mem_arena.h @@ -0,0 +1,58 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#ifndef _MEMARENA_H_ +#define _MEMARENA_H_ + +#ifdef _WIN32 +#include +#endif + +#include "common.h" + +// This class lets you create a block of anonymous RAM, and then arbitrarily map views into it. +// Multiple views can mirror the same section of the block, which makes it very convenient for emulating +// memory mirrors. + +class MemArena +{ +public: + void GrabLowMemSpace(size_t size); + void ReleaseSpace(); + void *CreateView(s64 offset, size_t size, void *base = nullptr); + void ReleaseView(void *view, size_t size); + + // This only finds 1 GB in 32-bit + static u8 *Find4GBBase(); +private: + +#ifdef _WIN32 + HANDLE hMemoryMapping; +#else + int fd; +#endif +}; + +enum { + MV_MIRROR_PREVIOUS = 1, + MV_FAKE_VMEM = 2, + MV_WII_ONLY = 4, +}; + +struct MemoryView +{ + u8 **out_ptr_low; + u8 **out_ptr; + u32 virtual_address; + u32 size; + u32 flags; +}; + +// Uses a memory arena to set up an emulator-friendly memory map according to +// a passed-in list of MemoryView structures. +u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena *arena); +void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena); + +#endif // _MEMARENA_H_ diff --git a/src/common/src/memory_util.cpp b/src/common/src/memory_util.cpp new file mode 100644 index 0000000000..346d2e5254 --- /dev/null +++ b/src/common/src/memory_util.cpp @@ -0,0 +1,197 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#include "common.h" +#include "memory_util.h" +#include "string_util.h" + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +#if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) +#include +#define PAGE_MASK (getpagesize() - 1) +#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK)) +#endif + +// This is purposely not a full wrapper for virtualalloc/mmap, but it +// provides exactly the primitive operations that Dolphin needs. + +void* AllocateExecutableMemory(size_t size, bool low) +{ +#if defined(_WIN32) + void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); +#else + static char *map_hint = 0; +#if defined(__x86_64__) && !defined(MAP_32BIT) + // This OS has no flag to enforce allocation below the 4 GB boundary, + // but if we hint that we want a low address it is very likely we will + // get one. + // An older version of this code used MAP_FIXED, but that has the side + // effect of discarding already mapped pages that happen to be in the + // requested virtual memory range (such as the emulated RAM, sometimes). + if (low && (!map_hint)) + map_hint = (char*)round_page(512*1024*1024); /* 0.5 GB rounded up to the next page */ +#endif + void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANON | MAP_PRIVATE +#if defined(__x86_64__) && defined(MAP_32BIT) + | (low ? MAP_32BIT : 0) +#endif + , -1, 0); +#endif /* defined(_WIN32) */ + + // printf("Mapped executable memory at %p (size %ld)\n", ptr, + // (unsigned long)size); + +#if defined(__FreeBSD__) + if (ptr == MAP_FAILED) + { + ptr = NULL; +#else + if (ptr == NULL) + { +#endif + PanicAlert("Failed to allocate executable memory"); + } +#if !defined(_WIN32) && defined(__x86_64__) && !defined(MAP_32BIT) + else + { + if (low) + { + map_hint += size; + map_hint = (char*)round_page(map_hint); /* round up to the next page */ + // printf("Next map will (hopefully) be at %p\n", map_hint); + } + } +#endif + +#if defined(_M_X64) + if ((u64)ptr >= 0x80000000 && low == true) + PanicAlert("Executable memory ended up above 2GB!"); +#endif + + return ptr; +} + +void* AllocateMemoryPages(size_t size) +{ +#ifdef _WIN32 + void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); +#else + void* ptr = mmap(0, size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); +#endif + + // printf("Mapped memory at %p (size %ld)\n", ptr, + // (unsigned long)size); + + if (ptr == NULL) + PanicAlert("Failed to allocate raw memory"); + + return ptr; +} + +void* AllocateAlignedMemory(size_t size,size_t alignment) +{ +#ifdef _WIN32 + void* ptr = _aligned_malloc(size,alignment); +#else + void* ptr = NULL; +#ifdef ANDROID + ptr = memalign(alignment, size); +#else + if (posix_memalign(&ptr, alignment, size) != 0) + ERROR_LOG(MEMMAP, "Failed to allocate aligned memory"); +#endif +#endif + + // printf("Mapped memory at %p (size %ld)\n", ptr, + // (unsigned long)size); + + if (ptr == NULL) + PanicAlert("Failed to allocate aligned memory"); + + return ptr; +} + +void FreeMemoryPages(void* ptr, size_t size) +{ + if (ptr) + { +#ifdef _WIN32 + + if (!VirtualFree(ptr, 0, MEM_RELEASE)) + PanicAlert("FreeMemoryPages failed!\n%s", GetLastErrorMsg()); + ptr = NULL; // Is this our responsibility? + +#else + munmap(ptr, size); +#endif + } +} + +void FreeAlignedMemory(void* ptr) +{ + if (ptr) + { +#ifdef _WIN32 + _aligned_free(ptr); +#else + free(ptr); +#endif + } +} + +void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) +{ +#ifdef _WIN32 + DWORD oldValue; + if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) + PanicAlert("WriteProtectMemory failed!\n%s", GetLastErrorMsg()); +#else + mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ); +#endif +} + +void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) +{ +#ifdef _WIN32 + DWORD oldValue; + if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldValue)) + PanicAlert("UnWriteProtectMemory failed!\n%s", GetLastErrorMsg()); +#else + mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ); +#endif +} + +std::string MemUsage() +{ +#ifdef _WIN32 +#pragma comment(lib, "psapi") + DWORD processID = GetCurrentProcessId(); + HANDLE hProcess; + PROCESS_MEMORY_COUNTERS pmc; + std::string Ret; + + // Print information about the memory usage of the process. + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); + if (NULL == hProcess) return "MemUsage Error"; + + if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) + Ret = StringFromFormat("%s K", ThousandSeparate(pmc.WorkingSetSize / 1024, 7).c_str()); + + CloseHandle(hProcess); + return Ret; +#else + return ""; +#endif +} diff --git a/src/common/src/memory_util.h b/src/common/src/memory_util.h new file mode 100644 index 0000000000..49b2589da3 --- /dev/null +++ b/src/common/src/memory_util.h @@ -0,0 +1,25 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + + +#ifndef _MEMORYUTIL_H +#define _MEMORYUTIL_H + +#ifndef _WIN32 +#include +#endif +#include + +void* AllocateExecutableMemory(size_t size, bool low = true); +void* AllocateMemoryPages(size_t size); +void FreeMemoryPages(void* ptr, size_t size); +void* AllocateAlignedMemory(size_t size,size_t alignment); +void FreeAlignedMemory(void* ptr); +void WriteProtectMemory(void* ptr, size_t size, bool executable = false); +void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); +std::string MemUsage(); + +inline int GetPageSize() { return 4096; } + +#endif diff --git a/src/common/src/misc.cpp b/src/common/src/misc.cpp new file mode 100644 index 0000000000..719a98cb9e --- /dev/null +++ b/src/common/src/misc.cpp @@ -0,0 +1,33 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "common.h" + +// Neither Android nor OS X support TLS +#if defined(__APPLE__) || (ANDROID && __clang__) +#define __thread +#endif + +// Generic function to get last error message. +// Call directly after the command or use the error num. +// This function might change the error code. +//const char* GetLastErrorMsg() +//{ +// static const size_t buff_size = 255; +// +//#ifdef _WIN32 +// static __declspec(thread) char err_str[buff_size] = {}; +// +// FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), +// MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), +// err_str, buff_size, NULL); +//#else +// static __thread char err_str[buff_size] = {}; +// +// // Thread safe (XSI-compliant) +// strerror_r(errno, err_str, buff_size); +//#endif +// +// return err_str; +//} diff --git a/src/common/src/msg_handler.cpp b/src/common/src/msg_handler.cpp new file mode 100644 index 0000000000..62cbe0aa79 --- /dev/null +++ b/src/common/src/msg_handler.cpp @@ -0,0 +1,107 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include // System + +#include "common.h" // Local +#include "string_util.h" + +bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style); +static MsgAlertHandler msg_handler = DefaultMsgHandler; +static bool AlertEnabled = true; + +std::string DefaultStringTranslator(const char* text); +static StringTranslator str_translator = DefaultStringTranslator; + +// Select which of these functions that are used for message boxes. If +// wxWidgets is enabled we will use wxMsgAlert() that is defined in Main.cpp +void RegisterMsgAlertHandler(MsgAlertHandler handler) +{ + msg_handler = handler; +} + +// Select translation function. For wxWidgets use wxStringTranslator in Main.cpp +void RegisterStringTranslator(StringTranslator translator) +{ + str_translator = translator; +} + +// enable/disable the alert handler +void SetEnableAlert(bool enable) +{ + AlertEnabled = enable; +} + +// This is the first stop for gui alerts where the log is updated and the +// correct window is shown +bool MsgAlert(bool yes_no, int Style, const char* format, ...) +{ + // Read message and write it to the log + std::string caption; + char buffer[2048]; + + static std::string info_caption; + static std::string warn_caption; + static std::string ques_caption; + static std::string crit_caption; + + if (!info_caption.length()) + { + info_caption = str_translator(_trans("Information")); + ques_caption = str_translator(_trans("Question")); + warn_caption = str_translator(_trans("Warning")); + crit_caption = str_translator(_trans("Critical")); + } + + switch(Style) + { + case INFORMATION: + caption = info_caption; + break; + case QUESTION: + caption = ques_caption; + break; + case WARNING: + caption = warn_caption; + break; + case CRITICAL: + caption = crit_caption; + break; + } + + va_list args; + va_start(args, format); + CharArrayFromFormatV(buffer, sizeof(buffer)-1, str_translator(format).c_str(), args); + va_end(args); + + ERROR_LOG(MASTER_LOG, "%s: %s", caption.c_str(), buffer); + + // Don't ignore questions, especially AskYesNo, PanicYesNo could be ignored + if (msg_handler && (AlertEnabled || Style == QUESTION || Style == CRITICAL)) + return msg_handler(caption.c_str(), buffer, yes_no, Style); + + return true; +} + +// Default non library dependent panic alert +bool DefaultMsgHandler(const char* caption, const char* text, bool yes_no, int Style) +{ +//#ifdef _WIN32 +// int STYLE = MB_ICONINFORMATION; +// if (Style == QUESTION) STYLE = MB_ICONQUESTION; +// if (Style == WARNING) STYLE = MB_ICONWARNING; +// +// return IDYES == MessageBox(0, UTF8ToTStr(text).c_str(), UTF8ToTStr(caption).c_str(), STYLE | (yes_no ? MB_YESNO : MB_OK)); +//#else + printf("%s\n", text); + return true; +//#endif +} + +// Default (non) translator +std::string DefaultStringTranslator(const char* text) +{ + return text; +} + diff --git a/src/common/src/msg_handler.h b/src/common/src/msg_handler.h new file mode 100644 index 0000000000..7de10c7b0a --- /dev/null +++ b/src/common/src/msg_handler.h @@ -0,0 +1,73 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _MSGHANDLER_H_ +#define _MSGHANDLER_H_ + +#include + +// Message alerts +enum MSG_TYPE +{ + INFORMATION, + QUESTION, + WARNING, + CRITICAL +}; + +typedef bool (*MsgAlertHandler)(const char* caption, const char* text, + bool yes_no, int Style); +typedef std::string (*StringTranslator)(const char* text); + +void RegisterMsgAlertHandler(MsgAlertHandler handler); +void RegisterStringTranslator(StringTranslator translator); + +extern bool MsgAlert(bool yes_no, int Style, const char* format, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 3, 4))) +#endif + ; +void SetEnableAlert(bool enable); + +#ifndef GEKKO +#ifdef _WIN32 + #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) + #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) + #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) + #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) + #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) + // Use these macros (that do the same thing) if the message should be translated. + #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, __VA_ARGS__) + #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, __VA_ARGS__) + #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, __VA_ARGS__) + #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, __VA_ARGS__) + #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, __VA_ARGS__) +#else + #define SuccessAlert(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) + #define PanicAlert(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) + #define PanicYesNo(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) + #define AskYesNo(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) + #define CriticalAlert(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) + // Use these macros (that do the same thing) if the message should be translated. + #define SuccessAlertT(format, ...) MsgAlert(false, INFORMATION, format, ##__VA_ARGS__) + #define PanicAlertT(format, ...) MsgAlert(false, WARNING, format, ##__VA_ARGS__) + #define PanicYesNoT(format, ...) MsgAlert(true, WARNING, format, ##__VA_ARGS__) + #define AskYesNoT(format, ...) MsgAlert(true, QUESTION, format, ##__VA_ARGS__) + #define CriticalAlertT(format, ...) MsgAlert(false, CRITICAL, format, ##__VA_ARGS__) +#endif +#else +// GEKKO + #define SuccessAlert(format, ...) ; + #define PanicAlert(format, ...) ; + #define PanicYesNo(format, ...) ; + #define AskYesNo(format, ...) ; + #define CriticalAlert(format, ...) ; + #define SuccessAlertT(format, ...) ; + #define PanicAlertT(format, ...) ; + #define PanicYesNoT(format, ...) ; + #define AskYesNoT(format, ...) ; + #define CriticalAlertT(format, ...) ; +#endif + +#endif // _MSGHANDLER_H_ diff --git a/src/common/src/scm_rev.h b/src/common/src/scm_rev.h new file mode 100644 index 0000000000..cb4eccfd0f --- /dev/null +++ b/src/common/src/scm_rev.h @@ -0,0 +1,4 @@ +#define SCM_REV_STR "7d11f8cedd7c135d96880f19ecbd3ff87a60a11f" +#define SCM_DESC_STR "3.5-254-dirty" +#define SCM_BRANCH_STR "master" +#define SCM_IS_MASTER 1 diff --git a/src/common/src/std_condition_variable.h b/src/common/src/std_condition_variable.h new file mode 100644 index 0000000000..d44545e61b --- /dev/null +++ b/src/common/src/std_condition_variable.h @@ -0,0 +1,170 @@ + +#ifndef CONDITION_VARIABLE_H_ +#define CONDITION_VARIABLE_H_ + +#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z)) +#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) + +#ifndef __has_include +#define __has_include(s) 0 +#endif + +#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ + +// GCC 4.4 provides +#include + +#elif __has_include() && !ANDROID + +// clang and libc++ provide 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 +#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& 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 + void wait(unique_lock& lock, Predicate pred) + { + while (!pred()) + wait(lock); + } + + //template + //cv_status wait_until(unique_lock& lock, + // const chrono::time_point& abs_time); + + //template + // bool wait_until(unique_lock& lock, + // const chrono::time_point& abs_time, + // Predicate pred); + + //template + //cv_status wait_for(unique_lock& lock, + // const chrono::duration& rel_time); + + //template + // bool wait_for(unique_lock& lock, + // const chrono::duration& rel_time, + // Predicate pred); + + native_handle_type native_handle() + { +#ifdef USE_EVENTS + return m_handle; +#else + return &m_handle; +#endif + } + +private: + native_type m_handle; +}; + +} + +#endif +#endif diff --git a/src/common/src/std_mutex.h b/src/common/src/std_mutex.h new file mode 100644 index 0000000000..ce46a2f591 --- /dev/null +++ b/src/common/src/std_mutex.h @@ -0,0 +1,365 @@ + +#ifndef MUTEX_H_ +#define MUTEX_H_ + +#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z)) +#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) + +#ifndef __has_include +#define __has_include(s) 0 +#endif + +#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ +// GCC 4.4 provides +#include +#elif __has_include() && !ANDROID +// Clang + libc++ +#include +#else + +// partial implementation for win32/pthread + +#include + +#if defined(_WIN32) +// WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +#else +// POSIX +#include + +#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 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 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 + //unique_lock(mutex_type& m, const chrono::time_point& abs_time); + + //template + //unique_lock(mutex_type& m, const chrono::duration& 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(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(u); +#endif + swap(other); + } + + void lock() + { + mutex()->lock(); + owns = true; + } + + bool try_lock() + { + owns = mutex()->try_lock(); + return owns; + } + + //template + //bool try_lock_for(const chrono::duration& rel_time); + //template + //bool try_lock_until(const chrono::time_point& 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 +void swap(unique_lock& x, unique_lock& y) +{ + x.swap(y); +} + +} + +#endif +#endif diff --git a/src/common/src/std_thread.h b/src/common/src/std_thread.h new file mode 100644 index 0000000000..e43d283443 --- /dev/null +++ b/src/common/src/std_thread.h @@ -0,0 +1,317 @@ + +#ifndef STD_THREAD_H_ +#define STD_THREAD_H_ + +#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z)) +#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) + +#ifndef __has_include +#define __has_include(s) 0 +#endif + +#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ +// GCC 4.4 provides +#ifndef _GLIBCXX_USE_SCHED_YIELD +#define _GLIBCXX_USE_SCHED_YIELD +#endif +#include +#elif __has_include() && !ANDROID +// Clang + libc++ +#include +#else + +// partial std::thread implementation for win32/pthread + +#include + +#if (_MSC_VER >= 1600) || (GCC_VERSION >= GCC_VER(4,3,0) && __GXX_EXPERIMENTAL_CXX0X__) +#define USE_RVALUE_REFERENCES +#endif + +#ifdef __APPLE__ +#import +#endif + +#if defined(_WIN32) +// WIN32 + +#define WIN32_LEAN_AND_MEAN +#include + +#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 +#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 + +#ifndef _POSIX_THREADS +#error unsupported platform (no pthreads?) +#endif + +#include + +#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 + //thread(C&& func, A&&... args); + + template + thread(C func) + { + StartThread(new Func(func)); + } + + template + thread(C func, A arg) + { + StartThread(new FuncArg(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(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(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(sysinfo.dwNumberOfProcessors); +#else + return 0; +#endif + } + +private: + id m_id; + +#ifdef _WIN32 + native_handle_type m_handle; +#endif + + template + void StartThread(F* param) + { +#ifdef USE_BEGINTHREADEX + m_handle = (HANDLE)_beginthreadex(NULL, 0, &RunAndDelete, param, 0, &m_id.m_thread); +#elif defined(_WIN32) + m_handle = CreateThread(NULL, 0, &RunAndDelete, 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, param)) + m_id = id(); +#endif + } + + template + class Func + { + public: + Func(C _func) : func(_func) {} + + void Run() { func(); } + + private: + C const func; + }; + + template + class FuncArg + { + public: + FuncArg(C _func, A _arg) : func(_func), arg(_arg) {} + + void Run() { func(arg); } + + private: + C const func; + A arg; + }; + + template + static THREAD_RETURN RunAndDelete(void* param) + { +#ifdef __APPLE__ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +#endif + static_cast(param)->Run(); + delete static_cast(param); +#ifdef __APPLE__ + [pool release]; +#endif + return 0; + } +}; + +namespace this_thread +{ + +inline void yield() +{ +#ifdef _WIN32 + SwitchToThread(); +#else + sleep(0); +#endif +} + +inline thread::id get_id() +{ +#ifdef _WIN32 + return GetCurrentThreadId(); +#else + return pthread_self(); +#endif +} + +} // namespace this_thread + +} // namespace std + +#undef USE_RVALUE_REFERENCES +#undef USE_BEGINTHREADEX +#undef THREAD_ID +#undef THREAD_RETURN +#undef THREAD_HANDLE + +#endif +#endif diff --git a/src/common/src/string_util.cpp b/src/common/src/string_util.cpp new file mode 100644 index 0000000000..ff4c5dbe05 --- /dev/null +++ b/src/common/src/string_util.cpp @@ -0,0 +1,531 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include +#include +#include + +#include "common.h" +#include "common_paths.h" +#include "string_util.h" + +#ifdef _WIN32 + #include +#else + #include + #include +#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(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& 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 +//#include + +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 +std::string CodeToUTF8(const char* fromcode, const std::basic_string& input) +{ + std::string result; + + iconv_t const conv_desc = iconv_open("UTF-8", fromcode); + if ((iconv_t)-1 == conv_desc) + { + ERROR_LOG(COMMON, "Iconv initialization failure [%s]: %s", fromcode, strerror(errno)); + } + else + { + size_t const in_bytes = sizeof(T) * input.size(); + size_t const out_buffer_size = 4 * in_bytes; + + std::string out_buffer; + out_buffer.resize(out_buffer_size); + + auto src_buffer = &input[0]; + size_t src_bytes = in_bytes; + auto dst_buffer = &out_buffer[0]; + size_t dst_bytes = out_buffer.size(); + + while (src_bytes != 0) + { + size_t const iconv_result = iconv(conv_desc, (char**)(&src_buffer), &src_bytes, + &dst_buffer, &dst_bytes); + + if ((size_t)-1 == iconv_result) + { + if (EILSEQ == errno || EINVAL == errno) + { + // Try to skip the bad character + if (src_bytes != 0) + { + --src_bytes; + ++src_buffer; + } + } + else + { + ERROR_LOG(COMMON, "iconv failure [%s]: %s", fromcode, strerror(errno)); + break; + } + } + } + + out_buffer.resize(out_buffer_size - dst_bytes); + out_buffer.swap(result); + + iconv_close(conv_desc); + } + + return result; +} + +std::string CP1252ToUTF8(const std::string& input) +{ + //return CodeToUTF8("CP1252//TRANSLIT", input); + //return CodeToUTF8("CP1252//IGNORE", input); + return CodeToUTF8("CP1252", input); +} + +std::string SHIFTJISToUTF8(const std::string& input) +{ + //return CodeToUTF8("CP932", input); + return CodeToUTF8("SJIS", input); +} + +std::string UTF16ToUTF8(const std::wstring& input) +{ + std::string result = + // CodeToUTF8("UCS-2", input); + // CodeToUTF8("UCS-2LE", input); + // CodeToUTF8("UTF-16", input); + CodeToUTF8("UTF-16LE", input); + + // TODO: why is this needed? + result.erase(std::remove(result.begin(), result.end(), 0x00), result.end()); + return result; +} + +#endif diff --git a/src/common/src/string_util.h b/src/common/src/string_util.h new file mode 100644 index 0000000000..31eaeb2463 --- /dev/null +++ b/src/common/src/string_util.h @@ -0,0 +1,111 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _STRINGUTIL_H_ +#define _STRINGUTIL_H_ + +#include + +#include +#include +#include +#include + +#include "common.h" + +std::string StringFromFormat(const char* format, ...); +// Cheap! +bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args); + +template +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 +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 +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& output); + +// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe" +bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension); + +void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename); +std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest); +std::string UriDecode(const std::string & sSrc); +std::string UriEncode(const std::string & sSrc); + +std::string CP1252ToUTF8(const std::string& str); +std::string SHIFTJISToUTF8(const std::string& str); +std::string UTF16ToUTF8(const std::wstring& str); + +#ifdef _WIN32 + +std::wstring UTF8ToUTF16(const std::string& str); + +#ifdef _UNICODE +inline std::string TStrToUTF8(const std::wstring& str) +{ return UTF16ToUTF8(str); } + +inline std::wstring UTF8ToTStr(const std::string& str) +{ return UTF8ToUTF16(str); } +#else +inline std::string TStrToUTF8(const std::string& str) +{ return str; } + +inline std::string UTF8ToTStr(const std::string& str) +{ return str; } +#endif + +#endif + +#endif // _STRINGUTIL_H_ diff --git a/src/common/src/thread.cpp b/src/common/src/thread.cpp new file mode 100644 index 0000000000..e75dfffebd --- /dev/null +++ b/src/common/src/thread.cpp @@ -0,0 +1,133 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "thread.h" +#include "common.h" + +#ifdef __APPLE__ +#include +#elif defined BSD4_4 +#include +#endif + +#ifdef USE_BEGINTHREADEX +#include +#endif + +namespace Common +{ + +int CurrentThreadId() +{ +#ifdef _WIN32 + return GetCurrentThreadId(); +#elif defined __APPLE__ + return mach_thread_self(); +#else + return 0; +#endif +} + +#ifdef _WIN32 + +void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) +{ + SetThreadAffinityMask(thread, mask); +} + +void SetCurrentThreadAffinity(u32 mask) +{ + SetThreadAffinityMask(GetCurrentThread(), mask); +} + +// Supporting functions +void SleepCurrentThread(int ms) +{ + Sleep(ms); +} + +void SwitchCurrentThread() +{ + SwitchToThread(); +} + +// Sets the debugger-visible name of the current thread. +// Uses undocumented (actually, it is now documented) trick. +// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp + +// This is implemented much nicer in upcoming msvc++, see: +// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx +void SetCurrentThreadName(const char* szThreadName) +{ + static const DWORD MS_VC_EXCEPTION = 0x406D1388; + + #pragma pack(push,8) + struct THREADNAME_INFO + { + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero + } info; + #pragma pack(pop) + + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = -1; //dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + {} +} + +#else // !WIN32, so must be POSIX threads + +void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) +{ +#ifdef __APPLE__ + thread_policy_set(pthread_mach_thread_np(thread), + THREAD_AFFINITY_POLICY, (integer_t *)&mask, 1); +#elif (defined __linux__ || defined BSD4_4) && !(defined ANDROID) + cpu_set_t cpu_set; + CPU_ZERO(&cpu_set); + + for (int i = 0; i != sizeof(mask) * 8; ++i) + if ((mask >> i) & 1) + CPU_SET(i, &cpu_set); + + pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set); +#endif +} + +void SetCurrentThreadAffinity(u32 mask) +{ + SetThreadAffinity(pthread_self(), mask); +} + +void SleepCurrentThread(int ms) +{ + usleep(1000 * ms); +} + +void SwitchCurrentThread() +{ + usleep(1000 * 1); +} + +void SetCurrentThreadName(const char* szThreadName) +{ +#ifdef __APPLE__ + pthread_setname_np(szThreadName); +#else + pthread_setname_np(pthread_self(), szThreadName); +#endif +} + +#endif + +} // namespace Common diff --git a/src/common/src/thread.h b/src/common/src/thread.h new file mode 100644 index 0000000000..3178c40f7e --- /dev/null +++ b/src/common/src/thread.h @@ -0,0 +1,156 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _THREAD_H_ +#define _THREAD_H_ + +#include "std_condition_variable.h" +#include "std_mutex.h" +#include "std_thread.h" + +// Don't include common.h here as it will break LogManager +#include "common_types.h" +#include +#include + +// This may not be defined outside _WIN32 +#ifndef _WIN32 +#ifndef INFINITE +#define INFINITE 0xffffffff +#endif + +//for gettimeofday and struct time(spec|val) +#include +#include +#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 lk(m_mutex); + if (!is_set) + { + is_set = true; + m_condvar.notify_one(); + } + } + + void Wait() + { + std::unique_lock lk(m_mutex); + m_condvar.wait(lk, IsSet(this)); + is_set = false; + } + + void Reset() + { + std::unique_lock 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 lk(m_mutex); + + // TODO: broken when next round of Sync()s + // is entered before all waiting threads return from the notify_all + + if (m_count == ++m_waiting) + { + m_waiting = 0; + m_condvar.notify_all(); + return true; + } + else + { + m_condvar.wait(lk, IsDoneWating(this)); + return false; + } + } + +private: + class IsDoneWating + { + public: + IsDoneWating(const Barrier* bar) + : m_bar(bar) + {} + + bool operator()() + { + return (0 == m_bar->m_waiting); + } + + private: + const Barrier* const m_bar; + }; + + std::condition_variable m_condvar; + std::mutex m_mutex; + const size_t m_count; + volatile size_t m_waiting; +}; + +void SleepCurrentThread(int ms); +void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms + +// Use this function during a spin-wait to make the current thread +// relax while another thread is working. This may be more efficient +// than using events because event functions use kernel calls. +inline void YieldCPU() +{ + std::this_thread::yield(); +} + +void SetCurrentThreadName(const char *name); + +} // namespace Common + +#endif // _THREAD_H_ diff --git a/src/common/src/thunk.h b/src/common/src/thunk.h new file mode 100644 index 0000000000..4b64a9b0e5 --- /dev/null +++ b/src/common/src/thunk.h @@ -0,0 +1,46 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _THUNK_H_ +#define _THUNK_H_ + +#include + +#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 thunks; + + const u8 *save_regs; + const u8 *load_regs; + +public: + ThunkManager() { + Init(); + } + ~ThunkManager() { + Shutdown(); + } + void *ProtectFunction(void *function, int num_params); +private: + void Init(); + void Shutdown(); + void Reset(); +}; + +#endif // _THUNK_H_ diff --git a/src/common/src/timer.cpp b/src/common/src/timer.cpp new file mode 100644 index 0000000000..0300133c51 --- /dev/null +++ b/src/common/src/timer.cpp @@ -0,0 +1,226 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include + +#ifdef _WIN32 +#include +#include +#include +#else +#include +#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 diff --git a/src/common/src/timer.h b/src/common/src/timer.h new file mode 100644 index 0000000000..9ee5719a3d --- /dev/null +++ b/src/common/src/timer.h @@ -0,0 +1,46 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +#include "common.h" +#include + +namespace Common +{ +class Timer +{ +public: + Timer(); + + void Start(); + void Stop(); + void Update(); + + // The time difference is always returned in milliseconds, regardless of alternative internal representation + u64 GetTimeDifference(); + void AddTimeDifference(); + + static void IncreaseResolution(); + static void RestoreResolution(); + static u64 GetTimeSinceJan1970(); + static u64 GetLocalTimeSinceJan1970(); + static double GetDoubleTime(); + + static std::string GetTimeFormatted(); + std::string GetTimeElapsedFormatted() const; + u64 GetTimeElapsed(); + + static u32 GetTimeMs(); + +private: + u64 m_LastTime; + u64 m_StartTime; + bool m_Running; +}; + +} // Namespace Common + +#endif // _TIMER_H_ diff --git a/src/common/src/version.cpp b/src/common/src/version.cpp new file mode 100644 index 0000000000..47d7b52adc --- /dev/null +++ b/src/common/src/version.cpp @@ -0,0 +1,45 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "common.h" +#include "scm_rev.h" + +#ifdef _DEBUG + #define BUILD_TYPE_STR "Debug " +#elif defined DEBUGFAST + #define BUILD_TYPE_STR "DebugFast " +#else + #define BUILD_TYPE_STR "" +#endif + +const char *scm_rev_str = "Dolphin " +#if !SCM_IS_MASTER + "[" SCM_BRANCH_STR "] " +#endif + +#ifdef __INTEL_COMPILER + BUILD_TYPE_STR SCM_DESC_STR "-ICC"; +#else + BUILD_TYPE_STR SCM_DESC_STR; +#endif + +#ifdef _M_X64 +#define NP_ARCH "x64" +#else +#ifdef _M_ARM +#define NP_ARCH "ARM" +#else +#define NP_ARCH "x86" +#endif +#endif + +#ifdef _WIN32 +const char *netplay_dolphin_ver = SCM_DESC_STR " W" NP_ARCH; +#elif __APPLE__ +const char *netplay_dolphin_ver = SCM_DESC_STR " M" NP_ARCH; +#else +const char *netplay_dolphin_ver = SCM_DESC_STR " L" NP_ARCH; +#endif + +const char *scm_rev_git_str = SCM_REV_STR; diff --git a/vsprops/CodeGen_Debug.props b/vsprops/code_generation_debug.props similarity index 100% rename from vsprops/CodeGen_Debug.props rename to vsprops/code_generation_debug.props diff --git a/vsprops/CodeGen_Release.props b/vsprops/code_generation_release.props similarity index 100% rename from vsprops/CodeGen_Release.props rename to vsprops/code_generation_release.props