diff --git a/.gitignore b/.gitignore
index e3ceeff00f..cea62cad43 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,8 @@
 /bin/VertexProgram.txt
 /bin/BreakPoints.dat
 /bin/textures
+/bin/*.lib
+/bin/*.exp
 rpcs3/git-version.h
 
 # Copyrighted files
diff --git a/.gitmodules b/.gitmodules
index 8bc866cb94..c0d4ac8d2d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,3 +2,6 @@
 	path = wxWidgets
 	url = https://github.com/DHrpcs3/wxWidgets.git
 	ignore = dirty
+[submodule "rpcs3-ffmpeg"]
+	path = ffmpeg
+	url = https://github.com/hrydgard/ppsspp-ffmpeg
diff --git a/Utilities/SMutex.cpp b/Utilities/SMutex.cpp
index 7d9e9fdbd7..1f48903f4e 100644
--- a/Utilities/SMutex.cpp
+++ b/Utilities/SMutex.cpp
@@ -6,9 +6,9 @@ __forceinline void SM_Sleep()
 	Sleep(1);
 }
 
-__forceinline std::thread::id SM_GetCurrentThreadId()
+__forceinline size_t SM_GetCurrentThreadId()
 {
-	return std::this_thread::get_id();
+	return std::this_thread::get_id().hash();
 }
 
 __forceinline u32 SM_GetCurrentCPUThreadId()
diff --git a/Utilities/SMutex.h b/Utilities/SMutex.h
index 2f934d4c96..b92d67e614 100644
--- a/Utilities/SMutex.h
+++ b/Utilities/SMutex.h
@@ -1,7 +1,7 @@
 #pragma once
 
 extern void SM_Sleep();
-extern std::thread::id SM_GetCurrentThreadId();
+extern size_t SM_GetCurrentThreadId();
 extern u32 SM_GetCurrentCPUThreadId();
 extern be_t<u32> SM_GetCurrentCPUThreadIdBE();
 
@@ -20,13 +20,13 @@ enum SMutexResult
 template
 <
 	typename T,
-	u32 free_value = 0,
-	u32 dead_value = ~0,
+	u64 free_value = 0,
+	u64 dead_value = ~0,
 	void (*wait)() = SM_Sleep
 >
 class SMutexBase
 {
-	static_assert(sizeof(T) == 4, "Invalid SMutexBase typename");
+	static_assert(sizeof(T) == sizeof(std::atomic<T>), "Invalid SMutexBase type");
 	std::atomic<T> owner;
 
 public:
@@ -157,14 +157,14 @@ public:
 	}
 };
 
-typedef SMutexBase<DWORD>
+typedef SMutexBase<size_t>
 	SMutexGeneral;
 typedef SMutexBase<u32>
 	SMutex;
 typedef SMutexBase<be_t<u32>>
 	SMutexBE;
 
-typedef SMutexLockerBase<std::thread::id, SM_GetCurrentThreadId>
+typedef SMutexLockerBase<size_t, SM_GetCurrentThreadId>
 	SMutexGeneralLocker;
 typedef SMutexLockerBase<u32, SM_GetCurrentCPUThreadId>
 	SMutexLocker;
diff --git a/Utilities/SQueue.h b/Utilities/SQueue.h
new file mode 100644
index 0000000000..1fee6f6727
--- /dev/null
+++ b/Utilities/SQueue.h
@@ -0,0 +1,99 @@
+#pragma once
+
+template<typename T, u32 SQSize = 666>
+class SQueue
+{
+	SMutex m_mutex;
+	u32 m_pos;
+	u32 m_count;
+	T m_data[SQSize];
+
+public:
+	SQueue()
+		: m_pos(0)
+		, m_count(0)
+	{
+	}
+
+	bool Push(T& data)
+	{
+		while (true)
+		{
+			if (Emu.IsStopped())
+			{
+				return false;
+			}
+
+			if (m_mutex.GetOwner() == m_mutex.GetDeadValue())
+			{
+				return false;
+			}
+
+			if (m_count >= SQSize)
+			{
+				Sleep(1);
+				continue;
+			}
+
+			{
+				SMutexLocker lock(m_mutex);
+
+				if (m_count >= SQSize) continue;
+
+				m_data[(m_pos + m_count++) % SQSize] = data;
+
+				return true;
+			}
+		}
+	}
+
+	bool Pop(T& data)
+	{
+		while (true)
+		{
+			if (Emu.IsStopped())
+			{
+				return false;
+			}
+
+			if (m_mutex.GetOwner() == m_mutex.GetDeadValue())
+			{
+				return false;
+			}
+
+			if (!m_count)
+			{
+				Sleep(1);
+				continue;
+			}
+
+			{
+				SMutexLocker lock(m_mutex);
+
+				if (!m_count) continue;
+
+				data = m_data[m_pos];
+				m_pos = (m_pos + 1) % SQSize;
+				m_count--;
+
+				return true;
+			}
+		}
+	}
+
+	volatile u32 GetCount()
+	{
+		return m_count;
+	}
+
+	volatile bool IsEmpty()
+	{
+		return !m_count;
+	}
+
+	void Clear()
+	{
+		SMutexLocker lock(m_mutex);
+		m_count = 0;
+	}
+};
\ No newline at end of file
diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp
index 6bd33d6b86..b71fc5eeea 100644
--- a/Utilities/Thread.cpp
+++ b/Utilities/Thread.cpp
@@ -117,10 +117,12 @@ thread::thread()
 }
 
 void thread::start(std::function<void()> func)
-{ // got a crash related with strings
-	m_thr = std::thread([this, func]()
+{
+	std::string name = m_name;
+
+	m_thr = std::thread([func, name]()
 	{
-		NamedThreadBase info(m_name);
+		NamedThreadBase info(name);
 		g_tls_this_thread = &info;
 
 		try
@@ -130,7 +132,7 @@ void thread::start(std::function<void()> func)
 		catch(...)
 		{
 			ConLog.Error("Crash :(");
-			std::terminate();
+			//std::terminate();
 		}
 	});
 }
diff --git a/ffmpeg b/ffmpeg
new file mode 160000
index 0000000000..8bcaa2485c
--- /dev/null
+++ b/ffmpeg
@@ -0,0 +1 @@
+Subproject commit 8bcaa2485c2434d7d7a9da17491bafb58de42bb6
diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp
index df197bc1d9..2d98e28e4b 100644
--- a/rpcs3/Emu/CPU/CPUThread.cpp
+++ b/rpcs3/Emu/CPU/CPUThread.cpp
@@ -296,7 +296,7 @@ void CPUThread::ExecOnce()
 
 void CPUThread::Task()
 {
-	ConLog.Write("%s enter", CPUThread::GetFName().wx_str());
+	if (Ini.HLELogging.GetValue()) ConLog.Write("%s enter", CPUThread::GetFName().wx_str());
 
 	const Array<u64>& bp = Emu.GetBreakPoints();
 
@@ -358,5 +358,5 @@ void CPUThread::Task()
 		ConLog.Success("Exit Code: %d", exitcode);
 	}
 
-	ConLog.Write("%s leave", CPUThread::GetFName().wx_str());
+	if (Ini.HLELogging.GetValue()) ConLog.Write("%s leave", CPUThread::GetFName().wx_str());
 }
diff --git a/rpcs3/Emu/Cell/MFC.h b/rpcs3/Emu/Cell/MFC.h
index ca8235c05a..6016924ae5 100644
--- a/rpcs3/Emu/Cell/MFC.h
+++ b/rpcs3/Emu/Cell/MFC.h
@@ -168,11 +168,11 @@ struct DMAC
 		switch(cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK))
 		{
 		case MFC_PUT_CMD:
-			memcpy(Memory + ea, Memory + ls_offset + lsa, size);
+			Memory.Copy(ea, ls_offset + lsa, size);
 		return true;
 
 		case MFC_GET_CMD:
-			memcpy(Memory + ls_offset + lsa, Memory + ea, size);
+			Memory.Copy(ls_offset + lsa, ea, size);
 		return true;
 
 		default:
diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp
index 9a5c84e854..49f2abfff2 100644
--- a/rpcs3/Emu/Cell/RawSPUThread.cpp
+++ b/rpcs3/Emu/Cell/RawSPUThread.cpp
@@ -251,7 +251,7 @@ u32 RawSPUThread::GetIndex() const
 
 void RawSPUThread::Task()
 {
-	ConLog.Write("%s enter", PPCThread::GetFName().wx_str());
+	if (Ini.HLELogging.GetValue()) ConLog.Write("%s enter", PPCThread::GetFName().wx_str());
 
 	const Array<u64>& bp = Emu.GetBreakPoints();
 
@@ -334,5 +334,5 @@ void RawSPUThread::Task()
 		ConLog.Error("Exception: %s", wxString(e).wx_str());
 	}
 
-	ConLog.Write("%s leave", PPCThread::GetFName().wx_str());
+	if (Ini.HLELogging.GetValue()) ConLog.Write("%s leave", PPCThread::GetFName().wx_str());
 }
diff --git a/rpcs3/Emu/DbgConsole.cpp b/rpcs3/Emu/DbgConsole.cpp
index d731b8009d..c24a258b5a 100644
--- a/rpcs3/Emu/DbgConsole.cpp
+++ b/rpcs3/Emu/DbgConsole.cpp
@@ -30,7 +30,14 @@ DbgConsole::~DbgConsole()
 
 void DbgConsole::Write(int ch, const wxString& text)
 {
-	while(m_dbg_buffer.IsBusy()) Sleep(1);
+	while (m_dbg_buffer.IsBusy())
+	{
+		if (Emu.IsStopped())
+		{
+			return;
+		}
+		Sleep(1);
+	}
 	m_dbg_buffer.Push(DbgPacket(ch, text));
 
 	if(!IsAlive()) Start();
@@ -47,6 +54,10 @@ void DbgConsole::Task()
 	{
 		if(!m_dbg_buffer.HasNewPacket())
 		{
+			if (Emu.IsStopped())
+			{
+				break;
+			}
 			Sleep(1);
 			continue;
 		}
diff --git a/rpcs3/Emu/FS/vfsDir.cpp b/rpcs3/Emu/FS/vfsDir.cpp
index 0d165c6ca5..8fcb8d4bde 100644
--- a/rpcs3/Emu/FS/vfsDir.cpp
+++ b/rpcs3/Emu/FS/vfsDir.cpp
@@ -30,7 +30,7 @@ bool vfsDir::Create(const wxString& path)
 
 bool vfsDir::IsExists(const wxString& path) const
 {
-	return m_stream->IsExists(path);
+	return m_stream->IsExists(path); // Crash (Access violation reading location 0x0000000000000000)
 }
 
 const Array<DirEntryInfo>& vfsDir::GetEntries() const
diff --git a/rpcs3/Emu/FS/vfsStreamMemory.cpp b/rpcs3/Emu/FS/vfsStreamMemory.cpp
index 8bb08090e6..feecb51330 100644
--- a/rpcs3/Emu/FS/vfsStreamMemory.cpp
+++ b/rpcs3/Emu/FS/vfsStreamMemory.cpp
@@ -32,7 +32,7 @@ u64 vfsStreamMemory::Write(const void* src, u64 size)
 
 	if(!size || !Memory.IsGoodAddr(m_addr + Tell(), size)) return 0;
 
-	memcpy(&Memory[m_addr + Tell()], src, size);
+	Memory.CopyFromReal(m_addr + Tell(), (void*)src, size);
 
 	return vfsStream::Write(src, size);
 }
@@ -46,7 +46,7 @@ u64 vfsStreamMemory::Read(void* dst, u64 size)
 
 	if(!size || !Memory.IsGoodAddr(m_addr + Tell(), size)) return 0;
 
-	memcpy(dst, &Memory[m_addr + Tell()], size);
+	Memory.CopyToReal(dst, m_addr + Tell(), size);
 
 	return vfsStream::Read(dst, size);
 }
diff --git a/rpcs3/Emu/GS/RSXThread.cpp b/rpcs3/Emu/GS/RSXThread.cpp
index e46b81e32c..c16bc64cef 100644
--- a/rpcs3/Emu/GS/RSXThread.cpp
+++ b/rpcs3/Emu/GS/RSXThread.cpp
@@ -56,7 +56,7 @@ void RSXVertexData::Load(u32 start, u32 count)
 		{
 		case 1:
 		{
-			memcpy(dst, src, size);
+			memcpy(dst, src, size); // may be dangerous
 		}
 		break;
 
diff --git a/rpcs3/Emu/Memory/Memory.cpp b/rpcs3/Emu/Memory/Memory.cpp
index df8178826e..8bf12506dc 100644
--- a/rpcs3/Emu/Memory/Memory.cpp
+++ b/rpcs3/Emu/Memory/Memory.cpp
@@ -48,18 +48,16 @@ bool MemoryBlock::GetMemFromAddr(void* dst, const u64 addr, const u32 size)
 {
 	if(!IsMyAddress(addr) || FixAddr(addr) + size > GetSize()) return false;
 
-	memcpy(dst, GetMem(FixAddr(addr)), size);
-
-	return true;
+	// mem cpy(dst, GetMem(FixAddr(addr)), size);
+	return Memory.CopyToReal(dst, (u32)addr, size);
 }
 
 bool MemoryBlock::SetMemFromAddr(void* src, const u64 addr, const u32 size)
 {
 	if(!IsMyAddress(addr) || FixAddr(addr) + size > GetSize()) return false;
 
-	memcpy(GetMem(FixAddr(addr)), src, size);
-
-	return true;
+	// mem cpy(GetMem(FixAddr(addr)), src, size);
+	return Memory.CopyFromReal((u32)addr, src, size);
 }
 
 bool MemoryBlock::GetMemFFromAddr(void* dst, const u64 addr)
diff --git a/rpcs3/Emu/Memory/Memory.h b/rpcs3/Emu/Memory/Memory.h
index 5e9ae0e863..ea670cb846 100644
--- a/rpcs3/Emu/Memory/Memory.h
+++ b/rpcs3/Emu/Memory/Memory.h
@@ -238,6 +238,106 @@ public:
 	u64 Read64(const u64 addr);
 	u128 Read128(const u64 addr);
 
+	bool CopyToReal(void* real, u32 from, u32 count) // (4K pages) copy from virtual to real memory
+	{
+		if (!count) return true;
+
+		u8* to = (u8*)real;
+
+		if (u32 frag = from & 4095)
+		{
+			if (!IsGoodAddr(from)) return false;
+			u32 num = 4096 - frag;
+			if (count < num) num = count;
+			memcpy(to, GetMemFromAddr(from), num);
+			to += num;
+			from += num;
+			count -= num;
+		}
+
+		for (u32 page = count / 4096; page > 0; page--)
+		{
+			if (!IsGoodAddr(from)) return false;
+			memcpy(to, GetMemFromAddr(from), 4096);
+			to += 4096;
+			from += 4096;
+			count -= 4096;
+		}
+
+		if (count)
+		{
+			if (!IsGoodAddr(from)) return false;
+			memcpy(to, GetMemFromAddr(from), count);
+		}
+
+		return true;
+	}
+
+	bool CopyFromReal(u32 to, void* real, u32 count) // (4K pages) copy from real to virtual memory
+	{
+		if (!count) return true;
+
+		u8* from = (u8*)real;
+
+		if (u32 frag = to & 4095)
+		{
+			if (!IsGoodAddr(to)) return false;
+			u32 num = 4096 - frag;
+			if (count < num) num = count;
+			memcpy(GetMemFromAddr(to), from, num);
+			to += num;
+			from += num;
+			count -= num;
+		}
+
+		for (u32 page = count / 4096; page > 0; page--)
+		{
+			if (!IsGoodAddr(to)) return false;
+			memcpy(GetMemFromAddr(to), from, 4096);
+			to += 4096;
+			from += 4096;
+			count -= 4096;
+		}
+
+		if (count)
+		{
+			if (!IsGoodAddr(to)) return false;
+			memcpy(GetMemFromAddr(to), from, count);
+		}
+
+		return true;
+
+	}
+
+	bool Copy(u32 to, u32 from, u32 count) // (4K pages) copy from virtual to virtual memory through real
+	{
+		if (u8* buf = (u8*)malloc(count))
+		{
+			if (CopyToReal(buf, from, count))
+			{
+				if (CopyFromReal(to, buf, count))
+				{
+					free(buf);
+					return true;
+				}
+				else
+				{
+					free(buf);
+					return false;
+				}
+			}
+			else
+			{
+				free(buf);
+				return false;
+			}
+		}
+		else
+		{
+			return false;
+		}
+	}
+
 	void ReadLeft(u8* dst, const u64 addr, const u32 size)
 	{
 		MemoryBlock& mem = GetMemByAddr(addr);
diff --git a/rpcs3/Emu/SysCalls/Callback.cpp b/rpcs3/Emu/SysCalls/Callback.cpp
index 017341218d..f64851b6c3 100644
--- a/rpcs3/Emu/SysCalls/Callback.cpp
+++ b/rpcs3/Emu/SysCalls/Callback.cpp
@@ -53,10 +53,33 @@ void Callback::Branch(bool wait)
 {
 	m_has_data = false;
 
+	static SMutexGeneral cb_mutex;
+
 	CPUThread& thr = Emu.GetCallbackThread();
 
-	while(Emu.IsRunning() && thr.IsAlive())
+again:
+
+	while (thr.IsAlive())
+	{
+		if (Emu.IsStopped())
+		{
+			ConLog.Warning("Callback::Branch() aborted");
+			return;
+		}
 		Sleep(1);
+	}
+
+	SMutexGeneralLocker lock(cb_mutex);
+
+	if (thr.IsAlive())
+	{
+		goto again;
+	}
+	if (Emu.IsStopped())
+	{
+		ConLog.Warning("Callback::Branch() aborted");
+		return;
+	}
 
 	thr.Stop();
 	thr.Reset();
@@ -74,8 +97,20 @@ void Callback::Branch(bool wait)
 
 	thr.Exec();
 
-	if(wait)
-		GetCurrentPPCThread()->Wait(thr);
+	if (!wait)
+	{
+		return;
+	}
+
+	while (thr.IsAlive())
+	{
+		if (Emu.IsStopped())
+		{
+			ConLog.Warning("Callback::Branch(true) aborted (end)");
+			return;
+		}
+		Sleep(1);
+	}
 }
 
 void Callback::SetName(const std::string& name)
diff --git a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp
index 50290f9e97..8c5915d6ff 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp
+++ b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp
@@ -135,6 +135,7 @@ struct CellAudioPortConfig
 
 struct AudioPortConfig
 {
+	SMutex m_mutex;
 	bool m_is_audio_port_opened;
 	bool m_is_audio_port_started;
 	u8 channel;
@@ -298,7 +299,7 @@ int cellAudioInit()
 	m_config.m_indexes = Memory.Alloc(sizeof(u64) * m_config.AUDIO_PORT_COUNT, 16);
 	memset(Memory + m_config.m_indexes, 0, sizeof(u64) * m_config.AUDIO_PORT_COUNT);
 
-	thread t("AudioThread", []()
+	thread t("Audio Thread", []()
 		{
 			WAVHeader header(2); // WAV file header (stereo)
 			
@@ -366,10 +367,12 @@ int cellAudioInit()
 					memcpy(buffer2, Memory + buf_addr, block_size * sizeof(float));
 					memset(Memory + buf_addr, 0, block_size * sizeof(float));
 
-					// TODO: atomic
-					port.counter = m_config.counter;
-					port.tag++; // absolute index of block that will be read
-					index = (position + 1) % port.block; // write new value
+					{
+						SMutexLocker lock(port.m_mutex);
+						port.counter = m_config.counter;
+						port.tag++; // absolute index of block that will be read
+						index = (position + 1) % port.block; // write new value
+					}
 
 					if (first_mix)
 					{
@@ -599,7 +602,7 @@ int cellAudioPortStop(u32 portNum)
 
 int cellAudioGetPortTimestamp(u32 portNum, u64 tag, mem64_t stamp)
 {
-	cellAudio.Warning("cellAudioGetPortTimestamp(portNum=0x%x, tag=0x%llx, stamp_addr=0x%x)", portNum, tag, stamp.GetAddr());
+	cellAudio.Log("cellAudioGetPortTimestamp(portNum=0x%x, tag=0x%llx, stamp_addr=0x%x)", portNum, tag, stamp.GetAddr());
 
 	if (portNum >= m_config.AUDIO_PORT_COUNT) 
 	{
@@ -618,7 +621,8 @@ int cellAudioGetPortTimestamp(u32 portNum, u64 tag, mem64_t stamp)
 
 	AudioPortConfig& port = m_config.m_ports[portNum];
 
-	// TODO: atomic
+	SMutexLocker lock(port.m_mutex);
+
 	stamp = m_config.start_time + (port.counter + (tag - port.tag)) * 256000000 / 48000;
 
 	return CELL_OK;
@@ -626,7 +630,7 @@ int cellAudioGetPortTimestamp(u32 portNum, u64 tag, mem64_t stamp)
 
 int cellAudioGetPortBlockTag(u32 portNum, u64 blockNo, mem64_t tag)
 {
-	cellAudio.Warning("cellAudioGetPortBlockTag(portNum=0x%x, blockNo=0x%llx, tag_addr=0x%x)", portNum, blockNo, tag.GetAddr());
+	cellAudio.Log("cellAudioGetPortBlockTag(portNum=0x%x, blockNo=0x%llx, tag_addr=0x%x)", portNum, blockNo, tag.GetAddr());
 
 	if (portNum >= m_config.AUDIO_PORT_COUNT) 
 	{
@@ -651,7 +655,8 @@ int cellAudioGetPortBlockTag(u32 portNum, u64 blockNo, mem64_t tag)
 		return CELL_AUDIO_ERROR_PARAM;
 	}
 
-	// TODO: atomic
+	SMutexLocker lock(port.m_mutex);
+
 	u64 tag_base = port.tag;
 	if (tag_base % port.block > blockNo)
 	{
@@ -709,13 +714,13 @@ int cellAudioSetNotifyEventQueue(u64 key)
 
 	m_config.event_key = key;
 
-	EventQueue* eq;
+	/*EventQueue* eq;
 	if (!Emu.GetEventManager().GetEventQueue(key, eq))
 	{
 		return CELL_AUDIO_ERROR_PARAM;
-	}
+	}*/
 
