Merge remote-tracking branch 'origin/master' into armjit

Conflicts:
	Core/HLE/sceKernelThread.cpp
This commit is contained in:
Henrik Rydgard 2013-01-09 00:43:44 +01:00
commit 789c4781c0
22 changed files with 626 additions and 185 deletions

View File

@ -177,8 +177,6 @@ elseif(X86)
Common/ABI.h
Common/CPUDetect.cpp
Common/CPUDetect.h
Common/MathUtil.cpp
Common/MathUtil.h
Common/Thunk.cpp
Common/Thunk.h
Common/x64Analyzer.cpp
@ -220,6 +218,8 @@ add_library(Common STATIC
Common/IniFile.h
Common/LogManager.cpp
Common/LogManager.h
Common/MathUtil.cpp
Common/MathUtil.h
Common/MemArena.cpp
Common/MemArena.h
Common/MemoryUtil.cpp

View File

@ -22,6 +22,10 @@
#include <assert.h>
#include <stdarg.h>
#ifdef __SYMBIAN32__
#include <e32std.h>
#endif
namespace ArmGen
{
@ -61,7 +65,11 @@ const u8 *ARMXEmitter::AlignCodePage()
void ARMXEmitter::FlushIcache()
{
#ifdef __SYMBIAN32__
User::IMB_Range( startcode, code );
#else
__builtin___clear_cache (startcode, code);
#endif
SLEEP(0);
}
void ARMXEmitter::SetCC(CCFlags cond)
@ -536,4 +544,4 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)
}
}
}
}

View File

@ -261,7 +261,7 @@ public:
case MODE_READ: x = (wchar_t*)*ptr; break;
case MODE_WRITE: memcpy(*ptr, x.c_str(), stringLen); break;
case MODE_MEASURE: break;
case MODE_VERIFY: _dbg_assert_msg_(COMMON, x == (wchar_t*)*ptr, "Savestate verification failure: \"%s\" != \"%s\" (at %p).\n", x.c_str(), (wchar_t*)*ptr, ptr); break;
case MODE_VERIFY: _dbg_assert_msg_(COMMON, x == (wchar_t*)*ptr, "Savestate verification failure: \"%ls\" != \"%ls\" (at %p).\n", x.c_str(), (wchar_t*)*ptr, ptr); break;
}
(*ptr) += stringLen;
}

View File

@ -45,6 +45,12 @@
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
#endif
#ifdef __SYMBIAN32__
#include <e32std.h>
#define SYMBIAN_CODECHUNCK_SIZE 1024*1024*17;
static RChunk* g_code_chunk = NULL;
static RHeap* g_code_heap = NULL;
#endif
// This is purposely not a full wrapper for virtualalloc/mmap, but it
// provides exactly the primitive operations that Dolphin needs.
@ -54,9 +60,16 @@ void* AllocateExecutableMemory(size_t size, bool low)
#if defined(_WIN32)
void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#elif defined(__SYMBIAN32__)
// On Symbian, we will need to create an RChunk and allocate with ->CreateLocalCode(size, size);
static char *map_hint = 0;
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, -1, 0);
//This function may be called more than once, and we want to create only one big
//memory chunck for all the executable code for the JIT
if( g_code_chunk == NULL && g_code_heap == NULL)
{
TInt minsize = SYMBIAN_CODECHUNCK_SIZE;
TInt maxsize = SYMBIAN_CODECHUNCK_SIZE + 3*4096; //some offsets
g_code_chunk->CreateLocalCode(minsize, maxsize);
g_code_heap = UserHeap::ChunkHeap(*g_code_chunk, minsize, 1, maxsize);
}
void* ptr = (void*) g_code_heap->Alloc( size );
#else
static char *map_hint = 0;
#if defined(__x86_64__) && !defined(MAP_32BIT)
@ -143,7 +156,8 @@ void* AllocateAlignedMemory(size_t size,size_t alignment)
// On Symbian, we will want to create an RChunk.
ptr = malloc(size);
#else
posix_memalign(&ptr, alignment, size);
if(posix_memalign(&ptr, alignment, size) != 0)
ptr = NULL;
#endif
#endif

View File

@ -55,25 +55,30 @@ std::string StringFromFormat(const char* format, ...)
{
va_list args;
char *buf = NULL;
std::string temp = "";
#ifdef _WIN32
int required = 0;
va_start(args, format);
required = _vscprintf(format, args);
buf = new char[required + 1];
vsnprintf(buf, required, format, args);
if(vsnprintf(buf, required, format, args) < 0)
buf[0] = '\0';
va_end(args);
buf[required] = '\0';
std::string temp = buf;
temp = buf;
delete[] buf;
#else
va_start(args, format);
vasprintf(&buf, format, args);
if(vasprintf(&buf, format, args) < 0)
buf = NULL;
va_end(args);
std::string temp = buf;
free(buf);
if(buf != NULL) {
temp = buf;
free(buf);
}
#endif
return temp;
}

View File

@ -143,7 +143,10 @@ bool SymbolMap::LoadSymbolMap(const char *filename)
while (!feof(f))
{
char line[512],temp[256];
fgets(line,511,f);
char *p = fgets(line,512,f);
if(p == NULL)
break;
if (strlen(line) < 4 || sscanf(line, "%s", temp) != 1)
continue;

View File

@ -123,7 +123,7 @@ void PSPOskDialog::RenderKeyboard()
else
temp[0] = '_';
PPGeDrawText(temp, previewLeftSide + (i * 16.0f), 40.0f, NULL, 0.5f, color);
PPGeDrawText(temp, previewLeftSide + (i * 16.0f), 40.0f, 0, 0.5f, color);
}
for (int row = 0; row < NUMKEYROWS; ++row)
{
@ -134,10 +134,10 @@ void PSPOskDialog::RenderKeyboard()
color = 0xFF7f7f7f;
temp[0] = oskKeys[row][col];
PPGeDrawText(temp, keyboardLeftSide + (25.0f * col), 70.0f + (25.0f * row), NULL, 0.6f, color);
PPGeDrawText(temp, keyboardLeftSide + (25.0f * col), 70.0f + (25.0f * row), 0, 0.6f, color);
if (selectedRow == row && col == selectedExtra)
PPGeDrawText("_", keyboardLeftSide + (25.0f * col), 70.0f + (25.0f * row), NULL, 0.6f, 0xFFFFFFFF);
PPGeDrawText("_", keyboardLeftSide + (25.0f * col), 70.0f + (25.0f * row), 0, 0.6f, 0xFFFFFFFF);
}
}

View File

@ -41,7 +41,9 @@ FileBlockDevice::~FileBlockDevice()
bool FileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr)
{
fseek(f, blockNumber * GetBlockSize(), SEEK_SET);
fread(outPtr, 2048, 1, f);
if(fread(outPtr, 1, 2048, f) != 2048)
DEBUG_LOG(LOADER, "Could not read 2048 bytes from block");
return true;
}
@ -80,10 +82,10 @@ CISOFileBlockDevice::CISOFileBlockDevice(std::string _filename)
f = fopen(_filename.c_str(), "rb");
CISO_H hdr;
fread(&hdr, 1, sizeof(CISO_H), f);
if (memcmp(hdr.magic, "CISO", 4) != 0)
size_t readSize = fread(&hdr, sizeof(CISO_H), 1, f);
if (readSize != 1 || memcmp(hdr.magic, "CISO", 4) != 0)
{
//ARGH!
WARN_LOG(LOADER, "Invalid CSO!");
}
else
{
@ -109,7 +111,8 @@ CISOFileBlockDevice::CISOFileBlockDevice(std::string _filename)
int indexSize = numBlocks + 1;
index = new u32[indexSize];
fread(index, 4, indexSize, f);
if(fread(index, sizeof(u32), indexSize, f) != indexSize)
memset(index, 0, indexSize * sizeof(u32));
}
CISOFileBlockDevice::~CISOFileBlockDevice()
@ -134,12 +137,12 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr)
u32 compressedReadSize = idx2 - idx;
fseek(f, compressedReadPos, SEEK_SET);
fread(inbuffer, compressedReadSize, 1, f);
size_t readSize = fread(inbuffer, 1, compressedReadSize, f);
if (plain)
{
memset(outPtr, 0, 2048);
memcpy(outPtr, inbuffer, compressedReadSize);
memcpy(outPtr, inbuffer, readSize);
}
else
{
@ -152,7 +155,7 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr)
ERROR_LOG(LOADER, "deflateInit ERROR : %s\n", (z.msg) ? z.msg : "???");
return 1;
}
z.avail_in = compressedReadSize;
z.avail_in = readSize;
z.next_out = outPtr;
z.avail_out = blockSize;
z.next_in = inbuffer;

View File

