Merge branch 'master' into Font-alloc/free

This commit is contained in:
shenweip authored and GitHub committed 2020-03-16 08:16:50 +08:00
commit c7db6b6466
67 files changed
+1599 -978

No files matched your search

+1
View File
@@ -272,6 +272,7 @@ if(NOT MSVC)
# Disable some warnings
add_definitions(-Wno-multichar)
add_definitions(-Wno-deprecated-register)
add_definitions(-Wno-tautological-pointer-compare)
# Don't compile with strict aliasing, we're not 100% aliasing-safe
add_compile_options(-fno-strict-aliasing)
+21
View File
@@ -0,0 +1,21 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "ppsspp_config.h"
#if PPSSPP_PLATFORM(SWITCH)
#include <switch.h>
#include "Common/Log.h"
#include "Common/GL/GLInterface/EGLSwitch.h"
EGLDisplay cInterfaceEGLSwitch::OpenDisplay() {
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
}
EGLNativeWindowType cInterfaceEGLSwitch::InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) {
return nwindowGetDefault();
}
void cInterfaceEGLSwitch::ShutdownPlatform() {
}
#endif
+24
View File
@@ -0,0 +1,24 @@
// Copyright 2014 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Common/GL/GLInterface/EGL.h"
class cInterfaceEGLSwitch : public cInterfaceEGL {
public:
cInterfaceEGLSwitch() {}
protected:
EGLDisplay OpenDisplay() override;
EGLNativeWindowType InitializePlatform(EGLNativeWindowType host_window, EGLConfig config) override;
void ShutdownPlatform() override;
void OverrideBackbufferDimensions(int internalWidth, int internalHeight) override {
internalWidth_ = internalWidth;
internalHeight_ = internalHeight;
}
private:
int internalWidth_ = 0;
int internalHeight_ = 0;
};
+5
View File
@@ -2,10 +2,13 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "ppsspp_config.h"
#include "Common/GL/GLInterfaceBase.h"
#ifdef __ANDROID__
#include "Common/GL/GLInterface/EGLAndroid.h"
#elif PPSSPP_PLATFORM(SWITCH)
#include "Common/GL/GLInterface/EGLSwitch.h"
#elif defined(__APPLE__)
#include "Common/GL/GLInterface/AGL.h"
#elif defined(_WIN32)
@@ -23,6 +26,8 @@
cInterfaceBase* HostGL_CreateGLInterface(){
#ifdef __ANDROID__
return new cInterfaceEGLAndroid;
#elif if PPSSPP_PLATFORM(SWITCH)
return new cInterfaceEGLSwitch;
#elif defined(__APPLE__)
return new cInterfaceAGL;
#elif defined(_WIN32)
+1
View File
@@ -236,6 +236,7 @@ bool g_vulkanMayBeAvailable = false;
static const char *device_name_blacklist[] = {
"NVIDIA:SHIELD Tablet K1",
"SDL:Horizon",
};
static const char *so_names[] = {
+2 -1
View File
@@ -776,6 +776,7 @@ static ConfigSetting graphicsSettings[] = {
ConfigSetting("LogFrameDrops", &g_Config.bLogFrameDrops, false, true, false),
ConfigSetting("InflightFrames", &g_Config.iInflightFrames, 3, true, false),
ConfigSetting("RenderDuplicateFrames", &g_Config.bRenderDuplicateFrames, false, true, true),
ConfigSetting(false),
};
@@ -968,7 +969,7 @@ static ConfigSetting systemParamSettings[] = {
ReportedConfigSetting("ButtonPreference", &g_Config.iButtonPreference, PSP_SYSTEMPARAM_BUTTON_CROSS, true, true),
ConfigSetting("LockParentalLevel", &g_Config.iLockParentalLevel, 0, true, true),
ConfigSetting("WlanAdhocChannel", &g_Config.iWlanAdhocChannel, PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC, true, true),
#if defined(USING_WIN_UI)
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
ConfigSetting("BypassOSKWithKeyboard", &g_Config.bBypassOSKWithKeyboard, false, true, true),
#endif
ConfigSetting("WlanPowerSave", &g_Config.bWlanPowerSave, (bool) PSP_SYSTEMPARAM_WLAN_POWERSAVE_OFF, true, true),
+1 -1
View File
@@ -207,6 +207,7 @@ public:
bool bGfxDebugOutput;
bool bGfxDebugSplitSubmit;
int iInflightFrames;
bool bRenderDuplicateFrames;
// Sound
bool bEnableSound;
@@ -410,7 +411,6 @@ public:
int iPSPModel;
int iFirmwareVersion;
// TODO: Make this work with your platform, too!
bool bBypassOSKWithKeyboard;
// Debugger
+24 -14
View File
@@ -28,9 +28,14 @@
#define NETCONF_CONNECT_APNET 0
#define NETCONF_STATUS_APNET 1
#define NETCONF_CONNECT_ADHOC 2
#define NETCONF_CONNECT_APNET_LAST 3
#define NETCONF_CREATE_ADHOC 4
#define NETCONF_JOIN_ADHOC 5
// Needs testing.
const static int NET_INIT_DELAY_US = 300000;
const static int NET_SHUTDOWN_DELAY_US = 26000;
PSPNetconfDialog::PSPNetconfDialog() {
}
@@ -39,7 +44,7 @@ PSPNetconfDialog::~PSPNetconfDialog() {
int PSPNetconfDialog::Init(u32 paramAddr) {
// Already running
if (status != SCE_UTILITY_STATUS_NONE && status != SCE_UTILITY_STATUS_SHUTDOWN)
if (status != SCE_UTILITY_STATUS_NONE)
return SCE_ERROR_UTILITY_INVALID_STATUS;
int size = Memory::Read_U32(paramAddr);
@@ -47,7 +52,7 @@ int PSPNetconfDialog::Init(u32 paramAddr) {
// Only copy the right size to support different request format
Memory::Memcpy(&request, paramAddr, size);
status = SCE_UTILITY_STATUS_INITIALIZE;
ChangeStatusInit(NET_INIT_DELAY_US);
// Eat any keys pressed before the dialog inited.
UpdateButtons();
@@ -67,6 +72,10 @@ void PSPNetconfDialog::DrawBanner() {
}
int PSPNetconfDialog::Update(int animSpeed) {
if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {
return SCE_ERROR_UTILITY_INVALID_STATUS;
}
UpdateButtons();
auto di = GetI18NCategory("Dialog");
auto err = GetI18NCategory("Error");
@@ -74,9 +83,7 @@ int PSPNetconfDialog::Update(int animSpeed) {
const ImageID confirmBtnImage = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? ImageID("I_CROSS") : ImageID("I_CIRCLE");
const int confirmBtn = g_Config.iButtonPreference == PSP_SYSTEMPARAM_BUTTON_CROSS ? CTRL_CROSS : CTRL_CIRCLE;
if (status == SCE_UTILITY_STATUS_INITIALIZE) {
status = SCE_UTILITY_STATUS_RUNNING;
} else if (status == SCE_UTILITY_STATUS_RUNNING && (request.netAction == NETCONF_CONNECT_APNET || request.netAction == NETCONF_STATUS_APNET)) {
if (request.netAction == NETCONF_CONNECT_APNET || request.netAction == NETCONF_STATUS_APNET || request.netAction == NETCONF_CONNECT_APNET_LAST) {
UpdateFade(animSpeed);
StartDraw();
DrawBanner();
@@ -87,26 +94,24 @@ int PSPNetconfDialog::Update(int animSpeed) {
if (IsButtonPressed(confirmBtn)) {
StartFade(false);
status = SCE_UTILITY_STATUS_FINISHED;
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
// TODO: When the dialog is aborted, does it really set the result to this?
// It seems to make Phantasy Star Portable 2 happy, so it should be okay for now.
request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT;
}
} else if (status == SCE_UTILITY_STATUS_RUNNING && (request.netAction == NETCONF_CONNECT_ADHOC || request.netAction == NETCONF_CREATE_ADHOC || request.netAction == NETCONF_JOIN_ADHOC)) {
if (request.NetconfData != NULL) {
EndDraw();
} else if (request.netAction == NETCONF_CONNECT_ADHOC || request.netAction == NETCONF_CREATE_ADHOC || request.netAction == NETCONF_JOIN_ADHOC) {
if (request.NetconfData.IsValid()) {
Shutdown(true);
if (sceNetAdhocctlCreate(request.NetconfData->groupName) == 0) {
status = SCE_UTILITY_STATUS_FINISHED;
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
return 0;
}
return -1;
}
} else if (status == SCE_UTILITY_STATUS_FINISHED) {
status = SCE_UTILITY_STATUS_SHUTDOWN;
}
EndDraw();
return 0;
}
@@ -114,7 +119,12 @@ int PSPNetconfDialog::Shutdown(bool force) {
if (status != SCE_UTILITY_STATUS_FINISHED && !force)
return SCE_ERROR_UTILITY_INVALID_STATUS;
return PSPDialog::Shutdown(force);
PSPDialog::Shutdown(force);
if (!force) {
ChangeStatusShutdown(NET_SHUTDOWN_DELAY_US);
}
return 0;
}
void PSPNetconfDialog::DoState(PointerWrap &p) {
+5
View File
@@ -44,6 +44,11 @@ public:
virtual int Shutdown(bool force = false) override;
virtual void DoState(PointerWrap &p) override;
protected:
bool UseAutoStatus() override {
return false;
}
private:
void DrawBanner();
SceUtilityNetconfParam request;
+47 -26
View File
@@ -16,6 +16,7 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include "base/NativeApp.h"
#include "i18n/i18n.h"
#include "math/math_util.h"
#include "util/text/utf8.h"
@@ -30,10 +31,6 @@
#include "Common/ChunkFile.h"
#include "GPU/GPUState.h"
#if defined(USING_WIN_UI)
#include "base/NativeApp.h"
#endif
#ifndef _WIN32
#include <ctype.h>
#include <math.h>
@@ -332,6 +329,9 @@ int PSPOskDialog::Init(u32 oskPtr) {
// Eat any keys pressed before the dialog inited.
UpdateButtons();
std::lock_guard<std::mutex> guard(nativeMutex_);
nativeStatus_ = PSPOskNativeStatus::IDLE;
StartFade(true);
return 0;
}
@@ -837,7 +837,6 @@ void PSPOskDialog::RenderKeyboard()
}
}
#if defined(USING_WIN_UI)
// TODO: Why does this have a 2 button press lag/delay when
// re-opening the dialog box? I don't get it.
int PSPOskDialog::NativeKeyboard() {
@@ -845,28 +844,51 @@ int PSPOskDialog::NativeKeyboard() {
return SCE_ERROR_UTILITY_INVALID_STATUS;
}
std::wstring titleText;
GetWideStringFromPSPPointer(titleText, oskParams->fields[0].desc);
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
bool beginInputBox = false;
if (nativeStatus_ == PSPOskNativeStatus::IDLE) {
std::lock_guard<std::mutex> guard(nativeMutex_);
if (nativeStatus_ == PSPOskNativeStatus::IDLE) {
nativeStatus_ = PSPOskNativeStatus::WAITING;
beginInputBox = true;
}
}
std::wstring defaultText;
GetWideStringFromPSPPointer(defaultText, oskParams->fields[0].intext);
if (beginInputBox) {
std::wstring titleText;
GetWideStringFromPSPPointer(titleText, oskParams->fields[0].desc);
if (defaultText.empty())
defaultText.assign(L"VALUE");
std::wstring defaultText;
GetWideStringFromPSPPointer(defaultText, oskParams->fields[0].intext);
// TODO: This is USING_WIN_UI only, so we rely on it being synchronous...
// But we should really have this set some state that is checked each time NativeKeyboard is called.
System_InputBoxGetString(ConvertWStringToUTF8(titleText), ConvertWStringToUTF8(defaultText), [&](bool result, const std::string &value) {
if (result) {
inputChars = ConvertUTF8ToWString(value);
u32 maxLength = FieldMaxLength();
if (inputChars.length() > maxLength) {
ERROR_LOG(SCEUTILITY, "NativeKeyboard: input text too long(%d characters/glyphs max), truncating to game-requested length.", maxLength);
inputChars.erase(maxLength, std::string::npos);
if (defaultText.empty())
defaultText.assign(L"VALUE");
System_InputBoxGetString(ConvertWStringToUTF8(titleText), ConvertWStringToUTF8(defaultText), [&](bool result, const std::string &value) {
std::lock_guard<std::mutex> guard(nativeMutex_);
if (nativeStatus_ != PSPOskNativeStatus::WAITING) {
return;
}
nativeValue_ = value;
nativeStatus_ = result ? PSPOskNativeStatus::SUCCESS : PSPOskNativeStatus::FAILURE;
});
} else if (nativeStatus_ == PSPOskNativeStatus::SUCCESS) {
inputChars = ConvertUTF8ToWString(nativeValue_);
nativeValue_.clear();
u32 maxLength = FieldMaxLength();
if (inputChars.length() > maxLength) {
ERROR_LOG(SCEUTILITY, "NativeKeyboard: input text too long(%d characters/glyphs max), truncating to game-requested length.", maxLength);
inputChars.erase(maxLength, std::string::npos);
}
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
});
nativeStatus_ = PSPOskNativeStatus::DONE;
} else if (nativeStatus_ == PSPOskNativeStatus::FAILURE) {
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, 0);
nativeStatus_ = PSPOskNativeStatus::DONE;
}
#endif
u16_le *outText = oskParams->fields[0].outtext;
@@ -886,7 +908,6 @@ int PSPOskDialog::NativeKeyboard() {
return 0;
}
#endif
int PSPOskDialog::Update(int animSpeed) {
if (GetStatus() != SCE_UTILITY_STATUS_RUNNING) {
@@ -908,12 +929,10 @@ int PSPOskDialog::Update(int animSpeed) {
int selectedRow = selectedChar / numKeyCols[currentKeyboard];
int selectedExtra = selectedChar % numKeyCols[currentKeyboard];
// TODO: Add your platforms here when you have a NativeKeyboard func.
#if defined(USING_WIN_UI)
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
// Windows: Fall back to the OSK/continue normally if we're in fullscreen.
// The dialog box doesn't work right if in fullscreen.
if(g_Config.bBypassOSKWithKeyboard && !g_Config.bFullScreen)
if (g_Config.bBypassOSKWithKeyboard && !g_Config.bFullScreen)
return NativeKeyboard();
#endif
@@ -1095,6 +1114,7 @@ int PSPOskDialog::Shutdown(bool force)
if (!force) {
ChangeStatusShutdown(OSK_SHUTDOWN_DELAY_US);
}
nativeStatus_ = PSPOskNativeStatus::IDLE;
return 0;
}
@@ -1113,6 +1133,7 @@ void PSPOskDialog::DoState(PointerWrap &p)
p.Do(oskOuttext);
p.Do(selectedChar);
p.Do(inputChars);
// Don't need to save state native status or value.
}
pspUtilityDialogCommon *PSPOskDialog::GetCommonParam()
+13 -2
View File
@@ -17,6 +17,7 @@
#pragma once
#include <mutex>
#include <string>
#include "Core/Dialog/PSPDialog.h"
@@ -202,6 +203,14 @@ static const std::string OskKeyboardNames[] =
"English Full-width",
};
enum class PSPOskNativeStatus {
IDLE,
DONE,
WAITING,
SUCCESS,
FAILURE,
};
class PSPOskDialog: public PSPDialog {
public:
PSPOskDialog();
@@ -222,9 +231,7 @@ private:
void ConvertUCS2ToUTF8(std::string& _string, const PSPPointer<u16_le>& em_address);
void ConvertUCS2ToUTF8(std::string& _string, const wchar_t *input);
void RenderKeyboard();
#if defined(USING_WIN_UI)
int NativeKeyboard();
#endif
std::wstring CombinationString(bool isInput); // for Japanese, Korean
std::wstring CombinationKorean(bool isInput); // for Korea
@@ -244,6 +251,10 @@ private:
OskKeyboardLanguage currentKeyboardLanguage;
bool isCombinated;
std::mutex nativeMutex_;
PSPOskNativeStatus nativeStatus_ = PSPOskNativeStatus::IDLE;
std::string nativeValue_;
int i_level; // for Korean Keyboard support
int i_value[3]; // for Korean Keyboard support
};
+2 -2
View File
@@ -21,7 +21,7 @@
class KernelThreadDebugInterface : public MIPSDebugInterface {
public:
KernelThreadDebugInterface(MIPSState *c, ThreadContext &t) : MIPSDebugInterface(c), ctx(t) {
KernelThreadDebugInterface(MIPSState *c, PSPThreadContext &t) : MIPSDebugInterface(c), ctx(t) {
}
unsigned int getPC() override { return ctx.pc; }
@@ -86,5 +86,5 @@ public:
}
protected:
ThreadContext &ctx;
PSPThreadContext &ctx;
};
+2 -2
View File
@@ -764,10 +764,10 @@ typedef struct {
#pragma pack(pop)
#endif
class AfterMatchingMipsCall : public Action {
class AfterMatchingMipsCall : public PSPAction {
public:
AfterMatchingMipsCall() {}
static Action *Create() { return new AfterMatchingMipsCall(); }
static PSPAction *Create() { return new AfterMatchingMipsCall(); }
void DoState(PointerWrap &p) override {
auto s = p.Section("AfterMatchingMipsCall", 1, 2);
if (!s)
+5 -1
View File
@@ -49,6 +49,7 @@
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/HLE/sceKernelInterrupt.h"
#include "Core/Util/PPGeDraw.h"
#include "GPU/GPU.h"
#include "GPU/GPUState.h"
@@ -753,9 +754,11 @@ void __DisplayFlip(int cyclesLate) {
// Also let's always flip for animated shaders.
const ShaderInfo *shaderInfo = g_Config.sPostShaderName == "Off" ? nullptr : GetPostShaderInfo(g_Config.sPostShaderName);
bool postEffectRequiresFlip = false;
// postEffectRequiresFlip is not compatible with frameskip unthrottling, see #12325.
if (shaderInfo && g_Config.iRenderingMode != FB_NON_BUFFERED_MODE)
postEffectRequiresFlip = shaderInfo->requires60fps;
postEffectRequiresFlip = (shaderInfo->requires60fps || g_Config.bRenderDuplicateFrames) && !(g_Config.bFrameSkipUnthrottle && !FrameTimingThrottled());
const bool fbDirty = gpu->FramebufferDirty();
if (fbDirty || noRecentFlip || postEffectRequiresFlip) {
int frameSleepPos = frameTimeHistoryPos;
CalculateFPS();
@@ -832,6 +835,7 @@ void __DisplayFlip(int cyclesLate) {
void hleAfterFlip(u64 userdata, int cyclesLate) {
gpu->BeginFrame(); // doesn't really matter if begin or end of frame.
PPGeNotifyFrame();
// This seems like as good a time as any to check if the config changed.
if (lagSyncScheduled != g_Config.bForceLagSync) {
+4 -4
View File
@@ -349,10 +349,10 @@ private:
DISALLOW_COPY_AND_ASSIGN(LoadedFont);
};
class PostAllocCallback : public Action {
class PostAllocCallback : public PSPAction {
public:
PostAllocCallback() {}
static Action *Create() { return new PostAllocCallback(); }
static PSPAction *Create() { return new PostAllocCallback(); }
void DoState(PointerWrap &p) override {
auto s = p.Section("PostAllocCallback", 1, 2);
if (!s)
@@ -371,10 +371,10 @@ private:
u32 errorCodePtr_;
};
class PostOpenCallback : public Action {
class PostOpenCallback : public PSPAction {
public:
PostOpenCallback() {}
static Action *Create() { return new PostOpenCallback(); }
static PSPAction *Create() { return new PostOpenCallback(); }
void DoState(PointerWrap &p) override {
auto s = p.Section("PostOpenCallback", 1);
if (!s)
+1 -1
View File
@@ -2731,7 +2731,7 @@ static int IoAsyncFinish(int id) {
break;
default:
ERROR_LOG_REPORT(SCEIO, "Unknown async op %d", params.op);
ERROR_LOG_REPORT(SCEIO, "Unknown async op %d", (int)params.op);
us = 0;
break;
}
+16 -22
View File
@@ -37,16 +37,14 @@ struct NativeAlarm
u32_le commonPtr;
};
struct Alarm : public KernelObject
{
struct PSPAlarm : public KernelObject {
const char *GetName() override {return "[Alarm]";}
const char *GetTypeName() override {return "Alarm";}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_ALMID; }
static int GetStaticIDType() { return SCE_KERNEL_TMID_Alarm; }
int GetIDType() const override { return SCE_KERNEL_TMID_Alarm; }
void DoState(PointerWrap &p) override
{
void DoState(PointerWrap &p) override {
auto s = p.Section("Alarm", 1);
if (!s)
return;
@@ -57,7 +55,7 @@ struct Alarm : public KernelObject
NativeAlarm alm;
};
void __KernelScheduleAlarm(Alarm *alarm, u64 micro);
void __KernelScheduleAlarm(PSPAlarm *alarm, u64 micro);
class AlarmIntrHandler : public IntrHandler
{
@@ -69,7 +67,7 @@ public:
u32 error;
int alarmID = triggeredAlarm.front();
Alarm *alarm = kernelObjects.Get<Alarm>(alarmID, error);
PSPAlarm *alarm = kernelObjects.Get<PSPAlarm>(alarmID, error);
if (error)
{
WARN_LOG(SCEKERNEL, "Ignoring deleted alarm %08x", alarmID);
@@ -95,7 +93,7 @@ public:
{
DEBUG_LOG(SCEKERNEL, "Rescheduling alarm %08x for +%dms", alarmID, result);
u32 error;
Alarm *alarm = kernelObjects.Get<Alarm>(alarmID, error);
PSPAlarm *alarm = kernelObjects.Get<PSPAlarm>(alarmID, error);
__KernelScheduleAlarm(alarm, result);
}
else
@@ -106,21 +104,19 @@ public:
DEBUG_LOG(SCEKERNEL, "Finished alarm %08x", alarmID);
// Delete the alarm if it's not rescheduled.
kernelObjects.Destroy<Alarm>(alarmID);
kernelObjects.Destroy<PSPAlarm>(alarmID);
}
}
};
static int alarmTimer = -1;
static void __KernelTriggerAlarm(u64 userdata, int cyclesLate)
{
static void __KernelTriggerAlarm(u64 userdata, int cyclesLate) {
int uid = (int) userdata;
u32 error;
Alarm *alarm = kernelObjects.Get<Alarm>(uid, error);
if (alarm)
{
PSPAlarm *alarm = kernelObjects.Get<PSPAlarm>(uid, error);
if (alarm) {
triggeredAlarm.push_back(uid);
__TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_SYSTIMER0_INTR);
}
@@ -144,14 +140,12 @@ void __KernelAlarmDoState(PointerWrap &p)
CoreTiming::RestoreRegisterEvent(alarmTimer, "Alarm", __KernelTriggerAlarm);
}
KernelObject *__KernelAlarmObject()
{
KernelObject *__KernelAlarmObject() {
// Default object to load from state.
return new Alarm;
return new PSPAlarm;
}
void __KernelScheduleAlarm(Alarm *alarm, u64 micro)
{
void __KernelScheduleAlarm(PSPAlarm *alarm, u64 micro) {
alarm->alm.schedule = CoreTiming::GetGlobalTimeUs() + micro;
CoreTiming::ScheduleEvent(usToCycles(micro), alarmTimer, alarm->GetUID());
}
@@ -161,7 +155,7 @@ static SceUID __KernelSetAlarm(u64 micro, u32 handlerPtr, u32 commonPtr)
if (!Memory::IsValidAddress(handlerPtr))
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
Alarm *alarm = new Alarm;
PSPAlarm *alarm = new PSPAlarm();
SceUID uid = kernelObjects.Create(alarm);
alarm->alm.size = NATIVEALARM_SIZE;
@@ -197,13 +191,13 @@ int sceKernelCancelAlarm(SceUID uid)
CoreTiming::UnscheduleEvent(alarmTimer, uid);
return kernelObjects.Destroy<Alarm>(uid);
return kernelObjects.Destroy<PSPAlarm>(uid);
}
int sceKernelReferAlarmStatus(SceUID uid, u32 infoPtr)
{
u32 error;
Alarm *alarm = kernelObjects.Get<Alarm>(uid, error);
PSPAlarm *alarm = kernelObjects.Get<PSPAlarm>(uid, error);
if (!alarm)
{
ERROR_LOG(SCEKERNEL, "sceKernelReferAlarmStatus(%08x, %08x): invalid alarm", uid, infoPtr);
@@ -228,4 +222,4 @@ int sceKernelReferAlarmStatus(SceUID uid, u32 infoPtr)
Memory::Write_U32(alarm->alm.commonPtr, infoPtr + 16);
return 0;
}
}
+3 -5
View File
@@ -48,15 +48,13 @@ static const u32 PSP_NUMBER_SUBINTERRUPTS = 32;
// INTERRUPT MANAGEMENT
//////////////////////////////////////////////////////////////////////////
class InterruptState
{
class InterruptState {
public:
void save();
void restore();
void clear();
void DoState(PointerWrap &p)
{
void DoState(PointerWrap &p) {
auto s = p.Section("InterruptState", 1);
if (!s)
return;
@@ -64,7 +62,7 @@ public:
p.Do(savedCpu);
}
ThreadContext savedCpu;
PSPThreadContext savedCpu;
};
// STATE
+51 -58
View File
@@ -155,8 +155,8 @@ void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting);
void ExportFuncSymbol(const FuncSymbolExport &func);
void UnexportFuncSymbol(const FuncSymbolExport &func);
class Module;
static bool KernelImportModuleFuncs(Module *module, u32 *firstImportStubAddr, bool reimporting = false);
class PSPModule;
static bool KernelImportModuleFuncs(PSPModule *module, u32 *firstImportStubAddr, bool reimporting = false);
struct NativeModule {
u32_le next;
@@ -228,10 +228,10 @@ enum NativeModuleStatus {
MODULE_STATUS_UNLOADING = 8,
};
class Module : public KernelObject {
class PSPModule : public KernelObject {
public:
Module() : textStart(0), textEnd(0), libstub(0), libstubend(0), memoryBlockAddr(0), isFake(false) {}
~Module() {
PSPModule() : textStart(0), textEnd(0), libstub(0), libstubend(0), memoryBlockAddr(0), isFake(false) {}
~PSPModule() {
if (memoryBlockAddr) {
// If it's either below user memory, or using a high kernel bit, it's in kernel.
if (memoryBlockAddr < PSP_GetUserMemoryBase() || memoryBlockAddr > PSP_GetUserMemoryEnd()) {
@@ -421,10 +421,10 @@ public:
KernelObject *__KernelModuleObject()
{
return new Module;
return new PSPModule;
}
class AfterModuleEntryCall : public Action {
class AfterModuleEntryCall : public PSPAction {
public:
AfterModuleEntryCall() {}
SceUID moduleID_;
@@ -438,7 +438,7 @@ public:
p.Do(moduleID_);
p.Do(retValAddr);
}
static Action *Create() {
static PSPAction *Create() {
return new AfterModuleEntryCall;
}
};
@@ -507,7 +507,7 @@ void __KernelModuleDoState(PointerWrap &p)
u32 error;
// We process these late, since they depend on loadedModules for interlinking.
for (SceUID moduleId : loadedModules) {
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (module && module->libstub != 0) {
if (!KernelImportModuleFuncs(module, nullptr, true)) {
ERROR_LOG(LOADER, "Something went wrong loading imports on load state");
@@ -655,7 +655,7 @@ void ImportVarSymbol(const VarSymbolImport &var) {
u32 error;
for (SceUID moduleId : loadedModules) {
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module || !module->ImportsOrExportsModuleName(var.moduleName)) {
continue;
}
@@ -676,7 +676,7 @@ void ImportVarSymbol(const VarSymbolImport &var) {
void ExportVarSymbol(const VarSymbolExport &var) {
u32 error;
for (SceUID moduleId : loadedModules) {
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module || !module->ImportsOrExportsModuleName(var.moduleName)) {
continue;
}
@@ -694,7 +694,7 @@ void ExportVarSymbol(const VarSymbolExport &var) {
void UnexportVarSymbol(const VarSymbolExport &var) {
u32 error;
for (SceUID moduleId : loadedModules) {
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module || !module->ImportsOrExportsModuleName(var.moduleName)) {
continue;
}
@@ -724,7 +724,7 @@ void ImportFuncSymbol(const FuncSymbolImport &func, bool reimporting) {
u32 error;
for (SceUID moduleId : loadedModules) {
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module || !module->ImportsOrExportsModuleName(func.moduleName)) {
continue;
}
@@ -765,7 +765,7 @@ void ExportFuncSymbol(const FuncSymbolExport &func) {
u32 error;
for (SceUID moduleId : loadedModules) {
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module || !module->ImportsOrExportsModuleName(func.moduleName)) {
continue;
}
@@ -790,7 +790,7 @@ void UnexportFuncSymbol(const FuncSymbolExport &func) {
u32 error;
for (SceUID moduleId : loadedModules) {
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module || !module->ImportsOrExportsModuleName(func.moduleName)) {
continue;
}
@@ -806,7 +806,7 @@ void UnexportFuncSymbol(const FuncSymbolExport &func) {
}
}
void Module::Cleanup() {
void PSPModule::Cleanup() {
MIPSAnalyst::ForgetFunctions(textStart, textEnd);
loadedModules.erase(GetUID());
@@ -909,7 +909,7 @@ static bool IsHLEVersionedModule(const char *name) {
return false;
}
static bool KernelImportModuleFuncs(Module *module, u32 *firstImportStubAddr, bool reimporting) {
static bool KernelImportModuleFuncs(PSPModule *module, u32 *firstImportStubAddr, bool reimporting) {
struct PspLibStubEntry {
u32_le name;
u16_le version;
@@ -1090,8 +1090,8 @@ static int gzipDecompress(u8 *OutBuffer, int OutBufferLength, u8 *InBuffer) {
return stream.total_out;
}
static Module *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAddress, bool fromTop, std::string *error_string, u32 *magic, u32 &error) {
Module *module = new Module;
static PSPModule *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAddress, bool fromTop, std::string *error_string, u32 *magic, u32 &error) {
PSPModule *module = new PSPModule();
kernelObjects.Create(module);
loadedModules.insert(module->GetUID());
memset(&module->nm, 0, sizeof(module->nm));
@@ -1133,7 +1133,7 @@ static Module *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAdd
if (size > elfSize) {
*error_string = StringFromFormat("ELF/PRX truncated: %d > %d", (int)size, (int)elfSize);
module->Cleanup();
kernelObjects.Destroy<Module>(module->GetUID());
kernelObjects.Destroy<PSPModule>(module->GetUID());
return nullptr;
}
const auto maxElfSize = std::max(head->elf_size, head->psp_size);
@@ -1166,7 +1166,7 @@ static Module *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAdd
if (addr == (u32)-1) {
error = SCE_KERNEL_ERROR_MEMBLOCK_ALLOC_FAILED;
module->Cleanup();
kernelObjects.Destroy<Module>(module->GetUID());
kernelObjects.Destroy<PSPModule>(module->GetUID());
} else {
error = 0;
module->memoryBlockAddr = addr;
@@ -1207,7 +1207,7 @@ static Module *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAdd
if (newptr)
delete [] newptr;
module->Cleanup();
kernelObjects.Destroy<Module>(module->GetUID());
kernelObjects.Destroy<PSPModule>(module->GetUID());
error = SCE_KERNEL_ERROR_UNSUPPORTED_PRX_TYPE;
return nullptr;
}
@@ -1221,7 +1221,7 @@ static Module *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAdd
if (newptr)
delete [] newptr;
module->Cleanup();
kernelObjects.Destroy<Module>(module->GetUID());
kernelObjects.Destroy<PSPModule>(module->GetUID());
error = result;
return nullptr;
}
@@ -1536,8 +1536,8 @@ static Module *__KernelLoadELFFromPtr(const u8 *ptr, size_t elfSize, u32 loadAdd
return module;
}
static Module *__KernelLoadModule(u8 *fileptr, size_t fileSize, SceKernelLMOption *options, std::string *error_string) {
Module *module = 0;
static PSPModule *__KernelLoadModule(u8 *fileptr, size_t fileSize, SceKernelLMOption *options, std::string *error_string) {
PSPModule *module = nullptr;
// Check for PBP
if (memcmp(fileptr, "\0PBP", 4) == 0) {
// PBP!
@@ -1583,8 +1583,7 @@ static Module *__KernelLoadModule(u8 *fileptr, size_t fileSize, SceKernelLMOptio
return module;
}
static void __KernelStartModule(Module *m, int args, const char *argp, SceKernelSMOption *options)
{
static void __KernelStartModule(PSPModule *m, int args, const char *argp, SceKernelSMOption *options) {
m->nm.status = MODULE_STATUS_STARTED;
if (m->nm.module_start_func != 0 && m->nm.module_start_func != (u32)-1)
{
@@ -1599,16 +1598,12 @@ static void __KernelStartModule(Module *m, int args, const char *argp, SceKernel
}
u32 __KernelGetModuleGP(SceUID uid)
{
u32 __KernelGetModuleGP(SceUID uid) {
u32 error;
Module *module = kernelObjects.Get<Module>(uid, error);
if (module)
{
PSPModule *module = kernelObjects.Get<PSPModule>(uid, error);
if (module) {
return module->nm.gp_value;
}
else
{
} else {
return 0;
}
}
@@ -1619,7 +1614,7 @@ void __KernelLoadReset() {
u32 error;
while (!loadedModules.empty()) {
SceUID moduleID = *loadedModules.begin();
Module *module = kernelObjects.Get<Module>(moduleID, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleID, error);
if (module) {
module->Cleanup();
} else {
@@ -1686,12 +1681,12 @@ bool __KernelLoadExec(const char *filename, u32 paramPtr, std::string *error_str
pspFileSystem.ReadFile(handle, temp, (size_t)info.size);
PSP_SetLoading("Loading modules...");
Module *module = __KernelLoadModule(temp, (size_t)info.size, 0, error_string);
PSPModule *module = __KernelLoadModule(temp, (size_t)info.size, 0, error_string);
if (!module || module->isFake) {
if (module) {
module->Cleanup();
kernelObjects.Destroy<Module>(module->GetUID());
kernelObjects.Destroy<PSPModule>(module->GetUID());
}
ERROR_LOG(LOADER, "Failed to load module %s", filename);
*error_string = "Failed to load executable: " + *error_string;
@@ -1770,7 +1765,7 @@ bool __KernelLoadGEDump(const std::string &base_filename, std::string *error_str
Memory::WriteUnchecked_U32(runDumpCode[i], mipsr4k.pc + (int)i * sizeof(u32_le));
}
Module *module = new Module;
PSPModule *module = new PSPModule();
kernelObjects.Create(module);
loadedModules.insert(module->GetUID());
memset(&module->nm, 0, sizeof(module->nm));
@@ -1847,7 +1842,7 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr) {
for (size_t i = 0; i < ARRAY_SIZE(lieAboutSuccessModules); i++) {
if (!strcmp(name, lieAboutSuccessModules[i])) {
Module *module = new Module;
PSPModule *module = new PSPModule();
kernelObjects.Create(module);
loadedModules.insert(module->GetUID());
memset(&module->nm, 0, sizeof(module->nm));
@@ -1898,7 +1893,7 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr) {
WARN_LOG_REPORT(LOADER, "sceKernelLoadModule: unsupported options size=%08x, flags=%08x, pos=%d, access=%d, data=%d, text=%d", lmoption->size, lmoption->flags, lmoption->position, lmoption->access, lmoption->mpiddata, lmoption->mpidtext);
}
Module *module = 0;
PSPModule *module = nullptr;
u8 *temp = new u8[(int)size];
u32 handle = pspFileSystem.OpenFile(name, FILEACCESS_READ);
pspFileSystem.ReadFile(handle, temp, (size_t)size);
@@ -1961,7 +1956,7 @@ static void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 ret
Memory::ReadStruct(optionAddr, &smoption);
}
u32 error;
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module) {
INFO_LOG(SCEMODULE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): error %08x", moduleId, argsize, argAddr, returnValueAddr, optionAddr, error);
RETURN(error);
@@ -2060,7 +2055,7 @@ static u32 sceKernelStopModule(u32 moduleId, u32 argSize, u32 argAddr, u32 retur
// TODO: In a lot of cases (even for errors), this should resched. Needs testing.
u32 error;
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module)
{
ERROR_LOG(SCEMODULE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): invalid module id", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
@@ -2133,12 +2128,12 @@ static u32 sceKernelUnloadModule(u32 moduleId)
{
INFO_LOG(SCEMODULE,"sceKernelUnloadModule(%i)", moduleId);
u32 error;
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module)
return hleDelayResult(error, "module unloaded", 150);
module->Cleanup();
kernelObjects.Destroy<Module>(moduleId);
kernelObjects.Destroy<PSPModule>(moduleId);
return hleDelayResult(moduleId, "module unloaded", 500);
}
@@ -2155,7 +2150,7 @@ u32 hleKernelStopUnloadSelfModuleWithOrWithoutStatus(u32 exitCode, u32 argSize,
// TODO: In a lot of cases (even for errors), this should resched. Needs testing.
u32 error;
Module *module = kernelObjects.Get<Module>(moduleID, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleID, error);
if (!module) {
if (WithStatus)
ERROR_LOG(SCEMODULE, "sceKernelStopUnloadSelfModuleWithStatus(%08x, %08x, %08x, %08x, %08x): invalid module id", exitCode, argSize, argp, statusAddr, optionAddr);
@@ -2204,7 +2199,7 @@ u32 hleKernelStopUnloadSelfModuleWithOrWithoutStatus(u32 exitCode, u32 argSize,
INFO_LOG(SCEMODULE, "sceKernelSelfStopUnloadModule(%08x, %08x, %08x): no stop func", exitCode, argSize, argp);
sceKernelExitDeleteThread(exitCode);
module->Cleanup();
kernelObjects.Destroy<Module>(moduleID);
kernelObjects.Destroy<PSPModule>(moduleID);
} else {
if (WithStatus)
ERROR_LOG_REPORT(SCEMODULE, "sceKernelStopUnloadSelfModuleWithStatus(%08x, %08x, %08x, %08x, %08x): bad stop func address", exitCode, argSize, argp, statusAddr, optionAddr);
@@ -2212,7 +2207,7 @@ u32 hleKernelStopUnloadSelfModuleWithOrWithoutStatus(u32 exitCode, u32 argSize,
ERROR_LOG_REPORT(SCEMODULE, "sceKernelSelfStopUnloadModule(%08x, %08x, %08x): bad stop func address", exitCode, argSize, argp);
sceKernelExitDeleteThread(exitCode);
module->Cleanup();
kernelObjects.Destroy<Module>(moduleID);
kernelObjects.Destroy<PSPModule>(moduleID);
}
} else {
if (WithStatus)
@@ -2248,7 +2243,7 @@ void __KernelReturnFromModuleFunc()
sceKernelDeleteThread(leftThreadID);
u32 error;
Module *module = kernelObjects.Get<Module>(leftModuleID, error);
PSPModule *module = kernelObjects.Get<PSPModule>(leftModuleID, error);
if (!module) {
ERROR_LOG_REPORT(SCEMODULE, "Returned from deleted module start/stop func");
return;
@@ -2278,7 +2273,7 @@ void __KernelReturnFromModuleFunc()
if (module->nm.status == MODULE_STATUS_UNLOADING) {
// TODO: Delete the waiting thread?
module->Cleanup();
kernelObjects.Destroy<Module>(leftModuleID);
kernelObjects.Destroy<PSPModule>(leftModuleID);
}
}
@@ -2288,11 +2283,9 @@ struct GetModuleIdByAddressArg
SceUID result;
};
static bool __GetModuleIdByAddressIterator(Module *module, GetModuleIdByAddressArg *state)
{
static bool __GetModuleIdByAddressIterator(PSPModule *module, GetModuleIdByAddressArg *state) {
const u32 start = module->memoryBlockAddr, size = module->memoryBlockSize;
if (start != 0 && start <= state->addr && start + size > state->addr)
{
if (start != 0 && start <= state->addr && start + size > state->addr) {
state->result = module->GetUID();
return false;
}
@@ -2351,7 +2344,7 @@ static u32 sceKernelLoadModuleByID(u32 id, u32 flags, u32 lmoptionPtr)
size_t size = pspFileSystem.SeekFile(handle, 0, FILEMOVE_END);
std::string error_string;
pspFileSystem.SeekFile(handle, pos, FILEMOVE_BEGIN);
Module *module = 0;
PSPModule *module = nullptr;
u8 *temp = new u8[size - pos];
pspFileSystem.ReadFile(handle, temp, size - pos);
u32 magic;
@@ -2409,7 +2402,7 @@ static SceUID sceKernelLoadModuleBufferUsbWlan(u32 size, u32 bufPtr, u32 flags,
WARN_LOG_REPORT(LOADER, "sceKernelLoadModuleBufferUsbWlan: unsupported options size=%08x, flags=%08x, pos=%d, access=%d, data=%d, text=%d", lmoption->size, lmoption->flags, lmoption->position, lmoption->access, lmoption->mpiddata, lmoption->mpidtext);
}
std::string error_string;
Module *module = 0;
PSPModule *module = nullptr;
u32 magic;
u32 error;
module = __KernelLoadELFFromPtr(Memory::GetPointer(bufPtr), size, 0, lmoption ? lmoption->position == PSP_SMEM_High : false, &error_string, &magic, error);
@@ -2451,7 +2444,7 @@ static u32 sceKernelQueryModuleInfo(u32 uid, u32 infoAddr)
{
INFO_LOG(SCEMODULE, "sceKernelQueryModuleInfo(%i, %08x)", uid, infoAddr);
u32 error;
Module *module = kernelObjects.Get<Module>(uid, error);
PSPModule *module = kernelObjects.Get<PSPModule>(uid, error);
if (!module)
return error;
if (!Memory::IsValidAddress(infoAddr)) {
@@ -2492,7 +2485,7 @@ static u32 sceKernelGetModuleIdList(u32 resultBuffer, u32 resultBufferSize, u32
u32 error;
for (SceUID moduleId : loadedModules) {
Module *module = kernelObjects.Get<Module>(moduleId, error);
PSPModule *module = kernelObjects.Get<PSPModule>(moduleId, error);
if (!module->isFake) {
if (resultBufferOffset < resultBufferSize) {
Memory::Write_U32(module->GetUID(), resultBuffer + resultBufferOffset);
+26 -35
View File
@@ -62,7 +62,7 @@ struct NativeMutex
s32_le numWaitThreads;
};
struct Mutex : public KernelObject
struct PSPMutex : public KernelObject
{
const char *GetName() override { return nm.name; }
const char *GetTypeName() override { return "Mutex"; }
@@ -189,7 +189,7 @@ void __KernelMutexDoState(PointerWrap &p)
KernelObject *__KernelMutexObject()
{
return new Mutex;
return new PSPMutex;
}
KernelObject *__KernelLwMutexObject()
@@ -202,8 +202,7 @@ void __KernelMutexShutdown()
mutexHeldLocks.clear();
}
static void __KernelMutexAcquireLock(Mutex *mutex, int count, SceUID thread)
{
static void __KernelMutexAcquireLock(PSPMutex *mutex, int count, SceUID thread) {
#if defined(_DEBUG)
auto locked = mutexHeldLocks.equal_range(thread);
for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter)
@@ -216,13 +215,11 @@ static void __KernelMutexAcquireLock(Mutex *mutex, int count, SceUID thread)
mutex->nm.lockThread = thread;
}
static void __KernelMutexAcquireLock(Mutex *mutex, int count)
{
static void __KernelMutexAcquireLock(PSPMutex *mutex, int count) {
__KernelMutexAcquireLock(mutex, count, __KernelGetCurThread());
}
static void __KernelMutexEraseLock(Mutex *mutex)
{
static void __KernelMutexEraseLock(PSPMutex *mutex) {
if (mutex->nm.lockThread != -1)
{
SceUID id = mutex->GetUID();
@@ -259,8 +256,7 @@ static std::vector<SceUID>::iterator __KernelMutexFindPriority(std::vector<SceUI
return best;
}
static bool __KernelUnlockMutexForThread(Mutex *mutex, SceUID threadID, u32 &error, int result)
{
static bool __KernelUnlockMutexForThread(PSPMutex *mutex, SceUID threadID, u32 &error, int result) {
if (!HLEKernel::VerifyWait(threadID, WAITTYPE_MUTEX, mutex->GetUID()))
return false;
@@ -283,8 +279,7 @@ static bool __KernelUnlockMutexForThread(Mutex *mutex, SceUID threadID, u32 &err
return true;
}
static bool __KernelUnlockMutexForThreadCheck(Mutex *mutex, SceUID threadID, u32 &error, int result, bool &wokeThreads)
{
static bool __KernelUnlockMutexForThreadCheck(PSPMutex *mutex, SceUID threadID, u32 &error, int result, bool &wokeThreads) {
if (mutex->nm.lockThread == -1 && __KernelUnlockMutexForThread(mutex, threadID, error, 0))
return true;
return false;
@@ -292,7 +287,7 @@ static bool __KernelUnlockMutexForThreadCheck(Mutex *mutex, SceUID threadID, u32
void __KernelMutexBeginCallback(SceUID threadID, SceUID prevCallbackId)
{
auto result = HLEKernel::WaitBeginCallback<Mutex, WAITTYPE_MUTEX, SceUID>(threadID, prevCallbackId, mutexWaitTimer);
auto result = HLEKernel::WaitBeginCallback<PSPMutex, WAITTYPE_MUTEX, SceUID>(threadID, prevCallbackId, mutexWaitTimer);
if (result == HLEKernel::WAIT_CB_SUCCESS)
DEBUG_LOG(SCEKERNEL, "sceKernelLockMutexCB: Suspending lock wait for callback");
else
@@ -301,7 +296,7 @@ void __KernelMutexBeginCallback(SceUID threadID, SceUID prevCallbackId)
void __KernelMutexEndCallback(SceUID threadID, SceUID prevCallbackId)
{
auto result = HLEKernel::WaitEndCallback<Mutex, WAITTYPE_MUTEX, SceUID>(threadID, prevCallbackId, mutexWaitTimer, __KernelUnlockMutexForThreadCheck);
auto result = HLEKernel::WaitEndCallback<PSPMutex, WAITTYPE_MUTEX, SceUID>(threadID, prevCallbackId, mutexWaitTimer, __KernelUnlockMutexForThreadCheck);
if (result == HLEKernel::WAIT_CB_RESUMED_WAIT)
DEBUG_LOG(SCEKERNEL, "sceKernelLockMutexCB: Resuming lock wait for callback");
}
@@ -324,7 +319,7 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio
if ((attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1)
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
Mutex *mutex = new Mutex();
PSPMutex *mutex = new PSPMutex();
SceUID id = kernelObjects.Create(mutex);
mutex->nm.size = sizeof(mutex->nm);
@@ -357,7 +352,7 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio
int sceKernelDeleteMutex(SceUID id)
{
u32 error;
Mutex *mutex = kernelObjects.Get<Mutex>(id, error);
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(id, error);
if (mutex)
{
DEBUG_LOG(SCEKERNEL, "sceKernelDeleteMutex(%i)", id);
@@ -373,7 +368,7 @@ int sceKernelDeleteMutex(SceUID id)
if (wokeThreads)
hleReSchedule("mutex deleted");
return kernelObjects.Destroy<Mutex>(id);
return kernelObjects.Destroy<PSPMutex>(id);
}
else
{
@@ -382,8 +377,7 @@ int sceKernelDeleteMutex(SceUID id)
}
}
static bool __KernelLockMutexCheck(Mutex *mutex, int count, u32 &error)
{
static bool __KernelLockMutexCheck(PSPMutex *mutex, int count, u32 &error) {
if (error)
return false;
@@ -411,8 +405,7 @@ static bool __KernelLockMutexCheck(Mutex *mutex, int count, u32 &error)
return false;
}
static bool __KernelLockMutex(Mutex *mutex, int count, u32 &error)
{
static bool __KernelLockMutex(PSPMutex *mutex, int count, u32 &error) {
if (!__KernelLockMutexCheck(mutex, count, error))
return false;
@@ -433,8 +426,7 @@ static bool __KernelLockMutex(Mutex *mutex, int count, u32 &error)
return false;
}
static bool __KernelUnlockMutex(Mutex *mutex, u32 &error)
{
static bool __KernelUnlockMutex(PSPMutex *mutex, u32 &error) {
__KernelMutexEraseLock(mutex);
bool wokeThreads = false;
@@ -459,7 +451,7 @@ static bool __KernelUnlockMutex(Mutex *mutex, u32 &error)
void __KernelMutexTimeout(u64 userdata, int cyclesLate)
{
SceUID threadID = (SceUID)userdata;
HLEKernel::WaitExecTimeout<Mutex, WAITTYPE_MUTEX>(threadID);
HLEKernel::WaitExecTimeout<PSPMutex, WAITTYPE_MUTEX>(threadID);
}
void __KernelMutexThreadEnd(SceUID threadID)
@@ -470,7 +462,7 @@ void __KernelMutexThreadEnd(SceUID threadID)
SceUID waitingMutexID = __KernelGetWaitID(threadID, WAITTYPE_MUTEX, error);
if (waitingMutexID)
{
Mutex *mutex = kernelObjects.Get<Mutex>(waitingMutexID, error);
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(waitingMutexID, error);
if (mutex)
HLEKernel::RemoveWaitingThread(mutex->waitingThreads, threadID);
}
@@ -481,7 +473,7 @@ void __KernelMutexThreadEnd(SceUID threadID)
{
// Need to increment early so erase() doesn't invalidate.
SceUID mutexID = (*iter++).second;
Mutex *mutex = kernelObjects.Get<Mutex>(mutexID, error);
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(mutexID, error);
if (mutex)
{
@@ -491,8 +483,7 @@ void __KernelMutexThreadEnd(SceUID threadID)
}
}
static void __KernelWaitMutex(Mutex *mutex, u32 timeoutPtr)
{
static void __KernelWaitMutex(PSPMutex *mutex, u32 timeoutPtr) {
if (timeoutPtr == 0 || mutexWaitTimer == -1)
return;
@@ -511,7 +502,7 @@ static void __KernelWaitMutex(Mutex *mutex, u32 timeoutPtr)
int sceKernelCancelMutex(SceUID uid, int count, u32 numWaitThreadsPtr)
{
u32 error;
Mutex *mutex = kernelObjects.Get<Mutex>(uid, error);
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(uid, error);
if (mutex)
{
bool lockable = count <= 0 || __KernelLockMutexCheck(mutex, count, error);
@@ -566,7 +557,7 @@ int sceKernelLockMutex(SceUID id, int count, u32 timeoutPtr)
{
DEBUG_LOG(SCEKERNEL, "sceKernelLockMutex(%i, %i, %08x)", id, count, timeoutPtr);
u32 error;
Mutex *mutex = kernelObjects.Get<Mutex>(id, error);
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(id, error);
if (__KernelLockMutex(mutex, count, error))
return 0;
@@ -591,7 +582,7 @@ int sceKernelLockMutexCB(SceUID id, int count, u32 timeoutPtr)
{
DEBUG_LOG(SCEKERNEL, "sceKernelLockMutexCB(%i, %i, %08x)", id, count, timeoutPtr);
u32 error;
Mutex *mutex = kernelObjects.Get<Mutex>(id, error);
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(id, error);
if (!__KernelLockMutexCheck(mutex, count, error))
{
@@ -630,7 +621,7 @@ int sceKernelTryLockMutex(SceUID id, int count)
{
DEBUG_LOG(SCEKERNEL, "sceKernelTryLockMutex(%i, %i)", id, count);
u32 error;
Mutex *mutex = kernelObjects.Get<Mutex>(id, error);
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(id, error);
if (__KernelLockMutex(mutex, count, error))
return 0;
@@ -645,7 +636,7 @@ int sceKernelUnlockMutex(SceUID id, int count)
{
DEBUG_LOG(SCEKERNEL, "sceKernelUnlockMutex(%i, %i)", id, count);
u32 error;
Mutex *mutex = kernelObjects.Get<Mutex>(id, error);
PSPMutex *mutex = kernelObjects.Get<PSPMutex>(id, error);
if (error)
return error;
@@ -672,7 +663,7 @@ int sceKernelUnlockMutex(SceUID id, int count)
int sceKernelReferMutexStatus(SceUID id, u32 infoAddr)
{
u32 error;
Mutex *m = kernelObjects.Get<Mutex>(id, error);
PSPMutex *m = kernelObjects.Get<PSPMutex>(id, error);
if (!m)
{
ERROR_LOG(SCEKERNEL, "sceKernelReferMutexStatus(%i, %08x): invalid mutex id", id, infoAddr);
@@ -1136,4 +1127,4 @@ int sceKernelReferLwMutexStatus(u32 workareaPtr, u32 infoPtr)
ERROR_LOG(SCEKERNEL, "%08x=sceKernelReferLwMutexStatus(%08x, %08x)", error, workareaPtr, infoPtr);
return error;
}
}
}
+17 -21
View File
@@ -53,8 +53,7 @@ struct NativeSemaphore
};
struct Semaphore : public KernelObject
{
struct PSPSemaphore : public KernelObject {
const char *GetName() override { return ns.name; }
const char *GetTypeName() override { return "Semaphore"; }
@@ -103,12 +102,11 @@ void __KernelSemaDoState(PointerWrap &p)
KernelObject *__KernelSemaphoreObject()
{
return new Semaphore;
return new PSPSemaphore;
}
// Returns whether the thread should be removed.
static bool __KernelUnlockSemaForThread(Semaphore *s, SceUID threadID, u32 &error, int result, bool &wokeThreads)
{
static bool __KernelUnlockSemaForThread(PSPSemaphore *s, SceUID threadID, u32 &error, int result, bool &wokeThreads) {
if (!HLEKernel::VerifyWait(threadID, WAITTYPE_SEMA, s->GetUID()))
return true;
@@ -139,7 +137,7 @@ static bool __KernelUnlockSemaForThread(Semaphore *s, SceUID threadID, u32 &erro
void __KernelSemaBeginCallback(SceUID threadID, SceUID prevCallbackId)
{
auto result = HLEKernel::WaitBeginCallback<Semaphore, WAITTYPE_SEMA, SceUID>(threadID, prevCallbackId, semaWaitTimer);
auto result = HLEKernel::WaitBeginCallback<PSPSemaphore, WAITTYPE_SEMA, SceUID>(threadID, prevCallbackId, semaWaitTimer);
if (result == HLEKernel::WAIT_CB_SUCCESS)
DEBUG_LOG(SCEKERNEL, "sceKernelWaitSemaCB: Suspending sema wait for callback");
else
@@ -148,15 +146,14 @@ void __KernelSemaBeginCallback(SceUID threadID, SceUID prevCallbackId)
void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId)
{
auto result = HLEKernel::WaitEndCallback<Semaphore, WAITTYPE_SEMA, SceUID>(threadID, prevCallbackId, semaWaitTimer, __KernelUnlockSemaForThread);
auto result = HLEKernel::WaitEndCallback<PSPSemaphore, WAITTYPE_SEMA, SceUID>(threadID, prevCallbackId, semaWaitTimer, __KernelUnlockSemaForThread);
if (result == HLEKernel::WAIT_CB_RESUMED_WAIT)
DEBUG_LOG(SCEKERNEL, "sceKernelWaitSemaCB: Resuming sema wait for callback");
}
// Resume all waiting threads (for delete / cancel.)
// Returns true if it woke any threads.
static bool __KernelClearSemaThreads(Semaphore *s, int reason)
{
static bool __KernelClearSemaThreads(PSPSemaphore *s, int reason) {
u32 error;
bool wokeThreads = false;
std::vector<SceUID>::iterator iter, end;
@@ -170,7 +167,7 @@ static bool __KernelClearSemaThreads(Semaphore *s, int reason)
int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr)
{
u32 error;
Semaphore *s = kernelObjects.Get<Semaphore>(id, error);
PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
if (s)
{
if (newCount > s->ns.maxCount)
@@ -215,7 +212,7 @@ int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
}
Semaphore *s = new Semaphore;
PSPSemaphore *s = new PSPSemaphore();
SceUID id = kernelObjects.Create(s);
s->ns.size = sizeof(NativeSemaphore);
@@ -244,7 +241,7 @@ int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32
int sceKernelDeleteSema(SceUID id)
{
u32 error;
Semaphore *s = kernelObjects.Get<Semaphore>(id, error);
PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
if (s)
{
DEBUG_LOG(SCEKERNEL, "sceKernelDeleteSema(%i)", id);
@@ -253,7 +250,7 @@ int sceKernelDeleteSema(SceUID id)
if (wokeThreads)
hleReSchedule("semaphore deleted");
return kernelObjects.Destroy<Semaphore>(id);
return kernelObjects.Destroy<PSPSemaphore>(id);
}
else
{
@@ -265,7 +262,7 @@ int sceKernelDeleteSema(SceUID id)
int sceKernelReferSemaStatus(SceUID id, u32 infoPtr)
{
u32 error;
Semaphore *s = kernelObjects.Get<Semaphore>(id, error);
PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
if (s)
{
DEBUG_LOG(SCEKERNEL, "sceKernelReferSemaStatus(%i, %08x)", id, infoPtr);
@@ -290,7 +287,7 @@ int sceKernelReferSemaStatus(SceUID id, u32 infoPtr)
int sceKernelSignalSema(SceUID id, int signal)
{
u32 error;
Semaphore *s = kernelObjects.Get<Semaphore>(id, error);
PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
if (s)
{
if (s->ns.currentCount + signal - (int) s->waitingThreads.size() > s->ns.maxCount)
@@ -336,10 +333,10 @@ void __KernelSemaTimeout(u64 userdata, int cycleslate)
u32 error;
SceUID uid = __KernelGetWaitID(threadID, WAITTYPE_SEMA, error);
HLEKernel::WaitExecTimeout<Semaphore, WAITTYPE_SEMA>(threadID);
HLEKernel::WaitExecTimeout<PSPSemaphore, WAITTYPE_SEMA>(threadID);
// If in FIFO mode, that may have cleared another thread to wake up.
Semaphore *s = kernelObjects.Get<Semaphore>(uid, error);
PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(uid, error);
if (s && (s->ns.attr & PSP_SEMA_ATTR_PRIORITY) == PSP_SEMA_ATTR_FIFO) {
bool wokeThreads;
std::vector<SceUID>::iterator iter = s->waitingThreads.begin();
@@ -351,8 +348,7 @@ void __KernelSemaTimeout(u64 userdata, int cycleslate)
}
}
static void __KernelSetSemaTimeout(Semaphore *s, u32 timeoutPtr)
{
static void __KernelSetSemaTimeout(PSPSemaphore *s, u32 timeoutPtr) {
if (timeoutPtr == 0 || semaWaitTimer == -1)
return;
@@ -378,7 +374,7 @@ static int __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, bool pro
hleEatCycles(500);
u32 error;
Semaphore *s = kernelObjects.Get<Semaphore>(id, error);
PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
if (s)
{
if (wantedCount > s->ns.maxCount)
@@ -438,7 +434,7 @@ int sceKernelPollSema(SceUID id, int wantedCount)
}
u32 error;
Semaphore *s = kernelObjects.Get<Semaphore>(id, error);
PSPSemaphore *s = kernelObjects.Get<PSPSemaphore>(id, error);
if (s)
{
if (s->ns.currentCount >= wantedCount && s->waitingThreads.size() == 0)
File diff suppressed because it is too large. Load diff
+14 -15
View File
@@ -26,7 +26,7 @@
// There's a good description of the thread scheduling rules in:
// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/ThreadManForUser.java
class Thread;
class PSPThread;
class DebugInterface;
int sceKernelChangeThreadPriority(SceUID threadID, int priority);
@@ -119,8 +119,7 @@ typedef void (* WaitEndCallbackFunc)(SceUID threadID, SceUID prevCallbackId);
void __KernelRegisterWaitTypeFuncs(WaitType type, WaitBeginCallbackFunc beginFunc, WaitEndCallbackFunc endFunc);
struct ThreadContext
{
struct PSPThreadContext {
void reset();
// r must be followed by f.
@@ -168,8 +167,8 @@ u32 __KernelGetCurThreadStackStart();
const char *__KernelGetThreadName(SceUID threadID);
bool KernelIsThreadDormant(SceUID threadID);
void __KernelSaveContext(ThreadContext *ctx, bool vfpuEnabled);
void __KernelLoadContext(ThreadContext *ctx, bool vfpuEnabled);
void __KernelSaveContext(PSPThreadContext *ctx, bool vfpuEnabled);
void __KernelLoadContext(PSPThreadContext *ctx, bool vfpuEnabled);
u32 __KernelResumeThreadFromWait(SceUID threadID, u32 retval); // can return an error value
u32 __KernelResumeThreadFromWait(SceUID threadID, u64 retval);
@@ -216,10 +215,10 @@ int sceKernelGetCallbackCount(SceUID cbId);
void sceKernelCheckCallback();
int sceKernelReferCallbackStatus(SceUID cbId, u32 statusAddr);
class Action;
class PSPAction;
// Not an official Callback object, just calls a mips function on the current thread.
void __KernelDirectMipsCall(u32 entryPoint, Action *afterAction, u32 args[], int numargs, bool reschedAfter);
void __KernelDirectMipsCall(u32 entryPoint, PSPAction *afterAction, u32 args[], int numargs, bool reschedAfter);
void __KernelReturnFromMipsCall(); // Called as HLE function
bool __KernelInCallback();
@@ -228,8 +227,8 @@ bool __KernelInCallback();
bool __KernelCheckCallbacks();
bool __KernelForceCallbacks();
bool __KernelCurHasReadyCallbacks();
void __KernelSwitchContext(Thread *target, const char *reason);
bool __KernelExecutePendingMipsCalls(Thread *currentThread, bool reschedAfter);
void __KernelSwitchContext(PSPThread *target, const char *reason);
bool __KernelExecutePendingMipsCalls(PSPThread *currentThread, bool reschedAfter);
void __KernelNotifyCallback(SceUID cbId, int notifyArg);
// Switch to an idle / non-user thread, if not already on one.
@@ -243,8 +242,8 @@ u32 __KernelSetThreadRA(SceUID threadID, u32 nid);
// A call into game code. These can be pending on a thread.
// Similar to Callback-s (NOT CallbackInfos) in JPCSP.
typedef Action *(*ActionCreator)();
Action *__KernelCreateAction(int actionType);
typedef PSPAction *(*ActionCreator)();
PSPAction *__KernelCreateAction(int actionType);
int __KernelRegisterActionType(ActionCreator creator);
void __KernelRestoreActionType(int actionType, ActionCreator creator);
@@ -258,7 +257,7 @@ struct MipsCall {
u32 cbId;
u32 args[6];
int numArgs;
Action *doAfter;
PSPAction *doAfter;
u32 savedPc;
u32 savedV0;
u32 savedV1;
@@ -279,10 +278,10 @@ struct MipsCall {
}
};
class Action
class PSPAction
{
public:
virtual ~Action() {}
virtual ~PSPAction() {}
virtual void run(MipsCall &call) = 0;
virtual void DoState(PointerWrap &p) = 0;
int actionTypeID;
@@ -300,7 +299,7 @@ enum ThreadStatus
THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND
};
void __KernelChangeThreadState(Thread *thread, ThreadStatus newStatus);
void __KernelChangeThreadState(PSPThread *thread, ThreadStatus newStatus);
typedef void (*ThreadCallback)(SceUID threadID);
void __KernelListenThreadEnd(ThreadCallback callback);
+2 -2
View File
@@ -355,11 +355,11 @@ static void AnalyzeMpeg(u8 *buffer, u32 validSize, MpegContext *ctx) {
INFO_LOG(ME, "First timestamp: %lld, Last timestamp: %lld", ctx->mpegFirstTimestamp, ctx->mpegLastTimestamp);
}
class PostPutAction : public Action {
class PostPutAction : public PSPAction {
public:
PostPutAction() {}
void setRingAddr(u32 ringAddr) { ringAddr_ = ringAddr; }
static Action *Create() { return new PostPutAction; }
static PSPAction *Create() { return new PostPutAction; }
void DoState(PointerWrap &p) override {
auto s = p.Section("PostPutAction", 1);
if (!s)
+80 -18
View File
@@ -21,6 +21,7 @@
#include "Common/ChunkFile.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/MIPS/MIPS.h"
#include "Core/Config.h"
#include "Core/MemMapHelpers.h"
@@ -40,6 +41,9 @@ static bool netInetInited;
static bool netApctlInited;
u32 netDropRate = 0;
u32 netDropDuration = 0;
u32 netPoolAddr = 0;
u32 netThread1Addr = 0;
u32 netThread2Addr = 0;
static struct SceNetMallocStat netMallocStat;
@@ -78,7 +82,7 @@ static void __UpdateApctlHandlers(int oldState, int newState, int flag, int erro
// This feels like a dubious proposition, mostly...
void __NetDoState(PointerWrap &p) {
auto s = p.Section("sceNet", 1, 2);
auto s = p.Section("sceNet", 1, 3);
if (!s)
return;
@@ -94,6 +98,28 @@ void __NetDoState(PointerWrap &p) {
p.Do(netDropRate);
p.Do(netDropDuration);
}
if (s < 3) {
netPoolAddr = 0;
netThread1Addr = 0;
netThread2Addr = 0;
} else {
p.Do(netPoolAddr);
p.Do(netThread1Addr);
p.Do(netThread2Addr);
}
}
static inline u32 AllocUser(u32 size, bool fromTop, const char *name) {
u32 addr = userMemory.Alloc(size, true, "netstack1");
if (addr == -1)
return 0;
return addr;
}
static inline void FreeUser(u32 &addr) {
if (addr != 0)
userMemory.Free(addr);
addr = 0;
}
static u32 sceNetTerm() {
@@ -103,35 +129,73 @@ static u32 sceNetTerm() {
WARN_LOG(SCENET, "sceNetTerm()");
netInited = false;
FreeUser(netPoolAddr);
FreeUser(netThread1Addr);
FreeUser(netThread2Addr);
return 0;
}
// TODO: should that struct actually be initialized here?
static u32 sceNetInit(u32 poolSize, u32 calloutPri, u32 calloutStack, u32 netinitPri, u32 netinitStack) {
// May need to Terminate old one first since the game (ie. GTA:VCS) might not called sceNetTerm before the next sceNetInit and behave strangely
if (netInited)
static int sceNetInit(u32 poolSize, u32 calloutPri, u32 calloutStack, u32 netinitPri, u32 netinitStack) {
// TODO: The correct behavior is actually to allocate more and leak the other threads/pool.
// But we reset here for historic reasons (GTA:VCS potentially triggers this.)
if (netInited)
sceNetTerm();
if (poolSize == 0) {
return hleLogError(SCENET, SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE, "invalid pool size");
} else if (calloutPri < 0x08 || calloutPri > 0x77) {
return hleLogError(SCENET, SCE_KERNEL_ERROR_ILLEGAL_PRIORITY, "invalid callout thread priority");
} else if (netinitPri < 0x08 || netinitPri > 0x77) {
return hleLogError(SCENET, SCE_KERNEL_ERROR_ILLEGAL_PRIORITY, "invalid init thread priority");
}
// TODO: Should also start the threads, probably? For now, let's just allocate.
// TODO: Respect the stack size if firmware set to 1.50?
u32 stackSize = 4096;
netThread1Addr = AllocUser(stackSize, true, "netstack1");
if (netThread1Addr == 0) {
return hleLogError(SCENET, SCE_KERNEL_ERROR_NO_MEMORY, "unable to allocate thread");
}
netThread2Addr = AllocUser(stackSize, true, "netstack2");
if (netThread2Addr == 0) {
FreeUser(netThread1Addr);
return hleLogError(SCENET, SCE_KERNEL_ERROR_NO_MEMORY, "unable to allocate thread");
}
netPoolAddr = AllocUser(poolSize, false, "netpool");
if (netPoolAddr == 0) {
FreeUser(netThread1Addr);
FreeUser(netThread2Addr);
return hleLogError(SCENET, SCE_KERNEL_ERROR_NO_MEMORY, "unable to allocate pool");
}
WARN_LOG(SCENET, "sceNetInit(poolsize=%d, calloutpri=%i, calloutstack=%d, netintrpri=%i, netintrstack=%d) at %08x", poolSize, calloutPri, calloutStack, netinitPri, netinitStack, currentMIPS->pc);
netInited = true;
netMallocStat.maximum = poolSize;
netMallocStat.free = poolSize;
netMallocStat.pool = 0;
return 0;
return hleLogSuccessI(SCENET, 0);
}
static u32 sceWlanGetEtherAddr(u32 addrAddr) {
// Read MAC Address from config
uint8_t mac[6] = {0};
if (!ParseMacAddress(g_Config.sMACAddress.c_str(), mac)) {
ERROR_LOG(SCENET, "Error parsing mac address %s", g_Config.sMACAddress.c_str());
if (!Memory::IsValidRange(addrAddr, 6)) {
// More correctly, it should crash.
return hleLogError(SCENET, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "illegal address");
}
DEBUG_LOG(SCENET, "sceWlanGetEtherAddr(%08x)", addrAddr);
for (int i = 0; i < 6; i++)
Memory::Write_U8(mac[i], addrAddr + i);
return 0;
u8 *addr = Memory::GetPointer(addrAddr);
// Read MAC Address from config
if (!ParseMacAddress(g_Config.sMACAddress.c_str(), addr)) {
ERROR_LOG(SCENET, "Error parsing mac address %s", g_Config.sMACAddress.c_str());
Memory::Memset(addrAddr, 0, 6);
} else {
CBreakPoints::ExecMemCheck(addrAddr, true, 6, currentMIPS->pc);
}
return hleLogSuccessI(SCENET, hleDelayResult(0, "get ether mac", 200));
}
static u32 sceNetGetLocalEtherAddr(u32 addrAddr) {
@@ -139,13 +203,11 @@ static u32 sceNetGetLocalEtherAddr(u32 addrAddr) {
}
static u32 sceWlanDevIsPowerOn() {
DEBUG_LOG(SCENET, "UNTESTED sceWlanDevIsPowerOn()");
return g_Config.bEnableWlan ? 1 : 0;
return hleLogSuccessVerboseI(SCENET, g_Config.bEnableWlan ? 1 : 0);
}
static u32 sceWlanGetSwitchState() {
VERBOSE_LOG(SCENET, "sceWlanGetSwitchState()");
return g_Config.bEnableWlan ? 1 : 0;
return hleLogSuccessVerboseI(SCENET, g_Config.bEnableWlan ? 1 : 0);
}
// Probably a void function, but often returns a useful value.
@@ -468,7 +530,7 @@ static int sceNetSetDropRate(u32 dropRate, u32 dropDuration)
}
const HLEFunction sceNet[] = {
{0X39AF39A6, &WrapU_UUUUU<sceNetInit>, "sceNetInit", 'x', "xxxxx"},
{0X39AF39A6, &WrapI_UUUUU<sceNetInit>, "sceNetInit", 'i', "xxxxx"},
{0X281928A9, &WrapU_V<sceNetTerm>, "sceNetTerm", 'x', "" },
{0X89360950, &WrapI_UU<sceNetEtherNtostr>, "sceNetEtherNtostr", 'i', "xx" },
{0XD27961C9, &WrapI_UU<sceNetEtherStrton>, "sceNetEtherStrton", 'i', "xx" },
+8 -10
View File
@@ -160,8 +160,6 @@ void __NetAdhocInit() {
}
u32 sceNetAdhocInit() {
// Library uninitialized
INFO_LOG(SCENET, "sceNetAdhocInit() at %08x", currentMIPS->pc);
if (!netAdhocInited) {
// Clear Translator Memory
memset(&pdp, 0, sizeof(pdp));
@@ -173,17 +171,17 @@ u32 sceNetAdhocInit() {
// Create fake PSP Thread for callback
// TODO: Should use a separated threads for friendFinder, matchingEvent, and matchingInput and created on AdhocctlInit & AdhocMatchingStart instead of here
#define PSP_THREAD_ATTR_KERNEL 0x00001000 // PSP_THREAD_ATTR_KERNEL is located in sceKernelThread.cpp instead of sceKernelThread.h :(
//threadAdhocID = __KernelCreateThreadInternal("AdhocThread", __KernelGetCurThreadModuleId(), dummyThreadHackAddr, 0x30, 4096, PSP_THREAD_ATTR_KERNEL);
threadAdhocID = __KernelCreateThread("AdhocThread", __KernelGetCurThreadModuleId(), dummyThreadHackAddr, 0x10, 0x1000, 0, 0, false);
// TODO: This should probably be a user thread, but maybe from sceNetAdhocctlInit?
threadAdhocID = __KernelCreateThread("AdhocThread", __KernelGetCurThreadModuleId(), dummyThreadHackAddr, 0x10, 0x1000, PSP_THREAD_ATTR_KERNEL, 0, true);
if (threadAdhocID > 0) {
__KernelStartThread(threadAdhocID, 0, 0);
}
// Return Success
return 0;
return hleLogSuccessInfoI(SCENET, 0, "at %08x", currentMIPS->pc);
}
// Already initialized
return ERROR_NET_ADHOC_ALREADY_INITIALIZED;
return hleLogWarning(SCENET, ERROR_NET_ADHOC_ALREADY_INITIALIZED, "already initialized");
}
static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) {
@@ -1419,7 +1417,6 @@ int sceNetAdhocctlCreateEnterGameModeMin(const char *group_name, int game_type,
}
int sceNetAdhocTerm() {
INFO_LOG(SCENET, "sceNetAdhocTerm()");
// WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup all the sockets right?
if (netAdhocctlInited) sceNetAdhocctlTerm();
@@ -1448,10 +1445,11 @@ int sceNetAdhocTerm() {
// if (_manage_modules != 0) sceUtilityUnloadModule(PSP_MODULE_NET_INET);
// Library shutdown
netAdhocInited = false;
return 0;
return hleLogSuccessInfoI(SCENET, 0);
} else {
// Seems to return this when called a second time after being terminated without another initialisation
return SCE_KERNEL_ERROR_LWMUTEX_NOT_FOUND;
// TODO: Reportedly returns SCE_KERNEL_ERROR_LWMUTEX_NOT_FOUND in some cases?
// Only seen returning 0 in tests.
return hleLogWarning(SCENET, 0, "already uninitialized");
}
}
+11 -4
View File
@@ -37,6 +37,13 @@
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceRtc.h"
#ifdef HAVE_LIBNX
// I guess that works...
#define setenv(x, y, z) (void*)0
#define tzset() (void*)0
#define unsetenv(x) (void*)0
#endif // HAVE_LIBNX
// This is a base time that everything is relative to.
// This way, time doesn't move strangely with savestates, turbo speed, etc.
static PSPTimeval rtcBaseTime;
@@ -494,7 +501,7 @@ static int sceRtcConvertLocalTimeToUTC(u32 tickLocalPtr,u32 tickUTCPtr)
long timezone_val;
_get_timezone(&timezone_val);
srcTick -= -timezone_val * 1000000ULL;
#elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux)
#elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux) && !defined(HAVE_LIBNX)
time_t timezone = 0;
tm *time = localtime(&timezone);
srcTick -= time->tm_gmtoff*1000000ULL;
@@ -519,7 +526,7 @@ static int sceRtcConvertUtcToLocalTime(u32 tickUTCPtr,u32 tickLocalPtr)
long timezone_val;
_get_timezone(&timezone_val);
srcTick += -timezone_val * 1000000ULL;
#elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux)
#elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux) && !defined(HAVE_LIBNX)
time_t timezone = 0;
tm *time = localtime(&timezone);
srcTick += time->tm_gmtoff*1000000ULL;
@@ -1054,7 +1061,7 @@ static int sceRtcFormatRFC2822LocalTime(u32 outPtr, u32 srcTickPtr)
long timezone_val;
_get_timezone(&timezone_val);
tz_seconds = -timezone_val;
#elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux)
#elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux) && !defined(HAVE_LIBNX)
time_t timezone = 0;
tm *time = localtime(&timezone);
tz_seconds = time->tm_gmtoff;
@@ -1091,7 +1098,7 @@ static int sceRtcFormatRFC3339LocalTime(u32 outPtr, u32 srcTickPtr)
long timezone_val;
_get_timezone(&timezone_val);
tz_seconds = -timezone_val;
#elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux)
#elif !defined(_AIX) && !defined(__sgi) && !defined(__hpux) && !defined(HAVE_LIBNX)
time_t timezone = 0;
tm *time = localtime(&timezone);
tz_seconds = time->tm_gmtoff;
+37 -34
View File
@@ -470,55 +470,46 @@ static int sceUtilityOskGetStatus()
}
static int sceUtilityNetconfInitStart(u32 paramsAddr)
{
static int sceUtilityNetconfInitStart(u32 paramsAddr) {
if (currentDialogActive && currentDialogType != UTILITY_DIALOG_NET) {
WARN_LOG(SCEUTILITY, "sceUtilityNetconfInitStart(%08x): wrong dialog type", paramsAddr);
return SCE_ERROR_UTILITY_WRONG_TYPE;
return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type");
}
oldStatus = 100;
currentDialogType = UTILITY_DIALOG_NET;
currentDialogActive = true;
int ret = netDialog.Init(paramsAddr);
INFO_LOG(SCEUTILITY, "%08x=sceUtilityNetconfInitStart(%08x)", ret, paramsAddr);
return ret;
currentDialogActive = true;
return hleLogSuccessInfoI(SCEUTILITY, netDialog.Init(paramsAddr));
}
static int sceUtilityNetconfShutdownStart()
{
static int sceUtilityNetconfShutdownStart() {
if (currentDialogType != UTILITY_DIALOG_NET) {
WARN_LOG(SCEUTILITY, "sceUtilityNetconfShutdownStart(): wrong dialog type");
return SCE_ERROR_UTILITY_WRONG_TYPE;
return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type");
}
currentDialogActive = false;
int ret = netDialog.Shutdown();
DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityNetconfShutdownStart()",ret);
return ret;
return hleLogSuccessI(SCEUTILITY, netDialog.Shutdown());
}
static int sceUtilityNetconfUpdate(int animSpeed)
{
int ret = netDialog.Update(animSpeed);
ERROR_LOG(SCEUTILITY, "UNIMPL %08x=sceUtilityNetconfUpdate(%i)", ret, animSpeed);
return ret;
}
static int sceUtilityNetconfGetStatus()
{
// Spam in Danball Senki BOOST
static int sceUtilityNetconfUpdate(int animSpeed) {
if (currentDialogType != UTILITY_DIALOG_NET) {
DEBUG_LOG(SCEUTILITY, "sceUtilityNetconfGetStatus(): wrong dialog type");
return SCE_ERROR_UTILITY_WRONG_TYPE;
return hleLogWarning(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type");
}
return hleLogSuccessI(SCEUTILITY, netDialog.Update(animSpeed));
}
static int sceUtilityNetconfGetStatus() {
if (currentDialogType != UTILITY_DIALOG_NET) {
// Spam in Danball Senki BOOST.
return hleLogDebug(SCEUTILITY, SCE_ERROR_UTILITY_WRONG_TYPE, "wrong dialog type");
}
int status = netDialog.GetStatus();
if (oldStatus != status) {
oldStatus = status;
DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityNetconfGetStatus()", status);
return hleLogSuccessI(SCEUTILITY, status);
}
return status;
return hleLogSuccessVerboseI(SCEUTILITY, status);
}
static int sceUtilityCheckNetParam(int id)
@@ -769,6 +760,18 @@ static u32 sceUtilityUnloadNetModule(u32 module)
return 0;
}
static int sceUtilityNpSigninInitStart(u32 paramsPtr) {
return hleLogError(SCEUTILITY, 0, "not implemented");
}
static int sceUtilityNpSigninUpdate(int animSpeed) {
return hleLogError(SCEUTILITY, 0, "not implemented");
}
static int sceUtilityNpSigninGetStatus() {
return hleLogError(SCEUTILITY, 0, "not implemented");
}
static void sceUtilityInstallInitStart(u32 unknown)
{
WARN_LOG_REPORT(SCEUTILITY, "UNIMPL sceUtilityInstallInitStart()");
@@ -934,7 +937,7 @@ const HLEFunction sceUtility[] =
{0X0251B134, &WrapI_U<sceUtilityScreenshotInitStart>, "sceUtilityScreenshotInitStart", 'i', "x" },
{0XF9E0008C, &WrapI_V<sceUtilityScreenshotShutdownStart>, "sceUtilityScreenshotShutdownStart", 'i', "" },
{0XAB083EA9, &WrapI_U<sceUtilityScreenshotUpdate>, "sceUtilityScreenshotUpdate", 'i', "x" },
{0XAB083EA9, &WrapI_U<sceUtilityScreenshotUpdate>, "sceUtilityScreenshotUpdate", 'i', "i" },
{0XD81957B7, &WrapI_V<sceUtilityScreenshotGetStatus>, "sceUtilityScreenshotGetStatus", 'i', "" },
{0X86A03A27, &WrapI_U<sceUtilityScreenshotContStart>, "sceUtilityScreenshotContStart", 'i', "x" },
@@ -947,10 +950,10 @@ const HLEFunction sceUtility[] =
{0XB57E95D9, &WrapI_V<sceUtilityGamedataInstallGetStatus>, "sceUtilityGamedataInstallGetStatus", 'i', "" },
{0X180F7B62, &WrapI_V<sceUtilityGamedataInstallAbort>, "sceUtilityGamedataInstallAbort", 'i', "" },
{0X16D02AF0, nullptr, "sceUtilityNpSigninInitStart", '?', "" },
{0XE19C97D6, nullptr, "sceUtilityNpSigninShutdownStart", '?', "" },
{0XF3FBC572, nullptr, "sceUtilityNpSigninUpdate", '?', "" },
{0X86ABDB1B, nullptr, "sceUtilityNpSigninGetStatus", '?', "" },
{0X16D02AF0, &WrapI_U<sceUtilityNpSigninInitStart>, "sceUtilityNpSigninInitStart", 'i', "x" },
{0XE19C97D6, nullptr, "sceUtilityNpSigninShutdownStart", 'i', "" },
{0XF3FBC572, &WrapI_I<sceUtilityNpSigninUpdate>, "sceUtilityNpSigninUpdate", 'i', "i" },
{0X86ABDB1B, &WrapI_V<sceUtilityNpSigninGetStatus>, "sceUtilityNpSigninGetStatus", 'i', "" },
{0X1281DA8E, &WrapV_U<sceUtilityInstallInitStart>, "sceUtilityInstallInitStart", 'v', "x" },
{0X5EF1C24A, nullptr, "sceUtilityInstallShutdownStart", '?', "" },
+4 -4
View File
@@ -338,10 +338,10 @@ int __v4l_startCapture(int ideal_width, int ideal_height) {
frmsize.index++;
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
INFO_LOG(HLE, "V4L2: frame size supported: %dx%d", frmsize.discrete.width, frmsize.discrete.height);
if (frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height
&& fmt.fmt.pix.width == 0 && fmt.fmt.pix.height == 0
|| frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height
&& frmsize.discrete.width < fmt.fmt.pix.width && frmsize.discrete.height < fmt.fmt.pix.height) {
bool matchesIdeal = frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height;
bool zeroPix = fmt.fmt.pix.width == 0 && fmt.fmt.pix.height == 0;
bool pixLarger = frmsize.discrete.width < fmt.fmt.pix.width && frmsize.discrete.height < fmt.fmt.pix.height;
if (matchesIdeal && (zeroPix || pixLarger)) {
fmt.fmt.pix.width = frmsize.discrete.width;
fmt.fmt.pix.height = frmsize.discrete.height;
}
+269 -24
View File
@@ -20,6 +20,7 @@
#include "base/stringutil.h"
#include "file/vfs.h"
#include "gfx/texture_atlas.h"
#include "gfx_es2/draw_text.h"
#include "image/zim_load.h"
#include "image/png_load.h"
#include "util/text/utf8.h"
@@ -103,6 +104,41 @@ static AtlasCharLine char_one_line;
static AtlasLineArray char_lines;
static AtlasTextMetrics char_lines_metrics;
static bool textDrawerInited = false;
static TextDrawer *textDrawer = nullptr;
struct PPGeTextDrawerCacheKey {
bool operator < (const PPGeTextDrawerCacheKey &other) const {
if (align != other.align)
return align < other.align;
if (wrapWidth != other.wrapWidth)
return wrapWidth < other.wrapWidth;
return text < other.text;
}
std::string text;
int align;
float wrapWidth;
};
struct PPGeTextDrawerImage {
TextStringEntry entry;
u32 ptr;
};
std::map<PPGeTextDrawerCacheKey, PPGeTextDrawerImage> textDrawerImages;
// Overwrite the current text lines buffer so it can be drawn later.
void PPGePrepareText(const char *text, float x, float y, int align, float scale, float lineHeightScale,
int WrapType = PPGE_LINE_NONE, int wrapWidth = 0);
// Get the metrics of the bounding box of the currently stated text.
void PPGeMeasureCurrentText(float *x, float *y, float *w, float *h, int *n);
// These functions must be called between PPGeBegin and PPGeEnd.
// Draw currently buffered text using the state from PPGeGetTextBoundingBox() call.
// Clears the buffer and state when done.
void PPGeDrawCurrentText(u32 color = 0xFFFFFFFF);
void PPGeSetTexture(u32 dataAddr, int width, int height);
//only 0xFFFFFF of data is used
static void WriteCmd(u8 cmd, u32 data) {
Memory::Write_U32((cmd << 24) | (data & 0xFFFFFF), dlWritePtr);
@@ -129,18 +165,18 @@ static void BeginVertexData() {
vertexStart = dataWritePtr;
}
static void Vertex(float x, float y, float u, float v, int tw, int th, u32 color = 0xFFFFFFFF) {
static void Vertex(float x, float y, float u, float v, int tw, int th, u32 color = 0xFFFFFFFF, float off = -0.5f) {
if (g_RemasterMode) {
PPGeRemasterVertex vtx;
vtx.x = x - 0.5f; vtx.y = y - 0.5f; vtx.z = 0;
vtx.u = u * tw - 0.5f; vtx.v = v * th - 0.5f;
vtx.x = x + off; vtx.y = y + off; vtx.z = 0;
vtx.u = u * tw + off; vtx.v = v * th + off;
vtx.color = color;
Memory::WriteStruct(dataWritePtr, &vtx);
dataWritePtr += sizeof(vtx);
} else {
PPGeVertex vtx;
vtx.x = x - 0.5f; vtx.y = y - 0.5f; vtx.z = 0;
vtx.u = u * tw - 0.5f; vtx.v = v * th - 0.5f;
vtx.x = x + off; vtx.y = y + off; vtx.z = 0;
vtx.u = u * tw + off; vtx.v = v * th + off;
vtx.color = color;
Memory::WriteStruct(dataWritePtr, &vtx);
dataWritePtr += sizeof(vtx);
@@ -232,13 +268,18 @@ void __PPGeInit()
free(imageData[0]);
// We can't create it here, because Android needs it on the right thread.
textDrawerInited = false;
textDrawer = nullptr;
textDrawerImages.clear();
DEBUG_LOG(SCEGE, "PPGe drawing library initialized. DL: %08x Data: %08x Atlas: %08x (%i) Args: %08x",
dlPtr, dataPtr, atlasPtr, atlasSize, listArgs.ptr);
}
void __PPGeDoState(PointerWrap &p)
{
auto s = p.Section("PPGeDraw", 1, 2);
auto s = p.Section("PPGeDraw", 1, 3);
if (!s)
return;
@@ -256,6 +297,30 @@ void __PPGeDoState(PointerWrap &p)
p.Do(listArgs);
}
if (s >= 3) {
uint32_t sz = (uint32_t)textDrawerImages.size();
p.Do(sz);
switch (p.mode) {
case PointerWrap::MODE_READ:
textDrawerImages.clear();
for (uint32_t i = 0; i < sz; ++i) {
// We only care about the pointers, so we can free them. We'll decimate right away.
PPGeTextDrawerCacheKey key{ StringFromFormat("__savestate__%d", i), -1, -1 };
textDrawerImages[key] = PPGeTextDrawerImage{};
p.Do(textDrawerImages[key].ptr);
}
break;
default:
for (const auto &im : textDrawerImages) {
p.Do(im.second.ptr);
}
break;
}
} else {
textDrawerImages.clear();
}
p.Do(dlPtr);
p.Do(dlWritePtr);
p.Do(dlSize);
@@ -291,6 +356,9 @@ void __PPGeShutdown()
dlPtr = 0;
savedContextPtr = 0;
listArgs = 0;
delete textDrawer;
textDrawer = nullptr;
}
void PPGeBegin()
@@ -625,9 +693,51 @@ static AtlasTextMetrics BreakLines(const char *text, const AtlasFont &atlasfont,
return metrics;
}
static bool HasTextDrawer() {
// We create this on first use so it's on the correct thread.
if (textDrawerInited) {
return textDrawer != nullptr;
}
// TODO: Should we pass a draw_?
textDrawer = TextDrawer::Create(nullptr);
if (textDrawer) {
textDrawer->SetFontScale(1.0f, 1.0f);
textDrawer->SetForcedDPIScale(1.0f);
textDrawer->SetFont(g_Config.sFont.c_str(), 20, 0);
}
textDrawerInited = true;
return textDrawer != nullptr;
}
void PPGeMeasureText(float *w, float *h, int *n,
const char *text, float scale, int WrapType, int wrapWidth)
{
if (HasTextDrawer()) {
float mw, mh;
textDrawer->SetFontScale(scale, scale);
int dtalign = (WrapType & PPGE_LINE_WRAP_WORD) ? FLAG_WRAP_TEXT : 0;
if (WrapType & PPGE_LINE_USE_ELLIPSIS)
dtalign |= FLAG_ELLIPSIZE_TEXT;
Bounds b(0, 0, wrapWidth <= 0 ? 480.0f : wrapWidth, 272.0f);
textDrawer->MeasureStringRect(text, strlen(text), b, &mw, &mh, dtalign);
if (w)
*w = mw;
if (h)
*h = mh;
if (n) {
// Cheap way to get the n.
float oneLine, twoLines;
textDrawer->MeasureString("|", 1, &mw, &oneLine);
textDrawer->MeasureStringRect("|\n|", 3, Bounds(0, 0, 480, 272), &mw, &twoLines);
float lineHeight = twoLines - oneLine;
*n = (int)((mh + (lineHeight - 1)) / lineHeight);
}
return;
}
const AtlasFont &atlasfont = g_ppge_atlas.fonts[0];
AtlasTextMetrics metrics = BreakLines(text, atlasfont, 0, 0, 0, scale, scale, WrapType, wrapWidth, true);
if (w) *w = metrics.maxWidth;
@@ -683,8 +793,110 @@ void PPGeDrawCurrentText(u32 color)
PPGeResetCurrentText();
}
void PPGeDrawText(const char *text, float x, float y, int align, float scale, u32 color)
{
// Return a value such that (1 << value) >= x
int GetPow2(int x) {
#ifdef __GNUC__
int ret = 31 - __builtin_clz(x | 1);
if ((1 << ret) < x)
#else
int ret = 0;
while ((1 << ret) < x)
#endif
ret++;
return ret;
}
static PPGeTextDrawerImage PPGeGetTextImage(const char *text, int align, float scale, float maxWidth, bool wrap) {
int tdalign = (align & PPGE_ALIGN_HCENTER) ? ALIGN_HCENTER : 0;
tdalign |= FLAG_ELLIPSIZE_TEXT;
if (wrap) {
tdalign |= FLAG_WRAP_TEXT;
}
PPGeTextDrawerCacheKey key{ text, tdalign, maxWidth / scale };
PPGeTextDrawerImage im;
auto cacheItem = textDrawerImages.find(key);
if (cacheItem != textDrawerImages.end()) {
im = cacheItem->second;
cacheItem->second.entry.lastUsedFrame = gpuStats.numFlips;
} else {
std::vector<uint8_t> bitmapData;
textDrawer->SetFontScale(scale, scale);
Bounds b(0, 0, maxWidth, 272.0f);
std::string cleaned = ReplaceAll(text, "\r", "");
textDrawer->DrawStringBitmapRect(bitmapData, im.entry, Draw::DataFormat::R8_UNORM, cleaned.c_str(), b, tdalign);
int bufwBytes = ((im.entry.bmWidth + 31) / 32) * 16;
u32 sz = bufwBytes * (im.entry.bmHeight + 1);
u32 origSz = sz;
im.ptr = __PPGeDoAlloc(sz, true, "PPGeText");
if (bitmapData.size() & 1)
bitmapData.resize(bitmapData.size() + 1);
if (im.ptr) {
int wBytes = (im.entry.bmWidth + 1) / 2;
u8 *ramPtr = (u8 *)Memory::GetPointer(im.ptr);
for (int y = 0; y < im.entry.bmHeight; ++y) {
for (int x = 0; x < wBytes; ++x) {
uint8_t c1 = bitmapData[y * im.entry.bmWidth + x * 2];
uint8_t c2 = bitmapData[y * im.entry.bmWidth + x * 2 + 1];
// Convert this to 4-bit palette values.
ramPtr[y * bufwBytes + x] = (c2 & 0xF0) | (c1 >> 4);
}
if (bufwBytes != wBytes) {
memset(ramPtr + y * bufwBytes + wBytes, 0, bufwBytes - wBytes);
}
}
memset(ramPtr + im.entry.bmHeight * bufwBytes, 0, bufwBytes + sz - origSz);
}
im.entry.lastUsedFrame = gpuStats.numFlips;
textDrawerImages[key] = im;
}
return im;
}
static void PPGeDrawTextImage(PPGeTextDrawerImage im, float x, float y, int align, float scale, u32 color) {
int bufw = ((im.entry.bmWidth + 31) / 32) * 32;
int wp2 = GetPow2(im.entry.bmWidth);
int hp2 = GetPow2(im.entry.bmHeight);
WriteCmd(GE_CMD_TEXADDR0, im.ptr & 0xFFFFF0);
WriteCmd(GE_CMD_TEXBUFWIDTH0, bufw | ((im.ptr & 0xFF000000) >> 8));
WriteCmd(GE_CMD_TEXSIZE0, wp2 | (hp2 << 8));
WriteCmd(GE_CMD_TEXFLUSH, 0);
float w = im.entry.width * scale;
float h = im.entry.height * scale;
if (align & PPGE_ALIGN_HCENTER)
x -= w / 2.0f;
else if (align & PPGE_ALIGN_RIGHT)
x -= w;
if (align & PPGE_ALIGN_VCENTER)
y -= h / 2.0f;
else if (align & PPGE_ALIGN_BOTTOM)
y -= h;
BeginVertexData();
float u1 = (float)im.entry.width / (1 << wp2);
float v1 = (float)im.entry.height / (1 << hp2);
Vertex(x, y, 0, 0, 1 << wp2, 1 << hp2, color, 0.0f);
Vertex(x + w, y + h, u1, v1, 1 << wp2, 1 << hp2, color, 0.0f);
EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
PPGeSetDefaultTexture();
}
void PPGeDrawText(const char *text, float x, float y, int align, float scale, u32 color) {
if (HasTextDrawer()) {
PPGeTextDrawerImage im = PPGeGetTextImage(text, align, scale, 480.0f - x, false);
PPGeDrawTextImage(im, x, y, align, scale, color);
return;
}
PPGePrepareText(text, x, y, align, scale, scale, PPGE_LINE_USE_ELLIPSIS);
PPGeDrawCurrentText(color);
}
@@ -718,10 +930,40 @@ void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, fl
s = StripTrailingWhite(s);
}
PPGePrepareText(s.c_str(), x, y, align, scale, scale, PPGE_LINE_USE_ELLIPSIS | PPGE_LINE_WRAP_WORD, wrapWidth);
int zoom = (PSP_CoreParameter().pixelHeight + 479) / 480;
float maxScaleDown = zoom == 1 ? 1.3f : 2.0f;
if (HasTextDrawer()) {
float actualWidth, actualHeight;
Bounds b(0, 0, wrapWidth <= 0 ? 480.0f - x : wrapWidth, wrapHeight);
int tdalign = (align & PPGE_ALIGN_HCENTER) ? ALIGN_HCENTER : 0;
textDrawer->SetFontScale(scale, scale);
textDrawer->MeasureStringRect(s.c_str(), s.size(), b, &actualWidth, &actualHeight, tdalign | FLAG_WRAP_TEXT);
if (wrapHeight != 0.0f && actualHeight > wrapHeight) {
// Cheap way to get the line height.
float oneLine, twoLines;
textDrawer->MeasureString("|", 1, &actualWidth, &oneLine);
textDrawer->MeasureStringRect("|\n|", 3, Bounds(0, 0, 480, 272), &actualWidth, &twoLines);
float lineHeight = twoLines - oneLine;
if (actualHeight > wrapHeight * maxScaleDown) {
float maxLines = floor(wrapHeight * maxScaleDown / lineHeight);
actualHeight = (maxLines + 1) * lineHeight;
// Add an ellipsis if it's just too long to be readable.
// On a PSP, it does this without scaling it down.
s = StripTrailingWhite(CropLinesToCount(s, (int)maxLines)) + "\n...";
}
scale *= wrapHeight / actualHeight;
}
PPGeTextDrawerImage im = PPGeGetTextImage(s.c_str(), align, scale, wrapWidth <= 0 ? 480.0f - x : wrapWidth, true);
PPGeDrawTextImage(im, x, y, align, scale, color);
return;
}
PPGePrepareText(s.c_str(), x, y, align, scale, scale, PPGE_LINE_USE_ELLIPSIS | PPGE_LINE_WRAP_WORD, wrapWidth);
float actualHeight = char_lines_metrics.lineHeight * char_lines_metrics.numLines;
if (wrapHeight != 0.0f && actualHeight > wrapHeight) {
if (actualHeight > wrapHeight * maxScaleDown) {
@@ -837,20 +1079,6 @@ void PPGeDrawImage(float x, float y, float w, float h, float u1, float v1, float
EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
}
// Return a value such that (1 << value) >= x
int GetPow2(int x)
{
#ifdef __GNUC__
int ret = 31 - __builtin_clz(x|1);
if ((1 << ret) < x)
#else
int ret = 0;
while ((1 << ret) < x)
#endif
ret++;
return ret;
}
void PPGeSetDefaultTexture()
{
WriteCmd(GE_CMD_TEXTUREMAPENABLE, 1);
@@ -1004,3 +1232,20 @@ void PPGeImage::SetTexture() {
}
}
void PPGeNotifyFrame() {
if (textDrawer) {
textDrawer->OncePerFrame();
}
// Do this always, in case the platform has no TextDrawer but save state did.
for (auto it = textDrawerImages.begin(); it != textDrawerImages.end(); ) {
if (it->second.entry.lastUsedFrame - gpuStats.numFlips >= 97) {
kernelMemory.Free(it->second.ptr);
it = textDrawerImages.erase(it);
} else {
++it;
}
}
PPGeImage::Decimate();
}
+4 -24
View File
@@ -45,10 +45,6 @@ void __PPGeShutdown();
void PPGeBegin();
void PPGeEnd();
// If you want to draw using this texture but not go through the PSP GE emulation,
// jsut call this. Will bind the texture to unit 0.
void PPGeBindTexture();
enum {
PPGE_ALIGN_LEFT = 0,
PPGE_ALIGN_RIGHT = 16,
@@ -65,11 +61,6 @@ enum {
PPGE_ALIGN_BOTTOMRIGHT = PPGE_ALIGN_BOTTOM | PPGE_ALIGN_RIGHT,
};
enum {
PPGE_ESCAPE_NONE,
PPGE_ESCAPE_BACKSLASHED,
};
enum {
PPGE_LINE_NONE = 0,
PPGE_LINE_USE_ELLIPSIS = 1, // use ellipses in too long words
@@ -81,19 +72,6 @@ enum {
void PPGeMeasureText(float *w, float *h, int *n,
const char *text, float scale, int WrapType = PPGE_LINE_NONE, int wrapWidth = 0);
// Overwrite the current text lines buffer so it can be drawn later.
void PPGePrepareText(const char *text, float x, float y, int align, float scale, float lineHeightScale,
int WrapType = PPGE_LINE_NONE, int wrapWidth = 0);
// Get the metrics of the bounding box of the currently stated text.
void PPGeMeasureCurrentText(float *x, float *y, float *w, float *h, int *n);
// These functions must be called between PPGeBegin and PPGeEnd.
// Draw currently buffered text using the state from PPGeGetTextBoundingBox() call.
// Clears the buffer and state when done.
void PPGeDrawCurrentText(u32 color = 0xFFFFFFFF);
// Draws some text using the one font we have.
// Clears the text buffer when done.
void PPGeDrawText(const char *text, float x, float y, int align, float scale = 1.0f, u32 color = 0xFFFFFFFF);
@@ -107,6 +85,8 @@ void PPGeDrawImage(ImageID atlasImage, float x, float y, int align, u32 color =
void PPGeDrawImage(ImageID atlasImage, float x, float y, float w, float h, int align, u32 color = 0xFFFFFFFF);
void PPGeDrawImage(float x, float y, float w, float h, float u1, float v1, float u2, float v2, int tw, int th, u32 color);
void PPGeNotifyFrame();
class PPGeImage {
public:
PPGeImage(const std::string &pspFilename);
@@ -132,8 +112,9 @@ public:
return height_;
}
private:
static void Decimate();
private:
static std::vector<PPGeImage *> loadedTextures_;
std::string filename_;
@@ -152,6 +133,5 @@ private:
void PPGeDrawRect(float x1, float y1, float x2, float y2, u32 color);
void PPGeSetDefaultTexture();
void PPGeSetTexture(u32 dataAddr, int width, int height);
void PPGeDisableTexture();
+2 -2
View File
@@ -1439,9 +1439,9 @@ void ClearRectangle(const VertexData &v0, const VertexData &v1)
DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1(), 0);
DrawingCoords scissorBR(gstate.getScissorX2(), gstate.getScissorY2(), 0);
minX = std::max(minX, (int)TransformUnit::DrawingToScreen(scissorTL).x);
maxX = std::max(0, std::min(maxX, (int)TransformUnit::DrawingToScreen(scissorBR).x));
maxX = std::max(0, std::min(maxX, (int)TransformUnit::DrawingToScreen(scissorBR).x + 16));
minY = std::max(minY, (int)TransformUnit::DrawingToScreen(scissorTL).y);
maxY = std::max(0, std::min(maxY, (int)TransformUnit::DrawingToScreen(scissorBR).y));
maxY = std::max(0, std::min(maxY, (int)TransformUnit::DrawingToScreen(scissorBR).y + 16));
const int w = (maxX - minX) / 16;
if (w <= 0)
+2 -2
View File
@@ -184,8 +184,8 @@ void DrawSprite(const VertexData& v0, const VertexData& v1) {
}
}
} else {
if (pos1.x > scissorBR.x) pos1.x = scissorBR.x;
if (pos1.y > scissorBR.y) pos1.y = scissorBR.y;
if (pos1.x > scissorBR.x) pos1.x = scissorBR.x + 1;
if (pos1.y > scissorBR.y) pos1.y = scissorBR.y + 1;
if (pos0.x < scissorTL.x) pos0.x = scissorTL.x;
if (pos0.y < scissorTL.y) pos0.y = scissorTL.y;
if (!gstate.isStencilTestEnabled() &&
+2 -2
View File
@@ -230,9 +230,9 @@ PermissionStatus System_GetPermissionStatus(SystemPermission permission) { retur
void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function<void(bool, const std::string &)> cb) {
QString text = emugl->InputBoxGetQString(QString::fromStdString(title), QString::fromStdString(defaultValue));
if (text.isEmpty()) {
cb(false, "");
NativeInputBoxReceived(cb, false, "");
} else {
cb(true, text.toStdString());
NativeInputBoxReceived(cb, true, text.toStdString());
}
}
+22 -7
View File
@@ -198,8 +198,9 @@ void OpenDirectory(const char *path) {
void LaunchBrowser(const char *url) {
#if PPSSPP_PLATFORM(SWITCH)
Uuid uuid = { 0 };
WebWifiConfig conf;
webWifiCreate(&conf, NULL, url, 0, 0);
webWifiCreate(&conf, NULL, url, uuid, 0);
webWifiShow(&conf, NULL);
#elif defined(MOBILE_DEVICE)
ILOG("Would have gone to %s but LaunchBrowser is not implemented on this platform", url);
@@ -220,8 +221,9 @@ void LaunchBrowser(const char *url) {
void LaunchMarket(const char *url) {
#if PPSSPP_PLATFORM(SWITCH)
Uuid uuid = { 0 };
WebWifiConfig conf;
webWifiCreate(&conf, NULL, url, 0, 0);
webWifiCreate(&conf, NULL, url, uuid, 0);
webWifiShow(&conf, NULL);
#elif defined(MOBILE_DEVICE)
ILOG("Would have gone to %s but LaunchMarket is not implemented on this platform", url);
@@ -448,6 +450,11 @@ int main(int argc, char *argv[]) {
}
}
#ifdef HAVE_LIBNX
socketInitializeDefault();
nxlinkStdio();
#endif // HAVE_LIBNX
glslang::InitializeProcess();
#if PPSSPP_PLATFORM(RPI)
@@ -544,9 +551,9 @@ int main(int argc, char *argv[]) {
}
// If we're on mobile, don't try for windowed either.
#if defined(MOBILE_DEVICE)
#if defined(MOBILE_DEVICE) && !PPSSPP_PLATFORM(SWITCH)
mode |= SDL_WINDOW_FULLSCREEN;
#elif defined(USING_FBDEV)
#elif defined(USING_FBDEV) || PPSSPP_PLATFORM(SWITCH)
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
#else
mode |= SDL_WINDOW_RESIZABLE;
@@ -592,14 +599,19 @@ int main(int argc, char *argv[]) {
// Mac / Linux
char path[2048];
#if PPSSPP_PLATFORM(SWITCH)
strcpy(path, "/switch/ppsspp/");
#else
const char *the_path = getenv("HOME");
if (!the_path) {
struct passwd* pwd = getpwuid(getuid());
struct passwd *pwd = getpwuid(getuid());
if (pwd)
the_path = pwd->pw_dir;
}
strcpy(path, the_path);
if (path[strlen(path)-1] != '/')
if (the_path)
strcpy(path, the_path);
#endif
if (strlen(path) > 0 && path[strlen(path) - 1] != '/')
strcat(path, "/");
NativeInit(remain_argc, (const char **)remain_argv, path, "/tmp", nullptr);
@@ -1139,5 +1151,8 @@ int main(int argc, char *argv[]) {
glslang::FinalizeProcess();
ILOG("Leaving main");
#ifdef HAVE_LIBNX
socketExit();
#endif
return 0;
}
+22 -19
View File
@@ -14,7 +14,6 @@
#include <ctype.h>
#include "util/text/utf8.h"
void ChatMenu::CreatePopupContents(UI::ViewGroup *parent) {
using namespace UI;
auto n = GetI18NCategory("Networking");
@@ -34,6 +33,7 @@ void ChatMenu::CreatePopupContents(UI::ViewGroup *parent) {
}
#endif
chatEdit_->OnEnter.Handle(this, &ChatMenu::OnSubmit);
#elif PPSSPP_PLATFORM(ANDROID)
bottom->Add(new Button(n->T("Chat Here"),new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->OnClick.Handle(this, &ChatMenu::OnSubmit);
bottom->Add(new Button(n->T("Send")))->OnClick.Handle(this, &ChatMenu::OnSubmit);
@@ -84,26 +84,31 @@ void ChatMenu::CreateViews() {
case 5:
box_ = new LinearLayout(ORIENT_VERTICAL, new AnchorLayoutParams(PopupWidth(), FillVertical() ? yres - 30 : WRAP_CONTENT, NONE, 240, 280, NONE, true));
break;
default:
box_ = nullptr;
break;
}
root_->Add(box_);
box_->SetBG(UI::Drawable(0x99303030));
box_->SetHasDropShadow(false);
if (box_) {
root_->Add(box_);
box_->SetBG(UI::Drawable(0x99303030));
box_->SetHasDropShadow(false);
View *title = new PopupHeader(n->T("Chat"));
box_->Add(title);
View *title = new PopupHeader(n->T("Chat"));
box_->Add(title);
CreatePopupContents(box_);
CreatePopupContents(box_);
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI)
UI::EnableFocusMovement(true);
root_->SetDefaultFocusView(box_);
box_->SubviewFocused(chatEdit_);
root_->SetFocus();
UI::EnableFocusMovement(true);
root_->SetDefaultFocusView(box_);
box_->SubviewFocused(chatEdit_);
root_->SetFocus();
#else
//root_->SetDefaultFocusView(box_);
//box_->SubviewFocused(scroll_);
//root_->SetFocus();
//root_->SetDefaultFocusView(box_);
//box_->SubviewFocused(scroll_);
//root_->SetFocus();
#endif
}
chatScreenVisible = true;
newChat = 0;
@@ -129,7 +134,6 @@ UI::EventReturn ChatMenu::OnSubmit(UI::EventParams &e) {
return UI::EVENT_DONE;
}
UI::EventReturn ChatMenu::OnQuickChat1(UI::EventParams &e) {
sendChat(g_Config.sQuickChat0);
return UI::EVENT_DONE;
@@ -191,7 +195,7 @@ std::vector<std::string> Split(const std::string& str)
void ChatMenu::UpdateChat() {
using namespace UI;
if (chatVert_ != NULL) {
if (chatVert_ != nullptr) {
chatVert_->Clear(); //read Access violation is proadhoc.cpp use NULL_->Clear() pointer?
std::vector<std::string> chatLog = getChatLog();
for (auto i : chatLog) {
@@ -208,11 +212,10 @@ void ChatMenu::UpdateChat() {
namecolor = 0xE53935;
}
if (i[displayname.length()] != ':') {
if (i.length() <= displayname.length() || i[displayname.length()] != ':') {
TextView *v = chatVert_->Add(new TextView(i, FLAG_DYNAMIC_ASCII, true));
v->SetTextColor(0xFF000000 | infocolor);
}
else {
} else {
LinearLayout *line = chatVert_->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, FILL_PARENT)));
TextView *nameView = line->Add(new TextView(displayname, FLAG_DYNAMIC_ASCII, true));
nameView->SetTextColor(0xFF000000 | namecolor);
+14 -7
View File
@@ -1,10 +1,12 @@
#pragma once
#include "ppsspp_config.h"
#include "file/file_util.h"
#include "ui/ui_screen.h"
class ChatMenu : public PopupScreen {
public:
ChatMenu() : PopupScreen("Chat") , toBottom_(true) {}
ChatMenu(): PopupScreen("Chat") {}
~ChatMenu();
void CreatePopupContents(UI::ViewGroup *parent) override;
void CreateViews() override;
@@ -12,7 +14,9 @@ public:
bool touch(const TouchInput &touch) override;
void update() override;
void UpdateChat();
bool toBottom_;
bool toBottom_ = true;
private:
UI::EventReturn OnSubmit(UI::EventParams &e);
UI::EventReturn OnQuickChat1(UI::EventParams &e);
@@ -20,8 +24,11 @@ private:
UI::EventReturn OnQuickChat3(UI::EventParams &e);
UI::EventReturn OnQuickChat4(UI::EventParams &e);
UI::EventReturn OnQuickChat5(UI::EventParams &e);
UI::TextEdit *chatEdit_;
UI::ScrollView *scroll_;
UI::LinearLayout *chatVert_;
UI::ViewGroup *box_;
};
#if PPSSPP_PLATFORM(WINDOWS) || defined(USING_QT_UI)
UI::TextEdit *chatEdit_ = nullptr;
#endif
UI::ScrollView *scroll_ = nullptr;
UI::LinearLayout *chatVert_ = nullptr;
UI::ViewGroup *box_ = nullptr;
};
+2
View File
@@ -1403,6 +1403,8 @@ void EmuScreen::render() {
return;
if (hasVisibleUI()) {
// In most cases, this should already be bound and a no-op.
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::KEEP, RPAction::DONT_CARE, RPAction::DONT_CARE });
cardboardDisableButton_->SetVisibility(g_Config.bEnableCardboardVR ? UI::V_VISIBLE : UI::V_GONE);
screenManager()->getUIContext()->BeginFrame();
renderUI();
+7 -2
View File
@@ -343,6 +343,11 @@ void GameSettingsScreen::CreateViews() {
return UI::EVENT_CONTINUE;
});
#endif
CheckBox *frameDuplication = graphicsSettings->Add(new CheckBox(&g_Config.bRenderDuplicateFrames, gr->T("Render duplicate frames to 60hz")));
frameDuplication->OnClick.Add([=](EventParams &e) {
settingInfo_->Show(gr->T("RenderDuplicateFrames Tip", "Can make framerate smoother in games that run at lower framerates"), e.v);
return UI::EVENT_CONTINUE;
});
if (GetGPUBackend() == GPUBackend::VULKAN || GetGPUBackend() == GPUBackend::OPENGL) {
static const char *bufferOptions[] = { "No buffer", "Up to 1", "Up to 2" };
@@ -801,8 +806,8 @@ void GameSettingsScreen::CreateViews() {
systemSettings->Add(new CheckBox(&g_Config.bEnableStateUndo, sy->T("Savestate slot backups")));
static const char *autoLoadSaveStateChoices[] = { "Off", "Oldest Save", "Newest Save", "Slot 1", "Slot 2", "Slot 3", "Slot 4", "Slot 5" };
systemSettings->Add(new PopupMultiChoice(&g_Config.iAutoLoadSaveState, sy->T("Auto Load Savestate"), autoLoadSaveStateChoices, 0, ARRAY_SIZE(autoLoadSaveStateChoices), sy->GetName(), screenManager()));
#if defined(USING_WIN_UI)
systemSettings->Add(new CheckBox(&g_Config.bBypassOSKWithKeyboard, sy->T("Enable Windows native keyboard", "Enable Windows native keyboard")));
#if defined(USING_WIN_UI) || defined(USING_QT_UI) || PPSSPP_PLATFORM(ANDROID)
systemSettings->Add(new CheckBox(&g_Config.bBypassOSKWithKeyboard, sy->T("Use system native keyboard")));
#endif
#if PPSSPP_PLATFORM(ANDROID)
auto memstickPath = systemSettings->Add(new ChoiceWithValueDisplay(&g_Config.memStickDirectory, sy->T("Change Memory Stick folder"), (const char *)nullptr));
+137 -29
View File
@@ -15,12 +15,20 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "ppsspp_config.h"
#include <algorithm>
#include <thread>
#include <mutex>
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
#include "Common/CommonWindows.h"
#include <netfw.h>
#endif
#include "base/timeutil.h"
#include "file/path.h"
// TODO: For text align flags, probably shouldn't be in gfx_es2/...
#include "gfx_es2/draw_buffer.h"
#include "i18n/i18n.h"
#include "json/json_reader.h"
#include "net/http_client.h"
@@ -39,6 +47,49 @@ static const int REPORT_PORT = 80;
static bool scanCancelled = false;
static bool scanAborted = false;
enum class ServerAllowStatus {
NO,
YES,
UNKNOWN,
};
static ServerAllowStatus IsServerAllowed(int port) {
#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP)
INetFwMgr *fwMgr = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwMgr), nullptr, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void **)&fwMgr);
if (FAILED(hr)) {
return ServerAllowStatus::UNKNOWN;
}
std::wstring app;
size_t sz;
do {
app.resize(app.size() + MAX_PATH);
// On failure, this will return the same value as passed in, but success will always be one lower.
sz = GetModuleFileName(nullptr, &app[0], (DWORD)app.size());
} while (sz >= app.size());
VARIANT allowedV, restrictedV;
VariantInit(&allowedV);
VariantInit(&restrictedV);
hr = fwMgr->IsPortAllowed(&app[0], NET_FW_IP_VERSION_ANY, port, nullptr, NET_FW_IP_PROTOCOL_TCP, &allowedV, &restrictedV);
fwMgr->Release();
if (FAILED(hr)) {
return ServerAllowStatus::UNKNOWN;
}
bool allowed = allowedV.vt == VT_BOOL && allowedV.boolVal != VARIANT_FALSE;
bool restricted = restrictedV.vt == VT_BOOL && restrictedV.boolVal != VARIANT_FALSE;
if (!allowed || restricted) {
return ServerAllowStatus::NO;
}
return ServerAllowStatus::YES;
#else
return ServerAllowStatus::UNKNOWN;
#endif
}
static std::string RemoteSubdir() {
if (g_Config.bRemoteISOManual) {
return g_Config.sRemoteISOSubdir;
@@ -47,45 +98,71 @@ static std::string RemoteSubdir() {
return "/";
}
static bool FindServer(std::string &resultHost, int &resultPort) {
bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort) {
http::Client http;
Buffer result;
int code = 500;
bool hadTimeouts = false;
std::string subdir = RemoteSubdir();
auto ri = GetI18NCategory("RemoteISO");
auto SetStatus = [&](const std::string &key, const std::string &host, int port) {
std::string formatted = ReplaceAll(ri->T(key), "[URL]", StringFromFormat("http://%s:%d/", host.c_str(), port));
std::lock_guard<std::mutex> guard(statusLock_);
statusMessage_ = formatted;
};
auto TryServer = [&](const std::string &host, int port) {
SetStatus("Resolving [URL]...", host, port);
if (!http.Resolve(host.c_str(), port)) {
SetStatus("Could not resolve [URL]", host, port);
return false;
}
SetStatus("Connecting to [URL]...", host, port);
// Don't wait as long for a connect - we need a good connection for smooth streaming anyway.
// This way if it's down, we'll find the right one faster.
if (http.Resolve(host.c_str(), port) && http.Connect(1, 10.0, &scanCancelled)) {
code = http.GET(subdir.c_str(), &result);
http.Disconnect();
if (!http.Connect(1, 10.0, &scanCancelled)) {
hadTimeouts = true;
SetStatus("Could not connect to [URL]", host, port);
return false;
}
if (code != 200) {
return false;
SetStatus("Loading game list from [URL]...", host, port);
code = http.GET(subdir.c_str(), &result);
http.Disconnect();
if (code != 200) {
if (code < 0) {
hadTimeouts = true;
}
SetStatus("Game list failed from [URL]", host, port);
return false;
}
// Make sure this isn't just the debugger. If so, move on.
std::string listing;
std::vector<std::string> items;
result.TakeAll(&listing);
SplitString(listing, '\n', items);
// Make sure this isn't just the debugger. If so, move on.
std::string listing;
std::vector<std::string> items;
result.TakeAll(&listing);
SplitString(listing, '\n', items);
bool supported = false;
for (const std::string &item : items) {
if (!RemoteISOFileSupported(item)) {
continue;
}
supported = true;
break;
bool supported = false;
for (const std::string &item : items) {
if (!RemoteISOFileSupported(item)) {
continue;
}
supported = true;
break;
}
if (supported) {
resultHost = host;
resultPort = port;
NOTICE_LOG(HLE, "RemoteISO found: %s : %d", host.c_str(), port);
return true;
}
if (supported) {
resultHost = host;
resultPort = port;
SetStatus("Connected to [URL]", host, port);
NOTICE_LOG(SYSTEM, "RemoteISO found: %s : %d", host.c_str(), port);
return true;
}
return false;
@@ -104,6 +181,7 @@ static bool FindServer(std::string &resultHost, int &resultPort) {
}
// Start by requesting a list of recent local ips for this network.
SetStatus("Looking for peers...", "", 0);
if (http.Resolve(REPORT_HOSTNAME, REPORT_PORT)) {
if (http.Connect(2, 20.0, &scanCancelled)) {
code = http.GET("/match/list", &result);
@@ -112,6 +190,9 @@ static bool FindServer(std::string &resultHost, int &resultPort) {
}
if (code != 200 || scanCancelled) {
if (!scanCancelled) {
SetStatus("Could not load peers, retrying soon...", "", 0);
}
return false;
}
@@ -122,11 +203,13 @@ static bool FindServer(std::string &resultHost, int &resultPort) {
JsonReader reader(json.c_str(), json.size());
if (!reader.ok()) {
SetStatus("Could not load peers, retrying soon...", "", 0);
return false;
}
const JsonValue entries = reader.rootArray();
if (entries.getTag() != JSON_ARRAY) {
SetStatus("Could not load peers, retrying soon...", "", 0);
return false;
}
@@ -143,7 +226,13 @@ static bool FindServer(std::string &resultHost, int &resultPort) {
}
}
// None of the local IPs were reachable.
// None of the local IPs were reachable. We'll retry again.
std::lock_guard<std::mutex> guard(statusLock_);
if (hadTimeouts) {
statusMessage_ = ri->T("RemoteISOScanningTimeout", "Scanning... check your desktop's firewall settings");
} else {
statusMessage_ = ri->T("RemoteISOScanning", "Scanning... click Share Games on your desktop");
}
return false;
}
@@ -163,12 +252,21 @@ static bool LoadGameList(const std::string &url, std::vector<std::string> &games
return !games.empty();
}
RemoteISOScreen::RemoteISOScreen() : serverRunning_(false), serverStopping_(false) {
RemoteISOScreen::RemoteISOScreen() {
}
void RemoteISOScreen::update() {
UIScreenWithBackground::update();
if (!WebServerStopped(WebServerFlags::DISCS)) {
auto result = IsServerAllowed(g_Config.iRemoteISOPort);
if (result == ServerAllowStatus::NO) {
firewallWarning_->SetVisibility(V_VISIBLE);
} else if (result == ServerAllowStatus::YES) {
firewallWarning_->SetVisibility(V_GONE);
}
}
bool nowRunning = !WebServerStopped(WebServerFlags::DISCS);
if (serverStopping_ && !nowRunning) {
serverStopping_ = false;
@@ -193,6 +291,9 @@ void RemoteISOScreen::CreateViews() {
leftColumnItems->Add(new TextView(ri->T("RemoteISODesc", "Games in your recent list will be shared"), new LinearLayoutParams(Margins(12, 5, 0, 5))));
leftColumnItems->Add(new TextView(ri->T("RemoteISOWifi", "Note: Connect both devices to the same wifi"), new LinearLayoutParams(Margins(12, 5, 0, 5))));
firewallWarning_ = leftColumnItems->Add(new TextView(ri->T("RemoteISOWinFirewall", "WARNING: Windows Firewall is blocking sharing"), new LinearLayoutParams(Margins(12, 5, 0, 5))));
firewallWarning_->SetTextColor(0xFF0000FF);
firewallWarning_->SetVisibility(V_GONE);
rightColumnItems->SetSpacing(0.0f);
Choice *browseChoice = new Choice(ri->T("Browse Games"));
@@ -250,7 +351,7 @@ UI::EventReturn RemoteISOScreen::HandleSettings(UI::EventParams &e) {
return EVENT_DONE;
}
RemoteISOConnectScreen::RemoteISOConnectScreen() : status_(ScanStatus::SCANNING), nextRetry_(0.0) {
RemoteISOConnectScreen::RemoteISOConnectScreen() {
scanCancelled = false;
scanAborted = false;
@@ -286,7 +387,7 @@ void RemoteISOConnectScreen::CreateViews() {
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));
LinearLayout *rightColumnItems = new LinearLayout(ORIENT_VERTICAL);
statusView_ = leftColumnItems->Add(new TextView(ri->T("RemoteISOScanning", "Scanning... click Share Games on your desktop"), new LinearLayoutParams(Margins(12, 5, 0, 5))));
statusView_ = leftColumnItems->Add(new TextView(ri->T("RemoteISOScanning", "Scanning... click Share Games on your desktop"), FLAG_WRAP_TEXT, false, new LinearLayoutParams(Margins(12, 5, 0, 5))));
rightColumnItems->SetSpacing(0.0f);
rightColumnItems->Add(new Choice(di->T("Cancel"), "", false, new AnchorLayoutParams(150, WRAP_CONTENT, 10, NONE, NONE, 10)))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
@@ -318,13 +419,14 @@ void RemoteISOConnectScreen::update() {
if (scanThread_->joinable())
scanThread_->join();
delete scanThread_;
statusMessage_.clear();
scanThread_ = new std::thread([](RemoteISOConnectScreen *thiz) {
thiz->ExecuteLoad();
}, this);
break;
case ScanStatus::FAILED:
nextRetry_ = real_time_now() + 30.0;
nextRetry_ = real_time_now() + 15.0;
status_ = ScanStatus::RETRY_SCAN;
break;
@@ -336,6 +438,7 @@ void RemoteISOConnectScreen::update() {
if (scanThread_->joinable())
scanThread_->join();
delete scanThread_;
statusMessage_.clear();
scanThread_ = new std::thread([](RemoteISOConnectScreen *thiz) {
thiz->ExecuteScan();
}, this);
@@ -347,6 +450,11 @@ void RemoteISOConnectScreen::update() {
screenManager()->push(new RemoteISOBrowseScreen(url_, games_));
break;
}
std::lock_guard<std::mutex> guard(statusLock_);
if (!statusMessage_.empty()) {
statusView_->SetText(statusMessage_);
}
}
void RemoteISOConnectScreen::ExecuteScan() {
+7 -4
View File
@@ -38,8 +38,9 @@ protected:
UI::EventReturn HandleBrowse(UI::EventParams &e);
UI::EventReturn HandleSettings(UI::EventParams &e);
bool serverRunning_;
bool serverStopping_;
UI::TextView *firewallWarning_ = nullptr;
bool serverRunning_ = false;
bool serverStopping_ = false;
};
enum class ScanStatus {
@@ -63,11 +64,13 @@ protected:
ScanStatus GetStatus();
void ExecuteScan();
void ExecuteLoad();
bool FindServer(std::string &resultHost, int &resultPort);
UI::TextView *statusView_;
ScanStatus status_;
double nextRetry_;
ScanStatus status_ = ScanStatus::SCANNING;
std::string statusMessage_;
double nextRetry_ = 0.0;
std::thread *scanThread_;
std::mutex statusLock_;
std::string host_;
+1 -1
View File
@@ -17,7 +17,7 @@
#include <string>
#include "base/display.h"
// TODO: For text align flags, probably shouldn';t be in gfx_es2/...
// TODO: For text align flags, probably shouldn't be in gfx_es2/...
#include "gfx_es2/draw_buffer.h"
#include "i18n/i18n.h"
#include "thin3d/thin3d.h"
+1 -1
View File
@@ -19,7 +19,7 @@
#include <mfapi.h>
#include <mfidl.h>
#include <Mfreadwrite.h>
#include <mfreadwrite.h>
#include <mutex>
#include <condition_variable>
#include <vector>
+2 -2
View File
@@ -341,9 +341,9 @@ void EnableCrashingOnCrashes() {
void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function<void(bool, const std::string &)> cb) {
std::string out;
if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), ConvertUTF8ToWString(title).c_str(), defaultValue, out)) {
cb(true, out);
NativeInputBoxReceived(cb, true, out);
} else {
cb(false, "");
NativeInputBoxReceived(cb, false, "");
}
}
+17 -17
View File
@@ -1043,32 +1043,32 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(J
return false;
}
retry:
auto tryInit = [&]() {
if (graphicsContext->InitFromRenderThread(wnd, desiredBackbufferSizeX, desiredBackbufferSizeY, backbuffer_format, androidVersion)) {
return true;
}
bool vulkan = g_Config.iGPUBackend == (int)GPUBackend::VULKAN;
int tries = 0;
if (!graphicsContext->InitFromRenderThread(wnd, desiredBackbufferSizeX, desiredBackbufferSizeY, backbuffer_format, androidVersion)) {
ELOG("Failed to initialize graphics context.");
return false;
};
if (!exitRenderLoop && (vulkan && tries < 2)) {
bool initSuccess = tryInit();
if (!initSuccess) {
if (!exitRenderLoop && g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {
ILOG("Trying again, this time with OpenGL.");
g_Config.iGPUBackend = (int)GPUBackend::OPENGL;
SetGPUBackend((GPUBackend)g_Config.iGPUBackend);
// If we were still supporting EGL for GL:
// tries++;
// goto retry;
SetGPUBackend(GPUBackend::OPENGL);
g_Config.iGPUBackend = (int)GetGPUBackend();
// If we were still supporting EGL for GL, we'd retry here:
//initSuccess = tryInit();
}
if (!initSuccess) {
delete graphicsContext;
graphicsContext = nullptr;
renderLoopRunning = false;
return false;
}
delete graphicsContext;
graphicsContext = nullptr;
renderLoopRunning = false;
return false;
}
if (!exitRenderLoop) {
-2
View File
@@ -30,7 +30,6 @@ void RotateRectToDisplayImpl(DisplayRect<T> &rect, T curRTWidth, T curRTHeight)
// Note that curRTWidth_ and curRTHeight_ are "swapped"!
T origX = rect.x;
T origY = rect.y;
T rtw = curRTHeight;
T rth = curRTWidth;
rect.x = clamp_value(rth - rect.h - origY, T{}, curRTHeight);
rect.y = origX;
@@ -43,7 +42,6 @@ void RotateRectToDisplayImpl(DisplayRect<T> &rect, T curRTWidth, T curRTHeight)
T origX = rect.x;
T origY = rect.y;
T rtw = curRTHeight;
T rth = curRTWidth;
rect.x = origY;
rect.y = clamp_value(rtw - rect.w - origX, T{}, curRTWidth);
T temp = rect.w;
+6
View File
@@ -11,6 +11,10 @@
#include <unistd.h>
#endif
#ifdef HAVE_LIBNX
#include <switch.h>
#endif // HAVE_LIBNX
static double curtime = 0;
static float curtime_f = 0;
@@ -79,6 +83,8 @@ int time_now_ms() {
void sleep_ms(int ms) {
#ifdef _WIN32
Sleep(ms);
#elif defined(HAVE_LIBNX)
svcSleepThread(ms * 1000000);
#else
usleep(ms * 1000);
#endif
+7 -5
View File
@@ -365,7 +365,7 @@ void DrawBuffer::DrawImage2GridH(ImageID atlas_image, float x1, float y1, float
class AtlasWordWrapper : public WordWrapper {
public:
// Note: maxW may be height if rotated.
AtlasWordWrapper(const AtlasFont &atlasfont, float scale, const char *str, float maxW) : WordWrapper(str, maxW), atlasfont_(atlasfont), scale_(scale) {
AtlasWordWrapper(const AtlasFont &atlasfont, float scale, const char *str, float maxW, int flags) : WordWrapper(str, maxW, flags), atlasfont_(atlasfont), scale_(scale) {
}
protected:
@@ -442,14 +442,15 @@ void DrawBuffer::MeasureTextRect(FontID font_id, const char *text, int count, co
}
std::string toMeasure = std::string(text, count);
if (align & FLAG_WRAP_TEXT) {
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
if (wrap) {
const AtlasFont *font = atlas->getFont(font_id);
if (!font) {
*w = 0.0f;
*h = 0.0f;
return;
}
AtlasWordWrapper wrapper(*font, fontscalex, toMeasure.c_str(), bounds.w);
AtlasWordWrapper wrapper(*font, fontscalex, toMeasure.c_str(), bounds.w, wrap);
toMeasure = wrapper.Wrapped();
}
MeasureTextCount(font_id, toMeasure.c_str(), (int)toMeasure.length(), w, h);
@@ -491,8 +492,9 @@ void DrawBuffer::DrawTextRect(FontID font, const char *text, float x, float y, f
}
std::string toDraw = text;
if (align & FLAG_WRAP_TEXT) {
AtlasWordWrapper wrapper(*atlas->getFont(font), fontscalex, toDraw.c_str(), w);
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
if (wrap) {
AtlasWordWrapper wrapper(*atlas->getFont(font), fontscalex, toDraw.c_str(), w, wrap);
toDraw = wrapper.Wrapped();
}
+1
View File
@@ -39,6 +39,7 @@ enum {
FLAG_DYNAMIC_ASCII = 2048,
FLAG_NO_PREFIX = 4096, // means to not process ampersands
FLAG_WRAP_TEXT = 8192,
FLAG_ELLIPSIZE_TEXT = 16384,
};
namespace Draw {
+39 -2
View File
@@ -24,8 +24,8 @@ float TextDrawerWordWrapper::MeasureWidth(const char *str, size_t bytes) {
return w;
}
void TextDrawer::WrapString(std::string &out, const char *str, float maxW) {
TextDrawerWordWrapper wrapper(this, str, maxW);
void TextDrawer::WrapString(std::string &out, const char *str, float maxW, int flags) {
TextDrawerWordWrapper wrapper(this, str, maxW, flags);
out = wrapper.Wrapped();
}
@@ -35,6 +35,8 @@ void TextDrawer::SetFontScale(float xscale, float yscale) {
}
float TextDrawer::CalculateDPIScale() {
if (ignoreGlobalDpi_)
return dpiScale_;
float scale = g_dpi_scale_y;
if (scale >= 1.0f) {
scale = 1.0f;
@@ -42,6 +44,41 @@ float TextDrawer::CalculateDPIScale() {
return scale;
}
void TextDrawer::DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) {
float x = bounds.x;
float y = bounds.y;
if (align & ALIGN_HCENTER) {
x = bounds.centerX();
} else if (align & ALIGN_RIGHT) {
x = bounds.x2();
}
if (align & ALIGN_VCENTER) {
y = bounds.centerY();
} else if (align & ALIGN_BOTTOM) {
y = bounds.y2();
}
std::string toDraw = str;
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
if (wrap) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toDraw, str, rotated ? bounds.h : bounds.w, wrap);
}
DrawString(target, toDraw.c_str(), x, y, color, align);
}
void TextDrawer::DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, const Bounds &bounds, int align) {
std::string toDraw = str;
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
if (wrap) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toDraw, str, rotated ? bounds.h : bounds.w, wrap);
}
DrawStringBitmap(bitmapData, entry, texFormat, toDraw.c_str(), align);
}
TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) {
TextDrawer *drawer = nullptr;
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
+15 -8
View File
@@ -58,11 +58,17 @@ public:
virtual void MeasureString(const char *str, size_t len, float *w, float *h) = 0;
virtual void MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) = 0;
virtual void DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) = 0;
virtual void DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) = 0;
void DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align);
virtual void DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, int align = ALIGN_TOPLEFT) = 0;
void DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, const Bounds &bounds, int align);
// Use for housekeeping like throwing out old strings.
virtual void OncePerFrame() = 0;
float CalculateDPIScale();
void SetForcedDPIScale(float dpi) {
dpiScale_ = dpi;
ignoreGlobalDpi_ = true;
}
// Factory function that selects implementation.
static TextDrawer *Create(Draw::DrawContext *draw);
@@ -72,7 +78,7 @@ protected:
Draw::DrawContext *draw_;
virtual void ClearCache() = 0;
void WrapString(std::string &out, const char *str, float maxWidth);
void WrapString(std::string &out, const char *str, float maxWidth, int flags);
struct CacheKey {
bool operator < (const CacheKey &other) const {
@@ -86,20 +92,21 @@ protected:
uint32_t fontHash;
};
int frameCount_;
float fontScaleX_;
float fontScaleY_;
float dpiScale_;
int frameCount_ = 0;
float fontScaleX_ = 1.0f;
float fontScaleY_ = 1.0f;
float dpiScale_ = 1.0f;
bool ignoreGlobalDpi_ = false;
};
class TextDrawerWordWrapper : public WordWrapper {
public:
TextDrawerWordWrapper(TextDrawer *drawer, const char *str, float maxW) : WordWrapper(str, maxW), drawer_(drawer) {
TextDrawerWordWrapper(TextDrawer *drawer, const char *str, float maxW, int flags) : WordWrapper(str, maxW, flags), drawer_(drawer) {
}
protected:
float MeasureWidth(const char *str, size_t bytes) override;
TextDrawer *drawer_;
};
};
+76 -70
View File
@@ -115,9 +115,10 @@ void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bou
}
std::string toMeasure = std::string(str, len);
if (align & FLAG_WRAP_TEXT) {
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
if (wrap) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w);
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w, wrap);
}
std::vector<std::string> lines;
@@ -154,6 +155,68 @@ void TextDrawerAndroid::MeasureStringRect(const char *str, size_t len, const Bou
*h = total_h * dpiScale_;
}
void TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, int align) {
if (!strlen(str)) {
bitmapData.clear();
return;
}
double size = 0.0;
auto iter = fontMap_.find(fontHash_);
if (iter != fontMap_.end()) {
size = iter->second.size;
} else {
ELOG("Missing font");
}
jstring jstr = env_->NewStringUTF(str);
uint32_t textSize = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, size);
int imageWidth = (short)(textSize >> 16);
int imageHeight = (short)(textSize & 0xFFFF);
if (imageWidth <= 0)
imageWidth = 1;
if (imageHeight <= 0)
imageHeight = 1;
jintArray imageData = (jintArray)env_->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, size);
env_->DeleteLocalRef(jstr);
entry.texture = nullptr;
entry.bmWidth = imageWidth;
entry.width = imageWidth;
entry.bmHeight = imageHeight;
entry.height = imageHeight;
entry.lastUsedFrame = frameCount_;
jint *jimage = env_->GetIntArrayElements(imageData, nullptr);
assert(env_->GetArrayLength(imageData) == imageWidth * imageHeight);
if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) {
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t));
uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0];
for (int x = 0; x < entry.bmWidth; x++) {
for (int y = 0; y < entry.bmHeight; y++) {
uint32_t v = jimage[imageWidth * y + x];
v = 0xFFF0 | ((v >> 12) & 0xF); // Just grab some bits from the green channel.
bitmapData16[entry.bmWidth * y + x] = (uint16_t)v;
}
}
} else if (texFormat == Draw::DataFormat::R8_UNORM) {
bitmapData.resize(entry.bmWidth * entry.bmHeight);
for (int x = 0; x < entry.bmWidth; x++) {
for (int y = 0; y < entry.bmHeight; y++) {
uint32_t v = jimage[imageWidth * y + x];
v = (v >> 12) & 0xF; // Just grab some bits from the green channel.
bitmapData[entry.bmWidth * y + x] = (uint8_t)(v | (v << 4));
}
}
} else {
ELOG("Bad TextDrawer format");
assert(false);
}
env_->ReleaseIntArrayElements(imageData, jimage, 0);
env_->DeleteLocalRef(imageData);
}
void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align) {
using namespace Draw;
std::string text(NormalizeString(std::string(str)));
@@ -169,66 +232,32 @@ void TextDrawerAndroid::DrawString(DrawBuffer &target, const char *str, float x,
if (iter != cache_.end()) {
entry = iter->second.get();
entry->lastUsedFrame = frameCount_;
if (entry->texture)
draw_->BindTexture(0, entry->texture);
} else {
double size = 0.0;
auto iter = fontMap_.find(fontHash_);
if (iter != fontMap_.end()) {
size = iter->second.size;
} else {
ELOG("Missing font");
}
jstring jstr = env_->NewStringUTF(text.c_str());
uint32_t textSize = env_->CallStaticIntMethod(cls_textRenderer, method_measureText, jstr, size);
int imageWidth = (short)(textSize >> 16);
int imageHeight = (short)(textSize & 0xFFFF);
if (imageWidth <= 0)
imageWidth = 1;
if (imageHeight <= 0)
imageHeight = 1;
jintArray imageData = (jintArray)env_->CallStaticObjectMethod(cls_textRenderer, method_renderText, jstr, size);
env_->DeleteLocalRef(jstr);
DataFormat texFormat = Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
entry = new TextStringEntry();
entry->bmWidth = imageWidth;
entry->width = imageWidth;
entry->bmHeight = imageHeight;
entry->height = imageHeight;
entry->lastUsedFrame = frameCount_;
TextureDesc desc{};
std::vector<uint8_t> bitmapData;
DrawStringBitmap(bitmapData, *entry, texFormat, text.c_str(), align);
desc.initData.push_back(&bitmapData[0]);
desc.type = TextureType::LINEAR2D;
desc.format = Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
desc.format = texFormat;
desc.width = entry->bmWidth;
desc.height = entry->bmHeight;
desc.depth = 1;
desc.mipLevels = 1;
desc.generateMips = false;
desc.tag = "TextDrawer";
uint16_t *bitmapData = new uint16_t[entry->bmWidth * entry->bmHeight];
jint* jimage = env_->GetIntArrayElements(imageData, nullptr);
assert(env_->GetArrayLength(imageData) == imageWidth * imageHeight);
for (int x = 0; x < entry->bmWidth; x++) {
for (int y = 0; y < entry->bmHeight; y++) {
uint32_t v = jimage[imageWidth * y + x];
v = 0xFFF0 | ((v >> 12) & 0xF); // Just grab some bits from the green channel.
bitmapData[entry->bmWidth * y + x] = (uint16_t)v;
}
}
env_->ReleaseIntArrayElements(imageData, jimage, 0);
env_->DeleteLocalRef(imageData);
desc.initData.push_back((uint8_t *)bitmapData);
entry->texture = draw_->CreateTexture(desc);
delete[] bitmapData;
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
if (entry->texture) {
draw_->BindTexture(0, entry->texture);
}
}
if (entry->texture) {
draw_->BindTexture(0, entry->texture);
}
float w = entry->bmWidth * fontScaleX_ * dpiScale_;
float h = entry->bmHeight * fontScaleY_ * dpiScale_;
DrawBuffer::DoAlign(align, &x, &y, &w, &h);
@@ -247,29 +276,6 @@ void TextDrawerAndroid::ClearCache() {
sizeCache_.clear();
}
void TextDrawerAndroid::DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) {
float x = bounds.x;
float y = bounds.y;
if (align & ALIGN_HCENTER) {
x = bounds.centerX();
} else if (align & ALIGN_RIGHT) {
x = bounds.x2();
}
if (align & ALIGN_VCENTER) {
y = bounds.centerY();
} else if (align & ALIGN_BOTTOM) {
y = bounds.y2();
}
std::string toDraw = str;
if (align & FLAG_WRAP_TEXT) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toDraw, str, rotated ? bounds.h : bounds.w);
}
DrawString(target, toDraw.c_str(), x, y, color, align);
}
void TextDrawerAndroid::OncePerFrame() {
frameCount_++;
// If DPI changed (small-mode, future proper monitor DPI support), drop everything.
+1 -1
View File
@@ -24,7 +24,7 @@ public:
void MeasureString(const char *str, size_t len, float *w, float *h) override;
void MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
void DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
void DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) override;
void DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, int align = ALIGN_TOPLEFT) override;
// Use for housekeeping like throwing out old strings.
void OncePerFrame() override;
+102 -71
View File
@@ -1,3 +1,4 @@
#include <cassert>
#include "base/display.h"
#include "base/logging.h"
#include "base/stringutil.h"
@@ -33,7 +34,7 @@ uint32_t TextDrawerQt::SetFont(const char *fontName, int size, int flags) {
return fontHash;
}
QFont* font = fontName ? new QFont(fontName) : new QFont();
QFont *font = fontName ? new QFont(fontName) : new QFont();
font->setPixelSize(size + 6);
fontMap_[fontHash] = font;
fontHash_ = fontHash;
@@ -41,22 +42,44 @@ uint32_t TextDrawerQt::SetFont(const char *fontName, int size, int flags) {
}
void TextDrawerQt::SetFont(uint32_t fontHandle) {
uint32_t fontHash = fontHandle;
auto iter = fontMap_.find(fontHash);
if (iter != fontMap_.end()) {
fontHash_ = fontHandle;
} else {
ELOG("Invalid font handle %08x", fontHandle);
}
}
void TextDrawerQt::MeasureString(const char *str, size_t len, float *w, float *h) {
QFont* font = fontMap_.find(fontHash_)->second;
QFontMetrics fm(*font);
QSize size = fm.size(0, QString::fromUtf8(str, (int)len));
*w = (float)size.width() * fontScaleX_;
*h = (float)size.height() * fontScaleY_;
CacheKey key{ std::string(str, len), fontHash_ };
TextMeasureEntry *entry;
auto iter = sizeCache_.find(key);
if (iter != sizeCache_.end()) {
entry = iter->second.get();
} else {
QFont* font = fontMap_.find(fontHash_)->second;
QFontMetrics fm(*font);
QSize size = fm.size(0, QString::fromUtf8(str, (int)len));
entry = new TextMeasureEntry();
entry->width = size.width();
entry->height = size.height();
sizeCache_[key] = std::unique_ptr<TextMeasureEntry>(entry);
}
entry->lastUsedFrame = frameCount_;
*w = entry->width * fontScaleX_ * dpiScale_;
*h = entry->height * fontScaleY_ * dpiScale_;
}
void TextDrawerQt::MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align) {
std::string toMeasure = std::string(str, len);
if (align & FLAG_WRAP_TEXT) {
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
if (wrap) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w);
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w, wrap);
}
QFont* font = fontMap_.find(fontHash_)->second;
@@ -66,71 +89,102 @@ void TextDrawerQt::MeasureStringRect(const char *str, size_t len, const Bounds &
*h = (float)size.height() * fontScaleY_;
}
void TextDrawerQt::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, int align) {
if (!strlen(str)) {
bitmapData.clear();
return;
}
QFont *font = fontMap_.find(fontHash_)->second;
QFontMetrics fm(*font);
QSize size = fm.size(0, QString::fromUtf8(str));
QImage image((size.width() + 3) & ~3, (size.height() + 3) & ~3, QImage::Format_ARGB32_Premultiplied);
if (image.isNull()) {
bitmapData.clear();
return;
}
image.fill(0);
QPainter painter;
painter.begin(&image);
painter.setFont(*font);
painter.setPen(0xFFFFFFFF);
// TODO: Involve ALIGN_HCENTER (bounds etc.)
painter.drawText(image.rect(), Qt::AlignTop | Qt::AlignLeft, QString::fromUtf8(str).replace("&&", "&"));
painter.end();
entry.texture = nullptr;
entry.bmWidth = entry.width = image.width();
entry.bmHeight = entry.height = image.height();
entry.lastUsedFrame = frameCount_;
if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) {
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t));
uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0];
for (int x = 0; x < entry.bmWidth; x++) {
for (int y = 0; y < entry.bmHeight; y++) {
bitmapData16[entry.bmWidth * y + x] = 0xfff0 | (image.pixel(x, y) >> 28);
}
}
} else if (texFormat == Draw::DataFormat::R8_UNORM) {
bitmapData.resize(entry.bmWidth * entry.bmHeight);
for (int x = 0; x < entry.bmWidth; x++) {
for (int y = 0; y < entry.bmHeight; y++) {
bitmapData[entry.bmWidth * y + x] = image.pixel(x, y) >> 24;
}
}
} else {
ELOG("Bad TextDrawer format");
assert(false);
}
}
void TextDrawerQt::DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align) {
using namespace Draw;
if (!strlen(str))
return;
uint32_t stringHash = hash::Adler32((const uint8_t *)str, strlen(str));
uint32_t entryHash = stringHash ^ fontHash_ ^ (align << 24);
CacheKey key{ std::string(str), fontHash_ };
target.Flush(true);
TextStringEntry *entry;
auto iter = cache_.find(entryHash);
auto iter = cache_.find(key);
if (iter != cache_.end()) {
entry = iter->second.get();
entry->lastUsedFrame = frameCount_;
} else {
QFont *font = fontMap_.find(fontHash_)->second;
QFontMetrics fm(*font);
QSize size = fm.size(0, QString::fromUtf8(str));
QImage image((size.width() + 3) & ~3, (size.height() + 3) & ~3, QImage::Format_ARGB32_Premultiplied);
if (image.isNull()) {
return;
}
image.fill(0);
QPainter painter;
painter.begin(&image);
painter.setFont(*font);
painter.setPen(color);
// TODO: Involve ALIGN_HCENTER (bounds etc.)
painter.drawText(image.rect(), Qt::AlignTop | Qt::AlignLeft, QString::fromUtf8(str).replace("&&", "&"));
painter.end();
DataFormat texFormat = Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
entry = new TextStringEntry();
entry->bmWidth = entry->width = image.width();
entry->bmHeight = entry->height = image.height();
TextureDesc desc{};
std::vector<uint8_t> bitmapData;
DrawStringBitmap(bitmapData, *entry, texFormat, str, align);
desc.initData.push_back(&bitmapData[0]);
desc.type = TextureType::LINEAR2D;
desc.format = Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
desc.format = texFormat;
desc.width = entry->bmWidth;
desc.height = entry->bmHeight;
desc.depth = 1;
desc.mipLevels = 1;
desc.tag = "TextDrawer";
uint16_t *bitmapData = new uint16_t[entry->bmWidth * entry->bmHeight];
for (int x = 0; x < entry->bmWidth; x++) {
for (int y = 0; y < entry->bmHeight; y++) {
bitmapData[entry->bmWidth * y + x] = 0xfff0 | image.pixel(x, y) >> 28;
}
}
desc.initData.push_back((uint8_t *)bitmapData);
entry->texture = draw_->CreateTexture(desc);
delete[] bitmapData;
cache_[entryHash] = std::unique_ptr<TextStringEntry>(entry);
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
}
float w = entry->bmWidth * fontScaleX_;
float h = entry->bmHeight * fontScaleY_;
entry->lastUsedFrame = frameCount_;
draw_->BindTexture(0, entry->texture);
if (entry->texture) {
draw_->BindTexture(0, entry->texture);
}
float w = entry->bmWidth * fontScaleX_ * dpiScale_;
float h = entry->bmHeight * fontScaleY_ * dpiScale_;
DrawBuffer::DoAlign(align, &x, &y, &w, &h);
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, 1.0f, 1.0f, color);
target.Flush(true);
if (entry->texture) {
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, 1.0f, 1.0f, color);
target.Flush(true);
}
}
void TextDrawerQt::ClearCache() {
@@ -148,29 +202,6 @@ void TextDrawerQt::ClearCache() {
fontHash_ = 0;
}
void TextDrawerQt::DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) {
float x = bounds.x;
float y = bounds.y;
if (align & ALIGN_HCENTER) {
x = bounds.centerX();
} else if (align & ALIGN_RIGHT) {
x = bounds.x2();
}
if (align & ALIGN_VCENTER) {
y = bounds.centerY();
} else if (align & ALIGN_BOTTOM) {
y = bounds.y2();
}
std::string toDraw = str;
if (align & FLAG_WRAP_TEXT) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toDraw, str, rotated ? bounds.h : bounds.w);
}
DrawString(target, toDraw.c_str(), x, y, color, align);
}
void TextDrawerQt::OncePerFrame() {
frameCount_++;
// If DPI changed (small-mode, future proper monitor DPI support), drop everything.
+4 -5
View File
@@ -17,7 +17,7 @@ public:
void MeasureString(const char *str, size_t len, float *w, float *h) override;
void MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
void DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
void DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) override;
void DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, int align = ALIGN_TOPLEFT) override;
// Use for housekeeping like throwing out old strings.
void OncePerFrame() override;
@@ -27,9 +27,8 @@ protected:
uint32_t fontHash_;
std::map<uint32_t, QFont *> fontMap_;
// The key is the CityHash of the string xor the fontHash_.
std::map<uint32_t, std::unique_ptr<TextStringEntry>> cache_;
std::map<uint32_t, std::unique_ptr<TextMeasureEntry>> sizeCache_;
std::map<CacheKey, std::unique_ptr<TextStringEntry>> cache_;
std::map<CacheKey, std::unique_ptr<TextMeasureEntry>> sizeCache_;
};
#endif
#endif
+116 -110
View File
@@ -1,3 +1,4 @@
#include <cassert>
#include "base/display.h"
#include "base/logging.h"
#include "base/stringutil.h"
@@ -153,11 +154,15 @@ void TextDrawerWin32::MeasureStringRect(const char *str, size_t len, const Bound
}
std::string toMeasure = std::string(str, len);
if (align & FLAG_WRAP_TEXT) {
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
if (wrap) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w);
WrapString(toMeasure, toMeasure.c_str(), rotated ? bounds.h : bounds.w, wrap);
}
TEXTMETRIC metrics{};
GetTextMetrics(ctx_->hDC, &metrics);
std::vector<std::string> lines;
SplitString(toMeasure, '\n', lines);
float total_w = 0.0f;
@@ -184,19 +189,114 @@ void TextDrawerWin32::MeasureStringRect(const char *str, size_t len, const Bound
if (total_w < entry->width * fontScaleX_) {
total_w = entry->width * fontScaleX_;
}
total_h += entry->height * fontScaleY_;
int h = i == lines.size() - 1 ? entry->height : metrics.tmHeight + metrics.tmExternalLeading;
total_h += h * fontScaleY_;
}
*w = total_w * dpiScale_;
*h = total_h * dpiScale_;
}
void TextDrawerWin32::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, int align) {
if (!strlen(str)) {
bitmapData.clear();
return;
}
std::wstring wstr = ConvertUTF8ToWString(ReplaceAll(str, "\n", "\r\n"));
SIZE size;
auto iter = fontMap_.find(fontHash_);
if (iter != fontMap_.end()) {
SelectObject(ctx_->hDC, iter->second->hFont);
}
// Set text properties
SetTextColor(ctx_->hDC, 0xFFFFFF);
SetBkColor(ctx_->hDC, 0);
SetTextAlign(ctx_->hDC, TA_TOP);
// This matters for multi-line text - DT_CENTER is horizontal only.
UINT dtAlign = (align & ALIGN_HCENTER) == 0 ? DT_LEFT : DT_CENTER;
RECT textRect = { 0 };
DrawTextExW(ctx_->hDC, (LPWSTR)wstr.c_str(), (int)wstr.size(), &textRect, DT_HIDEPREFIX | DT_TOP | dtAlign | DT_CALCRECT, 0);
size.cx = textRect.right;
size.cy = textRect.bottom;
if (size.cx > MAX_TEXT_WIDTH)
size.cx = MAX_TEXT_WIDTH;
if (size.cy > MAX_TEXT_HEIGHT)
size.cy = MAX_TEXT_HEIGHT;
// Prevent zero-sized textures, which can occur. Not worth to avoid
// creating the texture altogether in this case. One example is a string
// containing only '\r\n', see issue #10764.
if (size.cx == 0)
size.cx = 1;
if (size.cy == 0)
size.cy = 1;
entry.texture = nullptr;
entry.width = size.cx;
entry.height = size.cy;
entry.bmWidth = (size.cx + 3) & ~3;
entry.bmHeight = (size.cy + 3) & ~3;
entry.lastUsedFrame = frameCount_;
RECT rc = { 0 };
rc.right = entry.bmWidth;
rc.bottom = entry.bmHeight;
FillRect(ctx_->hDC, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
DrawTextExW(ctx_->hDC, (LPWSTR)wstr.c_str(), (int)wstr.size(), &rc, DT_HIDEPREFIX | DT_TOP | dtAlign, 0);
// Convert the bitmap to a Thin3D compatible array of 16-bit pixels. Can't use a single channel format
// because we need white. Well, we could using swizzle, but not all our backends support that.
if (texFormat == Draw::DataFormat::R8G8B8A8_UNORM || texFormat == Draw::DataFormat::B8G8R8A8_UNORM) {
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint32_t));
uint32_t *bitmapData32 = (uint32_t *)&bitmapData[0];
for (int y = 0; y < entry.bmHeight; y++) {
for (int x = 0; x < entry.bmWidth; x++) {
uint8_t bAlpha = (uint8_t)(ctx_->pBitmapBits[MAX_TEXT_WIDTH * y + x] & 0xff);
bitmapData32[entry.bmWidth * y + x] = (bAlpha << 24) | 0x00ffffff;
}
}
} else if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) {
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t));
uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0];
for (int y = 0; y < entry.bmHeight; y++) {
for (int x = 0; x < entry.bmWidth; x++) {
uint8_t bAlpha = (uint8_t)((ctx_->pBitmapBits[MAX_TEXT_WIDTH * y + x] & 0xff) >> 4);
bitmapData16[entry.bmWidth * y + x] = (bAlpha) | 0xfff0;
}
}
} else if (texFormat == Draw::DataFormat::A4R4G4B4_UNORM_PACK16) {
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t));
uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0];
for (int y = 0; y < entry.bmHeight; y++) {
for (int x = 0; x < entry.bmWidth; x++) {
uint8_t bAlpha = (uint8_t)((ctx_->pBitmapBits[MAX_TEXT_WIDTH * y + x] & 0xff) >> 4);
bitmapData16[entry.bmWidth * y + x] = (bAlpha << 12) | 0x0fff;
}
}
} else if (texFormat == Draw::DataFormat::R8_UNORM) {
bitmapData.resize(entry.bmWidth * entry.bmHeight);
for (int y = 0; y < entry.bmHeight; y++) {
for (int x = 0; x < entry.bmWidth; x++) {
uint8_t bAlpha = ctx_->pBitmapBits[MAX_TEXT_WIDTH * y + x] & 0xff;
bitmapData[entry.bmWidth * y + x] = bAlpha;
}
}
} else {
ELOG("Bad TextDrawer format");
assert(false);
}
}
void TextDrawerWin32::DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align) {
using namespace Draw;
if (!strlen(str))
return;
CacheKey key{ std::string(str), fontHash_ };
target.Flush(true);
TextStringEntry *entry;
@@ -206,52 +306,6 @@ void TextDrawerWin32::DrawString(DrawBuffer &target, const char *str, float x, f
entry = iter->second.get();
entry->lastUsedFrame = frameCount_;
} else {
// Render the string to our bitmap and save to a GL texture.
std::wstring wstr = ConvertUTF8ToWString(ReplaceAll(str, "\n", "\r\n"));
SIZE size;
auto iter = fontMap_.find(fontHash_);
if (iter != fontMap_.end()) {
SelectObject(ctx_->hDC, iter->second->hFont);
}
// Set text properties
SetTextColor(ctx_->hDC, 0xFFFFFF);
SetBkColor(ctx_->hDC, 0);
SetTextAlign(ctx_->hDC, TA_TOP);
// This matters for multi-line text - DT_CENTER is horizontal only.
UINT dtAlign = (align & ALIGN_HCENTER) == 0 ? DT_LEFT : DT_CENTER;
RECT textRect = { 0 };
DrawTextExW(ctx_->hDC, (LPWSTR)wstr.c_str(), (int)wstr.size(), &textRect, DT_HIDEPREFIX | DT_TOP | dtAlign | DT_CALCRECT, 0);
size.cx = textRect.right;
size.cy = textRect.bottom;
if (size.cx > MAX_TEXT_WIDTH)
size.cx = MAX_TEXT_WIDTH;
if (size.cy > MAX_TEXT_HEIGHT)
size.cy = MAX_TEXT_HEIGHT;
// Prevent zero-sized textures, which can occur. Not worth to avoid
// creating the texture altogether in this case. One example is a string
// containing only '\r\n', see issue #10764.
if (size.cx == 0)
size.cx = 1;
if (size.cy == 0)
size.cy = 1;
entry = new TextStringEntry();
entry->width = size.cx;
entry->height = size.cy;
entry->bmWidth = (size.cx + 3) & ~3;
entry->bmHeight = (size.cy + 3) & ~3;
entry->lastUsedFrame = frameCount_;
RECT rc = { 0 };
rc.right = entry->bmWidth;
rc.bottom = entry->bmHeight;
FillRect(ctx_->hDC, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
DrawTextExW(ctx_->hDC, (LPWSTR)wstr.c_str(), (int)wstr.size(), &rc, DT_HIDEPREFIX | DT_TOP | dtAlign, 0);
DataFormat texFormat;
// For our purposes these are equivalent, so just choose the supported one. D3D can emulate them.
if (draw_->GetDataFormatSupport(Draw::DataFormat::A4R4G4B4_UNORM_PACK16) & FMT_TEXTURE)
@@ -263,39 +317,14 @@ void TextDrawerWin32::DrawString(DrawBuffer &target, const char *str, float x, f
else
texFormat = Draw::DataFormat::R8G8B8A8_UNORM;
entry = new TextStringEntry();
// Convert the bitmap to a Thin3D compatible array of 16-bit pixels. Can't use a single channel format
// because we need white. Well, we could using swizzle, but not all our backends support that.
TextureDesc desc{};
uint32_t *bitmapData32 = nullptr;
uint16_t *bitmapData16 = nullptr;
if (texFormat == Draw::DataFormat::R8G8B8A8_UNORM || texFormat == Draw::DataFormat::B8G8R8A8_UNORM) {
bitmapData32 = new uint32_t[entry->bmWidth * entry->bmHeight];
for (int y = 0; y < entry->bmHeight; y++) {
for (int x = 0; x < entry->bmWidth; x++) {
uint8_t bAlpha = (uint8_t)(ctx_->pBitmapBits[MAX_TEXT_WIDTH * y + x] & 0xff);
bitmapData32[entry->bmWidth * y + x] = (bAlpha << 24) | 0x00ffffff;
}
}
desc.initData.push_back((uint8_t *)bitmapData32);
} else if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) {
bitmapData16 = new uint16_t[entry->bmWidth * entry->bmHeight];
for (int y = 0; y < entry->bmHeight; y++) {
for (int x = 0; x < entry->bmWidth; x++) {
uint8_t bAlpha = (uint8_t)((ctx_->pBitmapBits[MAX_TEXT_WIDTH * y + x] & 0xff) >> 4);
bitmapData16[entry->bmWidth * y + x] = (bAlpha) | 0xfff0;
}
}
desc.initData.push_back((uint8_t *)bitmapData16);
} else if (texFormat == Draw::DataFormat::A4R4G4B4_UNORM_PACK16) {
bitmapData16 = new uint16_t[entry->bmWidth * entry->bmHeight];
for (int y = 0; y < entry->bmHeight; y++) {
for (int x = 0; x < entry->bmWidth; x++) {
uint8_t bAlpha = (uint8_t)((ctx_->pBitmapBits[MAX_TEXT_WIDTH * y + x] & 0xff) >> 4);
bitmapData16[entry->bmWidth * y + x] = (bAlpha << 12) | 0x0fff;
}
}
desc.initData.push_back((uint8_t *)bitmapData16);
}
std::vector<uint8_t> bitmapData;
DrawStringBitmap(bitmapData, *entry, texFormat, str, align);
desc.initData.push_back(&bitmapData[0]);
desc.type = TextureType::LINEAR2D;
desc.format = texFormat;
@@ -305,14 +334,12 @@ void TextDrawerWin32::DrawString(DrawBuffer &target, const char *str, float x, f
desc.mipLevels = 1;
desc.tag = "TextDrawer";
entry->texture = draw_->CreateTexture(desc);
if (bitmapData16)
delete[] bitmapData16;
if (bitmapData32)
delete[] bitmapData32;
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
}
draw_->BindTexture(0, entry->texture);
if (entry->texture) {
draw_->BindTexture(0, entry->texture);
}
// Okay, the texture is bound, let's draw.
float w = entry->width * fontScaleX_ * dpiScale_;
@@ -320,8 +347,10 @@ void TextDrawerWin32::DrawString(DrawBuffer &target, const char *str, float x, f
float u = entry->width / (float)entry->bmWidth;
float v = entry->height / (float)entry->bmHeight;
DrawBuffer::DoAlign(align, &x, &y, &w, &h);
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, u, v, color);
target.Flush(true);
if (entry->texture) {
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, u, v, color);
target.Flush(true);
}
}
void TextDrawerWin32::RecreateFonts() {
@@ -340,29 +369,6 @@ void TextDrawerWin32::ClearCache() {
sizeCache_.clear();
}
void TextDrawerWin32::DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) {
float x = bounds.x;
float y = bounds.y;
if (align & ALIGN_HCENTER) {
x = bounds.centerX();
} else if (align & ALIGN_RIGHT) {
x = bounds.x2();
}
if (align & ALIGN_VCENTER) {
y = bounds.centerY();
} else if (align & ALIGN_BOTTOM) {
y = bounds.y2();
}
std::string toDraw = str;
if (align & FLAG_WRAP_TEXT) {
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
WrapString(toDraw, str, rotated ? bounds.h : bounds.w);
}
DrawString(target, toDraw.c_str(), x, y, color, align);
}
void TextDrawerWin32::OncePerFrame() {
frameCount_++;
// If DPI changed (small-mode, future proper monitor DPI support), drop everything.
+2 -2
View File
@@ -24,7 +24,7 @@ public:
void MeasureString(const char *str, size_t len, float *w, float *h) override;
void MeasureStringRect(const char *str, size_t len, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
void DrawString(DrawBuffer &target, const char *str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
void DrawStringRect(DrawBuffer &target, const char *str, const Bounds &bounds, uint32_t color, int align) override;
void DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, const char *str, int align = ALIGN_TOPLEFT) override;
// Use for housekeeping like throwing out old strings.
void OncePerFrame() override;
@@ -40,4 +40,4 @@ protected:
std::map<CacheKey, std::unique_ptr<TextMeasureEntry>> sizeCache_;
};
#endif
#endif
+5
View File
@@ -14,6 +14,7 @@ std::string I18NRepo::LanguageID() {
}
void I18NRepo::Clear() {
std::lock_guard<std::mutex> guard(catsLock_);
for (auto iter = cats_.begin(); iter != cats_.end(); ++iter) {
iter->second.reset();
}
@@ -33,6 +34,7 @@ const char *I18NCategory::T(const char *key, const char *def) {
// ILOG("translation key found in %s: %s", name_.c_str(), key);
return iter->second.text.c_str();
} else {
std::lock_guard<std::mutex> guard(missedKeyLock_);
if (def)
missedKeyLog_[key] = def;
else
@@ -53,6 +55,7 @@ void I18NCategory::SetMap(const std::map<std::string, std::string> &m) {
}
std::shared_ptr<I18NCategory> I18NRepo::GetCategory(const char *category) {
std::lock_guard<std::mutex> guard(catsLock_);
auto iter = cats_.find(category);
if (iter != cats_.end()) {
return iter->second;
@@ -94,6 +97,7 @@ bool I18NRepo::LoadIni(const std::string &languageID, const std::string &overrid
const std::vector<IniFile::Section> &sections = ini.Sections();
std::lock_guard<std::mutex> guard(catsLock_);
for (auto iter = sections.begin(); iter != sections.end(); ++iter) {
if (iter->name() != "") {
cats_[iter->name()].reset(LoadSection(&(*iter), iter->name().c_str()));
@@ -116,6 +120,7 @@ I18NCategory *I18NRepo::LoadSection(const IniFile::Section *section, const char
void I18NRepo::SaveIni(const std::string &languageID) {
IniFile ini;
ini.Load(GetIniPath(languageID));
std::lock_guard<std::mutex> guard(catsLock_);
for (auto iter = cats_.begin(); iter != cats_.end(); ++iter) {
std::string categoryName = iter->first;
IniFile::Section *section = ini.GetOrCreateSection(categoryName.c_str());
+6 -1
View File
@@ -10,6 +10,7 @@
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
@@ -44,20 +45,22 @@ public:
}
const std::map<std::string, std::string> &Missed() const {
std::lock_guard<std::mutex> guard(missedKeyLock_);
return missedKeyLog_;
}
void SetMap(const std::map<std::string, std::string> &m);
const std::map<std::string, I18NEntry> &GetMap() { return map_; }
void ClearMissed() { missedKeyLog_.clear(); }
const char *GetName() const { return name_.c_str(); }
private:
I18NCategory(I18NRepo *repo, const char *name) : name_(name) {}
void SetMap(const std::map<std::string, std::string> &m);
std::string name_;
std::map<std::string, I18NEntry> map_;
mutable std::mutex missedKeyLock_;
std::map<std::string, std::string> missedKeyLog_;
// Noone else can create these.
@@ -79,6 +82,7 @@ public:
std::shared_ptr<I18NCategory> GetCategory(const char *categoryName);
bool HasCategory(const char *categoryName) const {
std::lock_guard<std::mutex> guard(catsLock_);
return cats_.find(categoryName) != cats_.end();
}
const char *T(const char *category, const char *key, const char *def = 0);
@@ -89,6 +93,7 @@ private:
I18NCategory *LoadSection(const IniFile::Section *section, const char *name);
void SaveSection(IniFile &ini, IniFile::Section *section, std::shared_ptr<I18NCategory> cat);
mutable std::mutex catsLock_;
std::map<std::string, std::shared_ptr<I18NCategory>> cats_;
std::string languageID_;
+1
View File
@@ -24,6 +24,7 @@ int JsonGet::numChildren() const {
int count = 0;
if (value_.getTag() == JSON_OBJECT || value_.getTag() == JSON_ARRAY) {
for (auto it : value_) {
(void)it;
count++;
}
}
+4
View File
@@ -569,9 +569,13 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCal
}
}
break;
default:
break;
}
}
break;
default:
break;
}
delete steps[i];
}
+4 -3
View File
@@ -10,13 +10,14 @@ void SameThreadExecutor::Run(std::function<void()> func) {
}
void NewThreadExecutor::Run(std::function<void()> func) {
thread_ = std::thread(func);
threads_.push_back(std::thread(func));
}
NewThreadExecutor::~NewThreadExecutor() {
// If Run was ever called...
if (thread_.joinable())
thread_.join();
for (auto &thread : threads_)
thread.join();
threads_.clear();
}
} // namespace threading
+2 -1
View File
@@ -2,6 +2,7 @@
#include <functional>
#include <thread>
#include <vector>
namespace threading {
@@ -23,7 +24,7 @@ public:
void Run(std::function<void()> func) override;
private:
std::thread thread_;
std::vector<std::thread> threads_;
};
} // namespace threading
+68 -17
View File
@@ -1,4 +1,5 @@
#include <cstring>
#include "gfx_es2/draw_buffer.h"
#include "util/text/utf8.h"
#include "util/text/wrap_text.h"
@@ -75,39 +76,58 @@ std::string WordWrapper::Wrapped() {
}
bool WordWrapper::WrapBeforeWord() {
if (x_ + wordWidth_ > maxW_ && out_.size() > 0) {
if (IsShy(out_[out_.size() - 1])) {
// Soft hyphen, replace it with a real hyphen since we wrapped at it.
// TODO: There's an edge case here where the hyphen might not fit.
out_[out_.size() - 1] = '-';
if (flags_ & FLAG_WRAP_TEXT) {
if (x_ + wordWidth_ > maxW_ && !out_.empty()) {
if (IsShy(out_[out_.size() - 1])) {
// Soft hyphen, replace it with a real hyphen since we wrapped at it.
// TODO: There's an edge case here where the hyphen might not fit.
out_[out_.size() - 1] = '-';
}
out_ += "\n";
lastLineStart_ = out_.size();
x_ = 0.0f;
forceEarlyWrap_ = false;
return true;
}
}
if (flags_ & FLAG_ELLIPSIZE_TEXT) {
if (x_ + wordWidth_ > maxW_) {
if (!out_.empty() && IsSpace(out_[out_.size() - 1])) {
out_[out_.size() - 1] = '.';
out_ += "..";
} else {
out_ += "...";
}
x_ = maxW_;
}
out_ += "\n";
lastLineStart_ = out_.size();
x_ = 0.0f;
forceEarlyWrap_ = false;
return true;
}
return false;
}
void WordWrapper::AppendWord(int endIndex, bool addNewline) {
int nextWordIndex = lastIndex_;
int lastWordStartIndex = lastIndex_;
if (WrapBeforeWord()) {
// Advance to the first non-whitespace UTF-8 character in the following word (if any) to prevent starting the new line with a whitespace
UTF8 utf8Word(str_, nextWordIndex);
while (nextWordIndex < endIndex) {
UTF8 utf8Word(str_, lastWordStartIndex);
while (lastWordStartIndex < endIndex) {
const uint32_t c = utf8Word.next();
if (!IsSpace(c)) {
break;
}
nextWordIndex = utf8Word.byteIndex();
lastWordStartIndex = utf8Word.byteIndex();
}
}
// This will include the newline.
out_.append(str_ + nextWordIndex, str_ + endIndex);
if (addNewline) {
if (x_ < maxW_) {
out_.append(str_ + lastWordStartIndex, str_ + endIndex);
} else {
scanForNewline_ = true;
}
if (addNewline && (flags_ & FLAG_WRAP_TEXT)) {
out_ += "\n";
lastLineStart_ = out_.size();
scanForNewline_ = false;
} else {
// We may have appended a newline - check.
size_t pos = out_.substr(lastLineStart_).find_last_of("\n");
@@ -129,6 +149,10 @@ void WordWrapper::Wrap() {
return;
}
if (flags_ & FLAG_ELLIPSIZE_TEXT) {
ellipsisWidth_ = MeasureWidth("...", 3);
}
for (UTF8 utf(str_); !utf.end(); ) {
int beforeIndex = utf.byteIndex();
uint32_t c = utf.next();
@@ -142,6 +166,13 @@ void WordWrapper::Wrap() {
wordWidth_ = 0.0f;
// We wrapped once, so stop forcing.
forceEarlyWrap_ = false;
scanForNewline_ = false;
continue;
}
if (scanForNewline_) {
// We're discarding the rest of the characters until a newline (no wrapping.)
lastIndex_ = afterIndex;
continue;
}
@@ -175,13 +206,33 @@ void WordWrapper::Wrap() {
}
// Now, add the word so far (without this latest character) and break.
AppendWord(beforeIndex, true);
x_ = 0.0f;
if (lastLineStart_ != out_.size()) {
x_ = MeasureWidth(out_.c_str() + lastLineStart_, out_.size() - lastLineStart_);
} else {
x_ = 0.0f;
}
wordWidth_ = 0.0f;
forceEarlyWrap_ = false;
// The current character will be handled as part of the next word.
continue;
}
if ((flags_ & FLAG_ELLIPSIZE_TEXT) && wordWidth_ > 0.0f && x_ + newWordWidth + ellipsisWidth_ > maxW_) {
if ((flags_ & FLAG_WRAP_TEXT) == 0) {
// Now, add the word so far (without this latest character) and show the ellipsis.
AppendWord(beforeIndex, true);
if (lastLineStart_ != out_.size()) {
x_ = MeasureWidth(out_.c_str() + lastLineStart_, out_.size() - lastLineStart_);
} else {
x_ = 0.0f;
}
wordWidth_ = 0.0f;
forceEarlyWrap_ = false;
// The current character will be handled as part of the next word.
continue;
}
}
wordWidth_ = newWordWidth;
// Is this the end of a word via punctuation / CJK?
+8 -2
View File
@@ -4,8 +4,8 @@
class WordWrapper {
public:
WordWrapper(const char *str, float maxW)
: str_(str), maxW_(maxW) {
WordWrapper(const char *str, float maxW, int flags)
: str_(str), maxW_(maxW), flags_(flags) {
}
std::string Wrapped();
@@ -23,7 +23,9 @@ protected:
const char *const str_;
const float maxW_;
const int flags_;
std::string out_;
// Index of last output / start of current word.
int lastIndex_ = 0;
// Index of last line start.
@@ -32,6 +34,10 @@ protected:
float x_ = 0.0f;
// Most recent width of word since last index.
float wordWidth_ = 0.0f;
// Width of "..." when flag is set, zero otherwise.
float ellipsisWidth_ = 0.0f;
// Force the next word to cut partially and wrap.
bool forceEarlyWrap_ = false;
// Skip all characters until the next newline.
bool scanForNewline_ = false;
};
+2
View File
@@ -380,6 +380,8 @@ int main(int argc, const char* argv[])
g_Config.bMemStickInserted = true;
g_Config.bFragmentTestCache = true;
g_Config.iAudioLatency = 1;
g_Config.bEnableWlan = true;
g_Config.sMACAddress = "12:34:56:78:9A:BC";
#ifdef _WIN32
g_Config.internalDataDirectory = "";
+1 -1
View File
@@ -427,6 +427,7 @@ SOURCES_CXX += $(NATIVEDIR)/math/dataconv.cpp \
$(COREDIR)/HLE/sceNp.cpp \
$(COREDIR)/HLE/scePauth.cpp \
$(COREDIR)/HLE/sceUsbGps.cpp \
$(COREDIR)/HW/Camera.cpp \
$(COREDIR)/HW/SimpleAudioDec.cpp \
$(COREDIR)/HW/AsyncIOManager.cpp \
$(COREDIR)/HW/MediaEngine.cpp \
@@ -472,7 +473,6 @@ SOURCES_CXX += $(NATIVEDIR)/math/dataconv.cpp \
$(COREDIR)/System.cpp \
$(COREDIR)/Util/BlockAllocator.cpp \
$(COREDIR)/Util/PPGeDraw.cpp \
$(COREDIR)/Util/ppge_atlas.cpp \
$(COREDIR)/Util/AudioFormat.cpp \
$(EXTDIR)/disarm.cpp \
$(CORE_DIR)/UI/TextureUtil.cpp