This commit is contained in:
Aishou 2014-03-02 11:57:17 +01:00
commit 7a1d44b552
42 changed files with 2804 additions and 314 deletions

2
.gitignore vendored
View File

@ -41,6 +41,8 @@
/bin/VertexProgram.txt /bin/VertexProgram.txt
/bin/BreakPoints.dat /bin/BreakPoints.dat
/bin/textures /bin/textures
/bin/*.lib
/bin/*.exp
rpcs3/git-version.h rpcs3/git-version.h
# Copyrighted files # Copyrighted files

3
.gitmodules vendored
View File

@ -2,3 +2,6 @@
path = wxWidgets path = wxWidgets
url = https://github.com/DHrpcs3/wxWidgets.git url = https://github.com/DHrpcs3/wxWidgets.git
ignore = dirty ignore = dirty
[submodule "rpcs3-ffmpeg"]
path = ffmpeg
url = https://github.com/hrydgard/ppsspp-ffmpeg

View File

@ -6,9 +6,9 @@ __forceinline void SM_Sleep()
Sleep(1); 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() __forceinline u32 SM_GetCurrentCPUThreadId()

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
extern void SM_Sleep(); extern void SM_Sleep();
extern std::thread::id SM_GetCurrentThreadId(); extern size_t SM_GetCurrentThreadId();
extern u32 SM_GetCurrentCPUThreadId(); extern u32 SM_GetCurrentCPUThreadId();
extern be_t<u32> SM_GetCurrentCPUThreadIdBE(); extern be_t<u32> SM_GetCurrentCPUThreadIdBE();
@ -20,13 +20,13 @@ enum SMutexResult
template template
< <
typename T, typename T,
u32 free_value = 0, u64 free_value = 0,
u32 dead_value = ~0, u64 dead_value = ~0,
void (*wait)() = SM_Sleep void (*wait)() = SM_Sleep
> >
class SMutexBase 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; std::atomic<T> owner;
public: public:
@ -157,14 +157,14 @@ public:
} }
}; };
typedef SMutexBase<DWORD> typedef SMutexBase<size_t>
SMutexGeneral; SMutexGeneral;
typedef SMutexBase<u32> typedef SMutexBase<u32>
SMutex; SMutex;
typedef SMutexBase<be_t<u32>> typedef SMutexBase<be_t<u32>>
SMutexBE; SMutexBE;
typedef SMutexLockerBase<std::thread::id, SM_GetCurrentThreadId> typedef SMutexLockerBase<size_t, SM_GetCurrentThreadId>
SMutexGeneralLocker; SMutexGeneralLocker;
typedef SMutexLockerBase<u32, SM_GetCurrentCPUThreadId> typedef SMutexLockerBase<u32, SM_GetCurrentCPUThreadId>
SMutexLocker; SMutexLocker;

99
Utilities/SQueue.h Normal file
View File

@ -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;
}
};

View File

@ -117,10 +117,12 @@ thread::thread()
} }
void thread::start(std::function<void()> func) 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; g_tls_this_thread = &info;
try try
@ -130,7 +132,7 @@ void thread::start(std::function<void()> func)
catch(...) catch(...)
{ {
ConLog.Error("Crash :("); ConLog.Error("Crash :(");
std::terminate(); //std::terminate();
} }
}); });
} }

1
ffmpeg Submodule

@ -0,0 +1 @@
Subproject commit 8bcaa2485c2434d7d7a9da17491bafb58de42bb6

View File

@ -296,7 +296,7 @@ void CPUThread::ExecOnce()
void CPUThread::Task() 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(); const Array<u64>& bp = Emu.GetBreakPoints();
@ -358,5 +358,5 @@ void CPUThread::Task()
ConLog.Success("Exit Code: %d", exitcode); 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());
} }

View File

@ -168,11 +168,11 @@ struct DMAC
switch(cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK)) switch(cmd & ~(MFC_BARRIER_MASK | MFC_FENCE_MASK))
{ {
case MFC_PUT_CMD: case MFC_PUT_CMD:
memcpy(Memory + ea, Memory + ls_offset + lsa, size); Memory.Copy(ea, ls_offset + lsa, size);
return true; return true;
case MFC_GET_CMD: case MFC_GET_CMD:
memcpy(Memory + ls_offset + lsa, Memory + ea, size); Memory.Copy(ls_offset + lsa, ea, size);
return true; return true;
default: default:

View File

@ -251,7 +251,7 @@ u32 RawSPUThread::GetIndex() const
void RawSPUThread::Task() 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(); const Array<u64>& bp = Emu.GetBreakPoints();
@ -334,5 +334,5 @@ void RawSPUThread::Task()
ConLog.Error("Exception: %s", wxString(e).wx_str()); 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());
} }

View File

@ -30,7 +30,14 @@ DbgConsole::~DbgConsole()
void DbgConsole::Write(int ch, const wxString& text) 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)); m_dbg_buffer.Push(DbgPacket(ch, text));
if(!IsAlive()) Start(); if(!IsAlive()) Start();
@ -47,6 +54,10 @@ void DbgConsole::Task()
{ {
if(!m_dbg_buffer.HasNewPacket()) if(!m_dbg_buffer.HasNewPacket())
{ {
if (Emu.IsStopped())
{
break;
}
Sleep(1); Sleep(1);
continue; continue;
} }

View File

@ -30,7 +30,7 @@ bool vfsDir::Create(const wxString& path)
bool vfsDir::IsExists(const wxString& path) const 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 const Array<DirEntryInfo>& vfsDir::GetEntries() const

View File

@ -32,7 +32,7 @@ u64 vfsStreamMemory::Write(const void* src, u64 size)
if(!size || !Memory.IsGoodAddr(m_addr + Tell(), size)) return 0; 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); 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; 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); return vfsStream::Read(dst, size);
} }

View File

@ -56,7 +56,7 @@ void RSXVertexData::Load(u32 start, u32 count)
{ {
case 1: case 1:
{ {
memcpy(dst, src, size); memcpy(dst, src, size); // may be dangerous
} }
break; break;

View File

@ -48,18 +48,16 @@ bool MemoryBlock::GetMemFromAddr(void* dst, const u64 addr, const u32 size)
{ {
if(!IsMyAddress(addr) || FixAddr(addr) + size > GetSize()) return false; if(!IsMyAddress(addr) || FixAddr(addr) + size > GetSize()) return false;
memcpy(dst, GetMem(FixAddr(addr)), size); // mem cpy(dst, GetMem(FixAddr(addr)), size);
return Memory.CopyToReal(dst, (u32)addr, size);
return true;
} }
bool MemoryBlock::SetMemFromAddr(void* src, const u64 addr, const u32 size) bool MemoryBlock::SetMemFromAddr(void* src, const u64 addr, const u32 size)
{ {
if(!IsMyAddress(addr) || FixAddr(addr) + size > GetSize()) return false; if(!IsMyAddress(addr) || FixAddr(addr) + size > GetSize()) return false;
memcpy(GetMem(FixAddr(addr)), src, size); // mem cpy(GetMem(FixAddr(addr)), src, size);
return Memory.CopyFromReal((u32)addr, src, size);
return true;
} }
bool MemoryBlock::GetMemFFromAddr(void* dst, const u64 addr) bool MemoryBlock::GetMemFFromAddr(void* dst, const u64 addr)

View File

@ -238,6 +238,106 @@ public:
u64 Read64(const u64 addr); u64 Read64(const u64 addr);
u128 Read128(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) void ReadLeft(u8* dst, const u64 addr, const u32 size)
{ {
MemoryBlock& mem = GetMemByAddr(addr); MemoryBlock& mem = GetMemByAddr(addr);

View File

@ -53,10 +53,33 @@ void Callback::Branch(bool wait)
{ {
m_has_data = false; m_has_data = false;
static SMutexGeneral cb_mutex;
CPUThread& thr = Emu.GetCallbackThread(); CPUThread& thr = Emu.GetCallbackThread();
while(Emu.IsRunning() && thr.IsAlive()) again:
while (thr.IsAlive())
{
if (Emu.IsStopped())
{
ConLog.Warning("Callback::Branch() aborted");
return;
}
Sleep(1); Sleep(1);
}
SMutexGeneralLocker lock(cb_mutex);
if (thr.IsAlive())
{
goto again;
}
if (Emu.IsStopped())
{
ConLog.Warning("Callback::Branch() aborted");
return;
}
thr.Stop(); thr.Stop();
thr.Reset(); thr.Reset();
@ -74,8 +97,20 @@ void Callback::Branch(bool wait)
thr.Exec(); thr.Exec();
if(wait) if (!wait)
GetCurrentPPCThread()->Wait(thr); {
return;
}
while (thr.IsAlive())
{
if (Emu.IsStopped())
{
ConLog.Warning("Callback::Branch(true) aborted (end)");
return;
}
Sleep(1);
}
} }
void Callback::SetName(const std::string& name) void Callback::SetName(const std::string& name)

View File

@ -135,6 +135,7 @@ struct CellAudioPortConfig
struct AudioPortConfig struct AudioPortConfig
{ {
SMutex m_mutex;
bool m_is_audio_port_opened; bool m_is_audio_port_opened;
bool m_is_audio_port_started; bool m_is_audio_port_started;
u8 channel; u8 channel;
@ -298,7 +299,7 @@ int cellAudioInit()
m_config.m_indexes = Memory.Alloc(sizeof(u64) * m_config.AUDIO_PORT_COUNT, 16); 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); 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) WAVHeader header(2); // WAV file header (stereo)
@ -366,10 +367,12 @@ int cellAudioInit()
memcpy(buffer2, Memory + buf_addr, block_size * sizeof(float)); memcpy(buffer2, Memory + buf_addr, block_size * sizeof(float));
memset(Memory + buf_addr, 0, block_size * sizeof(float)); memset(Memory + buf_addr, 0, block_size * sizeof(float));
// TODO: atomic {
port.counter = m_config.counter; SMutexLocker lock(port.m_mutex);
port.tag++; // absolute index of block that will be read port.counter = m_config.counter;
index = (position + 1) % port.block; // write new value port.tag++; // absolute index of block that will be read
index = (position + 1) % port.block; // write new value
}
if (first_mix) if (first_mix)
{ {
@ -599,7 +602,7 @@ int cellAudioPortStop(u32 portNum)
int cellAudioGetPortTimestamp(u32 portNum, u64 tag, mem64_t stamp) 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) 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]; 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; stamp = m_config.start_time + (port.counter + (tag - port.tag)) * 256000000 / 48000;
return CELL_OK; 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) 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) 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; return CELL_AUDIO_ERROR_PARAM;
} }
// TODO: atomic SMutexLocker lock(port.m_mutex);
u64 tag_base = port.tag; u64 tag_base = port.tag;
if (tag_base % port.block > blockNo) if (tag_base % port.block > blockNo)
{ {
@ -709,13 +714,13 @@ int cellAudioSetNotifyEventQueue(u64 key)
m_config.event_key = key; m_config.event_key = key;
EventQueue* eq; /*EventQueue* eq;
if (!Emu.GetEventManager().GetEventQueue(key, eq)) if (!Emu.GetEventManager().GetEventQueue(key, eq))
{ {
return CELL_AUDIO_ERROR_PARAM; return CELL_AUDIO_ERROR_PARAM;
} }*/
// TODO: connect port // TODO: connect port (?????)
return CELL_OK; return CELL_OK;
} }