@ -201,6 +201,11 @@ template<u32 func(u32, int , int , int, int, int)> void WrapU_UIIIII() {
RETURN(retval);
}
template<u32 func(u32, int , int , int, u32)> void WrapU_UIIIU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
RETURN(retval);
}
template<u32 func(u32, int , int , int, int, int, int)> void WrapU_UIIIIII() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
RETURN(retval);
@ -211,6 +216,11 @@ template<u32 func(u32, u32)> void WrapU_UU() {
RETURN(retval);
}
template<u32 func(u32, u32, int)> void WrapU_UUI() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
}
template<u32 func(const char *, u32, u32, u32)> void WrapU_CUUU() {
u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
@ -225,6 +235,11 @@ template<u32 func(u32, int, u32, int, int)> void WrapU_UIUII() {
RETURN(retval);
}
template<u32 func(u32, int, u32, u32)> void WrapU_UIUU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
}
template<u32 func(u32, int, int)> void WrapU_UII() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
@ -439,6 +454,11 @@ template<u32 func(u32, u32, u32, int)> void WrapU_UUUI() {
RETURN(retval);
}
template<u32 func(u32, u32, u32, int, u32)> void WrapU_UUUIU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
RETURN(retval);
}
template<u32 func(u32, u32, int, u32)> void WrapU_UUIU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);

View File

@ -51,6 +51,7 @@ enum
static std::vector<HLEModule> moduleDB;
static std::vector<Syscall> unresolvedSyscalls;
static std::vector<Syscall> exportedCalls;
static int hleAfterSyscall = HLE_AFTER_NOTHING;
static char hleAfterSyscallReschedReason[512];
@ -63,6 +64,7 @@ void HLEDoState(PointerWrap &p)
{
Syscall sc = {0};
p.Do(unresolvedSyscalls, sc);
p.Do(exportedCalls, sc);
p.DoMarker("HLE");
}
@ -71,6 +73,7 @@ void HLEShutdown()
hleAfterSyscall = HLE_AFTER_NOTHING;
moduleDB.clear();
unresolvedSyscalls.clear();
exportedCalls.clear();
}
void RegisterModule(const char *name, int numFunctions, const HLEFunction *funcTable)
@ -124,15 +127,26 @@ const HLEFunction *GetFunc(const char *moduleName, u32 nib)
const char *GetFuncName(const char *moduleName, u32 nib)
{
_dbg_assert_msg_(HLE, moduleName != NULL, "Invalid module name.");
const HLEFunction *func = GetFunc(moduleName,nib);
if (func)
return func->name;
else
// Was this function exported previously?
static char temp[256];
for (auto it = exportedCalls.begin(), end = exportedCalls.end(); it != end; ++it)
{
static char temp[256];
sprintf(temp,"[UNK: 0x%08x ]",nib);
return temp;
if (!strcmp(it->moduleName, moduleName) && it->nid == nib)
{
sprintf(temp, "[EXP: 0x%08x]", nib);
return temp;
}
}
// No good, we can't find it.
sprintf(temp,"[UNK: 0x%08x]", nib);
return temp;
}
u32 GetSyscallOp(const char *moduleName, u32 nib)
@ -173,17 +187,34 @@ void WriteSyscall(const char *moduleName, u32 nib, u32 address)
}
else
{
// Did another module export this already?
for (auto it = exportedCalls.begin(), end = exportedCalls.end(); it != end; ++it)
{
if (!strcmp(it->moduleName, moduleName) && it->nid == nib)
{
Memory::Write_U32(MIPS_MAKE_J(it->symAddr), address); // j symAddr
Memory::Write_U32(MIPS_MAKE_NOP(), address + 4); // nop (delay slot)
return;
}
}
// Module inexistent.. for now; let's store the syscall for it to be resolved later
INFO_LOG(HLE,"Syscall (%s,%08x) unresolved, storing for later resolving", moduleName, nib);
Syscall sysc = {"", address, nib};
strncpy(sysc.moduleName, moduleName, 32);
sysc.moduleName[31] = '\0';
strncpy(sysc.moduleName, moduleName, KERNELOBJECT_MAX_NAME_LENGTH);
sysc.moduleName[KERNELOBJECT_MAX_NAME_LENGTH] = '\0';
unresolvedSyscalls.push_back(sysc);
// Write a trap so we notice this func if it's called before resolving.
Memory::Write_U32(MIPS_MAKE_JR_RA(), address); // jr ra
Memory::Write_U32(GetSyscallOp("(invalid syscall)", nib), address + 4);
}
}
void ResolveSyscall(const char *moduleName, u32 nib, u32 address)
{
_dbg_assert_msg_(HLE, moduleName != NULL, "Invalid module name.");
for (size_t i = 0; i < unresolvedSyscalls.size(); i++)
{
Syscall *sysc = &unresolvedSyscalls[i];
@ -196,6 +227,11 @@ void ResolveSyscall(const char *moduleName, u32 nib, u32 address)
Memory::Write_U32(MIPS_MAKE_NOP(), sysc->symAddr + 4);
}
}
Syscall ex = {"", address, nib};
strncpy(ex.moduleName, moduleName, KERNELOBJECT_MAX_NAME_LENGTH);
ex.moduleName[KERNELOBJECT_MAX_NAME_LENGTH] = '\0';
exportedCalls.push_back(ex);
}
const char *GetFuncName(int moduleIndex, int func)

View File

@ -377,6 +377,7 @@ const HLEFunction sceAtrac3plus[] =
{0x9CD7DE03,0,"sceAtracSetMOutHalfwayBufferAndGetID"},
{0x5622B7C1,WrapI_UIIU<sceAtracSetAA3DataAndGetID>,"sceAtracSetAA3DataAndGetID"},
{0x5DD66588,0,"sceAtracSetAA3HalfwayBufferAndGetID"},
{0x231FC6B7,0,"_sceAtracGetContextAddress"},
};

View File

@ -549,7 +549,7 @@ const HLEFunction ThreadManForUser[] =
{0x52089CA1,sceKernelGetThreadStackFreeSize,"sceKernelGetThreadStackFreeSize"},
{0xFFC36A14,WrapU_UU<sceKernelReferThreadRunStatus>,"sceKernelReferThreadRunStatus"},
{0x17c1684e,WrapU_UU<sceKernelReferThreadStatus>,"sceKernelReferThreadStatus"},
{0x2C34E053,0,"sceKernelReleaseWaitThread"},
{0x2C34E053,WrapI_I<sceKernelReleaseWaitThread>,"sceKernelReleaseWaitThread"},
{0x75156e8f,sceKernelResumeThread,"sceKernelResumeThread"},
{0x3ad58b8c,&WrapU_V<sceKernelSuspendDispatchThread>,"sceKernelSuspendDispatchThread"},
{0x27e22ec2,&WrapU_U<sceKernelResumeDispatchThread>,"sceKernelResumeDispatchThread"},
@ -560,7 +560,7 @@ const HLEFunction ThreadManForUser[] =
{0x9944f31f,sceKernelSuspendThread,"sceKernelSuspendThread"},
{0x616403ba,WrapI_U<sceKernelTerminateThread>,"sceKernelTerminateThread"},
{0x383f7bcc,WrapI_I<sceKernelTerminateDeleteThread>,"sceKernelTerminateDeleteThread"},
{0x840E8133,sceKernelWaitThreadEndCB,"sceKernelWaitThreadEndCB"},
{0x840E8133,WrapI_IU<sceKernelWaitThreadEndCB>,"sceKernelWaitThreadEndCB"},
{0xd13bde95,sceKernelCheckThreadStack,"sceKernelCheckThreadStack"},
{0x94416130,WrapU_UUUU<sceKernelGetThreadmanIdList>,"sceKernelGetThreadmanIdList"},
@ -586,7 +586,7 @@ const HLEFunction ThreadManForUser[] =
{0xE1619D7C,sceKernelSysClock2USecWide,"sceKernelSysClock2USecWide"},
{0x110dec9a,sceKernelUSec2SysClock,"sceKernelUSec2SysClock"},
{0x278C0DF5,sceKernelWaitThreadEnd,"sceKernelWaitThreadEnd"},
{0x278C0DF5,WrapI_IU<sceKernelWaitThreadEnd>,"sceKernelWaitThreadEnd"},
{0xd59ead2f,sceKernelWakeupThread,"sceKernelWakeupThread"}, //AI Go, audio?
{0x0C106E53,0,"sceKernelRegisterThreadEventHandler"},

View File