-	// TODO: connect port
+	// TODO: connect port (?????)
 
 	return CELL_OK;
 }
diff --git a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp
index 84e54578c2..aadfaf1a85 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp
+++ b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp
@@ -7,80 +7,646 @@
 void cellDmux_init();
 Module cellDmux(0x0007, cellDmux_init);
 
+void dmuxQueryAttr(u32 info_addr /* may be 0 */, mem_ptr_t<CellDmuxAttr> attr)
+{
+	attr->demuxerVerLower = 0x280000; // TODO: check values
+	attr->demuxerVerUpper = 0x260000;
+	attr->memSize = 0x10000; // 0x3e8e6 from ps3
+}
+
+void dmuxQueryEsAttr(u32 info_addr /* may be 0 */, const mem_ptr_t<CellCodecEsFilterId> esFilterId,
+					 const u32 esSpecificInfo_addr, mem_ptr_t<CellDmuxEsAttr> attr)
+{
+	if (esFilterId->filterIdMajor >= 0xe0)
+		attr->memSize = 0x600000; // 0x45fa49 from ps3
+	else
+		attr->memSize = 0x10000; // 0x73d9 from ps3
+
+	cellDmux.Warning("*** filter(0x%x, 0x%x, 0x%x, 0x%x)", (u32)esFilterId->filterIdMajor, (u32)esFilterId->filterIdMinor,
+		(u32)esFilterId->supplementalInfo1, (u32)esFilterId->supplementalInfo2);
+}
+
+u32 dmuxOpen(Demuxer* data)
+{
+	Demuxer& dmux = *data;
+
+	u32 dmux_id = cellDmux.GetNewId(data);
+
+	dmux.id = dmux_id;
+
+	thread t("Demuxer[" + std::to_string(dmux_id) + "] Thread", [&]()
+	{
+		ConLog.Write("Demuxer enter (mem=0x%x, size=0x%x, cb=0x%x, arg=0x%x)", dmux.memAddr, dmux.memSize, dmux.cbFunc, dmux.cbArg);
+
+		DemuxerTask task;
+		DemuxerStream stream;
+		ElementaryStream* esALL[192]; memset(esALL, 0, sizeof(esALL));
+		ElementaryStream** esAVC = &esALL[0]; // AVC (max 16)
+		ElementaryStream** esM2V = &esALL[16]; // MPEG-2 (max 16)
+		ElementaryStream** esDATA = &esALL[32]; // user data (max 16)
+		ElementaryStream** esATX = &esALL[48]; // ATRAC3+ (max 48)
+		ElementaryStream** esAC3 = &esALL[96]; // AC3 (max 48)
+		ElementaryStream** esPCM = &esALL[144]; // LPCM (max 48)
+
+		u32 cb_add = 0;
+
+		while (true)
+		{
+			if (Emu.IsStopped())
+			{
+				break;
+			}
+			
+			if (dmux.job.IsEmpty() && dmux.is_running)
+			{
+				// default task (demuxing) (if there is no other work)
+				be_t<u32> code;
+				be_t<u16> len;
+				u8 ch;
+
+				if (!stream.peek(code)) 
+				{
+					// demuxing finished
+					task.type = dmuxResetStream;
+					goto task;
+				}
+				else switch (code.ToLE())
+				{
+				case PACK_START_CODE:
+					{
+						stream.skip(14);
+					}
+					break;
+
+				case SYSTEM_HEADER_START_CODE:
+					{
+						stream.skip(18);
+					}
+					break;
+
+				case PADDING_STREAM:
+					{
+						stream.skip(4);
+						stream.get(len);
+						stream.skip(len);
+					}
+					break;
+
+				case PRIVATE_STREAM_2:
+					{
+						stream.skip(4);
+						stream.get(len);
+						stream.skip(len);
+					}
+					break;
+
+				case PRIVATE_STREAM_1:
+					{
+						// audio AT3+ (and probably LPCM or user data)
+						stream.skip(4);
+						stream.get(len);
+
+						// skipping...
+						stream.skip(len);
+					}
+					break;
+
+				case 0x1e0: case 0x1e1: case 0x1e2: case 0x1e3:
+				case 0x1e4: case 0x1e5: case 0x1e6: case 0x1e7:
+				case 0x1e8: case 0x1e9: case 0x1ea: case 0x1eb:
+				case 0x1ec: case 0x1ed: case 0x1ee: case 0x1ef:
+					{
+						// video AVC
+						ch = code - 0x1e0;
+						if (esAVC[ch])
+						{
+							ElementaryStream& es = *esAVC[ch];
+							if (es.isfull())
+							{
+								Sleep(1);
+								continue;
+							}
+
+							DemuxerStream backup = stream;
+
+							stream.skip(4);
+							stream.get(len);
+							PesHeader pes(stream);
+
+							if (!pes.new_au && !es.hasdata()) // fatal error
+							{
+								ConLog.Error("PES not found");
+								return;
+							}
+
+							if (pes.new_au && es.hasdata()) // new AU detected
+							{
+								if (es.hasunseen()) // hack, probably useless
+								{
+									stream = backup;
+									continue;
+								}
+								es.finish(stream);
+								// callback
+								mem_ptr_t<CellDmuxEsMsg> esMsg(a128(dmux.memAddr) + (cb_add ^= 16));
+								esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND;
+								esMsg->supplementalInfo = stream.userdata;
+								Callback cb;
+								cb.SetAddr(es.cbFunc);
+								cb.Handle(dmux.id, es.id, esMsg.GetAddr(), es.cbArg);
+								cb.Branch(false);
+							}
+
+							if (pes.new_au)
+							{
+								ConLog.Write("*** AVC AU detected (pts=0x%llx, dts=0x%llx)", pes.pts, pes.dts);
+							}
+
+							if (es.isfull())
+							{
+								stream = backup;
+								continue;
+							}
+							//stream = backup;
+							es.push(stream, len - pes.size - 3, pes);
+						}
+						else
+						{
+							stream.skip(4);
+							stream.get(len);
+							stream.skip(len);
+						}
+					}
+					break;
+
+				case 0x1c0: case 0x1c1: case 0x1c2: case 0x1c3:
+				case 0x1c4: case 0x1c5: case 0x1c6: case 0x1c7:
+				case 0x1c8: case 0x1c9: case 0x1ca: case 0x1cb:
+				case 0x1cc: case 0x1cd: case 0x1ce: case 0x1cf:
+				case 0x1d0: case 0x1d1: case 0x1d2: case 0x1d3:
+				case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
+				case 0x1d8: case 0x1d9: case 0x1da: case 0x1db:
+				case 0x1dc: case 0x1dd: case 0x1de: case 0x1df:
+					{
+						// unknown
+						ConLog.Warning("Unknown MPEG stream found");
+						stream.skip(4);
+						stream.get(len);
+						stream.skip(len);
+					}
+					break;
+
+				case USER_DATA_START_CODE:
+					{
+						ConLog.Error("USER_DATA_START_CODE found");
+						return;
+					}
+
+				default:
+					{
+						// search
+						stream.skip(1);
+					}
+					break;
+
+				}
+				continue;
+			}
+
+			// wait for task with yielding (if no default work)
+			if (!dmux.job.Pop(task))
+			{
+				break; // Emu is stopped
+			}
+task:
+			switch (task.type)
+			{
+			case dmuxSetStream:
+				{
+					bool do_wait = false;
+					for (u32 i = 0; i < 192; i++)
+					{
+						if (esALL[i])
+						{
+							if (esALL[i]->hasunseen()) // hack, probably useless
+							{
+								do_wait = true;
+								break;
+							}
+						}
+					}
+					if (do_wait) continue;
+					stream = task.stream;
+					ConLog.Write("*** stream updated(addr=0x%x, size=0x%x, discont=%d, userdata=0x%llx)",
+						stream.addr, stream.size, stream.discontinuity, stream.userdata);
+					if (stream.discontinuity) for (u32 i = 0; i < 192; i++)
+					{
+						if (esALL[i])
+						{
+							esALL[i]->reset();
+						}
+					}
+					dmux.is_running = true;
+				}
+				break;
+
+			case dmuxResetStream:
+			case dmuxResetStreamAndWaitDone:
+				{
+					// TODO: send CELL_DMUX_MSG_TYPE_DEMUX_DONE callback and provide waiting condition
+					mem_ptr_t<CellDmuxMsg> dmuxMsg(a128(dmux.memAddr) + (cb_add ^= 16));
+					dmuxMsg->msgType = CELL_DMUX_MSG_TYPE_DEMUX_DONE;
+					dmuxMsg->supplementalInfo = stream.userdata;
+					Callback cb;
+					cb.SetAddr(dmux.cbFunc);
+					cb.Handle(dmux.id, dmuxMsg.GetAddr(), dmux.cbArg);
+					cb.Branch(task.type == dmuxResetStreamAndWaitDone);
+					dmux.is_running = false;
+				}
+				break;
+
+			case dmuxClose:
+				{
+					dmux.is_finished = true;
+					ConLog.Write("Demuxer exit");
+					return;
+				}
+
+			case dmuxEnableEs:
+				{
+					ElementaryStream& es = *task.es.es_ptr;
+					if (es.fidMajor >= 0xe0 &&
+						es.fidMajor <= 0xef &&
+						es.fidMinor == 0 &&
+						es.sup1 == 1 &&
+						es.sup2 == 0)
+					{
+						esAVC[es.fidMajor - 0xe0] = task.es.es_ptr;
+					}
+					else
+					{
+						ConLog.Warning("dmuxEnableEs: (TODO) unsupported filter (0x%x, 0x%x, 0x%x, 0x%x)", es.fidMajor, es.fidMinor, es.sup1, es.sup2);
+					}
+					es.dmux = &dmux;
+				}
+				break;
+
+			case dmuxDisableEs:
+				{
+					ElementaryStream& es = *task.es.es_ptr;
+					if (es.dmux != &dmux)
+					{
+						ConLog.Warning("dmuxDisableEs: invalid elementary stream");
+						break;
+					}
+					for (u32 i = 0; i < 192; i++)
+					{
+						if (esALL[i] == &es)
+						{
+							esALL[i] = nullptr;
+						}
+					}
+					es.dmux = nullptr;
+					Emu.GetIdManager().RemoveID(task.es.es);
+				}
+				break;
+
+			case dmuxReleaseAu:
+				{
+					task.es.es_ptr->release();
+				}
+				break;
+
+			case dmuxFlushEs:
+				{
+					ElementaryStream& es = *task.es.es_ptr;
+
+					if (es.hasdata())
+					{
+						es.finish(stream);
+						// callback
+						mem_ptr_t<CellDmuxEsMsg> esMsg(a128(dmux.memAddr) + (cb_add ^= 16));
+						esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND;
+						esMsg->supplementalInfo = stream.userdata;
+						Callback cb;
+						cb.SetAddr(es.cbFunc);
+						cb.Handle(dmux.id, es.id, esMsg.GetAddr(), es.cbArg);
+						cb.Branch(false);
+					}
+
+					// callback
+					mem_ptr_t<CellDmuxEsMsg> esMsg(a128(dmux.memAddr) + (cb_add ^= 16));
+					esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE;
+					esMsg->supplementalInfo = stream.userdata;
+					Callback cb;
+					cb.SetAddr(es.cbFunc);
+					cb.Handle(dmux.id, es.id, esMsg.GetAddr(), es.cbArg);
+					cb.Branch(false);
+				}
+				break;
+
+			case dmuxResetEs:
+				{
+					task.es.es_ptr->reset();
+				}
+				break;
+
+			default:
+				ConLog.Error("Demuxer error: unknown task(%d)", task.type);
+				return;
+			}
+		}
+		ConLog.Warning("Demuxer aborted");
+	});
+
+	t.detach();
+
+	return dmux_id;
+}
+
 int cellDmuxQueryAttr(const mem_ptr_t<CellDmuxType> demuxerType, mem_ptr_t<CellDmuxAttr> demuxerAttr)
 {
-	cellDmux.Error("cellDmuxQueryAttr(demuxerType_addr=0x%x, demuxerAttr_addr=0x%x)", demuxerType.GetAddr(), demuxerAttr.GetAddr());
+	cellDmux.Warning("cellDmuxQueryAttr(demuxerType_addr=0x%x, demuxerAttr_addr=0x%x)", demuxerType.GetAddr(), demuxerAttr.GetAddr());
+
+	if (!demuxerType.IsGood() || !demuxerAttr.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (demuxerType->streamType != CELL_DMUX_STREAM_TYPE_PAMF)
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	dmuxQueryAttr(0, demuxerAttr);
 	return CELL_OK;
 }
 
 int cellDmuxQueryAttr2(const mem_ptr_t<CellDmuxType2> demuxerType2, mem_ptr_t<CellDmuxAttr> demuxerAttr)
 {
-	cellDmux.Error("cellDmuxQueryAttr2(demuxerType2_addr=0x%x, demuxerAttr_addr=0x%x)", demuxerType2.GetAddr(), demuxerAttr.GetAddr());
+	cellDmux.Warning("cellDmuxQueryAttr2(demuxerType2_addr=0x%x, demuxerAttr_addr=0x%x)", demuxerType2.GetAddr(), demuxerAttr.GetAddr());
+
+	if (!demuxerType2.IsGood() || !demuxerAttr.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (demuxerType2->streamType != CELL_DMUX_STREAM_TYPE_PAMF)
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	dmuxQueryAttr(demuxerType2->streamSpecificInfo_addr, demuxerAttr);
 	return CELL_OK;
 }
 
 int cellDmuxOpen(const mem_ptr_t<CellDmuxType> demuxerType, const mem_ptr_t<CellDmuxResource> demuxerResource, 
 				 const mem_ptr_t<CellDmuxCb> demuxerCb, mem32_t demuxerHandle)
 {
-	cellDmux.Error("cellDmuxOpen(demuxerType_addr=0x%x, demuxerResource_addr=0x%x, demuxerCb_addr=0x%x, demuxerHandle_addr=0x%x)",
+	cellDmux.Warning("cellDmuxOpen(demuxerType_addr=0x%x, demuxerResource_addr=0x%x, demuxerCb_addr=0x%x, demuxerHandle_addr=0x%x)",
 		demuxerType.GetAddr(), demuxerResource.GetAddr(), demuxerCb.GetAddr(), demuxerHandle.GetAddr());
+
+	if (!demuxerType.IsGood() || !demuxerResource.IsGood() || !demuxerCb.IsGood() || !demuxerHandle.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (demuxerType->streamType != CELL_DMUX_STREAM_TYPE_PAMF)
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	if (!Memory.IsGoodAddr(demuxerResource->memAddr, demuxerResource->memSize))
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	// TODO: check demuxerResource and demuxerCb arguments
+
+	demuxerHandle = dmuxOpen(new Demuxer(demuxerResource->memAddr, demuxerResource->memSize, demuxerCb->cbMsgFunc, demuxerCb->cbArg_addr));
+
 	return CELL_OK;
 }
 
 int cellDmuxOpenEx(const mem_ptr_t<CellDmuxType> demuxerType, const mem_ptr_t<CellDmuxResourceEx> demuxerResourceEx, 
 				   const mem_ptr_t<CellDmuxCb> demuxerCb, mem32_t demuxerHandle)
 {
-	cellDmux.Error("cellDmuxOpenEx(demuxerType_addr=0x%x, demuxerResourceEx_addr=0x%x, demuxerCb_addr=0x%x, demuxerHandle_addr=0x%x)",
+	cellDmux.Warning("cellDmuxOpenEx(demuxerType_addr=0x%x, demuxerResourceEx_addr=0x%x, demuxerCb_addr=0x%x, demuxerHandle_addr=0x%x)",
 		demuxerType.GetAddr(), demuxerResourceEx.GetAddr(), demuxerCb.GetAddr(), demuxerHandle.GetAddr());
+
+	if (!demuxerType.IsGood() || !demuxerResourceEx.IsGood() || !demuxerCb.IsGood() || !demuxerHandle.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (demuxerType->streamType != CELL_DMUX_STREAM_TYPE_PAMF)
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	if (!Memory.IsGoodAddr(demuxerResourceEx->memAddr, demuxerResourceEx->memSize))
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	// TODO: check demuxerResourceEx and demuxerCb arguments
+
+	demuxerHandle = dmuxOpen(new Demuxer(demuxerResourceEx->memAddr, demuxerResourceEx->memSize, demuxerCb->cbMsgFunc, demuxerCb->cbArg_addr));
+
 	return CELL_OK;
 }
 
 int cellDmuxOpen2(const mem_ptr_t<CellDmuxType2> demuxerType2, const mem_ptr_t<CellDmuxResource2> demuxerResource2, 
 				  const mem_ptr_t<CellDmuxCb> demuxerCb, mem32_t demuxerHandle)
 {
-	cellDmux.Error("cellDmuxOpen2(demuxerType2_addr=0x%x, demuxerResource2_addr=0x%x, demuxerCb_addr=0x%x, demuxerHandle_addr=0x%x)",
+	cellDmux.Warning("cellDmuxOpen2(demuxerType2_addr=0x%x, demuxerResource2_addr=0x%x, demuxerCb_addr=0x%x, demuxerHandle_addr=0x%x)",
 		demuxerType2.GetAddr(), demuxerResource2.GetAddr(), demuxerCb.GetAddr(), demuxerHandle.GetAddr());
+
+	if (!demuxerType2.IsGood() || !demuxerResource2.IsGood() || !demuxerCb.IsGood() || !demuxerHandle.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (demuxerType2->streamType != CELL_DMUX_STREAM_TYPE_PAMF)
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	if (!Memory.IsGoodAddr(demuxerResource2->memAddr, demuxerResource2->memSize))
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	// TODO: check demuxerType2, demuxerResource2 and demuxerCb arguments
+
+	demuxerHandle = dmuxOpen(new Demuxer(demuxerResource2->memAddr, demuxerResource2->memSize, demuxerCb->cbMsgFunc, demuxerCb->cbArg_addr));
+
 	return CELL_OK;
 }
 
 int cellDmuxClose(u32 demuxerHandle)
 {
-	cellDmux.Error("cellDmuxClose(demuxerHandle=0x%x)", demuxerHandle);
+	cellDmux.Warning("cellDmuxClose(demuxerHandle=%d)", demuxerHandle);
+
+	Demuxer* dmux;
+	if (!Emu.GetIdManager().GetIDData(demuxerHandle, dmux))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	dmux->job.Push(DemuxerTask(dmuxClose));
+
+	while (!dmux->is_finished)
+	{
+		if (Emu.IsStopped())
+		{
+			ConLog.Warning("cellDmuxClose(%d) aborted", demuxerHandle);
+			return CELL_OK;
+		}
+
+		Sleep(1);
+	}
+
+	Emu.GetIdManager().RemoveID(demuxerHandle);
 	return CELL_OK;
 }
 
 int cellDmuxSetStream(u32 demuxerHandle, const u32 streamAddress, u32 streamSize, bool discontinuity, u64 userData)
 {
-	cellDmux.Error("cellDmuxSetStream(demuxerHandle=0x%x, streamAddress=0x%x, streamSize=%d, discontinuity=%d, userData=0x%llx",
+	cellDmux.Log("cellDmuxSetStream(demuxerHandle=%d, streamAddress=0x%x, streamSize=%d, discontinuity=%d, userData=0x%llx",
 		demuxerHandle, streamAddress, streamSize, discontinuity, userData);
+
+	Demuxer* dmux;
+	if (!Emu.GetIdManager().GetIDData(demuxerHandle, dmux))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	if (!Memory.IsGoodAddr(streamAddress, streamSize))
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (dmux->is_running)
+	{
+		Sleep(1); // performance hack
+		return CELL_DMUX_ERROR_BUSY;
+	}
+
+	DemuxerTask task(dmuxSetStream);
+	auto& info = task.stream;
+	info.addr = streamAddress;
+	info.size = streamSize;
+	info.discontinuity = discontinuity;
+	info.userdata = userData;
+
+	dmux->job.Push(task);
+
+	while (!dmux->is_running)
+	{
+		if (Emu.IsStopped())
+		{
+			ConLog.Warning("cellDmuxSetStream(%d) aborted", demuxerHandle);
+			break;
+		}
+		Sleep(1);
+	}
 	return CELL_OK;
 }
 
 int cellDmuxResetStream(u32 demuxerHandle)
 {
-	cellDmux.Error("cellDmuxResetStream(demuxerHandle=0x%x)", demuxerHandle);
+	cellDmux.Log("cellDmuxResetStream(demuxerHandle=%d)", demuxerHandle);
+
+	Demuxer* dmux;
+	if (!Emu.GetIdManager().GetIDData(demuxerHandle, dmux))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	dmux->job.Push(DemuxerTask(dmuxResetStream));
+
 	return CELL_OK;
 }
 
 int cellDmuxResetStreamAndWaitDone(u32 demuxerHandle)
 {
-	cellDmux.Error("cellDmuxResetStreamAndWaitDone(demuxerHandle=0x%x)", demuxerHandle);
+	cellDmux.Log("cellDmuxResetStreamAndWaitDone(demuxerHandle=%d)", demuxerHandle);
+
+	Demuxer* dmux;
+	if (!Emu.GetIdManager().GetIDData(demuxerHandle, dmux))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	dmux->job.Push(DemuxerTask(dmuxResetStreamAndWaitDone));
+
+	while (dmux->is_running)
+	{
+		if (Emu.IsStopped())
+		{
+			ConLog.Warning("cellDmuxResetStreamAndWaitDone(%d) aborted", demuxerHandle);
+			break;
+		}
+		Sleep(1);
+	}
+
 	return CELL_OK;
 }
 
 int cellDmuxQueryEsAttr(const mem_ptr_t<CellDmuxType> demuxerType, const mem_ptr_t<CellCodecEsFilterId> esFilterId,
 						const u32 esSpecificInfo_addr, mem_ptr_t<CellDmuxEsAttr> esAttr)
 {
-	cellDmux.Error("cellDmuxQueryEsAttr(demuxerType_addr=0x%x, esFilterId_addr=0x%x, esSpecificInfo_addr=0x%x, esAttr_addr=0x%x)",
+	cellDmux.Warning("cellDmuxQueryEsAttr(demuxerType_addr=0x%x, esFilterId_addr=0x%x, esSpecificInfo_addr=0x%x, esAttr_addr=0x%x)",
 		demuxerType.GetAddr(), esFilterId.GetAddr(), esSpecificInfo_addr, esAttr.GetAddr());
+
+	if (!demuxerType.IsGood() || !esFilterId.IsGood() || !esAttr.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (!Memory.IsGoodAddr(esSpecificInfo_addr, 12))
+	{
+		cellDmux.Error("cellDmuxQueryEsAttr: invalid specific info addr (0x%x)", esSpecificInfo_addr);
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (demuxerType->streamType != CELL_DMUX_STREAM_TYPE_PAMF)
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	// TODO: check esFilterId and esSpecificInfo correctly
+
+	dmuxQueryEsAttr(0, esFilterId, esSpecificInfo_addr, esAttr);
 	return CELL_OK;
 }
 
 int cellDmuxQueryEsAttr2(const mem_ptr_t<CellDmuxType2> demuxerType2, const mem_ptr_t<CellCodecEsFilterId> esFilterId,
 						 const u32 esSpecificInfo_addr, mem_ptr_t<CellDmuxEsAttr> esAttr)
 {
-	cellDmux.Error("cellDmuxQueryEsAttr2(demuxerType2_addr=0x%x, esFilterId_addr=0x%x, esSpecificInfo_addr=0x%x, esAttr_addr=0x%x)",
+	cellDmux.Warning("cellDmuxQueryEsAttr2(demuxerType2_addr=0x%x, esFilterId_addr=0x%x, esSpecificInfo_addr=0x%x, esAttr_addr=0x%x)",
 		demuxerType2.GetAddr(), esFilterId.GetAddr(), esSpecificInfo_addr, esAttr.GetAddr());
+
+	if (!demuxerType2.IsGood() || !esFilterId.IsGood() || !esAttr.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (!Memory.IsGoodAddr(esSpecificInfo_addr, 12))
+	{
+		cellDmux.Error("cellDmuxQueryEsAttr2: invalid specific info addr (0x%x)", esSpecificInfo_addr);
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (demuxerType2->streamType != CELL_DMUX_STREAM_TYPE_PAMF)
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	// TODO: check demuxerType2, esFilterId and esSpecificInfo correctly
+
+	dmuxQueryEsAttr(demuxerType2->streamSpecificInfo_addr, esFilterId, esSpecificInfo_addr, esAttr);
 	return CELL_OK;
 }
 
@@ -88,61 +654,243 @@ int cellDmuxEnableEs(u32 demuxerHandle, const mem_ptr_t<CellCodecEsFilterId> esF
 					 const mem_ptr_t<CellDmuxEsResource> esResourceInfo, const mem_ptr_t<CellDmuxEsCb> esCb,
 					 const u32 esSpecificInfo_addr, mem32_t esHandle)
 {
-	cellDmux.Error("cellDmuxEnableEs(demuxerHandle=0x%x, esFilterId_addr=0x%x, esResourceInfo_addr=0x%x, esCb_addr=0x%x, "
+	cellDmux.Warning("cellDmuxEnableEs(demuxerHandle=%d, esFilterId_addr=0x%x, esResourceInfo_addr=0x%x, esCb_addr=0x%x, "
 		"esSpecificInfo_addr=0x%x, esHandle_addr=0x%x)", demuxerHandle, esFilterId.GetAddr(), esResourceInfo.GetAddr(),
 		esCb.GetAddr(), esSpecificInfo_addr, esHandle.GetAddr());
+
+	if (!esFilterId.IsGood() || !esResourceInfo.IsGood() || !esCb.IsGood() || !esHandle.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (!Memory.IsGoodAddr(esSpecificInfo_addr, 12))
+	{
+		cellDmux.Error("cellDmuxEnableEs: invalid specific info addr (0x%x)", esSpecificInfo_addr);
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	if (!Memory.IsGoodAddr(esResourceInfo->memAddr, esResourceInfo->memSize))
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	Demuxer* dmux;
+	if (!Emu.GetIdManager().GetIDData(demuxerHandle, dmux))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	// TODO: check esFilterId, esResourceInfo, esCb and esSpecificInfo correctly
+
+	ElementaryStream* es = new ElementaryStream(dmux, esResourceInfo->memAddr, esResourceInfo->memSize,
+		esFilterId->filterIdMajor, esFilterId->filterIdMinor, esFilterId->supplementalInfo1, esFilterId->supplementalInfo2,
+		esCb->cbEsMsgFunc, esCb->cbArg_addr, esSpecificInfo_addr);
+
+	u32 id = cellDmux.GetNewId(es);
+	es->id = id;
+	esHandle = id;
+
+	cellDmux.Warning("*** New ES(dmux=%d, addr=0x%x, size=0x%x, filter(0x%x, 0x%x, 0x%x, 0x%x), cb=0x%x(arg=0x%x), spec=0x%x): id = %d",
+		demuxerHandle, es->memAddr, es->memSize, es->fidMajor, es->fidMinor, es->sup1, es->sup2, (u32)esCb->cbEsMsgFunc, es->cbArg, es->spec, id);
+
+	DemuxerTask task(dmuxEnableEs);
+	task.es.es = id;
+	task.es.es_ptr = es;
+
+	dmux->job.Push(task);
 	return CELL_OK;
 }
 
 int cellDmuxDisableEs(u32 esHandle)
 {
-	cellDmux.Error("cellDmuxDisableEs(esHandle=0x%x)", esHandle);
+	cellDmux.Warning("cellDmuxDisableEs(esHandle=0x%x)", esHandle);
+
+	ElementaryStream* es;
+	if (!Emu.GetIdManager().GetIDData(esHandle, es))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	DemuxerTask task(dmuxDisableEs);
+	task.es.es = esHandle;
+	task.es.es_ptr = es;
+
+	es->dmux->job.Push(task);
 	return CELL_OK;
 }
 
 int cellDmuxResetEs(u32 esHandle)
 {
-	cellDmux.Error("cellDmuxResetEs(esHandle=0x%x)", esHandle);
+	cellDmux.Log("cellDmuxResetEs(esHandle=0x%x)", esHandle);
+
+	ElementaryStream* es;
+	if (!Emu.GetIdManager().GetIDData(esHandle, es))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	DemuxerTask task(dmuxResetEs);
+	task.es.es = esHandle;
+	task.es.es_ptr = es;
+
+	es->dmux->job.Push(task);
 	return CELL_OK;
 }
 
-int cellDmuxGetAu(u32 esHandle, const u32 auInfo_ptr_addr, u32 auSpecificInfo_ptr_addr)
+int cellDmuxGetAu(u32 esHandle, mem32_t auInfo_ptr, mem32_t auSpecificInfo_ptr)
 {
-	cellDmux.Error("cellDmuxGetAu(esHandle=0x%x, auInfo_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
-		esHandle, auInfo_ptr_addr, auSpecificInfo_ptr_addr);
+	cellDmux.Log("cellDmuxGetAu(esHandle=0x%x, auInfo_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
+		esHandle, auInfo_ptr.GetAddr(), auSpecificInfo_ptr.GetAddr());
+
+	ElementaryStream* es;
+	if (!Emu.GetIdManager().GetIDData(esHandle, es))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	if (!auInfo_ptr.IsGood() || !auSpecificInfo_ptr.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	u32 info;
+	u32 spec;
+	if (!es->peek(info, true, spec, true))
+	{
+		return CELL_DMUX_ERROR_EMPTY;
+	}
+
+	auInfo_ptr = info;
+	auSpecificInfo_ptr = spec;
 	return CELL_OK;
 }
 
-int cellDmuxPeekAu(u32 esHandle, const u32 auInfo_ptr_addr, u32 auSpecificInfo_ptr_addr)
+int cellDmuxPeekAu(u32 esHandle, mem32_t auInfo_ptr, mem32_t auSpecificInfo_ptr)
 {
-	cellDmux.Error("cellDmuxPeekAu(esHandle=0x%x, auInfo_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
-		esHandle, auInfo_ptr_addr, auSpecificInfo_ptr_addr);
+	cellDmux.Log("cellDmuxPeekAu(esHandle=0x%x, auInfo_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
+		esHandle, auInfo_ptr.GetAddr(), auSpecificInfo_ptr.GetAddr());
+
+	ElementaryStream* es;
+	if (!Emu.GetIdManager().GetIDData(esHandle, es))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	if (!auInfo_ptr.IsGood() || !auSpecificInfo_ptr.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	u32 info;
+	u32 spec;
+	if (!es->peek(info, true, spec, false))
+	{
+		return CELL_DMUX_ERROR_EMPTY;
+	}
+
+	auInfo_ptr = info;
+	auSpecificInfo_ptr = spec;
 	return CELL_OK;
 }
 
-int cellDmuxGetAuEx(u32 esHandle, const u32 auInfoEx_ptr_addr, u32 auSpecificInfo_ptr_addr)
+int cellDmuxGetAuEx(u32 esHandle, mem32_t auInfoEx_ptr, mem32_t auSpecificInfo_ptr)
 {
-	cellDmux.Error("cellDmuxGetAuEx(esHandle=0x%x, auInfoEx_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
-		esHandle, auInfoEx_ptr_addr, auSpecificInfo_ptr_addr);
+	cellDmux.Log("cellDmuxGetAuEx(esHandle=0x%x, auInfoEx_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
+		esHandle, auInfoEx_ptr.GetAddr(), auSpecificInfo_ptr.GetAddr());
+
+	ElementaryStream* es;
+	if (!Emu.GetIdManager().GetIDData(esHandle, es))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	if (!auInfoEx_ptr.IsGood() || !auSpecificInfo_ptr.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	u32 info;
+	u32 spec;
+	if (!es->peek(info, false, spec, true))
+	{
+		return CELL_DMUX_ERROR_EMPTY;
+	}
+
+	auInfoEx_ptr = info;
+	auSpecificInfo_ptr = spec;
 	return CELL_OK;
 }
 
-int cellDmuxPeekAuEx(u32 esHandle, const u32 auInfoEx_ptr_addr, u32 auSpecificInfo_ptr_addr)
+int cellDmuxPeekAuEx(u32 esHandle, mem32_t auInfoEx_ptr, mem32_t auSpecificInfo_ptr)
 {
-	cellDmux.Error("cellDmuxPeekAuEx(esHandle=0x%x, auInfoEx_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
-		esHandle, auInfoEx_ptr_addr, auSpecificInfo_ptr_addr);
+	cellDmux.Log("cellDmuxPeekAuEx(esHandle=0x%x, auInfoEx_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
+		esHandle, auInfoEx_ptr.GetAddr(), auSpecificInfo_ptr.GetAddr());
+
+	ElementaryStream* es;
+	if (!Emu.GetIdManager().GetIDData(esHandle, es))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	if (!auInfoEx_ptr.IsGood() || !auSpecificInfo_ptr.IsGood())
+	{
+		return CELL_DMUX_ERROR_FATAL;
+	}
+
+	u32 info;
+	u32 spec;
+	if (!es->peek(info, false, spec, false))
+	{
+		return CELL_DMUX_ERROR_EMPTY;
+	}
+
+	auInfoEx_ptr = info;
+	auSpecificInfo_ptr = spec;
 	return CELL_OK;
 }
 
 int cellDmuxReleaseAu(u32 esHandle)
 {
-	cellDmux.Error("cellDmuxReleaseAu(esHandle=0x%x)", esHandle);
+	cellDmux.Warning("(disabled) cellDmuxReleaseAu(esHandle=0x%x)", esHandle);
+
+	return CELL_OK;
+
+	ElementaryStream* es;
+	if (!Emu.GetIdManager().GetIDData(esHandle, es))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	if (!es->canrelease())
+	{
+		cellDmux.Error("cellDmuxReleaseAu: no AU");
+		return CELL_DMUX_ERROR_SEQ;
+		//return CELL_OK;
+	}
+
+	DemuxerTask task(dmuxReleaseAu);
+	task.es.es = esHandle;
+	task.es.es_ptr = es;
+
+	es->dmux->job.Push(task);
 	return CELL_OK;
 }
 
 int cellDmuxFlushEs(u32 esHandle)
 {
-	cellDmux.Error("cellDmuxFlushEs(esHandle=0x%x)", esHandle);
+	cellDmux.Log("cellDmuxFlushEs(esHandle=0x%x)", esHandle);
+
+	ElementaryStream* es;
+	if (!Emu.GetIdManager().GetIDData(esHandle, es))
+	{
+		return CELL_DMUX_ERROR_ARG;
+	}
+
+	DemuxerTask task(dmuxFlushEs);
+	task.es.es = esHandle;
+	task.es.es_ptr = es;
+
+	es->dmux->job.Push(task);
 	return CELL_OK;
 }
 
diff --git a/rpcs3/Emu/SysCalls/Modules/cellDmux.h b/rpcs3/Emu/SysCalls/Modules/cellDmux.h
index 9fa0067ee0..51e805ab07 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellDmux.h
+++ b/rpcs3/Emu/SysCalls/Modules/cellDmux.h
@@ -1,5 +1,10 @@
 #pragma once
 
+#include "Utilities/SQueue.h"
+
+// align size or address to 128
+#define a128(x) ((x + 127) & (~127))
+
 // Error Codes
 enum
 {
@@ -30,6 +35,118 @@ enum CellDmuxEsMsgType
 	CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE = 1,
 };
 
+enum CellDmuxPamfM2vLevel
+{
+	CELL_DMUX_PAMF_M2V_MP_LL = 0,
+	CELL_DMUX_PAMF_M2V_MP_ML,
+	CELL_DMUX_PAMF_M2V_MP_H14,
+	CELL_DMUX_PAMF_M2V_MP_HL,
+};
+
+enum CellDmuxPamfAvcLevel
+{
+	CELL_DMUX_PAMF_AVC_LEVEL_2P1 = 21,
+	CELL_DMUX_PAMF_AVC_LEVEL_3P0 = 30,
+	CELL_DMUX_PAMF_AVC_LEVEL_3P1 = 31,
+	CELL_DMUX_PAMF_AVC_LEVEL_3P2 = 32,
+	CELL_DMUX_PAMF_AVC_LEVEL_4P1 = 41,
+	CELL_DMUX_PAMF_AVC_LEVEL_4P2 = 42,
+};
+
+struct CellDmuxPamfAuSpecificInfoM2v
+{
+	be_t<u32> reserved1;
+};
+
+struct CellDmuxPamfAuSpecificInfoAvc
+{
+	be_t<u32> reserved1;
+};
+
+struct CellDmuxPamfAuSpecificInfoLpcm
+{
+	u8 channelAssignmentInfo;
+	u8 samplingFreqInfo;
+	u8 bitsPerSample;
+};
+
+struct CellDmuxPamfAuSpecificInfoAc3
+{
+	be_t<u32> reserved1;
+};
+
+struct CellDmuxPamfAuSpecificInfoAtrac3plus
+{
+	be_t<u32> reserved1;
+};
+
+struct CellDmuxPamfAuSpecificInfoUserData
+{
+	be_t<u32> reserved1;
+};
+
+struct CellDmuxPamfEsSpecificInfoM2v
+{
+	be_t<u32> profileLevel;
+};
+
+struct CellDmuxPamfEsSpecificInfoAvc
+{
+	be_t<u32> level;
+};
+
+struct CellDmuxPamfEsSpecificInfoLpcm
+{
+	be_t<u32> samplingFreq;
+	be_t<u32> numOfChannels;
+	be_t<u32> bitsPerSample;
+};
+
+struct CellDmuxPamfEsSpecificInfoAc3
+{
+	be_t<u32> reserved1;
+};
+
+struct CellDmuxPamfEsSpecificInfoAtrac3plus
+{
+	be_t<u32> reserved1;
+};
+
+struct CellDmuxPamfEsSpecificInfoUserData
+{
+	be_t<u32> reserved1;
+};
+
+enum CellDmuxPamfSamplingFrequency
+{
+	CELL_DMUX_PAMF_FS_48K = 48000,
+};
+
+enum CellDmuxPamfBitsPerSample
+{
+	CELL_DMUX_PAMF_BITS_PER_SAMPLE_16 = 16,
+	CELL_DMUX_PAMF_BITS_PER_SAMPLE_24 = 24,
+};
+
+enum CellDmuxPamfLpcmChannelAssignmentInfo
+{
+	CELL_DMUX_PAMF_LPCM_CH_M1 = 1,
+	CELL_DMUX_PAMF_LPCM_CH_LR = 3,
+	CELL_DMUX_PAMF_LPCM_CH_LRCLSRSLFE = 9,
+	CELL_DMUX_PAMF_LPCM_CH_LRCLSCS1CS2RSLFE = 11,
+};
+
+enum CellDmuxPamfLpcmFs
+{
+	CELL_DMUX_PAMF_LPCM_FS_48K = 1,
+};
+
+enum CellDmuxPamfLpcmBitsPerSamples
+{
+	CELL_DMUX_PAMF_LPCM_BITS_PER_SAMPLE_16 = 1,
+	CELL_DMUX_PAMF_LPCM_BITS_PER_SAMPLE_24 = 3,
+};
+
 struct CellDmuxMsg
 {
 	be_t<CellDmuxMsgType> msgType; //CellDmuxMsgType enum
@@ -44,13 +161,19 @@ struct CellDmuxEsMsg
 
 struct CellDmuxType 
 {
-	CellDmuxStreamType streamType;
+	be_t<CellDmuxStreamType> streamType;
 	be_t<u32> reserved[2]; //0
 };
 
+struct CellDmuxPamfSpecificInfo
+{
+	be_t<u32> thisSize;
+	bool programEndCodeCb;
+};
+
 struct CellDmuxType2
 {
-	CellDmuxStreamType streamType;
+	be_t<CellDmuxStreamType> streamType;
 	be_t<u32> streamSpecificInfo_addr;
 };
 
@@ -99,17 +222,21 @@ struct CellDmuxResource2
 	be_t<u32> shit[4];
 };
 
+typedef mem_func_ptr_t<void (*)(u32 demuxerHandle, mem_ptr_t<CellDmuxMsg> demuxerMsg, u32 cbArg_addr)> CellDmuxCbMsg;
+
 struct CellDmuxCb
 {
 	// CellDmuxCbMsg callback
-	be_t<mem_func_ptr_t<void (*)(u32 demuxerHandle_addr, mem_ptr_t<CellDmuxMsg> demuxerMsg, u32 cbArg_addr)>> cbMsgFunc; 
+	be_t<u32> cbMsgFunc; 
 	be_t<u32> cbArg_addr;
 };
 
+typedef mem_func_ptr_t<void (*)(u32 demuxerHandle, u32 esHandle, mem_ptr_t<CellDmuxEsMsg> esMsg, u32 cbArg_addr)> CellDmuxCbEsMsg;
+
 struct CellDmuxEsCb
 {
 	// CellDmuxCbEsMsg callback
-	be_t<mem_func_ptr_t<void (*)(u32 demuxerHandle_addr, u32 esHandle_addr, mem_ptr_t<CellDmuxEsMsg> esMsg, u32 cbArg_addr)>> cbEsMsgFunc;
+	be_t<u32> cbEsMsgFunc;
 	be_t<u32> cbArg_addr;
 };
 
@@ -153,3 +280,414 @@ struct CellDmuxAuInfoEx
 	CellCodecTimeStamp pts;
 	CellCodecTimeStamp dts;
 };
+
+/* Demuxer Thread Classes */
+
+enum
+{
+	/* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html */
+
+	PACKET_START_CODE_MASK   = 0xffffff00,
+	PACKET_START_CODE_PREFIX = 0x00000100,
+
+	USER_DATA_START_CODE     = 0x000001b2,
+	SEQUENCE_START_CODE      = 0x000001b3,
+	EXT_START_CODE           = 0x000001b5,
+	SEQUENCE_END_CODE        = 0x000001b7,
+	GOP_START_CODE           = 0x000001b8,
+	ISO_11172_END_CODE       = 0x000001b9,
+	PACK_START_CODE          = 0x000001ba,
+	SYSTEM_HEADER_START_CODE = 0x000001bb,
+	PROGRAM_STREAM_MAP       = 0x000001bc,
+	PRIVATE_STREAM_1         = 0x000001bd,
+	PADDING_STREAM           = 0x000001be,
+	PRIVATE_STREAM_2         = 0x000001bf,
+};
+
+enum
+{
+	MAX_AU = 640 * 1024 + 128, // 640 KB
+};
+
+struct DemuxerStream
+{
+	u32 addr;
+	u32 size;
+	u64 userdata;
+	bool discontinuity;
+
+	template<typename T>
+	bool get(T& out)
+	{
+		if (sizeof(T) > size) return false;
+
+		out = *(T*)Memory.VirtualToRealAddr(addr);
+		addr += sizeof(T);
+		size -= sizeof(T);
+
+		return true;
+	}
+
+	template<typename T>
+	bool peek(T& out)
+	{
+		if (sizeof(T) > size) return false;
+
+		out = *(T*)Memory.VirtualToRealAddr(addr);
+		return true;
+	}
+
+	void skip(u32 count)
+	{
+		addr += count;
+		size = size > count ? size - count : 0;
+	}
+
+	u64 get_ts(u8 c)
+	{
+		u8 v[4]; get((u32&)v); 
+		return
+			(((u64)c & 0x0e) << 29) | 
+			(((u64)v[0]) << 21) |
+			(((u64)v[1] & 0x7e) << 15) |
+			(((u64)v[2]) << 7) | ((u64)v[3] >> 1);
+	}
+
+	u64 get_ts()
+	{
+		u8 v; get(v);
+		return get_ts(v);
+	}
+};
+
+struct PesHeader
+{
+	u64 pts;
+	u64 dts;
+	u8 ch;
+	u8 size;
+	bool new_au;
+
+	PesHeader(DemuxerStream& stream)
+		: pts(0xffffffffffffffff)
+		, dts(0xffffffffffffffff)
+		, ch(0)
+		, size(0)
+		, new_au(true)
+	{
+		u16 header;
+		stream.get(header);
+		stream.get(size);
+		if (size)
+		{
+			//ConLog.Write(">>>>> Pes Header (size=%d)", size);
+			if (size < 10)
+			{
+				stream.skip(size);
+				return;
+			}
+			new_au = true;
+			u8 v;
+			stream.get(v);
+			if ((v & 0xF0) != 0x30)
+			{
+				ConLog.Error("Pts not found");
+				Emu.Pause();
+			}
+			pts = stream.get_ts(v);
+			stream.get(v);
+			if ((v & 0xF0) != 0x10)
+			{
+				ConLog.Error("Dts not found");
+				Emu.Pause();				
+			}
+			dts = stream.get_ts(v);
+			stream.skip(size - 10);
+		}
+	}
+};
+
+class ElementaryStream;
+
+enum DemuxerJobType
+{
+	dmuxSetStream,
+	dmuxResetStream,
+	dmuxResetStreamAndWaitDone,
+	dmuxEnableEs,
+	dmuxDisableEs,
+	dmuxResetEs,
+	dmuxReleaseAu,
+	dmuxFlushEs,
+	dmuxClose,
+};
+
+struct DemuxerTask
+{
+	DemuxerJobType type;
+
+	union
+	{
+		DemuxerStream stream;
+
+		struct
+		{
+			u32 es;
+			u32 auInfo_ptr_addr;
+			u32 auSpec_ptr_addr;
+			ElementaryStream* es_ptr;
+		} es;
+	};
+
+	DemuxerTask()
+	{
+	}
+
+	DemuxerTask(DemuxerJobType type)
+		: type(type)
+	{
+	}
+};
+
+class Demuxer
+{
+public:
+	SQueue<DemuxerTask> job;
+	const u32 memAddr;
+	const u32 memSize;
+	const u32 cbFunc;
+	const u32 cbArg;
+	u32 id;
+	volatile bool is_finished;
+	volatile bool is_running;
+
+
+	Demuxer(u32 addr, u32 size, u32 func, u32 arg)
+		: is_finished(false)
+		, is_running(false)
+		, memAddr(addr)
+		, memSize(size)
+		, cbFunc(func)
+		, cbArg(arg)
+	{
+	}
+};
+
+class ElementaryStream
+{
+	SMutex mutex;
+
+	u32 first_addr; // AU that will be released
+	u32 last_addr; // AU that is being written now
+	u32 last_size; // number of bytes written (after 128b header)
+	u32 peek_addr; // AU that will be obtained by GetAu(Ex)/PeekAu(Ex)
+	
+public:
+	Demuxer* dmux;
+	u32 id;
+	const u32 memAddr;
+	const u32 memSize;
+	const u32 fidMajor;
+	const u32 fidMinor;
+	const u32 sup1;
+	const u32 sup2;
+	const u32 cbFunc;
+	const u32 cbArg;
+	const u32 spec; //addr
+
+	ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, u32 cbFunc, u32 cbArg, u32 spec)
+		: dmux(dmux)
+		, memAddr(addr)
+		, memSize(size)
+		, fidMajor(fidMajor)
+		, fidMinor(fidMinor)
+		, sup1(sup1)
+		, sup2(sup2)
+		, cbFunc(cbFunc)
+		, cbArg(cbArg)
+		, spec(spec)
+		, first_addr(0)
+		, peek_addr(0)
+		, last_addr(a128(addr))
+		, last_size(0)
+	{
+	}
+
+	volatile bool hasunseen()
+	{
+		return peek_addr;
+	}
+
+	volatile bool hasdata()
+	{
+		return last_size;
+	}
+
+	bool isfull() // not multithread-safe
+	{
+		if (first_addr)
+		{
+			if (first_addr > last_addr)
+			{
+				return (first_addr - last_addr) < MAX_AU;
+			}
+			else
+			{
+				return (first_addr + MAX_AU) > (memAddr + memSize);
+			}
+		}
+		else
+		{
+			return false;
+		}
+	}
+
+	void finish(DemuxerStream& stream) // not multithread-safe
+	{
+		SMutexLocker lock(mutex);
+		//ConLog.Write("es::finish(): peek=0x%x, first=0x%x, last=0x%x, size=0x%x", peek_addr, first_addr, last_addr, last_size);
+		if (!first_addr)
+		{
+			first_addr = last_addr;
+		}
+		if (!peek_addr)
+		{
+			peek_addr = last_addr;
+		}
+		u32 new_addr = a128(last_addr + 128 + last_size);
+		if ((new_addr + MAX_AU) > (memAddr + memSize))
+		{
+			last_addr = memAddr;
+		}
+		else
+		{
+			last_addr = new_addr;
+		}
+		last_size = 0;
+	}
+
+	void push(DemuxerStream& stream, u32 size, PesHeader& pes)
+	{
+		SMutexLocker lock(mutex);
+		//ConLog.Write("es::push(): peek=0x%x, first=0x%x, last=0x%x, size=0x%x", peek_addr, first_addr, last_addr, last_size);
+		if (isfull())
+		{
+			ConLog.Error("ElementaryStream::push(): buffer is full");
+			Emu.Pause();
+			return;
+		}
+
+		u32 data_addr = last_addr + 128 + last_size;
+		last_size += size;
+		if (!Memory.Copy(data_addr, stream.addr, size))
+		{
+			ConLog.Error("ElementaryStream::push(): data copying failed");
+			Emu.Pause();
+			return;
+		}
+		stream.skip(size);
+
+		mem_ptr_t<CellDmuxAuInfoEx> info(last_addr);
+		info->auAddr = last_addr + 128;
+		info->auSize = last_size;
+		if (pes.size)
+		{
+			info->dts.lower = (u32)pes.dts;
+			info->dts.upper = (u32)(pes.dts >> 32);
+			info->pts.lower = (u32)pes.pts;
+			info->pts.upper = (u32)(pes.pts >> 32);
+			info->isRap = false; // TODO: set valid value
+			info->reserved = 0;
+			info->userData = stream.userdata;
+		}
+
+		mem_ptr_t<CellDmuxPamfAuSpecificInfoAvc> tail(last_addr + sizeof(CellDmuxAuInfoEx));
+		tail->reserved1 = 0;
+
+		mem_ptr_t<CellDmuxAuInfo> inf(last_addr + 64);
+		inf->auAddr = last_addr + 128;
+		inf->auSize = last_size;
+		if (pes.size)
+		{
+			inf->dtsLower = (u32)pes.dts;
+			inf->dtsUpper = (u32)(pes.dts >> 32);
+			inf->ptsLower = (u32)pes.pts;
+			inf->ptsUpper = (u32)(pes.pts >> 32);
+			inf->auMaxSize = 0; // ?????
+			inf->userData = stream.userdata;
+		}
+	}
+
+	volatile bool canrelease()
+	{
+		return first_addr;
+	}
+
+	void release()
+	{
+		SMutexLocker lock(mutex);
+		ConLog.Write("es::release(): peek=0x%x, first=0x%x, last=0x%x, size=0x%x", peek_addr, first_addr, last_addr, last_size);
+		if (!canrelease())
+		{
+			ConLog.Error("ElementaryStream::release(): buffer is empty");
+			Emu.Pause();
+			return;
+		}
+
+		u32 size = a128(Memory.Read32(first_addr + 4) + 128);
+		u32 new_addr = first_addr + size;
+		if (peek_addr <= first_addr) peek_addr = new_addr;
+		if (new_addr == last_addr)
+		{
+			first_addr = 0;
+		}
+		else if ((new_addr + MAX_AU) > (memAddr + memSize))
+		{
+			first_addr = memAddr;
+		}
+		else
+		{
+			first_addr = new_addr;
+		}
+	}
+
+	bool peek(u32& out_data, bool no_ex, u32& out_spec, bool update_index)
+	{
+		SMutexLocker lock(mutex);
+		/*ConLog.Write("es::peek(%sAu%s): peek=0x%x, first=0x%x, last=0x%x, size=0x%x", wxString(update_index ? "Get" : "Peek").wx_str(), 
+			wxString(no_ex ? "" : "Ex").wx_str(), peek_addr, first_addr, last_addr, last_size);*/
+		if (!peek_addr) return false;
+
+		out_data = peek_addr;
+		out_spec = out_data + sizeof(CellDmuxAuInfoEx);
+		if (no_ex) out_data += 64;
+
+		if (update_index)
+		{
+			u32 size = a128(Memory.Read32(peek_addr + 4) + 128);
+			u32 new_addr = peek_addr + size;
+			if (new_addr = last_addr)
+			{
+				peek_addr = 0;
+			}
+			else if ((new_addr + MAX_AU) > (memAddr + memSize))
+			{
+				peek_addr = memAddr;
+			}
+			else
+			{
+				peek_addr = new_addr;
+			}
+		}
+		return true;
+	}
+
+	void reset()
+	{
+		SMutexLocker lock(mutex);
+		first_addr = 0;
+		peek_addr = 0;
+		last_addr = a128(memAddr);
+		last_size = 0;
+	}
+};
diff --git a/rpcs3/Emu/SysCalls/Modules/cellGifDec.cpp b/rpcs3/Emu/SysCalls/Modules/cellGifDec.cpp
index 106725a816..b86104187c 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellGifDec.cpp
+++ b/rpcs3/Emu/SysCalls/Modules/cellGifDec.cpp
@@ -164,7 +164,7 @@ int cellGifDecDecodeData(u32 mainHandle, u32 subHandle, mem8_ptr_t data, const m
 	switch(current_outParam.outputColorSpace)
 	{
 	case CELL_GIFDEC_RGBA:
-		memcpy(data, image.get(), image_size);
+		Memory.CopyFromReal(data.GetAddr(), image.get(), image_size);
 	break;
 
 	case CELL_GIFDEC_ARGB:
diff --git a/rpcs3/Emu/SysCalls/Modules/cellJpgDec.cpp b/rpcs3/Emu/SysCalls/Modules/cellJpgDec.cpp
index 3b78ca94da..45e3ac79fb 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellJpgDec.cpp
+++ b/rpcs3/Emu/SysCalls/Modules/cellJpgDec.cpp
@@ -148,7 +148,7 @@ int cellJpgDecDecodeData(u32 mainHandle, u32 subHandle, mem8_ptr_t data, const m
 	case CELL_JPG_RGBA:
 	case CELL_JPG_RGB:
 		image_size *= current_outParam.outputColorSpace == CELL_JPG_RGBA ? 4 : 3;
-		memcpy(data, image.get(), image_size);
+		Memory.CopyFromReal(data.GetAddr(), image.get(), image_size);
 	break;
 
 	case CELL_JPG_ARGB:
diff --git a/rpcs3/Emu/SysCalls/Modules/cellPamf.cpp b/rpcs3/Emu/SysCalls/Modules/cellPamf.cpp
index 1559788875..6f5284e061 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellPamf.cpp
+++ b/rpcs3/Emu/SysCalls/Modules/cellPamf.cpp
@@ -17,26 +17,16 @@ int pamfStreamTypeToEsFilterId(u8 type, u8 ch, mem_ptr_t<CellCodecEsFilterId> pE
 	switch (type)
 	{
 	case CELL_PAMF_STREAM_TYPE_AVC:
-		switch (ch)
 		{
-		case 0:
+			if (ch < 16)
 			{
-				pEsFilterId->filterIdMajor = 0xe0; //fake info
+				pEsFilterId->filterIdMajor = 0xe0 + ch;
 				pEsFilterId->filterIdMinor = 0;
 				pEsFilterId->supplementalInfo1 = 0x01;
 				pEsFilterId->supplementalInfo2 = 0;
 			}
-			break;
-		case 1:
-			{
-				pEsFilterId->filterIdMajor = 0xe1;
-				pEsFilterId->filterIdMinor = 0;
-				pEsFilterId->supplementalInfo1 = 0x01;
-				pEsFilterId->supplementalInfo2 = 0;
-			}
-			break;
-		default:
-			cellPamf.Error("*** TODO: pamfStreamTypeToEsFilterId: CELL_PAMF_STREAM_TYPE_AVC (ch=%d)", ch);
+			else
+				cellPamf.Error("pamfStreamTypeToEsFilterId: invalid CELL_PAMF_STREAM_TYPE_AVC channel (ch=%d)", ch);
 		}
 		break;
 	case CELL_PAMF_STREAM_TYPE_ATRAC3PLUS:
@@ -96,7 +86,7 @@ u8 pamfGetStreamType(mem_ptr_t<CellPamfReader> pSelf, u8 stream)
 	case 0x80: return CELL_PAMF_STREAM_TYPE_PAMF_LPCM;
 	case 0xdd: return CELL_PAMF_STREAM_TYPE_USER_DATA;
 	default:
-		cellPamf.Error("pamfGetStreamType: unsupported stream type found(0x%x)",
+		cellPamf.Error("pamfGetStreamType: (TODO) unsupported stream type found(0x%x)",
 			pAddr->stream_headers[stream].type);
 		return 0;
 	}
@@ -104,12 +94,16 @@ u8 pamfGetStreamType(mem_ptr_t<CellPamfReader> pSelf, u8 stream)
 
 u8 pamfGetStreamChannel(mem_ptr_t<CellPamfReader> pSelf, u8 stream)
 {
-	cellPamf.Warning("TODO: pamfGetStreamChannel");
 	//TODO: get stream channel correctly
 	const mem_ptr_t<PamfHeader> pAddr(pSelf->pAddr);
 
 	if ((pAddr->stream_headers[stream].type == 0x1b) &&
-		(pAddr->stream_headers[stream].stream_id == 0xe1)) return 1;
+		(pAddr->stream_headers[stream].stream_id >= 0xe0) &&
+		(pAddr->stream_headers[stream].stream_id <= 0xef))
+	{
+		return pAddr->stream_headers[stream].stream_id - 0xe0;
+	}
+	cellPamf.Error("TODO: pamfGetStreamChannel (-> 0)");
 	return 0;
 }
 
@@ -122,7 +116,7 @@ int cellPamfGetHeaderSize(mem_ptr_t<PamfHeader> pAddr, u64 fileSize, mem64_t pSi
 		//return CELL_PAMF_ERROR_UNKNOWN_TYPE;
 
 	const u64 offset = (u64)pAddr->data_offset << 11;
-	pSize = offset /*? offset : 2048*/; //hack
+	pSize = offset;
 	return CELL_OK;
 }
 
@@ -135,12 +129,10 @@ int cellPamfGetHeaderSize2(mem_ptr_t<PamfHeader> pAddr, u64 fileSize, u32 attrib
 		//return CELL_PAMF_ERROR_UNKNOWN_TYPE;
 
 	const u64 offset = (u64)pAddr->data_offset << 11;
-	pSize = offset /*? offset : 2048*/; //hack
+	pSize = offset;
 	return CELL_OK;
 }
 
-//u32 hack_LastHeader = 0;
-
 int cellPamfGetStreamOffsetAndSize(mem_ptr_t<PamfHeader> pAddr, u64 fileSize, mem64_t pOffset, mem64_t pSize)
 {
 	cellPamf.Warning("cellPamfGetStreamOffsetAndSize(pAddr=0x%x, fileSize=%d, pOffset_addr=0x%x, pSize_addr=0x%x)",
@@ -150,10 +142,9 @@ int cellPamfGetStreamOffsetAndSize(mem_ptr_t<PamfHeader> pAddr, u64 fileSize, me
 		//return CELL_PAMF_ERROR_UNKNOWN_TYPE;
 
 	const u64 offset = (u64)pAddr->data_offset << 11;
-	pOffset = offset /*? offset : 2048*/; //hack
+	pOffset = offset;
 	const u64 size = (u64)pAddr->data_size << 11;
-	pSize = size /*? size : (fileSize - 2048)*/; //hack
-	//if (!(u32)pAddr->magic) hack_LastHeader = pAddr.GetAddr();
+	pSize = size;
 	return CELL_OK;
 }
 
@@ -177,7 +168,7 @@ int cellPamfReaderInitialize(mem_ptr_t<CellPamfReader> pSelf, mem_ptr_t<PamfHead
 		pSelf->fileSize = ((u64)pAddr->data_offset << 11) + ((u64)pAddr->data_size << 11);
 	}
 	pSelf->pAddr = pAddr.GetAddr();
-	//if (hack_LastHeader) memcpy(Memory + pAddr.GetAddr(), Memory + hack_LastHeader, 2048);
+
 	if (attribute & CELL_PAMF_ATTRIBUTE_VERIFY_ON)
 	{
 		//TODO
diff --git a/rpcs3/Emu/SysCalls/Modules/cellPamf.h b/rpcs3/Emu/SysCalls/Modules/cellPamf.h
index b88c473af0..152faa29c0 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellPamf.h
+++ b/rpcs3/Emu/SysCalls/Modules/cellPamf.h
@@ -135,13 +135,15 @@ enum
 };
 
 // Timestamp information (time in increments of 90 kHz)
-struct CellCodecTimeStamp {
+struct CellCodecTimeStamp
+{
 	be_t<u32> upper;
 	be_t<u32> lower;
 };
 
 // Entry point information
-struct CellPamfEp {
+struct CellPamfEp
+{
 	be_t<u32> indexN;
 	be_t<u32> nThRefPictureOffset;
 	CellCodecTimeStamp pts;
diff --git a/rpcs3/Emu/SysCalls/Modules/cellPngDec.cpp b/rpcs3/Emu/SysCalls/Modules/cellPngDec.cpp
index 35b1f8ab79..64d83d5b92 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellPngDec.cpp
+++ b/rpcs3/Emu/SysCalls/Modules/cellPngDec.cpp
@@ -91,7 +91,7 @@ int cellPngDecReadHeader(u32 mainHandle, u32 subHandle, mem_ptr_t<CellPngDecInfo
 	switch(subHandle_data->src.srcSelect.ToLE())
 	{
 	case CELL_PNGDEC_BUFFER:
-		memcpy(Memory.VirtualToRealAddr(buffer.GetAddr()), Memory.VirtualToRealAddr(subHandle_data->src.streamPtr.ToLE()), buffer.GetSize());
+		Memory.Copy(buffer.GetAddr(), subHandle_data->src.streamPtr.ToLE(), buffer.GetSize());
 		break;
 	case CELL_PNGDEC_FILE:
 		cellFsLseek(fd, 0, CELL_SEEK_SET, pos);
@@ -145,7 +145,7 @@ int cellPngDecDecodeData(u32 mainHandle, u32 subHandle, mem8_ptr_t data, const m
 	switch(subHandle_data->src.srcSelect.ToLE())
 	{
 	case CELL_PNGDEC_BUFFER:
-		memcpy(Memory.VirtualToRealAddr(png.GetAddr()), Memory.VirtualToRealAddr(subHandle_data->src.streamPtr.ToLE()), png.GetSize());
+		Memory.Copy(png.GetAddr(), subHandle_data->src.streamPtr.ToLE(), png.GetSize());
 		break;
 	case CELL_PNGDEC_FILE:
 		cellFsLseek(fd, 0, CELL_SEEK_SET, pos);
@@ -164,7 +164,7 @@ int cellPngDecDecodeData(u32 mainHandle, u32 subHandle, mem8_ptr_t data, const m
 	case CELL_PNGDEC_RGB:
 	case CELL_PNGDEC_RGBA:
 		image_size *= current_outParam.outputColorSpace == CELL_PNGDEC_RGBA ? 4 : 3;
-		memcpy(data, image.get(), image_size);
+		Memory.CopyFromReal(data.GetAddr(), image.get(), image_size);
 	break;
 
 	case CELL_PNGDEC_ARGB:
diff --git a/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp b/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp
index 74d3599841..c0dd39dcfe 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp
+++ b/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp
@@ -1,76 +1,578 @@
 #include "stdafx.h"
 #include "Emu/SysCalls/SysCalls.h"
 #include "Emu/SysCalls/SC_FUNC.h"
+#include "cellPamf.h"
+
+extern "C"
+{
+#include "libavcodec\avcodec.h"
+#include "libavformat\avformat.h"
+#include "libavutil\imgutils.h"
+}
+
 #include "cellVdec.h"
 
 void cellVdec_init();
 Module cellVdec(0x0005, cellVdec_init);
 
+int vdecRead(void* opaque, u8* buf, int buf_size)
+{
+	VideoDecoder& vdec = *(VideoDecoder*)opaque;
+
+	if (vdec.reader.size < (u32)buf_size) buf_size = vdec.reader.size;
+	if (!buf_size)
+	{
+		return AVERROR_EOF;
+	}
+	else if (!Memory.CopyToReal(buf, vdec.reader.addr, buf_size))
+	{
+		ConLog.Error("vdecRead: data reading failed (buf_size=0x%x)", buf_size);
+		Emu.Pause();
+		return 0;
+	}
+	else
+	{
+		vdec.reader.addr += buf_size;
+		vdec.reader.size -= buf_size;
+		return buf_size;
+	}
+}
+
+u32 vdecQueryAttr(CellVdecCodecType type, u32 profile, u32 spec_addr /* may be 0 */, mem_ptr_t<CellVdecAttr> attr)
+{
+	switch (type) // TODO: check profile levels
+	{
+	case CELL_VDEC_CODEC_TYPE_AVC: cellVdec.Warning("cellVdecQueryAttr: AVC (profile=%d)", profile); break;
+	case CELL_VDEC_CODEC_TYPE_MPEG2: cellVdec.Error("TODO: MPEG2 not supported"); break;
+	case CELL_VDEC_CODEC_TYPE_DIVX: cellVdec.Error("TODO: DIVX not supported"); break;
+	default: return CELL_VDEC_ERROR_ARG;
+	}
+
+	// TODO: check values
+	attr->decoderVerLower = 0x280000; // from dmux
+	attr->decoderVerUpper = 0x260000;
+	attr->memSize = 4 * 1024 * 1024;
+	attr->cmdDepth = 16;
+	return CELL_OK;
+}
+
+u32 vdecOpen(VideoDecoder* data)
+{
+	VideoDecoder& vdec = *data;
+
+	u32 vdec_id = cellVdec.GetNewId(data);
+
+	vdec.id = vdec_id;
+
+	thread t("Video Decoder[" + std::to_string(vdec_id) + "] Thread", [&]()
+	{
+		ConLog.Write("Video Decoder enter()");
+
+		VdecTask task;
+
+		while (true)
+		{
+			if (Emu.IsStopped())
+			{
+				break;
+			}
+
+			if (vdec.job.IsEmpty() && vdec.is_running)
+			{
+				// TODO: default task (not needed?)
+				Sleep(1);
+				continue;
+			}
+
+			if (vdec.has_picture) // hack
+			{
+				Sleep(1);
+				continue;
+			}
+
+			if (!vdec.job.Pop(task))
+			{
+				break;
+			}
+
+			switch (task.type)
+			{
+			case vdecStartSeq:
+				{
+					// TODO: reset data
+					ConLog.Warning("vdecStartSeq()");
+					vdec.is_running = true;
+				}
+				break;
+
+			case vdecEndSeq:
+				{
+					Callback cb;
+					cb.SetAddr(vdec.cbFunc);
+					cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_SEQDONE, 0, vdec.cbArg);
+					cb.Branch(false);
+					ConLog.Warning("vdecEndSeq()");
+					vdec.is_running = false;
+				}
+				break;
+
+			case vdecDecodeAu:
+				{
+					struct vdecPacket : AVPacket
+					{
+						vdecPacket(u32 size)
+						{
+							av_init_packet(this);
+							data = (u8*)av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
+							memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+							this->size = size + FF_INPUT_BUFFER_PADDING_SIZE;
+						}
+
+						~vdecPacket()
+						{
+							av_free(data);
+							//av_free_packet(this);
+						}
+
+					} au(task.size);
+
+					if ((task.pts || task.dts) && task.pts != ~0 && task.dts != ~0)
+					{
+						vdec.pts = task.pts;
+						vdec.dts = task.dts;
+						au.pts = vdec.pts;
+						au.dts = vdec.dts;
+						au.flags = AV_PKT_FLAG_KEY;
+					}
+					else
+					{
+						au.pts = vdec.pts;
+						au.dts = vdec.dts;
+					}
+
+					if (task.mode != CELL_VDEC_DEC_MODE_NORMAL)
+					{
+						ConLog.Error("vdecDecodeAu: unsupported decoding mode(%d)", task.mode);
+						break;
+					}
+
+					vdec.reader.addr = task.addr;
+					vdec.reader.size = task.size;
+
+					if (!Memory.CopyToReal(au.data, task.addr, task.size))
+					{
+						ConLog.Error("vdecDecodeAu: AU data accessing failed(addr=0x%x, size=0x%x)", task.addr, task.size);
+						break;
+					}
+
+					/*{
+						wxFile dump;
+						dump.Open(wxString::Format("0x%llx-0x%llx.dump", au.pts, au.dts), wxFile::write);
+						dump.Write(au.data, task.size + FF_INPUT_BUFFER_PADDING_SIZE);
+						dump.Close();
+					}*/
+
+					int got_picture = 0;
+
+					//vdec.ctx->flags |= CODEC_FLAG_TRUNCATED;
+					//vdec.ctx->flags2 |= CODEC_FLAG2_CHUNKS;
+					vdec.ctx->flags2 |= CODEC_FLAG2_LOCAL_HEADER;
+					vdec.ctx->codec_tag = *(u32*)"DAVC";
+					//vdec.ctx->stream_codec_tag = *(u32*)"DAVC";
+
+					//avcodec_get_frame_defaults(vdec.frame);
+					
+
+					int decode = avcodec_decode_video2(vdec.ctx, vdec.frame, &got_picture, &au);
+					if (decode < 0)
+					{
+						ConLog.Error("vdecDecodeAu: AU decoding error(%d)", decode);
+						break;
+					}
+
+					if (got_picture)
+					{
+						ConLog.Write("got_picture (%d, vdec: pts=0x%llx, dts=0x%llx)", got_picture, vdec.pts, vdec.dts);
+					
+						/*if (vdec.out_data[0]) av_freep(vdec.out_data[0]);
+
+						int err = av_image_alloc(vdec.out_data, vdec.linesize, vdec.ctx->width, vdec.ctx->height, vdec.ctx->pix_fmt, 1);
+						if (err < 0)
+						{
+							ConLog.Error("vdecDecodeAu: av_image_alloc failed(%d)", err);
+							Emu.Pause();
+							return;
+						}
+						
+						vdec.buf_size = err;
+
+						av_image_copy(vdec.out_data, vdec.linesize, (const u8**)(vdec.frame->data), vdec.frame->linesize,
+							vdec.ctx->pix_fmt, vdec.ctx->width, vdec.ctx->height);*/
+						vdec.buf_size = a128(av_image_get_buffer_size(vdec.ctx->pix_fmt, vdec.ctx->width, vdec.ctx->height, 1));
+
+						vdec.userdata = task.userData;
+						vdec.has_picture = true;
+
+						Callback cb;
+						cb.SetAddr(vdec.cbFunc);
+						cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_PICOUT, 0, vdec.cbArg);
+						cb.Branch(false);
+					}
+				
+					ConLog.Write("Frame decoded (pts=0x%llx, dts=0x%llx, addr=0x%x, result=0x%x)", au.pts, au.dts, task.addr, decode);
+
+					Callback cb;
+					cb.SetAddr(vdec.cbFunc);
+					cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_AUDONE, 0, vdec.cbArg);
+					cb.Branch(false);
+				}
+				break;
+
+			case vdecClose:
+				{
+					vdec.is_finished = true;
+					ConLog.Write("Video Decoder exit");
+					return;
+				}
+
+			case vdecSetFrameRate:
+				{
+					ConLog.Error("TODO: vdecSetFrameRate(%d)", task.frc);
+					return;
+				}
+
+			default:
+				ConLog.Error("Video Decoder error: unknown task(%d)", task.type);
+				return;
+			}
+		}
+
+		ConLog.Warning("Video Decoder aborted");
+	});
+
+	t.detach();
+
+	return vdec_id;
+}
+
 int cellVdecQueryAttr(const mem_ptr_t<CellVdecType> type, mem_ptr_t<CellVdecAttr> attr)
 {
-	cellVdec.Error("cellVdecQueryAttr(type_addr=0x%x, attr_addr=0x%x)", type.GetAddr(), attr.GetAddr());
-	return CELL_OK;
+	cellVdec.Warning("cellVdecQueryAttr(type_addr=0x%x, attr_addr=0x%x)", type.GetAddr(), attr.GetAddr());
+
+	if (!type.IsGood() || !attr.IsGood())
+	{
+		return CELL_VDEC_ERROR_FATAL;
+	}
+
+	return vdecQueryAttr(type->codecType, type->profileLevel, 0, attr);
 }
 
 int cellVdecQueryAttrEx(const mem_ptr_t<CellVdecTypeEx> type, mem_ptr_t<CellVdecAttr> attr)
 {
-	cellVdec.Error("cellVdecQueryAttrEx(type_addr=0x%x, attr_addr=0x%x)", type.GetAddr(), attr.GetAddr());
-	return CELL_OK;
+	cellVdec.Warning("cellVdecQueryAttrEx(type_addr=0x%x, attr_addr=0x%x)", type.GetAddr(), attr.GetAddr());
+
+	if (!type.IsGood() || !attr.IsGood())
+	{
+		return CELL_VDEC_ERROR_FATAL;
+	}
+
+	return vdecQueryAttr(type->codecType, type->profileLevel, type->codecSpecificInfo_addr, attr);
 }
 
 int cellVdecOpen(const mem_ptr_t<CellVdecType> type, const mem_ptr_t<CellVdecResource> res, const mem_ptr_t<CellVdecCb> cb, mem32_t handle)
 {
-	cellVdec.Error("cellVdecOpen(type_addr=0x%x, res_addr=0x%x, cb_addr=0x%x, handle_addr=0x%x)",
+	cellVdec.Warning("cellVdecOpen(type_addr=0x%x, res_addr=0x%x, cb_addr=0x%x, handle_addr=0x%x)",
 		type.GetAddr(), res.GetAddr(), cb.GetAddr(), handle.GetAddr());
+
+	if (!type.IsGood() || !res.IsGood() || !cb.IsGood() || !handle.IsGood())
+	{
+		return CELL_VDEC_ERROR_FATAL;
+	}
+
+	if (!Memory.IsGoodAddr(res->memAddr, res->memSize) || !Memory.IsGoodAddr(cb->cbFunc))
+	{
+		return CELL_VDEC_ERROR_FATAL;
+	}
+
+	handle = vdecOpen(new VideoDecoder(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg));
+
 	return CELL_OK;
 }
 
 int cellVdecOpenEx(const mem_ptr_t<CellVdecTypeEx> type, const mem_ptr_t<CellVdecResourceEx> res, const mem_ptr_t<CellVdecCb> cb, mem32_t handle)
 {
-	cellVdec.Error("cellVdecOpenEx(type_addr=0x%x, res_addr=0x%x, cb_addr=0x%x, handle_addr=0x%x)",
+	cellVdec.Warning("cellVdecOpenEx(type_addr=0x%x, res_addr=0x%x, cb_addr=0x%x, handle_addr=0x%x)",
 		type.GetAddr(), res.GetAddr(), cb.GetAddr(), handle.GetAddr());
+
+	if (!type.IsGood() || !res.IsGood() || !cb.IsGood() || !handle.IsGood())
+	{
+		return CELL_VDEC_ERROR_FATAL;
+	}
+
+	if (!Memory.IsGoodAddr(res->memAddr, res->memSize) || !Memory.IsGoodAddr(cb->cbFunc))
+	{
+		return CELL_VDEC_ERROR_FATAL;
+	}
+
+	handle = vdecOpen(new VideoDecoder(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg));
+
 	return CELL_OK;
 }
 
 int cellVdecClose(u32 handle)
 {
-	cellVdec.Error("cellVdecClose(handle=0x%x)", handle);
+	cellVdec.Warning("cellVdecClose(handle=%d)", handle);
+
+	VideoDecoder* vdec;
+	if (!Emu.GetIdManager().GetIDData(handle, vdec))
+	{
+		return CELL_VDEC_ERROR_ARG;
+	}
+
+	vdec->job.Push(VdecTask(vdecClose));
+
+	while (!vdec->is_finished)
+	{
+		if (Emu.IsStopped())
+		{
+			ConLog.Warning("cellVdecClose(%d) aborted", handle);
+			break;
+		}
+		Sleep(1);
+	}
+
+	Emu.GetIdManager().RemoveID(handle);
 	return CELL_OK;
 }
 
 int cellVdecStartSeq(u32 handle)
 {
-	cellVdec.Error("cellVdecStartSeq(handle=0x%x)", handle);
+	cellVdec.Log("cellVdecStartSeq(handle=%d)", handle);
+
+	VideoDecoder* vdec;
+	if (!Emu.GetIdManager().GetIDData(handle, vdec))
+	{
+		return CELL_VDEC_ERROR_ARG;
+	}
+
+	vdec->job.Push(VdecTask(vdecStartSeq));
 	return CELL_OK;
 }
 
 int cellVdecEndSeq(u32 handle)
 {
-	cellVdec.Error("cellVdecEndSeq(handle=0x%x)", handle);
+	cellVdec.Log("cellVdecEndSeq(handle=%d)", handle);
+
+	VideoDecoder* vdec;
+	if (!Emu.GetIdManager().GetIDData(handle, vdec))
+	{
+		return CELL_VDEC_ERROR_ARG;
+	}
+
+	vdec->job.Push(VdecTask(vdecEndSeq));
 	return CELL_OK;
 }
 
 int cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, const mem_ptr_t<CellVdecAuInfo> auInfo)
 {
-	cellVdec.Error("cellVdecDecodeAu(handle=0x%x, mode=0x%x, auInfo_addr=0x%x)", handle, mode, auInfo.GetAddr());
+	cellVdec.Log("cellVdecDecodeAu(handle=%d, mode=0x%x, auInfo_addr=0x%x)", handle, mode, auInfo.GetAddr());
+
+	VideoDecoder* vdec;
+	if (!Emu.GetIdManager().GetIDData(handle, vdec))
+	{
+		return CELL_VDEC_ERROR_ARG;
+	}
+
+	// TODO: check info
+	VdecTask task(vdecDecodeAu);
+	task.mode = mode;
+	task.addr = auInfo->startAddr;
+	task.size = auInfo->size;
+	task.dts = (u64)auInfo->dts.lower | ((u64)auInfo->dts.upper << 32);
+	task.pts = (u64)auInfo->pts.lower | ((u64)auInfo->pts.upper << 32);
+	task.userData = auInfo->userData;
+	task.specData = auInfo->codecSpecificData;
+
+	vdec->job.Push(task);
 	return CELL_OK;
 }
 
 int cellVdecGetPicture(u32 handle, const mem_ptr_t<CellVdecPicFormat> format, u32 out_addr)
 {
-	cellVdec.Error("cellVdecGetPicture(handle=0x%x, format_addr=0x%x, out_addr=0x%x)", handle, format.GetAddr(), out_addr);
+	cellVdec.Warning("cellVdecGetPicture(handle=%d, format_addr=0x%x, out_addr=0x%x)", handle, format.GetAddr(), out_addr);
+
+	VideoDecoder* vdec;
+	if (!Emu.GetIdManager().GetIDData(handle, vdec))
+	{
+		return CELL_VDEC_ERROR_ARG;
+	}
+
+	if (!format.IsGood())
+	{
+		return CELL_VDEC_ERROR_FATAL;
+	}
+
+	if (!vdec->has_picture)
+	{
+		return CELL_VDEC_ERROR_EMPTY;
+	}
+
+	if (out_addr)
+	{
+		if (!Memory.IsGoodAddr(out_addr, vdec->buf_size))
+		{
+			return CELL_VDEC_ERROR_FATAL;
+		}
+
+		if (format->formatType != CELL_VDEC_PICFMT_YUV420_PLANAR)
+		{
+			cellVdec.Error("cellVdecGetPicture: TODO: unknown formatType(%d)", (u32)format->formatType);
+			return CELL_OK;
+		}
+		if (format->colorMatrixType != CELL_VDEC_COLOR_MATRIX_TYPE_BT709)
+		{
+			cellVdec.Error("cellVdecGetPicture: TODO: unknown colorMatrixType(%d)", (u32)format->colorMatrixType);
+			return CELL_OK;
+		}
+
+		AVFrame& frame = *vdec->frame;
+
+		u8* buf = (u8*)malloc(vdec->buf_size);
+		if (!buf)
+		{
+			cellVdec.Error("cellVdecGetPicture: malloc failed (out of memory)");
+			Emu.Pause();
+			return CELL_OK;
+		}
+
+		// TODO: zero padding bytes
+
+		int err = av_image_copy_to_buffer(buf, vdec->buf_size, frame.data, frame.linesize, vdec->ctx->pix_fmt, frame.width, frame.height, 1);
+		if (err < 0)
+		{
+			cellVdec.Error("cellVdecGetPicture: av_image_copy_to_buffer failed(%d)", err);
+			Emu.Pause();
+		}
+
+		if (!Memory.CopyFromReal(out_addr, buf, vdec->buf_size))
+		{
+			cellVdec.Error("cellVdecGetPicture: data copying failed");
+			Emu.Pause();
+		}
+
+		/*
+		u32 size0 = frame.linesize[0] * frame.height;
+		u32 size1 = frame.linesize[1] * frame.height / 2;
+		u32 size2 = frame.linesize[2] * frame.height / 2;
+		ConLog.Write("*** size0=0x%x, size1=0x%x, size2=0x%x, buf_size=0x%x (res=0x%x)", size0, size1, size2, vdec->buf_size, err);
+		*/
+
+		free(buf);
+	}
+
+	vdec->has_picture = false;
 	return CELL_OK;
 }
 
-int cellVdecGetPicItem(u32 handle, const u32 picItem_ptr_addr)
+int cellVdecGetPicItem(u32 handle, mem32_t picItem_ptr)
 {
-	cellVdec.Error("cellVdecGetPicItem(handle=0x%x, picItem_ptr_addr=0x%x)", handle, picItem_ptr_addr);
+	cellVdec.Warning("cellVdecGetPicItem(handle=%d, picItem_ptr_addr=0x%x)", handle, picItem_ptr.GetAddr());
+
+	VideoDecoder* vdec;
+	if (!Emu.GetIdManager().GetIDData(handle, vdec))
+	{
+		return CELL_VDEC_ERROR_ARG;
+	}
+
+	if (!picItem_ptr.IsGood())
+	{
+		return CELL_VDEC_ERROR_FATAL;
+	}
+
+	if (!vdec->has_picture)
+	{
+		return CELL_VDEC_ERROR_EMPTY;
+	}
+
+	mem_ptr_t<CellVdecPicItem> info(vdec->memAddr);
+
+	info->codecType = vdec->type;
+	info->startAddr = 0x00000123; // invalid value (no address for picture)
+	info->size = vdec->buf_size;
+	info->auNum = 1;
+	info->auPts[0].lower = vdec->pts;
+	info->auPts[0].upper = vdec->pts >> 32;
+	info->auPts[1].lower = 0xffffffff;
+	info->auPts[1].upper = 0xffffffff;
+	info->auDts[0].lower = vdec->dts;
+	info->auDts[0].upper = vdec->dts >> 32;
+	info->auDts[1].lower = 0xffffffff;
+	info->auDts[1].upper = 0xffffffff;
+	info->auUserData[0] = vdec->userdata;
+	info->auUserData[1] = 0;
+	info->status = CELL_OK;
+	info->attr = CELL_VDEC_PICITEM_ATTR_NORMAL;
+	info->picInfo_addr = vdec->memAddr + sizeof(CellVdecPicItem);
+
+	mem_ptr_t<CellVdecAvcInfo> avc(vdec->memAddr + sizeof(CellVdecPicItem));
+
+	avc->horizontalSize = vdec->frame->width; // ???
+	avc->verticalSize = vdec->frame->height;
+	switch (vdec->frame->pict_type)
+	{
+	case AV_PICTURE_TYPE_I: avc->pictureType[0] = CELL_VDEC_AVC_PCT_I; break;
+	case AV_PICTURE_TYPE_P: avc->pictureType[0] = CELL_VDEC_AVC_PCT_P; break;
+	case AV_PICTURE_TYPE_B: avc->pictureType[0] = CELL_VDEC_AVC_PCT_B; break;
+	default: avc->pictureType[0] = CELL_VDEC_AVC_PCT_UNKNOWN; break; // ???
+	}
+	avc->pictureType[1] = CELL_VDEC_AVC_PCT_UNKNOWN; // ???
+	avc->idrPictureFlag = false; // ???
+	avc->aspect_ratio_idc = CELL_VDEC_AVC_ARI_SAR_UNSPECIFIED; // ???
+	avc->sar_height = 0;
+	avc->sar_width = 0;
+	avc->pic_struct = CELL_VDEC_AVC_PSTR_FRAME; // ???
+	avc->picOrderCount[0] = 0; // ???
+	avc->picOrderCount[1] = 0;
+	avc->vui_parameters_present_flag = true; // ???
+	avc->frame_mbs_only_flag = true; // ??? progressive
+	avc->video_signal_type_present_flag = true; // ???
+	avc->video_format = CELL_VDEC_AVC_VF_COMPONENT; // ???
+	avc->video_full_range_flag = false; // ???
+	avc->colour_description_present_flag = true;
+	avc->colour_primaries = CELL_VDEC_AVC_CP_ITU_R_BT_709_5; // ???
+	avc->transfer_characteristics = CELL_VDEC_AVC_TC_ITU_R_BT_709_5;
+	avc->matrix_coefficients = CELL_VDEC_AVC_MXC_ITU_R_BT_709_5; // important
+	avc->timing_info_present_flag = true;
+	avc->frameRateCode = CELL_VDEC_AVC_FRC_30000DIV1001; // important (!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!)
+	avc->fixed_frame_rate_flag = true;
+	avc->low_delay_hrd_flag = true; // ???
+	avc->entropy_coding_mode_flag = true; // ???
+	avc->nalUnitPresentFlags = 0; // ???
+	avc->ccDataLength[0] = 0;
+	avc->ccDataLength[1] = 0;
+	avc->reserved[0] = 0;
+	avc->reserved[1] = 0;
+	
+	picItem_ptr = info.GetAddr();
+
 	return CELL_OK;
 }
 
 int cellVdecSetFrameRate(u32 handle, CellVdecFrameRate frc)
 {
-	cellVdec.Error("cellVdecSetFrameRate(handle=0x%x, frc=0x%x)", handle, frc);
+	cellVdec.Log("cellVdecSetFrameRate(handle=%d, frc=0x%x)", handle, frc);
+
+	VideoDecoder* vdec;
+	if (!Emu.GetIdManager().GetIDData(handle, vdec))
+	{
+		return CELL_VDEC_ERROR_ARG;
+	}
+
+	// TODO: check frc value and set frame rate
+	VdecTask task(vdecSetFrameRate);
+	task.frc = frc;
+
+	vdec->job.Push(task);
 	return CELL_OK;
 }
 
@@ -87,4 +589,6 @@ void cellVdec_init()
 	cellVdec.AddFunc(0x807c861a, cellVdecGetPicture);
 	cellVdec.AddFunc(0x17c702b9, cellVdecGetPicItem);
 	cellVdec.AddFunc(0xe13ef6fc, cellVdecSetFrameRate);
+
+	avcodec_register_all();
 }
\ No newline at end of file
diff --git a/rpcs3/Emu/SysCalls/Modules/cellVdec.h b/rpcs3/Emu/SysCalls/Modules/cellVdec.h
index 771924fea5..f2ca2320fd 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellVdec.h
+++ b/rpcs3/Emu/SysCalls/Modules/cellVdec.h
@@ -1,5 +1,8 @@
 #pragma once
-#include "cellPamf.h"
+
+#include "Utilities/SQueue.h"
+
+#define a128(x) ((x + 127) & (~127))
 
 // Error Codes
 enum
@@ -30,7 +33,7 @@ enum CellVdecMsgType
 };
 
 // Decoder Operation Mode
-enum CellVdecDecodeMode
+enum CellVdecDecodeMode : u32
 {
 	CELL_VDEC_DEC_MODE_NORMAL,
 	CELL_VDEC_DEC_MODE_B_SKIP,
@@ -164,11 +167,13 @@ struct CellVdecPicFormat
 	u8 alpha;
 };
 
+typedef mem_func_ptr_t<void (*)(u32 handle_addr, CellVdecMsgType msgType, int msgData, u32 cbArg_addr)> CellVdecCbMsg;
+
 // Callback Function Information
 struct CellVdecCb
 {
-	be_t<mem_func_ptr_t<void (*)(u32 handle_addr, CellVdecMsgType msgType, int msgData, u32 cbArg_addr)>> cbFunc;
-	be_t<u32> cbArg_addr;
+	be_t<u32> cbFunc;
+	be_t<u32> cbArg;
 };
 
 // Max CC Data Length
@@ -338,16 +343,18 @@ struct CellVdecAvcInfo
 	AVC_transfer_characteristics transfer_characteristics;
 	AVC_matrix_coefficients matrix_coefficients;
 	bool timing_info_present_flag;
-	CellVdecFrameRate frameRateCode;
+	AVC_FrameRateCode frameRateCode; // ???
 	bool fixed_frame_rate_flag;
 	bool low_delay_hrd_flag;
 	bool entropy_coding_mode_flag;
-	be_t<AVC_NulUnitPresentFlags> nalUnitPresentFlags;
+	be_t<u16> nalUnitPresentFlags;
 	u8 ccDataLength[2];
 	u8 ccData[2][CELL_VDEC_AVC_CCD_MAX];
 	be_t<u64> reserved[2];
 };
 
+const int sz = sizeof(CellVdecAvcInfo);
+
 // DIVX Profile
 enum DIVX_level : u8
 {
@@ -634,4 +641,153 @@ struct CellVdecMpeg2Info
 	u8 ccDataLength[2];
 	u8 ccData[2][128];
 	be_t<u64> reserved[2];
+};
+
+/* Video Decoder Thread Classes */
+
+enum VdecJobType : u32
+{
+	vdecStartSeq,
+	vdecEndSeq,
+	vdecDecodeAu,
+	vdecSetFrameRate,
+	vdecClose,
+};
+
+struct VdecTask
+{
+	VdecJobType type;
+	union
+	{
+		u32 frc;
+		CellVdecDecodeMode mode;
+	};
+	u32 addr;
+	u32 size;
+	u64 pts;
+	u64 dts;
+	u64 userData;
+	u64 specData;
+
+	VdecTask(VdecJobType type)
+		: type(type)
+	{
+	}
+
+	VdecTask()
+	{
+	}
+};
+
+int vdecRead(void* opaque, u8* buf, int buf_size);
+
+class VideoDecoder
+{
+public:
+	SQueue<VdecTask> job;
+	u32 id;
+	volatile bool is_running;
+	volatile bool is_finished;
+
+	AVCodec* codec;
+	AVCodecContext* ctx;
+	AVFormatContext* fmt;
+	AVFrame* frame;
+	AVDictionary* opts;
+	u8* io_buf;
+	u32 buf_size;
+	u64 pts;
+	u64 dts;
+	u64 pos;
+	u64 userdata;
+	volatile bool has_picture;
+
+	struct VideoReader
+	{
+		u32 addr;
+		u32 size;
+	} reader;
+
+	const CellVdecCodecType type;
+	const u32 profile;
+	const u32 memAddr;
+	const u32 memSize;
+	const u32 cbFunc;
+	const u32 cbArg;
+
+	VideoDecoder(CellVdecCodecType type, u32 profile, u32 addr, u32 size, u32 func, u32 arg)
+		: type(type)
+		, profile(profile)
+		, memAddr(addr)
+		, memSize(size)
+		, cbFunc(func)
+		, cbArg(arg)
+		, is_finished(false)
+		, is_running(false)
+		, has_picture(false)
+		, pos(0)
+	{
+		codec = avcodec_find_decoder(AV_CODEC_ID_H264);
+		if (!codec)
+		{
+			ConLog.Error("VideoDecoder(): avcodec_find_decoder failed");
+			Emu.Pause();
+			return;
+		}
+		ctx = avcodec_alloc_context3(codec);
+		if (!ctx)
+		{
+			ConLog.Error("VideoDecoder(): avcodec_alloc_context3 failed");
+			Emu.Pause();
+			return;
+		}
+		opts = nullptr;
+		int err = avcodec_open2(ctx, codec, &opts);
+		if (err) // TODO: not multithread safe
+		{
+			ConLog.Error("VideoDecoder(): avcodec_open2 failed(%d)", err);
+			Emu.Pause();
+			return;
+		}
+		frame = av_frame_alloc();
+		if (!frame)
+		{
+			ConLog.Error("VideoDecoder(): av_frame_alloc failed");
+			Emu.Pause();
+			return;
+		}
+		fmt = avformat_alloc_context();
+		if (!fmt)
+		{
+			ConLog.Error("VideoDecoder(): avformat_alloc_context failed");
+			Emu.Pause();
+			return;
+		}
+		io_buf = (u8*)av_malloc(4096);
+		fmt->pb = avio_alloc_context(io_buf, 4096, 0, this, vdecRead, NULL, NULL);
+		if (!fmt->pb)
+		{
+			ConLog.Error("VideoDecoder(): avio_alloc_context failed");
+			Emu.Pause();
+			return;
+		}
+		//memset(&out_data, 0, sizeof(out_data));
+		//memset(&linesize, 0, sizeof(linesize));
+	}
+
+	~VideoDecoder()
+	{
+		if (io_buf) av_free(io_buf);
+		if (fmt)
+		{
+			avformat_free_context(fmt);
+		}
+		if (frame) av_frame_free(&frame);
+		if (ctx)
+		{
+			avcodec_close(ctx);
+			av_free(ctx);
+		}
+		//if (out_data[0]) av_freep(out_data[0]);
+	}
 };
\ No newline at end of file
diff --git a/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp b/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp
index 0154af54c1..8073080424 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp
+++ b/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp
@@ -8,35 +8,186 @@ Module cellVpost(0x0008, cellVpost_init);
 
 int cellVpostQueryAttr(const mem_ptr_t<CellVpostCfgParam> cfgParam, mem_ptr_t<CellVpostAttr> attr)
 {
-	cellVpost.Error("cellVpostQueryAttr(cfgParam_addr=0x%x, attr_addr=0x%x)", cfgParam.GetAddr(), attr.GetAddr());
+	cellVpost.Warning("cellVpostQueryAttr(cfgParam_addr=0x%x, attr_addr=0x%x)", cfgParam.GetAddr(), attr.GetAddr());
+
+	if (!cfgParam.IsGood()) return CELL_VPOST_ERROR_Q_ARG_CFG_NULL;
+	if (!attr.IsGood()) return CELL_VPOST_ERROR_Q_ARG_ATTR_NULL;
+
+	// TODO: check cfgParam and output values
+
+	attr->delay = 0;
+	attr->memSize = 4 * 1024 * 1024;
+	attr->vpostVerLower = 0x280000; // from dmux
+	attr->vpostVerUpper = 0x260000;
+
 	return CELL_OK;
 }
 
+u32 vpostOpen(VpostInstance* data)
+{
+	u32 id = cellVpost.GetNewId(data);
+
+	ConLog.Write("*** Vpost instance created (to_rgba=%d): id = %d", data->to_rgba, id);
+
+	return id;
+}
+
 int cellVpostOpen(const mem_ptr_t<CellVpostCfgParam> cfgParam, const mem_ptr_t<CellVpostResource> resource, mem32_t handle)
 {
-	cellVpost.Error("cellVpostOpen(cfgParam_addr=0x%x, resource_addr=0x%x, handle_addr=0x%x)",
+	cellVpost.Warning("cellVpostOpen(cfgParam_addr=0x%x, resource_addr=0x%x, handle_addr=0x%x)",
 		cfgParam.GetAddr(), resource.GetAddr(), handle.GetAddr());
+
+	if (!cfgParam.IsGood()) return CELL_VPOST_ERROR_O_ARG_CFG_NULL;
+	if (!resource.IsGood()) return CELL_VPOST_ERROR_O_ARG_RSRC_NULL;
+	if (!handle.IsGood()) return CELL_VPOST_ERROR_O_ARG_HDL_NULL;
+
+	// TODO: check values
+	handle = vpostOpen(new VpostInstance(cfgParam->outPicFmt == CELL_VPOST_PIC_FMT_OUT_RGBA_ILV));
 	return CELL_OK;
 }
 
 int cellVpostOpenEx(const mem_ptr_t<CellVpostCfgParam> cfgParam, const mem_ptr_t<CellVpostResourceEx> resource, mem32_t handle)
 {
-	cellVpost.Error("cellVpostOpenEx(cfgParam_addr=0x%x, resource_addr=0x%x, handle_addr=0x%x)",
+	cellVpost.Warning("cellVpostOpenEx(cfgParam_addr=0x%x, resource_addr=0x%x, handle_addr=0x%x)",
 		cfgParam.GetAddr(), resource.GetAddr(), handle.GetAddr());
+
+	if (!cfgParam.IsGood()) return CELL_VPOST_ERROR_O_ARG_CFG_NULL;
+	if (!resource.IsGood()) return CELL_VPOST_ERROR_O_ARG_RSRC_NULL;
+	if (!handle.IsGood()) return CELL_VPOST_ERROR_O_ARG_HDL_NULL;
+
+	// TODO: check values
+	handle = vpostOpen(new VpostInstance(cfgParam->outPicFmt == CELL_VPOST_PIC_FMT_OUT_RGBA_ILV));
 	return CELL_OK;
 }
 
 int cellVpostClose(u32 handle)
 {
-	cellVpost.Error("cellVpostClose(handle=0x%x)", handle);
+	cellVpost.Warning("cellVpostClose(handle=0x%x)", handle);
+
+	VpostInstance* vpost;
+	if (!Emu.GetIdManager().GetIDData(handle, vpost))
+	{
+		return CELL_VPOST_ERROR_C_ARG_HDL_INVALID;
+	}
+
+	Emu.GetIdManager().RemoveID(handle);	
 	return CELL_OK;
 }
 
 int cellVpostExec(u32 handle, const u32 inPicBuff_addr, const mem_ptr_t<CellVpostCtrlParam> ctrlParam,
 				  u32 outPicBuff_addr, mem_ptr_t<CellVpostPictureInfo> picInfo)
 {
-	cellVpost.Error("cellVpostExec(handle=0x%x, inPicBuff_addr=0x%x, ctrlParam_addr=0x%x, outPicBuff_addr=0x%x, picInfo_addr=0x%x)",
+	cellVpost.Warning("cellVpostExec(handle=0x%x, inPicBuff_addr=0x%x, ctrlParam_addr=0x%x, outPicBuff_addr=0x%x, picInfo_addr=0x%x)",
 		handle, inPicBuff_addr, ctrlParam.GetAddr(), outPicBuff_addr, picInfo.GetAddr());
+
+	VpostInstance* vpost;
+	if (!Emu.GetIdManager().GetIDData(handle, vpost))
+	{
+		return CELL_VPOST_ERROR_E_ARG_HDL_INVALID;
+	}
+
+	if (!ctrlParam.IsGood())
+	{
+		return CELL_VPOST_ERROR_E_ARG_CTRL_INVALID;
+	}
+
+	u32 w = ctrlParam->inWidth;
+	u32 h = ctrlParam->inHeight;
+
+	if (!Memory.IsGoodAddr(inPicBuff_addr, w*h*3/2))
+	{
+		return CELL_VPOST_ERROR_E_ARG_INPICBUF_INVALID;
+	}
+
+	if (!Memory.IsGoodAddr(outPicBuff_addr, w*h*4))
+	{
+		return CELL_VPOST_ERROR_E_ARG_OUTPICBUF_INVALID;
+	}
+
+	if (!picInfo.IsGood())
+	{
+		return CELL_VPOST_ERROR_E_ARG_PICINFO_NULL;
+	}
+
+	ctrlParam->inWindow; // ignored
+	ctrlParam->outWindow; // ignored
+	ctrlParam->execType; // ignored
+	ctrlParam->scalerType; // ignored
+	ctrlParam->ipcType; // ignored
+
+	picInfo->inWidth = ctrlParam->inWidth; // copy
+	picInfo->inHeight = ctrlParam->inHeight; // copy
+	picInfo->inDepth = CELL_VPOST_PIC_DEPTH_8; // fixed
+	picInfo->inScanType = CELL_VPOST_SCAN_TYPE_P; // TODO
+	picInfo->inPicFmt = CELL_VPOST_PIC_FMT_IN_YUV420_PLANAR; // fixed
+	picInfo->inChromaPosType = ctrlParam->inChromaPosType; // copy
+	picInfo->inPicStruct = CELL_VPOST_PIC_STRUCT_PFRM; // TODO
+	picInfo->inQuantRange = ctrlParam->inQuantRange; // copy
+	picInfo->inColorMatrix = ctrlParam->inColorMatrix; // copy
+
+	picInfo->outWidth = picInfo->inWidth; // TODO (resampling)
+	picInfo->outHeight = picInfo->inHeight; // TODO
+	picInfo->outDepth = CELL_VPOST_PIC_DEPTH_8; // fixed
+	picInfo->outScanType = CELL_VPOST_SCAN_TYPE_P; // TODO
+	picInfo->outPicFmt = CELL_VPOST_PIC_FMT_OUT_RGBA_ILV; // TODO
+	picInfo->outChromaPosType = ctrlParam->inChromaPosType; // ???
+	picInfo->outPicStruct = picInfo->inPicStruct; // ???
+	picInfo->outQuantRange = ctrlParam->inQuantRange; // ???
+	picInfo->outColorMatrix = ctrlParam->inColorMatrix; // ???
+
+	picInfo->userData = ctrlParam->userData; // copy
+	picInfo->reserved1 = 0;
+	picInfo->reserved2 = 0;
+
+	u8* pY = (u8*)malloc(w*h);
+	u8* pU = (u8*)malloc(w*h/4);
+	u8* pV = (u8*)malloc(w*h/4);
+	u32* res = (u32*)malloc(w*h*4);
+	const u8 alpha = ctrlParam->outAlpha;
+
+	if (!Memory.CopyToReal(pY, inPicBuff_addr, w*h))
+	{
+		cellVpost.Error("cellVpostExec: data copying failed(pY)");
+	}
+
+	if (!Memory.CopyToReal(pU, inPicBuff_addr + w*h, w*h/4))
+	{
+		cellVpost.Error("cellVpostExec: data copying failed(pU)");
+	}
+
+	if (!Memory.CopyToReal(pV, inPicBuff_addr + w*h + w*h/4, w*h/4))
+	{
+		cellVpost.Error("cellVpostExec: data copying failed(pV)");
+	}
+
+	for (u32 i = 0; i < h; i++) for (u32 j = 0; j < w; j++)
+	{
+		float Cr = pV[(i/2)*(w/2)+j/2];
+		float Cb = pU[(i/2)*(w/2)+j/2];
+		float Y = pY[i*w+j];
+
+		int R = Y + 1.5701f * Cr;
+		if (R < 0) R = 0;
+		if (R > 255) R = 255;
+		int G = Y - 0.1870f * Cb - 0.4664f * Cr;
+		if (G < 0) G = 0;
+		if (G > 255) G = 255;
+		int B = Y - 1.8556f * Cb;
+		if (B < 0) B = 0;
+		if (B > 255) B = 255;
+		res[i*w+j] = ((u32)alpha << 24) | (B << 16) | (G << 8) | (R);
+	}
+
+	if (!Memory.CopyFromReal(outPicBuff_addr, res, w*h*4))
+	{
+		cellVpost.Error("cellVpostExec: data copying failed(result)");
+		Emu.Pause();
+	}
+
+	free(pY);
+	free(pU);
+	free(pV);
+	free(res);
 	return CELL_OK;
 }
 
diff --git a/rpcs3/Emu/SysCalls/Modules/cellVpost.h b/rpcs3/Emu/SysCalls/Modules/cellVpost.h
index 3e54a55050..ab54a695fa 100644
--- a/rpcs3/Emu/SysCalls/Modules/cellVpost.h
+++ b/rpcs3/Emu/SysCalls/Modules/cellVpost.h
@@ -315,3 +315,14 @@ struct CellVpostPictureInfo
 	be_t<u32> reserved1;
 	be_t<u32> reserved2;
 };
+
+class VpostInstance
+{
+public:
+	const bool to_rgba;
+
+	VpostInstance(bool rgba)
+		: to_rgba(rgba)
+	{
+	}
+};
\ No newline at end of file
diff --git a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp
index 7560c1f37f..175ea47fb5 100644
--- a/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp
+++ b/rpcs3/Emu/SysCalls/Modules/sysPrxForUser.cpp
@@ -124,7 +124,7 @@ int sys_raw_spu_image_load(int id, mem_ptr_t<sys_spu_image> img)
 {
 	sysPrxForUser.Warning("sys_raw_spu_image_load(id=0x%x, img_addr=0x%x)", id, img.GetAddr());
 
-	memcpy(Memory + RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * id, Memory + (u32)img->segs_addr, 256 * 1024);
+	Memory.Copy(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * id, (u32)img->segs_addr, 256 * 1024);
 	Memory.Write32(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * id + RAW_SPU_PROB_OFFSET + SPU_NPC_offs, 
 		(u32)img->entry_point);
 
diff --git a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp
index 9aefb67151..c71e6f6742 100644
--- a/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp
+++ b/rpcs3/Emu/SysCalls/Modules/sys_fs.cpp
@@ -150,46 +150,67 @@ void fsAioRead(u32 fd, mem_ptr_t<CellFsAio> aio, int xid, mem_func_ptr_t<void (*
 
 	const wxString path = orig_file->GetPath().AfterFirst('/');
 
-	u64 nbytes = (u64)aio->size;
-	const u32 buf_addr = (u32)aio->buf_addr;
+	u64 nbytes = aio->size;
+	u32 buf_addr = aio->buf_addr;
 
-	u64 res;
-	u32 error;
+	u32 res = 0;
+	u32 error = CELL_OK;
 
-	if(Memory.IsGoodAddr(buf_addr))
+	vfsStream& file = *(vfsStream*)orig_file;
+	const u64 old_pos = file.Tell();
+	file.Seek((u64)aio->offset);
+
+	u32 count = nbytes;
+	if (nbytes != (u64)count)
 	{
-		/*
-		//open the file again (to prevent access conflicts roughly)
-		vfsLocalFile file(path, vfsRead);
-		*/
-		vfsStream& file = *(vfsStream*)orig_file;
-		if(!Memory.IsGoodAddr(buf_addr, nbytes))
-		{
-			MemoryBlock& block = Memory.GetMemByAddr(buf_addr);
-			nbytes = block.GetSize() - (buf_addr - block.GetStartAddr());
-		}
-
-		const u64 old_pos = file.Tell();
-		file.Seek((u64)aio->offset);
-		res = nbytes ? file.Read(Memory.GetMemFromAddr(buf_addr), nbytes) : 0;
-		file.Seek(old_pos);
-		error = CELL_OK;
+		error = CELL_ENOMEM;
+		goto fin;
 	}
-	else
+
+	if (!Memory.IsGoodAddr(buf_addr))
 	{
-		res = 0;
 		error = CELL_EFAULT;
+		goto fin;
 	}
 
-	ConLog.Warning("*** fsAioRead(fd=%d, offset=0x%llx, buf_addr=0x%x, size=0x%x, res=0x%x, xid=0x%x [%s])",
-		fd, (u64)aio->offset, buf_addr, (u64)aio->size, res, xid, path.wx_str());
+	if (count) if (u32 frag = buf_addr & 4095) // memory page fragment
+	{
+		u32 req = min(count, 4096 - frag);
+		u32 read = file.Read(Memory + buf_addr, req);
+		buf_addr += req;
+		res += read;
+		count -= req;
+		if (read < req) goto fin;
+	}
+
+	for (u32 pages = count / 4096; pages > 0; pages--) // full pages
+	{
+		if (!Memory.IsGoodAddr(buf_addr)) goto fin; // ??? (probably EFAULT)
+		u32 read = file.Read(Memory + buf_addr, 4096);
+		buf_addr += 4096;
+		res += read;
+		count -= 4096;
+		if (read < 4096) goto fin;
+	}
+
+	if (count) // last fragment
+	{
+		if (!Memory.IsGoodAddr(buf_addr)) goto fin;
+		res += file.Read(Memory + buf_addr, count);
+	}
+
+fin:
+	file.Seek(old_pos);
+
+	ConLog.Warning("*** fsAioRead(fd=%d, offset=0x%llx, buf_addr=0x%x, size=0x%x, error=0x%x, res=0x%x, xid=0x%x [%s])",
+		fd, (u64)aio->offset, buf_addr, (u64)aio->size, error, res, xid, path.wx_str());
 
 	if (func) // start callback thread
 	{
 		func.async(aio, error, xid, res);
 	}
 
-	CPUThread& thr = Emu.GetCallbackThread();
+	/*CPUThread& thr = Emu.GetCallbackThread();
 	while (thr.IsAlive())
 	{
 		Sleep(1);
@@ -198,7 +219,7 @@ void fsAioRead(u32 fd, mem_ptr_t<CellFsAio> aio, int xid, mem_func_ptr_t<void (*
 			ConLog.Warning("fsAioRead() aborted");
 			break;
 		}
-	}
+	}*/
 
 	g_FsAioReadCur++;
 }
diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp
index e253a7a140..97555928d8 100644
--- a/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp
+++ b/rpcs3/Emu/SysCalls/lv2/SC_Condition.cpp
@@ -6,7 +6,7 @@ SysCallBase sys_cond("sys_cond");
 
 int sys_cond_create(mem32_t cond_id, u32 mutex_id, mem_ptr_t<sys_cond_attribute> attr)
 {
-	sys_cond.Log("sys_cond_create(cond_id_addr=0x%x, mutex_id=%d, attr_addr=%d)",
+	sys_cond.Log("sys_cond_create(cond_id_addr=0x%x, mutex_id=%d, attr_addr=0x%x)",
 		cond_id.GetAddr(), mutex_id, attr.GetAddr());
 
 	if (!cond_id.IsGood() || !attr.IsGood())
@@ -28,7 +28,7 @@ int sys_cond_create(mem32_t cond_id, u32 mutex_id, mem_ptr_t<sys_cond_attribute>
 
 	if (mutex->is_recursive)
 	{
-		sys_cond.Warning("Recursive mutex(%d)", mutex_id);
+		sys_cond.Warning("*** condition on recursive mutex(%d)", mutex_id);
 	}
 
 	Cond* cond = new Cond(mutex, attr->name_u64);
@@ -60,6 +60,136 @@ int sys_cond_destroy(u32 cond_id)
 	return CELL_OK;
 }
 
+int sys_cond_signal(u32 cond_id)
+{
+	sys_cond.Log("sys_cond_signal(cond_id=%d)", cond_id);
+
+	Cond* cond;
+	if (!Emu.GetIdManager().GetIDData(cond_id, cond))
+	{
+		return CELL_ESRCH;
+	}
+
+	Mutex* mutex = cond->mutex;
+	u32 tid = GetCurrentPPUThread().GetId();
+
+	bool was_locked = (mutex->m_mutex.GetOwner() == tid);
+
+	if (u32 target = (mutex->protocol == SYS_SYNC_PRIORITY ? cond->m_queue.pop_prio() : cond->m_queue.pop()))
+	{
+		if (!was_locked) // mutex hasn't been locked (don't care about mutex state)
+		{
+			mutex->m_mutex.lock(tid);
+			mutex->recursive = 1;
+			mutex->m_mutex.unlock(tid, target);
+		}
+		else // mutex has been locked (should preserve original mutex state)
+		{
+			mutex->recursive = 1;
+			mutex->m_mutex.unlock(tid, target);
+			mutex->m_mutex.lock(tid);
+			mutex->recursive = 1;
+		}
+	}
+
+	if (Emu.IsStopped())
+	{
+		ConLog.Warning("sys_cond_signal(id=%d) aborted", cond_id);
+	}
+
+	return CELL_OK;
+}
+
+int sys_cond_signal_all(u32 cond_id)
+{
+	sys_cond.Log("sys_cond_signal_all(cond_id=%d)", cond_id);
+
+	Cond* cond;
+	if (!Emu.GetIdManager().GetIDData(cond_id, cond))
+	{
+		return CELL_ESRCH;
+	}
+
+	Mutex* mutex = cond->mutex;
+	u32 tid = GetCurrentPPUThread().GetId();
+
+	bool was_locked = (mutex->m_mutex.GetOwner() == tid);
+
+	while (u32 target = (mutex->protocol == SYS_SYNC_PRIORITY ? cond->m_queue.pop_prio() : cond->m_queue.pop()))
+	{
+		if (!was_locked)
+		{
+			mutex->m_mutex.lock(tid);
+			mutex->recursive = 1;
+			mutex->m_mutex.unlock(tid, target);
+		}
+		else
+		{
+			mutex->recursive = 1;
+			mutex->m_mutex.unlock(tid, target);
+			mutex->m_mutex.lock(tid);
+			mutex->recursive = 1;
+		}
+	}
+
+	if (Emu.IsStopped())
+	{
+		ConLog.Warning("sys_cond_signal_all(id=%d) aborted", cond_id);
+	}
+
+	return CELL_OK;
+}
+
+int sys_cond_signal_to(u32 cond_id, u32 thread_id)
+{
+	sys_cond.Log("sys_cond_signal_to(cond_id=%d, thread_id=%d)", cond_id, thread_id);
+
+	Cond* cond;
+	if (!Emu.GetIdManager().GetIDData(cond_id, cond))
+	{
+		return CELL_ESRCH;
+	}
+
+	if (!Emu.GetIdManager().CheckID(thread_id))
+	{
+		return CELL_ESRCH;
+	}
+
+	if (!cond->m_queue.invalidate(thread_id))
+	{
+		return CELL_EPERM;
+	}
+
+	Mutex* mutex = cond->mutex;
+	u32 tid = GetCurrentPPUThread().GetId();
+
+	bool was_locked = (mutex->m_mutex.GetOwner() == tid);
+
+	u32 target = thread_id;
+	{
+		if (!was_locked)
+		{
+			mutex->m_mutex.lock(tid);
+			mutex->recursive = 1;
+			mutex->m_mutex.unlock(tid, target);
+		}
+		else
+		{
+			mutex->recursive = 1;
+			mutex->m_mutex.unlock(tid, target);
+			mutex->m_mutex.lock(tid);
+			mutex->recursive = 1;
+		}
+	}
+
+	if (Emu.IsStopped())
+	{
+		ConLog.Warning("sys_cond_signal_to(id=%d, to=%d) aborted", cond_id, thread_id);
+	}
+
+	return CELL_OK;
+}
+
 int sys_cond_wait(u32 cond_id, u64 timeout)
 {
 	sys_cond.Log("sys_cond_wait(cond_id=%d, timeout=%lld)", cond_id, timeout);
@@ -113,99 +243,4 @@ int sys_cond_wait(u32 cond_id, u64 timeout)
 			return CELL_OK;
 		}
 	}
-}
-
-int sys_cond_signal(u32 cond_id)
-{
-	sys_cond.Log("sys_cond_signal(cond_id=%d)", cond_id);
-
-	Cond* cond;
-	if (!Emu.GetIdManager().GetIDData(cond_id, cond))
-	{
-		return CELL_ESRCH;
-	}
-
-	Mutex* mutex = cond->mutex;
-	u32 tid = GetCurrentPPUThread().GetId();
-
-	if (u32 target = (mutex->protocol == SYS_SYNC_PRIORITY ? mutex->m_queue.pop_prio() : mutex->m_queue.pop()))
-	{
-		if (mutex->m_mutex.trylock(target) != SMR_OK)
-		{
-			mutex->m_mutex.lock(tid);
-			mutex->recursive = 1;
-			mutex->m_mutex.unlock(tid, target);
-		}
-	}
-
-	if (Emu.IsStopped())
-	{
-		ConLog.Warning("sys_cond_signal(id=%d) aborted", cond_id);
-	}
-
-	return CELL_OK;
-}
-
-int sys_cond_signal_all(u32 cond_id)
-{
-	sys_cond.Log("sys_cond_signal_all(cond_id=%d)", cond_id);
-
-	Cond* cond;
-	if (!Emu.GetIdManager().GetIDData(cond_id, cond))
-	{
-		return CELL_ESRCH;
-	}
-
-	Mutex* mutex = cond->mutex;
-	u32 tid = GetCurrentPPUThread().GetId();
-
-	while (u32 target = (mutex->protocol == SYS_SYNC_PRIORITY ? mutex->m_queue.pop_prio() : mutex->m_queue.pop()))
-	{
-		if (mutex->m_mutex.trylock(target) != SMR_OK)
-		{
-			mutex->m_mutex.lock(tid);
-			mutex->recursive = 1;
-			mutex->m_mutex.unlock(tid, target);
-		}
-	}
-
-	if (Emu.IsStopped())
-	{
-		ConLog.Warning("sys_cond_signal_all(id=%d) aborted", cond_id);
-	}
-
-	return CELL_OK;
-}
-
-int sys_cond_signal_to(u32 cond_id, u32 thread_id)
-{
-	sys_cond.Log("sys_cond_signal_to(cond_id=%d, thread_id=%d)", cond_id, thread_id);
-
-	Cond* cond;
-	if (!Emu.GetIdManager().GetIDData(cond_id, cond))
-	{
-		return CELL_ESRCH;
-	}
-
-	if (!cond->m_queue.invalidate(thread_id))
-	{
-		return CELL_EPERM;
-	}
-
-	Mutex* mutex = cond->mutex;
-	u32 tid = GetCurrentPPUThread().GetId();
-
-	if (mutex->m_mutex.trylock(thread_id) != SMR_OK)
-	{
-		mutex->m_mutex.lock(tid);
-		mutex->recursive = 1;
-		mutex->m_mutex.unlock(tid, thread_id);
-	}
-
-	if (Emu.IsStopped())
-	{
-		ConLog.Warning("sys_cond_signal_to(id=%d, to=%d) aborted", cond_id, thread_id);
-	}
-
-	return CELL_OK;
 }
\ 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 5a40ca84d6..b3340930ea 100644
--- a/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp
+++ b/rpcs3/Emu/SysCalls/lv2/SC_FileSystem.cpp
@@ -94,16 +94,43 @@ int cellFsRead(u32 fd, u32 buf_addr, u64 nbytes, mem64_t nread)
 	vfsStream* file;
 	if(!sys_fs.CheckId(fd, file)) return CELL_ESRCH;
 
-	if(Memory.IsGoodAddr(buf_addr) && !Memory.IsGoodAddr(buf_addr, nbytes))
+	if (nread.GetAddr() && !nread.IsGood()) return CELL_EFAULT;
+
+	u32 res = 0;
+	u32 count = nbytes;
+	if (nbytes != (u64)count) return CELL_ENOMEM;
+
+	if (!Memory.IsGoodAddr(buf_addr)) return CELL_EFAULT;
+
+	if (count) if (u32 frag = buf_addr & 4095) // memory page fragment
 	{
-		MemoryBlock& block = Memory.GetMemByAddr(buf_addr);
-		nbytes = block.GetSize() - (buf_addr - block.GetStartAddr());
+		u32 req = min(count, 4096 - frag);
+		u32 read = file->Read(Memory + buf_addr, req);
+		buf_addr += req;
+		res += read;
+		count -= req;
+		if (read < req) goto fin;
 	}
 
-	const u64 res = nbytes ? file->Read(Memory.GetMemFromAddr(buf_addr), nbytes) : 0;
+	for (u32 pages = count / 4096; pages > 0; pages--) // full pages
+	{
+		if (!Memory.IsGoodAddr(buf_addr)) goto fin; // ??? (probably EFAULT)
+		u32 read = file->Read(Memory + buf_addr, 4096);
+		buf_addr += 4096;
+		res += read;
+		count -= 4096;
+		if (read < 4096) goto fin;
+	}
 
-	if(nread.IsGood())
-		nread = res;
+	if (count) // last fragment
+	{
+		if (!Memory.IsGoodAddr(buf_addr)) goto fin;
+		res += file->Read(Memory + buf_addr, count);
+	}
+
+fin:
+
+	if (nread.GetAddr()) nread = res; // write value if not NULL
 
 	return CELL_OK;
 }
diff --git a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp
index 520883fecf..5206dc5627 100644
--- a/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp
+++ b/rpcs3/Emu/SysCalls/lv2/SC_GCM.cpp
@@ -14,7 +14,7 @@ int cellGcmCallback(u32 context_addr, u32 count)
 
 	const s32 res = ctx.current - ctx.begin - ctrl.put;
 
-	if(res > 0) memcpy(&Memory[ctx.begin], &Memory[ctx.current - res], res);
+	if(res > 0) Memory.Copy(ctx.begin, ctx.current - res, res);
 
 	ctx.current = ctx.begin + res;
 
diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.cpp
index 64539b0002..2b02cd1276 100644
--- a/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.cpp
+++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwcond.cpp
@@ -84,13 +84,22 @@ int sys_lwcond_signal(mem_ptr_t<sys_lwcond_t> lwcond)
 	mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
 	be_t<u32> tid = GetCurrentPPUThread().GetId();
 
+	bool was_locked = (mutex->mutex.GetOwner() == tid);
+
 	if (be_t<u32> target = (mutex->attribute.ToBE() == se32(SYS_SYNC_PRIORITY) ? sq->pop_prio() : sq->pop()))
 	{
-		if (mutex->mutex.owner.trylock(target) != SMR_OK)
+		if (!was_locked)
 		{
-			mutex->mutex.owner.lock(tid);
+			mutex->mutex.lock(tid);
+			mutex->recursive_count = 1;
+			mutex->mutex.unlock(tid, target);
+		}
+		else
+		{
+			mutex->recursive_count = 1;
+			mutex->mutex.unlock(tid, target);
+			mutex->mutex.lock(tid);
 			mutex->recursive_count = 1;
-			mutex->mutex.owner.unlock(tid, target);
 		}
 	}
 
@@ -120,13 +129,22 @@ int sys_lwcond_signal_all(mem_ptr_t<sys_lwcond_t> lwcond)
 	mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
 	be_t<u32> tid = GetCurrentPPUThread().GetId();
 
+	bool was_locked = (mutex->mutex.GetOwner() == tid);
+
 	while (be_t<u32> target = (mutex->attribute.ToBE() == se32(SYS_SYNC_PRIORITY) ? sq->pop_prio() : sq->pop()))
 	{
-		if (mutex->mutex.owner.trylock(target) != SMR_OK)
+		if (!was_locked)
 		{
-			mutex->mutex.owner.lock(tid);
+			mutex->mutex.lock(tid);
+			mutex->recursive_count = 1;
+			mutex->mutex.unlock(tid, target);
+		}
+		else
+		{
+			mutex->recursive_count = 1;
+			mutex->mutex.unlock(tid, target);
+			mutex->mutex.lock(tid);
 			mutex->recursive_count = 1;
-			mutex->mutex.owner.unlock(tid, target);
 		}
 	}
 
@@ -153,6 +171,11 @@ int sys_lwcond_signal_to(mem_ptr_t<sys_lwcond_t> lwcond, u32 ppu_thread_id)
 		return CELL_ESRCH;
 	}
 
+	if (!Emu.GetIdManager().CheckID(ppu_thread_id))
+	{
+		return CELL_ESRCH;
+	}
+
 	if (!sq->invalidate(ppu_thread_id))
 	{
 		return CELL_EPERM;
@@ -161,13 +184,23 @@ int sys_lwcond_signal_to(mem_ptr_t<sys_lwcond_t> lwcond, u32 ppu_thread_id)
 	mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
 	be_t<u32> tid = GetCurrentPPUThread().GetId();
 
-	be_t<u32> target = ppu_thread_id;
+	bool was_locked = (mutex->mutex.GetOwner() == tid);
 
-	if (mutex->mutex.owner.trylock(target) != SMR_OK)
+	be_t<u32> target = ppu_thread_id;
 	{
-		mutex->mutex.owner.lock(tid);
-		mutex->recursive_count = 1;
-		mutex->mutex.owner.unlock(tid, target);
+		if (!was_locked)
+		{
+			mutex->mutex.lock(tid);
+			mutex->recursive_count = 1;
+			mutex->mutex.unlock(tid, target);
+		}
+		else
+		{
+			mutex->recursive_count = 1;
+			mutex->mutex.unlock(tid, target);
+			mutex->mutex.lock(tid);
+			mutex->recursive_count = 1;
+		}
 	}
 
 	if (Emu.IsStopped())
@@ -197,7 +230,7 @@ int sys_lwcond_wait(mem_ptr_t<sys_lwcond_t> lwcond, u64 timeout)
 	u32 tid_le = GetCurrentPPUThread().GetId();
 	be_t<u32> tid = tid_le;
 
-	if (mutex->mutex.owner.GetOwner() != tid)
+	if (mutex->mutex.GetOwner() != tid)
 	{
 		return CELL_EPERM; // caller must own this lwmutex
 	}
@@ -205,7 +238,7 @@ int sys_lwcond_wait(mem_ptr_t<sys_lwcond_t> lwcond, u64 timeout)
 	sq->push(tid_le);
 
 	mutex->recursive_count = 0;
-	mutex->mutex.owner.unlock(tid);
+	mutex->mutex.unlock(tid);
 
 	u32 counter = 0;
 	const u32 max_counter = timeout ? (timeout / 1000) : ~0;
@@ -216,7 +249,7 @@ int sys_lwcond_wait(mem_ptr_t<sys_lwcond_t> lwcond, u64 timeout)
 		case SMR_OK: mutex->unlock(tid); break;
 		case SMR_SIGNAL: return CELL_OK;
 		} */
-		if (mutex->mutex.owner.GetOwner() == tid)
+		if (mutex->mutex.GetOwner() == tid)
 		{
 			_mm_mfence();
 			mutex->recursive_count = 1;
diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp
index 97de2549d0..2a42841c31 100644
--- a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp
+++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.cpp
@@ -28,9 +28,8 @@ int sys_lwmutex_create(mem_ptr_t<sys_lwmutex_t> lwmutex, mem_ptr_t<sys_lwmutex_a
 	}
 
 	lwmutex->attribute = attr->attr_protocol | attr->attr_recursive;
-	lwmutex->mutex.all_info() = 0;
-	lwmutex->mutex.all_info() = 0;
-	lwmutex->mutex.owner.initialize();
+	lwmutex->all_info() = ~0;
+	lwmutex->mutex.initialize();
 	//lwmutex->waiter = lwmutex->owner.GetOwner();
 	lwmutex->pad = 0;
 	lwmutex->recursive_count = 0;
@@ -54,10 +53,10 @@ int sys_lwmutex_destroy(mem_ptr_t<sys_lwmutex_t> lwmutex)
 	if (!Emu.GetIdManager().CheckID(sq_id)) return CELL_ESRCH;
 
 	// try to make it unable to lock
-	switch (int res = lwmutex->trylock(lwmutex->mutex.owner.GetDeadValue()))
+	switch (int res = lwmutex->trylock(lwmutex->mutex.GetDeadValue()))
 	{
 	case CELL_OK:
-		lwmutex->mutex.all_info() = 0;
+		lwmutex->all_info() = 0;
 		lwmutex->attribute = 0;
 		lwmutex->sleep_queue = 0;
 		Emu.GetIdManager().RemoveID(sq_id);
@@ -208,7 +207,7 @@ int sys_lwmutex_t::trylock(be_t<u32> tid)
 {
 	if (!attribute.ToBE()) return CELL_EINVAL;
 
-	if (tid == mutex.owner.GetOwner())
+	if (tid == mutex.GetOwner())
 	{
 		if (attribute.ToBE() & se32(SYS_SYNC_RECURSIVE))
 		{
@@ -222,7 +221,7 @@ int sys_lwmutex_t::trylock(be_t<u32> tid)
 		}
 	}
 
-	switch (mutex.owner.trylock(tid))
+	switch (mutex.trylock(tid))
 	{
 	case SMR_OK: recursive_count = 1; return CELL_OK;
 	case SMR_FAILED: return CELL_EBUSY;
@@ -232,7 +231,7 @@ int sys_lwmutex_t::trylock(be_t<u32> tid)
 
 int sys_lwmutex_t::unlock(be_t<u32> tid)
 {
-	if (tid != mutex.owner.GetOwner())
+	if (tid != mutex.GetOwner())
 	{
 		return CELL_EPERM;
 	}
@@ -251,8 +250,8 @@ int sys_lwmutex_t::unlock(be_t<u32> tid)
 				target = attribute.ToBE() & se32(SYS_SYNC_FIFO) ? sq->pop() : sq->pop_prio();
 			case se32(SYS_SYNC_RETRY): break;
 			}
-			if (target) mutex.owner.unlock(tid, target);
-			else mutex.owner.unlock(tid);
+			if (target) mutex.unlock(tid, target);
+			else mutex.unlock(tid);
 		}
 		return CELL_OK;
 	}
@@ -277,7 +276,7 @@ int sys_lwmutex_t::lock(be_t<u32> tid, u64 timeout)
 	default: break;
 	}
 
-	switch (mutex.owner.lock(tid, timeout))
+	switch (mutex.lock(tid, timeout))
 	{
 	case SMR_OK:
 		sq->invalidate(tid);
diff --git a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h
index 24c4f30edb..971363a92f 100644
--- a/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h
+++ b/rpcs3/Emu/SysCalls/lv2/SC_Lwmutex.h
@@ -65,13 +65,9 @@ struct SleepQueue
 
 struct sys_lwmutex_t
 {
-	struct sys_lwmutex_lock_info_t
-	{
-		/* volatile */ SMutexBase<be_t<u32>, 0xffffffff, 0> owner;
-		/* volatile */ be_t<u32> waiter; // not used
-		u64 &all_info(){return *(reinterpret_cast<u64*>(this));
-		}
-	}mutex;
+	/* volatile */ SMutexBase<be_t<u32>, ~0, 0> mutex;
+	/* volatile */ be_t<u32> waiter; // not used
+	u64 &all_info(){return *(reinterpret_cast<u64*>(this));}
 	be_t<u32> attribute;
 	be_t<u32> recursive_count;
 	be_t<u32> sleep_queue;
diff --git a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp
index 4b3c858264..02b898207e 100644
--- a/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp
+++ b/rpcs3/Emu/SysCalls/lv2/SC_SPU_Thread.cpp
@@ -134,7 +134,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.AllocAlign(256 * 1024);
-	memcpy(Memory + spu_offset, Memory + (u32)img->segs_addr, 256 * 1024);
+	Memory.CopyToReal(Memory + spu_offset, (u32)img->segs_addr, 256 * 1024);
 	//initialize from new place:
 	new_thread.SetOffset(spu_offset);
 	new_thread.SetEntry(spu_ep);
diff --git a/rpcs3/Gui/ConLog.cpp b/rpcs3/Gui/ConLog.cpp
index 33a324dd9e..7b5ee9300c 100644
--- a/rpcs3/Gui/ConLog.cpp
+++ b/rpcs3/Gui/ConLog.cpp
@@ -136,11 +136,22 @@ void LogWriter::WriteToLog(std::string prefix, std::string value, std::string co
 	if(wxThread::IsMain())
 #endif
 	{
-		while(LogBuffer.IsBusy()) wxYieldIfNeeded();
+		while(LogBuffer.IsBusy())
+		{
+			// need extra break condition?
+			wxYieldIfNeeded();
+		}
 	}
 	else
 	{
-		while(LogBuffer.IsBusy()) Sleep(1);
+		while (LogBuffer.IsBusy())
+		{
+			if (Emu.IsStopped())
+			{
+				break;
+			}
+			Sleep(1);
+		}
 	}
 
 	//if(LogBuffer.put == LogBuffer.get) LogBuffer.Flush();
diff --git a/rpcs3/Gui/MemoryViewer.cpp b/rpcs3/Gui/MemoryViewer.cpp
index 41d2fc85fb..90e36a0175 100644
--- a/rpcs3/Gui/MemoryViewer.cpp
+++ b/rpcs3/Gui/MemoryViewer.cpp
@@ -29,10 +29,10 @@ MemoryViewerPanel::MemoryViewerPanel(wxWindow* parent)
 	s_tools_mem_bytes.Add(sc_bytes);
 
 	wxStaticBoxSizer& s_tools_mem_buttons = *new wxStaticBoxSizer(wxHORIZONTAL, this, "Control");
-	wxButton* b_fprev = new wxButton(this, wxID_ANY, "\u00AB", wxDefaultPosition, wxSize(21, 21));
+	wxButton* b_fprev = new wxButton(this, wxID_ANY, "<<", wxDefaultPosition, wxSize(21, 21));
 	wxButton* b_prev  = new wxButton(this, wxID_ANY, "<", wxDefaultPosition, wxSize(21, 21));
 	wxButton* b_next  = new wxButton(this, wxID_ANY, ">", wxDefaultPosition, wxSize(21, 21));
-	wxButton* b_fnext = new wxButton(this, wxID_ANY, "\u00BB", wxDefaultPosition, wxSize(21, 21));
+	wxButton* b_fnext = new wxButton(this, wxID_ANY, ">>", wxDefaultPosition, wxSize(21, 21));
 	s_tools_mem_buttons.Add(b_fprev);
 	s_tools_mem_buttons.Add(b_prev);
 	s_tools_mem_buttons.Add(b_next);
diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj
index e26b99909b..2485aa67e7 100644
--- a/rpcs3/rpcs3.vcxproj
+++ b/rpcs3/rpcs3.vcxproj
@@ -69,20 +69,20 @@
   </ImportGroup>
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <IncludePath>.\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\..\ffmpeg;$(IncludePath)</IncludePath>
+    <IncludePath>.\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\Include;$(IncludePath)</IncludePath>
     <OutDir>$(SolutionDir)bin\</OutDir>
     <LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath>
     <TargetName>$(ProjectName)-$(PlatformShortName)-dbg</TargetName>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <IncludePath>.\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\..\ffmpeg;$(IncludePath)</IncludePath>
+    <IncludePath>.\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\Include;$(IncludePath)</IncludePath>
     <OutDir>$(SolutionDir)bin\</OutDir>
     <LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath>
     <TargetName>$(ProjectName)-$(PlatformShortName)-dbg</TargetName>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <LinkIncremental>false</LinkIncremental>
-    <IncludePath>.\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\..\ffmpeg;$(IncludePath)</IncludePath>
+    <IncludePath>.\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\Include;$(IncludePath)</IncludePath>
     <OutDir>$(SolutionDir)bin\</OutDir>
     <LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath>
     <LinkIncremental>false</LinkIncremental>
@@ -91,7 +91,7 @@
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <LinkIncremental>false</LinkIncremental>
-    <IncludePath>.\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\..\ffmpeg;$(IncludePath)</IncludePath>
+    <IncludePath>.\;..\wxWidgets\include;..\SDL-1.3.0-5538\include;..\SDL_image-1.2.10;..\pthreads-2.8.0;..\;..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\Include;$(IncludePath)</IncludePath>
     <OutDir>$(SolutionDir)bin\</OutDir>
     <LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath>
     <LinkIncremental>false</LinkIncremental>
@@ -109,10 +109,10 @@
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>wxmsw31ud_adv.lib;wxbase31ud.lib;wxmsw31ud_core.lib;wxmsw31ud_aui.lib;wxtiffd.lib;wxjpegd.lib;wxpngd.lib;wxzlibd.lib;odbc32.lib;odbccp32.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>wxmsw31ud_adv.lib;wxbase31ud.lib;wxmsw31ud_core.lib;wxmsw31ud_aui.lib;wxtiffd.lib;wxjpegd.lib;wxpngd.lib;wxzlibd.lib;odbc32.lib;odbccp32.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;rpcrt4.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <DataExecutionPrevention>false</DataExecutionPrevention>
-      <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_lib</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_lib;..\ffmpeg\Windows\x86\lib</AdditionalLibraryDirectories>
     </Link>
     <PreBuildEvent>
       <Command>
@@ -129,14 +129,17 @@
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
-      <AdditionalDependencies>wxmsw31ud_adv.lib;wxbase31ud.lib;wxmsw31ud_core.lib;wxmsw31ud_aui.lib;wxtiffd.lib;wxjpegd.lib;wxpngd.lib;wxzlibd.lib;odbc32.lib;odbccp32.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>wxmsw31ud_adv.lib;wxbase31ud.lib;wxmsw31ud_core.lib;wxmsw31ud_aui.lib;wxtiffd.lib;wxjpegd.lib;wxpngd.lib;wxzlibd.lib;odbc32.lib;odbccp32.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;rpcrt4.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <DataExecutionPrevention>false</DataExecutionPrevention>
-      <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_x64_lib</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_x64_lib;..\ffmpeg\Windows\x86_64\lib</AdditionalLibraryDirectories>
     </Link>
     <PreBuildEvent>
       <Command>"$(SolutionDir)\Utilities\git-version-gen.cmd"</Command>
     </PreBuildEvent>
+    <ProjectReference>
+      <UseLibraryDependencyInputs>false</UseLibraryDependencyInputs>
+    </ProjectReference>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
@@ -158,12 +161,12 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>wxmsw31u_adv.lib;wxbase31u.lib;wxmsw31u_core.lib;wxmsw31u_aui.lib;odbc32.lib;odbccp32.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;rpcrt4.lib;wxtiff.lib;wxjpeg.lib;wxpng.lib;wxzlib.lib;wxregexu.lib;wxexpat.lib;wsock32.lib;wininet.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>wxmsw31u_adv.lib;wxbase31u.lib;wxmsw31u_core.lib;wxmsw31u_aui.lib;odbc32.lib;odbccp32.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;rpcrt4.lib;wxtiff.lib;wxjpeg.lib;wxpng.lib;wxzlib.lib;wxregexu.lib;wxexpat.lib;wsock32.lib;wininet.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreAllDefaultLibraries>
       </IgnoreAllDefaultLibraries>
       <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <DataExecutionPrevention>false</DataExecutionPrevention>
-      <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_lib</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_lib;..\ffmpeg\Windows\x86\lib</AdditionalLibraryDirectories>
     </Link>
     <PreBuildEvent>
       <Command>
@@ -190,12 +193,12 @@
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <EnableCOMDATFolding>true</EnableCOMDATFolding>
       <OptimizeReferences>true</OptimizeReferences>
-      <AdditionalDependencies>wxmsw31u_adv.lib;wxbase31u.lib;wxmsw31u_core.lib;wxmsw31u_aui.lib;odbc32.lib;odbccp32.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;rpcrt4.lib;wxtiff.lib;wxjpeg.lib;wxpng.lib;wxzlib.lib;wxregexu.lib;wxexpat.lib;wsock32.lib;wininet.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>wxmsw31u_adv.lib;wxbase31u.lib;wxmsw31u_core.lib;wxmsw31u_aui.lib;odbc32.lib;odbccp32.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;rpcrt4.lib;wxtiff.lib;wxjpeg.lib;wxpng.lib;wxzlib.lib;wxregexu.lib;wxexpat.lib;wsock32.lib;wininet.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <IgnoreAllDefaultLibraries>
       </IgnoreAllDefaultLibraries>
       <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
       <DataExecutionPrevention>false</DataExecutionPrevention>
-      <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_x64_lib</AdditionalLibraryDirectories>
+      <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_x64_lib;..\ffmpeg\Windows\x86_64\lib</AdditionalLibraryDirectories>
     </Link>
     <PreBuildEvent>
       <Command>
@@ -343,10 +346,12 @@
     <ClInclude Include="..\Utilities\IdManager.h" />
     <ClInclude Include="..\Utilities\MTProgressDialog.h" />
     <ClInclude Include="..\Utilities\SMutex.h" />
+    <ClInclude Include="..\Utilities\SQueue.h" />
     <ClInclude Include="..\Utilities\Thread.h" />
     <ClInclude Include="..\Utilities\Timer.h" />
     <ClInclude Include="Emu\Audio\AudioManager.h" />
     <ClInclude Include="Emu\Audio\cellAudio.h" />
+    <ClInclude Include="Emu\Cell\MFC.h" />
     <ClInclude Include="Emu\Cell\PPCDecoder.h" />
     <ClInclude Include="Emu\Cell\PPCDisAsm.h" />
     <ClInclude Include="Emu\Cell\PPCInstrTable.h" />
diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters
index 94f355e832..836298b66a 100644
--- a/rpcs3/rpcs3.vcxproj.filters
+++ b/rpcs3/rpcs3.vcxproj.filters
@@ -603,5 +603,11 @@
     <ClInclude Include="..\Utilities\SMutex.h">
       <Filter>Utilities</Filter>
     </ClInclude>
+    <ClInclude Include="..\Utilities\SQueue.h">
+      <Filter>Utilities</Filter>
+    </ClInclude>
+    <ClInclude Include="Emu\Cell\MFC.h">
+      <Filter>Include</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>
\ No newline at end of file