View File

@ -7,80 +7,646 @@
void cellDmux_init(); void cellDmux_init();
Module cellDmux(0x0007, 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) 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; return CELL_OK;
} }
int cellDmuxQueryAttr2(const mem_ptr_t<CellDmuxType2> demuxerType2, mem_ptr_t<CellDmuxAttr> demuxerAttr) 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; return CELL_OK;
} }
int cellDmuxOpen(const mem_ptr_t<CellDmuxType> demuxerType, const mem_ptr_t<CellDmuxResource> demuxerResource, int cellDmuxOpen(const mem_ptr_t<CellDmuxType> demuxerType, const mem_ptr_t<CellDmuxResource> demuxerResource,
const mem_ptr_t<CellDmuxCb> demuxerCb, mem32_t demuxerHandle) 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()); 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; return CELL_OK;
} }
int cellDmuxOpenEx(const mem_ptr_t<CellDmuxType> demuxerType, const mem_ptr_t<CellDmuxResourceEx> demuxerResourceEx, int cellDmuxOpenEx(const mem_ptr_t<CellDmuxType> demuxerType, const mem_ptr_t<CellDmuxResourceEx> demuxerResourceEx,
const mem_ptr_t<CellDmuxCb> demuxerCb, mem32_t demuxerHandle) 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()); 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; return CELL_OK;
} }
int cellDmuxOpen2(const mem_ptr_t<CellDmuxType2> demuxerType2, const mem_ptr_t<CellDmuxResource2> demuxerResource2, int cellDmuxOpen2(const mem_ptr_t<CellDmuxType2> demuxerType2, const mem_ptr_t<CellDmuxResource2> demuxerResource2,
const mem_ptr_t<CellDmuxCb> demuxerCb, mem32_t demuxerHandle) 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()); 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; return CELL_OK;
} }
int cellDmuxClose(u32 demuxerHandle) 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; return CELL_OK;
} }
int cellDmuxSetStream(u32 demuxerHandle, const u32 streamAddress, u32 streamSize, bool discontinuity, u64 userData) 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); 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; return CELL_OK;
} }
int cellDmuxResetStream(u32 demuxerHandle) 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; return CELL_OK;
} }
int cellDmuxResetStreamAndWaitDone(u32 demuxerHandle) 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; return CELL_OK;
} }
int cellDmuxQueryEsAttr(const mem_ptr_t<CellDmuxType> demuxerType, const mem_ptr_t<CellCodecEsFilterId> esFilterId, int cellDmuxQueryEsAttr(const mem_ptr_t<CellDmuxType> demuxerType, const mem_ptr_t<CellCodecEsFilterId> esFilterId,
const u32 esSpecificInfo_addr, mem_ptr_t<CellDmuxEsAttr> esAttr) 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()); 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; return CELL_OK;
} }
int cellDmuxQueryEsAttr2(const mem_ptr_t<CellDmuxType2> demuxerType2, const mem_ptr_t<CellCodecEsFilterId> esFilterId, int cellDmuxQueryEsAttr2(const mem_ptr_t<CellDmuxType2> demuxerType2, const mem_ptr_t<CellCodecEsFilterId> esFilterId,
const u32 esSpecificInfo_addr, mem_ptr_t<CellDmuxEsAttr> esAttr) 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()); 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; 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 mem_ptr_t<CellDmuxEsResource> esResourceInfo, const mem_ptr_t<CellDmuxEsCb> esCb,
const u32 esSpecificInfo_addr, mem32_t esHandle) 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(), "esSpecificInfo_addr=0x%x, esHandle_addr=0x%x)", demuxerHandle, esFilterId.GetAddr(), esResourceInfo.GetAddr(),
esCb.GetAddr(), esSpecificInfo_addr, esHandle.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; return CELL_OK;
} }
int cellDmuxDisableEs(u32 esHandle) 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; return CELL_OK;
} }
int cellDmuxResetEs(u32 esHandle) 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; 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)", cellDmux.Log("cellDmuxGetAu(esHandle=0x%x, auInfo_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
esHandle, auInfo_ptr_addr, auSpecificInfo_ptr_addr); 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; 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)", cellDmux.Log("cellDmuxPeekAu(esHandle=0x%x, auInfo_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
esHandle, auInfo_ptr_addr, auSpecificInfo_ptr_addr); 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; 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)", cellDmux.Log("cellDmuxGetAuEx(esHandle=0x%x, auInfoEx_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
esHandle, auInfoEx_ptr_addr, auSpecificInfo_ptr_addr); 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; 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)", cellDmux.Log("cellDmuxPeekAuEx(esHandle=0x%x, auInfoEx_ptr_addr=0x%x, auSpecificInfo_ptr_addr=0x%x)",
esHandle, auInfoEx_ptr_addr, auSpecificInfo_ptr_addr); 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; return CELL_OK;
} }
int cellDmuxReleaseAu(u32 esHandle) 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; return CELL_OK;
} }
int cellDmuxFlushEs(u32 esHandle) 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; return CELL_OK;
} }