@ -44,6 +44,22 @@ enum {
PSP_THREAD_ATTR_USER = 0x80000000
};
enum {
// Function exports.
NID_MODULE_START = 0xD632ACDB,
NID_MODULE_STOP = 0xCEE8593C,
NID_MODULE_REBOOT_BEFORE = 0x2F064FA6,
NID_MODULE_REBOOT_PHASE = 0xADF12745,
NID_MODULE_BOOTSTART = 0xD3744BE0,
// Variable exports.
NID_MODULE_INFO = 0xF01D73A7,
NID_MODULE_START_THREAD_PARAMETER = 0x0F7C276C,
NID_MODULE_STOP_THREAD_PARAMETER = 0xCF0CC697,
NID_MODULE_REBOOT_BEFORE_THREAD_PARAMETER = 0xF4F4299D,
NID_MODULE_SDK_VERSION = 0x11B97506,
};
static const char *blacklistedModules[] = {
"sceATRAC3plus_Library",
"sceFont_Library",
@ -229,6 +245,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
{
Module *module = new Module;
kernelObjects.Create(module);
memset(&module->nm, 0, sizeof(module->nm));
u8 *newptr = 0;
if (*(u32*)ptr == 0x4543537e) { // "~SCE"
@ -464,8 +481,68 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
{
u32 nid = residentPtr[j];
u32 exportAddr = residentPtr[ent->fcount + ent->vcount + j];
ResolveSyscall(name, nid, exportAddr);
switch (nid)
{
case NID_MODULE_START:
module->nm.module_start_func = exportAddr;
break;
case NID_MODULE_STOP:
module->nm.module_stop_func = exportAddr;
break;
case NID_MODULE_REBOOT_BEFORE:
module->nm.module_reboot_before_func = exportAddr;
break;
case NID_MODULE_REBOOT_PHASE:
module->nm.module_reboot_phase_func = exportAddr;
break;
case NID_MODULE_BOOTSTART:
module->nm.module_bootstart_func = exportAddr;
break;
default:
ResolveSyscall(name, nid, exportAddr);
}
}
for (u32 j = 0; j < ent->vcount; j++)
{
u32 nid = residentPtr[ent->fcount + j];
u32 exportAddr = residentPtr[ent->fcount + ent->vcount + ent->fcount + j];
switch (nid)
{
case NID_MODULE_INFO:
break;
case NID_MODULE_START_THREAD_PARAMETER:
if (Memory::Read_U32(exportAddr) != 3)
WARN_LOG(LOADER, "Strange value at module_start_thread_parameter export: %08x", Memory::Read_U32(exportAddr));
module->nm.module_start_thread_priority = Memory::Read_U32(exportAddr + 4);
module->nm.module_start_thread_stacksize = Memory::Read_U32(exportAddr + 8);
module->nm.module_start_thread_attr = Memory::Read_U32(exportAddr + 12);
break;
case NID_MODULE_STOP_THREAD_PARAMETER:
if (Memory::Read_U32(exportAddr) != 3)
WARN_LOG(LOADER, "Strange value at module_stop_thread_parameter export: %08x", Memory::Read_U32(exportAddr));
module->nm.module_stop_thread_priority = Memory::Read_U32(exportAddr + 4);
module->nm.module_stop_thread_stacksize = Memory::Read_U32(exportAddr + 8);
module->nm.module_stop_thread_attr = Memory::Read_U32(exportAddr + 12);
break;
case NID_MODULE_REBOOT_BEFORE_THREAD_PARAMETER:
if (Memory::Read_U32(exportAddr) != 3)
WARN_LOG(LOADER, "Strange value at module_reboot_before_thread_parameter export: %08x", Memory::Read_U32(exportAddr));
module->nm.module_reboot_before_thread_priority = Memory::Read_U32(exportAddr + 4);
module->nm.module_reboot_before_thread_stacksize = Memory::Read_U32(exportAddr + 8);
module->nm.module_reboot_before_thread_attr = Memory::Read_U32(exportAddr + 12);
break;
case NID_MODULE_SDK_VERSION:
DEBUG_LOG(LOADER, "Module SDK: %08x", Memory::Read_U32(exportAddr));
break;
default:
DEBUG_LOG(LOADER, "Unexpected variable with nid: %08x", nid);
break;
}
}
if (ent->size > 4)
{
ent = (PspLibEntEntry*)((u8*)ent + ent->size * 4);
@ -562,6 +639,12 @@ Module *__KernelLoadModule(u8 *fileptr, SceKernelLMOption *options, std::string
void __KernelStartModule(Module *m, int args, const char *argp, SceKernelSMOption *options)
{
if (m->nm.module_start_func != 0 || m->nm.module_start_func != -1)
{
if (m->nm.module_start_func != m->nm.entry_addr)
WARN_LOG(LOADER, "Main module has start func (%08x) different from entry (%08x)?", m->nm.module_start_func, m->nm.entry_addr);
}
__KernelSetupRootThread(m->GetUID(), args, argp, options->priority, options->stacksize, options->attribute);
mainModuleID = m->GetUID();
//TODO: if current thread, put it in wait state, waiting for the new thread
@ -621,6 +704,14 @@ bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std::
option.priority = 0x20;
option.stacksize = 0x40000; // crazy? but seems to be the truth
// Replace start options with module-specified values if they exist.
if (module->nm.module_start_thread_attr != 0)
option.attribute = module->nm.module_start_thread_attr;
if (module->nm.module_start_thread_priority != 0)
option.priority = module->nm.module_start_thread_priority;
if (module->nm.module_start_thread_stacksize != 0)
option.stacksize = module->nm.module_start_thread_stacksize;
__KernelStartModule(module, (u32)strlen(filename) + 1, filename, &option);
__KernelStartIdleThreads();

View File

@ -412,7 +412,7 @@ public:
// Utils
bool isRunning() const { return (nt.status & THREADSTATUS_RUNNING) != 0; }
bool isStopped() const { return (nt.status & THREADSTATUS_DORMANT) != 0; }
bool isReady() const { return (nt.status & THREADSTATUS_DORMANT) != 0; }
bool isReady() const { return (nt.status & THREADSTATUS_READY) != 0; }
bool isWaiting() const { return (nt.status & THREADSTATUS_WAIT) != 0; }
bool isSuspended() const { return (nt.status & THREADSTATUS_SUSPEND) != 0; }
@ -468,6 +468,7 @@ void __KernelExecuteMipsCallOnCurrentThread(int callId, bool reschedAfter);
Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr);
void __KernelResetThread(Thread *t);
void __KernelCancelWakeup(SceUID threadID);
void __KernelCancelThreadEndTimeout(SceUID threadID);
bool __KernelCheckThreadCallbacks(Thread *thread, bool force);
//////////////////////////////////////////////////////////////////////////
@ -485,6 +486,7 @@ std::vector<ThreadCallback> threadEndListeners;
SceUID threadIdleID[2];
int eventScheduledWakeup;
int eventThreadEndTimeout;
bool dispatchEnabled = true;
@ -570,6 +572,7 @@ u32 __KernelInterruptReturnAddress()
}
void hleScheduledWakeup(u64 userdata, int cyclesLate);
void hleThreadEndTimeout(u64 userdata, int cyclesLate);
void __KernelThreadingInit()
{
@ -600,6 +603,7 @@ void __KernelThreadingInit()
WriteSyscall("FakeSysCalls", NID_INTERRUPTRETURN, intReturnHackAddr);
eventScheduledWakeup = CoreTiming::RegisterEvent("ScheduledWakeup", &hleScheduledWakeup);
eventThreadEndTimeout = CoreTiming::RegisterEvent("ThreadEndTimeout", &hleThreadEndTimeout);
actionAfterMipsCall = __KernelRegisterActionType(ActionAfterMipsCall::Create);
actionAfterCallback = __KernelRegisterActionType(ActionAfterCallback::Create);
@ -610,6 +614,7 @@ void __KernelThreadingInit()
// These idle threads are later started in LoadExec, which calls __KernelStartIdleThreads below.
__KernelListenThreadEnd(__KernelCancelWakeup);
__KernelListenThreadEnd(__KernelCancelThreadEndTimeout);
}
void __KernelThreadingDoState(PointerWrap &p)
@ -629,6 +634,8 @@ void __KernelThreadingDoState(PointerWrap &p)
p.Do(eventScheduledWakeup);
CoreTiming::RestoreRegisterEvent(eventScheduledWakeup, "ScheduledWakeup", &hleScheduledWakeup);
p.Do(eventThreadEndTimeout);
CoreTiming::RestoreRegisterEvent(eventThreadEndTimeout, "ThreadEndTimeout", &hleThreadEndTimeout);
p.Do(actionAfterMipsCall);
__KernelRestoreActionType(actionAfterMipsCall, ActionAfterMipsCall::Create);
p.Do(actionAfterCallback);
@ -1066,6 +1073,34 @@ void __KernelCancelWakeup(SceUID threadID)
CoreTiming::UnscheduleEvent(eventScheduledWakeup, threadID);
}
void hleThreadEndTimeout(u64 userdata, int cyclesLate)
{
SceUID threadID = (SceUID) userdata;
SceUID waitID = (SceUID) (userdata >> 32);
u32 error;
// Just in case it was woken on its own.
if (__KernelGetWaitID(threadID, WAITTYPE_THREADEND, error) == waitID)
{
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
if (Memory::IsValidAddress(timeoutPtr))
Memory::Write_U32(0, timeoutPtr);
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
}
}
void __KernelScheduleThreadEndTimeout(SceUID threadID, SceUID waitID, s64 usFromNow)
{
s64 cycles = usToCycles(usFromNow);
CoreTiming::ScheduleEvent(cycles, eventThreadEndTimeout, (u64) threadID | ((u64) waitID << 32));
}
void __KernelCancelThreadEndTimeout(SceUID threadID)
{
CoreTiming::UnscheduleEvent(eventThreadEndTimeout, threadID);
}
void __KernelRemoveFromThreadQueue(Thread *t)
{
for (size_t i = 0; i < threadqueue.size(); i++)
@ -1240,7 +1275,7 @@ void __KernelResetThread(Thread *t)
t->nt.waitID = 0;
memset(&t->waitInfo, 0, sizeof(t->waitInfo));
t->nt.exitStatus = 0;
t->nt.exitStatus = SCE_KERNEL_ERROR_NOT_DORMANT;
t->isProcessingCallbacks = false;
// TODO: Is this correct?
t->pendingMipsCalls.clear();
@ -1273,7 +1308,7 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32
t->nt.wakeupCount = 0;
t->nt.initialStack = 0;
t->nt.waitID = 0;
t->nt.exitStatus = 0;
t->nt.exitStatus = SCE_KERNEL_ERROR_DORMANT;
t->nt.waitType = WAITTYPE_NONE;
if (moduleId)
@ -1282,11 +1317,8 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32
t->nt.gpreg = 0; // sceKernelStartThread will take care of this.
t->moduleId = moduleId;
if (name) {
strncpy(t->nt.name, name, 32);
} else {
ERROR_LOG(HLE, "Threads must have names!");
}
strncpy(t->nt.name, name, KERNELOBJECT_MAX_NAME_LENGTH);
t->nt.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0';
return t;
}
@ -1315,6 +1347,27 @@ void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int pr
int sceKernelCreateThread(const char *threadName, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr)
{
if (threadName == NULL)
{
ERROR_LOG(HLE, "SCE_KERNEL_ERROR_ERROR = sceKernelCreateThread(): NULL name");
return SCE_KERNEL_ERROR_ERROR;
}
// TODO: PSP actually fails for many of these cases, but trying for compat.
if (stacksize < 0x200 || stacksize >= 0x20000000)
{
WARN_LOG(HLE, "sceKernelCreateThread(name=\"%s\"): bogus stack size %08x, using 0x4000", threadName, stacksize);
stacksize = 0x4000;
}
if (prio < 0x08 || prio > 0x77)
WARN_LOG(HLE, "sceKernelCreateThread(name=\"%s\"): bogus priority %08x", threadName, prio);
if (!Memory::IsValidAddress(entry))
WARN_LOG(HLE, "sceKernelCreateThread(name=\"%s\"): invalid entry %08x", threadName, entry);
// We're assuming all threads created are user threads.
if ((attr & PSP_THREAD_ATTR_KERNEL) == 0)
attr |= PSP_THREAD_ATTR_USER;
SceUID id;
__KernelCreateThread(id, curModule, threadName, entry, prio, stacksize, attr);
INFO_LOG(HLE, "%i = sceKernelCreateThread(name=\"%s\", entry=%08x, prio=%x, stacksize=%i)", id, threadName, entry, prio, stacksize);
@ -1426,16 +1479,14 @@ void __KernelReturnFromThread()
thread->FreeStack();
}
thread->nt.exitStatus = thread->context.r[2];
thread->nt.exitStatus = currentMIPS->r[2];
thread->nt.status = THREADSTATUS_DORMANT;
__KernelFireThreadEnd(thread);
// TODO: Need to remove the thread from any ready queues.
// Find threads that waited for me
// Wake them
if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread()))
hleReSchedule("return from thread");
__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread(), thread->nt.exitStatus, true);
hleReSchedule("thread returned");
// The stack will be deallocated when the thread is deleted.
}
@ -1450,10 +1501,8 @@ void sceKernelExitThread()
thread->nt.exitStatus = PARAM(0);
__KernelFireThreadEnd(thread);
//Find threads that waited for me
// Wake them
if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread()))
hleReSchedule("exited thread");
__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread(), thread->nt.exitStatus, true);
hleReSchedule("thread exited");
// The stack will be deallocated when the thread is deleted.
}
@ -1468,10 +1517,8 @@ void _sceKernelExitThread()
thread->nt.exitStatus = PARAM(0);
__KernelFireThreadEnd(thread);
//Find threads that waited for this one
// Wake them
if (!__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread()))
hleReSchedule("_exit thread");
__KernelTriggerWait(WAITTYPE_THREADEND, __KernelGetCurThread(), thread->nt.exitStatus, true);
hleReSchedule("thread _exited");
// The stack will be deallocated when the thread is deleted.
}
@ -1487,15 +1534,17 @@ void sceKernelExitDeleteThread()
t->nt.status = THREADSTATUS_DORMANT;
t->nt.exitStatus = PARAM(0);
__KernelFireThreadEnd(t);
// TODO: Why not?
//userMemory.Free(currentThread->stackBlock);
t->stackBlock = 0;
__KernelRemoveFromThreadQueue(t);
currentThread = 0;
RETURN(kernelObjects.Destroy<Thread>(threadHandle));
__KernelTriggerWait(WAITTYPE_THREADEND, threadHandle, t->nt.exitStatus, true);
hleReSchedule("thead exited with delete");
__KernelTriggerWait(WAITTYPE_THREADEND, threadHandle);
RETURN(kernelObjects.Destroy<Thread>(threadHandle));
}
else
{
@ -1540,11 +1589,9 @@ int sceKernelDeleteThread(int threadHandle)
__KernelRemoveFromThreadQueue(t);
__KernelFireThreadEnd(t);
__KernelTriggerWait(WAITTYPE_THREADEND, threadHandle);
// TODO: Should this reschedule ever? Probably no?
__KernelTriggerWait(WAITTYPE_THREADEND, threadHandle, SCE_KERNEL_ERROR_THREAD_TERMINATED, true);
//TODO: should we really reschedule here?
//if (!__KernelTriggerWait(WAITTYPE_THREADEND, threadHandle))
// hleReSchedule("thread deleted");
return kernelObjects.Destroy<Thread>(threadHandle);
}
@ -1566,8 +1613,8 @@ int sceKernelTerminateDeleteThread(int threadno)
INFO_LOG(HLE, "sceKernelTerminateDeleteThread(%i)", threadno);
//TODO: should we really reschedule here?
if (!__KernelTriggerWait(WAITTYPE_THREADEND, threadno))
hleReSchedule("termdeletethread");
__KernelTriggerWait(WAITTYPE_THREADEND, threadno, SCE_KERNEL_ERROR_THREAD_TERMINATED, false);
hleReSchedule("termdeletethread");
// TODO: Why not delete?
return 0; //kernelObjects.Destroy<Thread>(threadno));
@ -1589,9 +1636,11 @@ int sceKernelTerminateThread(u32 threadID)
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
{
t->nt.exitStatus = SCE_KERNEL_ERROR_THREAD_TERMINATED;
t->nt.status = THREADSTATUS_DORMANT;
__KernelFireThreadEnd(t);
__KernelTriggerWait(WAITTYPE_THREADEND, threadID);
// TODO: Should this really reschedule?
__KernelTriggerWait(WAITTYPE_THREADEND, threadID, t->nt.exitStatus, true);
}
// TODO: Return an error if it doesn't exist?
return 0;
@ -1811,47 +1860,84 @@ void sceKernelSleepThreadCB()
__KernelCheckCallbacks();
}
void sceKernelWaitThreadEnd()
int sceKernelWaitThreadEnd(SceUID threadID, u32 timeoutPtr)
{
SceUID id = PARAM(0);
DEBUG_LOG(HLE,"sceKernelWaitThreadEnd(%i)",id);
DEBUG_LOG(HLE, "sceKernelWaitThreadEnd(%i, %08x)", threadID);
if (threadID == 0 || threadID == currentThread)
return SCE_KERNEL_ERROR_ILLEGAL_THID;
u32 error;
Thread *t = kernelObjects.Get<Thread>(id, error);
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
{
if (t->nt.status != THREADSTATUS_DORMANT) {
__KernelWaitCurThread(WAITTYPE_THREADEND, id, 0, 0, false);
} else {
DEBUG_LOG(HLE,"sceKernelWaitThreadEnd - thread %i already ended. Doing nothing.", id);
if (t->nt.status != THREADSTATUS_DORMANT)
{
if (Memory::IsValidAddress(timeoutPtr))
__KernelScheduleThreadEndTimeout(currentThread, threadID, Memory::Read_U32(timeoutPtr));
__KernelWaitCurThread(WAITTYPE_THREADEND, threadID, 0, timeoutPtr, false);
}
return t->nt.exitStatus;
}
else
{
ERROR_LOG(HLE,"sceKernelWaitThreadEnd - bad thread %i", id);
ERROR_LOG(HLE, "sceKernelWaitThreadEnd - bad thread %i", threadID);
return error;
}
RETURN(0);
}
void sceKernelWaitThreadEndCB()
int sceKernelWaitThreadEndCB(SceUID threadID, u32 timeoutPtr)
{
SceUID id = PARAM(0);
DEBUG_LOG(HLE,"sceKernelWaitThreadEnd(%i)",id);
DEBUG_LOG(HLE, "sceKernelWaitThreadEnd(%i)", threadID);
if (threadID == 0 || threadID == currentThread)
return SCE_KERNEL_ERROR_ILLEGAL_THID;
u32 error;
Thread *t = kernelObjects.Get<Thread>(id, error);
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
{
if (t->nt.status != THREADSTATUS_DORMANT) {
__KernelWaitCurThread(WAITTYPE_THREADEND, id, 0, 0, true);
} else {
DEBUG_LOG(HLE,"sceKernelWaitThreadEnd - thread %i already ended. Doing nothing.", id);
hleCheckCurrentCallbacks();
if (t->nt.status != THREADSTATUS_DORMANT)
{
if (Memory::IsValidAddress(timeoutPtr))
__KernelScheduleThreadEndTimeout(currentThread, threadID, Memory::Read_U32(timeoutPtr));
__KernelWaitCurThread(WAITTYPE_THREADEND, threadID, 0, timeoutPtr, true);
}
__KernelCheckCallbacks();
return t->nt.exitStatus;
}
else
{
ERROR_LOG(HLE,"sceKernelWaitThreadEnd - bad thread %i", id);
ERROR_LOG(HLE, "sceKernelWaitThreadEnd - bad thread %i", threadID);
return error;
}
}
int sceKernelReleaseWaitThread(SceUID threadID)
{
DEBUG_LOG(HLE, "sceKernelReleaseWaitThread(%i)", threadID);
if (__KernelInCallback())
WARN_LOG(HLE, "UNTESTED sceKernelReleaseWaitThread() might not do the right thing in a callback");
if (threadID == 0 || threadID == currentThread)
return SCE_KERNEL_ERROR_ILLEGAL_THID;
u32 error;
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
{
if (!t->isWaiting())
return SCE_KERNEL_ERROR_NOT_WAIT;
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_RELEASE_WAIT);
hleReSchedule("thread released from wait");
return 0;
}
else
{
ERROR_LOG(HLE, "sceKernelReleaseWaitThread - bad thread %i", threadID);
return error;
}
RETURN(0);
}
void sceKernelSuspendThread()
@ -2113,7 +2199,10 @@ void __KernelSwitchContext(Thread *target, const char *reason)
__KernelSaveContext(&cur->context);
oldPC = currentMIPS->pc;
oldUID = cur->GetUID();
oldName = cur->GetName();
// Profile on Windows shows this takes time, skip it.
if (DEBUG_LEVEL <= MAX_LOGLEVEL)
oldName = cur->GetName();
}
currentThread = target->GetUID();
__KernelLoadContext(&target->context);

