diff --git a/.gitignore b/.gitignore index f8681c035f..274d7b4459 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,8 @@ rpcs3/git-version.h # Copyrighted files /bin/data/ /bin/dev_flash/data/font +/bin/dev_flash/sys +/bin/dev_flash/vsh # Ignore installed games except test homebrews !/bin/dev_hdd0/game/ diff --git a/README.md b/README.md index c626e494b7..956bde47ba 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,11 @@ rpcs3 ===== -PS3 emulator/debugger \ No newline at end of file +PS3 emulator/debugger + +The [FAQ](https://github.com/DHrpcs3/rpcs3/wiki/FAQ) has some basic information. + +For discussion about the emulator and PS3 emulation please visit the [official forums](http://www.emunewz.net/forum/forumdisplay.php?fid=162). + +If you want to contribute please take a took at the [Coding Style](https://github.com/DHrpcs3/rpcs3/wiki/Coding-Style) and [Roadmap](https://github.com/DHrpcs3/rpcs3/wiki/Roadmap) pages. + diff --git a/Utilities/BEType.h b/Utilities/BEType.h index cc6a6fed62..cfd2c4265e 100644 --- a/Utilities/BEType.h +++ b/Utilities/BEType.h @@ -2,6 +2,10 @@ #include "Utilities/GNU.h" +#define se16(x) const_se_t::value +#define se32(x) const_se_t::value +#define se64(x) const_se_t::value + template struct se_t; template struct se_t { static __forceinline void func(T& dst, const T src) { (u8&)dst = (u8&)src; } }; template struct se_t { static __forceinline void func(T& dst, const T src) { (u16&)dst = _byteswap_ushort((u16&)src); } }; diff --git a/Utilities/IdManager.h b/Utilities/IdManager.h index 4e95b29b51..527fcb320f 100644 --- a/Utilities/IdManager.h +++ b/Utilities/IdManager.h @@ -34,12 +34,12 @@ public: struct ID { - wxString m_name; + std::string m_name; u8 m_attr; IDData* m_data; template - ID(const wxString& name, T* data, const u8 attr) + ID(const std::string& name, T* data, const u8 attr) : m_name(name) , m_attr(attr) { @@ -58,8 +58,6 @@ struct ID class IdManager { - ArrayF IDs; - static const ID_TYPE s_first_id = 1; static const ID_TYPE s_max_id = -1; @@ -99,7 +97,7 @@ public: } template - ID_TYPE GetNewID(const wxString& name = wxEmptyString, T* data = nullptr, const u8 attr = 0) + ID_TYPE GetNewID(const std::string& name = "", T* data = nullptr, const u8 attr = 0) { std::lock_guard lock(m_mtx_main); diff --git a/Utilities/SMutex.cpp b/Utilities/SMutex.cpp new file mode 100644 index 0000000000..3c1eecb3fc --- /dev/null +++ b/Utilities/SMutex.cpp @@ -0,0 +1,26 @@ +#include +#include + +__forceinline void SM_Sleep() +{ + Sleep(1); +} + +__forceinline DWORD SM_GetCurrentThreadId() +{ + return GetCurrentThreadId(); +} + +__forceinline u32 SM_GetCurrentCPUThreadId() +{ + if (CPUThread* t = GetCurrentCPUThread()) + { + return t->GetId(); + } + return 0; +} + +__forceinline be_t SM_GetCurrentCPUThreadIdBE() +{ + return SM_GetCurrentCPUThreadId(); +} \ No newline at end of file diff --git a/Utilities/SMutex.h b/Utilities/SMutex.h new file mode 100644 index 0000000000..ac5ec269e8 --- /dev/null +++ b/Utilities/SMutex.h @@ -0,0 +1,155 @@ +#pragma once +#include + +extern void SM_Sleep(); +extern DWORD SM_GetCurrentThreadId(); +extern u32 SM_GetCurrentCPUThreadId(); +extern be_t SM_GetCurrentCPUThreadIdBE(); + +enum SMutexResult +{ + SMR_OK = 0, // succeeded (lock, trylock, unlock) + SMR_FAILED, // failed (trylock, unlock) + SMR_DEADLOCK, // mutex reached deadlock (lock, trylock) + SMR_SIGNAL = SMR_DEADLOCK, // unlock can be used for signaling specific thread + SMR_PERMITTED, // not owner of the mutex (unlock) + SMR_ABORT, // emulator has been stopped (lock, trylock, unlock) + SMR_DESTROYED, // mutex has been destroyed (lock, trylock, unlock) + SMR_TIMEOUT, // timed out (lock) +}; + +template +< + typename T, + u32 free_value = 0, + u32 dead_value = ~0, + void (wait)() = SM_Sleep +> +class SMutexBase +{ + static_assert(sizeof(T) == 4, "Invalid SMutexBase typename"); + std::atomic owner; + +public: + SMutexBase() + : owner((T)free_value) + { + } + + ~SMutexBase() + { + lock((T)dead_value); + owner = (T)dead_value; + } + + __forceinline T GetOwner() const + { + return (T&)owner; + } + + SMutexResult trylock(T tid) + { + T old = (T)free_value; + + if (!owner.compare_exchange_strong(old, tid)) + { + if (old == tid) + { + return SMR_DEADLOCK; + } + if (Emu.IsStopped()) + { + return SMR_ABORT; + } + if (old == (T)dead_value) + { + return SMR_DESTROYED; + } + + return SMR_FAILED; + } + + return SMR_OK; + } + + SMutexResult unlock(T tid, T to = (T)free_value) + { + T old = tid; + + if (!owner.compare_exchange_strong(old, to)) + { + if (old == (T)free_value) + { + return SMR_FAILED; + } + if (old == (T)dead_value) + { + return SMR_DESTROYED; + } + + return SMR_PERMITTED; + } + + return SMR_OK; + } + + SMutexResult lock(T tid, u64 timeout = 0) + { + u64 counter = 0; + + while (true) + { + switch (SMutexResult res = trylock(tid)) + { + case SMR_FAILED: break; + default: return res; + } + + wait(); + + if (timeout && counter++ > timeout) + { + return SMR_TIMEOUT; + } + } + } +}; + +template +class SMutexLockerBase +{ + SMutexBase& sm; +public: + const T tid; + + SMutexLockerBase(SMutexBase& _sm) + : sm(_sm) + , tid(get_tid()) + { + if (!tid) + { + ConLog.Error("SMutexLockerBase: thread id == 0"); + Emu.Pause(); + } + sm.lock(tid); + } + + ~SMutexLockerBase() + { + sm.unlock(tid); + } +}; + +typedef SMutexBase + SMutexGeneral; +typedef SMutexBase + SMutex; +typedef SMutexBase> + SMutexBE; + +typedef SMutexLockerBase + SMutexGeneralLocker; +typedef SMutexLockerBase + SMutexLocker; +typedef SMutexLockerBase, SM_GetCurrentCPUThreadIdBE> + SMutexBELocker; \ No newline at end of file diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index eab5e860d4..17c58d3ef6 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1,29 +1,11 @@ #include "stdafx.h" #include "Thread.h" -static DWORD g_tls_this_thread = 0xFFFFFFFF; - -struct __init_tls -{ - //NamedThreadBase m_main_thr; - - __init_tls() - { - g_tls_this_thread = ::TlsAlloc(); - //m_main_thr.SetThreadName("Main Thread"); - //::TlsSetValue(g_tls_this_thread, &m_main_thr); - ::TlsSetValue(g_tls_this_thread, nullptr); - } - - ~__init_tls() - { - ::TlsFree(g_tls_this_thread); - } -} _init_tls; +__declspec(thread) NamedThreadBase* g_tls_this_thread = nullptr; NamedThreadBase* GetCurrentNamedThread() { - return (NamedThreadBase*)::TlsGetValue(g_tls_this_thread); + return g_tls_this_thread; } std::string NamedThreadBase::GetThreadName() const @@ -62,7 +44,7 @@ void ThreadBase::Start() m_executor = new std::thread( [this]() { - ::TlsSetValue(g_tls_this_thread, this); + g_tls_this_thread = this; Task(); @@ -130,7 +112,7 @@ thread::thread() void thread::start(std::function func) { - m_thr = std::thread([this, func]() { NamedThreadBase info(m_name); ::TlsSetValue(g_tls_this_thread, &info); func(); }); + m_thr = std::thread([this, func]() { NamedThreadBase info(m_name); g_tls_this_thread = &info; func(); }); } void thread::detach() diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 1a7ff1b011..df0320a54a 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -1,6 +1,8 @@ #include "stdafx.h" #include "CPUThread.h" +reservation_struct reservation; + CPUThread* GetCurrentCPUThread() { return (CPUThread*)GetCurrentNamedThread(); @@ -89,14 +91,12 @@ void CPUThread::Wait(const CPUThread& thr) bool CPUThread::Sync() { - wxCriticalSectionLocker lock(m_cs_sync); - return m_sync_wait; } int CPUThread::ThreadStatus() { - if(Emu.IsStopped()) + if(Emu.IsStopped() || IsStopped() || IsPaused()) { return CPUThread_Stopped; } @@ -236,7 +236,7 @@ void CPUThread::Pause() DoPause(); Emu.CheckStatus(); - ThreadBase::Stop(); + // ThreadBase::Stop(); // "Abort() called" exception #ifndef QT_UI wxGetApp().SendDbgCommand(DID_PAUSED_THREAD, this); #endif diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index 54a5ae6a8c..4010adc7c5 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -1,6 +1,25 @@ #pragma once #include "Emu/Memory/MemoryBlock.h" #include "Emu/CPU/CPUDecoder.h" +#include "Utilities/SMutex.h" + +struct reservation_struct +{ + SMutex mutex; // mutex for updating reservation_owner and data + volatile u32 owner; // id of thread that got reservation + volatile u32 addr; + volatile u32 size; + volatile u32 data32; + volatile u64 data64; + // atm, PPU can't break SPU MFC reservation correctly + + __forceinline void clear() + { + owner = 0; + } +}; + +extern reservation_struct reservation; enum CPUThreadType { @@ -109,7 +128,7 @@ protected: CPUThread(CPUThreadType type); public: - ~CPUThread(); + virtual ~CPUThread(); u32 m_wait_thread_id; diff --git a/rpcs3/Emu/CPU/CPUThreadManager.cpp b/rpcs3/Emu/CPU/CPUThreadManager.cpp index 3dd17d02d1..46bdce578b 100644 --- a/rpcs3/Emu/CPU/CPUThreadManager.cpp +++ b/rpcs3/Emu/CPU/CPUThreadManager.cpp @@ -36,7 +36,7 @@ CPUThread& CPUThreadManager::AddThread(CPUThreadType type) default: assert(0); } - new_thread->SetId(Emu.GetIdManager().GetNewID(wxString::Format("%s Thread", new_thread->GetTypeString().mb_str()), new_thread)); + new_thread->SetId(Emu.GetIdManager().GetNewID(wxString::Format("%s Thread", new_thread->GetTypeString().mb_str()).mb_str(), new_thread)); m_threads.Add(new_thread); #ifndef QT_UI @@ -76,6 +76,8 @@ void CPUThreadManager::RemoveThread(const u32 id) s32 CPUThreadManager::GetThreadNumById(CPUThreadType type, u32 id) { + std::lock_guard lock(m_mtx_thread); + s32 num = 0; for(u32 i=0; i lock(m_mtx_thread); + for(u32 i=0; i> sh; - for (uint b = 14; b >= 0; b--) + for (uint b = 14; ~b; b--) { CPU.VPR[vd]._u8[b] = (CPU.VPR[va]._u8[b] >> sh) | (CPU.VPR[va]._u8[b+1] << (8 - sh)); } @@ -2362,9 +2362,12 @@ private: void LWARX(u32 rd, u32 ra, u32 rb) { const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; - CPU.reserve_addr = addr; - CPU.reserve = true; - CPU.GPR[rd] = Memory.Read32(addr); + + SMutexLocker lock(reservation.mutex); + reservation.owner = lock.tid; + reservation.addr = addr; + reservation.size = 4; + reservation.data32 = CPU.GPR[rd] = Memory.Read32(addr); } void LDX(u32 rd, u32 ra, u32 rb) { @@ -2535,9 +2538,12 @@ private: void LDARX(u32 rd, u32 ra, u32 rb) { const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; - CPU.reserve_addr = addr; - CPU.reserve = true; - CPU.GPR[rd] = Memory.Read64(addr); + + SMutexLocker lock(reservation.mutex); + reservation.owner = lock.tid; + reservation.addr = addr; + reservation.size = 8; + reservation.data64 = CPU.GPR[rd] = Memory.Read64(addr); } void DCBF(u32 ra, u32 rb) { @@ -2650,25 +2656,19 @@ private: } void STWCX_(u32 rs, u32 ra, u32 rb) { - CPU.SetCR(0, CPU.XER.SO ? CR_SO : 0); - - if(CPU.reserve) - { - const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; - if(addr == CPU.reserve_addr) - { - Memory.Write32(addr, CPU.GPR[rs]); - CPU.SetCR_EQ(0, true); - CPU.reserve = false; - } - else - { - static const bool u = 0; - if(u) Memory.Write32(addr, CPU.GPR[rs]); - CPU.SetCR_EQ(0, u); - CPU.reserve = false; - } + SMutexLocker lock(reservation.mutex); + if (lock.tid == reservation.owner && reservation.addr == addr && reservation.size == 4) + { + // Memory.Write32(addr, CPU.GPR[rs]); + CPU.SetCR_EQ(0, InterlockedCompareExchange((volatile long*)(Memory + addr), (u32)CPU.GPR[rs], reservation.data32) == reservation.data32); + reservation.clear(); + } + else + { + CPU.SetCR_EQ(0, false); + if (lock.tid == reservation.owner) reservation.clear(); } } void STWX(u32 rs, u32 ra, u32 rb) @@ -2709,23 +2709,19 @@ private: } void STDCX_(u32 rs, u32 ra, u32 rb) { - CPU.SetCR(0, CPU.XER.SO ? CR_SO : 0); - if(!CPU.reserve) - { - const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; + const u64 addr = ra ? CPU.GPR[ra] + CPU.GPR[rb] : CPU.GPR[rb]; - if(addr == CPU.reserve_addr) - { - Memory.Write64(addr, CPU.GPR[rs]); - CPU.SetCR_EQ(0, true); - } - else - { - static const bool u = 0; - if(u) Memory.Write64(addr, CPU.GPR[rs]); - CPU.SetCR_EQ(0, u); - CPU.reserve = false; - } + SMutexLocker lock(reservation.mutex); + if (lock.tid == reservation.owner && reservation.addr == addr && reservation.size == 8) + { + // Memory.Write64(addr, CPU.GPR[rs]); + CPU.SetCR_EQ(0, InterlockedCompareExchange64((volatile long long*)(Memory + addr), CPU.GPR[rs], reservation.data64) == reservation.data64); + reservation.clear(); + } + else + { + CPU.SetCR_EQ(0, false); + if (lock.tid == reservation.owner) reservation.clear(); } } void STBX(u32 rs, u32 ra, u32 rb) diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index 2886049787..91dce7d6a8 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -22,7 +22,6 @@ PPUThread::PPUThread() : PPCThread(CPU_THREAD_PPU) PPUThread::~PPUThread() { - //~PPCThread(); } void PPUThread::DoReset() @@ -45,9 +44,6 @@ void PPUThread::DoReset() VSCR.VSCR = 0; cycle = 0; - - reserve = false; - reserve_addr = 0; } void PPUThread::AddArgv(const wxString& arg) @@ -68,6 +64,7 @@ void PPUThread::InitRegs() SetPc(pc); + /* const s32 thread_num = Emu.GetCPU().GetThreadNumById(GetType(), GetId()); if(thread_num < 0) @@ -76,6 +73,7 @@ void PPUThread::InitRegs() Emu.Pause(); return; } + */ /* const s32 tls_size = Emu.GetTLSFilesz() * thread_num; @@ -120,7 +118,7 @@ void PPUThread::InitRegs() GPR[6] = m_args[3]; } - u32 prx_mem = Memory.PRXMem.Alloc(0x10000); + u32 prx_mem = Memory.PRXMem.AllocAlign(0x10000); Memory.Write64(prx_mem, 0xDEADBEEFABADCAFE); GPR[0] = pc; diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index fa63ae9800..74c200b104 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -370,14 +370,14 @@ struct PPCdouble case _FPCLASS_PINF: return FPR_PINF; } #else - switch (fpc) - { - case FP_NAN: return FPR_QNAN; - case FP_INFINITE: return signbit(_double) ? FPR_NINF : FPR_PINF; - case FP_SUBNORMAL: return signbit(_double) ? FPR_ND : FPR_PD; - case FP_ZERO: return signbit(_double) ? FPR_NZ : FPR_PZ; - default: return signbit(_double) ? FPR_NN : FPR_PN; - } + switch (fpc) + { + case FP_NAN: return FPR_QNAN; + case FP_INFINITE: return signbit(_double) ? FPR_NINF : FPR_PINF; + case FP_SUBNORMAL: return signbit(_double) ? FPR_ND : FPR_PD; + case FP_ZERO: return signbit(_double) ? FPR_NZ : FPR_PZ; + default: return signbit(_double) ? FPR_NN : FPR_PN; + } #endif throw wxString::Format("PPCdouble::UpdateType() -> unknown fpclass (0x%04x).", fpc); @@ -602,14 +602,11 @@ public: }; }; - u64 reserve_addr; - bool reserve; - u64 cycle; public: PPUThread(); - ~PPUThread(); + virtual ~PPUThread(); inline u8 GetCR(const u8 n) const { diff --git a/rpcs3/Emu/Cell/RawSPUThread.h b/rpcs3/Emu/Cell/RawSPUThread.h index 2227a4cdc0..5a19973afc 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.h +++ b/rpcs3/Emu/Cell/RawSPUThread.h @@ -50,7 +50,7 @@ class RawSPUThread public: RawSPUThread(u32 index, CPUThreadType type = CPU_THREAD_RAW_SPU); - ~RawSPUThread(); + virtual ~RawSPUThread(); virtual bool Read8(const u64 addr, u8* value) override; virtual bool Read16(const u64 addr, u16* value) override; diff --git a/rpcs3/Emu/Cell/SPUInterpreter.h b/rpcs3/Emu/Cell/SPUInterpreter.h index 1dcb845bd5..63d67deb2d 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.h +++ b/rpcs3/Emu/Cell/SPUInterpreter.h @@ -32,15 +32,8 @@ private: //0 - 10 void STOP(u32 code) { - if(code & 0x2000) - { - CPU.SetExitStatus(code & 0xfff); - } - else - { - ConLog.Warning("STOP: 0x%x", code); - //Emu.Pause(); - } + ConLog.Warning("STOP: 0x%x (m_exit_status -> 0)", code); + CPU.SetExitStatus(0); CPU.Stop(); } void LNOP() @@ -146,7 +139,7 @@ private: void ROTH(u32 rt, u32 ra, u32 rb) { for (int h = 0; h < 8; h++) - CPU.GPR[rt]._u16[h] = (CPU.GPR[ra]._u16[h] << (CPU.GPR[rb]._u16[h] & 0xf)) | (CPU.GPR[ra]._u16[h] >> (16 - (CPU.GPR[rb]._u32[h] & 0xf))); + CPU.GPR[rt]._u16[h] = (CPU.GPR[ra]._u16[h] << (CPU.GPR[rb]._u16[h] & 0xf)) | (CPU.GPR[ra]._u16[h] >> (16 - (CPU.GPR[rb]._u16[h] & 0xf))); } void ROTHM(u32 rt, u32 ra, u32 rb) { diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 60e3cf9238..6a0d8f5892 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -61,7 +61,7 @@ void SPUThread::InitRegs() u64 SPUThread::GetFreeStackSize() const { - return (GetStackAddr() + GetStackSize()) - GPR[1]._u64[3]; + return (GetStackAddr() + GetStackSize()) - GPR[1]._u32[3]; } void SPUThread::DoRun() diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 4e876195c9..e4cd1ea0e6 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -4,8 +4,6 @@ #include "MFC.h" #include -extern std::mutex g_SyncMutex; //can provide compatability for CellSyncMutex through SPU<>PPU and SPU<>SPU - static const char* spu_reg_name[128] = { "$LR", "$SP", "$2", "$3", "$4", "$5", "$6", "$7", @@ -391,7 +389,7 @@ public: else { #ifdef _M_X64 - _InterlockedOr64((volatile __int64*)m_indval, ((u64)value << 32) | 1); + InterlockedOr64((volatile __int64*)m_indval, ((u64)value << 32) | 1); #else ConLog.Error("PushUncond_OR(): no code compiled"); #endif @@ -516,34 +514,80 @@ public: case MFC_PUT_CMD: case MFC_GET_CMD: { - if (enable_log) ConLog.Write("DMA %s%s%s: lsa = 0x%x, ea = 0x%llx, tag = 0x%x, size = 0x%x, cmd = 0x%x", + /* if (enable_log) ConLog.Write("DMA %s%s%s: lsa = 0x%x, ea = 0x%llx, tag = 0x%x, size = 0x%x, cmd = 0x%x", op & MFC_PUT_CMD ? "PUT" : "GET", op & MFC_BARRIER_MASK ? "B" : "", op & MFC_FENCE_MASK ? "F" : "", - lsa, ea, tag, size, cmd); - MFCArgs.CMDStatus.SetValue(dmac.Cmd(cmd, tag, lsa, ea, size)); + lsa, ea, tag, size, cmd); */ + if (op & MFC_PUT_CMD) + { + SMutexLocker lock(reservation.mutex); + MFCArgs.CMDStatus.SetValue(dmac.Cmd(cmd, tag, lsa, ea, size)); + if ((reservation.addr + reservation.size > ea && reservation.addr <= ea + size) || + (ea + size > reservation.addr && ea <= reservation.addr + reservation.size)) + { + reservation.clear(); + } + } + else + { + MFCArgs.CMDStatus.SetValue(dmac.Cmd(cmd, tag, lsa, ea, size)); + } } break; case MFC_GETLLAR_CMD: case MFC_PUTLLC_CMD: case MFC_PUTLLUC_CMD: + case MFC_PUTQLLUC_CMD: { - if (op == MFC_GETLLAR_CMD) - { - g_SyncMutex.lock(); - } - - ConLog.Warning("DMA %s: lsa=0x%x, ea = 0x%llx, (tag) = 0x%x, (size) = 0x%x, cmd = 0x%x", - op == MFC_GETLLAR_CMD ? "GETLLAR" : op == MFC_PUTLLC_CMD ? "PUTLLC" : "PUTLLUC", + if (enable_log) ConLog.Write("DMA %s: lsa=0x%x, ea = 0x%llx, (tag) = 0x%x, (size) = 0x%x, cmd = 0x%x", + op == MFC_GETLLAR_CMD ? "GETLLAR" : op == MFC_PUTLLC_CMD ? "PUTLLC" : op == MFC_PUTLLUC_CMD ? "PUTLLUC" : "PUTQLLUC", lsa, ea, tag, size, cmd); - dmac.ProcessCmd(op == MFC_GETLLAR_CMD ? MFC_GET_CMD : MFC_PUT_CMD, tag, lsa, ea, 128); - Prxy.AtomicStat.PushUncond(op == MFC_GETLLAR_CMD ? MFC_GETLLAR_SUCCESS : op == MFC_PUTLLC_CMD ? MFC_PUTLLC_SUCCESS : MFC_PUTLLUC_SUCCESS); - - if (op == MFC_PUTLLC_CMD || op == MFC_PUTLLUC_CMD) + if (op == MFC_GETLLAR_CMD) // get reservation { - g_SyncMutex.unlock(); + SMutexLocker lock(reservation.mutex); + reservation.owner = lock.tid; + reservation.addr = ea; + reservation.size = 128; + dmac.ProcessCmd(MFC_GET_CMD, tag, lsa, ea, 128); + Prxy.AtomicStat.PushUncond(MFC_GETLLAR_SUCCESS); + } + else if (op == MFC_PUTLLC_CMD) // store conditional + { + SMutexLocker lock(reservation.mutex); + if (reservation.owner == lock.tid) // succeeded + { + if (reservation.addr == ea && reservation.size == 128) + { + dmac.ProcessCmd(MFC_PUT_CMD, tag, lsa, ea, 128); + Prxy.AtomicStat.PushUncond(MFC_PUTLLC_SUCCESS); + } + else + { + Prxy.AtomicStat.PushUncond(MFC_PUTLLC_FAILURE); + } + reservation.clear(); + } + else // failed + { + Prxy.AtomicStat.PushUncond(MFC_PUTLLC_FAILURE); + } + } + else // store unconditional + { + SMutexLocker lock(reservation.mutex); + dmac.ProcessCmd(MFC_PUT_CMD, tag, lsa, ea, 128); + if (op == MFC_PUTLLUC_CMD) + { + Prxy.AtomicStat.PushUncond(MFC_PUTLLUC_SUCCESS); + } + if ((reservation.addr + reservation.size > ea && reservation.addr <= ea + size) || + (ea + size > reservation.addr && ea <= reservation.addr + reservation.size)) + { + reservation.clear(); + } } } break; @@ -698,7 +742,7 @@ public: public: SPUThread(CPUThreadType type = CPU_THREAD_SPU); - ~SPUThread(); + virtual ~SPUThread(); virtual wxString RegsToString() { diff --git a/rpcs3/Emu/FS/vfsDirBase.cpp b/rpcs3/Emu/FS/vfsDirBase.cpp index 0d29a26585..8e5c945e36 100644 --- a/rpcs3/Emu/FS/vfsDirBase.cpp +++ b/rpcs3/Emu/FS/vfsDirBase.cpp @@ -1,3 +1,49 @@ #include "stdafx.h" #include "vfsDirBase.h" +vfsDirBase::vfsDirBase(const wxString& path) +{ + Open(path); +} + +vfsDirBase::~vfsDirBase() +{ +} + +bool vfsDirBase::Open(const wxString& path) +{ + if(!IsOpened()) + Close(); + + if(!IsExists(path)) + return false; + + m_cwd += '/' + path; + return true; +} + +bool vfsDirBase::IsOpened() const +{ + return !m_cwd.IsEmpty(); +} + +bool vfsDirBase::IsExists(const wxString& path) const +{ + return wxDirExists(path); +} + +const Array& vfsDirBase::GetEntries() const +{ + return m_entries; +} + +void vfsDirBase::Close() +{ + m_cwd = wxEmptyString; + m_entries.Clear(); +} + +wxString vfsDirBase::GetPath() const +{ + return m_cwd; +} \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsDirBase.h b/rpcs3/Emu/FS/vfsDirBase.h index 969a2d1df5..a1d5301efe 100644 --- a/rpcs3/Emu/FS/vfsDirBase.h +++ b/rpcs3/Emu/FS/vfsDirBase.h @@ -1,19 +1,51 @@ #pragma once -struct DirInfo +enum DirEntryFlags { - wxString m_name; + DirEntry_TypeDir = 0x0, + DirEntry_TypeFile = 0x1, + DirEntry_TypeMask = 0x1, + DirEntry_PermWritable = 0x20, + DirEntry_PermReadable = 0x40, + DirEntry_PermExecutable = 0x80, +}; +struct DirEntryInfo +{ + wxString name; + u32 flags; + time_t create_time; + time_t access_time; + time_t modify_time; + + DirEntryInfo() + : flags(0) + , create_time(0) + , access_time(0) + , modify_time(0) + { + } }; class vfsDirBase { - virtual bool Open(const wxString& path)=0; - virtual Array GetEntryes()=0; - virtual void Close()=0; +protected: + wxString m_cwd; + Array m_entries; + +public: + vfsDirBase(const wxString& path); + virtual ~vfsDirBase(); + + virtual bool Open(const wxString& path); + virtual bool IsOpened() const; + virtual bool IsExists(const wxString& path) const; + virtual const Array& GetEntries() const; + virtual void Close(); + virtual wxString GetPath() const; virtual bool Create(const wxString& path)=0; - virtual bool Exists(const wxString& path)=0; + //virtual bool Create(const DirEntryInfo& info)=0; virtual bool Rename(const wxString& from, const wxString& to)=0; virtual bool Remove(const wxString& path)=0; }; \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsLocalDir.cpp b/rpcs3/Emu/FS/vfsLocalDir.cpp new file mode 100644 index 0000000000..291b34f9d2 --- /dev/null +++ b/rpcs3/Emu/FS/vfsLocalDir.cpp @@ -0,0 +1,63 @@ +#include "stdafx.h" +#include "vfsLocalDir.h" +#include + +vfsLocalDir::vfsLocalDir(const wxString& path) + : vfsDirBase(path) + , m_pos(0) +{ +} + +vfsLocalDir::~vfsLocalDir() +{ +} + +bool vfsLocalDir::Open(const wxString& path) +{ + if(!vfsDirBase::Open(path)) + return false; + + wxDir dir; + + if(!dir.Open(path)) + return false; + + wxString name; + for(bool is_ok = dir.GetFirst(&name); is_ok; is_ok = dir.GetNext(&name)) + { + wxString dir_path = path + wxFILE_SEP_PATH + name; + + DirEntryInfo& info = m_entries[m_entries.Move(new DirEntryInfo())]; + info.name = name; + + info.flags |= wxDirExists(dir_path) ? DirEntry_TypeDir : DirEntry_TypeFile; + if(wxIsWritable(dir_path)) info.flags |= DirEntry_PermWritable; + if(wxIsReadable(dir_path)) info.flags |= DirEntry_PermReadable; + if(wxIsExecutable(dir_path)) info.flags |= DirEntry_PermExecutable; + } + + return true; +} + +const DirEntryInfo* vfsLocalDir::Read() +{ + if (m_pos >= m_entries.GetCount()) + return 0; + + return &m_entries[m_pos++]; +} + +bool vfsLocalDir::Create(const wxString& path) +{ + return wxFileName::Mkdir(path, 0777, wxPATH_MKDIR_FULL); +} + +bool vfsLocalDir::Rename(const wxString& from, const wxString& to) +{ + return false; +} + +bool vfsLocalDir::Remove(const wxString& path) +{ + return wxRmdir(path); +} \ No newline at end of file diff --git a/rpcs3/Emu/FS/vfsLocalDir.h b/rpcs3/Emu/FS/vfsLocalDir.h new file mode 100644 index 0000000000..860064aa13 --- /dev/null +++ b/rpcs3/Emu/FS/vfsLocalDir.h @@ -0,0 +1,19 @@ +#pragma once +#include "vfsDirBase.h" + +class vfsLocalDir : public vfsDirBase +{ +private: + u32 m_pos; + +public: + vfsLocalDir(const wxString& path = wxEmptyString); + virtual ~vfsLocalDir(); + + virtual bool Open(const wxString& path) override; + const DirEntryInfo* Read(); + + virtual bool Create(const wxString& path) override; + virtual bool Rename(const wxString& from, const wxString& to) override; + virtual bool Remove(const wxString& path) override; +}; \ No newline at end of file diff --git a/rpcs3/Emu/GS/GL/GLFragmentProgram.cpp b/rpcs3/Emu/GS/GL/GLFragmentProgram.cpp index fc55c3241e..6e6fd21d2d 100644 --- a/rpcs3/Emu/GS/GL/GLFragmentProgram.cpp +++ b/rpcs3/Emu/GS/GL/GLFragmentProgram.cpp @@ -133,10 +133,11 @@ std::string GLFragmentDecompilerThread::AddCond(int fp16) std::string GLFragmentDecompilerThread::AddConst() { - if(m_parr.HasParam(PARAM_UNIFORM, "vec4", std::string("fc") + std::to_string(m_size + 4 * 4))) - { - return std::string("fc") + std::to_string(m_size + 4 * 4); - } + std::string name = std::string("fc") + std::to_string(m_size + 4 * 4); + if(m_parr.HasParam(PARAM_UNIFORM, "vec4", name)) + { + return name; + } mem32_ptr_t data(m_addr + m_size + m_offset); @@ -145,7 +146,7 @@ std::string GLFragmentDecompilerThread::AddConst() u32 y = GetData(data[1]); u32 z = GetData(data[2]); u32 w = GetData(data[3]); - return m_parr.AddParam(PARAM_UNIFORM, "vec4", std::string("fc") + std::to_string(m_size + 4 * 4), + return m_parr.AddParam(PARAM_UNIFORM, "vec4", name, std::string("vec4(") + std::to_string((float&)x) + ", " + std::to_string((float&)y) + ", " + std::to_string((float&)z) + ", " + std::to_string((float&)w) + ")"); } diff --git a/rpcs3/Emu/GS/RSXThread.h b/rpcs3/Emu/GS/RSXThread.h index c6bb8ba5fb..fe05924aa6 100644 --- a/rpcs3/Emu/GS/RSXThread.h +++ b/rpcs3/Emu/GS/RSXThread.h @@ -494,6 +494,8 @@ protected: Reset(); } + virtual ~RSXThread() {} + void Reset() { m_set_color_mask = false; diff --git a/rpcs3/Emu/Memory/DynamicMemoryBlockBase.inl b/rpcs3/Emu/Memory/DynamicMemoryBlockBase.inl index bbdafbd80f..7af30bc435 100644 --- a/rpcs3/Emu/Memory/DynamicMemoryBlockBase.inl +++ b/rpcs3/Emu/Memory/DynamicMemoryBlockBase.inl @@ -10,11 +10,13 @@ DynamicMemoryBlockBase::DynamicMemoryBlockBase() template const u32 DynamicMemoryBlockBase::GetUsedSize() const { + std::lock_guard lock(m_lock); + u32 size = 0; - for(u32 i=0; i::IsInMyRange(const u64 addr, const u32 size) template bool DynamicMemoryBlockBase::IsMyAddress(const u64 addr) { - for(u32 i=0; i= m_used_mem[i].addr && addr < m_used_mem[i].addr + m_used_mem[i].size) - { - return true; - } - } + if (!IsInMyRange(addr)) return false; - return false; + const u32 index = MemoryBlock::FixAddr(addr) >> 12; + + return m_pages[index] && !m_locked[index]; } template MemoryBlock* DynamicMemoryBlockBase::SetRange(const u64 start, const u32 size) { - m_max_size = size; + std::lock_guard lock(m_lock); + + m_max_size = PAGE_4K(size); MemoryBlock::SetRange(start, 0); + const u32 page_count = m_max_size >> 12; + m_pages.SetCount(page_count); + m_locked.SetCount(page_count); + memset(m_pages.GetPtr(), 0, sizeof(u8*) * page_count); + memset(m_locked.GetPtr(), 0, sizeof(u8*) * page_count); + return this; } template void DynamicMemoryBlockBase::Delete() { - m_used_mem.Clear(); + std::lock_guard lock(m_lock); + + m_allocated.Clear(); m_max_size = 0; + m_pages.Clear(); + m_locked.Clear(); + MemoryBlock::Delete(); } template -bool DynamicMemoryBlockBase::Alloc(u64 addr, u32 size) +bool DynamicMemoryBlockBase::AllocFixed(u64 addr, u32 size) { + size = PAGE_4K(size + (addr & 4095)); // align size + + addr &= ~4095; // align start address + if(!IsInMyRange(addr, size)) { assert(0); @@ -78,43 +93,77 @@ bool DynamicMemoryBlockBase::Alloc(u64 addr, u32 size) return false; } - for(u32 i=0; i lock(m_lock); + + for(u32 i=0; i= m_used_mem[i].addr && addr < m_used_mem[i].addr + m_used_mem[i].size) return false; + if(addr >= m_allocated[i].addr && addr < m_allocated[i].addr + m_allocated[i].size) return false; } - AppendUsedMem(addr, size); + AppendMem(addr, size); return true; } template -void DynamicMemoryBlockBase::AppendUsedMem(u64 addr, u32 size) +void DynamicMemoryBlockBase::AppendMem(u64 addr, u32 size) /* private */ { - m_used_mem.Move(new MemBlockInfo(addr, size)); + u8* pointer = (u8*)m_allocated[m_allocated.Move(new MemBlockInfo(addr, size))].mem; + + const u32 first = MemoryBlock::FixAddr(addr) >> 12; + + const u32 last = first + ((size - 1) >> 12); + + for (u32 i = first; i <= last; i++) + { + m_pages[i] = pointer; + m_locked[i] = nullptr; + pointer += 4096; + } } template -u64 DynamicMemoryBlockBase::Alloc(u32 size) +u64 DynamicMemoryBlockBase::AllocAlign(u32 size, u32 align) { - for(u64 addr = MemoryBlock::GetStartAddr(); addr <= MemoryBlock::GetEndAddr() - size;) + size = PAGE_4K(size); + u32 exsize; + + if (align <= 4096) + { + align = 0; + exsize = size; + } + else + { + align &= ~4095; + exsize = size + align - 1; + } + + std::lock_guard lock(m_lock); + + for(u64 addr = MemoryBlock::GetStartAddr(); addr <= MemoryBlock::GetEndAddr() - exsize;) { bool is_good_addr = true; - for(u32 i=0; i= m_used_mem[i].addr && addr < m_used_mem[i].addr + m_used_mem[i].size) || - (m_used_mem[i].addr >= addr && m_used_mem[i].addr < addr + size)) + if((addr >= m_allocated[i].addr && addr < m_allocated[i].addr + m_allocated[i].size) || + (m_allocated[i].addr >= addr && m_allocated[i].addr < addr + exsize)) { is_good_addr = false; - addr = m_used_mem[i].addr + m_used_mem[i].size; + addr = m_allocated[i].addr + m_allocated[i].size; break; } } if(!is_good_addr) continue; - AppendUsedMem(addr, size); + if (align) + { + addr = (addr + (align - 1)) & ~(align - 1); + } + + AppendMem(addr, size); return addr; } @@ -125,18 +174,38 @@ u64 DynamicMemoryBlockBase::Alloc(u32 size) template bool DynamicMemoryBlockBase::Alloc() { - return Alloc(GetSize() - GetUsedSize()) != 0; + return AllocAlign(GetSize() - GetUsedSize()) != 0; } template bool DynamicMemoryBlockBase::Free(u64 addr) -{ - for(u32 i=0; i lock(m_lock); + + for (u32 num = 0; num < m_allocated.GetCount(); num++) { - if(addr == m_used_mem[i].addr) + if (addr == m_allocated[num].addr) { - if(IsLocked(m_used_mem[i].addr)) return false; - m_used_mem.RemoveAt(i); + /* if(IsLocked(m_allocated[num].addr)) return false; */ + + const u32 first = MemoryBlock::FixAddr(addr) >> 12; + + const u32 last = first + ((m_allocated[num].size - 1) >> 12); + + // check if locked: + for (u32 i = first; i <= last; i++) + { + if (!m_pages[i] || m_locked[i]) return false; + } + + // clear pointers: + for (u32 i = first; i <= last; i++) + { + m_pages[i] = nullptr; + m_locked[i] = nullptr; + } + + m_allocated.RemoveAt(num); return true; } } @@ -145,15 +214,15 @@ bool DynamicMemoryBlockBase::Free(u64 addr) } template -u8* DynamicMemoryBlockBase::GetMem(u64 addr) const +u8* DynamicMemoryBlockBase::GetMem(u64 addr) const // lock-free, addr is fixed { - for(u32 i=0; i> 12; - if(addr >= _addr && addr < _addr + m_used_mem[i].size) + if (index < m_pages.GetCount()) + { + if (u8* res = m_pages[index]) { - return (u8*)m_used_mem[i].mem + addr - _addr; + return res + (addr & 4095); } } @@ -163,28 +232,28 @@ u8* DynamicMemoryBlockBase::GetMem(u64 addr) const } template -bool DynamicMemoryBlockBase::IsLocked(const u64 addr) +bool DynamicMemoryBlockBase::IsLocked(u64 addr) // lock-free { - for(u32 i=0; i> 12; + + if (index < m_locked.GetCount()) { - return true; + if (m_locked[index]) return true; } } return false; } -template -void DynamicMemoryBlockBase::AppendLockedMem(u64 addr, u32 size) -{ - m_locked_mem.Move(new MemBlockInfo(addr, size)); -} - template bool DynamicMemoryBlockBase::Lock(u64 addr, u32 size) { + size = PAGE_4K(size); // align size + + addr &= ~4095; // align start address + if(!IsInMyRange(addr, size)) { assert(0); @@ -196,33 +265,58 @@ bool DynamicMemoryBlockBase::Lock(u64 addr, u32 size) return false; } - AppendLockedMem(addr, size); + const u32 first = MemoryBlock::FixAddr(addr) >> 12; + + const u32 last = first + ((size - 1) >> 12); + + for (u32 i = first; i <= last; i++) + { + if (u8* pointer = m_pages[i]) + { + m_locked[i] = pointer; + m_pages[i] = nullptr; + } + else // already locked or empty + { + } + } return true; } template -bool DynamicMemoryBlockBase::Unlock(u64 addr , u32 size) +bool DynamicMemoryBlockBase::Unlock(u64 addr, u32 size) { - for(u32 i=0; i> 12; + + const u32 last = first + ((size - 1) >> 12); + + for (u32 i = first; i <= last; i++) + { + if (u8* pointer = m_locked[i]) + { + m_pages[i] = pointer; + m_locked[i] = nullptr; + } + else // already unlocked or empty { - if(m_locked_mem.Get(i).size > size) - { - m_locked_mem.Get(i).size -= size; - } - else if(m_locked_mem.Get(i).size == size) - { - m_locked_mem.RemoveAt(i); - } - else - { - return false; - } - return true; } } - return false; + return true; } diff --git a/rpcs3/Emu/Memory/Memory.h b/rpcs3/Emu/Memory/Memory.h index 053c0cf38d..b179619fc9 100644 --- a/rpcs3/Emu/Memory/Memory.h +++ b/rpcs3/Emu/Memory/Memory.h @@ -341,7 +341,7 @@ public: u64 Alloc(const u32 size, const u32 align) { - return UserMemory->Alloc(AlignAddr(size, align)); + return UserMemory->AllocAlign(size, align); } bool Free(const u64 addr) diff --git a/rpcs3/Emu/Memory/MemoryBlock.h b/rpcs3/Emu/Memory/MemoryBlock.h index 8fe58bdcfd..2b0d11c04e 100644 --- a/rpcs3/Emu/Memory/MemoryBlock.h +++ b/rpcs3/Emu/Memory/MemoryBlock.h @@ -1,5 +1,7 @@ #pragma once +#define PAGE_4K(x) (x + 4095) & ~(4095) + struct MemInfo { u64 addr; @@ -21,8 +23,8 @@ struct MemBlockInfo : public MemInfo void* mem; MemBlockInfo(u64 _addr, u32 _size) - : MemInfo(_addr, _size) - , mem(malloc(_size)) + : MemInfo(_addr, PAGE_4K(_size)) + , mem(_aligned_malloc(PAGE_4K(_size), 128)) { if(!mem) { @@ -35,7 +37,7 @@ struct MemBlockInfo : public MemInfo ~MemBlockInfo() { - free(mem); + _aligned_free(mem); mem = nullptr; } }; @@ -120,8 +122,8 @@ public: u8* GetMem() const { return mem; } virtual u8* GetMem(u64 addr) const { return mem + addr; } - virtual bool Alloc(u64 addr, u32 size) { return false; } - virtual u64 Alloc(u32 size) { return 0; } + virtual bool AllocFixed(u64 addr, u32 size) { return false; } + virtual u64 AllocAlign(u32 size, u32 align = 1) { return 0; } virtual bool Alloc() { return false; } virtual bool Free(u64 addr) { return false; } virtual bool Lock(u64 addr, u32 size) { return false; } @@ -190,8 +192,11 @@ class NullMemoryBlock : public MemoryBlock template class DynamicMemoryBlockBase : public PT { - Array m_used_mem; - Array m_locked_mem; + mutable std::mutex m_lock; + Array m_allocated; // allocation info + Array m_pages; // real addresses of every 4096 byte pages (array size should be fixed) + Array m_locked; // locked pages should be moved here + u32 m_max_size; public: @@ -209,8 +214,8 @@ public: virtual void Delete(); - virtual bool Alloc(u64 addr, u32 size); - virtual u64 Alloc(u32 size); + virtual bool AllocFixed(u64 addr, u32 size); + virtual u64 AllocAlign(u32 size, u32 align = 1); virtual bool Alloc(); virtual bool Free(u64 addr); virtual bool Lock(u64 addr, u32 size); @@ -219,8 +224,7 @@ public: virtual u8* GetMem(u64 addr) const; private: - void AppendUsedMem(u64 addr, u32 size); - void AppendLockedMem(u64 addr, u32 size); + void AppendMem(u64 addr, u32 size); }; class VirtualMemoryBlock : public MemoryBlock diff --git a/rpcs3/Emu/SysCalls/Modules.cpp b/rpcs3/Emu/SysCalls/Modules.cpp index 4d02cd0dc9..04afe0c9fb 100644 --- a/rpcs3/Emu/SysCalls/Modules.cpp +++ b/rpcs3/Emu/SysCalls/Modules.cpp @@ -204,21 +204,21 @@ void UnloadModules() g_modules_funcs_list.Clear(); } -Module* GetModuleByName(const wxString& name) +Module* GetModuleByName(const std::string& name) { for(u32 i=0; iGetName().Cmp(name) == 0) + if(g_modules[0][i] && g_modules[0][i]->GetName() == name) { return g_modules[0][i]; } - if(g_modules[1][i] && g_modules[1][i]->GetName().Cmp(name) == 0) + if(g_modules[1][i] && g_modules[1][i]->GetName() == name) { return g_modules[1][i]; } - if(g_modules[2][i] && g_modules[2][i]->GetName().Cmp(name) == 0) + if(g_modules[2][i] && g_modules[2][i]->GetName() == name) { return g_modules[2][i]; } @@ -396,12 +396,12 @@ u16 Module::GetID() const return m_id; } -wxString Module::GetName() const +std::string Module::GetName() const { return m_name; } -void Module::SetName(const wxString& name) +void Module::SetName(const std::string& name) { m_name = name; } @@ -412,7 +412,7 @@ void Module::Log(const u32 id, wxString fmt, ...) { va_list list; va_start(list, fmt); - ConLog.Write(GetName() + wxString::Format("[%d]: ", id) + wxString::FormatV(fmt, list)); + ConLog.Write(GetName() + wxString::Format("[%d]: ", id).mb_str() + wxString::FormatV(fmt, list).mb_str()); va_end(list); } } @@ -423,7 +423,7 @@ void Module::Log(wxString fmt, ...) { va_list list; va_start(list, fmt); - ConLog.Write(GetName() + ": " + wxString::FormatV(fmt, list)); + ConLog.Write(GetName() + ": " + wxString::FormatV(fmt, list).mb_str()); va_end(list); } } @@ -432,7 +432,7 @@ void Module::Warning(const u32 id, wxString fmt, ...) { va_list list; va_start(list, fmt); - ConLog.Warning(GetName() + wxString::Format("[%d] warning: ", id) + wxString::FormatV(fmt, list)); + ConLog.Warning(GetName() + wxString::Format("[%d] warning: ", id).mb_str() + wxString::FormatV(fmt, list).mb_str()); va_end(list); } @@ -440,7 +440,7 @@ void Module::Warning(wxString fmt, ...) { va_list list; va_start(list, fmt); - ConLog.Warning(GetName() + " warning: " + wxString::FormatV(fmt, list)); + ConLog.Warning(GetName() + " warning: " + wxString::FormatV(fmt, list).mb_str()); va_end(list); } @@ -448,7 +448,7 @@ void Module::Error(const u32 id, wxString fmt, ...) { va_list list; va_start(list, fmt); - ConLog.Error(GetName() + wxString::Format("[%d] error: ", id) + wxString::FormatV(fmt, list)); + ConLog.Error(GetName() + wxString::Format("[%d] error: ", id).mb_str() + wxString::FormatV(fmt, list).mb_str()); va_end(list); } @@ -456,16 +456,16 @@ void Module::Error(wxString fmt, ...) { va_list list; va_start(list, fmt); - ConLog.Error(GetName() + " error: " + wxString::FormatV(fmt, list)); + ConLog.Error(GetName() + " error: " + wxString::FormatV(fmt, list).mb_str()); va_end(list); } bool Module::CheckID(u32 id) const { - return Emu.GetIdManager().CheckID(id) && !Emu.GetIdManager().GetID(id).m_name.Cmp(GetName()); + return Emu.GetIdManager().CheckID(id) && Emu.GetIdManager().GetID(id).m_name == GetName(); } bool Module::CheckID(u32 id, ID*& _id) const { - return Emu.GetIdManager().CheckID(id) && !(_id = &Emu.GetIdManager().GetID(id))->m_name.Cmp(GetName()); + return Emu.GetIdManager().CheckID(id) && (_id = &Emu.GetIdManager().GetID(id))->m_name == GetName(); } diff --git a/rpcs3/Emu/SysCalls/Modules.h b/rpcs3/Emu/SysCalls/Modules.h index 5b8a497d35..e7e9f28d7f 100644 --- a/rpcs3/Emu/SysCalls/Modules.h +++ b/rpcs3/Emu/SysCalls/Modules.h @@ -27,7 +27,7 @@ struct ModuleFunc class Module { - wxString m_name; + std::string m_name; const u16 m_id; bool m_is_loaded; void (*m_load_func)(); @@ -49,8 +49,8 @@ public: bool IsLoaded() const; u16 GetID() const; - wxString GetName() const; - void SetName(const wxString& name); + std::string GetName() const; + void SetName(const std::string& name); public: void Log(const u32 id, wxString fmt, ...); @@ -95,5 +95,5 @@ bool CallFunc(u32 num); bool UnloadFunc(u32 id); void UnloadModules(); u32 GetFuncNumById(u32 id); -Module* GetModuleByName(const wxString& name); +Module* GetModuleByName(const std::string& name); Module* GetModuleById(u16 id); diff --git a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp index 118abc319a..2cdafe3fe0 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp @@ -83,7 +83,7 @@ struct CellAudioPortConfig struct CellAudioConfig //custom structure { bool m_is_audio_initialized; - bool m_is_audio_port_open; + bool m_is_audio_port_opened; bool m_is_audio_port_started; }; @@ -216,7 +216,7 @@ int cellAudioPortOpen(mem_ptr_t audioParam, mem32_t portNum) cellAudio.Warning("cellAudioPortOpen(audioParam_addr=0x%x,portNum_addr=0x%x)",audioParam.GetAddr(),portNum.GetAddr()); if(!audioParam.IsGood() || !portNum.IsGood()) return CELL_AUDIO_ERROR_PORT_OPEN; - m_config->m_is_audio_port_open = true; + m_config->m_is_audio_port_opened = true; m_param->nChannel = audioParam->nChannel; m_param->nBlock = audioParam->nBlock; @@ -238,13 +238,13 @@ int cellAudioGetPortConfig(u32 portNum, mem_ptr_t portConfi //if(portNum > 7) return CELL_AUDIO_ERROR_PORT_FULL; - if(m_config->m_is_audio_port_open == false) + if(!m_config->m_is_audio_port_opened) { portConfig->status = CELL_AUDIO_STATUS_CLOSE; return CELL_OK; } - if(m_config->m_is_audio_port_started == true) + if(m_config->m_is_audio_port_started) { portConfig->status = CELL_AUDIO_STATUS_RUN; } @@ -268,7 +268,7 @@ int cellAudioPortStart(u32 portNum) { cellAudio.Warning("cellAudioPortStart(portNum=0x%x)",portNum); - if (m_config->m_is_audio_port_open == true) + if (!m_config->m_is_audio_port_opened) return CELL_AUDIO_ERROR_PORT_OPEN; m_config->m_is_audio_port_started = true; @@ -279,10 +279,10 @@ int cellAudioPortClose(u32 portNum) { cellAudio.Warning("cellAudioPortClose(portNum=0x%x)",portNum); - if (m_config->m_is_audio_port_open == false) + if (!m_config->m_is_audio_port_opened) return CELL_AUDIO_ERROR_PORT_NOT_OPEN; - m_config->m_is_audio_port_open = false; + m_config->m_is_audio_port_opened = false; return CELL_OK; } @@ -1016,6 +1016,6 @@ void cellAudio_init() void cellAudio_unload() { m_config->m_is_audio_initialized = false; - m_config->m_is_audio_port_open = false; + m_config->m_is_audio_port_opened = false; m_config->m_is_audio_port_started = false; } \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/cellFont.cpp b/rpcs3/Emu/SysCalls/Modules/cellFont.cpp index d72f8dd943..3dcd9eaed5 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellFont.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellFont.cpp @@ -2,8 +2,7 @@ #include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SC_FUNC.h" #include "cellFont.h" - -//#include "stblib/stb_truetype.h" +#include "stblib/stb_truetype.h" void cellFont_init(); void cellFont_unload(); @@ -91,7 +90,16 @@ struct CellFontConfig struct CellFontRenderer { - u32 SystemReserved_addr; //void *systemReserved[64]; + void *systemReserved[64]; +}; + +//Custom enum to determine the origin of a CellFont object +enum +{ + CELL_FONT_OPEN_FONTSET, + CELL_FONT_OPEN_FONT_FILE, + CELL_FONT_OPEN_FONT_INSTANCE, + CELL_FONT_OPEN_MEMORY, }; struct CellFont @@ -101,7 +109,10 @@ struct CellFont be_t scale_y; be_t slant; be_t renderer_addr; - //stbtt_fontinfo stbfont; + + stbtt_fontinfo stbfont; + be_t fontdata_addr; + be_t origin; }; struct CellFontType @@ -163,6 +174,16 @@ struct CellFontGlyphMetrics } Vertical; }; +struct CellFontImageTransInfo +{ + be_t Image_addr; + be_t imageWidthByte; + be_t imageWidth; + be_t imageHeight; + be_t Surface_addr; + be_t surfWidthByte; +}; + struct CellFontRendererConfig { struct BufferingPolicy @@ -276,10 +297,12 @@ int cellFontOpenFontMemory(mem_ptr_t library, u32 fontAddr, u32 if (!Memory.IsGoodAddr(fontAddr)) return CELL_FONT_ERROR_FONT_OPEN_FAILED; - //stbtt_InitFont(&(font->stbfont), (unsigned char*)Memory.VirtualToRealAddr(fontAddr), 0); - font->renderer_addr = NULL; - //TODO: Write data in font + if (!stbtt_InitFont(&(font->stbfont), (unsigned char*)Memory.VirtualToRealAddr(fontAddr), 0)) + return CELL_FONT_ERROR_FONT_OPEN_FAILED; + font->renderer_addr = NULL; + font->fontdata_addr = fontAddr; + font->origin = CELL_FONT_OPEN_MEMORY; return CELL_FONT_OK; } @@ -293,10 +316,11 @@ int cellFontOpenFontFile(mem_ptr_t library, mem8_ptr_t fontPath return CELL_FONT_ERROR_FONT_OPEN_FAILED; u32 fileSize = f.GetSize(); - MemoryAllocator buffer(fileSize); - f.Read(Memory.VirtualToRealAddr(buffer.GetAddr()), fileSize); - cellFontOpenFontMemory(library.GetAddr(), buffer.GetAddr(), fileSize, subNum, uniqueId, font.GetAddr()); - return CELL_FONT_OK; + u32 bufferAddr = Memory.Alloc(fileSize, 1); // Freed in cellFontCloseFont + f.Read(Memory.VirtualToRealAddr(bufferAddr), fileSize); + int ret = cellFontOpenFontMemory(library.GetAddr(), bufferAddr, fileSize, subNum, uniqueId, font.GetAddr()); + font->origin = CELL_FONT_OPEN_FONT_FILE; + return ret; } int cellFontOpenFontset(mem_ptr_t library, mem_ptr_t fontType, mem_ptr_t font) @@ -379,7 +403,8 @@ int cellFontOpenFontset(mem_ptr_t library, mem_ptr_torigin = CELL_FONT_OPEN_FONTSET; Memory.Free(file_addr); return ret; } @@ -387,6 +412,17 @@ int cellFontOpenFontset(mem_ptr_t library, mem_ptr_t openedFont, mem_ptr_t font) { cellFont.Warning("cellFontOpenFontInstance(openedFont=0x%x, font=0x%x)", openedFont.GetAddr(), font.GetAddr()); + + if (!openedFont.IsGood() || !font.IsGood()) + return CELL_FONT_ERROR_INVALID_PARAMETER; + + font->renderer_addr = openedFont->renderer_addr; + font->scale_x = openedFont->scale_x; + font->scale_y = openedFont->scale_y; + font->slant = openedFont->slant; + font->stbfont = openedFont->stbfont; + font->origin = CELL_FONT_OPEN_FONT_INSTANCE; + return CELL_FONT_OK; } @@ -437,8 +473,9 @@ void cellFontRenderSurfaceSetScissor(mem_ptr_t surface, s int cellFontSetScalePixel(mem_ptr_t font, float w, float h) { - h = GetCurrentPPUThread().FPR[1]; // TODO: Something is wrong with the float arguments - cellFont.Warning("cellFontSetScalePixel(font_addr=0x%x, w=%f, h=%f)", font.GetAddr(), w, h); + w = GetCurrentPPUThread().FPR[1]; // TODO: Something is wrong with the float arguments + h = GetCurrentPPUThread().FPR[2]; // TODO: Something is wrong with the float arguments + cellFont.Log("cellFontSetScalePixel(font_addr=0x%x, w=%f, h=%f)", font.GetAddr(), w, h); if (!font.IsGood()) return CELL_FONT_ERROR_INVALID_PARAMETER; @@ -450,16 +487,19 @@ int cellFontSetScalePixel(mem_ptr_t font, float w, float h) int cellFontGetHorizontalLayout(mem_ptr_t font, mem_ptr_t layout) { - cellFont.Warning("cellFontGetHorizontalLayout(font_addr=0x%x, layout_addr=0x%x)", + cellFont.Log("cellFontGetHorizontalLayout(font_addr=0x%x, layout_addr=0x%x)", font.GetAddr(), layout.GetAddr()); if (!font.IsGood() || !layout.IsGood()) return CELL_FONT_ERROR_INVALID_PARAMETER; - //TODO: This values are (probably) wrong and just for testing purposes! Find the way of calculating them. - layout->baseLineY = font->scale_y - 4; - layout->lineHeight = font->scale_y; - layout->effectHeight = 4; + int ascent, descent, lineGap; + float scale = stbtt_ScaleForPixelHeight(&(font->stbfont), font->scale_y); + stbtt_GetFontVMetrics(&(font->stbfont), &ascent, &descent, &lineGap); + + layout->baseLineY = ascent * scale; + layout->lineHeight = (ascent-descent+lineGap) * scale; + layout->effectHeight = lineGap * scale; return CELL_FONT_OK; } @@ -496,15 +536,74 @@ int cellFontDestroyRenderer() return CELL_FONT_OK; } -int cellFontSetupRenderScalePixel() +int cellFontSetupRenderScalePixel(mem_ptr_t font, float w, float h) { - UNIMPLEMENTED_FUNC(cellFont); + w = GetCurrentPPUThread().FPR[1]; // TODO: Something is wrong with the float arguments + h = GetCurrentPPUThread().FPR[2]; // TODO: Something is wrong with the float arguments + cellFont.Log("cellFontSetupRenderScalePixel(font_addr=0x%x, w=%f, h=%f)", font.GetAddr(), w, h); + + if (!font.IsGood()) + return CELL_FONT_ERROR_INVALID_PARAMETER; + if (!font->renderer_addr) + return CELL_FONT_ERROR_RENDERER_UNBIND; + + // TODO: ? return CELL_FONT_OK; } -int cellFontGetRenderCharGlyphMetrics() +int cellFontGetRenderCharGlyphMetrics(mem_ptr_t font, u32 code, mem_ptr_t metrics) { - UNIMPLEMENTED_FUNC(cellFont); + cellFont.Log("cellFontGetRenderCharGlyphMetrics(font_addr=0x%x, code=0x%x, metrics_addr=0x%x)", + font.GetAddr(), code, metrics.GetAddr()); + + if (!font.IsGood() || !metrics.IsGood()) + return CELL_FONT_ERROR_INVALID_PARAMETER; + if (!font->renderer_addr) + return CELL_FONT_ERROR_RENDERER_UNBIND; + + // TODO: ? + return CELL_FONT_OK; +} + +int cellFontRenderCharGlyphImage(mem_ptr_t font, u32 code, mem_ptr_t surface, float x, float y, mem_ptr_t metrics, mem_ptr_t transInfo) +{ + x = GetCurrentPPUThread().FPR[1]; // TODO: Something is wrong with the float arguments + y = GetCurrentPPUThread().FPR[2]; // TODO: Something is wrong with the float arguments + cellFont.Log("cellFontRenderCharGlyphImage(font_addr=0x%x, code=0x%x, surface_addr=0x%x, x=%f, y=%f, metrics_addr=0x%x, trans_addr=0x%x)", + font.GetAddr(), code, surface.GetAddr(), x, y, metrics.GetAddr(), transInfo.GetAddr()); + + if (!font.IsGood() || !surface.IsGood() || !metrics.IsGood() || !transInfo.IsGood()) + return CELL_FONT_ERROR_INVALID_PARAMETER; + if (!font->renderer_addr) + return CELL_FONT_ERROR_RENDERER_UNBIND; + + // Render the character + int width, height, xoff, yoff; + float scale = stbtt_ScaleForPixelHeight(&(font->stbfont), font->scale_y); + unsigned char* box = stbtt_GetCodepointBitmap(&(font->stbfont), scale, scale, code, &width, &height, &xoff, &yoff); + if (!box) return CELL_OK; + + // Get the baseLineY value + int baseLineY; + int ascent, descent, lineGap; + stbtt_GetFontVMetrics(&(font->stbfont), &ascent, &descent, &lineGap); + baseLineY = ascent * scale; + + // Move the rendered character to the surface + unsigned char* buffer = (unsigned char*)Memory.VirtualToRealAddr(surface->buffer_addr); + for (u32 ypos = 0; ypos < height; ypos++){ + if ((u32)y + ypos + yoff + baseLineY >= surface->height) + break; + + for (u32 xpos = 0; xpos < width; xpos++){ + if ((u32)x + xpos >= surface->width) + break; + + // TODO: There are some oddities in the position of the character in the final buffer + buffer[((int)y + ypos + yoff + baseLineY)*surface->width + (int)x+xpos] = box[ypos*width + xpos]; + } + } + stbtt_FreeBitmap(box, 0); return CELL_FONT_OK; } @@ -516,7 +615,8 @@ int cellFontEndLibrary() int cellFontSetEffectSlant(mem_ptr_t font, float slantParam) { - cellFont.Warning("cellFontSetEffectSlant(font_addr=0x%x, slantParam=%f)", font.GetAddr(), slantParam); + slantParam = GetCurrentPPUThread().FPR[1]; // TODO: Something is wrong with the float arguments + cellFont.Log("cellFontSetEffectSlant(font_addr=0x%x, slantParam=%f)", font.GetAddr(), slantParam); if (!font.IsGood() || slantParam < -1.0 || slantParam > 1.0) return CELL_FONT_ERROR_INVALID_PARAMETER; @@ -532,47 +632,57 @@ int cellFontGetEffectSlant(mem_ptr_t font, mem32_t slantParam) if (!font.IsGood() || !slantParam.IsGood()) return CELL_FONT_ERROR_INVALID_PARAMETER; - slantParam = font->slant; - return CELL_FONT_OK; -} - -int cellFontRenderCharGlyphImage() -{ - UNIMPLEMENTED_FUNC(cellFont); + slantParam = font->slant; //Does this conversion from be_t to *mem32_t work? return CELL_FONT_OK; } int cellFontGetFontIdCode(mem_ptr_t font, u32 code, mem32_t fontId, mem32_t fontCode) { - cellFont.Warning("cellFontGetFontIdCode(font_addr=0x%x, code=0x%x, fontId_addr=0x%x, fontCode_addr=0x%x", + cellFont.Log("cellFontGetFontIdCode(font_addr=0x%x, code=0x%x, fontId_addr=0x%x, fontCode_addr=0x%x", font.GetAddr(), code, fontId.GetAddr(), fontCode.GetAddr()); if (!font.IsGood() || !fontId.IsGood()) //fontCode isn't used return CELL_FONT_ERROR_INVALID_PARAMETER; + // TODO: ? return CELL_FONT_OK; } -int cellFontCloseFont() +int cellFontCloseFont(mem_ptr_t font) { - UNIMPLEMENTED_FUNC(cellFont); + cellFont.Warning("cellFontCloseFont(font_addr=0x%x)", font.GetAddr()); + + if (!font.IsGood()) + return CELL_FONT_ERROR_INVALID_PARAMETER; + + if (font->origin == CELL_FONT_OPEN_FONTSET || + font->origin == CELL_FONT_OPEN_FONT_FILE || + font->origin == CELL_FONT_OPEN_MEMORY) + Memory.Free(font->fontdata_addr); + return CELL_FONT_OK; } int cellFontGetCharGlyphMetrics(mem_ptr_t font, u32 code, mem_ptr_t metrics) { - cellFont.Warning("cellFontGetCharGlyphMetrics(font_addr=0x%x, code=0x%x, metrics_addr=0x%x", + cellFont.Log("cellFontGetCharGlyphMetrics(font_addr=0x%x, code=0x%x, metrics_addr=0x%x", font.GetAddr(), code, metrics.GetAddr()); if (!font.IsGood() || metrics.IsGood()) return CELL_FONT_ERROR_INVALID_PARAMETER; - //TODO: This values are (probably) wrong and just for testing purposes! Find the way of calculating them. - metrics->width = 0; - metrics->height = 0; - metrics->Horizontal.bearingX = 0; + int x0, y0, x1, y1; + int advanceWidth, leftSideBearing; + float scale = stbtt_ScaleForPixelHeight(&(font->stbfont), font->scale_y); + stbtt_GetCodepointBox(&(font->stbfont), code, &x0, &y0, &x1, &y1); + stbtt_GetCodepointHMetrics(&(font->stbfont), code, &advanceWidth, &leftSideBearing); + + // TODO: Add the rest of the information + metrics->width = (x1-x0) * scale; + metrics->height = (y1-y0) * scale; + metrics->Horizontal.bearingX = (float)leftSideBearing * scale; metrics->Horizontal.bearingY = 0; - metrics->Horizontal.advance = 0; + metrics->Horizontal.advance = (float)advanceWidth * scale; metrics->Vertical.bearingX = 0; metrics->Vertical.bearingY = 0; metrics->Vertical.advance = 0; diff --git a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp index e3942e2fa0..99927ac4d1 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellGcmSys.cpp @@ -63,7 +63,7 @@ int cellGcmInit(u32 context_addr, u32 cmdSize, u32 ioSize, u32 ioAddress) { local_size = 0xf900000; //TODO local_addr = Memory.RSXFBMem.GetStartAddr(); - Memory.RSXFBMem.Alloc(local_size); + Memory.RSXFBMem.AllocAlign(local_size); } cellGcmSys.Warning("*** local memory(addr=0x%x, size=0x%x)", local_addr, local_size); @@ -78,7 +78,7 @@ int cellGcmInit(u32 context_addr, u32 cmdSize, u32 ioSize, u32 ioAddress) current_config.coreFrequency = re32(500000000); InitOffsetTable(); - Memory.RSXCMDMem.Alloc(cmdSize); + Memory.RSXCMDMem.AllocAlign(cmdSize); Memory.MemoryBlocks.push_back(Memory.RSXIOMem.SetRange(0x50000000, 0x10000000/*256MB*/));//TODO: implement allocateAdressSpace in memoryBase cellGcmMapEaIoAddress(ioAddress, 0, ioSize); @@ -89,7 +89,7 @@ int cellGcmInit(u32 context_addr, u32 cmdSize, u32 ioSize, u32 ioAddress) current_context.current = current_context.begin; current_context.callback = re32(Emu.GetRSXCallback() - 4); - gcm_info.context_addr = Memory.MainMem.Alloc(0x1000); + gcm_info.context_addr = Memory.MainMem.AllocAlign(0x1000); gcm_info.control_addr = gcm_info.context_addr + 0x40; Memory.WriteData(gcm_info.context_addr, current_context); @@ -167,7 +167,7 @@ int cellGcmSetPrepareFlip(mem_ptr_t ctxt, u32 id) return CELL_GCM_ERROR_FAILURE; } - GSLockCurrent gslock(GS_LOCK_WAIT_FLUSH); + GSLockCurrent gslock(GS_LOCK_WAIT_FLUSH); // could stall on exit u32 current = re(ctxt->current); u32 end = re(ctxt->end); @@ -682,7 +682,7 @@ int32_t cellGcmMapLocalMemory(u64 address, u64 size) { local_size = 0xf900000; //TODO local_addr = Memory.RSXFBMem.GetStartAddr(); - Memory.RSXFBMem.Alloc(local_size); + Memory.RSXFBMem.AllocAlign(local_size); Memory.Write32(address, local_addr); Memory.Write32(size, local_size); } @@ -749,7 +749,7 @@ int32_t cellGcmUnmapEaIoAddress(u64 ea) ea = ea >> 20; io = Memory.Read16(offsetTable.io + (ea*sizeof(u16))); - for(int i=0; i> 20; ea = Memory.Read16(offsetTable.ea + (io*sizeof(u16))); - for(int i=0; i pSelf, u32 pInfo_addr, pInfo->numberOfChannels = pAudio->channels; pInfo->samplingFrequency = CELL_PAMF_FS_48kHz; - if (pAudio->bps = 0x40) + if (pAudio->bps == 0x40) pInfo->bitsPerSample = CELL_PAMF_BIT_LENGTH_16; else //TODO: CELL_PAMF_BIT_LENGTH_24 diff --git a/rpcs3/Emu/SysCalls/Modules/cellSync.cpp b/rpcs3/Emu/SysCalls/Modules/cellSync.cpp index 452f6b1724..4a7cb65e63 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSync.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSync.cpp @@ -4,9 +4,7 @@ #include void cellSync_init(); -void cellSync_unload(); -Module cellSync("cellSync", cellSync_init, nullptr, cellSync_unload); -std::mutex g_SyncMutex; +Module cellSync("cellSync", cellSync_init); // Return Codes enum @@ -56,8 +54,13 @@ int cellSyncMutexInitialize(mem_ptr_t mutex) return CELL_SYNC_ERROR_ALIGN; } - { // global mutex - std::lock_guard lock(g_SyncMutex); //??? + { + SMutexLocker lock(reservation.mutex); + if ((reservation.addr + reservation.size > mutex.GetAddr() && reservation.addr <= mutex.GetAddr() + 4) || + (mutex.GetAddr() + 4 > reservation.addr && mutex.GetAddr() <= reservation.addr + reservation.size)) + { + reservation.clear(); + } mutex->m_data = 0; return CELL_OK; } @@ -77,8 +80,13 @@ int cellSyncMutexLock(mem_ptr_t mutex) } be_t old_order; - { // global mutex - std::lock_guard lock(g_SyncMutex); + { + SMutexLocker lock(reservation.mutex); + if ((reservation.addr + reservation.size > mutex.GetAddr() && reservation.addr <= mutex.GetAddr() + 4) || + (mutex.GetAddr() + 4 > reservation.addr && mutex.GetAddr() <= reservation.addr + reservation.size)) + { + reservation.clear(); + } old_order = mutex->m_order; mutex->m_order = mutex->m_order + 1; if (old_order == mutex->m_freed) @@ -98,7 +106,6 @@ int cellSyncMutexLock(mem_ptr_t mutex) mutex.GetAddr(), (u16)old_order, (u16)mutex->m_order, (u16)mutex->m_freed); } } - //while (_InterlockedExchange((volatile long*)&mutex->m_data, 1)) Sleep(1); _mm_mfence(); return CELL_OK; } @@ -115,8 +122,13 @@ int cellSyncMutexTryLock(mem_ptr_t mutex) { return CELL_SYNC_ERROR_ALIGN; } - { /* global mutex */ - std::lock_guard lock(g_SyncMutex); + { + SMutexLocker lock(reservation.mutex); + if ((reservation.addr + reservation.size > mutex.GetAddr() && reservation.addr <= mutex.GetAddr() + 4) || + (mutex.GetAddr() + 4 > reservation.addr && mutex.GetAddr() <= reservation.addr + reservation.size)) + { + reservation.clear(); + } if (mutex->m_order != mutex->m_freed) { return CELL_SYNC_ERROR_BUSY; @@ -140,7 +152,12 @@ int cellSyncMutexUnlock(mem_ptr_t mutex) } { /* global mutex */ - std::lock_guard lock(g_SyncMutex); + SMutexLocker lock(reservation.mutex); + if ((reservation.addr + reservation.size > mutex.GetAddr() && reservation.addr <= mutex.GetAddr() + 4) || + (mutex.GetAddr() + 4 > reservation.addr && mutex.GetAddr() <= reservation.addr + reservation.size)) + { + reservation.clear(); + } mutex->m_freed = mutex->m_freed + 1; return CELL_OK; } @@ -152,9 +169,4 @@ void cellSync_init() cellSync.AddFunc(0x1bb675c2, cellSyncMutexLock); cellSync.AddFunc(0xd06918c4, cellSyncMutexTryLock); cellSync.AddFunc(0x91f2b7b0, cellSyncMutexUnlock); -} - -void cellSync_unload() -{ - g_SyncMutex.unlock(); } \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp b/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp index 7b1b65c290..4b13707b9a 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellSysutil.cpp @@ -460,6 +460,11 @@ int cellSysutilCheckCallback() cellSysutil.Log("cellSysutilCheckCallback()"); Emu.GetCallbackManager().m_exit_callback.Check(); + CPUThread& thr = Emu.GetCallbackThread(); + + while(Emu.IsRunning() && thr.IsAlive()) + Sleep(1); + return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp index dd43be259e..d87bea125e 100644 --- a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp @@ -164,6 +164,7 @@ void sysPrxForUser_init() //sysPrxForUser.AddFunc(0xaede4b03, sys_heap_free); //sysPrxForUser.AddFunc(0x8a561d92, sys_heap_delete_heap); sysPrxForUser.AddFunc(0xb2fcf2c8, sys_heap_create_heap); + sysPrxForUser.AddFunc(0x44265c08, _sys_heap_memalign); sysPrxForUser.AddFunc(0xb257540b, sys_mmapper_allocate_memory); sysPrxForUser.AddFunc(0xdc578057, sys_mmapper_map_memory); diff --git a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp index a6fa5a6830..2c5ade09ac 100644 --- a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp +++ b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp @@ -176,8 +176,8 @@ void fsAioRead(u32 fd, mem_ptr_t aio, int xid, mem_func_ptr_toffset, buf_addr, (u64)aio->size, res, xid, path.c_str()); @@ -195,6 +195,7 @@ int cellFsAioRead(mem_ptr_t aio, mem32_t aio_id, mem_func_ptr_t bool CheckId(u32 id, T*& data) @@ -105,7 +106,7 @@ public: template<> bool CheckId(u32 id, ID*& _id) { - return Emu.GetIdManager().CheckID(id) && !(_id = &Emu.GetIdManager().GetID(id))->m_name.Cmp(GetName()); + return Emu.GetIdManager().CheckID(id) && (_id = &Emu.GetIdManager().GetID(id))->m_name == GetName(); } template @@ -126,19 +127,22 @@ extern int sys_game_process_exitspawn(u32 path_addr, u32 argv_addr, u32 envp_add u32 data, u32 data_size, int prio, u64 flags ); //sys_event -extern int sys_event_flag_create(u32 eflag_id_addr, u32 attr_addr, u64 init); -extern int sys_event_flag_destroy(u32 eflag_id); -extern int sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, u32 result_addr, u32 timeout); -extern int sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, u32 result_addr); -extern int sys_event_flag_set(u32 eflag_id, u64 bitptn); -extern int sys_event_flag_clear(u32 eflag_id, u64 bitptn); -extern int sys_event_flag_cancel(u32 eflag_id, u32 num_addr); -extern int sys_event_flag_get(u32 eflag_id, u32 flag_addr); -extern int sys_event_queue_create(u32 equeue_id_addr, u32 attr_addr, u64 event_queue_key, int size); +extern int sys_event_queue_create(mem32_t equeue_id, mem_ptr_t attr, u64 event_queue_key, int size); extern int sys_event_queue_receive(u32 equeue_id, u32 event_addr, u32 timeout); extern int sys_event_port_create(u32 eport_id_addr, int port_type, u64 name); extern int sys_event_port_connect_local(u32 event_port_id, u32 event_queue_id); extern int sys_event_port_send(u32 event_port_id, u64 data1, u64 data2, u64 data3); +extern int sys_event_queue_drain(u32 event_queue_id); + +//sys_event_flag +extern int sys_event_flag_create(mem32_t eflag_id, mem_ptr_t attr, u64 init); +extern int sys_event_flag_destroy(u32 eflag_id); +extern int sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result, u32 timeout); +extern int sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result); +extern int sys_event_flag_set(u32 eflag_id, u64 bitptn); +extern int sys_event_flag_clear(u32 eflag_id, u64 bitptn); +extern int sys_event_flag_cancel(u32 eflag_id, mem32_t num); +extern int sys_event_flag_get(u32 eflag_id, mem64_t flags); //sys_semaphore extern int sys_semaphore_create(u32 sem_addr, u32 attr_addr, int initial_val, int max_val); @@ -188,10 +192,9 @@ extern int sys_ppu_thread_get_priority(u32 thread_id, u32 prio_addr); extern int sys_ppu_thread_get_stack_information(u32 info_addr); extern int sys_ppu_thread_stop(u32 thread_id); extern int sys_ppu_thread_restart(u32 thread_id); -extern int sys_ppu_thread_create(u32 thread_id_addr, u32 entry, u32 arg, int prio, u32 stacksize, u64 flags, u32 threadname_addr); +extern int sys_ppu_thread_create(u32 thread_id_addr, u32 entry, u64 arg, int prio, u32 stacksize, u64 flags, u32 threadname_addr); extern void sys_ppu_thread_once(u32 once_ctrl_addr, u32 entry); extern int sys_ppu_thread_get_id(const u32 id_addr); -extern int sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq, u64 req, u32 spup_addr); //memory extern int sys_memory_container_create(u32 cid_addr, u32 yield_size); @@ -225,7 +228,7 @@ extern int cellFsRead(u32 fd, u32 buf_addr, u64 nbytes, mem64_t nread); extern int cellFsWrite(u32 fd, u32 buf_addr, u64 nbytes, mem64_t nwrite); extern int cellFsClose(u32 fd); extern int cellFsOpendir(u32 path_addr, mem32_t fd); -extern int cellFsReaddir(u32 fd, u32 dir_addr, mem64_t nread); +extern int cellFsReaddir(u32 fd, mem_ptr_t dir, mem64_t nread); extern int cellFsClosedir(u32 fd); extern int cellFsStat(u32 path_addr, mem_ptr_t sb); extern int cellFsFstat(u32 fd, mem_ptr_t sb); @@ -293,17 +296,20 @@ extern int sys_tty_write(u32 ch, u64 buf_addr, u32 len, u64 pwritelen_addr); //sys_heap extern int sys_heap_create_heap(const u32 heap_addr, const u32 start_addr, const u32 size); extern int sys_heap_malloc(const u32 heap_addr, const u32 size); +extern int _sys_heap_memalign(u32 heap_id, u32 align, u32 size, u64 p4); //sys_spu extern int sys_spu_image_open(mem_ptr_t img, u32 path_addr); extern int sys_spu_thread_initialize(mem32_t thread, u32 group, u32 spu_num, mem_ptr_t img, mem_ptr_t attr, mem_ptr_t arg); extern int sys_spu_thread_set_argument(u32 id, mem_ptr_t arg); +extern int sys_spu_thread_group_destroy(u32 id); extern int sys_spu_thread_group_start(u32 id); extern int sys_spu_thread_group_suspend(u32 id); extern int sys_spu_thread_group_create(mem32_t id, u32 num, int prio, mem_ptr_t attr); extern int sys_spu_thread_create(mem32_t thread_id, mem32_t entry, u64 arg, int prio, u32 stacksize, u64 flags, u32 threadname_addr); extern int sys_spu_thread_connect_event(u32 id, u32 eq, u32 et, u8 spup); extern int sys_spu_thread_group_join(u32 id, mem32_t cause, mem32_t status); +extern int sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq, u64 req, u32 spup_addr); extern int sys_raw_spu_create(mem32_t id, u32 attr_addr); extern int sys_spu_initialize(u32 max_usable_spu, u32 max_raw_spu); extern int sys_spu_thread_write_ls(u32 id, u32 address, u64 value, u32 type); @@ -312,6 +318,8 @@ extern int sys_spu_thread_write_spu_mb(u32 id, u32 value); extern int sys_spu_thread_set_spu_cfg(u32 id, u64 value); extern int sys_spu_thread_get_spu_cfg(u32 id, mem64_t value); extern int sys_spu_thread_write_snr(u32 id, u32 number, u32 value); +extern int sys_spu_thread_bind_queue(u32 id, u32 spuq, u32 spuq_num); +extern int sys_spu_thread_get_exit_status(u32 id, mem32_t status); //sys_time extern int sys_time_get_timezone(mem32_t timezone, mem32_t summertime); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp index 723fe1bfc2..92ebc62956 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp @@ -27,7 +27,7 @@ struct condition int sys_cond_create(u32 cond_addr, u32 mutex_id, u32 attr_addr) { - sys_cond.Log("sys_cond_create(cond_addr=0x%x, mutex_id=0x%x, attr_addr=%d)", + sys_cond.Warning("sys_cond_create(cond_addr=0x%x, mutex_id=0x%x, attr_addr=%d)", cond_addr, mutex_id, attr_addr); if(!Memory.IsGoodAddr(cond_addr) || !Memory.IsGoodAddr(attr_addr)) return CELL_EFAULT; @@ -68,9 +68,32 @@ int sys_cond_wait(u32 cond_id, u64 timeout) condition* cond_data = nullptr; if(!sys_cond.CheckId(cond_id, cond_data)) return CELL_ESRCH; - cond_data->cond.WaitTimeout(timeout ? timeout : INFINITE); + u32 counter = 0; + const u32 max_counter = timeout ? (timeout / 1000) : 20000; + do + { + if (Emu.IsStopped()) return CELL_ETIMEDOUT; - return CELL_OK; + switch (cond_data->cond.WaitTimeout(1)) + { + case wxCOND_NO_ERROR: return CELL_OK; + case wxCOND_TIMEOUT: break; + default: return CELL_EPERM; + } + + if (counter++ > max_counter) + { + if (!timeout) + { + sys_cond.Warning("sys_cond_wait(cond_id=0x%x, timeout=0x%llx): TIMEOUT", cond_id, timeout); + counter = 0; + } + else + { + return CELL_ETIMEDOUT; + } + } + } while (true); } int sys_cond_signal(u32 cond_id) diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp index 1f1461b86c..357f8cbfcf 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Event.cpp @@ -5,128 +5,25 @@ SysCallBase sys_event("sys_event"); -int sys_event_flag_create(u32 eflag_id_addr, u32 attr_addr, u64 init) -{ - sys_event.Warning("sys_event_flag_create(eflag_id_addr=0x%x, attr_addr=0x%x, init=0x%llx)", eflag_id_addr, attr_addr, init); - - if(!Memory.IsGoodAddr(eflag_id_addr, 4) || !Memory.IsGoodAddr(attr_addr, sizeof(sys_event_flag_attr))) - { - return CELL_EFAULT; - } - - sys_event_flag_attr attr = (sys_event_flag_attr&)Memory[attr_addr]; - attr.protocol = re(attr.protocol); - attr.pshared = re(attr.pshared); - attr.ipc_key = re(attr.ipc_key); - attr.flags = re(attr.flags); - attr.type = re(attr.type); - - sys_event.Warning("name = %s", attr.name); - sys_event.Warning("type = %d", attr.type); - - Memory.Write32(eflag_id_addr, sys_event.GetNewId(new event_flag(init, attr))); - - return CELL_OK; -} - -int sys_event_flag_destroy(u32 eflag_id) -{ - sys_event.Warning("sys_event_flag_destroy(eflag_id=0x%x)", eflag_id); - - if(!sys_event.CheckId(eflag_id)) return CELL_ESRCH; - - Emu.GetIdManager().RemoveID(eflag_id); - - return CELL_OK; -} - -int sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, u32 result_addr, u32 timeout) -{ - sys_event.Warning("Unimplemented function: sys_event_flag_wait(eflag_id=0x%x, bitptn=0x%llx, mode=0x%x, result_addr=0x%x, timeout=0x%x)" - , eflag_id, bitptn, mode, result_addr, timeout); - return CELL_OK; -} - -int sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, u32 result_addr) -{ - sys_event.Warning("Unimplemented function: sys_event_flag_trywait(eflag_id=0x%x, bitptn=0x%llx, mode=0x%x, result_addr=0x%x)" - , eflag_id, bitptn, mode, result_addr); - return CELL_OK; -} - -int sys_event_flag_set(u32 eflag_id, u64 bitptn) -{ - sys_event.Warning("sys_event_flag_set(eflag_id=0x%x, bitptn=0x%llx)", eflag_id, bitptn); - - event_flag* event_flag_data = nullptr; - if(!sys_event.CheckId(eflag_id, event_flag_data)) return CELL_ESRCH; - - event_flag_data->pattern |= bitptn; - - return CELL_OK; -} - -int sys_event_flag_clear(u32 eflag_id, u64 bitptn) -{ - sys_event.Warning("sys_event_flag_clear(eflag_id=0x%x, bitptn=0x%llx)", eflag_id, bitptn); - - event_flag* event_flag_data = nullptr; - if(!sys_event.CheckId(eflag_id, event_flag_data)) return CELL_ESRCH; - - event_flag_data->pattern &= bitptn; - - return CELL_OK; -} - -int sys_event_flag_cancel(u32 eflag_id, u32 num_addr) -{ - sys_event.Warning("Unimplemented function: sys_event_flag_cancel(eflag_id=0x%x, num_addr=0x%x)" - , eflag_id, num_addr); - return CELL_OK; -} - -int sys_event_flag_get(u32 eflag_id, u32 flag_addr) -{ - sys_event.Warning("sys_event_flag_get(eflag_id=0x%x, flag_addr=0x%x)", eflag_id, flag_addr); - - if(!Memory.IsGoodAddr(flag_addr, 4)) - { - return CELL_EFAULT; - } - - event_flag* event_flag_data = nullptr; - if(!sys_event.CheckId(eflag_id, event_flag_data)) return CELL_ESRCH; - - Memory.Write64(flag_addr, event_flag_data->pattern); - - return CELL_OK; -} - //128 -int sys_event_queue_create(u32 equeue_id_addr, u32 attr_addr, u64 event_queue_key, int size) +int sys_event_queue_create(mem32_t equeue_id, mem_ptr_t attr, u64 event_queue_key, int size) { sys_event.Warning("sys_event_queue_create(equeue_id_addr=0x%x, attr_addr=0x%x, event_queue_key=0x%llx, size=%d)", - equeue_id_addr, attr_addr, event_queue_key, size); + equeue_id.GetAddr(), attr.GetAddr(), event_queue_key, size); if(size <= 0 || size > 127) { return CELL_EINVAL; } - if(!Memory.IsGoodAddr(equeue_id_addr, 4) || !Memory.IsGoodAddr(attr_addr, sizeof(sys_event_queue_attr))) + if(!equeue_id.IsGood() || !attr.IsGood()) { return CELL_EFAULT; } - auto& attr = (sys_event_queue_attr&)Memory[attr_addr]; - sys_event.Warning("name = %s", attr.name); - sys_event.Warning("type = %d", re(attr.type)); - EventQueue* equeue = new EventQueue(); - equeue->size = size; - equeue->pos = 0; - equeue->type = re(attr.type); - strncpy(equeue->name, attr.name, 8); - Memory.Write32(equeue_id_addr, sys_event.GetNewId(equeue)); + equeue_id = sys_event.GetNewId(new EventQueue((u32)attr->protocol, (int)attr->type, attr->name_u64, event_queue_key, size)); + sys_event.Warning("*** event_queue created[%s] (protocol=0x%x, type=0x%x): id = %d", + attr->name, (u32)attr->protocol, (int)attr->type, equeue_id.GetValue()); return CELL_OK; } @@ -176,10 +73,10 @@ int sys_event_queue_receive(u32 equeue_id, u32 event_addr, u32 timeout) { auto dst = (sys_event_data&)Memory[event_addr]; - re(dst.source, equeue->ports[i]->name); - re(dst.data1, equeue->ports[i]->data1); - re(dst.data2, equeue->ports[i]->data2); - re(dst.data3, equeue->ports[i]->data3); + dst.source = equeue->ports[i]->name; + dst.data1 = equeue->ports[i]->data1; + dst.data2 = equeue->ports[i]->data2; + dst.data3 = equeue->ports[i]->data3; equeue->ports[i]->has_data = false; @@ -264,5 +161,12 @@ int sys_event_port_send(u32 event_port_id, u64 data1, u64 data2, u64 data3) eport->data2 = data2; eport->data3 = data3; + return CELL_OK; +} + +int sys_event_queue_drain(u32 event_queue_id) +{ + sys_event.Error("sys_event_queue_drain(event_queue_id=0x%x)", event_queue_id); + return CELL_OK; } \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Event_flag.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Event_flag.cpp new file mode 100644 index 0000000000..607d21aae2 --- /dev/null +++ b/rpcs3/Emu/SysCalls/lv2/SC_Event_flag.cpp @@ -0,0 +1,115 @@ +#include "stdafx.h" +#include "Emu/SysCalls/SysCalls.h" +#include "Emu/SysCalls/lv2/SC_Event_flag.h" + +SysCallBase sys_event_flag("sys_event_flag"); + +int sys_event_flag_create(mem32_t eflag_id, mem_ptr_t attr, u64 init) +{ + sys_event_flag.Warning("sys_event_flag_create(eflag_id_addr=0x%x, attr_addr=0x%x, init=0x%llx)", eflag_id.GetAddr(), attr.GetAddr(), init); + + if(!eflag_id.IsGood() || !attr.IsGood()) + { + return CELL_EFAULT; + } + + switch (attr->protocol.ToBE()) + { + case se32(SYS_SYNC_PRIORITY): sys_event_flag.Warning("TODO: SYS_SYNC_PRIORITY attr"); break; + case se32(SYS_SYNC_RETRY): break; + case se32(SYS_SYNC_PRIORITY_INHERIT): sys_event_flag.Warning("TODO: SYS_SYNC_PRIORITY_INHERIT attr"); break; + case se32(SYS_SYNC_FIFO): sys_event_flag.Warning("TODO: SYS_SYNC_FIFO attr"); break; + default: return CELL_EINVAL; + } + + if (attr->pshared.ToBE() != se32(0x200)) + { + return CELL_EINVAL; + } + + switch (attr->type.ToBE()) + { + case se32(SYS_SYNC_WAITER_SINGLE): sys_event_flag.Warning("TODO: SYS_SYNC_WAITER_SINGLE type"); break; + case se32(SYS_SYNC_WAITER_MULTIPLE): sys_event_flag.Warning("TODO: SYS_SYNC_WAITER_MULTIPLE type"); break; + default: return CELL_EINVAL; + } + + eflag_id = sys_event_flag.GetNewId(new event_flag(init, (u32)attr->protocol, (int)attr->type)); + + sys_event_flag.Warning("*** event_flag created[%s] (protocol=%d, type=%d): id = %d", attr->name, (u32)attr->protocol, (int)attr->type, eflag_id.GetValue()); + + return CELL_OK; +} + +int sys_event_flag_destroy(u32 eflag_id) +{ + sys_event_flag.Warning("sys_event_flag_destroy(eflag_id=0x%x)", eflag_id); + + event_flag* ef; + if(!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; + + Emu.GetIdManager().RemoveID(eflag_id); + + return CELL_OK; +} + +int sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result, u32 timeout) +{ + sys_event_flag.Error("sys_event_flag_wait(eflag_id=0x%x, bitptn=0x%llx, mode=0x%x, result_addr=0x%x, timeout=0x%x)", + eflag_id, bitptn, mode, result.GetAddr(), timeout); + return CELL_OK; +} + +int sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result) +{ + sys_event_flag.Error("sys_event_flag_trywait(eflag_id=0x%x, bitptn=0x%llx, mode=0x%x, result_addr=0x%x)", + eflag_id, bitptn, mode, result.GetAddr()); + return CELL_OK; +} + +int sys_event_flag_set(u32 eflag_id, u64 bitptn) +{ + sys_event_flag.Warning("sys_event_flag_set(eflag_id=0x%x, bitptn=0x%llx)", eflag_id, bitptn); + + event_flag* ef; + if(!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; + + ef->flags |= bitptn; + + return CELL_OK; +} + +int sys_event_flag_clear(u32 eflag_id, u64 bitptn) +{ + sys_event_flag.Warning("sys_event_flag_clear(eflag_id=0x%x, bitptn=0x%llx)", eflag_id, bitptn); + + event_flag* ef; + if(!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; + + ef->flags &= bitptn; + + return CELL_OK; +} + +int sys_event_flag_cancel(u32 eflag_id, mem32_t num) +{ + sys_event_flag.Error("sys_event_flag_cancel(eflag_id=0x%x, num_addr=0x%x)", eflag_id, num.GetAddr()); + return CELL_OK; +} + +int sys_event_flag_get(u32 eflag_id, mem64_t flags) +{ + sys_event_flag.Warning("sys_event_flag_get(eflag_id=0x%x, flags_addr=0x%x)", eflag_id, flags.GetAddr()); + + if (!flags.IsGood()) + { + return CELL_EFAULT; + } + + event_flag* ef; + if(!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH; + + flags = ef->flags; + + return CELL_OK; +} \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Event_flag.h b/rpcs3/Emu/SysCalls/lv2/SC_Event_flag.h new file mode 100644 index 0000000000..80983e6a6d --- /dev/null +++ b/rpcs3/Emu/SysCalls/lv2/SC_Event_flag.h @@ -0,0 +1,31 @@ +#pragma once + +enum +{ + SYS_SYNC_WAITER_SINGLE = 0x10000, + SYS_SYNC_WAITER_MULTIPLE = 0x20000, +}; + +struct sys_event_flag_attr +{ + be_t protocol; + be_t pshared; + be_t ipc_key; + be_t flags; + be_t type; + char name[8]; +}; + +struct event_flag +{ + std::atomic flags; + const u32 m_protocol; + const int m_type; + + event_flag(u64 pattern, u32 protocol, int type) + : flags(pattern) + , m_protocol(protocol) + , m_type(type) + { + } +}; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp b/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp index d78c3bc5c7..830f634421 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp @@ -132,21 +132,54 @@ int cellFsClose(u32 fd) int cellFsOpendir(u32 path_addr, mem32_t fd) { const wxString& path = Memory.ReadString(path_addr); - sys_fs.Error("cellFsOpendir(path_addr: 0x%x(%s), fd_addr: 0x%x)", path_addr, path.mb_str(), fd.GetAddr()); - if(!Memory.IsGoodAddr(path_addr) || !fd.IsGood()) return CELL_EFAULT; + sys_fs.Warning("cellFsOpendir(path=\"%s\", fd_addr=0x%x)", path.mb_str(), fd.GetAddr()); + + if(!Memory.IsGoodAddr(path_addr) || !fd.IsGood()) + return CELL_EFAULT; + wxString localPath; + Emu.GetVFS().GetDevice(path, localPath); + vfsLocalDir* dir = new vfsLocalDir(localPath); + if(!dir->Open(localPath)) + return CELL_ENOENT; + + fd = sys_fs.GetNewId(dir); return CELL_OK; } -int cellFsReaddir(u32 fd, u32 dir_addr, mem64_t nread) +int cellFsReaddir(u32 fd, mem_ptr_t dir, mem64_t nread) { - sys_fs.Error("cellFsReaddir(fd: %d, dir_addr: 0x%x, nread_addr: 0x%x)", fd, dir_addr, nread.GetAddr()); + sys_fs.Log("cellFsReaddir(fd=%d, dir_addr=0x%x, nread_addr=0x%x)", fd, dir.GetAddr(), nread.GetAddr()); + + vfsLocalDir* directory; + if(!sys_fs.CheckId(fd, directory)) + return CELL_ESRCH; + if(!dir.IsGood() || !nread.IsGood()) + return CELL_EFAULT; + + const DirEntryInfo* info = directory->Read(); + if(info) + { + nread = 1; + Memory.WriteString(dir.GetAddr()+2, info->name.mb_str()); + dir->d_namlen = info->name.Length(); + dir->d_type = (info->flags & 0x1) ? CELL_FS_TYPE_REGULAR : CELL_FS_TYPE_DIRECTORY; + } + else + { + nread = 0; + } + return CELL_OK; } int cellFsClosedir(u32 fd) { - sys_fs.Error("cellFsClosedir(fd: %d)", fd); + sys_fs.Log("cellFsClosedir(fd=%d)", fd); + + if(!Emu.GetIdManager().RemoveID(fd)) + return CELL_ESRCH; + return CELL_OK; } @@ -231,11 +264,12 @@ int cellFsFstat(u32 fd, mem_ptr_t sb) int cellFsMkdir(u32 path_addr, u32 mode) { const wxString& ps3_path = Memory.ReadString(path_addr); - wxString path; - Emu.GetVFS().GetDevice(ps3_path, path); - sys_fs.Log("cellFsMkdir(path: %s, mode: 0x%x)", path.mb_str(), mode); - if(wxDirExists(path)) return CELL_EEXIST; - if(!wxMkdir(path)) return CELL_EBUSY; + sys_fs.Log("cellFsMkdir(path=\"%s\", mode=0x%x)", ps3_path.mb_str(), mode); + + wxString localPath; + Emu.GetVFS().GetDevice(ps3_path, localPath); + if(wxDirExists(localPath)) return CELL_EEXIST; + if(!wxMkdir(localPath)) return CELL_EBUSY; return CELL_OK; } @@ -248,7 +282,7 @@ int cellFsRename(u32 from_addr, u32 to_addr) Emu.GetVFS().GetDevice(ps3_from, from); Emu.GetVFS().GetDevice(ps3_to, to); - sys_fs.Log("cellFsRename(from: %s, to: %s)", from.mb_str(), to.mb_str()); + sys_fs.Log("cellFsRename(from=\"%s\", to=\"%s\")", ps3_from.mb_str(), ps3_to.mb_str()); if(!wxFileExists(from)) return CELL_ENOENT; if(wxFileExists(to)) return CELL_EEXIST; if(!wxRenameFile(from, to)) return CELL_EBUSY; // (TODO: RenameFile(a,b) = CopyFile(a,b) + RemoveFile(a), therefore file "a" will not be removed if it is opened) @@ -258,20 +292,23 @@ int cellFsRename(u32 from_addr, u32 to_addr) int cellFsRmdir(u32 path_addr) { const wxString& ps3_path = Memory.ReadString(path_addr); - wxString path; - Emu.GetVFS().GetDevice(ps3_path, path); - sys_fs.Log("cellFsRmdir(path: %s)", path.mb_str()); - if(!wxDirExists(path)) return CELL_ENOENT; - if(!wxRmdir(path)) return CELL_EBUSY; // (TODO: Under certain conditions it is not able to delete the folder) + sys_fs.Log("cellFsRmdir(path=\"%s\")", ps3_path.mb_str()); + + wxString localPath; + Emu.GetVFS().GetDevice(ps3_path, localPath); + if(!wxDirExists(localPath)) return CELL_ENOENT; + if(!wxRmdir(localPath)) return CELL_EBUSY; // (TODO: Under certain conditions it is not able to delete the folder) return CELL_OK; } int cellFsUnlink(u32 path_addr) { const wxString& ps3_path = Memory.ReadString(path_addr); - wxString path; - Emu.GetVFS().GetDevice(ps3_path, path); - sys_fs.Error("cellFsUnlink(path: %s)", path.mb_str()); + sys_fs.Warning("cellFsUnlink(path=\"%s\")", ps3_path.mb_str()); + + wxString localPath; + Emu.GetVFS().GetDevice(ps3_path, localPath); + wxRemoveFile(localPath); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp index 707651a5e8..cd29d5e144 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp @@ -7,7 +7,7 @@ extern gcmInfo gcm_info; int cellGcmCallback(u32 context_addr, u32 count) { - GSLockCurrent gslock(GS_LOCK_WAIT_FLUSH); + GSLockCurrent gslock(GS_LOCK_WAIT_FLUSH); // could stall on exit CellGcmContextData& ctx = (CellGcmContextData&)Memory[context_addr]; CellGcmControl& ctrl = (CellGcmControl&)Memory[gcm_info.control_addr]; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Heap.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Heap.cpp index ff80ed4700..eff04fb5cc 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Heap.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Heap.cpp @@ -18,9 +18,12 @@ struct HeapInfo }; int sys_heap_create_heap(const u32 heap_addr, const u32 align, const u32 size) -{ +{ sc_heap.Warning("sys_heap_create_heap(heap_addr=0x%x, align=0x%x, size=0x%x)", heap_addr, align, size); - return sc_heap.GetNewId(new HeapInfo(heap_addr, align, size)); + + u32 heap_id = sc_heap.GetNewId(new HeapInfo(heap_addr, align, size)); + sc_heap.Warning("*** sys_heap created(): id=0x%x", heap_id); + return heap_id; } int sys_heap_malloc(const u32 heap_id, const u32 size) @@ -30,5 +33,15 @@ int sys_heap_malloc(const u32 heap_id, const u32 size) HeapInfo* heap; if(!sc_heap.CheckId(heap_id, heap)) return CELL_ESRCH; - return Memory.Alloc(size, heap->align); + return Memory.Alloc(size, 1); +} + +int _sys_heap_memalign(u32 heap_id, u32 align, u32 size, u64 p4) +{ + sc_heap.Warning("_sys_heap_memalign(heap_id=0x%x, align=0x%x, size=0x%x, p4=0x%llx, ... ???)", heap_id, align, size, p4); + + HeapInfo* heap; + if(!sc_heap.CheckId(heap_id, heap)) return CELL_ESRCH; + + return Memory.Alloc(size, align); } \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.cpp index 2c2cb84322..33da310eea 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.cpp @@ -7,25 +7,16 @@ SysCallBase sys_lwcond("sys_lwcond"); int sys_lwcond_create(mem_ptr_t lwcond, mem_ptr_t lwmutex, mem_ptr_t attr) { - sys_lwcond.Warning("sys_lwcond_create(lwcond_addr=0x%x, lwmutex_addr=0x%x, attr_addr=0x%x)", + sys_lwcond.Log("sys_lwcond_create(lwcond_addr=0x%x, lwmutex_addr=0x%x, attr_addr=0x%x)", lwcond.GetAddr(), lwmutex.GetAddr(), attr.GetAddr()); if (!lwcond.IsGood() || !lwmutex.IsGood() || !attr.IsGood()) return CELL_EFAULT; - u32 protocol = (u32)lwmutex->attribute & SYS_SYNC_ATTR_PROTOCOL_MASK; - switch (protocol) - { - case SYS_SYNC_PRIORITY: break; - case SYS_SYNC_RETRY: sys_lwcond.Error("Invalid SYS_SYNC_RETRY attr"); break; - case SYS_SYNC_PRIORITY_INHERIT: sys_lwcond.Warning("TODO: SYS_SYNC_PRIORITY_INHERIT attr"); break; - case SYS_SYNC_FIFO: break; - default: sys_lwcond.Error("Invalid lwmutex protocol(%d)", protocol); break; - } + lwcond->lwmutex = lwmutex.GetAddr(); + lwcond->lwcond_queue = sys_lwcond.GetNewId(new LWCond(attr->name_u64)); - lwcond->lwmutex_addr = lwmutex.GetAddr(); - lwcond->lwcond_queue = sys_lwcond.GetNewId(new LWCond(protocol, *(u64*)&attr->name)); - - sys_lwcond.Warning("*** lwcond created [%s] (protocol=0x%x): id=%d", attr->name, protocol, (u32)lwcond->lwcond_queue); + sys_lwcond.Warning("*** lwcond created [%s] (attr=0x%x, lwmutex.sq=0x%x): id=0x%x", + attr->name, (u32)lwmutex->attribute, (u32)lwmutex->sleep_queue, (u32)lwcond->lwcond_queue); return CELL_OK; } @@ -44,21 +35,21 @@ int sys_lwcond_destroy(mem_ptr_t lwcond) int sys_lwcond_signal(mem_ptr_t lwcond) { - sys_lwcond.Warning("sys_lwcond_signal(lwcond_addr=0x%x)", lwcond.GetAddr()); + sys_lwcond.Log("sys_lwcond_signal(lwcond_addr=0x%x)", lwcond.GetAddr()); if (!lwcond.IsGood()) return CELL_EFAULT; LWCond* lwc; u32 id = (u32)lwcond->lwcond_queue; if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH; - lwc->signal(); + lwc->signal(mem_ptr_t(lwcond->lwmutex)->attribute); return CELL_OK; } int sys_lwcond_signal_all(mem_ptr_t lwcond) { - sys_lwcond.Warning("sys_lwcond_signal_all(lwcond_addr=0x%x)", lwcond.GetAddr()); + sys_lwcond.Log("sys_lwcond_signal_all(lwcond_addr=0x%x)", lwcond.GetAddr()); if (!lwcond.IsGood()) return CELL_EFAULT; LWCond* lwc; @@ -72,7 +63,7 @@ int sys_lwcond_signal_all(mem_ptr_t lwcond) int sys_lwcond_signal_to(mem_ptr_t lwcond, u32 ppu_thread_id) { - sys_lwcond.Warning("sys_lwcond_signal_to(lwcond_addr=0x%x, ppu_thread_id=%d)", lwcond.GetAddr(), ppu_thread_id); + sys_lwcond.Log("sys_lwcond_signal_to(lwcond_addr=0x%x, ppu_thread_id=%d)", lwcond.GetAddr(), ppu_thread_id); if (!lwcond.IsGood()) return CELL_EFAULT; LWCond* lwc; @@ -86,16 +77,17 @@ int sys_lwcond_signal_to(mem_ptr_t lwcond, u32 ppu_thread_id) int sys_lwcond_wait(mem_ptr_t lwcond, u64 timeout) { - sys_lwcond.Warning("sys_lwcond_wait(lwcond_addr=0x%x, timeout=%llu)", lwcond.GetAddr(), timeout); + sys_lwcond.Log("sys_lwcond_wait(lwcond_addr=0x%x, timeout=%llu)", lwcond.GetAddr(), timeout); if (!lwcond.IsGood()) return CELL_EFAULT; LWCond* lwc; u32 id = (u32)lwcond->lwcond_queue; if (!sys_lwcond.CheckId(id, lwc)) return CELL_ESRCH; const u32 tid = GetCurrentPPUThread().GetId(); - mem_ptr_t lwmutex((u32)lwcond->lwmutex_addr); - if ((u32)lwmutex->owner != tid) return CELL_EPERM; // caller must own this lwmutex + mem_ptr_t lwmutex(lwcond->lwmutex); + + if ((u32)lwmutex->owner.GetOwner() != tid) return CELL_EPERM; // caller must own this lwmutex lwc->begin_waiting(tid); u32 counter = 0; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.h b/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.h index c72347e3b3..037f47f67b 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.h +++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.h @@ -2,12 +2,16 @@ struct sys_lwcond_attribute_t { - char name[8]; + union + { + char name[8]; + u64 name_u64; + }; }; struct sys_lwcond_t { - be_t lwmutex_addr; + be_t lwmutex; be_t lwcond_queue; }; @@ -18,22 +22,20 @@ struct LWCond std::mutex m_lock; Array waiters; // list of waiting threads Array signaled; // list of signaled threads - u32 m_protocol; // protocol u64 m_name; // not used - LWCond(u32 prot, u64 name) + LWCond(u64 name) : m_name(name) - , m_protocol(prot) { } - void signal() + void signal(u32 _protocol) { std::lock_guard lock(m_lock); if (waiters.GetCount()) { - if (m_protocol == SYS_SYNC_PRIORITY) + if ((_protocol & SYS_SYNC_ATTR_PROTOCOL_MASK) == SYS_SYNC_PRIORITY) { u64 max_prio = 0; u32 sel = 0; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp index 76c4f87cbb..386e99f5f9 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp @@ -5,8 +5,6 @@ SysCallBase sc_lwmutex("sys_lwmutex"); -std::mutex g_lwmutex; - int sys_lwmutex_create(mem_ptr_t lwmutex, mem_ptr_t attr) { sc_lwmutex.Log("sys_lwmutex_create(lwmutex_addr=0x%x, lwmutex_attr_addr=0x%x)", @@ -14,29 +12,32 @@ int sys_lwmutex_create(mem_ptr_t lwmutex, mem_ptr_tattr_recursive) + switch (attr->attr_recursive.ToBE()) { - case SYS_SYNC_RECURSIVE: break; - case SYS_SYNC_NOT_RECURSIVE: break; - default: return CELL_EINVAL; + case se32(SYS_SYNC_RECURSIVE): break; + case se32(SYS_SYNC_NOT_RECURSIVE): break; + default: sc_lwmutex.Error("Unknown 0x%x recursive attr", (u32)attr->attr_recursive); return CELL_EINVAL; } - switch ((u32)attr->attr_protocol) + switch (attr->attr_protocol.ToBE()) { - case SYS_SYNC_PRIORITY: sc_lwmutex.Warning("TODO: SYS_SYNC_PRIORITY attr"); break; - case SYS_SYNC_RETRY: sc_lwmutex.Warning("TODO: SYS_SYNC_RETRY attr"); break; - case SYS_SYNC_PRIORITY_INHERIT: sc_lwmutex.Warning("TODO: SYS_SYNC_PRIORITY_INHERIT attr"); break; - case SYS_SYNC_FIFO: sc_lwmutex.Warning("TODO: SYS_SYNC_FIFO attr"); break; - default: return CELL_EINVAL; + case se32(SYS_SYNC_PRIORITY): break; + case se32(SYS_SYNC_RETRY): break; + case se32(SYS_SYNC_PRIORITY_INHERIT): sc_lwmutex.Error("Invalid SYS_SYNC_PRIORITY_INHERIT protocol attr"); return CELL_EINVAL; + case se32(SYS_SYNC_FIFO): break; + default: sc_lwmutex.Error("Unknown 0x%x protocol attr", (u32)attr->attr_protocol); return CELL_EINVAL; } - lwmutex->attribute = (u32)attr->attr_protocol | (u32)attr->attr_recursive; + lwmutex->attribute = attr->attr_protocol | attr->attr_recursive; lwmutex->all_info = 0; lwmutex->pad = 0; lwmutex->recursive_count = 0; - lwmutex->sleep_queue = 0; - sc_lwmutex.Warning("*** lwmutex created [%s] (attribute=0x%x): id=???", attr->name, (u32)lwmutex->attribute); + u32 sq_id = sc_lwmutex.GetNewId(new SleepQueue(attr->name_u64)); + lwmutex->sleep_queue = sq_id; + + sc_lwmutex.Log("*** lwmutex created [%s] (attribute=0x%x): sleep_queue=0x%x", + attr->name, (u32)lwmutex->attribute, sq_id); return CELL_OK; } @@ -47,23 +48,18 @@ int sys_lwmutex_destroy(mem_ptr_t lwmutex) if (!lwmutex.IsGood()) return CELL_EFAULT; - if (!lwmutex->attribute) return CELL_EINVAL; + u32 sq_id = lwmutex->sleep_queue; + if (!Emu.GetIdManager().CheckID(sq_id)) return CELL_ESRCH; - { // global lock - std::lock_guard lock(g_lwmutex); - - if (!lwmutex->owner) - { - lwmutex->owner = ~0; // make it unable to lock - lwmutex->attribute = 0; - } - else - { - return CELL_EBUSY; - } + // try to make it unable to lock + switch (int res = lwmutex->trylock(~0)) + { + case CELL_OK: + lwmutex->attribute = 0; + lwmutex->sleep_queue = 0; + Emu.GetIdManager().RemoveID(sq_id); + default: return res; } - - return CELL_OK; } int sys_lwmutex_lock(mem_ptr_t lwmutex, u64 timeout) @@ -72,37 +68,7 @@ int sys_lwmutex_lock(mem_ptr_t lwmutex, u64 timeout) if (!lwmutex.IsGood()) return CELL_EFAULT; - if (!lwmutex->attribute) return CELL_EINVAL; - - const u32 tid = GetCurrentPPUThread().GetId(); - - int res = lwmutex->trylock(tid); - if (res != CELL_EBUSY) return res; - - u32 counter = 0; - const u32 max_counter = timeout ? (timeout / 1000) : 20000; - do // waiting - { - if (Emu.IsStopped()) return CELL_ETIMEDOUT; - Sleep(1); - - res = lwmutex->trylock(tid); - if (res != CELL_EBUSY) return res; - if (!lwmutex->attribute) return CELL_EINVAL; - - if (counter++ > max_counter) - { - if (!timeout) - { - sc_lwmutex.Warning("sys_lwmutex_lock(lwmutex_addr=0x%x): TIMEOUT", lwmutex.GetAddr()); - counter = 0; - } - else - { - return CELL_ETIMEDOUT; - } - } - } while (true); + return lwmutex->lock(GetCurrentPPUThread().GetId(), timeout ? ((timeout < 1000) ? 1 : (timeout / 1000)) : 0); } int sys_lwmutex_trylock(mem_ptr_t lwmutex) @@ -111,8 +77,6 @@ int sys_lwmutex_trylock(mem_ptr_t lwmutex) if (!lwmutex.IsGood()) return CELL_EFAULT; - if (!lwmutex->attribute) return CELL_EINVAL; - return lwmutex->trylock(GetCurrentPPUThread().GetId()); } @@ -122,8 +86,6 @@ int sys_lwmutex_unlock(mem_ptr_t lwmutex) if (!lwmutex.IsGood()) return CELL_EFAULT; - if (!lwmutex->unlock(GetCurrentPPUThread().GetId())) return CELL_EPERM; - - return CELL_OK; + return lwmutex->unlock(GetCurrentPPUThread().GetId()); } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h index 1072d7eca5..7275724b08 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h +++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h @@ -1,4 +1,5 @@ #pragma once +#include // attr_protocol (waiting scheduling policy) enum @@ -9,7 +10,7 @@ enum SYS_SYNC_PRIORITY = 2, // Basic Priority Inheritance Protocol SYS_SYNC_PRIORITY_INHERIT = 3, - // ???? + // Not selected while unlocking SYS_SYNC_RETRY = 4, // SYS_SYNC_ATTR_PROTOCOL_MASK = 0xF, @@ -30,10 +31,104 @@ struct sys_lwmutex_attribute_t { be_t attr_protocol; be_t attr_recursive; - char name[8]; + union + { + char name[8]; + u64 name_u64; + }; }; -extern std::mutex g_lwmutex; +class SleepQueue +{ + /* struct q_rec + { + u32 tid; + u64 prio; + q_rec(u32 tid, u64 prio): tid(tid), prio(prio) {} + }; */ + + SMutex m_mutex; + Array list; + u64 m_name; + +public: + SleepQueue(u64 name) + : m_name(name) + { + } + + void push(u32 tid) + { + SMutexLocker lock(m_mutex); + list.AddCpy(tid); + } + + u32 pop() // SYS_SYNC_FIFO + { + SMutexLocker lock(m_mutex); + + while (true) + { + if (list.GetCount()) + { + u32 res = list[0]; + list.RemoveAt(0); + if (Emu.GetIdManager().CheckID(res)) + // check thread + { + return res; + } + } + return 0; + }; + } + + u32 pop_prio() // SYS_SYNC_PRIORITY + { + SMutexLocker lock(m_mutex); + + while (true) + { + if (list.GetCount()) + { + u64 max_prio = 0; + u32 sel = 0; + for (u32 i = 0; i < list.GetCount(); i++) + { + CPUThread* t = Emu.GetCPU().GetThread(list[i]); + if (!t) + { + list[i] = 0; + sel = i; + break; + } + + u64 prio = t->GetPrio(); + if (prio > max_prio) + { + max_prio = prio; + sel = i; + } + } + u32 res = list[sel]; + list.RemoveAt(sel); + /* if (Emu.GetIdManager().CheckID(res)) */ + if (res) + // check thread + { + return res; + } + } + return 0; + } + } + + u32 pop_prio_inherit() // (TODO) + { + ConLog.Error("TODO: SleepQueue::pop_prio_inherit()"); + Emu.Pause(); + } +}; struct sys_lwmutex_t { @@ -41,8 +136,8 @@ struct sys_lwmutex_t { struct // sys_lwmutex_lock_info_t { - /* volatile */ be_t owner; - /* volatile */ be_t waiter; + /* volatile */ SMutexBE owner; + /* volatile */ be_t waiter; // not used }; struct { @@ -54,77 +149,103 @@ struct sys_lwmutex_t be_t sleep_queue; be_t pad; - int trylock(u32 tid) + int trylock(be_t tid) { - std::lock_guard lock(g_lwmutex); // global lock + if (!attribute.ToBE()) return CELL_EINVAL; - if ((u32)attribute & SYS_SYNC_RECURSIVE) + if (tid == owner.GetOwner()) { - if (tid == (u32)owner) + if (attribute.ToBE() & se32(SYS_SYNC_RECURSIVE)) { - recursive_count = (u32)recursive_count + 1; - if ((u32)recursive_count == 0xffffffff) return CELL_EKRESOURCE; + recursive_count += 1; + if (!recursive_count.ToBE()) return CELL_EKRESOURCE; return CELL_OK; } - } - else // recursive not allowed - { - if (tid == (u32)owner) + else { return CELL_EDEADLK; } } - if (!(u32)owner) // try lock + switch (owner.trylock(tid)) { - owner = tid; - recursive_count = 1; - return CELL_OK; - } - else - { - return CELL_EBUSY; + case SMR_OK: recursive_count = 1; return CELL_OK; + case SMR_FAILED: return CELL_EBUSY; + default: return CELL_EINVAL; } } - bool unlock(u32 tid) + int unlock(be_t tid) { - std::lock_guard lock(g_lwmutex); // global lock - - if (tid != (u32)owner) + if (tid != owner.GetOwner()) { - return false; + return CELL_EPERM; } else { - recursive_count = (u32)recursive_count - 1; - if (!(u32)recursive_count) + recursive_count -= 1; + if (!recursive_count.ToBE()) { - waiter = 0; // not used yet - owner = 0; // release + be_t target = 0; + switch (attribute.ToBE() & se32(SYS_SYNC_ATTR_PROTOCOL_MASK)) + { + case se32(SYS_SYNC_FIFO): + case se32(SYS_SYNC_PRIORITY): + SleepQueue* sq; + if (!Emu.GetIdManager().GetIDData(sleep_queue, sq)) return CELL_ESRCH; + target = attribute.ToBE() & se32(SYS_SYNC_FIFO) ? sq->pop() : sq->pop_prio(); + case se32(SYS_SYNC_RETRY): default: owner.unlock(tid, target); break; + } } - return true; + return CELL_OK; + } + } + + int lock(be_t tid, u64 timeout) + { + switch (int res = trylock(tid)) + { + case CELL_EBUSY: break; + default: return res; + } + + switch (attribute.ToBE() & se32(SYS_SYNC_ATTR_PROTOCOL_MASK)) + { + case se32(SYS_SYNC_PRIORITY): + case se32(SYS_SYNC_FIFO): + SleepQueue* sq; + if (!Emu.GetIdManager().GetIDData(sleep_queue, sq)) return CELL_ESRCH; + sq->push(tid); + default: break; + } + + switch (owner.lock(tid, timeout)) + { + case SMR_OK: case SMR_SIGNAL: recursive_count = 1; return CELL_OK; + case SMR_TIMEOUT: return CELL_ETIMEDOUT; + default: return CELL_EINVAL; } } }; -struct lwmutex_locker +class lwmutex_locker { -private: mem_ptr_t m_mutex; - u32 m_id; -public: - const int res; + be_t m_id; - lwmutex_locker(u32 lwmutex_addr, u32 tid) + lwmutex_locker(mem_ptr_t lwmutex, be_t tid, u64 timeout = 0) : m_id(tid) - , m_mutex(lwmutex_addr) - , res(m_mutex->trylock(m_id)) + , m_mutex(lwmutex) { + if (int res = m_mutex->lock(m_id, timeout)) + { + ConLog.Error("lwmutex_locker: m_mutex->lock failed(res=0x%x)", res); + Emu.Pause(); + } } ~lwmutex_locker() { - if (res == CELL_OK) m_mutex->unlock(m_id); + m_mutex->unlock(m_id); } }; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp index 86f24d65dd..526e1d56ab 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Memory.cpp @@ -6,7 +6,7 @@ SysCallBase sc_mem("memory"); int sys_memory_container_create(u32 cid_addr, u32 yield_size) { - sc_mem.Warning("(HACK!) sys_memory_container_create(cid_addr=0x%x,yield_size=0x%x)", cid_addr, yield_size); + sc_mem.Warning("sys_memory_container_create(cid_addr=0x%x,yield_size=0x%x)", cid_addr, yield_size); if(!Memory.IsGoodAddr(cid_addr, 4)) { @@ -15,17 +15,13 @@ int sys_memory_container_create(u32 cid_addr, u32 yield_size) yield_size &= ~0xfffff; //round down to 1 MB granularity - //alignment hack (Memory.Alloc does not support alignment yet): alloc size is increased - u64 addr = Memory.Alloc(yield_size + 0x100000, 0x100000); //1 MB alignment (???) + u64 addr = Memory.Alloc(yield_size, 0x100000); //1 MB alignment if(!addr) { return CELL_ENOMEM; } - //fix alignment: - addr = (addr + 0x100000) & ~0xfffff; - Memory.Write32(cid_addr, sc_mem.GetNewId(new MemoryContainerInfo(addr, yield_size))); return CELL_OK; } @@ -49,20 +45,18 @@ int sys_memory_container_destroy(u32 cid) int sys_memory_allocate(u32 size, u32 flags, u32 alloc_addr_addr) { //0x30000100; - sc_mem.Warning("(HACK!) sys_memory_allocate(size=0x%x, flags=0x%x)", size, flags); + sc_mem.Log("sys_memory_allocate(size=0x%x, flags=0x%x)", size, flags); u32 addr; switch(flags) { case SYS_MEMORY_PAGE_SIZE_1M: if(size & 0xfffff) return CELL_EALIGN; - addr = Memory.Alloc(size + 0x100000, 0x100000); - addr = (addr + 0x100000) & ~0xfffff; + addr = Memory.Alloc(size, 1); break; case SYS_MEMORY_PAGE_SIZE_64K: if(size & 0xffff) return CELL_EALIGN; - addr = Memory.Alloc(size + 0x10000, 0x10000); - addr = (addr + 0x10000) & ~0xffff; + addr = Memory.Alloc(size, 1); break; default: return CELL_EINVAL; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp index b700ec7ae3..2bd289413a 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Mutex.cpp @@ -1,12 +1,13 @@ #include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" #include "SC_Mutex.h" +#include "Utilities/SMutex.h" SysCallBase sys_mtx("sys_mutex"); int sys_mutex_create(u32 mutex_id_addr, u32 attr_addr) { - sys_mtx.Log("sys_mutex_create(mutex_id_addr=0x%x, attr_addr=0x%x)", + sys_mtx.Warning("sys_mutex_create(mutex_id_addr=0x%x, attr_addr=0x%x)", mutex_id_addr, attr_addr); if(!Memory.IsGoodAddr(mutex_id_addr) || !Memory.IsGoodAddr(attr_addr)) return CELL_EFAULT; @@ -48,9 +49,28 @@ int sys_mutex_lock(u32 mutex_id, u64 timeout) mutex* mtx_data = nullptr; if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH; - mtx_data->mtx.Lock(); + u32 counter = 0; + const u32 max_counter = timeout ? (timeout / 1000) : 20000; + do + { + if (Emu.IsStopped()) return CELL_ETIMEDOUT; - return CELL_OK; + if (mtx_data->mtx.TryLock() == wxMUTEX_NO_ERROR) return CELL_OK; + Sleep(1); + + if (counter++ > max_counter) + { + if (!timeout) + { + sys_mtx.Warning("sys_mutex_lock(mutex_id=0x%x, timeout=0x%llx): TIMEOUT", mutex_id, timeout); + counter = 0; + } + else + { + return CELL_ETIMEDOUT; + } + } + } while (true); } int sys_mutex_trylock(u32 mutex_id) @@ -60,7 +80,7 @@ int sys_mutex_trylock(u32 mutex_id) mutex* mtx_data = nullptr; if(!sys_mtx.CheckId(mutex_id, mtx_data)) return CELL_ESRCH; - if(mtx_data->mtx.TryLock()) return 1; + if (mtx_data->mtx.TryLock() != wxMUTEX_NO_ERROR) return CELL_EBUSY; return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp index 9e8d8f8f70..9ab7bee427 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_PPU_Thread.cpp @@ -131,7 +131,7 @@ int sys_ppu_thread_restart(u32 thread_id) return CELL_OK; } -int sys_ppu_thread_create(u32 thread_id_addr, u32 entry, u32 arg, int prio, u32 stacksize, u64 flags, u32 threadname_addr) +int sys_ppu_thread_create(u32 thread_id_addr, u32 entry, u64 arg, int prio, u32 stacksize, u64 flags, u32 threadname_addr) { sysPrxForUser.Log("sys_ppu_thread_create(thread_id_addr=0x%x, entry=0x%x, arg=0x%x, prio=%d, stacksize=0x%x, flags=0x%llx, threadname_addr=0x%x('%s'))", thread_id_addr, entry, arg, prio, stacksize, flags, threadname_addr, Memory.ReadString(threadname_addr).mb_str()); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp index 0fd91cbe8e..7e4ddd320c 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Process.cpp @@ -40,11 +40,8 @@ int sys_process_getppid() int sys_process_exit(int errorcode) { sc_p.Warning("sys_process_exit(%d)", errorcode); -#ifdef _DEBUG - Emu.Pause(); -#else - Emu.Stop(); -#endif + Emu.Pause(); // Emu.Stop() does crash + ConLog.Success("Process finished"); return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Rwlock.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Rwlock.cpp index d4f6ecae7a..d07ab05677 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Rwlock.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_Rwlock.cpp @@ -10,23 +10,22 @@ int sys_rwlock_create(mem32_t rw_lock_id, mem_ptr_t attr if (!rw_lock_id.IsGood() || !attr.IsGood()) return CELL_EFAULT; - switch ((u32)attr->attr_protocol) + switch (attr->attr_protocol.ToBE()) { - case SYS_SYNC_PRIORITY: sys_rwlock.Warning("TODO: SYS_SYNC_PRIORITY attr"); break; - case SYS_SYNC_RETRY: sys_rwlock.Error("Invalid SYS_SYNC_RETRY attr"); break; - case SYS_SYNC_PRIORITY_INHERIT: sys_rwlock.Warning("TODO: SYS_SYNC_PRIORITY_INHERIT attr"); break; - case SYS_SYNC_FIFO: break; + case se32(SYS_SYNC_PRIORITY): sys_rwlock.Warning("TODO: SYS_SYNC_PRIORITY attr"); break; + case se32(SYS_SYNC_RETRY): sys_rwlock.Error("Invalid SYS_SYNC_RETRY attr"); break; + case se32(SYS_SYNC_PRIORITY_INHERIT): sys_rwlock.Warning("TODO: SYS_SYNC_PRIORITY_INHERIT attr"); break; + case se32(SYS_SYNC_FIFO): break; default: return CELL_EINVAL; } - if ((u32)attr->attr_pshared != 0x200) + if (attr->attr_pshared.ToBE() != se32(0x200)) { sys_rwlock.Error("Invalid attr_pshared(0x%x)", (u32)attr->attr_pshared); return CELL_EINVAL; } - rw_lock_id = sys_rwlock.GetNewId(new RWLock((u32)attr->attr_protocol, (u32)attr->attr_pshared, - (u64)attr->key, (s32)attr->flags, *(u64*)&attr->name)); + rw_lock_id = sys_rwlock.GetNewId(new RWLock((u32)attr->attr_protocol, attr->name_u64)); sys_rwlock.Warning("*** rwlock created [%s] (protocol=0x%x): id=%d", attr->name, (u32)attr->attr_protocol, rw_lock_id.GetValue()); @@ -52,7 +51,7 @@ int sys_rwlock_destroy(u32 rw_lock_id) int sys_rwlock_rlock(u32 rw_lock_id, u64 timeout) { - sys_rwlock.Warning("sys_rwlock_rlock(rw_lock_id=%d, timeout=%llu)", rw_lock_id, timeout); + sys_rwlock.Log("sys_rwlock_rlock(rw_lock_id=%d, timeout=%llu)", rw_lock_id, timeout); RWLock* rw; if (!sys_rwlock.CheckId(rw_lock_id, rw)) return CELL_ESRCH; @@ -86,7 +85,7 @@ int sys_rwlock_rlock(u32 rw_lock_id, u64 timeout) int sys_rwlock_tryrlock(u32 rw_lock_id) { - sys_rwlock.Warning("sys_rwlock_tryrlock(rw_lock_id=%d)", rw_lock_id); + sys_rwlock.Log("sys_rwlock_tryrlock(rw_lock_id=%d)", rw_lock_id); RWLock* rw; if (!sys_rwlock.CheckId(rw_lock_id, rw)) return CELL_ESRCH; @@ -98,7 +97,7 @@ int sys_rwlock_tryrlock(u32 rw_lock_id) int sys_rwlock_runlock(u32 rw_lock_id) { - sys_rwlock.Warning("sys_rwlock_runlock(rw_lock_id=%d)", rw_lock_id); + sys_rwlock.Log("sys_rwlock_runlock(rw_lock_id=%d)", rw_lock_id); RWLock* rw; if (!sys_rwlock.CheckId(rw_lock_id, rw)) return CELL_ESRCH; @@ -110,7 +109,7 @@ int sys_rwlock_runlock(u32 rw_lock_id) int sys_rwlock_wlock(u32 rw_lock_id, u64 timeout) { - sys_rwlock.Warning("sys_rwlock_wlock(rw_lock_id=%d, timeout=%llu)", rw_lock_id, timeout); + sys_rwlock.Log("sys_rwlock_wlock(rw_lock_id=%d, timeout=%llu)", rw_lock_id, timeout); RWLock* rw; if (!sys_rwlock.CheckId(rw_lock_id, rw)) return CELL_ESRCH; @@ -146,7 +145,7 @@ int sys_rwlock_wlock(u32 rw_lock_id, u64 timeout) int sys_rwlock_trywlock(u32 rw_lock_id) { - sys_rwlock.Warning("sys_rwlock_trywlock(rw_lock_id=%d)", rw_lock_id); + sys_rwlock.Log("sys_rwlock_trywlock(rw_lock_id=%d)", rw_lock_id); RWLock* rw; if (!sys_rwlock.CheckId(rw_lock_id, rw)) return CELL_ESRCH; @@ -161,7 +160,7 @@ int sys_rwlock_trywlock(u32 rw_lock_id) int sys_rwlock_wunlock(u32 rw_lock_id) { - sys_rwlock.Warning("sys_rwlock_wunlock(rw_lock_id=%d)", rw_lock_id); + sys_rwlock.Log("sys_rwlock_wunlock(rw_lock_id=%d)", rw_lock_id); RWLock* rw; if (!sys_rwlock.CheckId(rw_lock_id, rw)) return CELL_ESRCH; diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Rwlock.h b/rpcs3/Emu/SysCalls/lv2/SC_Rwlock.h index 720f137db7..dc9c563b6d 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_Rwlock.h +++ b/rpcs3/Emu/SysCalls/lv2/SC_Rwlock.h @@ -6,8 +6,12 @@ struct sys_rwlock_attribute_t be_t attr_pshared; // == 0x200 (NOT SHARED) be_t key; // process-shared key (not used) be_t flags; // process-shared flags (not used) - be_t pad; - char name[8]; + be_t pad; // not used + union + { + char name[8]; + u64 name_u64; + }; }; #pragma pack() @@ -20,21 +24,15 @@ struct RWLock Array rlock_list; // read lock list u32 m_protocol; // TODO - u32 m_pshared; // not used - u64 m_key; // not used - s32 m_flags; // not used union { - u64 m_name_data; // not used + u64 m_name_u64; char m_name[8]; }; - RWLock(u32 protocol, u32 pshared, u64 key, s32 flags, u64 name) + RWLock(u32 protocol, u64 name) : m_protocol(protocol) - , m_pshared(pshared) - , m_key(key) - , m_flags(flags) - , m_name_data(name) + , m_name_u64(name) , wlock_thread(0) { } diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp index c45680cb8c..d816cffbe9 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp @@ -40,7 +40,7 @@ u32 LoadSpuImage(vfsStream& stream, u32& spu_ep) ELFLoader l(stream); l.LoadInfo(); const u32 alloc_size = 256 * 1024 /*0x1000000 - stream.GetSize()*/; - u32 spu_offset = Memory.MainMem.Alloc(alloc_size); + u32 spu_offset = Memory.MainMem.AllocAlign(alloc_size); l.LoadData(spu_offset); spu_ep = l.GetEntry(); return spu_offset; @@ -104,7 +104,7 @@ int sys_spu_thread_initialize(mem32_t thread, u32 group, u32 spu_num, mem_ptr_t< return CELL_ESRCH; } - if(!thread.IsGood() || !img.IsGood() || !attr.IsGood() || !attr.IsGood()) + if(!thread.IsGood() || !img.IsGood() || !attr.IsGood() || !arg.IsGood()) { return CELL_EFAULT; } @@ -133,7 +133,7 @@ int sys_spu_thread_initialize(mem32_t thread, u32 group, u32 spu_num, mem_ptr_t< CPUThread& new_thread = Emu.GetCPU().AddThread(CPU_THREAD_SPU); //copy SPU image: - u32 spu_offset = Memory.MainMem.Alloc(256 * 1024); + u32 spu_offset = Memory.MainMem.AllocAlign(256 * 1024); memcpy(Memory + spu_offset, Memory + (u32)img->segs_addr, 256 * 1024); //initialize from new place: new_thread.SetOffset(spu_offset); @@ -177,24 +177,70 @@ int sys_spu_thread_set_argument(u32 id, mem_ptr_t arg) return CELL_OK; } -//173 -int sys_spu_thread_group_start(u32 id) +//165 +int sys_spu_thread_get_exit_status(u32 id, mem32_t status) { - sc_spu.Warning("sys_spu_thread_group_start(id=0x%x)", id); + sc_spu.Warning("sys_spu_thread_get_exit_status(id=0x%x, status_addr=0x%x)", id, status.GetAddr()); - if(!Emu.GetIdManager().CheckID(id)) + if (!status.IsGood()) + { + return CELL_EFAULT; + } + + CPUThread* thr = Emu.GetCPU().GetThread(id); + + if(!thr || (thr->GetType() != CPU_THREAD_SPU && thr->GetType() != CPU_THREAD_RAW_SPU)) { return CELL_ESRCH; } + u32 res; + if (!(*(SPUThread*)thr).SPU.Out_MBox.Pop(res) || !thr->IsStopped()) + { + return CELL_ESTAT; + } + + status = res; + return CELL_OK; +} + +//171 +int sys_spu_thread_group_destroy(u32 id) +{ + sc_spu.Warning("sys_spu_thread_group_destroy(id=0x%x)", id); + SpuGroupInfo* group_info; if(!Emu.GetIdManager().GetIDData(id, group_info)) { return CELL_ESRCH; } - //Emu.Pause(); - for (int i = 0; i < group_info->list.GetCount(); i++) + if (group_info->lock) // ??? + { + return CELL_EBUSY; + } + + for (u32 i = 0; i < group_info->list.GetCount(); i++) + { + Emu.GetCPU().RemoveThread(group_info->list[i]); + } + + Emu.GetIdManager().RemoveID(id); + return CELL_OK; +} + +//173 +int sys_spu_thread_group_start(u32 id) +{ + sc_spu.Warning("sys_spu_thread_group_start(id=0x%x)", id); + + SpuGroupInfo* group_info; + if(!Emu.GetIdManager().GetIDData(id, group_info)) + { + return CELL_ESRCH; + } + + for (u32 i = 0; i < group_info->list.GetCount(); i++) { CPUThread* t; if (t = Emu.GetCPU().GetThread(group_info->list[i])) @@ -218,10 +264,9 @@ int sys_spu_thread_group_suspend(u32 id) } //Emu.Pause(); - for (int i = 0; i < group_info->list.GetCount(); i++) + for (u32 i = 0; i < group_info->list.GetCount(); i++) { - CPUThread* t; - if (t = Emu.GetCPU().GetThread(group_info->list[i])) + if (CPUThread* t = Emu.GetCPU().GetThread(group_info->list[i])) { t->Pause(); } @@ -273,14 +318,17 @@ int sys_spu_thread_group_join(u32 id, mem32_t cause, mem32_t status) cause = SYS_SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT; status = 0; //unspecified because of ALL_THREADS_EXIT - for (int i = 0; i < group_info->list.GetCount(); i++) + for (u32 i = 0; i < group_info->list.GetCount(); i++) { - while (Emu.GetCPU().GetThread(group_info->list[i])) + while (CPUThread* t = Emu.GetCPU().GetThread(group_info->list[i])) { - Sleep(1); + if (!t->IsRunning()) + { + break; + } if (Emu.IsStopped()) return CELL_OK; + Sleep(1); } - group_info->list[i] = 0; } group_info->lock = 0; // release lock @@ -514,7 +562,7 @@ int sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq, u64 req, u32 return CELL_ESRCH; } - for(int i=0; ilist.GetCount(); ++i) + for(u32 i=0; ilist.GetCount(); ++i) { CPUThread* t; if(t = Emu.GetCPU().GetThread(group->list[i])) @@ -537,3 +585,10 @@ int sys_spu_thread_group_connect_event_all_threads(u32 id, u32 eq, u64 req, u32 } return CELL_OK; } + +int sys_spu_thread_bind_queue(u32 id, u32 spuq, u32 spuq_num) +{ + sc_spu.Error("sys_spu_thread_bind_queue(id=0x%x, spuq=0x%x, spuq_num=0x%x)", id, spuq, spuq_num); + + return CELL_OK; +} diff --git a/rpcs3/Emu/SysCalls/lv2/SC_TTY.cpp b/rpcs3/Emu/SysCalls/lv2/SC_TTY.cpp index 52f6ec1573..f19531243d 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_TTY.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_TTY.cpp @@ -11,7 +11,7 @@ int sys_tty_read(u32 ch, u64 buf_addr, u32 len, u64 preadlen_addr) int sys_tty_write(u32 ch, u64 buf_addr, u32 len, u64 pwritelen_addr) { - if(ch < 0 || ch > 15 || len <= 0) return CELL_EINVAL; + if(ch < 0 || ch > 15 || (s32)len <= 0) return CELL_EINVAL; if(!Memory.IsGoodAddr(buf_addr)) return CELL_EFAULT; Emu.GetDbgCon().Write(ch, Memory.ReadString(buf_addr, len)); diff --git a/rpcs3/Emu/SysCalls/lv2/SC_VM.cpp b/rpcs3/Emu/SysCalls/lv2/SC_VM.cpp index 307dcaf75c..827c6214f2 100644 --- a/rpcs3/Emu/SysCalls/lv2/SC_VM.cpp +++ b/rpcs3/Emu/SysCalls/lv2/SC_VM.cpp @@ -108,7 +108,7 @@ int sys_vm_return_memory(u32 addr, u32 size) } // The memory size to return should not be superior to the virtual size in use minus 1MB. - if((current_ct->size - size - 0x100000) < 0) + if(current_ct->size < (size + 0x100000)) { return CELL_EBUSY; } @@ -130,7 +130,7 @@ int sys_vm_lock(u32 addr, u32 size) } // The memory size to return should not be superior to the virtual size to lock minus 1MB. - if((current_ct->size - size - 0x100000) < 0) + if(current_ct->size < (size + 0x100000)) { return CELL_EBUSY; } diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index dc6301ee77..fb8749efcd 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -305,7 +305,7 @@ void Emulator::Load() ConLog.Write("offset = 0x%llx", Memory.MainMem.GetStartAddr()); ConLog.Write("max addr = 0x%x", l.GetMaxAddr()); thread.SetOffset(Memory.MainMem.GetStartAddr()); - Memory.MainMem.Alloc(Memory.MainMem.GetStartAddr() + l.GetMaxAddr(), 0xFFFFED - l.GetMaxAddr()); + Memory.MainMem.AllocFixed(Memory.MainMem.GetStartAddr() + l.GetMaxAddr(), 0xFFFFED - l.GetMaxAddr()); thread.SetEntry(l.GetEntry() - Memory.MainMem.GetStartAddr()); break; @@ -314,12 +314,12 @@ void Emulator::Load() m_ppu_callback_thr = &GetCPU().AddThread(CPU_THREAD_PPU); thread.SetEntry(l.GetEntry()); - Memory.StackMem.Alloc(0x1000); + Memory.StackMem.AllocAlign(0x1000); thread.InitStack(); thread.AddArgv(m_elf_path); //thread.AddArgv("-emu"); - m_rsx_callback = Memory.MainMem.Alloc(4 * 4) + 4; + m_rsx_callback = Memory.MainMem.AllocAlign(4 * 4) + 4; Memory.Write32(m_rsx_callback - 4, m_rsx_callback); mem32_ptr_t callback_data(m_rsx_callback); @@ -327,7 +327,7 @@ void Emulator::Load() callback_data += SC(2); callback_data += BCLR(0x10 | 0x04, 0, 0, 0); - m_ppu_thr_exit = Memory.MainMem.Alloc(4 * 4); + m_ppu_thr_exit = Memory.MainMem.AllocAlign(4 * 4); mem32_ptr_t ppu_thr_exit_data(m_ppu_thr_exit); ppu_thr_exit_data += ADDI(3, 0, 0); diff --git a/rpcs3/Emu/event.h b/rpcs3/Emu/event.h index 8c5d94c360..516ea06ed7 100644 --- a/rpcs3/Emu/event.h +++ b/rpcs3/Emu/event.h @@ -1,61 +1,62 @@ -#pragma once - -struct sys_event_flag_attr -{ - u32 protocol; - u32 pshared; - u64 ipc_key; - int flags; - int type; - char name[8]; -}; - -struct event_flag -{ - sys_event_flag_attr attr; - u64 pattern; - - event_flag(u64 pattern, sys_event_flag_attr attr) - : pattern(pattern) - , attr(attr) - { - } -}; - -struct sys_event_queue_attr -{ - u32 attr_protocol; - int type; - char name[8]; -}; - -struct sys_event_data -{ - u64 source; - u64 data1; - u64 data2; - u64 data3; -}; - -struct EventQueue; - -struct EventPort -{ - u64 name; - u64 data1; - u64 data2; - u64 data3; - bool has_data; - CPUThread* thread; - EventQueue* queue[127]; - int pos; -}; - -struct EventQueue -{ - EventPort* ports[127]; - int size; - int pos; - int type; - char name[8]; +#pragma once + +enum EventQueueType +{ + SYS_PPU_QUEUE = 1, + SYS_SPU_QUEUE = 2, +}; + +struct sys_event_queue_attr +{ + be_t protocol; // SYS_SYNC_PRIORITY or SYS_SYNC_FIFO + be_t type; + union + { + char name[8]; + u64 name_u64; + }; +}; + +struct sys_event_data +{ + be_t source; + be_t data1; + be_t data2; + be_t data3; +}; + +struct EventQueue; + +struct EventPort +{ + u64 name; + u64 data1; + u64 data2; + u64 data3; + bool has_data; + CPUThread* thread; + EventQueue* queue[127]; + int pos; +}; + +struct EventQueue +{ + EventPort* ports[127]; + int size; + int pos; + + u32 m_protocol; + int m_type; + u64 m_name; + u64 m_key; + + EventQueue(u32 protocol, int type, u64 name, u64 key, int size) + : m_type(type) + , m_protocol(protocol) + , m_name(name) + , m_key(key) + , size(size) + , pos(0) + { + } }; \ No newline at end of file diff --git a/rpcs3/Gui/MainFrame.cpp b/rpcs3/Gui/MainFrame.cpp index 5a032d4cdf..1f3d8f1d9c 100644 --- a/rpcs3/Gui/MainFrame.cpp +++ b/rpcs3/Gui/MainFrame.cpp @@ -66,47 +66,6 @@ wxString GetPaneName() return wxString::Format("Pane_%d", pane_num++); } -bool wxMoveDir(wxString sFrom, wxString sTo) -{ - if (sFrom[sFrom.Len() - 1] != '\\' && sFrom[sFrom.Len() - 1] != '/') sFrom += wxFILE_SEP_PATH; - if (sTo[sTo.Len() - 1] != '\\' && sTo[sTo.Len() - 1] != '/') sTo += wxFILE_SEP_PATH; - - if (!::wxDirExists(sFrom)) { - ::wxLogError(wxT("%s does not exist!\r\nCan not copy directory"), sFrom.c_str()); - return false; - } - if (!wxDirExists(sTo)) { - if (!wxFileName::Mkdir(sTo, 0777, wxPATH_MKDIR_FULL)) { - ::wxLogError(wxT("%s could not be created!"), sTo.c_str()); - return false; - } - } - - wxDir fDir(sFrom); - wxString sNext = wxEmptyString; - bool bIsFile = fDir.GetFirst(&sNext); - while (bIsFile) { - const wxString sFileFrom = sFrom + sNext; - const wxString sFileTo = sTo + sNext; - if (::wxDirExists(sFileFrom)) { - wxMoveDir(sFileFrom, sFileTo); - ::wxRmdir(sFileFrom); - } - else { - if (!::wxFileExists(sFileTo)) { - if (!::wxCopyFile(sFileFrom, sFileTo)) { - ::wxLogError(wxT("Could not copy %s to %s !"), sFileFrom.c_str(), sFileTo.c_str()); - return false; - } - } - ::wxRemoveFile(sFileFrom); - } - bIsFile = fDir.GetNext(&sNext); - } - ::wxRmdir(sFrom); - return true; -} - MainFrame::MainFrame() : FrameBase(nullptr, wxID_ANY, "", "MainFrame", wxSize(800, 600)) , m_aui_mgr(this) @@ -267,47 +226,38 @@ void MainFrame::InstallPkg(wxCommandEvent& WXUNUSED(event)) Emu.Stop(); wxString fileName = ctrl.GetPath(); - - { - wxProgressDialog pdlg("Please wait", "Installing PKG...", 0, this, wxPD_APP_MODAL); - pkg_unpack((const char *)fileName.mb_str()); - } + if (!pkg_unpack((const char *)fileName.mb_str())) + ConLog.Error("Could not unpack PKG!"); + else ConLog.Success("PKG: extract done."); if (!wxRemoveFile(ctrl.GetPath()+".dec")) ConLog.Warning("Could not delete the decoded DEC file"); - // Copy the PKG to dev_hdd0 and format the path to be identical to the PS3. - wxString gamePath = "\\dev_hdd0\\game\\"; pkg_header *header; pkg_info((const char *)fileName.mb_str(), &header); - // Get the PKG title ID from the header and format it (should match TITLE ID from PARAM.SFO). wxString titleID_full (header->title_id); wxString titleID = titleID_full.SubString(7, 15); - // Travel to bin folder. - wxSetWorkingDirectory(wxGetCwd() + "\\..\\"); - - // Save the main dir. wxString mainDir = wxGetCwd(); + wxString gamePath = "\\dev_hdd0\\game\\"; - // Set PKG dir. - wxString oldPkgDir = wxT(mainDir + "\\" + titleID_full); - wxString newPkgDir = wxT(mainDir + gamePath + titleID); - - // Move the final folder. - wxMoveDir(oldPkgDir, newPkgDir); + wxString pkgDir = wxT(mainDir + gamePath + titleID); // Save the title ID. Emu.SetTitleID(titleID); - ConLog.Success("PKG: extract done."); - - // Travel to the main dir. - wxSetWorkingDirectory(mainDir); - + //Refresh game list m_game_viewer->Refresh(); - Emu.BootGame(newPkgDir.c_str()); + + if(Emu.BootGame(pkgDir.c_str())) + { + ConLog.Success("Game: boot done."); + } + else + { + ConLog.Error("Ps3 executable not found in folder (%s)", pkgDir.c_str()); + } } void MainFrame::BootElf(wxCommandEvent& WXUNUSED(event)) diff --git a/rpcs3/Gui/RSXDebugger.cpp b/rpcs3/Gui/RSXDebugger.cpp index 2b6990a32a..c442a10c99 100644 --- a/rpcs3/Gui/RSXDebugger.cpp +++ b/rpcs3/Gui/RSXDebugger.cpp @@ -293,11 +293,11 @@ void RSXDebugger::OnClickBuffer(wxMouseEvent& event) if (event.GetId() == p_buffer_colorD->GetId()) SHOW_BUFFER(3); if (event.GetId() == p_buffer_tex->GetId()) { - if(Memory.IsGoodAddr(GetAddress(render.m_textures[m_cur_texture].GetOffset(), render.m_textures[m_cur_texture].GetLocation())) && render.m_textures[m_cur_texture].m_width && render.m_textures[m_cur_texture].m_height) + if(Memory.IsGoodAddr(GetAddress(render.m_textures[m_cur_texture].GetOffset(), render.m_textures[m_cur_texture].GetLocation())) && render.m_textures[m_cur_texture].GetWidth() && render.m_textures[m_cur_texture].GetHeight()) MemoryViewerPanel::ShowImage(this, - GetAddress(render.m_textures[m_cur_texture].GetOffset(), render.m_textures[m_cur_texture].GetLocation()), 0, - render.m_textures[m_cur_texture].m_width, - render.m_textures[m_cur_texture].m_height, false); + GetAddress(render.m_textures[m_cur_texture].GetOffset(), render.m_textures[m_cur_texture].GetLocation()), 1, + render.m_textures[m_cur_texture].GetWidth(), + render.m_textures[m_cur_texture].GetHeight(), false); } #undef SHOW_BUFFER @@ -424,8 +424,8 @@ void RSXDebugger::GetBuffers() unsigned char* TexBuffer = (unsigned char*)Memory.VirtualToRealAddr(TexBuffer_addr); - u32 width = render.m_textures[m_cur_texture].m_width; - u32 height = render.m_textures[m_cur_texture].m_height; + u32 width = render.m_textures[m_cur_texture].GetWidth(); + u32 height = render.m_textures[m_cur_texture].GetHeight(); unsigned char* buffer = (unsigned char*)malloc(width * height * 3); memcpy(buffer, TexBuffer, width * height * 3); @@ -488,13 +488,13 @@ void RSXDebugger::GetTexture() m_list_texture->SetItem(i, 1, wxString::Format("0x%x", GetAddress(render.m_textures[i].GetOffset(), render.m_textures[i].GetLocation()))); m_list_texture->SetItem(i, 2, render.m_textures[i].isCubemap() ? "True" : "False"); m_list_texture->SetItem(i, 3, wxString::Format("%dD", render.m_textures[i].GetDimension())); - m_list_texture->SetItem(i, 4, render.m_textures[i].m_enabled ? "True" : "False"); + m_list_texture->SetItem(i, 4, render.m_textures[i].IsEnabled() ? "True" : "False"); m_list_texture->SetItem(i, 5, wxString::Format("0x%x", render.m_textures[i].GetFormat())); m_list_texture->SetItem(i, 6, wxString::Format("0x%x", render.m_textures[i].Getmipmap())); m_list_texture->SetItem(i, 7, wxString::Format("0x%x", render.m_textures[i].m_pitch)); m_list_texture->SetItem(i, 8, wxString::Format("%dx%d", - render.m_textures[i].m_width, - render.m_textures[i].m_height)); + render.m_textures[i].GetWidth(), + render.m_textures[i].GetHeight())); m_list_texture->SetItemBackgroundColour(i, wxColour(m_cur_texture == i ? "Wheat" : "White")); } diff --git a/rpcs3/Loader/ELF32.cpp b/rpcs3/Loader/ELF32.cpp index 34e997753b..5db69803d1 100644 --- a/rpcs3/Loader/ELF32.cpp +++ b/rpcs3/Loader/ELF32.cpp @@ -181,9 +181,9 @@ bool ELF32Loader::LoadPhdrData(u64 _offset) switch(machine) { - case MACHINE_SPU: Memory.MainMem.Alloc(phdr_arr[i].p_vaddr + offset, phdr_arr[i].p_memsz); break; - case MACHINE_MIPS: Memory.PSPMemory.RAM.Alloc(phdr_arr[i].p_vaddr + offset, phdr_arr[i].p_memsz); break; - case MACHINE_ARM: Memory.PSVMemory.RAM.Alloc(phdr_arr[i].p_vaddr + offset, phdr_arr[i].p_memsz); break; + case MACHINE_SPU: Memory.MainMem.AllocFixed(phdr_arr[i].p_vaddr + offset, phdr_arr[i].p_memsz); break; + case MACHINE_MIPS: Memory.PSPMemory.RAM.AllocFixed(phdr_arr[i].p_vaddr + offset, phdr_arr[i].p_memsz); break; + case MACHINE_ARM: Memory.PSVMemory.RAM.AllocFixed(phdr_arr[i].p_vaddr + offset, phdr_arr[i].p_memsz); break; default: continue; diff --git a/rpcs3/Loader/ELF64.cpp b/rpcs3/Loader/ELF64.cpp index 023fbd052c..e8180408ba 100644 --- a/rpcs3/Loader/ELF64.cpp +++ b/rpcs3/Loader/ELF64.cpp @@ -240,7 +240,7 @@ bool ELF64Loader::LoadPhdrData(u64 offset) case 0x00000001: //LOAD if(phdr_arr[i].p_memsz) { - Memory.MainMem.Alloc(offset + phdr_arr[i].p_vaddr, phdr_arr[i].p_memsz); + Memory.MainMem.AllocFixed(offset + phdr_arr[i].p_vaddr, phdr_arr[i].p_memsz); if(phdr_arr[i].p_filesz) { @@ -336,7 +336,7 @@ bool ELF64Loader::LoadPhdrData(u64 offset) stub.s_text = re(stub.s_text); const wxString& module_name = Memory.ReadString(stub.s_modulename); - Module* module = GetModuleByName(module_name); + Module* module = GetModuleByName(module_name.mb_str()); if(module) { //module->SetLoaded(); @@ -358,8 +358,8 @@ bool ELF64Loader::LoadPhdrData(u64 offset) ConLog.Write("*** text: 0x%x", stub.s_text); #endif static const u32 section = 4 * 3; - u64 tbl = Memory.MainMem.Alloc(stub.s_imports * 4 * 2); - u64 dst = Memory.MainMem.Alloc(stub.s_imports * section); + u64 tbl = Memory.MainMem.AllocAlign(stub.s_imports * 4 * 2); + u64 dst = Memory.MainMem.AllocAlign(stub.s_imports * section); for(u32 i=0; i - .\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;$(IncludePath) + .\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\..\ffmpeg;$(IncludePath) $(SolutionDir)bin\ ..\libs\$(Configuration)\;$(LibraryPath) $(ProjectName)-$(PlatformShortName)-dbg - .\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;$(IncludePath) + .\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\..\ffmpeg;$(IncludePath) $(SolutionDir)bin\ ..\libs\$(Configuration)\;$(LibraryPath) $(ProjectName)-$(PlatformShortName)-dbg false - .\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;$(IncludePath) + .\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\..\ffmpeg;$(IncludePath) $(SolutionDir)bin\ ..\libs\$(Configuration)\;$(LibraryPath) false @@ -91,7 +91,7 @@ false - .\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;$(IncludePath) + .\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\..\ffmpeg;$(IncludePath) $(SolutionDir)bin\ ..\libs\$(Configuration)\;$(LibraryPath) false @@ -205,6 +205,7 @@ NotUsing NotUsing + @@ -221,8 +222,10 @@ + + @@ -246,6 +249,7 @@ + @@ -323,6 +327,7 @@ + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 4eabc97c06..af7c242429 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -373,6 +373,18 @@ Emu\SysCalls\lv2 + + Emu\FS + + + Emu\FS + + + Utilities + + + Emu\SysCalls\lv2 + Emu\GS @@ -555,5 +567,8 @@ Include + + Utilities + \ No newline at end of file diff --git a/rpcs3/stdafx.h b/rpcs3/stdafx.h index 18a72ee8dc..9d71b93fe2 100644 --- a/rpcs3/stdafx.h +++ b/rpcs3/stdafx.h @@ -206,7 +206,9 @@ enum Status #include "Emu/System.h" #include "Emu/Cell/PPUThread.h" +#include "Emu/FS/vfsDirBase.h" #include "Emu/FS/vfsFileBase.h" +#include "Emu/FS/vfsLocalDir.h" #include "Emu/FS/vfsLocalFile.h" #include "Emu/FS/vfsFile.h" #include "Emu/FS/vfsStream.h" diff --git a/stblib/stb_truetype.h b/stblib/stb_truetype.h new file mode 100644 index 0000000000..94f27df15e --- /dev/null +++ b/stblib/stb_truetype.h @@ -0,0 +1,2066 @@ +// stb_truetype.h - v0.7 - public domain +// authored from 2009-2013 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// +// Bug/warning reports: +// "Zer" on mollyrocket (with fix) +// Cass Everitt +// stoiko (Haemimont Games) +// Brian Hook +// Walter van Niftrik +// +// VERSION HISTORY +// +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (STB) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevokable license to copy +// and modify this file as you see fit. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start, +// and you can cut and paste from it to move to more advanced) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetCodepointKernAdvance() +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since they different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// ADVANCED USAGE +// +// Quality: +// +// - Use the functions with Subpixel at the end to allow your characters +// to have subpixel positioning. Since the font is anti-aliased, not +// hinted, this is very import for quality. (This is not possible with +// baked fonts.) +// +// - Kerning is now supported, and if you're supporting subpixel rendering +// then kerning is worth using to give your text a polished look. +// +// Performance: +// +// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; +// if you don't do this, stb_truetype is forced to do the conversion on +// every call. +// +// - There are a lot of memory allocations. We should modify it to take +// a temp buffer and allocate from the temp buffer (without freeing), +// should help performance a lot. +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.6c, 2050 LOC) +// +// Documentation & header file 520 LOC \___ 660 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 620 LOC ---- 620 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Bitmap management 100 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs +GLstbtt_uint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=0; + char *text = "Heljo World!"; + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype. + +//#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // #define your own STBTT_sort() to override this to avoid qsort + #ifndef STBTT_sort + #include + #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func) + #endif + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) malloc(x) + #define STBTT_free(x,u) free(x) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +//#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. You can just skip +// this step if you know it's that kind of font. + + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +typedef struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph +} stbtt_fontinfo; + +extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +extern float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +extern void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 +// @TODO; for now always returns 0! + +extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy; + unsigned char type,padding; + } stbtt_vertex; +#endif + +extern int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates + +extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +extern unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +extern void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +extern void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +extern unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +extern void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +extern void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata); + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +extern const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +//#ifdef STB_TRUETYPE_IMPLEMENTATION + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) + + #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) + #define ttSHORT(p) (* (stbtt_int16 *) (p)) + #define ttULONG(p) (* (stbtt_uint32 *) (p)) + #define ttLONG(p) (* (stbtt_int32 *) (p)) + +#else + + stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#endif + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(const stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*14); + } + } + return -1; +} + +int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + stbtt_uint8 *data = (stbtt_uint8 *) data2; + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + stbtt_uint16 item, offset, start, end; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 start, end; + searchRange >>= 1; + start = ttUSHORT(data + search + 2 + segcount*2 + 2); + end = ttUSHORT(data + search + 2); + start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2); + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + end = ttUSHORT(data + index_map + 14 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + return 1; +} + +int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0,y0,x1,y1; + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) + x0=y0=x1=y1=0; // e.g. space character + // now move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x + shift_x); + if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil (x1 * scale_x + shift_x); + if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y + shift_y); +} +void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + +typedef struct stbtt__active_edge +{ + int x,dx; + float ey; + struct stbtt__active_edge *next; + int valid; +} stbtt__active_edge; + +#define FIXSHIFT 10 +#define FIX (1 << FIXSHIFT) +#define FIXMASK (FIX-1) + +static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!! + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(e->y0 <= start_point); + if (!z) return z; + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = -STBTT_ifloor(FIX * -dxdy); + else + z->dx = STBTT_ifloor(FIX * dxdy); + z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0))); + z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->valid = e->invert ? 1 : -1; + return z; +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->valid; + } else { + int x1 = e->x; w += e->valid; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> FIXSHIFT; + int j = x1 >> FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->valid); + z->valid = 0; + STBTT_free(z, userdata); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata); + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + while (active) { + stbtt__active_edge *z = active; + active = active->next; + STBTT_free(z, userdata); + } + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +static int stbtt__edge_compare(const void *p, const void *q) +{ + stbtt__edge *a = (stbtt__edge *) p; + stbtt__edge *b = (stbtt__edge *) q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; + int vsubsample = result->h < 8 ? 15 : 5; + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = p[a].y * y_scale_inv * vsubsample + shift_y; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = p[b].y * y_scale_inv * vsubsample + shift_y; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +// returns number of contours +stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + stbtt_InitFont(&f, data, offset); + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 2; + if (y+gh+2 > bottom_y) + bottom_y = y+gh+2; + } + return bottom_y; +} + +void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +//#endif // STB_TRUETYPE_IMPLEMENTATION diff --git a/unpkg/unpkg.c b/unpkg/unpkg.c index f406f1c026..b5367e1e88 100644 --- a/unpkg/unpkg.c +++ b/unpkg/unpkg.c @@ -65,13 +65,15 @@ static int pkg_sanity_check(FILE *f, FILE *g, pkg_header **h_ptr, const char *fn case PKG_RELEASE_TYPE_DEBUG: { ConLog.Warning ("UnPkg: Debug PKG detected."); + wxProgressDialog pdlg ("PKG Decrypter / Installer", "Please wait, recrypting...", 0, 0, wxPD_AUTO_HIDE | wxPD_APP_MODAL); + u8* data; u8 sha_key[0x40]; int i; f= fopen(fname, "rb"); - fseek(f, 0, SEEK_END); - int nlen = ftell(f); - fseek(f, 0, SEEK_SET); + _fseeki64(f, 0, SEEK_END); + int nlen = _ftelli64(f); + _fseeki64(f, 0, SEEK_SET); data = (u8*)malloc(nlen); fread(data, 1, nlen, f); fclose(f); @@ -93,6 +95,7 @@ static int pkg_sanity_check(FILE *f, FILE *g, pkg_header **h_ptr, const char *fn u8 hash[0x14]; sha1(sha_crap, 0x40, hash); for(i=0;i<0x10;i++) data[dptr+i] ^= hash[i]; + set_u64(sha_crap+0x38, get_u64(sha_crap+0x38)+1); } @@ -114,20 +117,21 @@ static int pkg_sanity_check(FILE *f, FILE *g, pkg_header **h_ptr, const char *fn // add hash sha1(data, nlen-0x20, &data[nlen-0x20]); + fwrite(data, 1, nlen, g); //fclose(g); // not close the file for continuing - fseek(g, 0, SEEK_END); - tmp = ftell(g); + _fseeki64(g, 0, SEEK_END); + tmp = _ftelli64(g); } break; - + case PKG_RELEASE_TYPE_RELEASE: { ConLog.Warning ("UnPkg: Retail PKG detected."); - fseek(f, 0, SEEK_END); - tmp = ftell(f); + _fseeki64(f, 0, SEEK_END); + tmp = _ftelli64(f); } break; @@ -188,19 +192,19 @@ static void print_pkg_header(pkg_header *header) hash_tostring(qa, header->qa_digest, sizeof(header->qa_digest)); hash_tostring(kl, header->klicensee, sizeof(header->klicensee)); - ConLog.Write("Magic: %x\n", ntohl(header->magic)); - ConLog.Write("Release Type: %x\n", ntohl(header->rel_type) >> 16 & (0xffff)); - ConLog.Write("Platform Type: %x\n", ntohl(header->rel_type) & (0xffff)); - ConLog.Write("Header size: %x\n", ntohl(header->header_size)); - ConLog.Write("Unk1: %x\n", ntohl(header->unk1)); - ConLog.Write("Metadata size: %x\n", ntohl(header->meta_size)); - ConLog.Write("File count: %u\n", ntohl(header->file_count)); - ConLog.Write("Pkg size: %llu\n", ntohll(header->pkg_size)); - ConLog.Write("Data offset: %llx\n", ntohll(header->data_offset)); - ConLog.Write("Data size: %llu\n", ntohll(header->data_size)); - ConLog.Write("TitleID: %s\n", header->title_id); - ConLog.Write("QA Digest: %s\n", qa); - ConLog.Write( "KLicensee: %s\n", kl); + ConLog.Write("Magic: 0x%x", ntohl(header->magic)); + ConLog.Write("Release Type: 0x%x", ntohl(header->rel_type) >> 16 & (0xffff)); + ConLog.Write("Platform Type: 0x%x", ntohl(header->rel_type) & (0xffff)); + ConLog.Write("Header size: 0x%x", ntohl(header->header_size)); + ConLog.Write("Unk1: 0x%x", ntohl(header->unk1)); + ConLog.Write("Metadata size: 0x%x", ntohl(header->meta_size)); + ConLog.Write("File count: %u", ntohl(header->file_count)); + ConLog.Write("Pkg size: %llu", ntohll(header->pkg_size)); + ConLog.Write("Data offset: 0x%llx", ntohll(header->data_offset)); + ConLog.Write("Data size: 0x%llu", ntohll(header->data_size)); + ConLog.Write("TitleID: %s", header->title_id); + ConLog.Write("QA Digest: %s", qa); + ConLog.Write( "KLicensee: %s", kl); } static void *pkg_info(const char *fname, pkg_header **h_ptr) @@ -244,6 +248,9 @@ static void pkg_crypt(const u8 *key, const u8 *kl, FILE *f, u32 l; u64 hi, lo; + int max = len / BUF_SIZE; + wxProgressDialog pdlg("PKG Decrypter / Installer", "Please wait, decrypting...", max, 0, wxPD_AUTO_HIDE | wxPD_APP_MODAL); + parts = len / BUF_SIZE; if (len % BUF_SIZE != 0) parts++; @@ -278,18 +285,19 @@ static void pkg_crypt(const u8 *key, const u8 *kl, FILE *f, } fwrite(out_buf, 1, l, out); + pdlg.Update(i); } - + pdlg.Update(max); } -static void pkg_unpack_file(pkg_file_entry *fentry, FILE *dec) +static bool pkg_unpack_file(pkg_file_entry *fentry, FILE *dec) { FILE *out = NULL; u64 size; u32 tmp; u8 buf[BUF_SIZE]; - fseek(dec, fentry->name_offset, SEEK_SET); + _fseeki64(dec, fentry->name_offset, SEEK_SET); memset(buf, 0, sizeof(buf)); fread(buf, fentry->name_size, 1, dec); @@ -301,7 +309,7 @@ static void pkg_unpack_file(pkg_file_entry *fentry, FILE *dec) case PKG_FILE_ENTRY_SDAT: case PKG_FILE_ENTRY_REGULAR: out = fopen((char *)buf, "wb"); - fseek(dec, fentry->file_offset, SEEK_SET); + _fseeki64(dec, fentry->file_offset, SEEK_SET); for (size = 0; size < fentry->file_size; ) { size += fread(buf, sizeof(u8), BUF_SIZE, dec); @@ -320,14 +328,18 @@ static void pkg_unpack_file(pkg_file_entry *fentry, FILE *dec) mkdir ((char *)buf); break; } + return true; } static void pkg_unpack_data(u32 file_count, FILE *dec) { + int max = file_count; + wxProgressDialog pdlg ("PKG Decrypter / Installer", "Please wait, unpacking...", max, 0, wxPD_AUTO_HIDE | wxPD_APP_MODAL); + u32 i; pkg_file_entry *file_table = NULL; - fseek(dec, 0, SEEK_SET); + _fseeki64(dec, 0, SEEK_SET); file_table = (pkg_file_entry *)malloc(sizeof(pkg_file_entry)*file_count); i = fread(file_table, sizeof(pkg_file_entry), file_count, dec); @@ -346,27 +358,39 @@ static void pkg_unpack_data(u32 file_count, FILE *dec) (file_table+i)->file_size = ntohll((file_table+i)->file_size); (file_table+i)->type = ntohl((file_table+i)->type); - pkg_unpack_file(file_table+i, dec); - + if(pkg_unpack_file(file_table+i, dec)) pdlg.Update(i); } free(file_table); + pdlg.Update(max); } -static void pkg_unpack(const char *fname) +bool pkg_unpack(const char *fname) { FILE *f, *dec; char *dec_fname; pkg_header *header; - int ret; struct stat sb; - + f = (FILE*) pkg_info(fname, &header); if (f == NULL) - return; + return false; + + // Save the main dir. + wxString mainDir = wxGetCwd(); + + // Set the working directory. + wxSetWorkingDirectory(wxGetCwd() + "\\dev_hdd0\\game\\"); - fseek(f, ntohll(header->data_offset), SEEK_SET); + std::string gamePath = "\\dev_hdd0\\game\\"; + + // Get the PKG title ID from the header and format it (should match TITLE ID from PARAM.SFO). + std::string titleID_full (header->title_id); + std::string titleID = titleID_full.substr(7, 9); + std::string pkgDir = mainDir + gamePath + titleID; + + _fseeki64(f, ntohll(header->data_offset), SEEK_SET); dec_fname = (char*)malloc(strlen(fname)+4); memset(dec_fname, 0, strlen(fname)+4); @@ -377,29 +401,33 @@ static void pkg_unpack(const char *fname) { ConLog.Error("UnPkg: Could not create temp file for decrypted data."); free(header); - return; + return false; } unlink(dec_fname); pkg_crypt(PKG_AES_KEY, header->klicensee, f, ntohll(header->data_size), dec); - fseek(dec, 0, SEEK_SET); + _fseeki64(dec, 0, SEEK_SET); fclose(f); if (stat(header->title_id, &sb) != 0) { - ret = mkdir(header->title_id); - if (ret < 0) + if (mkdir(titleID.c_str()) < 0) { ConLog.Error("UnPkg: Could not mkdir."); + ConLog.Error("UnPkg: Possibly, folder already exists in dev_hdd0\\game : %s", titleID.c_str()); + wxSetWorkingDirectory(mainDir); free(header); - return; + return false; } } - chdir(header->title_id); + chdir(titleID.c_str()); pkg_unpack_data(ntohl(header->file_count), dec); fclose(dec); + + wxSetWorkingDirectory(mainDir); + return true; } \ No newline at end of file diff --git a/unpkg/unpkg.h b/unpkg/unpkg.h index 2f06d4241c..fa8262eb0e 100644 --- a/unpkg/unpkg.h +++ b/unpkg/unpkg.h @@ -164,11 +164,11 @@ static void *pkg_info(const char *fname, pkg_header **h_ptr); static void pkg_crypt(const u8 *key, const u8 *kl, FILE *f, u64 len, FILE *out); -static void pkg_unpack(const char *fname); +bool pkg_unpack(const char *fname); static void pkg_unpack_data(u32 file_count, FILE *dec); -static void pkg_unpack_file(pkg_file_entry *fentry, FILE *dec);; +static bool pkg_unpack_file(pkg_file_entry *fentry, FILE *dec);; static int pkg_pack_data(file_table_tr *ftr, pkg_file_entry *table, int file_count, sha1_context *ctx, FILE *out);