View File

@ -1,5 +1,10 @@
#pragma once #pragma once
#include "Utilities/SQueue.h"
// align size or address to 128
#define a128(x) ((x + 127) & (~127))
// Error Codes // Error Codes
enum enum
{ {
@ -30,6 +35,118 @@ enum CellDmuxEsMsgType
CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE = 1, 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 struct CellDmuxMsg
{ {
be_t<CellDmuxMsgType> msgType; //CellDmuxMsgType enum be_t<CellDmuxMsgType> msgType; //CellDmuxMsgType enum
@ -44,13 +161,19 @@ struct CellDmuxEsMsg
struct CellDmuxType struct CellDmuxType
{ {
CellDmuxStreamType streamType; be_t<CellDmuxStreamType> streamType;
be_t<u32> reserved[2]; //0 be_t<u32> reserved[2]; //0
}; };
struct CellDmuxPamfSpecificInfo
{
be_t<u32> thisSize;
bool programEndCodeCb;
};
struct CellDmuxType2 struct CellDmuxType2
{ {
CellDmuxStreamType streamType; be_t<CellDmuxStreamType> streamType;
be_t<u32> streamSpecificInfo_addr; be_t<u32> streamSpecificInfo_addr;
}; };
@ -99,17 +222,21 @@ struct CellDmuxResource2
be_t<u32> shit[4]; be_t<u32> shit[4];
}; };
typedef mem_func_ptr_t<void (*)(u32 demuxerHandle, mem_ptr_t<CellDmuxMsg> demuxerMsg, u32 cbArg_addr)> CellDmuxCbMsg;
struct CellDmuxCb struct CellDmuxCb
{ {
// CellDmuxCbMsg callback // 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; 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 struct CellDmuxEsCb
{ {
// CellDmuxCbEsMsg callback // 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; be_t<u32> cbArg_addr;
}; };
@ -153,3 +280,414 @@ struct CellDmuxAuInfoEx
CellCodecTimeStamp pts; CellCodecTimeStamp pts;
CellCodecTimeStamp dts; 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;
}
};

View File

@ -164,7 +164,7 @@ int cellGifDecDecodeData(u32 mainHandle, u32 subHandle, mem8_ptr_t data, const m
switch(current_outParam.outputColorSpace) switch(current_outParam.outputColorSpace)
{ {
case CELL_GIFDEC_RGBA: case CELL_GIFDEC_RGBA:
memcpy(data, image.get(), image_size); Memory.CopyFromReal(data.GetAddr(), image.get(), image_size);
break; break;
case CELL_GIFDEC_ARGB: case CELL_GIFDEC_ARGB:

View File

@ -148,7 +148,7 @@ int cellJpgDecDecodeData(u32 mainHandle, u32 subHandle, mem8_ptr_t data, const m
case CELL_JPG_RGBA: case CELL_JPG_RGBA:
case CELL_JPG_RGB: case CELL_JPG_RGB:
image_size *= current_outParam.outputColorSpace == CELL_JPG_RGBA ? 4 : 3; 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; break;
case CELL_JPG_ARGB: case CELL_JPG_ARGB:

View File

@ -17,26 +17,16 @@ int pamfStreamTypeToEsFilterId(u8 type, u8 ch, mem_ptr_t<CellCodecEsFilterId> pE
switch (type) switch (type)
{ {
case CELL_PAMF_STREAM_TYPE_AVC: 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->filterIdMinor = 0;
pEsFilterId->supplementalInfo1 = 0x01; pEsFilterId->supplementalInfo1 = 0x01;
pEsFilterId->supplementalInfo2 = 0; pEsFilterId->supplementalInfo2 = 0;
} }
break; else
case 1: cellPamf.Error("pamfStreamTypeToEsFilterId: invalid CELL_PAMF_STREAM_TYPE_AVC channel (ch=%d)", ch);
{
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);
} }
break; break;
case CELL_PAMF_STREAM_TYPE_ATRAC3PLUS: 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 0x80: return CELL_PAMF_STREAM_TYPE_PAMF_LPCM;
case 0xdd: return CELL_PAMF_STREAM_TYPE_USER_DATA; case 0xdd: return CELL_PAMF_STREAM_TYPE_USER_DATA;
default: 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); pAddr->stream_headers[stream].type);
return 0; return 0;
} }
@ -104,12 +94,16 @@ u8 pamfGetStreamType(mem_ptr_t<CellPamfReader> pSelf, u8 stream)
u8 pamfGetStreamChannel(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 //TODO: get stream channel correctly
const mem_ptr_t<PamfHeader> pAddr(pSelf->pAddr); const mem_ptr_t<PamfHeader> pAddr(pSelf->pAddr);
if ((pAddr->stream_headers[stream].type == 0x1b) && 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; return 0;
} }
@ -122,7 +116,7 @@ int cellPamfGetHeaderSize(mem_ptr_t<PamfHeader> pAddr, u64 fileSize, mem64_t pSi
//return CELL_PAMF_ERROR_UNKNOWN_TYPE; //return CELL_PAMF_ERROR_UNKNOWN_TYPE;
const u64 offset = (u64)pAddr->data_offset << 11; const u64 offset = (u64)pAddr->data_offset << 11;
pSize = offset /*? offset : 2048*/; //hack pSize = offset;
return CELL_OK; return CELL_OK;
} }
@ -135,12 +129,10 @@ int cellPamfGetHeaderSize2(mem_ptr_t<PamfHeader> pAddr, u64 fileSize, u32 attrib
//return CELL_PAMF_ERROR_UNKNOWN_TYPE; //return CELL_PAMF_ERROR_UNKNOWN_TYPE;
const u64 offset = (u64)pAddr->data_offset << 11; const u64 offset = (u64)pAddr->data_offset << 11;
pSize = offset /*? offset : 2048*/; //hack pSize = offset;
return CELL_OK; return CELL_OK;
} }
//u32 hack_LastHeader = 0;
int cellPamfGetStreamOffsetAndSize(mem_ptr_t<PamfHeader> pAddr, u64 fileSize, mem64_t pOffset, mem64_t pSize) 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)", 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; //return CELL_PAMF_ERROR_UNKNOWN_TYPE;
const u64 offset = (u64)pAddr->data_offset << 11; const u64 offset = (u64)pAddr->data_offset << 11;
pOffset = offset /*? offset : 2048*/; //hack pOffset = offset;
const u64 size = (u64)pAddr->data_size << 11; const u64 size = (u64)pAddr->data_size << 11;
pSize = size /*? size : (fileSize - 2048)*/; //hack pSize = size;
//if (!(u32)pAddr->magic) hack_LastHeader = pAddr.GetAddr();
return CELL_OK; 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->fileSize = ((u64)pAddr->data_offset << 11) + ((u64)pAddr->data_size << 11);
} }
pSelf->pAddr = pAddr.GetAddr(); pSelf->pAddr = pAddr.GetAddr();
//if (hack_LastHeader) memcpy(Memory + pAddr.GetAddr(), Memory + hack_LastHeader, 2048);
if (attribute & CELL_PAMF_ATTRIBUTE_VERIFY_ON) if (attribute & CELL_PAMF_ATTRIBUTE_VERIFY_ON)
{ {
//TODO //TODO

View File

@ -135,13 +135,15 @@ enum
}; };
// Timestamp information (time in increments of 90 kHz) // Timestamp information (time in increments of 90 kHz)
struct CellCodecTimeStamp { struct CellCodecTimeStamp
{
be_t<u32> upper; be_t<u32> upper;
be_t<u32> lower; be_t<u32> lower;
}; };
// Entry point information // Entry point information
struct CellPamfEp { struct CellPamfEp
{
be_t<u32> indexN; be_t<u32> indexN;
be_t<u32> nThRefPictureOffset; be_t<u32> nThRefPictureOffset;
CellCodecTimeStamp pts; CellCodecTimeStamp pts;

View File

@ -91,7 +91,7 @@ int cellPngDecReadHeader(u32 mainHandle, u32 subHandle, mem_ptr_t<CellPngDecInfo
switch(subHandle_data->src.srcSelect.ToLE()) switch(subHandle_data->src.srcSelect.ToLE())
{ {
case CELL_PNGDEC_BUFFER: 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; break;
case CELL_PNGDEC_FILE: case CELL_PNGDEC_FILE:
cellFsLseek(fd, 0, CELL_SEEK_SET, pos); 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()) switch(subHandle_data->src.srcSelect.ToLE())
{ {
case CELL_PNGDEC_BUFFER: 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; break;
case CELL_PNGDEC_FILE: case CELL_PNGDEC_FILE:
cellFsLseek(fd, 0, CELL_SEEK_SET, pos); 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_RGB:
case CELL_PNGDEC_RGBA: case CELL_PNGDEC_RGBA:
image_size *= current_outParam.outputColorSpace == CELL_PNGDEC_RGBA ? 4 : 3; 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; break;
case CELL_PNGDEC_ARGB: case CELL_PNGDEC_ARGB:

View File

@ -1,76 +1,578 @@
#include "stdafx.h" #include "stdafx.h"
#include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SysCalls.h"
#include "Emu/SysCalls/SC_FUNC.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" #include "cellVdec.h"
void cellVdec_init(); void cellVdec_init();
Module cellVdec(0x0005, 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) 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()); cellVdec.Warning("cellVdecQueryAttr(type_addr=0x%x, attr_addr=0x%x)", type.GetAddr(), attr.GetAddr());
return CELL_OK;
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) 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()); cellVdec.Warning("cellVdecQueryAttrEx(type_addr=0x%x, attr_addr=0x%x)", type.GetAddr(), attr.GetAddr());
return CELL_OK;
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) 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()); 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; 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) 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()); 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; return CELL_OK;
} }
int cellVdecClose(u32 handle) 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; return CELL_OK;
} }
int cellVdecStartSeq(u32 handle) 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; return CELL_OK;
} }
int cellVdecEndSeq(u32 handle) 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; return CELL_OK;
} }
int cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, const mem_ptr_t<CellVdecAuInfo> auInfo) 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; return CELL_OK;
} }
int cellVdecGetPicture(u32 handle, const mem_ptr_t<CellVdecPicFormat> format, u32 out_addr) 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; 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; return CELL_OK;
} }
int cellVdecSetFrameRate(u32 handle, CellVdecFrameRate frc) 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; return CELL_OK;
} }
@ -87,4 +589,6 @@ void cellVdec_init()
cellVdec.AddFunc(0x807c861a, cellVdecGetPicture); cellVdec.AddFunc(0x807c861a, cellVdecGetPicture);
cellVdec.AddFunc(0x17c702b9, cellVdecGetPicItem); cellVdec.AddFunc(0x17c702b9, cellVdecGetPicItem);
cellVdec.AddFunc(0xe13ef6fc, cellVdecSetFrameRate); cellVdec.AddFunc(0xe13ef6fc, cellVdecSetFrameRate);
avcodec_register_all();
} }