View File

@ -40,9 +40,10 @@ void sceKernelGetThreadCurrentPriority();
int sceKernelStartThread(SceUID threadToStartID, u32 argSize, u32 argBlockPtr);
u32 sceKernelSuspendDispatchThread();
u32 sceKernelResumeDispatchThread(u32 suspended);
void sceKernelWaitThreadEnd();
int sceKernelWaitThreadEnd(SceUID threadID, u32 timeoutPtr);
u32 sceKernelReferThreadStatus(u32 uid, u32 statusPtr);
u32 sceKernelReferThreadRunStatus(u32 uid, u32 statusPtr);
int sceKernelReleaseWaitThread(SceUID threadID);
void sceKernelChangeCurrentThreadAttr();
void sceKernelRotateThreadReadyQueue();
void sceKernelCheckThreadStack();
@ -52,7 +53,7 @@ void sceKernelWakeupThread();
void sceKernelCancelWakeupThread();
int sceKernelTerminateDeleteThread(int threadno);
int sceKernelTerminateThread(u32 threadID);
void sceKernelWaitThreadEndCB();
int sceKernelWaitThreadEndCB(SceUID threadID, u32 timeoutPtr);
void sceKernelGetThreadExitStatus();
u32 sceKernelGetThreadmanIdType(u32);
u32 sceKernelGetThreadmanIdList(u32 type, u32 readBufPtr, u32 readBufSize, u32 idCountPtr);

