mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-23 11:19:03 +02:00
Merge branch 'master' into Font-alloc/free
This commit is contained in:
67 files changed
+1599
-978
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
+191
-252
File diff suppressed because it is too large.
Load diff
+14
-15
@@ -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);
|
||||
|
||||
@@ -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
@@ -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" },
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
};
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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() {
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
};
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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> §ions = 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());
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,9 +569,13 @@ void GLQueueRunner::RunSteps(const std::vector<GLRStep *> &steps, bool skipGLCal
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
delete steps[i];
|
||||
}
|
||||
|
||||
@@ -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,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
|
||||
@@ -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?
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 = "";
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user