View File

@ -1,5 +1,8 @@
#pragma once #pragma once
#include "cellPamf.h"
#include "Utilities/SQueue.h"
#define a128(x) ((x + 127) & (~127))
// Error Codes // Error Codes
enum enum
@ -30,7 +33,7 @@ enum CellVdecMsgType
}; };
// Decoder Operation Mode // Decoder Operation Mode
enum CellVdecDecodeMode enum CellVdecDecodeMode : u32
{ {
CELL_VDEC_DEC_MODE_NORMAL, CELL_VDEC_DEC_MODE_NORMAL,
CELL_VDEC_DEC_MODE_B_SKIP, CELL_VDEC_DEC_MODE_B_SKIP,
@ -164,11 +167,13 @@ struct CellVdecPicFormat
u8 alpha; u8 alpha;
}; };
typedef mem_func_ptr_t<void (*)(u32 handle_addr, CellVdecMsgType msgType, int msgData, u32 cbArg_addr)> CellVdecCbMsg;
// Callback Function Information // Callback Function Information
struct CellVdecCb struct CellVdecCb
{ {
be_t<mem_func_ptr_t<void (*)(u32 handle_addr, CellVdecMsgType msgType, int msgData, u32 cbArg_addr)>> cbFunc; be_t<u32> cbFunc;
be_t<u32> cbArg_addr; be_t<u32> cbArg;
}; };
// Max CC Data Length // Max CC Data Length
@ -338,16 +343,18 @@ struct CellVdecAvcInfo
AVC_transfer_characteristics transfer_characteristics; AVC_transfer_characteristics transfer_characteristics;
AVC_matrix_coefficients matrix_coefficients; AVC_matrix_coefficients matrix_coefficients;
bool timing_info_present_flag; bool timing_info_present_flag;
CellVdecFrameRate frameRateCode; AVC_FrameRateCode frameRateCode; // ???
bool fixed_frame_rate_flag; bool fixed_frame_rate_flag;
bool low_delay_hrd_flag; bool low_delay_hrd_flag;
bool entropy_coding_mode_flag; bool entropy_coding_mode_flag;
be_t<AVC_NulUnitPresentFlags> nalUnitPresentFlags; be_t<u16> nalUnitPresentFlags;
u8 ccDataLength[2]; u8 ccDataLength[2];
u8 ccData[2][CELL_VDEC_AVC_CCD_MAX]; u8 ccData[2][CELL_VDEC_AVC_CCD_MAX];
be_t<u64> reserved[2]; be_t<u64> reserved[2];
}; };
const int sz = sizeof(CellVdecAvcInfo);
// DIVX Profile // DIVX Profile
enum DIVX_level : u8 enum DIVX_level : u8
{ {
@ -634,4 +641,153 @@ struct CellVdecMpeg2Info
u8 ccDataLength[2]; u8 ccDataLength[2];
u8 ccData[2][128]; u8 ccData[2][128];
be_t<u64> reserved[2]; 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]);
}
}; };