View File

@ -28,17 +28,25 @@ static bool useMediaEngine;
// MPEG AVC elementary stream.
static const int MPEG_AVC_ES_SIZE = 2048; // MPEG packet size.
// MPEG ATRAC elementary stream.
static const int MPEG_ATRAC_ES_SIZE = 2112;
static const int MPEG_ATRAC_ES_OUTPUT_SIZE = 8192;
// MPEG PCM elementary stream.
static const int MPEG_PCM_ES_SIZE = 320;
static const int MPEG_PCM_ES_OUTPUT_SIZE = 320;
// MPEG Userdata elementary stream.
static const int MPEG_DATA_ES_SIZE = 0xA0000;
static const int MPEG_DATA_ES_OUTPUT_SIZE = 0xA0000;
// MPEG analysis results.
static const int MPEG_VERSION_0012 = 0;
static const int MPEG_VERSION_0013 = 1;
static const int MPEG_VERSION_0014 = 2;
static const int MPEG_VERSION_0015 = 3;
// MPEG streams.
static const int MPEG_AVC_STREAM = 0;
static const int MPEG_ATRAC_STREAM = 1;
@ -69,6 +77,9 @@ static const int NUM_ES_BUFFERS = 2;
static const int PSP_ERROR_MPEG_NO_DATA = 0x80618001;
static const int TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650 = 0X00;
static const int TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888 = 0X03;
int getMaxAheadTimestamp(const SceMpegRingBuffer &ringbuf) {
return std::max(40000, ringbuf.packets * 700); // empiric value from JPCSP, thanks!
}
@ -82,9 +93,10 @@ struct AvcContext {
};
struct StreamInfo
{
{
int type;
int num;
int sid;
};
typedef std::map<u32, StreamInfo> StreamInfoMap;
@ -114,6 +126,10 @@ struct MpegContext {
p.Do(avcRegistered);
p.Do(atracRegistered);
p.Do(pcmRegistered);
p.Do(dataRegistered);
p.Do(ignoreAtrac);
p.Do(ignorePcm);
p.Do(ignoreAvc);
p.Do(isAnalyzed);
p.Do<StreamInfo>(streamMap);
mediaengine->DoState(p);
@ -143,6 +159,11 @@ struct MpegContext {
bool avcRegistered;
bool atracRegistered;
bool pcmRegistered;
bool dataRegistered;
bool ignoreAtrac;
bool ignorePcm;
bool ignoreAvc;
bool isAnalyzed;
@ -322,23 +343,22 @@ void __MpegShutdown() {
mpegMap.clear();
}
void sceMpegInit()
u32 sceMpegInit()
{
WARN_LOG(HLE, "sceMpegInit()");
RETURN(0);
return 0;
}
u32 sceMpegRingbufferQueryMemSize(int packets)
{
DEBUG_LOG(HLE, "sceMpegRingbufferQueryMemSize(%i)", packets);
return packets * (104 + 2048);
int size = packets * (104 + 2048);
return size;
}
u32 sceMpegRingbufferConstruct(u32 ringbufferAddr, u32 numPackets, u32 data, u32 size, u32 callbackAddr, u32 callbackArg)
{
DEBUG_LOG(HLE, "sceMpegRingbufferConstruct(%08x, %i, %08x, %i, %08x, %i)",
ringbufferAddr, numPackets, data, size, callbackAddr, callbackArg);
DEBUG_LOG(HLE, "sceMpegRingbufferConstruct(%08x, %i, %08x, %i, %08x, %i)", ringbufferAddr, numPackets, data, size, callbackAddr, callbackArg);
SceMpegRingBuffer ring;
InitRingbuffer(&ring, numPackets, data, size, callbackAddr, callbackArg);
Memory::WriteStruct(ringbufferAddr, &ring);
@ -384,6 +404,10 @@ u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr, u32 f
ctx->avcRegistered = false;
ctx->atracRegistered = false;
ctx->pcmRegistered = false;
ctx->dataRegistered = false;
ctx->ignoreAtrac = false;
ctx->ignorePcm = false;
ctx->ignoreAvc = false;
ctx->defaultFrameWidth = frameWidth;
for (int i = 0; i < NUM_ES_BUFFERS; i++) {
ctx->esBuffers[i] = false;
@ -428,12 +452,17 @@ int sceMpegAvcDecodeMode(u32 mpeg, u32 modeAddr)
if (Memory::IsValidAddress(modeAddr)) {
int mode = Memory::Read_U32(modeAddr);
int pixelMode = Memory::Read_U32(modeAddr + 4);
ctx->videoPixelMode = pixelMode;
return 0;
if (pixelMode >= TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650 && pixelMode <= TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888) {
ctx->videoPixelMode = pixelMode;
} else {
ERROR_LOG(HLE, "sceMpegAvcDecodeMode(%i, %i): unknown pixelMode ", mode, pixelMode);
}
} else {
WARN_LOG(HLE, "sceMpegAvcDecodeMode(%08x, %08x): invalid modeAddr", mpeg, modeAddr);
return -1;
ERROR_LOG(HLE, "sceMpegAvcDecodeMode(%08x, %08x): invalid modeAddr", mpeg, modeAddr);
return -1;
}
return 0;
}
int sceMpegQueryStreamOffset(u32 mpeg, u32 bufferAddr, u32 offsetAddr)
@ -451,13 +480,13 @@ int sceMpegQueryStreamOffset(u32 mpeg, u32 bufferAddr, u32 offsetAddr)
if (ctx->mpegMagic != PSMF_MAGIC) {
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad PSMF magic");
return -1; //ERROR_MPEG_INVALID_VALUE
return ERROR_MPEG_INVALID_VALUE;
} else if (ctx->mpegVersion < 0) {
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad version");
return -1; //ERROR_MPEG_BAD_VERSION
return ERROR_MPEG_BAD_VERSION;
} else if ((ctx->mpegOffset & 2047) != 0 || ctx->mpegOffset == 0) {
ERROR_LOG(HLE, "sceMpegQueryStreamOffset: Bad offset");
return -1; //ERROR_MPEG_INVALID_VALUE
return ERROR_MPEG_INVALID_VALUE;
}
Memory::Write_U32(ctx->mpegOffset, offsetAddr);
return 0;
@ -510,6 +539,12 @@ int sceMpegRegistStream(u32 mpeg, u32 streamType, u32 streamNum)
case MPEG_PCM_STREAM:
ctx->pcmRegistered = true;
break;
case MPEG_DATA_STREAM:
ctx->dataRegistered = true;
break;
default :
DEBUG_LOG(HLE, "sceMpegRegistStream(%i) : unknown stream type", streamType);
break;
}
// ...
u32 sid = streamIdGen++;
@ -664,16 +699,50 @@ u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr, u32 i
u32 sceMpegAvcDecodeStop(u32 mpeg, u32 frameWidth, u32 bufferAddr, u32 statusAddr)
{
WARN_LOG(HLE, "HACK sceMpegAvcDecodeStop(%08x, %08x, %08x, statusAddr=%08x)",
mpeg, frameWidth, bufferAddr, statusAddr);
ERROR_LOG(HLE, "sceMpegAvcDecodeStop(%08x, %08x, %08x, %08x)", mpeg, frameWidth, bufferAddr, statusAddr);
if (Memory::IsValidAddress(statusAddr)) {
Memory::Write_U32(0,statusAddr);
} else {
ERROR_LOG(HLE, "sceMpegAvcDecodeStop(%08x, %08x): invalid statusAddr", mpeg, statusAddr);
return -1;
}
return 0;
}
void sceMpegUnRegistStream()
u32 sceMpegUnRegistStream(u32 mpeg, int streamUid)
{
WARN_LOG(HLE, "HACK sceMpegUnRegistStream(...)");
RETURN(0);
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx)
{
WARN_LOG(HLE, "sceMpegUnRegistStream(%08x, %i, %i): bad mpeg handle", mpeg, streamUid);
return -1;
}
StreamInfo info;
switch (info.type) {
case MPEG_AVC_STREAM:
ctx->avcRegistered = false;
break;
case MPEG_AUDIO_STREAM:
case MPEG_ATRAC_STREAM:
ctx->atracRegistered = false;
break;
case MPEG_PCM_STREAM:
ctx->pcmRegistered = false;
break;
case MPEG_DATA_STREAM:
ctx->dataRegistered = false;
break;
default :
DEBUG_LOG(HLE, "sceMpegUnRegistStream(%i) : unknown streamID ", streamUid);
break;
}
ctx->streamMap[streamUid] = info;
info.type = -1;
info.sid = -1 ;
ctx->isAnalyzed = false;
return 0;
}
int sceMpegAvcDecodeDetail(u32 mpeg, u32 detailAddr)
@ -704,21 +773,25 @@ int sceMpegAvcDecodeDetail(u32 mpeg, u32 detailAddr)
return 0;
}
void sceMpegAvcDecodeStopYCbCr()
u32 sceMpegAvcDecodeStopYCbCr(u32 mpeg, u32 bufferAddr, u32 statusAddr)
{
WARN_LOG(HLE, "HACK sceMpegAvcDecodeStopYCbCr(...)");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceMpegAvcDecodeStopYCbCr(%08x, %08x, %08x)", mpeg, bufferAddr, statusAddr);
return 0;
}
int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initAddr)
{
WARN_LOG(HLE, "HACK sceMpegAvcDecodeYCbCr(%08x, %08x, %08x, %08x)", mpeg, auAddr, bufferAddr, initAddr);
ERROR_LOG(HLE, "UNIMPL sceMpegAvcDecodeYCbCr(%08x, %08x, %08x, %08x)", mpeg, auAddr, bufferAddr, initAddr);
return 0;
}
u32 sceMpegAvcDecodeFlush(u32 mpeg)
{
MpegContext *ctx = getMpegCtx(mpeg);
ERROR_LOG(HLE, "UNIMPL sceMpegAvcDecodeFlush(%08x)", mpeg);
if ( ctx->videoFrameCount > 0 || ctx->audioFrameCount > 0) {
//__MpegFinish();
}
return 0;
}
@ -781,9 +854,6 @@ int sceMpegRingbufferAvailableSize(u32 ringbufferAddr)
return ringbuffer.packetsFree;
}
void PostPutAction::run(MipsCall &call) {
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ringAddr_, &ringbuffer);
@ -908,11 +978,11 @@ int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
return result;
}
void sceMpegFinish()
u32 sceMpegFinish()
{
WARN_LOG(HLE, "sceMpegFinish(...)");
ERROR_LOG(HLE, "sceMpegFinish(...)");
//__MpegFinish();
RETURN(0);
return 0;
}
u32 sceMpegQueryMemSize()
@ -973,47 +1043,103 @@ int sceMpegQueryPcmEsSize(u32 mpeg, u32 esSizeAddr, u32 outSizeAddr)
}
void sceMpegChangeGetAuMode()
u32 sceMpegChangeGetAuMode(u32 mpeg, int streamUid, int mode)
{
WARN_LOG(HLE, "HACK sceMpegChangeGetAuMode(...)");
RETURN(0);
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegChangeGetAuMode(%08x, %i, %i): bad mpeg handle", mpeg, streamUid, mode);
return -1;
}
StreamInfo info;
info.sid = streamUid;
if (info.sid) {
switch (info.type) {
case MPEG_AVC_STREAM:
if(mode == MPEG_AU_MODE_DECODE) {
ctx->ignoreAvc = false;
} else if (mode == MPEG_AU_MODE_SKIP) {
ctx->ignoreAvc = true;
}
break;
case MPEG_AUDIO_STREAM:
case MPEG_ATRAC_STREAM:
if(mode == MPEG_AU_MODE_DECODE) {
ctx->ignoreAtrac = false;
} else if (mode == MPEG_AU_MODE_SKIP) {
ctx->ignoreAtrac = true;
}
break;
case MPEG_PCM_STREAM:
if(mode == MPEG_AU_MODE_DECODE) {
ctx->ignorePcm = false;
} else if (mode == MPEG_AU_MODE_SKIP) {
ctx->ignorePcm = true;
}
break;
default:
ERROR_LOG(HLE, "UNIMPL sceMpegChangeGetAuMode(%08x, %i): unkown streamID", mpeg, streamUid);
break;
}
} else {
ERROR_LOG(HLE, "UNIMPL sceMpegChangeGetAuMode(%08x, %i): unkown streamID", mpeg, streamUid);
}
return 0;
}
void sceMpegGetPcmAu()
u32 sceMpegChangeGetAvcAuMode(u32 mpeg, u32 stream_addr, int mode)
{
WARN_LOG(HLE, "HACK sceMpegGetPcmAu(...)");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceMpegChangeGetAvcAuMode(%08x, %08x, %i)", mpeg, stream_addr, mode);
return 0;
}
void sceMpegRingbufferQueryPackNum()
u32 sceMpegGetPcmAu(u32 mpeg, int streamUid, u32 auAddr, u32 attrAddr)
{
WARN_LOG(HLE, "HACK sceMpegRingbufferQueryPackNum(...)");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceMpegGetPcmAu(%08x, %i, %08x, %08x)", mpeg, streamUid, auAddr, attrAddr);
return 0;
}
void sceMpegFlushAllStream()
u32 sceMpegRingbufferQueryPackNum(int memorySize)
{
WARN_LOG(HLE, "HACK sceMpegFlushAllStream(...)");
RETURN(0);
ERROR_LOG(HLE, "sceMpegRingbufferQueryPackNum(%i)", memorySize);
int packets = memorySize / (2048 + 104);
return packets;
}
void sceMpegAvcCopyYCbCr()
u32 sceMpegFlushAllStream(u32 mpeg)
{
WARN_LOG(HLE, "HACK sceMpegAvcCopyYCbCr(...)");
RETURN(0);
MpegContext *ctx = getMpegCtx(mpeg);
ERROR_LOG(HLE, "UNIMPL sceMpegFlushAllStream(%08x)", mpeg);
if ( ctx->videoFrameCount > 0 || ctx->audioFrameCount > 0) {
//__MpegFinish();
}
return 0;
}
void sceMpegAtracDecode()
u32 sceMpegFlushStream(u32 mpeg, int stream_addr)
{
WARN_LOG(HLE, "HACK sceMpegAtracDecode(...)");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceMpegFlushStream(%08x, %i)", mpeg , stream_addr);
//__MpegFinish();
return 0;
}
u32 sceMpegAvcCopyYCbCr(u32 mpeg, u32 sourceAddr, u32 YCbCrAddr)
{
ERROR_LOG(HLE, "UNIMPL sceMpegAvcCopyYCbCr(%08x, %08x, %08x)", mpeg, sourceAddr, YCbCrAddr);
return 0;
}
u32 sceMpegAtracDecode(u32 mpeg, u32 auAddr, u32 bufferAddr, int init)
{
ERROR_LOG(HLE, "UNIMPL sceMpegAtracDecode(%08x, %08x, %08x, %i)", mpeg, auAddr, bufferAddr, init);
return 0;
}
// YCbCr -> RGB color space conversion
void sceMpegAvcCsc()
u32 sceMpegAvcCsc(u32 mpeg, u32 sourceAddr, u32 rangeAddr, int frameWidth, u32 destAddr)
{
WARN_LOG(HLE, "HACK sceMpegAvcCsc(...)");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceMpegAvcCsc(%08x, %08x, %08x, %i, %08x)", mpeg, sourceAddr, rangeAddr, frameWidth, destAddr);
return 0;
}
u32 sceMpegRingbufferDestruct(u32 ringbufferAddr)
@ -1023,10 +1149,10 @@ u32 sceMpegRingbufferDestruct(u32 ringbufferAddr)
return 0;
}
void sceMpegAvcInitYCbCr()
u32 sceMpegAvcInitYCbCr(u32 mpeg, int mode, int width, int height, u32 ycbcr_addr)
{
WARN_LOG(HLE, "HACK sceMpegAvcInitYCbCr(...)");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceMpegAvcInitYCbCr(%08x, %i, %i, %i, %08x)", mpeg, mode, width, height, ycbcr_addr);
return 0;
}
int sceMpegAvcQueryYCbCrSize(u32 mpeg, u32 mode, u32 width, u32 height, u32 resultAddr)
@ -1034,7 +1160,7 @@ int sceMpegAvcQueryYCbCrSize(u32 mpeg, u32 mode, u32 width, u32 height, u32 resu
if ((width & 15) != 0 || (height & 15) != 0 || height > 272 || width > 480)
{
ERROR_LOG(HLE, "sceMpegAvcQueryYCbCrSize: bad w/h %i x %i", width, height);
return -1;
return ERROR_MPEG_INVALID_VALUE;
}
DEBUG_LOG(HLE, "sceMpegAvcQueryYCbCrSize(%08x, %i, %i, %i, %08x)", mpeg, mode, width, height, resultAddr);
@ -1043,6 +1169,36 @@ int sceMpegAvcQueryYCbCrSize(u32 mpeg, u32 mode, u32 width, u32 height, u32 resu
return 0;
}
int sceMp3Decode(u32 mp3, u32 outPcmPtr)
{
ERROR_LOG(HLE, "UNIMPL sceMp3Decoder(%08x,%08x)", mp3, outPcmPtr);
return 0;
}
int sceMp3ResetPlayPosition(u32 mp3)
{
ERROR_LOG(HLE, "UNIMPL sceMp3ResetPlayPosition(%08x)", mp3);
return 0;
}
int sceMp3CheckStreamDataNeeded(u32 mp3)
{
ERROR_LOG(HLE, "UNIMPL sceMp3CheckStreamDataNeeded(%08x)", mp3);
return 0;
}
u32 sceMpegQueryUserdataEsSize(u32 mpeg, u32 esSizeAddr, u32 outSizeAddr)
{
if (Memory::IsValidAddress(esSizeAddr) && Memory::IsValidAddress(outSizeAddr)) {
DEBUG_LOG(HLE, "sceMpegQueryUserdataEsSize(%08x, %08x, %08x)", mpeg, esSizeAddr, outSizeAddr);
Memory::Write_U32(MPEG_DATA_ES_SIZE, esSizeAddr);
Memory::Write_U32(MPEG_DATA_ES_OUTPUT_SIZE, outSizeAddr);
return 0;
}
ERROR_LOG(HLE, "sceMpegQueryUserdataEsSize - bad pointers(%08x, %08x, %08x)", mpeg, esSizeAddr, outSizeAddr);
return -1;
}
const HLEFunction sceMpeg[] =
{
@ -1054,15 +1210,16 @@ const HLEFunction sceMpeg[] =
{0x21ff80e4,WrapI_UUU<sceMpegQueryStreamOffset>,"sceMpegQueryStreamOffset"},
{0x611e9e11,WrapU_UU<sceMpegQueryStreamSize>,"sceMpegQueryStreamSize"},
{0x42560f23,WrapI_UUU<sceMpegRegistStream>,"sceMpegRegistStream"},
{0x591a4aa2,sceMpegUnRegistStream,"sceMpegUnRegistStream"},
{0x707b7629,sceMpegFlushAllStream,"sceMpegFlushAllStream"},
{0x591a4aa2,WrapU_UI<sceMpegUnRegistStream>,"sceMpegUnRegistStream"},
{0x707b7629,WrapU_U<sceMpegFlushAllStream>,"sceMpegFlushAllStream"},
{0x500F0429,WrapU_UI<sceMpegFlushStream>,"sceMpegFlushStream"},
{0xa780cf7e,WrapI_U<sceMpegMallocAvcEsBuf>,"sceMpegMallocAvcEsBuf"},
{0xceb870b1,WrapI_UI<sceMpegFreeAvcEsBuf>,"sceMpegFreeAvcEsBuf"},
{0x167afd9e,WrapI_UUU<sceMpegInitAu>,"sceMpegInitAu"},
{0x682a619b,sceMpegInit,"sceMpegInit"},
{0x682a619b,WrapU_V<sceMpegInit>,"sceMpegInit"},
{0x606a4649,WrapI_U<sceMpegDelete>,"sceMpegDelete"},
{0x874624d6,sceMpegFinish,"sceMpegFinish"},
{0x800c44df,sceMpegAtracDecode,"sceMpegAtracDecode"},
{0x874624d6,WrapU_V<sceMpegFinish>,"sceMpegFinish"},
{0x800c44df,WrapU_UUUI<sceMpegAtracDecode>,"sceMpegAtracDecode"},
{0x0e3c2e9d,&WrapU_UUUUU<sceMpegAvcDecode>,"sceMpegAvcDecode"},
{0x740fccd1,&WrapU_UUUU<sceMpegAvcDecodeStop>,"sceMpegAvcDecodeStop"},
{0x4571cc64,&WrapU_U<sceMpegAvcDecodeFlush>,"sceMpegAvcDecodeFlush"},
@ -1073,23 +1230,25 @@ const HLEFunction sceMpeg[] =
{0xb240a59e,WrapU_UUU<sceMpegRingbufferPut>,"sceMpegRingbufferPut"},
{0xb5f6dc87,WrapI_U<sceMpegRingbufferAvailableSize>,"sceMpegRingbufferAvailableSize"},
{0xd7a29f46,WrapU_I<sceMpegRingbufferQueryMemSize>,"sceMpegRingbufferQueryMemSize"},
{0x769BEBB6,sceMpegRingbufferQueryPackNum,"sceMpegRingbufferQueryPackNum"},
{0x769BEBB6,WrapU_I<sceMpegRingbufferQueryPackNum>,"sceMpegRingbufferQueryPackNum"},
{0x211a057c,WrapI_UUUUU<sceMpegAvcQueryYCbCrSize>,"sceMpegAvcQueryYCbCrSize"},
{0xf0eb1125,WrapI_UUUU<sceMpegAvcDecodeYCbCr>,"sceMpegAvcDecodeYCbCr"},
{0xf2930c9c,sceMpegAvcDecodeStopYCbCr,"sceMpegAvcDecodeStopYCbCr"},
{0x67179b1b,sceMpegAvcInitYCbCr,"sceMpegAvcInitYCbCr"},
{0x0558B075,sceMpegAvcCopyYCbCr,"sceMpegAvcCopyYCbCr"},
{0x31bd0272,sceMpegAvcCsc,"sceMpegAvcCsc"},
{0x9DCFB7EA,sceMpegChangeGetAuMode,"sceMpegChangeGetAuMode"},
{0x8C1E027D,sceMpegGetPcmAu,"sceMpegGetPcmAu"},
{0xf2930c9c,WrapU_UUU<sceMpegAvcDecodeStopYCbCr>,"sceMpegAvcDecodeStopYCbCr"},
{0x67179b1b,WrapU_UIIIU<sceMpegAvcInitYCbCr>,"sceMpegAvcInitYCbCr"},
{0x0558B075,WrapU_UUU<sceMpegAvcCopyYCbCr>,"sceMpegAvcCopyYCbCr"},
{0x31bd0272,WrapU_UUUIU<sceMpegAvcCsc>,"sceMpegAvcCsc"},
{0x9DCFB7EA,WrapU_UII<sceMpegChangeGetAuMode>,"sceMpegChangeGetAuMode"},
{0x8C1E027D,WrapU_UIUU<sceMpegGetPcmAu>,"sceMpegGetPcmAu"},
{0xC02CF6B5,WrapI_UUU<sceMpegQueryPcmEsSize>,"sceMpegQueryPcmEsSize"},
{0xC45C99CC,WrapU_UUU<sceMpegQueryUserdataEsSize>,"sceMpegQueryUserdataEsSize"},
{0x234586AE,WrapU_UUI<sceMpegChangeGetAvcAuMode>,"sceMpegChangeGetAvcAuMode"},
};
const HLEFunction sceMp3[] =
{
{0x07EC321A,0,"sceMp3ReserveMp3Handle"},
{0x0DB149F4,0,"sceMp3NotifyAddStreamData"},
{0x2A368661,0,"sceMp3ResetPlayPosition"},
{0x2A368661,WrapI_U<sceMp3ResetPlayPosition>,"sceMp3ResetPlayPosition"},
{0x354D27EA,0,"sceMp3GetSumDecodedSample"},
{0x35750070,0,"sceMp3InitResource"},
{0x3C2FA058,0,"sceMp3TermResource"},
@ -1102,10 +1261,12 @@ const HLEFunction sceMp3[] =
{0x8AB81558,0,"sceMp3StartEntry"},
{0x8F450998,0,"sceMp3GetSamplingRate"},
{0xA703FE0F,0,"sceMp3GetInfoToAddStreamData"},
{0xD021C0FB,0,"sceMp3Decode"},
{0xD0A56296,0,"sceMp3CheckStreamDataNeeded"},
{0xD021C0FB,WrapI_UU<sceMp3Decode>,"sceMp3Decode"},
{0xD0A56296,WrapI_U<sceMp3CheckStreamDataNeeded>,"sceMp3CheckStreamDataNeeded"},
{0xD8F54A51,0,"sceMp3GetLoopNum"},
{0xF5478233,0,"sceMp3ReleaseMp3Handle"},
{0xAE6D2027,0,"sceMp3GetVersion"},
{0x3548AEC8,0,"sceMp3GetFrameNum"},
};
void Register_sceMpeg()

View File

@ -313,7 +313,7 @@ u32 scePsmfGetNumberOfStreams(u32 psmfStruct)
ERROR_LOG(HLE, "scePsmfGetNumberOfStreams - invalid psmf");
return ERROR_PSMF_NOT_FOUND;
}
INFO_LOG(HLE, "%i=scePsmfGetNumberOfStreams(%08x)", psmf->getNumStreams(), psmf);
INFO_LOG(HLE, "%i=scePsmfGetNumberOfStreams(%08x)", psmf->getNumStreams(), psmfStruct);
return psmf->getNumStreams();
}

