merged upstream changes

This commit is contained in:
Siddharth 2013-10-27 11:48:59 +05:30
commit b50f4d4287
60 changed files with 775 additions and 424 deletions

View File

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

View File

@ -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)

View File

@ -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 <windows.h>
#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 <algorithm>
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);

View File

@ -24,6 +24,10 @@
#include <pthread_np.h>
#endif
#ifdef BLACKBERRY
#include <sys/neutrino.h>
#endif
#ifdef USE_BEGINTHREADEX
#include <process.h>
#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);
}

View File

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

View File

@ -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", &currentDirectory, "");
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);

View File

@ -130,7 +130,7 @@
<Message>Updating git-version.cpp</Message>
</PreBuildEvent>
<Lib>
<AdditionalDependencies>..\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>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -587,4 +587,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -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<std::recursive_mutex> 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

View File

@ -79,6 +79,7 @@ namespace CoreTiming
u64 GetTicks();
u64 GetIdleTicks();
u64 GetGlobalTimeUs();
// Returns the event_type identifier.
int RegisterEvent(const char *name, TimedCallback callback);

View File

@ -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)

View File

@ -34,6 +34,28 @@ template<u64 func(u32)> void WrapU64_U() {
currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF;
}
template<u64 func(int)> void WrapU64_I() {
u64 retval = func(PARAM(0));
currentMIPS->r[2] = retval & 0xFFFFFFFF;
currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF;
}
template<u64 func(u32, u64)> 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<u64 func(int, u64)> 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<int func(int, u64)> void WrapI_IU64() {
u64 param_one = currentMIPS->r[6];
param_one |= (u64)(currentMIPS->r[7]) << 32;
@ -76,6 +98,13 @@ template<u32 func(u32, u64, u32, u32)> void WrapU_UU64UU() {
RETURN(retval);
}
template<u32 func(int, u64, u32, u32)> 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<int func(int, int, const char*, u64)> void WrapI_IICU64() {
u64 param_three = currentMIPS->r[8];
param_three |= (u64)(currentMIPS->r[9]) << 32;

View File

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

View File

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

View File

@ -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.

View File

@ -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>, "sceKernelCancelFpl"},
{0xD8199E4C,WrapI_IU<sceKernelReferFplStatus>, "sceKernelReferFplStatus"},
{0x20fff560,WrapU_CU<sceKernelCreateVTimer>,"sceKernelCreateVTimer"},
{0x328F9E52,WrapU_U<sceKernelDeleteVTimer>,"sceKernelDeleteVTimer"},
{0xc68d9437,WrapU_U<sceKernelStartVTimer>,"sceKernelStartVTimer"},
{0xD0AEEE87,WrapU_U<sceKernelStopVTimer>,"sceKernelStopVTimer"},
{0xD2D615EF,WrapU_U<sceKernelCancelVTimerHandler>,"sceKernelCancelVTimerHandler"},
{0xB3A59970,WrapU_UU<sceKernelGetVTimerBase>,"sceKernelGetVTimerBase"},
{0xB7C18B77,WrapU64_U<sceKernelGetVTimerBaseWide>,"sceKernelGetVTimerBaseWide"},
{0x034A921F,WrapU_UU<sceKernelGetVTimerTime>,"sceKernelGetVTimerTime"},
{0xC0B3FFD2,WrapU64_U<sceKernelGetVTimerTimeWide>,"sceKernelGetVTimerTimeWide"},
{0x5F32BEAA,WrapU_UU<sceKernelReferVTimerStatus>,"sceKernelReferVTimerStatus"},
{0x542AD630,WrapU_UU<sceKernelSetVTimerTime>,"sceKernelSetVTimerTime"},
{0xFB6425C3,WrapU_UU64<sceKernelSetVTimerTimeWide>,"sceKernelSetVTimerTimeWide"},
{0xd8b299ae,WrapU_UUUU<sceKernelSetVTimerHandler>,"sceKernelSetVTimerHandler"},
{0x53B00E9A,WrapU_UU64UU<sceKernelSetVTimerHandlerWide>,"sceKernelSetVTimerHandlerWide"},
{0x20fff560,WrapU_CU<sceKernelCreateVTimer>, "sceKernelCreateVTimer", HLE_NOT_IN_INTERRUPT},
{0x328F9E52,WrapU_I<sceKernelDeleteVTimer>, "sceKernelDeleteVTimer", HLE_NOT_IN_INTERRUPT},
{0xc68d9437,WrapU_I<sceKernelStartVTimer>, "sceKernelStartVTimer"},
{0xD0AEEE87,WrapU_I<sceKernelStopVTimer>, "sceKernelStopVTimer"},
{0xD2D615EF,WrapU_I<sceKernelCancelVTimerHandler>, "sceKernelCancelVTimerHandler"},
{0xB3A59970,WrapU_IU<sceKernelGetVTimerBase>, "sceKernelGetVTimerBase"},
{0xB7C18B77,WrapU64_I<sceKernelGetVTimerBaseWide>, "sceKernelGetVTimerBaseWide"},
{0x034A921F,WrapU_IU<sceKernelGetVTimerTime>, "sceKernelGetVTimerTime"},
{0xC0B3FFD2,WrapU64_I<sceKernelGetVTimerTimeWide>, "sceKernelGetVTimerTimeWide"},
{0x5F32BEAA,WrapU_IU<sceKernelReferVTimerStatus>, "sceKernelReferVTimerStatus"},
{0x542AD630,WrapU_IU<sceKernelSetVTimerTime>, "sceKernelSetVTimerTime"},
{0xFB6425C3,WrapU64_IU64<sceKernelSetVTimerTimeWide>, "sceKernelSetVTimerTimeWide"},
{0xd8b299ae,WrapU_IUUU<sceKernelSetVTimerHandler>, "sceKernelSetVTimerHandler"},
{0x53B00E9A,WrapU_IU64UU<sceKernelSetVTimerHandlerWide>, "sceKernelSetVTimerHandlerWide"},
// Names are just guesses, not correct.
{0x8daff657,WrapI_CUUUUU<sceKernelCreateTls>, "sceKernelCreateTls"},

View File

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

View File

@ -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.

View File

@ -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 <algorithm>
#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<SceUID> 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<VTimer>(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<VTimer>(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<VTimer>(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<VTimer>(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;
}