View File

@ -8,35 +8,186 @@ Module cellVpost(0x0008, cellVpost_init);
int cellVpostQueryAttr(const mem_ptr_t<CellVpostCfgParam> cfgParam, mem_ptr_t<CellVpostAttr> attr) 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; 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) 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()); 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; return CELL_OK;
} }
int cellVpostOpenEx(const mem_ptr_t<CellVpostCfgParam> cfgParam, const mem_ptr_t<CellVpostResourceEx> resource, mem32_t handle) 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()); 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; return CELL_OK;
} }
int cellVpostClose(u32 handle) 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; return CELL_OK;
} }
int cellVpostExec(u32 handle, const u32 inPicBuff_addr, const mem_ptr_t<CellVpostCtrlParam> ctrlParam, int cellVpostExec(u32 handle, const u32 inPicBuff_addr, const mem_ptr_t<CellVpostCtrlParam> ctrlParam,
u32 outPicBuff_addr, mem_ptr_t<CellVpostPictureInfo> picInfo) 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()); 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; return CELL_OK;
} }

View File

@ -315,3 +315,14 @@ struct CellVpostPictureInfo
be_t<u32> reserved1; be_t<u32> reserved1;
be_t<u32> reserved2; be_t<u32> reserved2;
}; };
class VpostInstance
{
public:
const bool to_rgba;
VpostInstance(bool rgba)
: to_rgba(rgba)
{
}
};

