diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d96a73acb..7ce043f0a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -560,6 +560,8 @@ add_library(native STATIC native/gfx_es2/fbo.h native/gfx_es2/gl_state.cpp native/gfx_es2/gl_state.h + native/gfx_es2/gpu_features.cpp + native/gfx_es2/gpu_features.h native/gfx_es2/glsl_program.cpp native/gfx_es2/glsl_program.h native/gfx_es2/vertex_format.cpp diff --git a/Common/CPUDetect.cpp b/Common/CPUDetect.cpp index 40c82e481d..0aec27a983 100644 --- a/Common/CPUDetect.cpp +++ b/Common/CPUDetect.cpp @@ -58,22 +58,12 @@ void __cpuidex(int regs[4], int cpuid_leaf, int ecxval) ELOG("CPUID %08x failed!", cpuid_leaf); } #else - asm volatile ( -#if defined(__i386__) - "pushl %%ebx;\n\t" -#endif + asm ( "cpuid;\n\t" "movl %%ebx, %1;\n\t" -#if defined(__i386__) - "popl %%ebx;\n\t" -#endif :"=a" (regs[0]), "=m" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) :"a" (cpuid_leaf), "c" (ecxval) -#if !defined(__i386__) :"%ebx"); -#else - ); -#endif #endif } void __cpuid(int regs[4], int cpuid_leaf) diff --git a/Common/KeyMap.cpp b/Common/KeyMap.cpp index 7c0772dc46..3583d17d87 100644 --- a/Common/KeyMap.cpp +++ b/Common/KeyMap.cpp @@ -15,16 +15,20 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#ifdef _WIN32 +#include +#endif + #include "file/ini_file.h" #include "input/input_state.h" -#include "../Core/Config.h" #include "base/NativeApp.h" + #include "KeyMap.h" #include "../Core/HLE/sceUtility.h" +#include "../Core/Config.h" #include - namespace KeyMap { KeyDef AxisDef(int deviceId, int axisId, int direction); @@ -37,7 +41,7 @@ struct DefMappingStruct { KeyMapping g_controllerMap; -static const DefMappingStruct defaultKeyboardKeyMap[] = { +static const DefMappingStruct defaultQwertyKeyboardKeyMap[] = { {CTRL_SQUARE, NKCODE_A}, {CTRL_TRIANGLE, NKCODE_S}, {CTRL_CIRCLE, NKCODE_X}, @@ -65,6 +69,62 @@ static const DefMappingStruct defaultKeyboardKeyMap[] = { {VIRTKEY_PAUSE , NKCODE_ESCAPE}, }; +static const DefMappingStruct defaultAzertyKeyboardKeyMap[] = { + {CTRL_SQUARE, NKCODE_Q}, + {CTRL_TRIANGLE, NKCODE_S}, + {CTRL_CIRCLE, NKCODE_X}, + {CTRL_CROSS, NKCODE_W}, + {CTRL_LTRIGGER, NKCODE_Q}, + {CTRL_RTRIGGER, NKCODE_W}, + + {CTRL_START, NKCODE_SPACE}, +#ifdef _WIN32 + {CTRL_SELECT, NKCODE_V}, +#else + {CTRL_SELECT, NKCODE_ENTER}, +#endif + {CTRL_UP , NKCODE_DPAD_UP}, + {CTRL_DOWN , NKCODE_DPAD_DOWN}, + {CTRL_LEFT , NKCODE_DPAD_LEFT}, + {CTRL_RIGHT, NKCODE_DPAD_RIGHT}, + {VIRTKEY_AXIS_Y_MAX, NKCODE_I}, + {VIRTKEY_AXIS_Y_MIN, NKCODE_K}, + {VIRTKEY_AXIS_X_MIN, NKCODE_J}, + {VIRTKEY_AXIS_X_MAX, NKCODE_L}, + {VIRTKEY_RAPID_FIRE , NKCODE_SHIFT_LEFT}, + {VIRTKEY_UNTHROTTLE , NKCODE_TAB}, + {VIRTKEY_SPEED_TOGGLE, NKCODE_GRAVE}, + {VIRTKEY_PAUSE , NKCODE_ESCAPE}, +}; + +static const DefMappingStruct defaultQwertzKeyboardKeyMap[] = { + {CTRL_SQUARE, NKCODE_A}, + {CTRL_TRIANGLE, NKCODE_S}, + {CTRL_CIRCLE, NKCODE_X}, + {CTRL_CROSS, NKCODE_Y}, + {CTRL_LTRIGGER, NKCODE_Q}, + {CTRL_RTRIGGER, NKCODE_W}, + + {CTRL_START, NKCODE_SPACE}, +#ifdef _WIN32 + {CTRL_SELECT, NKCODE_V}, +#else + {CTRL_SELECT, NKCODE_ENTER}, +#endif + {CTRL_UP , NKCODE_DPAD_UP}, + {CTRL_DOWN , NKCODE_DPAD_DOWN}, + {CTRL_LEFT , NKCODE_DPAD_LEFT}, + {CTRL_RIGHT, NKCODE_DPAD_RIGHT}, + {VIRTKEY_AXIS_Y_MAX, NKCODE_I}, + {VIRTKEY_AXIS_Y_MIN, NKCODE_K}, + {VIRTKEY_AXIS_X_MIN, NKCODE_J}, + {VIRTKEY_AXIS_X_MAX, NKCODE_L}, + {VIRTKEY_RAPID_FIRE , NKCODE_SHIFT_LEFT}, + {VIRTKEY_UNTHROTTLE , NKCODE_TAB}, + {VIRTKEY_SPEED_TOGGLE, NKCODE_GRAVE}, + {VIRTKEY_PAUSE , NKCODE_ESCAPE}, +}; + static const DefMappingStruct default360KeyMap[] = { {VIRTKEY_AXIS_X_MIN, JOYSTICK_AXIS_X, -1}, {VIRTKEY_AXIS_X_MAX, JOYSTICK_AXIS_X, +1}, @@ -265,7 +325,34 @@ static void SetDefaultKeyMap(int deviceId, const DefMappingStruct *array, size_t void SetDefaultKeyMap(DefaultMaps dmap, bool replace) { switch (dmap) { case DEFAULT_MAPPING_KEYBOARD: - SetDefaultKeyMap(DEVICE_ID_KEYBOARD, defaultKeyboardKeyMap, ARRAY_SIZE(defaultKeyboardKeyMap), replace); + { + bool azerty = false; + bool qwertz = false; +#ifdef _WIN32 + HKL localeId = GetKeyboardLayout(0); + + // TODO: Is this list complete enough? + switch ((int)localeId & 0xFFFF) { + case 0x407: + qwertz = true; + break; + case 0x040c: + case 0x080c: + case 0x1009: + azerty = true; + break; + default: + break; + } +#endif + if (azerty) { + SetDefaultKeyMap(DEVICE_ID_KEYBOARD, defaultAzertyKeyboardKeyMap, ARRAY_SIZE(defaultAzertyKeyboardKeyMap), replace); + } else if (qwertz) { + SetDefaultKeyMap(DEVICE_ID_KEYBOARD, defaultQwertzKeyboardKeyMap, ARRAY_SIZE(defaultQwertzKeyboardKeyMap), replace); + } else { + SetDefaultKeyMap(DEVICE_ID_KEYBOARD, defaultQwertyKeyboardKeyMap, ARRAY_SIZE(defaultQwertyKeyboardKeyMap), replace); + } + } break; case DEFAULT_MAPPING_X360: SetDefaultKeyMap(DEVICE_ID_X360_0, default360KeyMap, ARRAY_SIZE(default360KeyMap), replace); diff --git a/Common/Thread.cpp b/Common/Thread.cpp index 8439845be4..38073a030d 100644 --- a/Common/Thread.cpp +++ b/Common/Thread.cpp @@ -24,6 +24,10 @@ #include #endif +#ifdef BLACKBERRY +#include +#endif + #ifdef USE_BEGINTHREADEX #include #endif @@ -38,7 +42,7 @@ int CurrentThreadId() #elif defined __APPLE__ return mach_thread_self(); #else - return 0; + return pthread_self(); #endif } @@ -142,7 +146,11 @@ void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) void SetCurrentThreadAffinity(u32 mask) { +#ifdef BLACKBERRY + ThreadCtl(_NTO_TCTL_RUNMASK, &mask); +#else SetThreadAffinity(pthread_self(), mask); +#endif } static pthread_key_t threadname_key; @@ -170,6 +178,9 @@ static void ThreadnameKeyAlloc() void SetCurrentThreadName(const char* szThreadName) { +#ifdef BLACKBERRY + pthread_setname_np(pthread_self(), szThreadName); +#else pthread_once(&threadname_key_once, ThreadnameKeyAlloc); void* threadname; @@ -177,6 +188,7 @@ void SetCurrentThreadName(const char* szThreadName) free(threadname); pthread_setspecific(threadname_key, strdup(szThreadName)); +#endif INFO_LOG(COMMON, "%s(%s)\n", __FUNCTION__, szThreadName); } diff --git a/Common/Timer.cpp b/Common/Timer.cpp index 3488c98146..e4bb67d075 100644 --- a/Common/Timer.cpp +++ b/Common/Timer.cpp @@ -35,7 +35,12 @@ u32 Timer::GetTimeMs() { #ifdef _WIN32 return timeGetTime(); +#elif defined(BLACKBERRY) + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + return (u32)(time.tv_sec * 1000 + time.tv_nsec / 1000000); #else + // REALTIME is probably not a good idea for measuring updates. struct timeval t; (void)gettimeofday(&t, NULL); return ((u32)(t.tv_sec * 1000 + t.tv_usec / 1000)); diff --git a/Core/Config.cpp b/Core/Config.cpp index 57f110af6d..d06b03a89e 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -23,6 +23,7 @@ #include "Config.h" #include "file/ini_file.h" #include "i18n/i18n.h" +#include "gfx_es2/gpu_features.h" #include "HLE/sceUtility.h" #include "Common/CPUDetect.h" @@ -35,8 +36,7 @@ extern bool isJailed; Config::Config() { } Config::~Config() { } -void Config::Load(const char *iniFileName, const char *controllerIniFilename) -{ +void Config::Load(const char *iniFileName, const char *controllerIniFilename) { iniFilename_ = FindConfigFile(iniFileName != NULL ? iniFileName : "ppsspp.ini"); controllerIniFilename_ = FindConfigFile(controllerIniFilename != NULL ? controllerIniFilename : "controls.ini"); @@ -59,6 +59,9 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) general->Get("CurrentDirectory", ¤tDirectory, ""); general->Get("ShowDebuggerOnLoad", &bShowDebuggerOnLoad, false); + if (!File::Exists(currentDirectory)) + currentDirectory = ""; + std::string defaultLangRegion = "en_US"; if (bFirstRun) { std::string langRegion = System_GetProperty(SYSPROP_LANGREGION); @@ -91,7 +94,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) IniFile::Section *recent = iniFile.GetOrCreateSection("Recent"); recent->Get("MaxRecent", &iMaxRecent, 30); - + // Fix issue from switching from uint (hex in .ini) to int (dec) if (iMaxRecent == 0) iMaxRecent = 30; @@ -119,7 +122,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) #endif cpu->Get("SeparateCPUThread", &bSeparateCPUThread, false); cpu->Get("AtomicAudioLocks", &bAtomicAudioLocks, false); - + #ifdef __SYMBIAN32__ cpu->Get("SeparateIOThread", &bSeparateIOThread, false); #else @@ -130,14 +133,13 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics"); graphics->Get("ShowFPSCounter", &iShowFPSCounter, false); - graphics->Get("RenderingMode", &iRenderingMode, - // Many ARMv6 devices have serious problems with buffered rendering. -#if defined(ARM) && !defined(ARMV7) - 0 -#else - 1 -#endif - ); // default is buffered rendering mode + + int renderingModeDefault = 1; // Buffered + if (System_GetProperty(SYSPROP_NAME) == "samsung:GT-S5360") { + renderingModeDefault = 0; // Non-buffered + } + + graphics->Get("RenderingMode", &iRenderingMode, renderingModeDefault); graphics->Get("SoftwareRendering", &bSoftwareRendering, false); graphics->Get("HardwareTransform", &bHardwareTransform, true); graphics->Get("TextureFiltering", &iTexFiltering, 1); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 3ee7695ff4..2d8cdc7d58 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -130,7 +130,7 @@ Updating git-version.cpp - ..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avdevice.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;%(AdditionalDependencies) + %(AdditionalDependencies) @@ -587,4 +587,4 @@ - + \ No newline at end of file diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 6ab5cf2e55..b9867e9908 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -76,6 +76,8 @@ int slicelength; MEMORY_ALIGNED16(s64) globalTimer; s64 idledCycles; +s64 lastGlobalTimeTicks; +s64 lastGlobalTimeUs; static std::recursive_mutex externalEventSection; @@ -84,6 +86,11 @@ void (*advanceCallback)(int cyclesExecuted) = NULL; void SetClockFrequencyMHz(int cpuMhz) { + // When the mhz changes, we keep track of what "time" it was before hand. + // This way, time always moves forward, even if mhz is changed. + lastGlobalTimeUs = GetGlobalTimeUs(); + lastGlobalTimeTicks = GetTicks(); + CPU_HZ = cpuMhz * 1000000; // TODO: Rescale times of scheduled events? } @@ -93,6 +100,13 @@ int GetClockFrequencyMHz() return CPU_HZ / 1000000; } +u64 GetGlobalTimeUs() +{ + s64 ticksSinceLast = GetTicks() - lastGlobalTimeTicks; + s64 usSinceLast = ticksSinceLast / GetClockFrequencyMHz(); + return lastGlobalTimeUs + usSinceLast; +} + Event* GetNewEvent() { @@ -162,6 +176,8 @@ void Init() slicelength = INITIAL_SLICE_LENGTH; globalTimer = 0; idledCycles = 0; + lastGlobalTimeTicks = 0; + lastGlobalTimeUs = 0; hasTsEvents = 0; } @@ -624,7 +640,7 @@ void DoState(PointerWrap &p) { std::lock_guard lk(externalEventSection); - auto s = p.Section("CoreTiming", 1); + auto s = p.Section("CoreTiming", 1, 2); if (!s) return; @@ -640,6 +656,14 @@ void DoState(PointerWrap &p) p.Do(slicelength); p.Do(globalTimer); p.Do(idledCycles); + + if (s >= 2) { + p.Do(lastGlobalTimeTicks); + p.Do(lastGlobalTimeUs); + } else { + lastGlobalTimeTicks = 0; + lastGlobalTimeUs = 0; + } } } // namespace diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index 1b72f03eb7..8c53987e3a 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -79,6 +79,7 @@ namespace CoreTiming u64 GetTicks(); u64 GetIdleTicks(); + u64 GetGlobalTimeUs(); // Returns the event_type identifier. int RegisterEvent(const char *name, TimedCallback callback); diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index 9763605fa6..e45c66fa6e 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -260,7 +260,7 @@ struct SaveFileInfo memset(title, 0, 128); memset(saveTitle, 0, 128); memset(saveDetail, 0, 1024); - modif_time = {0}; + memset(&modif_time, 0, sizeof(modif_time)); } void DoState(PointerWrap &p) diff --git a/Core/HLE/FunctionWrappers.h b/Core/HLE/FunctionWrappers.h index b3f362d5ed..b80a51b139 100644 --- a/Core/HLE/FunctionWrappers.h +++ b/Core/HLE/FunctionWrappers.h @@ -34,6 +34,28 @@ template void WrapU64_U() { currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF; } +template void WrapU64_I() { + u64 retval = func(PARAM(0)); + currentMIPS->r[2] = retval & 0xFFFFFFFF; + currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF; +} + +template void WrapU64_UU64() { + u64 param_one = currentMIPS->r[6]; + param_one |= (u64)(currentMIPS->r[7]) << 32; + u64 retval = func(PARAM(0), param_one); + currentMIPS->r[2] = retval & 0xFFFFFFFF; + currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF; +} + +template void WrapU64_IU64() { + u64 param_one = currentMIPS->r[6]; + param_one |= (u64)(currentMIPS->r[7]) << 32; + u64 retval = func(PARAM(0), param_one); + currentMIPS->r[2] = retval & 0xFFFFFFFF; + currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF; +} + template void WrapI_IU64() { u64 param_one = currentMIPS->r[6]; param_one |= (u64)(currentMIPS->r[7]) << 32; @@ -76,6 +98,13 @@ template void WrapU_UU64UU() { RETURN(retval); } +template void WrapU_IU64UU() { + u64 param_one = currentMIPS->r[6]; + param_one |= (u64)(currentMIPS->r[7]) << 32; + u32 retval = func(PARAM(0), param_one, PARAM(4), PARAM(5)); + RETURN(retval); +} + template void WrapI_IICU64() { u64 param_three = currentMIPS->r[8]; param_three |= (u64)(currentMIPS->r[9]) << 32; diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index cde6f42f7d..27bcfda8ff 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -120,7 +120,7 @@ void __CtrlUpdateLatch() ctrlOldButtons = buttons; - ctrlBufs[ctrlBuf].frame = (u32) (CoreTiming::GetTicks() / CoreTiming::GetClockFrequencyMHz()); + ctrlBufs[ctrlBuf].frame = (u32) CoreTiming::GetGlobalTimeUs(); if (!analogEnabled) memset(ctrlBufs[ctrlBuf].analog, CTRL_ANALOG_CENTER, sizeof(ctrlBufs[ctrlBuf].analog)); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 6a5f9720be..8d0b52415d 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -25,6 +25,10 @@ #include "base/logging.h" #include "base/timeutil.h" +#ifndef _XBOX +#include "gfx_es2/gl_state.h" +#endif + #include "Common/Thread.h" #include "Core/CoreTiming.h" #include "Core/CoreParameter.h" @@ -179,7 +183,7 @@ void __DisplayInit() { } void __DisplayDoState(PointerWrap &p) { - auto s = p.Section("sceDisplay", 1); + auto s = p.Section("sceDisplay", 1, 2); if (!s) return; @@ -205,10 +209,17 @@ void __DisplayDoState(PointerWrap &p) { p.Do(leaveVblankEvent); CoreTiming::RestoreRegisterEvent(leaveVblankEvent, "LeaveVBlank", &hleLeaveVblank); p.Do(afterFlipEvent); - CoreTiming::RestoreRegisterEvent(afterFlipEvent, "AfterFlipEVent", &hleAfterFlip); + CoreTiming::RestoreRegisterEvent(afterFlipEvent, "AfterFlip", &hleAfterFlip); p.Do(gstate); p.Do(gstate_c); +#ifndef _XBOX + if (s < 2) { + // This shouldn't have been savestated anyway, but it was. + // It's unlikely to overlap with the first value in gpuStats. + p.ExpectVoid(&gl_extensions.gpuVendor, sizeof(gl_extensions.gpuVendor)); + } +#endif p.Do(gpuStats); gpu->DoState(p); diff --git a/Core/HLE/sceDmac.cpp b/Core/HLE/sceDmac.cpp index 5d9d8d3022..c5e82b72a3 100644 --- a/Core/HLE/sceDmac.cpp +++ b/Core/HLE/sceDmac.cpp @@ -71,6 +71,7 @@ u32 sceDmacMemcpy(u32 dst, u32 src, u32 size) { ERROR_LOG(HLE, "sceDmacMemcpy(dest=%08x, src=%08x, size=%i): illegal size", dst, src, size); return 0x80000023; } + if (dmacMemcpyDeadline > CoreTiming::GetTicks()) { WARN_LOG_REPORT(HLE, "sceDmacMemcpy(dest=%08x, src=%08x, size=%i): overlapping read", dst, src, size); // TODO: Should block, seems like copy doesn't start until previous finishes. diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index d77bf7aeab..e242f3e72d 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -58,6 +58,7 @@ #include "sceKernelEventFlag.h" #include "sceKernelVTimer.h" #include "sceKernelTime.h" +#include "sceMp3.h" #include "sceMpeg.h" #include "sceNet.h" #include "sceNetAdhoc.h" @@ -157,6 +158,7 @@ void __KernelShutdown() __NetAdhocShutdown(); __FontShutdown(); + __Mp3Shutdown(); __MpegShutdown(); __PsmfShutdown(); __PPGeShutdown(); @@ -231,6 +233,7 @@ void __KernelDoState(PointerWrap &p) __ImposeDoState(p); __IoDoState(p); __JpegDoState(p); + __Mp3DoState(p); __MpegDoState(p); __NetDoState(p); __NetAdhocDoState(p); @@ -833,20 +836,20 @@ const HLEFunction ThreadManForUser[] = {0xA8AA591F,WrapI_IU, "sceKernelCancelFpl"}, {0xD8199E4C,WrapI_IU, "sceKernelReferFplStatus"}, - {0x20fff560,WrapU_CU,"sceKernelCreateVTimer"}, - {0x328F9E52,WrapU_U,"sceKernelDeleteVTimer"}, - {0xc68d9437,WrapU_U,"sceKernelStartVTimer"}, - {0xD0AEEE87,WrapU_U,"sceKernelStopVTimer"}, - {0xD2D615EF,WrapU_U,"sceKernelCancelVTimerHandler"}, - {0xB3A59970,WrapU_UU,"sceKernelGetVTimerBase"}, - {0xB7C18B77,WrapU64_U,"sceKernelGetVTimerBaseWide"}, - {0x034A921F,WrapU_UU,"sceKernelGetVTimerTime"}, - {0xC0B3FFD2,WrapU64_U,"sceKernelGetVTimerTimeWide"}, - {0x5F32BEAA,WrapU_UU,"sceKernelReferVTimerStatus"}, - {0x542AD630,WrapU_UU,"sceKernelSetVTimerTime"}, - {0xFB6425C3,WrapU_UU64,"sceKernelSetVTimerTimeWide"}, - {0xd8b299ae,WrapU_UUUU,"sceKernelSetVTimerHandler"}, - {0x53B00E9A,WrapU_UU64UU,"sceKernelSetVTimerHandlerWide"}, + {0x20fff560,WrapU_CU, "sceKernelCreateVTimer", HLE_NOT_IN_INTERRUPT}, + {0x328F9E52,WrapU_I, "sceKernelDeleteVTimer", HLE_NOT_IN_INTERRUPT}, + {0xc68d9437,WrapU_I, "sceKernelStartVTimer"}, + {0xD0AEEE87,WrapU_I, "sceKernelStopVTimer"}, + {0xD2D615EF,WrapU_I, "sceKernelCancelVTimerHandler"}, + {0xB3A59970,WrapU_IU, "sceKernelGetVTimerBase"}, + {0xB7C18B77,WrapU64_I, "sceKernelGetVTimerBaseWide"}, + {0x034A921F,WrapU_IU, "sceKernelGetVTimerTime"}, + {0xC0B3FFD2,WrapU64_I, "sceKernelGetVTimerTimeWide"}, + {0x5F32BEAA,WrapU_IU, "sceKernelReferVTimerStatus"}, + {0x542AD630,WrapU_IU, "sceKernelSetVTimerTime"}, + {0xFB6425C3,WrapU64_IU64, "sceKernelSetVTimerTimeWide"}, + {0xd8b299ae,WrapU_IUUU, "sceKernelSetVTimerHandler"}, + {0x53B00E9A,WrapU_IU64UU, "sceKernelSetVTimerHandlerWide"}, // Names are just guesses, not correct. {0x8daff657,WrapI_CUUUUU, "sceKernelCreateTls"}, diff --git a/Core/HLE/sceKernelAlarm.cpp b/Core/HLE/sceKernelAlarm.cpp index 7c0ede0280..02c2b5ff86 100644 --- a/Core/HLE/sceKernelAlarm.cpp +++ b/Core/HLE/sceKernelAlarm.cpp @@ -150,7 +150,7 @@ KernelObject *__KernelAlarmObject() void __KernelScheduleAlarm(Alarm *alarm, u64 ticks) { - alarm->alm.schedule = (CoreTiming::GetTicks() + ticks) / (u64) CoreTiming::GetClockFrequencyMHz(); + alarm->alm.schedule = CoreTiming::GetGlobalTimeUs() + ticks / (u64) CoreTiming::GetClockFrequencyMHz(); CoreTiming::ScheduleEvent((int) ticks, alarmTimer, alarm->GetUID()); } diff --git a/Core/HLE/sceKernelTime.cpp b/Core/HLE/sceKernelTime.cpp index 28a95502d0..c57c3c3394 100644 --- a/Core/HLE/sceKernelTime.cpp +++ b/Core/HLE/sceKernelTime.cpp @@ -50,7 +50,7 @@ void __KernelTimeDoState(PointerWrap &p) int sceKernelGetSystemTime(u32 sysclockPtr) { - u64 t = CoreTiming::GetTicks() / CoreTiming::GetClockFrequencyMHz(); + u64 t = CoreTiming::GetGlobalTimeUs(); if (Memory::IsValidAddress(sysclockPtr)) Memory::Write_U64(t, sysclockPtr); DEBUG_LOG(SCEKERNEL, "sceKernelGetSystemTime(out:%16llx)", t); @@ -62,7 +62,7 @@ int sceKernelGetSystemTime(u32 sysclockPtr) u32 sceKernelGetSystemTimeLow() { // This clock should tick at 1 Mhz. - u64 t = CoreTiming::GetTicks() / CoreTiming::GetClockFrequencyMHz(); + u64 t = CoreTiming::GetGlobalTimeUs(); VERBOSE_LOG(SCEKERNEL,"%08x=sceKernelGetSystemTimeLow()",(u32)t); hleEatCycles(165); hleReSchedule("system time"); @@ -71,7 +71,7 @@ u32 sceKernelGetSystemTimeLow() u64 sceKernelGetSystemTimeWide() { - u64 t = CoreTiming::GetTicks() / CoreTiming::GetClockFrequencyMHz(); + u64 t = CoreTiming::GetGlobalTimeUs(); DEBUG_LOG(SCEKERNEL,"%i=sceKernelGetSystemTimeWide()",(u32)t); hleEatCycles(250); hleReSchedule("system time"); @@ -80,9 +80,9 @@ u64 sceKernelGetSystemTimeWide() int sceKernelUSec2SysClock(u32 usec, u32 clockPtr) { - DEBUG_LOG(SCEKERNEL,"sceKernelUSec2SysClock(%i, %08x )", usec, clockPtr); + DEBUG_LOG(SCEKERNEL,"sceKernelUSec2SysClock(%i, %08x)", usec, clockPtr); if (Memory::IsValidAddress(clockPtr)) - Memory::Write_U32((usec & 0xFFFFFFFFL), clockPtr); + Memory::Write_U64((usec & 0xFFFFFFFFL), clockPtr); hleEatCycles(165); return 0; } @@ -125,7 +125,7 @@ int sceKernelSysClock2USecWide(u32 lowClock, u32 highClock, u32 lowPtr, u32 high u32 sceKernelLibcClock() { - u32 retVal = (u32) (CoreTiming::GetTicks() / CoreTiming::GetClockFrequencyMHz()); + u32 retVal = (u32) CoreTiming::GetGlobalTimeUs(); DEBUG_LOG(SCEKERNEL, "%i = sceKernelLibcClock", retVal); hleEatCycles(330); hleReSchedule("libc clock"); @@ -134,7 +134,7 @@ u32 sceKernelLibcClock() u32 sceKernelLibcTime(u32 outPtr) { - u32 t = (u32) start_time + (u32) (CoreTiming::GetTicks() / CPU_HZ); + u32 t = (u32) start_time + (u32) (CoreTiming::GetGlobalTimeUs() / 1000000ULL); DEBUG_LOG(SCEKERNEL, "%i = sceKernelLibcTime(%08X)", t, outPtr); // The PSP sure takes its sweet time on this function. diff --git a/Core/HLE/sceKernelVTimer.cpp b/Core/HLE/sceKernelVTimer.cpp index 9438372270..b7db9152f1 100644 --- a/Core/HLE/sceKernelVTimer.cpp +++ b/Core/HLE/sceKernelVTimer.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include "Core/CoreTiming.h" #include "Core/Reporting.h" #include "sceKernel.h" @@ -25,6 +26,7 @@ #include "ChunkFile.h" static int vtimerTimer = -1; +static SceUID runningVTimer = 0; static std::list vtimers; struct NativeVTimer { @@ -46,16 +48,18 @@ struct VTimer : public KernelObject { int GetIDType() const { return SCE_KERNEL_TMID_VTimer; } virtual void DoState(PointerWrap &p) { - auto s = p.Section("VTimer", 1); + auto s = p.Section("VTimer", 1, 2); if (!s) return; p.Do(nvt); - p.Do(memoryPtr); + if (s < 2) { + u32 memoryPtr; + p.Do(memoryPtr); + } } NativeVTimer nvt; - u32 memoryPtr; }; KernelObject *__KernelVTimerObject() { @@ -66,24 +70,23 @@ u64 __getVTimerRunningTime(VTimer *vt) { if (vt->nvt.active == 0) return 0; - return cyclesToUs(CoreTiming::GetTicks()) - vt->nvt.base; + return CoreTiming::GetGlobalTimeUs() - vt->nvt.base; } u64 __getVTimerCurrentTime(VTimer* vt) { return vt->nvt.current + __getVTimerRunningTime(vt); } -void __cancelVTimer(SceUID id) { +int __KernelCancelVTimer(SceUID id) { u32 error; VTimer *vt = kernelObjects.Get(id, error); - if (error) - return; + if (!vt) + return error; CoreTiming::UnscheduleEvent(vtimerTimer, id); - vt->nvt.schedule = 0; vt->nvt.handlerAddr = 0; - vt->nvt.commonAddr = 0; + return 0; } void __KernelScheduleVTimer(VTimer *vt, u64 schedule) { @@ -91,25 +94,42 @@ void __KernelScheduleVTimer(VTimer *vt, u64 schedule) { vt->nvt.schedule = schedule; - if (vt->nvt.active == 1 && vt->nvt.handlerAddr != 0) - CoreTiming::ScheduleEvent(usToCycles((u64)vt->nvt.schedule), vtimerTimer, vt->GetUID()); + if (vt->nvt.active == 1 && vt->nvt.handlerAddr != 0) { + // The "real" base is base + current. But when setting the time, base is important. + // The schedule is relative to those. + u64 cyclesIntoFuture; + // It seems like the minimum is approximately 200us? + if (schedule < __getVTimerCurrentTime(vt)) + cyclesIntoFuture = usToCycles(200); + else { + u64 goalUs = vt->nvt.base + schedule - vt->nvt.current; + if (goalUs < CoreTiming::GetGlobalTimeUs()) + cyclesIntoFuture = usToCycles(200); + else + cyclesIntoFuture = usToCycles(goalUs - CoreTiming::GetGlobalTimeUs()); + } + + CoreTiming::ScheduleEvent(cyclesIntoFuture, vtimerTimer, vt->GetUID()); + } } -void __rescheduleVTimer(SceUID id, int delay) { +void __rescheduleVTimer(SceUID id, u32 delay) { u32 error; VTimer *vt = kernelObjects.Get(id, error); if (error) return; - if (delay < 0) + if (delay < 100) delay = 100; - __KernelScheduleVTimer(vt, delay); + __KernelScheduleVTimer(vt, vt->nvt.schedule + delay); } class VTimerIntrHandler : public IntrHandler { + static const int HANDLER_STACK_SPACE = 48; + public: VTimerIntrHandler() : IntrHandler(PSP_SYSTIMER1_INTR) {} @@ -122,31 +142,36 @@ public: if (error) return false; - if (vtimer->memoryPtr == 0) { - u32 size = 16; - vtimer->memoryPtr = kernelMemory.Alloc(size, true, "VTimer"); - } + // Reserve some stack space for arguments. + u32 argArea = currentMIPS->r[MIPS_REG_SP]; + currentMIPS->r[MIPS_REG_SP] -= HANDLER_STACK_SPACE; - Memory::Write_U64(vtimer->nvt.schedule, vtimer->memoryPtr); - Memory::Write_U64(__getVTimerCurrentTime(vtimer), vtimer->memoryPtr + 8); + Memory::Write_U64(vtimer->nvt.schedule, argArea - 16); + Memory::Write_U64(__getVTimerCurrentTime(vtimer), argArea - 8); currentMIPS->pc = vtimer->nvt.handlerAddr; currentMIPS->r[MIPS_REG_A0] = vtimer->GetUID(); - currentMIPS->r[MIPS_REG_A1] = vtimer->memoryPtr; - currentMIPS->r[MIPS_REG_A2] = vtimer->memoryPtr + 8; + currentMIPS->r[MIPS_REG_A1] = argArea - 16; + currentMIPS->r[MIPS_REG_A2] = argArea - 8; currentMIPS->r[MIPS_REG_A3] = vtimer->nvt.commonAddr; + runningVTimer = vtimerID; + return true; } virtual void handleResult(PendingInterrupt &pend) { - int result = currentMIPS->r[MIPS_REG_V0]; + u32 result = currentMIPS->r[MIPS_REG_V0]; + + currentMIPS->r[MIPS_REG_SP] += HANDLER_STACK_SPACE; int vtimerID = vtimers.front(); vtimers.pop_front(); + runningVTimer = 0; + if (result == 0) - __cancelVTimer(vtimerID); + __KernelCancelVTimer(vtimerID); else __rescheduleVTimer(vtimerID, result); } @@ -164,19 +189,27 @@ void __KernelTriggerVTimer(u64 userdata, int cyclesLate) { } void __KernelVTimerDoState(PointerWrap &p) { - auto s = p.Section("sceKernelVTimer", 1); + auto s = p.Section("sceKernelVTimer", 1, 2); if (!s) return; p.Do(vtimerTimer); p.Do(vtimers); CoreTiming::RestoreRegisterEvent(vtimerTimer, "VTimer", __KernelTriggerVTimer); + + if (s >= 2) + p.Do(runningVTimer); + else + runningVTimer = 0; } void __KernelVTimerInit() { vtimers.clear(); __RegisterIntrHandler(PSP_SYSTIMER1_INTR, new VTimerIntrHandler()); vtimerTimer = CoreTiming::RegisterEvent("VTimer", __KernelTriggerVTimer); + + // Intentionally starts at 0. This explains the behavior where 0 is treated differently outside a timer. + runningVTimer = 0; } u32 sceKernelCreateVTimer(const char *name, u32 optParamAddr) { @@ -193,7 +226,6 @@ u32 sceKernelCreateVTimer(const char *name, u32 optParamAddr) { vtimer->nvt.size = sizeof(NativeVTimer); strncpy(vtimer->nvt.name, name, KERNELOBJECT_MAX_NAME_LENGTH); vtimer->nvt.name[KERNELOBJECT_MAX_NAME_LENGTH] = '\0'; - vtimer->memoryPtr = 0; if (optParamAddr != 0) { u32 size = Memory::Read_U32(optParamAddr); @@ -204,7 +236,7 @@ u32 sceKernelCreateVTimer(const char *name, u32 optParamAddr) { return id; } -u32 sceKernelDeleteVTimer(u32 uid) { +u32 sceKernelDeleteVTimer(SceUID uid) { DEBUG_LOG(SCEKERNEL, "sceKernelDeleteVTimer(%08x)", uid); u32 error; @@ -222,13 +254,10 @@ u32 sceKernelDeleteVTimer(u32 uid) { } } - if (vt->memoryPtr != 0) - kernelMemory.Free(vt->memoryPtr); - return kernelObjects.Destroy(uid); } -u32 sceKernelGetVTimerBase(u32 uid, u32 baseClockAddr) { +u32 sceKernelGetVTimerBase(SceUID uid, u32 baseClockAddr) { DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerBase(%08x, %08x)", uid, baseClockAddr); u32 error; @@ -245,7 +274,7 @@ u32 sceKernelGetVTimerBase(u32 uid, u32 baseClockAddr) { return 0; } -u64 sceKernelGetVTimerBaseWide(u32 uid) { +u64 sceKernelGetVTimerBaseWide(SceUID uid) { DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerBaseWide(%08x)", uid); u32 error; @@ -253,13 +282,13 @@ u64 sceKernelGetVTimerBaseWide(u32 uid) { if (error) { WARN_LOG(SCEKERNEL, "%08x=sceKernelGetVTimerBaseWide(%08x)", error, uid); - return error; + return -1; } return vt->nvt.base; } -u32 sceKernelGetVTimerTime(u32 uid, u32 timeClockAddr) { +u32 sceKernelGetVTimerTime(SceUID uid, u32 timeClockAddr) { DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerTime(%08x, %08x)", uid, timeClockAddr); u32 error; @@ -277,7 +306,7 @@ u32 sceKernelGetVTimerTime(u32 uid, u32 timeClockAddr) { return 0; } -u64 sceKernelGetVTimerTimeWide(u32 uid) { +u64 sceKernelGetVTimerTimeWide(SceUID uid) { DEBUG_LOG(SCEKERNEL, "sceKernelGetVTimerTimeWide(%08x)", uid); u32 error; @@ -285,22 +314,24 @@ u64 sceKernelGetVTimerTimeWide(u32 uid) { if (error) { WARN_LOG(SCEKERNEL, "%08x=sceKernelGetVTimerTimeWide(%08x)", error, uid); - return error; + return -1; } u64 time = __getVTimerCurrentTime(vt); return time; } -u64 __setVTimer(VTimer *vt, u64 time) { +u64 __KernelSetVTimer(VTimer *vt, u64 time) { u64 current = __getVTimerCurrentTime(vt); - vt->nvt.base = vt->nvt.base + __getVTimerCurrentTime(vt) - time; - vt->nvt.current = 0; + vt->nvt.current = time - __getVTimerRunningTime(vt); + + // Run if we're now passed the schedule. + __KernelScheduleVTimer(vt, vt->nvt.schedule); return current; } -u32 sceKernelSetVTimerTime(u32 uid, u32 timeClockAddr) { +u32 sceKernelSetVTimerTime(SceUID uid, u32 timeClockAddr) { DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerTime(%08x, %08x)", uid, timeClockAddr); u32 error; @@ -313,38 +344,43 @@ u32 sceKernelSetVTimerTime(u32 uid, u32 timeClockAddr) { u64 time = Memory::Read_U64(timeClockAddr); if (Memory::IsValidAddress(timeClockAddr)) - Memory::Write_U64(__setVTimer(vt, time), timeClockAddr); + Memory::Write_U64(__KernelSetVTimer(vt, time), timeClockAddr); return 0; } -u32 sceKernelSetVTimerTimeWide(u32 uid, u64 timeClock) { - DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerTimeWide(%08x, %llu", uid, timeClock); +u64 sceKernelSetVTimerTimeWide(SceUID uid, u64 timeClock) { + if (__IsInInterrupt()) { + WARN_LOG(SCEKERNEL, "sceKernelSetVTimerTimeWide(%08x, %llu): in interrupt", uid, timeClock); + return -1; + } + DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerTimeWide(%08x, %llu)", uid, timeClock); u32 error; VTimer *vt = kernelObjects.Get(uid, error); - if (error) { + if (error || vt == NULL) { WARN_LOG(SCEKERNEL, "%08x=sceKernelSetVTimerTimeWide(%08x, %llu)", error, uid, timeClock); - return error; - } - - if (vt == NULL) { return -1; } - return __setVTimer(vt, timeClock); + return __KernelSetVTimer(vt, timeClock); } void __startVTimer(VTimer *vt) { vt->nvt.active = 1; - vt->nvt.base = cyclesToUs(CoreTiming::GetTicks()); + vt->nvt.base = CoreTiming::GetGlobalTimeUs(); if (vt->nvt.handlerAddr != 0) __KernelScheduleVTimer(vt, vt->nvt.schedule); } -u32 sceKernelStartVTimer(u32 uid) { +u32 sceKernelStartVTimer(SceUID uid) { + if (uid == runningVTimer) { + WARN_LOG(SCEKERNEL, "sceKernelStartVTimer(%08x): invalid vtimer", uid); + return SCE_KERNEL_ERROR_ILLEGAL_VTID; + } + DEBUG_LOG(SCEKERNEL, "sceKernelStartVTimer(%08x)", uid); u32 error; @@ -362,12 +398,17 @@ u32 sceKernelStartVTimer(u32 uid) { } void __stopVTimer(VTimer *vt) { - vt->nvt.current += __getVTimerCurrentTime(vt); + // This increases (__getVTimerCurrentTime includes nvt.current.) + vt->nvt.current = __getVTimerCurrentTime(vt); vt->nvt.active = 0; vt->nvt.base = 0; } -u32 sceKernelStopVTimer(u32 uid) { +u32 sceKernelStopVTimer(SceUID uid) { + if (uid == runningVTimer) { + WARN_LOG(SCEKERNEL, "sceKernelStopVTimer(%08x): invalid vtimer", uid); + return SCE_KERNEL_ERROR_ILLEGAL_VTID; + } DEBUG_LOG(SCEKERNEL, "sceKernelStopVTimer(%08x)", uid); u32 error; @@ -384,8 +425,8 @@ u32 sceKernelStopVTimer(u32 uid) { return error; } -u32 sceKernelSetVTimerHandler(u32 uid, u32 scheduleAddr, u32 handlerFuncAddr, u32 commonAddr) { - if (uid == 0) { +u32 sceKernelSetVTimerHandler(SceUID uid, u32 scheduleAddr, u32 handlerFuncAddr, u32 commonAddr) { + if (uid == runningVTimer) { WARN_LOG(SCEKERNEL, "sceKernelSetVTimerHandler(%08x, %08x, %08x, %08x): invalid vtimer", uid, scheduleAddr, handlerFuncAddr, commonAddr); return SCE_KERNEL_ERROR_ILLEGAL_VTID; } @@ -402,16 +443,18 @@ u32 sceKernelSetVTimerHandler(u32 uid, u32 scheduleAddr, u32 handlerFuncAddr, u3 u64 schedule = Memory::Read_U64(scheduleAddr); vt->nvt.handlerAddr = handlerFuncAddr; - if (handlerFuncAddr) + if (handlerFuncAddr) { vt->nvt.commonAddr = commonAddr; - - __KernelScheduleVTimer(vt, schedule); + __KernelScheduleVTimer(vt, schedule); + } else { + __KernelScheduleVTimer(vt, vt->nvt.schedule); + } return 0; } -u32 sceKernelSetVTimerHandlerWide(u32 uid, u64 schedule, u32 handlerFuncAddr, u32 commonAddr) { - if (uid == 0) { +u32 sceKernelSetVTimerHandlerWide(SceUID uid, u64 schedule, u32 handlerFuncAddr, u32 commonAddr) { + if (uid == runningVTimer) { WARN_LOG(SCEKERNEL, "sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x): invalid vtimer", uid, schedule, handlerFuncAddr, commonAddr); return SCE_KERNEL_ERROR_ILLEGAL_VTID; } @@ -427,24 +470,29 @@ u32 sceKernelSetVTimerHandlerWide(u32 uid, u64 schedule, u32 handlerFuncAddr, u3 DEBUG_LOG(SCEKERNEL, "sceKernelSetVTimerHandlerWide(%08x, %llu, %08x, %08x)", uid, schedule, handlerFuncAddr, commonAddr); vt->nvt.handlerAddr = handlerFuncAddr; - if (handlerFuncAddr) + if (handlerFuncAddr) { vt->nvt.commonAddr = commonAddr; - - __KernelScheduleVTimer(vt, schedule); + __KernelScheduleVTimer(vt, schedule); + } else { + __KernelScheduleVTimer(vt, vt->nvt.schedule); + } return 0; } -u32 sceKernelCancelVTimerHandler(u32 uid) { +u32 sceKernelCancelVTimerHandler(SceUID uid) { + if (uid == runningVTimer) { + WARN_LOG(SCEKERNEL, "sceKernelCancelVTimerHandler(%08x): invalid vtimer", uid); + return SCE_KERNEL_ERROR_ILLEGAL_VTID; + } + DEBUG_LOG(SCEKERNEL, "sceKernelCancelVTimerHandler(%08x)", uid); //__cancelVTimer checks if uid is valid - __cancelVTimer(uid); - - return 0; + return __KernelCancelVTimer(uid); } -u32 sceKernelReferVTimerStatus(u32 uid, u32 statusAddr) { +u32 sceKernelReferVTimerStatus(SceUID uid, u32 statusAddr) { DEBUG_LOG(SCEKERNEL, "sceKernelReferVTimerStatus(%08x, %08x)", uid, statusAddr); u32 error; @@ -455,8 +503,12 @@ u32 sceKernelReferVTimerStatus(u32 uid, u32 statusAddr) { return error; } - if (Memory::IsValidAddress(statusAddr)) - Memory::WriteStruct(statusAddr, &vt->nvt); + if (Memory::IsValidAddress(statusAddr)) { + NativeVTimer status = vt->nvt; + u32 size = Memory::Read_U32(statusAddr); + status.current = __getVTimerCurrentTime(vt); + Memory::Memcpy(statusAddr, &status, std::min(size, (u32)sizeof(status))); + } return 0; } diff --git a/Core/HLE/sceKernelVTimer.h b/Core/HLE/sceKernelVTimer.h index 9153af3c06..e31ff3f11d 100644 --- a/Core/HLE/sceKernelVTimer.h +++ b/Core/HLE/sceKernelVTimer.h @@ -18,21 +18,20 @@ #pragma once u32 sceKernelCreateVTimer(const char *name, u32 optParamAddr); -u32 sceKernelDeleteVTimer(u32 uid); -u32 sceKernelStartVTimer(u32 uid); -u32 sceKernelStopVTimer(u32 uid); -u32 sceKernelSetVTimerHandler(u32 uid, u32 scheduleAddr, u32 handlerFuncAddr, u32 commonAddr); -u32 sceKernelSetVTimerHandlerWide(u32 uid, u64 schedule, u32 handlerFuncAddr, u32 commonAddr); -u32 sceKernelCancelVTimerHandler(u32 uid); -u32 sceKernelReferVTimerStatus(u32 uid, u32 statusAddr); -u32 sceKernelGetVTimerBase(u32 uid, u32 baseClockAddr); //SceKernelSysClock -u64 sceKernelGetVTimerBaseWide(u32 uid); -u32 sceKernelGetVTimerTime(u32 uid, u32 timeClockAddr); -u64 sceKernelGetVTimerTimeWide(u32 uid); -u32 sceKernelSetVTimerTime(u32 uid, u32 timeClockAddr); -u32 sceKernelSetVTimerTimeWide(u32 uid, u64 timeClock); +u32 sceKernelDeleteVTimer(SceUID uid); +u32 sceKernelStartVTimer(SceUID uid); +u32 sceKernelStopVTimer(SceUID uid); +u32 sceKernelSetVTimerHandler(SceUID uid, u32 scheduleAddr, u32 handlerFuncAddr, u32 commonAddr); +u32 sceKernelSetVTimerHandlerWide(SceUID uid, u64 schedule, u32 handlerFuncAddr, u32 commonAddr); +u32 sceKernelCancelVTimerHandler(SceUID uid); +u32 sceKernelReferVTimerStatus(SceUID uid, u32 statusAddr); +u32 sceKernelGetVTimerBase(SceUID uid, u32 baseClockAddr); //SceKernelSysClock +u64 sceKernelGetVTimerBaseWide(SceUID uid); +u32 sceKernelGetVTimerTime(SceUID uid, u32 timeClockAddr); +u64 sceKernelGetVTimerTimeWide(SceUID uid); +u32 sceKernelSetVTimerTime(SceUID uid, u32 timeClockAddr); +u64 sceKernelSetVTimerTimeWide(SceUID uid, u64 timeClock); -// TODO void _sceKernelReturnFromTimerHandler(); void __KernelVTimerInit(); diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 87eba22da1..5c9e6ab696 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -22,7 +22,6 @@ #include "Core/HLE/sceMp3.h" #include "Core/HW/MediaEngine.h" #include "Core/Reporting.h" -#include "Core/HW/MediaEngine.h" #ifdef USE_FFMPEG #ifndef PRId64 @@ -40,11 +39,37 @@ extern "C" { static const int MP3_BITRATES[] = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}; +struct Mp3Context; +int __Mp3InitContext(Mp3Context *ctx); + struct Mp3Context { + Mp3Context() +#ifdef USE_FFMPEG + : avformat_context(NULL), avio_context(NULL), resampler_context(NULL) { +#else + { +#endif + } + + ~Mp3Context() { +#ifdef USE_FFMPEG + if (avio_context != NULL) { + av_free(avio_context->buffer); + av_free(avio_context); + } + if (avformat_context != NULL) { + avformat_free_context(avformat_context); + } + if (resampler_context != NULL) { + swr_free(&resampler_context); + } +#endif + } + void DoState(PointerWrap &p) { - auto s = p.Section("Mp3Context", 1, 2); - if (!s) - return; + auto s = p.Section("Mp3Context", 1); + if (!s) + return; p.Do(mp3StreamStart); p.Do(mp3StreamEnd); @@ -52,18 +77,20 @@ struct Mp3Context { p.Do(mp3BufSize); p.Do(mp3PcmBuf); p.Do(mp3PcmBufSize); + p.Do(readPosition); + p.Do(bufferRead); + p.Do(bufferWrite); + p.Do(bufferAvailable); p.Do(mp3DecodedBytes); - if (s >= 2) - p.Do(mp3SumDecodedSamples); - else - mp3SumDecodedSamples = 0; p.Do(mp3LoopNum); p.Do(mp3MaxSamples); - p.Do(mp3Bitrate); + p.Do(mp3SumDecodedSamples); p.Do(mp3Channels); + p.Do(mp3Bitrate); p.Do(mp3SamplingRate); p.Do(mp3Version); - p.DoClass(mediaengine); + + __Mp3InitContext(this); } int mp3StreamStart; @@ -83,7 +110,6 @@ struct Mp3Context { int mp3LoopNum; int mp3MaxSamples; int mp3SumDecodedSamples; - MediaEngine *mediaengine; int mp3Channels; int mp3Bitrate; @@ -99,19 +125,28 @@ struct Mp3Context { }; static std::map mp3Map; -static u32 lastMp3Handle = 0; Mp3Context *getMp3Ctx(u32 mp3) { - if (mp3Map.find(mp3) == mp3Map.end()) { - ERROR_LOG(ME, "Bad mp3 handle %08x - using last one (%08x) instead", mp3, lastMp3Handle); - mp3 = lastMp3Handle; - } - if (mp3Map.find(mp3) == mp3Map.end()) return NULL; return mp3Map[mp3]; } +void __Mp3Shutdown() { + for (auto it = mp3Map.begin(), end = mp3Map.end(); it != end; ++it) { + delete it->second; + } + mp3Map.clear(); +} + +void __Mp3DoState(PointerWrap &p) { + auto s = p.Section("sceMp3", 0, 1); + if (!s) + return; + + p.Do(mp3Map); +} + /* MP3 */ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { DEBUG_LOG(ME, "sceMp3Decode(%08x,%08x)", mp3, outPcmPtr); @@ -236,7 +271,7 @@ int sceMp3CheckStreamDataNeeded(u32 mp3) { return ctx->bufferAvailable != ctx->mp3BufSize && ctx->readPosition < ctx->mp3StreamEnd; } -int readFunc(void *opaque, uint8_t *buf, int buf_size) { +static int readFunc(void *opaque, uint8_t *buf, int buf_size) { Mp3Context *ctx = static_cast(opaque); int res = 0; @@ -300,11 +335,6 @@ u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { ctx->mp3Bitrate = 128; ctx->mp3SamplingRate = 44100; -#ifdef USE_FFMPEG - ctx->avformat_context = NULL; - ctx->avio_context = NULL; -#endif - mp3Map[mp3Addr] = ctx; return mp3Addr; } @@ -321,28 +351,10 @@ int sceMp3TermResource() { return 0; } -int sceMp3Init(u32 mp3) { - DEBUG_LOG(ME, "sceMp3Init(%08x)", mp3); - - Mp3Context *ctx = getMp3Ctx(mp3); - if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; - } - - // Read in the header and swap the endian - int header = Memory::Read_U32(ctx->mp3Buf); - header = (header >> 24) | - ((header<<8) & 0x00FF0000) | - ((header>>8) & 0x0000FF00) | - (header << 24); - - ctx->mp3Version = ((header >> 19) & 0x3); - +int __Mp3InitContext(Mp3Context *ctx) { #ifdef USE_FFMPEG InitFFmpeg(); - - u8* avio_buffer = static_cast(av_malloc(ctx->mp3BufSize)); + u8 *avio_buffer = static_cast(av_malloc(ctx->mp3BufSize)); ctx->avio_context = avio_alloc_context(avio_buffer, ctx->mp3BufSize, 0, ctx, readFunc, NULL, NULL); ctx->avformat_context = avformat_alloc_context(); ctx->avformat_context->pb = ctx->avio_context; @@ -384,12 +396,6 @@ int sceMp3Init(u32 mp3) { ctx->decoder_context->sample_rate, 0, NULL); - // Let's just grab this info from FFMPEG, it seems more reliable than the code above. - - ctx->mp3SamplingRate = ctx->decoder_context->sample_rate; - ctx->mp3Channels = ctx->decoder_context->channels; - ctx->mp3Bitrate = ctx->decoder_context->bit_rate / 1000; - if (!ctx->resampler_context) { ERROR_LOG(ME, "Could not allocate resampler context %d", ret); return -1; @@ -400,6 +406,39 @@ int sceMp3Init(u32 mp3) { return -1; } + return 0; +#endif +} + +int sceMp3Init(u32 mp3) { + DEBUG_LOG(ME, "sceMp3Init(%08x)", mp3); + + Mp3Context *ctx = getMp3Ctx(mp3); + if (!ctx) { + ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); + return -1; + } + + // Read in the header and swap the endian + int header = Memory::Read_U32(ctx->mp3Buf); + header = (header >> 24) | + ((header<<8) & 0x00FF0000) | + ((header>>8) & 0x0000FF00) | + (header << 24); + + ctx->mp3Version = ((header >> 19) & 0x3); + +#ifdef USE_FFMPEG + int ret = __Mp3InitContext(ctx); + if (ret != 0) + return ret; + + // Let's just grab this info from FFMPEG, it seems more reliable than the code above. + + ctx->mp3SamplingRate = ctx->decoder_context->sample_rate; + ctx->mp3Channels = ctx->decoder_context->channels; + ctx->mp3Bitrate = ctx->decoder_context->bit_rate / 1000; + av_dump_format(ctx->avformat_context, 0, "mp3", 0); #endif @@ -549,10 +588,6 @@ int sceMp3ReleaseMp3Handle(u32 mp3) { return -1; } -#ifdef USE_FFMPEG - av_free(ctx->avio_context->buffer); - av_free(ctx->avio_context); -#endif mp3Map.erase(mp3Map.find(mp3)); delete ctx; diff --git a/Core/HLE/sceMp3.h b/Core/HLE/sceMp3.h index f855286ecd..f4ca0b0b5e 100644 --- a/Core/HLE/sceMp3.h +++ b/Core/HLE/sceMp3.h @@ -19,4 +19,9 @@ #include "HLE.h" -void Register_sceMp3(); \ No newline at end of file +class PointerWrap; + +void Register_sceMp3(); + +void __Mp3Shutdown(); +void __Mp3DoState(PointerWrap &p); \ No newline at end of file diff --git a/Core/HLE/sceRtc.cpp b/Core/HLE/sceRtc.cpp index 940b45aad5..1703248bb2 100644 --- a/Core/HLE/sceRtc.cpp +++ b/Core/HLE/sceRtc.cpp @@ -55,7 +55,7 @@ const int PSP_TIME_INVALID_MICROSECONDS = -7; u64 __RtcGetCurrentTick() { // TODO: It's probably expecting ticks since January 1, 0001? - return cyclesToUs(CoreTiming::GetTicks()) + rtcBaseTicks; + return CoreTiming::GetGlobalTimeUs() + rtcBaseTicks; } #if defined(_WIN32) @@ -79,7 +79,7 @@ time_t rtc_timegm(struct tm *tm) return _mkgmtime(tm); } -#elif defined(__GLIBC__) && !defined(ANDROID) +#elif (defined(__GLIBC__) && !defined(ANDROID)) || defined(BLACKBERRY) || defined(__SYMBIAN32__) #define rtc_timegm timegm #else @@ -131,7 +131,7 @@ void __RtcDoState(PointerWrap &p) void __RtcTimeOfDay(PSPTimeval *tv) { - s64 additionalUs = cyclesToUs(CoreTiming::GetTicks()); + s64 additionalUs = CoreTiming::GetGlobalTimeUs(); *tv = rtcBaseTime; s64 adjustedUs = additionalUs + tv->tv_usec; @@ -409,7 +409,7 @@ int sceRtcConvertLocalTimeToUTC(u32 tickLocalPtr,u32 tickUTCPtr) { u64 srcTick = Memory::Read_U64(tickLocalPtr); // TODO : Let the user select his timezone / daylight saving instead of taking system param ? -#if defined(__GLIBC__) || defined(__SYMBIAN32__) +#if defined(__GLIBC__) || defined(BLACKBERRY) || defined(__SYMBIAN32__) time_t timezone = 0; tm *time = localtime(&timezone); srcTick -= time->tm_gmtoff*1000000ULL; @@ -432,7 +432,7 @@ int sceRtcConvertUtcToLocalTime(u32 tickUTCPtr,u32 tickLocalPtr) { u64 srcTick = Memory::Read_U64(tickUTCPtr); // TODO : Let the user select his timezone / daylight saving instead of taking system param ? -#if defined(__GLIBC__) || defined(__SYMBIAN32__) +#if defined(__GLIBC__) || defined(BLACKBERRY) || defined(__SYMBIAN32__) time_t timezone = 0; tm *time = localtime(&timezone); srcTick += time->tm_gmtoff*1000000ULL; diff --git a/Core/HLE/sceSas.cpp b/Core/HLE/sceSas.cpp index 96193da23b..f1bb204a85 100644 --- a/Core/HLE/sceSas.cpp +++ b/Core/HLE/sceSas.cpp @@ -451,7 +451,6 @@ u32 sceSasSetOutputMode(u32 core, u32 outputMode) { return 0; } - u32 sceSasGetAllEnvelopeHeights(u32 core, u32 heightsAddr) { DEBUG_LOG(SCESAS, "sceSasGetAllEnvelopeHeights(%08x, %i)", core, heightsAddr); @@ -460,8 +459,8 @@ u32 sceSasGetAllEnvelopeHeights(u32 core, u32 heightsAddr) { } for (int i = 0; i < PSP_SAS_VOICES_MAX; i++) { - int voiceHeight = sas->voices[i].envelope.GetHeight(); - Memory::Write_U32(voiceHeight, heightsAddr + i * 4); + int voiceHeight = sas->voices[i].envelope.GetHeight(); + Memory::Write_U32(voiceHeight, heightsAddr + i * 4); } return 0; diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 1cd9c77b77..902e8c06da 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -290,7 +290,7 @@ SasInstance::SasInstance() audioDump = fopen("D:\\audio.raw", "wb"); #endif memset(&waveformEffect, 0, sizeof(waveformEffect)); - waveformEffect.type = -1; + waveformEffect.type = PSP_SAS_EFFECT_TYPE_OFF; waveformEffect.isDryOn = 1; } @@ -338,134 +338,148 @@ static inline s16 clamp_s16(int i) { return i; } +void SasVoice::ReadSamples(s16 *output, int numSamples) { + // Read N samples into the resample buffer. Could do either PCM or VAG here. + switch (type) { + case VOICETYPE_VAG: + { + vag.GetSamples(output, numSamples); + if (vag.End()) { + // NOTICE_LOG(SAS, "Hit end of VAG audio"); + playing = false; + on = false; // ?? + } + } + break; + case VOICETYPE_PCM: + { + u32 size = std::min(pcmSize * 2 - pcmIndex, (int)(numSamples * sizeof(s16))); + if (!on) { + pcmIndex = 0; + break; + } + Memory::Memcpy(output, pcmAddr + pcmIndex, size); + int remaining = numSamples * sizeof(s16) - size; + if (remaining > 0) { + memset(output + size, 0, remaining); + } + pcmIndex += size; + if (pcmIndex >= pcmSize * 2) { + pcmIndex = 0; + } + } + break; + case VOICETYPE_ATRAC3: + { + int ret = atrac3.getNextSamples(output, numSamples); + if (ret) { + // Hit atrac3 voice end + playing = false; + on = false; // ?? + } + } + break; + default: + { + memset(output, 0, numSamples * sizeof(s16)); + } + break; + } +} + +void SasInstance::MixVoice(SasVoice &voice) { + switch (voice.type) { + case VOICETYPE_VAG: + if (voice.type == VOICETYPE_VAG && !voice.vagAddr) + break; + case VOICETYPE_PCM: + if (voice.type == VOICETYPE_PCM && !voice.pcmAddr) + break; + default: + // Load resample history (so we can use a wide filter) + resampleBuffer[0] = voice.resampleHist[0]; + resampleBuffer[1] = voice.resampleHist[1]; + + // Figure out number of samples to read. + // Actually this is not entirely correct - we need to get one extra sample, and store it + // for the next time around. A little complicated... + // But for now, see Smoothness HACKERY below :P + u32 numSamples = (voice.sampleFrac + grainSize * voice.pitch) / PSP_SAS_PITCH_BASE; + if ((int)numSamples > grainSize * 4) { + ERROR_LOG(SASMIX, "numSamples too large, clamping: %i vs %i", numSamples, grainSize * 4); + numSamples = grainSize * 4; + } + + voice.ReadSamples(resampleBuffer + 2, numSamples); + + // Smoothness HACKERY + resampleBuffer[2 + numSamples] = resampleBuffer[2 + numSamples - 1]; + + // Save resample history + voice.resampleHist[0] = resampleBuffer[2 + numSamples - 2]; + voice.resampleHist[1] = resampleBuffer[2 + numSamples - 1]; + + // Resample to the correct pitch, writing exactly "grainSize" samples. + // This is a HORRIBLE resampler by the way. + // TODO: Special case no-resample case (and 2x and 0.5x) for speed, it's not uncommon + + u32 sampleFrac = voice.sampleFrac; + const int MAX_CONFIG_VOLUME = 20; + int volumeShift = (MAX_CONFIG_VOLUME - g_Config.iSFXVolume); + if (volumeShift < 0) volumeShift = 0; + for (int i = 0; i < grainSize; i++) { + // For now: nearest neighbour, not even using the resample history at all. + int sample = resampleBuffer[sampleFrac / PSP_SAS_PITCH_BASE + 2]; + sampleFrac += voice.pitch; + + // The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1. + // Reduce it to 14 bits, by shifting off 15. Round up by adding (1 << 14) first. + int envelopeValue = voice.envelope.GetHeight(); + envelopeValue = (envelopeValue + (1 << 14)) >> 15; + + // We just scale by the envelope before we scale by volumes. + // Again, we round up by adding (1 << 14) first (*after* multiplying.) + sample = ((sample * envelopeValue) + (1 << 14)) >> 15; + + // We mix into this 32-bit temp buffer and clip in a second loop + // Ideally, the shift right should be there too but for now I'm concerned about + // not overflowing. + mixBuffer[i * 2] += (sample * voice.volumeLeft ) >> volumeShift; // Max = 16 and Min = 12(default) + mixBuffer[i * 2 + 1] += (sample * voice.volumeRight) >> volumeShift; // Max = 16 and Min = 12(default) + sendBuffer[i * 2] += sample * voice.volumeLeftSend >> 12; + sendBuffer[i * 2 + 1] += sample * voice.volumeRightSend >> 12; + voice.envelope.Step(); + } + + voice.sampleFrac = sampleFrac; + // Let's hope grainSize is a power of 2. + //voice.sampleFrac &= grainSize * PSP_SAS_PITCH_BASE - 1; + voice.sampleFrac -= numSamples * PSP_SAS_PITCH_BASE; + + if (voice.envelope.HasEnded()) + { + // NOTICE_LOG(SAS, "Hit end of envelope"); + voice.playing = false; + } + } +} + void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) { int voicesPlayingCount = 0; + for (int v = 0; v < PSP_SAS_VOICES_MAX; v++) { SasVoice &voice = voices[v]; if (!voice.playing || voice.paused) continue; voicesPlayingCount++; - - // TODO: Special case no-resample case for speed - - switch (voice.type) { - case VOICETYPE_VAG: - if (voice.type == VOICETYPE_VAG && !voice.vagAddr) - break; - case VOICETYPE_PCM: - if (voice.type == VOICETYPE_PCM && !voice.pcmAddr) - break; - default: - // Load resample history (so we can use a wide filter) - resampleBuffer[0] = voice.resampleHist[0]; - resampleBuffer[1] = voice.resampleHist[1]; - - // Figure out number of samples to read. - // Actually this is not entirely correct - we need to get one extra sample, and store it - // for the next time around. A little complicated... - // But for now, see Smoothness HACKERY below :P - u32 numSamples = (voice.sampleFrac + grainSize * voice.pitch) / PSP_SAS_PITCH_BASE; - if ((int)numSamples > grainSize * 4) { - ERROR_LOG(SASMIX, "numSamples too large, clamping: %i vs %i", numSamples, grainSize * 4); - numSamples = grainSize * 4; - } - - // Read N samples into the resample buffer. Could do either PCM or VAG here. - switch (voice.type) { - case VOICETYPE_VAG: - { - voice.vag.GetSamples(resampleBuffer + 2, numSamples); - if (voice.vag.End()) { - // NOTICE_LOG(SAS, "Hit end of VAG audio"); - voice.playing = false; - voice.on = false; // ?? - } - } - break; - case VOICETYPE_PCM: - { - u32 size = std::min(voice.pcmSize * 2 - voice.pcmIndex, (int)(numSamples * sizeof(s16))); - memset(resampleBuffer + 2, 0, numSamples * sizeof(s16)); - if (!voice.on) { - voice.pcmIndex = 0; - break; - } - Memory::Memcpy(resampleBuffer + 2, voice.pcmAddr + voice.pcmIndex, size); - voice.pcmIndex += size; - if (voice.pcmIndex >= voice.pcmSize * 2) { - voice.pcmIndex = 0; - } - } - break; - case VOICETYPE_ATRAC3: - { - int ret = voice.atrac3.getNextSamples(resampleBuffer + 2, numSamples); - if (ret) { - // Hit atrac3 voice end - voice.playing = false; - voice.on = false; // ?? - } - } - break; - default: - { - memset(resampleBuffer + 2, 0, numSamples * sizeof(s16)); - } - break; - } - // Smoothness HACKERY - resampleBuffer[2 + numSamples] = resampleBuffer[2 + numSamples - 1]; - - // Save resample history - voice.resampleHist[0] = resampleBuffer[2 + numSamples - 2]; - voice.resampleHist[1] = resampleBuffer[2 + numSamples - 1]; - - // Resample to the correct pitch, writing exactly "grainSize" samples. - u32 sampleFrac = voice.sampleFrac; - const int MAX_CONFIG_VOLUME = 20; - int volumeShift = (MAX_CONFIG_VOLUME - g_Config.iSFXVolume); - if (volumeShift < 0) volumeShift = 0; - for (int i = 0; i < grainSize; i++) { - // For now: nearest neighbour, not even using the resample history at all. - int sample = resampleBuffer[sampleFrac / PSP_SAS_PITCH_BASE + 2]; - sampleFrac += voice.pitch; - - // The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1. - // Reduce it to 14 bits, by shifting off 15. Round up by adding (1 << 14) first. - int envelopeValue = voice.envelope.GetHeight(); - envelopeValue = (envelopeValue + (1 << 14)) >> 15; - - // We just scale by the envelope before we scale by volumes. - // Again, we round up by adding (1 << 14) first (*after* multiplying.) - sample = ((sample * envelopeValue) + (1 << 14)) >> 15; - - // We mix into this 32-bit temp buffer and clip in a second loop - // Ideally, the shift right should be there too but for now I'm concerned about - // not overflowing. - mixBuffer[i * 2] += (sample * voice.volumeLeft ) >> volumeShift; // Max = 16 and Min = 12(default) - mixBuffer[i * 2 + 1] += (sample * voice.volumeRight) >> volumeShift; // Max = 16 and Min = 12(default) - sendBuffer[i * 2] += sample * voice.volumeLeftSend >> 12; - sendBuffer[i * 2 + 1] += sample * voice.volumeRightSend >> 12; - voice.envelope.Step(); - } - voice.sampleFrac = sampleFrac; - // Let's hope grainSize is a power of 2. - //voice.sampleFrac &= grainSize * PSP_SAS_PITCH_BASE - 1; - voice.sampleFrac -= numSamples * PSP_SAS_PITCH_BASE; - - if (voice.envelope.HasEnded()) - { - // NOTICE_LOG(SAS, "Hit end of envelope"); - voice.playing = false; - } - } + MixVoice(voice); } - //if (voicesPlayingCount) - // NOTICE_LOG(SAS, "Sas mixed %i voices", voicesPlayingCount); - // Okay, apply effects processing to the Send buffer alone here. - // Reverb, echo, what have you. - // TODO + // Okay, apply effects processing to the Send buffer. + //if (waveformEffect.type != PSP_SAS_EFFECT_TYPE_OFF) + // ApplyReverb(); + + // Then mix the send buffer in with the rest. // Alright, all voices mixed. Let's convert and clip, and at the same time, wipe mixBuffer for next time. Could also dither. s16 *outp = (s16 *)Memory::GetPointer(outAddr); @@ -500,6 +514,12 @@ void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) { #endif } +void SasInstance::ApplyReverb() { + // for (int i = 0; i < grainSize * 2; i += 2) { + // modify sendBuffer + // } +} + void SasInstance::DoState(PointerWrap &p) { auto s = p.Section("SasInstance", 1); if (!s) diff --git a/Core/HW/SasAudio.h b/Core/HW/SasAudio.h index 0463bc3abf..d4ff3af1c8 100644 --- a/Core/HW/SasAudio.h +++ b/Core/HW/SasAudio.h @@ -50,6 +50,17 @@ enum { PSP_SAS_ENVELOPE_HEIGHT_MAX = 0x40000000, PSP_SAS_ENVELOPE_FREQ_MAX = 0x7FFFFFFF, + + PSP_SAS_EFFECT_TYPE_OFF = -1, + PSP_SAS_EFFECT_TYPE_ROOM = 0, + PSP_SAS_EFFECT_TYPE_UNK1 = 1, + PSP_SAS_EFFECT_TYPE_UNK2 = 2, + PSP_SAS_EFFECT_TYPE_UNK3 = 3, + PSP_SAS_EFFECT_TYPE_HALL = 4, + PSP_SAS_EFFECT_TYPE_SPACE = 5, + PSP_SAS_EFFECT_TYPE_ECHO = 6, + PSP_SAS_EFFECT_TYPE_DELAY = 7, + PSP_SAS_EFFECT_TYPE_PIPE = 8, }; struct WaveformEffect @@ -204,6 +215,8 @@ struct SasVoice void DoState(PointerWrap &p); + void ReadSamples(s16 *output, int numSamples); + bool playing; bool paused; // a voice can be playing AND paused. In that case, it won't play. bool on; // key-on, key-off. @@ -225,6 +238,11 @@ struct SasVoice int volumeLeft; int volumeRight; + + // I am pretty sure that volumeLeftSend and effectLeft really are the same thing (and same for right of course). + // We currently have nothing that ever modifies volume*Send. + // One game that uses an effect (probably a reverb) is MHU. + int volumeLeftSend; // volume to "Send" (audio-lingo) to the effects processing engine, like reverb int volumeRightSend; int effectLeft; @@ -258,6 +276,10 @@ public: FILE *audioDump; void Mix(u32 outAddr, u32 inAddr = 0, int leftVol = 0, int rightVol = 0); + void MixVoice(SasVoice &voice); + + // Applies reverb to send buffer, according to waveformEffect. + void ApplyReverb(); void DoState(PointerWrap &p); diff --git a/Core/MIPS/x86/RegCacheFPU.cpp b/Core/MIPS/x86/RegCacheFPU.cpp index b8767479d7..35f0d8d6f5 100644 --- a/Core/MIPS/x86/RegCacheFPU.cpp +++ b/Core/MIPS/x86/RegCacheFPU.cpp @@ -23,7 +23,7 @@ u32 FPURegCache::tempValues[NUM_TEMPS]; -FPURegCache::FPURegCache() : mips(0), emit(0) { +FPURegCache::FPURegCache() : mips(0), initialReady(false), emit(0) { memset(regs, 0, sizeof(regs)); memset(xregs, 0, sizeof(xregs)); vregs = regs + 32; @@ -31,21 +31,31 @@ FPURegCache::FPURegCache() : mips(0), emit(0) { void FPURegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) { this->mips = mips; + + if (!initialReady) + SetupInitialRegs(); + + memcpy(xregs, xregsInitial, sizeof(xregs)); + memcpy(regs, regsInitial, sizeof(regs)); +} + +void FPURegCache::SetupInitialRegs() { for (int i = 0; i < NUM_X_FPREGS; i++) { - xregs[i].mipsReg = -1; - xregs[i].dirty = false; + xregsInitial[i].mipsReg = -1; + xregsInitial[i].dirty = false; } - memset(regs, 0, sizeof(regs)); + memset(regsInitial, 0, sizeof(regsInitial)); OpArg base = GetDefaultLocation(0); for (int i = 0; i < 32; i++) { - regs[i].location = base; + regsInitial[i].location = base; base.IncreaseOffset(sizeof(float)); } base = GetDefaultLocation(32); for (int i = 32; i < NUM_MIPS_FPRS; i++) { - regs[i].location = base; + regsInitial[i].location = base; base.IncreaseOffset(sizeof(float)); } + initialReady = true; } void FPURegCache::SpillLock(int p1, int p2, int p3, int p4) { diff --git a/Core/MIPS/x86/RegCacheFPU.h b/Core/MIPS/x86/RegCacheFPU.h index 5f7340f25d..90ebdaa0d9 100644 --- a/Core/MIPS/x86/RegCacheFPU.h +++ b/Core/MIPS/x86/RegCacheFPU.h @@ -155,11 +155,16 @@ public: X64Reg GetFreeXReg(); private: const int *GetAllocationOrder(int &count); + void SetupInitialRegs(); MIPSCachedFPReg regs[NUM_MIPS_FPRS]; X64CachedFPReg xregs[NUM_X_FPREGS]; MIPSCachedFPReg *vregs; + bool initialReady; + MIPSCachedFPReg regsInitial[NUM_MIPS_FPRS]; + X64CachedFPReg xregsInitial[NUM_X_FPREGS]; + // TEMP0, etc. are swapped in here if necessary (e.g. on x86.) static u32 tempValues[NUM_TEMPS]; diff --git a/Core/System.cpp b/Core/System.cpp index 79d7fc42c2..1adf7089ec 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -171,7 +171,6 @@ void CPU_Init() { Memory::Init(); mipsr4k.Reset(); - mipsr4k.pc = 0; host->AttemptLoadSymbolMap(); diff --git a/GPU/Common/PostShader.cpp b/GPU/Common/PostShader.cpp index dc4ef9264f..2f959b4908 100644 --- a/GPU/Common/PostShader.cpp +++ b/GPU/Common/PostShader.cpp @@ -83,6 +83,14 @@ void LoadPostShaderInfo(std::vector directories) { section.Get("Vertex", &temp, ""); info.vertexShaderFile = path + "/" + temp; section.Get("OutputResolution", &info.outputResolution, false); + +#ifdef USING_GLES2 + // Let's ignore shaders we can't support. TODO: Check for GLES 3.0 + bool requiresIntegerSupport; + section.Get("RequiresIntSupport", &requiresIntegerSupport, false); + if (requiresIntegerSupport) + continue; +#endif shaderInfo.erase(std::find(shaderInfo.begin(), shaderInfo.end(), info.name), shaderInfo.end()); shaderInfo.push_back(info); } diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index 94d76e8a9d..1e7c83024d 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -176,10 +176,10 @@ void ComputeFragmentShaderID(FragmentShaderID *id) { } id->d[0] |= (lmode & 1) << 7; - id->d[0] |= gstate.isAlphaTestEnabled() << 8; + id->d[0] |= enableAlphaTest << 8; if (enableAlphaTest) id->d[0] |= gstate.getAlphaTestFunction() << 9; - id->d[0] |= gstate.isColorTestEnabled() << 12; + id->d[0] |= enableColorTest << 12; if (enableColorTest) id->d[0] |= gstate.getColorTestFunction() << 13; // color test func id->d[0] |= (enableFog & 1) << 15; diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 63331c17fc..80b60f88ad 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1395,11 +1395,11 @@ void FramebufferManager::DestroyAllFBOs() { vfbs_.clear(); } -void FramebufferManager::UpdateFromMemory(u32 addr, int size) { +void FramebufferManager::UpdateFromMemory(u32 addr, int size, bool safe) { addr &= ~0x40000000; // TODO: Could go through all FBOs, but probably not important? // TODO: Could also check for inner changes, but video is most important. - if (addr == DisplayFramebufAddr() || addr == PrevDisplayFramebufAddr()) { + if (addr == DisplayFramebufAddr() || addr == PrevDisplayFramebufAddr() || safe) { // TODO: Deleting the FBO is a heavy hammer solution, so let's only do it if it'd help. if (!Memory::IsValidAddress(displayFramebufPtr_)) return; @@ -1416,6 +1416,8 @@ void FramebufferManager::UpdateFromMemory(u32 addr, int size) { // TODO: This without the fbo_unbind() above would be better than destroying the FBO. // However, it doesn't seem to work for Star Ocean, at least if (useBufferedRendering_ && vfb->fbo) { + DisableState(); + glstate.viewport.set(0, 0, vfb->renderWidth, vfb->renderHeight); fbo_bind_as_render_target(vfb->fbo); needUnbind = true; DrawPixels(Memory::GetPointer(addr | 0x04000000), vfb->format, vfb->fb_stride); diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index bdefcbba20..bf669f2c47 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -128,7 +128,7 @@ public: void DeviceLost(); void CopyDisplayToOutput(); void SetRenderFrameBuffer(); // Uses parameters computed from gstate - void UpdateFromMemory(u32 addr, int size); + void UpdateFromMemory(u32 addr, int size, bool safe); void SetLineWidth(); #ifdef USING_GLES2 diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index c26484475c..e5b53d18d1 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -1436,10 +1436,13 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { case GE_CMD_UNKNOWN_FC: case GE_CMD_UNKNOWN_FD: case GE_CMD_UNKNOWN_FE: - case GE_CMD_UNKNOWN_FF: if (data != 0) WARN_LOG_REPORT_ONCE(unknowncmd, G3D, "Unknown GE command : %08x ", op); break; + case GE_CMD_UNKNOWN_FF: + // This is hit in quite a few games, supposedly it is a no-op. + // Might be used for debugging or something? + break; default: GPUCommon::ExecuteOp(op, diff); @@ -1533,7 +1536,7 @@ void GLES_GPU::InvalidateCacheInternal(u32 addr, int size, GPUInvalidationType t textureCache_.InvalidateAll(type); if (type != GPU_INVALIDATE_ALL) - framebufferManager_.UpdateFromMemory(addr, size); + framebufferManager_.UpdateFromMemory(addr, size, type == GPU_INVALIDATE_SAFE); } void GLES_GPU::UpdateMemory(u32 dest, u32 src, int size) { diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 957fe2a855..57134d8b5f 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -226,6 +226,17 @@ void TransformDrawEngine::ApplyDrawState(int prim) { } } + // Some Android devices (especially Mali, it seems) composite badly if there's alpha in the backbuffer. + // So in non-buffered rendering, we will simply consider the dest alpha to be zero in blending equations. +#ifdef ANDROID + if (g_Config.iRenderingMode == 0) { + if (glBlendFuncA == GL_DST_ALPHA) glBlendFuncA = GL_ZERO; + if (glBlendFuncB == GL_DST_ALPHA) glBlendFuncB = GL_ZERO; + if (glBlendFuncA == GL_ONE_MINUS_DST_ALPHA) glBlendFuncA = GL_ONE; + if (glBlendFuncB == GL_ONE_MINUS_DST_ALPHA) glBlendFuncB = GL_ONE; + } +#endif + // At this point, through all paths above, glBlendFuncA and glBlendFuncB will be set right somehow. if (!gstate.isStencilTestEnabled() && gstate.isDepthWriteEnabled()) { // Fixes some Persona 2 issues, may be correct? (that is, don't change dest alpha at all if blending) diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index e9b4bbfb12..50e95030f4 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -223,7 +223,7 @@ void GenerateVertexShader(int prim, u32 vertType, char *buffer, bool useHWTransf } #endif } - if (doTexture && (!prescale || gstate.getUVGenMode() == GE_TEXMAP_ENVIRONMENT_MAP)) { + if (doTexture && (!prescale || gstate.getUVGenMode() == GE_TEXMAP_ENVIRONMENT_MAP || gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX)) { WRITE(p, "uniform vec4 u_uvscaleoffset;\n"); } for (int i = 0; i < 4; i++) { diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 016f26214a..57915863cd 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -206,26 +206,25 @@ u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, PSPPointer 0x01FFFFFF) { //numStacks = 0; //stack = NULL; - oldCompatibility = false; - } - - u64 currentTicks = CoreTiming::GetTicks(); - for (int i = 0; i < DisplayListMaxCount; ++i) { - if (dls[i].state != PSP_GE_DL_STATE_NONE && dls[i].state != PSP_GE_DL_STATE_COMPLETED) { - if (dls[i].pc == listpc && !oldCompatibility) { - ERROR_LOG(G3D, "sceGeListEnqueue: can't enqueue, list address %08X already used", listpc); - return 0x80000021; + for (int i = 0; i < DisplayListMaxCount; ++i) { + if (dls[i].state != PSP_GE_DL_STATE_NONE && dls[i].state != PSP_GE_DL_STATE_COMPLETED) { + if (dls[i].pc == listpc) { + ERROR_LOG(G3D, "sceGeListEnqueue: can't enqueue, list address %08X already used", listpc); + return 0x80000021; + } + //if(dls[i].stack == stack) { + // ERROR_LOG(G3D, "sceGeListEnqueue: can't enqueue, list stack %08X already used", context); + // return 0x80000021; + //} } - //if(dls[i].stack == stack) { - // ERROR_LOG(G3D, "sceGeListEnqueue: can't enqueue, list stack %08X already used", context); - // return 0x80000021; - //} } } + for (int i = 0; i < DisplayListMaxCount; ++i) { int possibleID = (i + nextListID) % DisplayListMaxCount; auto possibleList = dls[possibleID]; diff --git a/Qt/PPSSPP.pro b/Qt/PPSSPP.pro index 35a14c3f1a..96a3918dfd 100755 --- a/Qt/PPSSPP.pro +++ b/Qt/PPSSPP.pro @@ -20,7 +20,7 @@ win32|greaterThan(QT_MAJOR_VERSION,4) { greaterThan(QT_MAJOR_VERSION,4): QT += widgets mobile_platform: MOBILITY += sensors -symbian: MOBILITY += systeminfo +symbian: MOBILITY += systeminfo feedback # PPSSPP Libs win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/release/ @@ -53,13 +53,17 @@ linux { } } qnx: LIBS += -lscreen -symbian: LIBS += -llibglib -lhwrmvibraclient +symbian: LIBS += -llibglib -lRemConCoreApi -lRemConInterfaceBase # Avoids problems with some compilers unix:!symbian: LIBS += -lz # Main SOURCES += ../native/base/QtMain.cpp HEADERS += ../native/base/QtMain.h +symbian { + SOURCES += ../native/base/SymbianMediaKeys.cpp + HEADERS += ../native/base/SymbianMediaKeys.h +} # UI SOURCES += ../UI/*Screen.cpp \ diff --git a/Qt/QtHost.cpp b/Qt/QtHost.cpp index bc03fb1ee2..5146ec6584 100644 --- a/Qt/QtHost.cpp +++ b/Qt/QtHost.cpp @@ -432,8 +432,7 @@ void NativeInitGraphics() void NativeRender() { - EnableFZ(); - // Clearing the screen at the start of the frame is an optimization for tiled mobile GPUs, as it then doesn't need to keep it around between frames. + // Clearing the screen at the start of the frame is an optimization for tiled mobile GPUs, as it then doesn't need to keep it around between frames. glClearColor(0,0,0,1); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); diff --git a/README.md b/README.md index 7353d6900c..f5fbee62db 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,11 @@ What's new in 0.9.5 * Android-x86 support * Unofficial port for modified Xbox 360 consoles * Atrac3+ plugin no longer required. Symbian now supports Atrac3+ audio. +* Symbian audio and ffmpeg is now threaded for more consistent media processing. * Haptic feedback support for mobile devices. * Accurate system information for mobile devices. * Qt audio has been fixed. +* Analog controller support for Blackberry. Basic build instructions diff --git a/UI/CwCheatScreen.cpp b/UI/CwCheatScreen.cpp index ea821d0c34..b1f8a9a5fb 100644 --- a/UI/CwCheatScreen.cpp +++ b/UI/CwCheatScreen.cpp @@ -71,10 +71,10 @@ void CwCheatScreen::CreateViews() { LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(400, FILL_PARENT)); leftColumn->Add(new ItemHeader(k->T("Options"))); - leftColumn->Add(new Choice(d->T("Back")))->OnClick.Handle(this, &CwCheatScreen::OnBack); - //leftColumn->Add(new Choice(k->T("Add Cheat")))->OnClick.Handle(this, &CwCheatScreen::OnAddCheat); - leftColumn->Add(new Choice(k->T("Import Cheats")))->OnClick.Handle(this, &CwCheatScreen::OnImportCheat); - leftColumn->Add(new Choice(k->T("Enable/Disable All")))->OnClick.Handle(this, &CwCheatScreen::OnEnableAll); + leftColumn->Add(new Choice(d->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); + //leftColumn->Add(new Choice(k->T("Add Cheat")))->OnClick.Handle(this, &CwCheatScreen::OnAddCheat); + leftColumn->Add(new Choice(k->T("Import Cheats")))->OnClick.Handle(this, &CwCheatScreen::OnImportCheat); + leftColumn->Add(new Choice(k->T("Enable/Disable All")))->OnClick.Handle(this, &CwCheatScreen::OnEnableAll); ScrollView *rightScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(0.5f)); rightScroll->SetScrollToTop(false); @@ -91,8 +91,7 @@ void CwCheatScreen::CreateViews() { } } -UI::EventReturn CwCheatScreen::OnBack(UI::EventParams ¶ms) { - screenManager()->finishDialog(this, DR_OK); +void CwCheatScreen::onFinish(DialogResult result) { os.open(activeCheatFile.c_str()); for (int j = 0; j < (int)cheatList.size(); j++) { os << cheatList[j]; @@ -103,10 +102,8 @@ UI::EventReturn CwCheatScreen::OnBack(UI::EventParams ¶ms) { os.close(); g_Config.bReloadCheats = true; if (MIPSComp::jit) { - auto blocks = MIPSComp::jit->GetBlockCache(); - blocks->Clear(); + MIPSComp::jit->ClearCache(); } - return UI::EVENT_DONE; } UI::EventReturn CwCheatScreen::OnEnableAll(UI::EventParams ¶ms) { diff --git a/UI/CwCheatScreen.h b/UI/CwCheatScreen.h index 69829b950d..783bbf7549 100644 --- a/UI/CwCheatScreen.h +++ b/UI/CwCheatScreen.h @@ -41,6 +41,7 @@ public: UI::EventReturn OnImportCheat(UI::EventParams ¶ms); UI::EventReturn OnEnableAll(UI::EventParams ¶ms); + virtual void onFinish(DialogResult result); protected: virtual void CreateViews(); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index bf4366fc5f..eb77207076 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -132,9 +132,8 @@ EmuScreen::~EmuScreen() { } void EmuScreen::dialogFinished(const Screen *dialog, DialogResult result) { - // TODO: improve the way with which we got commands from PauseMenu. - // DR_CANCEL means clicked on "continue", DR_OK means clicked on "back to menu", + // DR_CANCEL/DR_BACK means clicked on "continue", DR_OK means clicked on "back to menu", // DR_YES means a message sent to PauseMenu by NativeMessageReceived. if (result == DR_OK) { screenManager()->switchScreen(new MainScreen()); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index a15e19caf0..2574f441ed 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -84,7 +84,7 @@ void GameSettingsScreen::CreateViews() { ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0f)); root_->Add(leftColumn); - root_->Add(new Choice(d->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &GameSettingsScreen::OnBack); + root_->Add(new Choice(d->T("Back"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle(this, &UIScreen::OnBack); TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 200, new AnchorLayoutParams(10, 0, 10, 0, false)); @@ -371,15 +371,7 @@ void GameSettingsScreen::sendMessage(const char *message, const char *value) { } } -UI::EventReturn GameSettingsScreen::OnBack(UI::EventParams &e) { - // If we're in-game, return to the game via DR_CANCEL. - if (PSP_IsInited()) { - screenManager()->finishDialog(this, DR_CANCEL); - host->UpdateScreen(); - } else { - screenManager()->finishDialog(this, DR_OK); - } - +void GameSettingsScreen::onFinish(DialogResult result) { if (g_Config.bEnableSound) { if (PSP_IsInited() && !IsAudioInitialised()) Audio_Init(); @@ -391,6 +383,16 @@ UI::EventReturn GameSettingsScreen::OnBack(UI::EventParams &e) { host->UpdateUI(); KeyMap::UpdateConfirmCancelKeys(); +} + +UI::EventReturn GameSettingsScreen::OnBack(UI::EventParams &e) { + // If we're in-game, return to the game via DR_CANCEL. + if (PSP_IsInited()) { + screenManager()->finishDialog(this, DR_CANCEL); + host->UpdateScreen(); + } else { + screenManager()->finishDialog(this, DR_OK); + } return UI::EVENT_DONE; } @@ -508,21 +510,16 @@ void DeveloperToolsScreen::CreateViews() { list->Add(new ItemHeader(de->T("Language"))); list->Add(new Choice(de->T("Load language ini")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLoadLanguageIni); list->Add(new Choice(de->T("Save language ini")))->OnClick.Handle(this, &DeveloperToolsScreen::OnSaveLanguageIni); - list->Add(new Choice(d->T("Back")))->OnClick.Handle(this, &DeveloperToolsScreen::OnBack); + list->Add(new Choice(d->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); } -UI::EventReturn DeveloperToolsScreen::OnBack(UI::EventParams &e) { - screenManager()->finishDialog(this, DR_OK); - +void DeveloperToolsScreen::onFinish(DialogResult result) { g_Config.Save(); - - return UI::EVENT_DONE; } void GameSettingsScreen::CallbackRestoreDefaults(bool yes) { - if(yes) + if (yes) g_Config.RestoreDefaults(); - host->UpdateUI(); } diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 4adf49c050..e4981e297c 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -28,6 +28,7 @@ public: : gamePath_(gamePath), gameID_(gameID), iAlternateSpeedPercent_(3), enableReports_(false) {} virtual void update(InputState &input); + virtual void onFinish(DialogResult result); UI::Event OnLanguageChanged; UI::Event OnRecentChanged; @@ -91,6 +92,7 @@ private: class DeveloperToolsScreen : public UIDialogScreenWithBackground { public: DeveloperToolsScreen() {} + virtual void onFinish(DialogResult result); protected: virtual void CreateViews(); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index a69f93a119..7da60cbdad 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -704,14 +704,6 @@ void GamePauseScreen::update(InputState &input) { UIScreen::update(input); } -void GamePauseScreen::key(const KeyInput &key) { - if ((key.flags & KEY_DOWN) && UI::IsEscapeKeyCode(key.keyCode)) { - screenManager()->finishDialog(this, DR_CANCEL); - } else { - UIScreen::key(key); - } -} - void DrawBackground(float alpha); void GamePauseScreen::DrawBackground(UIContext &dc) { diff --git a/UI/MainScreen.h b/UI/MainScreen.h index 6e28b3d175..96590a0e0d 100644 --- a/UI/MainScreen.h +++ b/UI/MainScreen.h @@ -56,7 +56,6 @@ class GamePauseScreen : public UIScreen { public: GamePauseScreen(const std::string &filename) : UIScreen(), gamePath_(filename), saveSlots_(NULL) {} ~GamePauseScreen(); - virtual void key(const KeyInput &key); protected: virtual void DrawBackground(UIContext &dc); @@ -81,4 +80,4 @@ private: UI::ChoiceStrip *saveSlots_; UI::Choice *saveStateButton_; UI::Choice *loadStateButton_; -}; \ No newline at end of file +}; diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 222bf51a0d..4ac617d46d 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -569,8 +569,6 @@ void TakeScreenshot() { } void NativeRender() { - EnableFZ(); - glstate.depthWrite.set(GL_TRUE); glstate.colorMask.set(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); diff --git a/UI/TouchControlLayoutScreen.cpp b/UI/TouchControlLayoutScreen.cpp index 7707fc58a9..1a0d839986 100644 --- a/UI/TouchControlLayoutScreen.cpp +++ b/UI/TouchControlLayoutScreen.cpp @@ -225,9 +225,11 @@ void TouchControlLayoutScreen::touch(const TouchInput &touch) { } }; -UI::EventReturn TouchControlLayoutScreen::OnBack(UI::EventParams &e) { - g_Config.Save(); + +UI::EventReturn TouchControlLayoutScreen::OnBack(UI::EventParams &e) { + + // Hm, wtf? if (PSP_IsInited()) { screenManager()->finishDialog(this, DR_CANCEL); } else { @@ -237,6 +239,10 @@ UI::EventReturn TouchControlLayoutScreen::OnBack(UI::EventParams &e) { return UI::EVENT_DONE; }; +void TouchControlLayoutScreen::onFinish(DialogResult reason) { + g_Config.Save(); +} + UI::EventReturn TouchControlLayoutScreen::OnVisibility(UI::EventParams &e) { screenManager()->push(new TouchControlVisibilityScreen()); return UI::EVENT_DONE; diff --git a/UI/TouchControlLayoutScreen.h b/UI/TouchControlLayoutScreen.h index a35623635e..8db09235b7 100644 --- a/UI/TouchControlLayoutScreen.h +++ b/UI/TouchControlLayoutScreen.h @@ -24,13 +24,14 @@ class DragDropButton; -class TouchControlLayoutScreen : public UIDialogScreenWithBackground{ +class TouchControlLayoutScreen : public UIDialogScreenWithBackground { public: TouchControlLayoutScreen(); virtual void CreateViews(); virtual void touch(const TouchInput &touch); virtual void dialogFinished(const Screen *dialog, DialogResult result); + virtual void onFinish(DialogResult reason); protected: virtual UI::EventReturn OnBack(UI::EventParams &e); diff --git a/UI/TouchControlVisibilityScreen.cpp b/UI/TouchControlVisibilityScreen.cpp index 6b4b364f82..949b41cb10 100644 --- a/UI/TouchControlVisibilityScreen.cpp +++ b/UI/TouchControlVisibilityScreen.cpp @@ -29,7 +29,7 @@ void TouchControlVisibilityScreen::CreateViews() { LinearLayout *topBar = new LinearLayout(ORIENT_HORIZONTAL); I18NCategory *di = GetI18NCategory("Dialog"); - topBar->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &TouchControlVisibilityScreen::OnBack); + topBar->Add(new Choice(di->T("Back")))->OnClick.Handle(this, &UIScreen::OnBack); topBar->Add(new Choice(di->T("Toggle All")))->OnClick.Handle(this, &TouchControlVisibilityScreen::OnToggleAll); vert->Add(topBar); @@ -94,12 +94,8 @@ void TouchControlVisibilityScreen::CreateViews() { } } -UI::EventReturn TouchControlVisibilityScreen::OnBack(UI::EventParams &e) { - screenManager()->finishDialog(this, DR_OK); - +void TouchControlVisibilityScreen::onFinish(DialogResult result) { g_Config.Save(); - - return UI::EVENT_DONE; } UI::EventReturn TouchControlVisibilityScreen::OnToggleAll(UI::EventParams &e) { diff --git a/UI/TouchControlVisibilityScreen.h b/UI/TouchControlVisibilityScreen.h index d5732e1e23..85b4a6f124 100644 --- a/UI/TouchControlVisibilityScreen.h +++ b/UI/TouchControlVisibilityScreen.h @@ -25,11 +25,12 @@ namespace UI { class CheckBox; } -class TouchControlVisibilityScreen : public UIScreenWithBackground { +class TouchControlVisibilityScreen : public UIDialogScreenWithBackground { public: TouchControlVisibilityScreen() { } virtual void CreateViews(); + virtual void onFinish(DialogResult result); protected: virtual UI::EventReturn OnToggleAll(UI::EventParams &e); diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 1fc1dd7fd6..7a80b84279 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -127,7 +127,7 @@ false - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avdevice.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;xinput.lib;d3d9.lib;%(AdditionalDependencies) + Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;%(AdditionalDependencies) true Windows MachineX86 @@ -160,7 +160,7 @@ false - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avdevice.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;%(AdditionalDependencies) + Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;%(AdditionalDependencies) true $(OutDir)$(ProjectName).pdb Windows @@ -194,7 +194,7 @@ false - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avdevice.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;%(AdditionalDependencies) + Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) true @@ -235,7 +235,7 @@ false - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avdevice.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;%(AdditionalDependencies) + Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) true Windows diff --git a/Windows/W32Util/TabControl.h b/Windows/W32Util/TabControl.h index 489bb55ae9..6dfda63f68 100644 --- a/Windows/W32Util/TabControl.h +++ b/Windows/W32Util/TabControl.h @@ -16,11 +16,16 @@ public: void ShowTab(HWND pageHandle); void NextTab(bool cycle); void PreviousTab(bool cycle); - int CurrentTabIndex() { return currentTab; }; - HWND CurrentTabHandle() { return tabs[currentTab].pageHandle; }; + int CurrentTabIndex() { return currentTab; } + HWND CurrentTabHandle() { + if (currentTab < 0 || currentTab >= (int)tabs.size()) { + return NULL; + } + return tabs[currentTab].pageHandle; + } void SetShowTabTitles(bool enabled); - void SetIgnoreBottomMargin(bool enabled) { ignoreBottomMargin = enabled; }; - bool GetShowTabTitles() { return showTabTitles; }; + void SetIgnoreBottomMargin(bool enabled) { ignoreBottomMargin = enabled; } + bool GetShowTabTitles() { return showTabTitles; } void SetMinTabWidth(int w); private: diff --git a/assets/shaders/defaultshaders.ini b/assets/shaders/defaultshaders.ini index 68a3f2c941..1b518b2a8b 100644 --- a/assets/shaders/defaultshaders.ini +++ b/assets/shaders/defaultshaders.ini @@ -45,3 +45,4 @@ Name=Spline36 Upscaler Fragment=upscale_spline36.fsh Vertex=upscale_spline36.vsh OutputResolution=True +RequiresIntSupport=True \ No newline at end of file diff --git a/ffmpeg b/ffmpeg index 338c515b93..657ba596a6 160000 --- a/ffmpeg +++ b/ffmpeg @@ -1 +1 @@ -Subproject commit 338c515b93735f48c7696259cc7fb3bb04403571 +Subproject commit 657ba596a6f0592ace6164445d163d7f67e5df25 diff --git a/headless/Headless.cpp b/headless/Headless.cpp index f5223b0fd7..ab15b27a40 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -326,6 +326,10 @@ int main(int argc, const char* argv[]) g_Config.iInternalResolution = 1; g_Config.bFrameSkipUnthrottle = false; +#ifdef _WIN32 + InitSysDirectories(); +#endif + #if defined(ANDROID) #elif defined(BLACKBERRY) || defined(__SYMBIAN32__) #elif !defined(_WIN32) diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index b0127fc1b8..a198857a0f 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -100,7 +100,7 @@ Console true - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avdevice.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;xinput.lib;d3d9.lib;d3dx9d.lib;%(AdditionalDependencies) + Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;xinput.lib;d3d9.lib;d3dx9d.lib;%(AdditionalDependencies) 0x00400000 false true @@ -123,7 +123,7 @@ Console true - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avdevice.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9d.lib;%(AdditionalDependencies) + Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9d.lib;%(AdditionalDependencies) 0x00400000 false true @@ -150,7 +150,7 @@ true true true - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avdevice.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;%(AdditionalDependencies) + Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;%(AdditionalDependencies) 0x00400000 false true @@ -178,7 +178,7 @@ true true true - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avdevice.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;%(AdditionalDependencies) + Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;%(AdditionalDependencies) 0x00400000 false true diff --git a/native b/native index 4c8157694a..55810809b0 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit 4c8157694a60e4922818431d73da9a3f983b343e +Subproject commit 55810809b0a0d4153bdeafd41c737ca48f2f6926 diff --git a/pspautotests b/pspautotests index 268575b633..bf68a5fefc 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 268575b6331558e0a2f09a5c640cbb10777b5ec9 +Subproject commit bf68a5fefcd951b5c62d69f41474650586bafcf9 diff --git a/test.py b/test.py index 2003c2a0bf..9ef9c7fd41 100755 --- a/test.py +++ b/test.py @@ -178,6 +178,7 @@ tests_good = [ "threads/threads/release", "threads/threads/rotate", "threads/threads/stackfree", + "threads/threads/start", "threads/threads/suspend", "threads/threads/threadend", "threads/threads/threads", @@ -190,6 +191,17 @@ tests_good = [ "threads/vpl/refer", "threads/vpl/try", "threads/vpl/vpl", + "threads/vtimers/vtimer", + "threads/vtimers/cancelhandler", + "threads/vtimers/create", + "threads/vtimers/delete", + "threads/vtimers/getbase", + "threads/vtimers/gettime", + "threads/vtimers/interrupt", + "threads/vtimers/refer", + "threads/vtimers/settime", + "threads/vtimers/start", + "threads/vtimers/stop", "utility/savedata/autosave", "utility/savedata/filelist", "utility/savedata/makedata", @@ -218,9 +230,8 @@ tests_next = [ "threads/scheduling/dispatch", "threads/scheduling/scheduling", "threads/threads/create", - "threads/threads/start", "threads/threads/terminate", - "threads/vtimers/vtimer", + "threads/vtimers/sethandler", "threads/vpl/create", "utility/savedata/getsize", "utility/savedata/idlist",