View File

@ -38,8 +38,12 @@ EmuFileType Identify_File(const char *filename)
return FILETYPE_ERROR;
}
u32 id;
fread(&id,4,1,f);
size_t readSize = fread(&id,4,1,f);
fclose(f);
if(readSize != 1)
return FILETYPE_ERROR;
if (id == 'FLE\x7F')
{
if (strstr(filename,".plf") || strstr(filename,"BOOT.BIN") || strstr(filename,".elf") || strstr(filename,".prx") )

View File

@ -351,7 +351,8 @@ namespace MIPSAnalyst
{
FILE *file = fopen(filename,"wb");
u32 num = 0;
fwrite(&num,4,1,file); //fill in later
if(fwrite(&num,4,1,file) != 1) //fill in later
WARN_LOG(CPU, "Could not store hash map %s", filename);
for (vector<Function>::iterator iter = functions.begin(); iter!=functions.end(); iter++)
{
@ -363,12 +364,16 @@ namespace MIPSAnalyst
strcpy(temp.name, f.name);
temp.hash=f.hash;
temp.size=f.size;
fwrite((char*)&temp,sizeof(temp),1,file);
if(fwrite((char*)&temp,sizeof(temp),1,file) != 1) {
WARN_LOG(CPU, "Could not store hash map %s", filename);
break;
}
num++;
}
}
fseek(file,0,SEEK_SET);
fwrite(&num,4,1,file); //fill in later
if(fwrite(&num,4,1,file) != 1) //fill in later
WARN_LOG(CPU, "Could not store hash map %s", filename);
fclose(file);
}
@ -380,25 +385,26 @@ namespace MIPSAnalyst
FILE *file = fopen(filename, "rb");
int num;
fread(&num,4,1,file);
for (int i=0; i<num; i++)
{
HashMapFunc temp;
fread(&temp,sizeof(temp),1,file);
map<u32,Function*>::iterator iter = hashToFunction.find(temp.hash);
if (iter != hashToFunction.end())
if(fread(&num,4,1,file) == 1) {
for (int i=0; i<num; i++)
{
//yay, found a function!
Function &f = *(iter->second);
if (f.size==temp.size)
{
strcpy(f.name, temp.name);
f.hash=temp.hash;
f.size=temp.size;
HashMapFunc temp;
if(fread(&temp,sizeof(temp),1,file) == 1) {
map<u32,Function*>::iterator iter = hashToFunction.find(temp.hash);
if (iter != hashToFunction.end())
{
//yay, found a function!
Function &f = *(iter->second);
if (f.size==temp.size)
{
strcpy(f.name, temp.name);
f.hash=temp.hash;
f.size=temp.size;
}
}
}
}
}
fclose(file);
}
void CompileLeafs()

View File

@ -931,7 +931,7 @@ void MIPSInterpret(u32 op) //only for those rare ones
// Try to disassemble it
char disasm[256];
MIPSDisAsm(op, currentMIPS->pc, disasm);
_dbg_assert_msg_(CPU, 0, disasm);
_dbg_assert_msg_(CPU, 0, "%s", disasm);
currentMIPS->pc += 4;
}
}