View File

@ -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()); 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, Memory.Write32(RAW_SPU_BASE_ADDR + RAW_SPU_OFFSET * id + RAW_SPU_PROB_OFFSET + SPU_NPC_offs,
(u32)img->entry_point); (u32)img->entry_point);

View File

@ -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('/'); const wxString path = orig_file->GetPath().AfterFirst('/');
u64 nbytes = (u64)aio->size; u64 nbytes = aio->size;
const u32 buf_addr = (u32)aio->buf_addr; u32 buf_addr = aio->buf_addr;
u64 res; u32 res = 0;
u32 error; 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)
{ {
/* error = CELL_ENOMEM;
//open the file again (to prevent access conflicts roughly) goto fin;
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;
} }
else
if (!Memory.IsGoodAddr(buf_addr))
{ {
res = 0;
error = CELL_EFAULT; 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])", if (count) if (u32 frag = buf_addr & 4095) // memory page fragment
fd, (u64)aio->offset, buf_addr, (u64)aio->size, res, xid, path.wx_str()); {
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 if (func) // start callback thread
{ {
func.async(aio, error, xid, res); func.async(aio, error, xid, res);
} }
CPUThread& thr = Emu.GetCallbackThread(); /*CPUThread& thr = Emu.GetCallbackThread();
while (thr.IsAlive()) while (thr.IsAlive())
{ {
Sleep(1); 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"); ConLog.Warning("fsAioRead() aborted");
break; break;
} }
} }*/
g_FsAioReadCur++; g_FsAioReadCur++;
} }

View File

@ -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) 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()); cond_id.GetAddr(), mutex_id, attr.GetAddr());
if (!cond_id.IsGood() || !attr.IsGood()) 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) 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); Cond* cond = new Cond(mutex, attr->name_u64);
@ -60,6 +60,136 @@ int sys_cond_destroy(u32 cond_id)
return CELL_OK; 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) int sys_cond_wait(u32 cond_id, u64 timeout)
{ {
sys_cond.Log("sys_cond_wait(cond_id=%d, timeout=%lld)", cond_id, 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; 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;
} }

View File

@ -94,16 +94,43 @@ int cellFsRead(u32 fd, u32 buf_addr, u64 nbytes, mem64_t nread)
vfsStream* file; vfsStream* file;
if(!sys_fs.CheckId(fd, file)) return CELL_ESRCH; 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); u32 req = min(count, 4096 - frag);
nbytes = block.GetSize() - (buf_addr - block.GetStartAddr()); 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()) if (count) // last fragment
nread = res; {
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; return CELL_OK;
} }

View File

@ -14,7 +14,7 @@ int cellGcmCallback(u32 context_addr, u32 count)
const s32 res = ctx.current - ctx.begin - ctrl.put; 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; ctx.current = ctx.begin + res;

View File