View File

@ -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();

View File

@ -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<u32, Mp3Context *> 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<Mp3Context*>(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<u8*>(av_malloc(ctx->mp3BufSize));
u8 *avio_buffer = static_cast<u8*>(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;

View File

@ -19,4 +19,9 @@
#include "HLE.h"
void Register_sceMp3();
class PointerWrap;
void Register_sceMp3();
void __Mp3Shutdown();
void __Mp3DoState(PointerWrap &p);

View File

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

View File

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

View File

@ -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)

View File

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

View File

@ -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) {

View File

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

View File

@ -171,7 +171,6 @@ void CPU_Init() {
Memory::Init();
mipsr4k.Reset();
mipsr4k.pc = 0;
host->AttemptLoadSymbolMap();

View File

@ -83,6 +83,14 @@ void LoadPostShaderInfo(std::vector<std::string> 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);
}

View File

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

View File

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

View File

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

View File

@ -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) {

View File

@ -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)

View File

@ -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++) {

View File

@ -206,26 +206,25 @@ u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, PSPPointer<Ps
return SCE_KERNEL_ERROR_INVALID_POINTER;
int id = -1;
bool oldCompatibility = true;
u64 currentTicks = CoreTiming::GetTicks();
// Check compatibility
if (sceKernelGetCompiledSdkVersion() > 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];

View File

@ -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 \

View File

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

View File

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

View File

@ -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<CwCheatScreen>(this, &CwCheatScreen::OnBack);
//leftColumn->Add(new Choice(k->T("Add Cheat")))->OnClick.Handle<CwCheatScreen>(this, &CwCheatScreen::OnAddCheat);
leftColumn->Add(new Choice(k->T("Import Cheats")))->OnClick.Handle<CwCheatScreen>(this, &CwCheatScreen::OnImportCheat);
leftColumn->Add(new Choice(k->T("Enable/Disable All")))->OnClick.Handle<CwCheatScreen>(this, &CwCheatScreen::OnEnableAll);
leftColumn->Add(new Choice(d->T("Back")))->OnClick.Handle<UIScreen>(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 &params) {
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 &params) {
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 &params) {

View File

@ -41,6 +41,7 @@ public:
UI::EventReturn OnImportCheat(UI::EventParams &params);
UI::EventReturn OnEnableAll(UI::EventParams &params);
virtual void onFinish(DialogResult result);
protected:
virtual void CreateViews();

View File

@ -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());

View File

@ -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<UIScreen>(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<UIScreen>(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();
}

View File

@ -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();

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

@ -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<TouchControlVisibilityScreen>(this, &TouchControlVisibilityScreen::OnBack);
topBar->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(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) {

View File

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

View File

@ -127,7 +127,7 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<AdditionalDependencies>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)</AdditionalDependencies>
<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)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
@ -160,7 +160,7 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<AdditionalDependencies>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)</AdditionalDependencies>
<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)</AdditionalDependencies>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>$(OutDir)$(ProjectName).pdb</ProgramDatabaseFile>
<SubSystem>Windows</SubSystem>
@ -194,7 +194,7 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<AdditionalDependencies>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)</AdditionalDependencies>
<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)</AdditionalDependencies>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -235,7 +235,7 @@
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<AdditionalDependencies>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)</AdditionalDependencies>
<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)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Windows</SubSystem>

View File

@ -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:

View File

@ -45,3 +45,4 @@ Name=Spline36 Upscaler
Fragment=upscale_spline36.fsh
Vertex=upscale_spline36.vsh
OutputResolution=True
RequiresIntSupport=True

2
ffmpeg

@ -1 +1 @@
Subproject commit 338c515b93735f48c7696259cc7fb3bb04403571
Subproject commit 657ba596a6f0592ace6164445d163d7f67e5df25

View File

@ -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)

View File

@ -100,7 +100,7 @@
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>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)</AdditionalDependencies>
<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)</AdditionalDependencies>
<BaseAddress>0x00400000</BaseAddress>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<FixedBaseAddress>true</FixedBaseAddress>
@ -123,7 +123,7 @@
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>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)</AdditionalDependencies>
<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)</AdditionalDependencies>
<BaseAddress>0x00400000</BaseAddress>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<FixedBaseAddress>true</FixedBaseAddress>
@ -150,7 +150,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>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)</AdditionalDependencies>
<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)</AdditionalDependencies>
<BaseAddress>0x00400000</BaseAddress>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<FixedBaseAddress>true</FixedBaseAddress>
@ -178,7 +178,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>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)</AdditionalDependencies>
<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)</AdditionalDependencies>
<BaseAddress>0x00400000</BaseAddress>
<RandomizedBaseAddress>false</RandomizedBaseAddress>
<FixedBaseAddress>true</FixedBaseAddress>

2
native

@ -1 +1 @@
Subproject commit 4c8157694a60e4922818431d73da9a3f983b343e
Subproject commit 55810809b0a0d4153bdeafd41c737ca48f2f6926

@ -1 +1 @@
Subproject commit 268575b6331558e0a2f09a5c640cbb10777b5ec9
Subproject commit bf68a5fefcd951b5c62d69f41474650586bafcf9

15
test.py
View File

@ -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",