View File

@ -16,13 +16,11 @@ arm {
x86 {
SOURCES += ../Common/ABI.cpp \
../Common/CPUDetect.cpp \
../Common/MathUtil.cpp \
../Common/Thunk.cpp \
../Common/x64Analyzer.cpp \
../Common/x64Emitter.cpp
HEADERS += ../Common/ABI.h \
../Common/CPUDetect.h \
../Common/MathUtil.h \
../Common/Thunk.h \
../Common/x64Analyzer.h \
../Common/x64Emitter.h
@ -47,10 +45,10 @@ SOURCES += ../Common/ColorUtil.cpp \
../Common/Hash.cpp \
../Common/IniFile.cpp \
../Common/LogManager.cpp \
../Common/MathUtil.cpp \
../Common/MemArena.cpp \
../Common/MemoryUtil.cpp \
../Common/Misc.cpp \
../Common/MathUtil.cpp \
../Common/MsgHandler.cpp \
../Common/StringUtil.cpp \
../Common/Thread.cpp \
@ -66,9 +64,9 @@ HEADERS += ../Common/ColorUtil.h \
../Common/Hash.h \
../Common/IniFile.h \
../Common/LogManager.h \
../Common/MathUtil.h \
../Common/MemArena.h \
../Common/MemoryUtil.h \
../Common/MathUtil.h \
../Common/MsgHandler.h \
../Common/StringUtil.h \
../Common/Thread.h \

View File

@ -1,4 +1,4 @@
blackberry|symbian: CONFIG += mobile_platform
blackberry|symbian|contains(MEEGO_EDITION,harmattan): CONFIG += mobile_platform
unix:!blackberry:!symbian:!macx: CONFIG += linux
# Global specific
@ -17,6 +17,7 @@ mobile_platform: DEFINES += USING_GLES2
# Platform specific
contains(MEEGO_EDITION,harmattan): DEFINES += MEEGO_EDITION_HARMATTAN "_SYS_UCONTEXT_H=1"
blackberry: {
# They try to force QCC with all mkspecs
# QCC is 4.4.1, we need 4.6.3