@ -84,13 +84,22 @@ int sys_lwcond_signal(mem_ptr_t<sys_lwcond_t> lwcond)
mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex); mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
be_t<u32> tid = GetCurrentPPUThread().GetId(); 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 (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->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); mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
be_t<u32> tid = GetCurrentPPUThread().GetId(); 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())) 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->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; return CELL_ESRCH;
} }
if (!Emu.GetIdManager().CheckID(ppu_thread_id))
{
return CELL_ESRCH;
}
if (!sq->invalidate(ppu_thread_id)) if (!sq->invalidate(ppu_thread_id))
{ {
return CELL_EPERM; 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); mem_ptr_t<sys_lwmutex_t> mutex(lwcond->lwmutex);
be_t<u32> tid = GetCurrentPPUThread().GetId(); 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); if (!was_locked)
mutex->recursive_count = 1; {
mutex->mutex.owner.unlock(tid, target); 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()) 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(); u32 tid_le = GetCurrentPPUThread().GetId();
be_t<u32> tid = tid_le; 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 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); sq->push(tid_le);
mutex->recursive_count = 0; mutex->recursive_count = 0;
mutex->mutex.owner.unlock(tid); mutex->mutex.unlock(tid);
u32 counter = 0; u32 counter = 0;
const u32 max_counter = timeout ? (timeout / 1000) : ~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_OK: mutex->unlock(tid); break;
case SMR_SIGNAL: return CELL_OK; case SMR_SIGNAL: return CELL_OK;
} */ } */
if (mutex->mutex.owner.GetOwner() == tid) if (mutex->mutex.GetOwner() == tid)
{ {
_mm_mfence(); _mm_mfence();
mutex->recursive_count = 1; mutex->recursive_count = 1;

View File

@ -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->attribute = attr->attr_protocol | attr->attr_recursive;
lwmutex->mutex.all_info() = 0; lwmutex->all_info() = ~0;
lwmutex->mutex.all_info() = 0; lwmutex->mutex.initialize();
lwmutex->mutex.owner.initialize();
//lwmutex->waiter = lwmutex->owner.GetOwner(); //lwmutex->waiter = lwmutex->owner.GetOwner();
lwmutex->pad = 0; lwmutex->pad = 0;
lwmutex->recursive_count = 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; if (!Emu.GetIdManager().CheckID(sq_id)) return CELL_ESRCH;
// try to make it unable to lock // 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: case CELL_OK:
lwmutex->mutex.all_info() = 0; lwmutex->all_info() = 0;
lwmutex->attribute = 0; lwmutex->attribute = 0;
lwmutex->sleep_queue = 0; lwmutex->sleep_queue = 0;
Emu.GetIdManager().RemoveID(sq_id); 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 (!attribute.ToBE()) return CELL_EINVAL;
if (tid == mutex.owner.GetOwner()) if (tid == mutex.GetOwner())
{ {
if (attribute.ToBE() & se32(SYS_SYNC_RECURSIVE)) 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_OK: recursive_count = 1; return CELL_OK;
case SMR_FAILED: return CELL_EBUSY; 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) int sys_lwmutex_t::unlock(be_t<u32> tid)
{ {
if (tid != mutex.owner.GetOwner()) if (tid != mutex.GetOwner())
{ {
return CELL_EPERM; 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(); target = attribute.ToBE() & se32(SYS_SYNC_FIFO) ? sq->pop() : sq->pop_prio();
case se32(SYS_SYNC_RETRY): break; case se32(SYS_SYNC_RETRY): break;
} }
if (target) mutex.owner.unlock(tid, target); if (target) mutex.unlock(tid, target);
else mutex.owner.unlock(tid); else mutex.unlock(tid);
} }
return CELL_OK; return CELL_OK;
} }
@ -277,7 +276,7 @@ int sys_lwmutex_t::lock(be_t<u32> tid, u64 timeout)
default: break; default: break;
} }
switch (mutex.owner.lock(tid, timeout)) switch (mutex.lock(tid, timeout))
{ {
case SMR_OK: case SMR_OK:
sq->invalidate(tid); sq->invalidate(tid);

View File

@ -65,13 +65,9 @@ struct SleepQueue
struct sys_lwmutex_t struct sys_lwmutex_t
{ {
struct sys_lwmutex_lock_info_t /* volatile */ SMutexBase<be_t<u32>, ~0, 0> mutex;
{ /* volatile */ be_t<u32> waiter; // not used
/* volatile */ SMutexBase<be_t<u32>, 0xffffffff, 0> owner; u64 &all_info(){return *(reinterpret_cast<u64*>(this));}
/* volatile */ be_t<u32> waiter; // not used
u64 &all_info(){return *(reinterpret_cast<u64*>(this));
}
}mutex;
be_t<u32> attribute; be_t<u32> attribute;
be_t<u32> recursive_count; be_t<u32> recursive_count;
be_t<u32> sleep_queue; be_t<u32> sleep_queue;

View File

@ -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); CPUThread& new_thread = Emu.GetCPU().AddThread(CPU_THREAD_SPU);
//copy SPU image: //copy SPU image:
u32 spu_offset = Memory.MainMem.AllocAlign(256 * 1024); 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: //initialize from new place:
new_thread.SetOffset(spu_offset); new_thread.SetOffset(spu_offset);
new_thread.SetEntry(spu_ep); new_thread.SetEntry(spu_ep);

View File

@ -136,11 +136,22 @@ void LogWriter::WriteToLog(std::string prefix, std::string value, std::string co
if(wxThread::IsMain()) if(wxThread::IsMain())
#endif #endif
{ {
while(LogBuffer.IsBusy()) wxYieldIfNeeded(); while(LogBuffer.IsBusy())
{
// need extra break condition?
wxYieldIfNeeded();
}
} }
else else
{ {
while(LogBuffer.IsBusy()) Sleep(1); while (LogBuffer.IsBusy())
{
if (Emu.IsStopped())
{
break;
}
Sleep(1);
}
} }
//if(LogBuffer.put == LogBuffer.get) LogBuffer.Flush(); //if(LogBuffer.put == LogBuffer.get) LogBuffer.Flush();

View File

@ -29,10 +29,10 @@ MemoryViewerPanel::MemoryViewerPanel(wxWindow* parent)
s_tools_mem_bytes.Add(sc_bytes); s_tools_mem_bytes.Add(sc_bytes);
wxStaticBoxSizer& s_tools_mem_buttons = *new wxStaticBoxSizer(wxHORIZONTAL, this, "Control"); 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_prev = new wxButton(this, wxID_ANY, "<", wxDefaultPosition, wxSize(21, 21));
wxButton* b_next = 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_fprev);
s_tools_mem_buttons.Add(b_prev); s_tools_mem_buttons.Add(b_prev);
s_tools_mem_buttons.Add(b_next); s_tools_mem_buttons.Add(b_next);

View File

@ -69,20 +69,20 @@
</ImportGroup> </ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <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> <OutDir>$(SolutionDir)bin\</OutDir>
<LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath> <LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath>
<TargetName>$(ProjectName)-$(PlatformShortName)-dbg</TargetName> <TargetName>$(ProjectName)-$(PlatformShortName)-dbg</TargetName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <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> <OutDir>$(SolutionDir)bin\</OutDir>
<LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath> <LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath>
<TargetName>$(ProjectName)-$(PlatformShortName)-dbg</TargetName> <TargetName>$(ProjectName)-$(PlatformShortName)-dbg</TargetName>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental> <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> <OutDir>$(SolutionDir)bin\</OutDir>
<LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath> <LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath>
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
@ -91,7 +91,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental> <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> <OutDir>$(SolutionDir)bin\</OutDir>
<LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath> <LibraryPath>..\libs\$(Configuration)\;$(LibraryPath)</LibraryPath>
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
@ -109,10 +109,10 @@
</ClCompile> </ClCompile>
<Link> <Link>
<GenerateDebugInformation>true</GenerateDebugInformation> <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> <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<DataExecutionPrevention>false</DataExecutionPrevention> <DataExecutionPrevention>false</DataExecutionPrevention>
<AdditionalLibraryDirectories>..\wxWidgets\lib\vc_lib</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_lib;..\ffmpeg\Windows\x86\lib</AdditionalLibraryDirectories>
</Link> </Link>
<PreBuildEvent> <PreBuildEvent>
<Command> <Command>
@ -129,14 +129,17 @@
</ClCompile> </ClCompile>
<Link> <Link>
<GenerateDebugInformation>true</GenerateDebugInformation> <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> <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<DataExecutionPrevention>false</DataExecutionPrevention> <DataExecutionPrevention>false</DataExecutionPrevention>
<AdditionalLibraryDirectories>..\wxWidgets\lib\vc_x64_lib</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_x64_lib;..\ffmpeg\Windows\x86_64\lib</AdditionalLibraryDirectories>
</Link> </Link>
<PreBuildEvent> <PreBuildEvent>
<Command>"$(SolutionDir)\Utilities\git-version-gen.cmd"</Command> <Command>"$(SolutionDir)\Utilities\git-version-gen.cmd"</Command>
</PreBuildEvent> </PreBuildEvent>
<ProjectReference>
<UseLibraryDependencyInputs>false</UseLibraryDependencyInputs>
</ProjectReference>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
@ -158,12 +161,12 @@
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <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>
</IgnoreAllDefaultLibraries> </IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries> <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<DataExecutionPrevention>false</DataExecutionPrevention> <DataExecutionPrevention>false</DataExecutionPrevention>
<AdditionalLibraryDirectories>..\wxWidgets\lib\vc_lib</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_lib;..\ffmpeg\Windows\x86\lib</AdditionalLibraryDirectories>
</Link> </Link>
<PreBuildEvent> <PreBuildEvent>
<Command> <Command>
@ -190,12 +193,12 @@
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <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>
</IgnoreAllDefaultLibraries> </IgnoreAllDefaultLibraries>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries> <IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<DataExecutionPrevention>false</DataExecutionPrevention> <DataExecutionPrevention>false</DataExecutionPrevention>
<AdditionalLibraryDirectories>..\wxWidgets\lib\vc_x64_lib</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>..\wxWidgets\lib\vc_x64_lib;..\ffmpeg\Windows\x86_64\lib</AdditionalLibraryDirectories>
</Link> </Link>
<PreBuildEvent> <PreBuildEvent>
<Command> <Command>
@ -343,10 +346,12 @@
<ClInclude Include="..\Utilities\IdManager.h" /> <ClInclude Include="..\Utilities\IdManager.h" />
<ClInclude Include="..\Utilities\MTProgressDialog.h" /> <ClInclude Include="..\Utilities\MTProgressDialog.h" />
<ClInclude Include="..\Utilities\SMutex.h" /> <ClInclude Include="..\Utilities\SMutex.h" />
<ClInclude Include="..\Utilities\SQueue.h" />
<ClInclude Include="..\Utilities\Thread.h" /> <ClInclude Include="..\Utilities\Thread.h" />
<ClInclude Include="..\Utilities\Timer.h" /> <ClInclude Include="..\Utilities\Timer.h" />
<ClInclude Include="Emu\Audio\AudioManager.h" /> <ClInclude Include="Emu\Audio\AudioManager.h" />
<ClInclude Include="Emu\Audio\cellAudio.h" /> <ClInclude Include="Emu\Audio\cellAudio.h" />
<ClInclude Include="Emu\Cell\MFC.h" />
<ClInclude Include="Emu\Cell\PPCDecoder.h" /> <ClInclude Include="Emu\Cell\PPCDecoder.h" />
<ClInclude Include="Emu\Cell\PPCDisAsm.h" /> <ClInclude Include="Emu\Cell\PPCDisAsm.h" />
<ClInclude Include="Emu\Cell\PPCInstrTable.h" /> <ClInclude Include="Emu\Cell\PPCInstrTable.h" />

View File

@ -603,5 +603,11 @@
<ClInclude Include="..\Utilities\SMutex.h"> <ClInclude Include="..\Utilities\SMutex.h">
<Filter>Utilities</Filter> <Filter>Utilities</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\Utilities\SQueue.h">
<Filter>Utilities</Filter>
</ClInclude>
<ClInclude Include="Emu\Cell\MFC.h">
<Filter>Include</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>