mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
a5431f127a
@ -609,6 +609,8 @@ add_library(${CoreLibName} ${CoreLinkType}
|
||||
Core/Dialog/PSPDialog.h
|
||||
Core/Dialog/PSPMsgDialog.cpp
|
||||
Core/Dialog/PSPMsgDialog.h
|
||||
Core/Dialog/PSPOskDialog.cpp
|
||||
Core/Dialog/PSPOskDialog.h
|
||||
Core/Dialog/PSPPlaceholderDialog.cpp
|
||||
Core/Dialog/PSPPlaceholderDialog.h
|
||||
Core/Dialog/PSPSaveDialog.cpp
|
||||
|
@ -1,6 +1,12 @@
|
||||
set(SRCS
|
||||
Debugger/Breakpoints.cpp
|
||||
Debugger/SymbolMap.cpp
|
||||
Dialog/PSPDialog.cpp
|
||||
Dialog/PSPMsgDialog.cpp
|
||||
Dialog/PSPPlaceholderDialog.cpp
|
||||
Dialog/PSPSaveDialog.cpp
|
||||
Dialog/SavedataParam.cpp
|
||||
Dialog/PSPOskDialog.cpp
|
||||
MIPS/MIPS.cpp
|
||||
MIPS/MIPSAnalyst.cpp
|
||||
MIPS/MIPSCodeUtils.cpp
|
||||
|
@ -121,6 +121,7 @@
|
||||
<ClCompile Include="Debugger\SymbolMap.cpp" />
|
||||
<ClCompile Include="Dialog\PSPDialog.cpp" />
|
||||
<ClCompile Include="Dialog\PSPMsgDialog.cpp" />
|
||||
<ClCompile Include="Dialog\PSPOskDialog.cpp" />
|
||||
<ClCompile Include="Dialog\PSPPlaceholderDialog.cpp" />
|
||||
<ClCompile Include="Dialog\PSPSaveDialog.cpp" />
|
||||
<ClCompile Include="Dialog\SavedataParam.cpp" />
|
||||
@ -263,6 +264,7 @@
|
||||
<ClInclude Include="Debugger\SymbolMap.h" />
|
||||
<ClInclude Include="Dialog\PSPDialog.h" />
|
||||
<ClInclude Include="Dialog\PSPMsgDialog.h" />
|
||||
<ClInclude Include="Dialog\PSPOskDialog.h" />
|
||||
<ClInclude Include="Dialog\PSPPlaceholderDialog.h" />
|
||||
<ClInclude Include="Dialog\PSPSaveDialog.h" />
|
||||
<ClInclude Include="Dialog\SavedataParam.h" />
|
||||
|
@ -339,6 +339,9 @@
|
||||
<ClCompile Include="Dialog\SavedataParam.cpp">
|
||||
<Filter>Dialog</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Dialog\PSPOskDialog.cpp">
|
||||
<Filter>Dialog</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ELF\ElfReader.h">
|
||||
@ -623,6 +626,9 @@
|
||||
<ClInclude Include="Dialog\SavedataParam.h">
|
||||
<Filter>Dialog</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Dialog\PSPOskDialog.h">
|
||||
<Filter>Dialog</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="CMakeLists.txt" />
|
||||
|
@ -48,6 +48,11 @@ void PSPDialog::EndDraw()
|
||||
PPGeEnd();
|
||||
}
|
||||
|
||||
void PSPDialog::DisplayMessage(std::string text)
|
||||
{
|
||||
PPGeDrawText(text.c_str(), 480/2, 100, PPGE_ALIGN_CENTER, 0.5f, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
void PSPDialog::Shutdown()
|
||||
{
|
||||
status = SCE_UTILITY_STATUS_SHUTDOWN;
|
||||
|
@ -24,6 +24,20 @@
|
||||
#define SCE_UTILITY_DIALOG_RESULT_CANCEL 1
|
||||
#define SCE_UTILITY_DIALOG_RESULT_ABORT 2
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int size; /** Size of the structure */
|
||||
int language; /** Language */
|
||||
int buttonSwap; /** Set to 1 for X/O button swap */
|
||||
int graphicsThread; /** Graphics thread priority */
|
||||
int accessThread; /** Access/fileio thread priority (SceJobThread) */
|
||||
int fontThread; /** Font thread priority (ScePafThread) */
|
||||
int soundThread; /** Sound thread priority */
|
||||
int result; /** Result */
|
||||
int reserved[4]; /** Set to 0 */
|
||||
|
||||
} pspUtilityDialogCommon;
|
||||
|
||||
|
||||
class PSPDialog
|
||||
{
|
||||
@ -49,7 +63,7 @@ public:
|
||||
void EndDraw();
|
||||
protected:
|
||||
bool IsButtonPressed(int checkButton);
|
||||
|
||||
void DisplayMessage(std::string text);
|
||||
DialogStatus status;
|
||||
|
||||
unsigned int lastButtons;
|
||||
|
@ -60,16 +60,12 @@ void PSPMsgDialog::Init(unsigned int paramAddr)
|
||||
|
||||
}
|
||||
|
||||
void PSPMsgDialog::DisplayMessage(std::string text)
|
||||
{
|
||||
PPGeDrawText(text.c_str(), 480/2, 100, PPGE_ALIGN_CENTER, 0.5f, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
void PSPMsgDialog::DisplayBack()
|
||||
{
|
||||
PPGeDrawImage(cancelButtonImg, 250, 220, 20, 20, 0, 0xFFFFFFFF);
|
||||
PPGeDrawText("Back", 270, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
void PSPMsgDialog::DisplayYesNo()
|
||||
{
|
||||
|
||||
@ -87,7 +83,6 @@ void PSPMsgDialog::DisplayYesNo()
|
||||
}
|
||||
void PSPMsgDialog::DisplayEnterBack()
|
||||
{
|
||||
|
||||
PPGeDrawImage(okButtonImg, 200, 220, 20, 20, 0, 0xFFFFFFFF);
|
||||
PPGeDrawText("Enter", 230, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
|
||||
PPGeDrawImage(cancelButtonImg, 290, 220, 20, 20, 0, 0xFFFFFFFF);
|
||||
@ -107,7 +102,6 @@ void PSPMsgDialog::Update()
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const char *text;
|
||||
if (messageDialog.type == 0) {
|
||||
char temp[256];
|
||||
|
@ -24,20 +24,6 @@
|
||||
#define SCE_UTILITY_MSGDIALOG_OPTION_YESNO 0x00000010
|
||||
#define SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO 0x00000100
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int size; /** Size of the structure */
|
||||
int language; /** Language */
|
||||
int buttonSwap; /** Set to 1 for X/O button swap */
|
||||
int graphicsThread; /** Graphics thread priority */
|
||||
int accessThread; /** Access/fileio thread priority (SceJobThread) */
|
||||
int fontThread; /** Font thread priority (ScePafThread) */
|
||||
int soundThread; /** Sound thread priority */
|
||||
int result; /** Result */
|
||||
int reserved[4]; /** Set to 0 */
|
||||
|
||||
} pspUtilityDialogCommon;
|
||||
|
||||
struct pspMessageDialog
|
||||
{
|
||||
pspUtilityDialogCommon common;
|
||||
@ -60,7 +46,6 @@ public:
|
||||
void Shutdown();
|
||||
|
||||
private :
|
||||
void DisplayMessage(std::string text);
|
||||
void DisplayBack();
|
||||
void DisplayYesNo();
|
||||
void DisplayEnterBack();
|
||||
|
238
Core/Dialog/PSPOskDialog.cpp
Normal file
238
Core/Dialog/PSPOskDialog.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "PSPOskDialog.h"
|
||||
#include "../Util/PPGeDraw.h"
|
||||
#include "../HLE/sceCtrl.h"
|
||||
|
||||
|
||||
/**
|
||||
* Enumeration for input language
|
||||
*/
|
||||
enum SceUtilityOskInputLanguage
|
||||
{
|
||||
PSP_UTILITY_OSK_LANGUAGE_DEFAULT = 0x00,
|
||||
PSP_UTILITY_OSK_LANGUAGE_JAPANESE = 0x01,
|
||||
PSP_UTILITY_OSK_LANGUAGE_ENGLISH = 0x02,
|
||||
PSP_UTILITY_OSK_LANGUAGE_FRENCH = 0x03,
|
||||
PSP_UTILITY_OSK_LANGUAGE_SPANISH = 0x04,
|
||||
PSP_UTILITY_OSK_LANGUAGE_GERMAN = 0x05,
|
||||
PSP_UTILITY_OSK_LANGUAGE_ITALIAN = 0x06,
|
||||
PSP_UTILITY_OSK_LANGUAGE_DUTCH = 0x07,
|
||||
PSP_UTILITY_OSK_LANGUAGE_PORTUGESE = 0x08,
|
||||
PSP_UTILITY_OSK_LANGUAGE_RUSSIAN = 0x09,
|
||||
PSP_UTILITY_OSK_LANGUAGE_KOREAN = 0x0a
|
||||
};
|
||||
|
||||
/**
|
||||
* Enumeration for OSK internal state
|
||||
*/
|
||||
enum SceUtilityOskState
|
||||
{
|
||||
PSP_UTILITY_OSK_DIALOG_NONE = 0, /**< No OSK is currently active */
|
||||
PSP_UTILITY_OSK_DIALOG_INITING, /**< The OSK is currently being initialized */
|
||||
PSP_UTILITY_OSK_DIALOG_INITED, /**< The OSK is initialised */
|
||||
PSP_UTILITY_OSK_DIALOG_VISIBLE, /**< The OSK is visible and ready for use */
|
||||
PSP_UTILITY_OSK_DIALOG_QUIT, /**< The OSK has been cancelled and should be shut down */
|
||||
PSP_UTILITY_OSK_DIALOG_FINISHED /**< The OSK has successfully shut down */
|
||||
};
|
||||
|
||||
/**
|
||||
* Enumeration for OSK field results
|
||||
*/
|
||||
enum SceUtilityOskResult
|
||||
{
|
||||
PSP_UTILITY_OSK_RESULT_UNCHANGED = 0,
|
||||
PSP_UTILITY_OSK_RESULT_CANCELLED,
|
||||
PSP_UTILITY_OSK_RESULT_CHANGED
|
||||
};
|
||||
|
||||
/**
|
||||
* Enumeration for input types (these are limited by initial choice of language)
|
||||
*/
|
||||
enum SceUtilityOskInputType
|
||||
{
|
||||
PSP_UTILITY_OSK_INPUTTYPE_ALL = 0x00000000,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_LATIN_DIGIT = 0x00000001,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_LATIN_SYMBOL = 0x00000002,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_LATIN_LOWERCASE = 0x00000004,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_LATIN_UPPERCASE = 0x00000008,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_DIGIT = 0x00000100,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_SYMBOL = 0x00000200,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_LOWERCASE = 0x00000400,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_UPPERCASE = 0x00000800,
|
||||
// http://en.wikipedia.org/wiki/Hiragana
|
||||
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HIRAGANA = 0x00001000,
|
||||
// http://en.wikipedia.org/wiki/Katakana
|
||||
// Half-width Katakana
|
||||
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_HALF_KATAKANA = 0x00002000,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KATAKANA = 0x00004000,
|
||||
// http://en.wikipedia.org/wiki/Kanji
|
||||
PSP_UTILITY_OSK_INPUTTYPE_JAPANESE_KANJI = 0x00008000,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_LOWERCASE = 0x00010000,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_RUSSIAN_UPPERCASE = 0x00020000,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_KOREAN = 0x00040000,
|
||||
PSP_UTILITY_OSK_INPUTTYPE_URL = 0x00080000
|
||||
};
|
||||
|
||||
/**
|
||||
* OSK Field data
|
||||
*/
|
||||
typedef struct _SceUtilityOskData
|
||||
{
|
||||
/** Unknown. Pass 0. */
|
||||
int unk_00;
|
||||
/** Unknown. Pass 0. */
|
||||
int unk_04;
|
||||
/** One of ::SceUtilityOskInputLanguage */
|
||||
int language;
|
||||
/** Unknown. Pass 0. */
|
||||
int unk_12;
|
||||
/** One or more of ::SceUtilityOskInputType (types that are selectable by pressing SELECT) */
|
||||
int inputtype;
|
||||
/** Number of lines */
|
||||
int lines;
|
||||
/** Unknown. Pass 0. */
|
||||
int unk_24;
|
||||
/** Description text */
|
||||
u32 descPtr;
|
||||
/** Initial text */
|
||||
u32 intextPtr;
|
||||
/** Length of output text */
|
||||
int outtextlength;
|
||||
/** Pointer to the output text */
|
||||
u32 outtextPtr;
|
||||
/** Result. One of ::SceUtilityOskResult */
|
||||
int result;
|
||||
/** The max text that can be input */
|
||||
int outtextlimit;
|
||||
|
||||
} SceUtilityOskData;
|
||||
|
||||
/**
|
||||
* OSK parameters
|
||||
*/
|
||||
typedef struct _SceUtilityOskParams
|
||||
{
|
||||
pspUtilityDialogCommon base;
|
||||
int datacount; /** Number of input fields */
|
||||
u32 SceUtilityOskDataPtr; /** Pointer to the start of the data for the input fields */
|
||||
int state; /** The local OSK state, one of ::SceUtilityOskState */
|
||||
int unk_60;/** Unknown. Pass 0 */
|
||||
|
||||
} SceUtilityOskParams;
|
||||
|
||||
SceUtilityOskParams oskParams;
|
||||
SceUtilityOskData oskData;
|
||||
std::string oskDesc;
|
||||
std::string oskIntext;
|
||||
std::string oskOuttext;
|
||||
|
||||
int oskParamsAddr;
|
||||
|
||||
|
||||
PSPOskDialog::PSPOskDialog() : PSPDialog() {
|
||||
|
||||
}
|
||||
|
||||
PSPOskDialog::~PSPOskDialog() {
|
||||
}
|
||||
|
||||
|
||||
// Same as get string but read out 16bit
|
||||
void PSPOskDialog::HackyGetStringWide(std::string& _string, const u32 em_address)
|
||||
{
|
||||
char stringBuffer[2048];
|
||||
char *string = stringBuffer;
|
||||
char c;
|
||||
u32 addr = em_address;
|
||||
while ((c = (Memory::Read_U16(addr))))
|
||||
{
|
||||
*string++ = c;
|
||||
addr+=2;
|
||||
}
|
||||
*string++ = '\0';
|
||||
_string = stringBuffer;
|
||||
}
|
||||
|
||||
|
||||
int PSPOskDialog::Init(u32 oskPtr)
|
||||
{
|
||||
status = SCE_UTILITY_STATUS_INITIALIZE;
|
||||
|
||||
memset(&oskParams, 0, sizeof(oskParams));
|
||||
memset(&oskData, 0, sizeof(oskData));
|
||||
oskParamsAddr = oskPtr;
|
||||
if (Memory::IsValidAddress(oskPtr))
|
||||
{
|
||||
Memory::ReadStruct(oskPtr, &oskParams);
|
||||
Memory::ReadStruct(oskParams.SceUtilityOskDataPtr, &oskData);
|
||||
HackyGetStringWide(oskDesc, oskData.descPtr);
|
||||
HackyGetStringWide(oskIntext, oskData.intextPtr);
|
||||
HackyGetStringWide(oskOuttext, oskData.outtextPtr);
|
||||
Memory::WriteStruct(oskParams.SceUtilityOskDataPtr, &oskData);
|
||||
Memory::WriteStruct(oskPtr, &oskParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PSPOskDialog::Update()
|
||||
{
|
||||
buttons = __CtrlPeekButtons();
|
||||
//__UtilityUpdate();
|
||||
if (status == SCE_UTILITY_STATUS_INITIALIZE)
|
||||
{
|
||||
status = SCE_UTILITY_STATUS_RUNNING;
|
||||
}
|
||||
else if (status == SCE_UTILITY_STATUS_RUNNING)
|
||||
{
|
||||
StartDraw();
|
||||
|
||||
DisplayMessage(oskDesc);
|
||||
PPGeDrawImage(I_CROSS, 200, 220, 20, 20, 0, 0xFFFFFFFF);
|
||||
PPGeDrawText("Ignore", 230, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
|
||||
// TODO : Dialogs should take control over input and not send them to the game while displaying
|
||||
if (IsButtonPressed(CTRL_CROSS))
|
||||
{
|
||||
status = SCE_UTILITY_STATUS_FINISHED;
|
||||
}
|
||||
EndDraw();
|
||||
}
|
||||
else if (status == SCE_UTILITY_STATUS_FINISHED)
|
||||
{
|
||||
status = SCE_UTILITY_STATUS_SHUTDOWN;
|
||||
}
|
||||
// just fake the return values to be "000000" as this will work for most cases e.g. when restricted to entering just numbers
|
||||
Memory::Write_U16(0x0030,oskData.outtextPtr);
|
||||
Memory::Write_U16(0x0030,oskData.outtextPtr+2);
|
||||
Memory::Write_U16(0x0030,oskData.outtextPtr+4);
|
||||
Memory::Write_U16(0x0030,oskData.outtextPtr+6);
|
||||
Memory::Write_U16(0x0030,oskData.outtextPtr+8);
|
||||
Memory::Write_U16(0x0030,oskData.outtextPtr+10);
|
||||
Memory::Write_U16(0x0030,oskData.outtextPtr+12);
|
||||
oskData.outtextlength = 6;
|
||||
oskParams.base.result= 0;
|
||||
oskData.result = SceUtilityOskResult::PSP_UTILITY_OSK_RESULT_CHANGED;
|
||||
Memory::WriteStruct(oskParams.SceUtilityOskDataPtr, &oskData);
|
||||
Memory::WriteStruct(oskParamsAddr, &oskParams);
|
||||
}
|
32
Core/Dialog/PSPOskDialog.h
Normal file
32
Core/Dialog/PSPOskDialog.h
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2012- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PSPDialog.h"
|
||||
#include "../Core/MemMap.h"
|
||||
class PSPOskDialog: public PSPDialog {
|
||||
public:
|
||||
PSPOskDialog();
|
||||
virtual ~PSPOskDialog();
|
||||
|
||||
virtual int Init(u32 oskPtr);
|
||||
virtual void Update();
|
||||
private:
|
||||
void HackyGetStringWide(std::string& _string, const u32 em_address);
|
||||
};
|
||||
|
@ -326,10 +326,10 @@ const HLEFunction ThreadManForUser[] =
|
||||
{0x812346E4,&WrapU_IU<sceKernelClearEventFlag>, "sceKernelClearEventFlag"},
|
||||
{0xEF9E4C70,&WrapU_I<sceKernelDeleteEventFlag>, "sceKernelDeleteEventFlag"},
|
||||
{0x1fb15a32,&WrapU_IU<sceKernelSetEventFlag>, "sceKernelSetEventFlag"},
|
||||
{0x402FCF22,&WrapV_IUUUU<sceKernelWaitEventFlag>, "sceKernelWaitEventFlag"},
|
||||
{0x328C546A,&WrapV_IUUUU<sceKernelWaitEventFlagCB>, "sceKernelWaitEventFlagCB"},
|
||||
{0x402FCF22,&WrapI_IUUUU<sceKernelWaitEventFlag>, "sceKernelWaitEventFlag"},
|
||||
{0x328C546A,&WrapI_IUUUU<sceKernelWaitEventFlagCB>, "sceKernelWaitEventFlagCB"},
|
||||
{0x30FD48F0,&WrapI_IUUUU<sceKernelPollEventFlag>, "sceKernelPollEventFlag"},
|
||||
{0xCD203292,&WrapU_V<sceKernelCancelEventFlag>, "sceKernelCancelEventFlag"},
|
||||
{0xCD203292,&WrapU_IUU<sceKernelCancelEventFlag>, "sceKernelCancelEventFlag"},
|
||||
{0xA66B0120,&WrapU_IU<sceKernelReferEventFlagStatus>, "sceKernelReferEventFlagStatus"},
|
||||
|
||||
{0x8FFDF9A2,&WrapI_IIU<sceKernelCancelSema>, "sceKernelCancelSema"},
|
||||
|
@ -19,13 +19,13 @@
|
||||
|
||||
#include "HLE.h"
|
||||
#include "../MIPS/MIPS.h"
|
||||
#include "../../Core/CoreTiming.h"
|
||||
|
||||
#include "sceKernel.h"
|
||||
#include "sceKernelThread.h"
|
||||
#include "sceKernelEventFlag.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
void __KernelEventFlagTimeout(u64 userdata, int cycleslate);
|
||||
|
||||
struct NativeEventFlag
|
||||
{
|
||||
@ -79,37 +79,162 @@ enum PspEventFlagAttributes
|
||||
enum PspEventFlagWaitTypes
|
||||
{
|
||||
/** Wait for all bits in the pattern to be set */
|
||||
PSP_EVENT_WAITAND = 0,
|
||||
PSP_EVENT_WAITAND = 0x00,
|
||||
/** Wait for one or more bits in the pattern to be set */
|
||||
PSP_EVENT_WAITOR = 1,
|
||||
PSP_EVENT_WAITOR = 0x01,
|
||||
/** Clear the entire pattern when it matches. */
|
||||
PSP_EVENT_WAITCLEARALL = 0x10,
|
||||
/** Clear the wait pattern when it matches */
|
||||
PSP_EVENT_WAITCLEAR = 0x20
|
||||
PSP_EVENT_WAITCLEAR = 0x20,
|
||||
|
||||
PSP_EVENT_WAITKNOWN = PSP_EVENT_WAITCLEAR | PSP_EVENT_WAITCLEARALL | PSP_EVENT_WAITOR,
|
||||
};
|
||||
|
||||
bool eventFlagInitComplete = false;
|
||||
int eventFlagWaitTimer = 0;
|
||||
|
||||
void __KernelEventFlagInit()
|
||||
{
|
||||
eventFlagWaitTimer = CoreTiming::RegisterEvent("EventFlagTimeout", &__KernelEventFlagTimeout);
|
||||
eventFlagInitComplete = true;
|
||||
}
|
||||
|
||||
bool __KernelEventFlagMatches(u32 *pattern, u32 bits, u8 wait, u32 outAddr)
|
||||
{
|
||||
if ((wait & PSP_EVENT_WAITOR)
|
||||
? (bits & *pattern) /* one or more bits of the mask */
|
||||
: ((bits & *pattern) == bits)) /* all the bits of the mask */
|
||||
{
|
||||
if (Memory::IsValidAddress(outAddr))
|
||||
Memory::Write_U32(*pattern, outAddr);
|
||||
|
||||
if (wait & PSP_EVENT_WAITCLEAR)
|
||||
*pattern &= ~bits;
|
||||
if (wait & PSP_EVENT_WAITCLEARALL)
|
||||
*pattern = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool __KernelUnlockEventFlagForThread(EventFlag *e, EventFlagTh &th, u32 &error, int result, bool &wokeThreads)
|
||||
{
|
||||
SceUID waitID = __KernelGetWaitID(th.tid, WAITTYPE_EVENTFLAG, error);
|
||||
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(th.tid, error);
|
||||
|
||||
// The waitID may be different after a timeout.
|
||||
if (waitID != e->GetUID())
|
||||
return true;
|
||||
|
||||
// If result is an error code, we're just letting it go.
|
||||
if (result == 0)
|
||||
{
|
||||
if (!__KernelEventFlagMatches(&e->nef.currentPattern, th.bits, th.wait, th.outAddr))
|
||||
return false;
|
||||
|
||||
e->nef.numWaitThreads--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, we set the current result since we're bailing.
|
||||
if (Memory::IsValidAddress(th.outAddr))
|
||||
Memory::Write_U32(e->nef.currentPattern, th.outAddr);
|
||||
}
|
||||
|
||||
if (timeoutPtr != 0 && eventFlagWaitTimer != 0)
|
||||
{
|
||||
// Remove any event for this thread.
|
||||
u64 cyclesLeft = CoreTiming::UnscheduleEvent(eventFlagWaitTimer, th.tid);
|
||||
Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr);
|
||||
}
|
||||
|
||||
__KernelResumeThreadFromWait(th.tid, result);
|
||||
wokeThreads = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool __KernelClearEventFlagThreads(EventFlag *e, int reason)
|
||||
{
|
||||
u32 error;
|
||||
bool wokeThreads = false;
|
||||
std::vector<EventFlagTh>::iterator iter, end;
|
||||
for (iter = e->waitingThreads.begin(), end = e->waitingThreads.end(); iter != end; ++iter)
|
||||
__KernelUnlockEventFlagForThread(e, *iter, error, reason, wokeThreads);
|
||||
e->waitingThreads.clear();
|
||||
|
||||
return wokeThreads;
|
||||
}
|
||||
|
||||
//SceUID sceKernelCreateEventFlag(const char *name, int attr, int bits, SceKernelEventFlagOptParam *opt);
|
||||
int sceKernelCreateEventFlag(const char *name, u32 flag_attr, u32 flag_initPattern, u32 optPtr)
|
||||
{
|
||||
if (!eventFlagInitComplete)
|
||||
__KernelEventFlagInit();
|
||||
|
||||
if (!name)
|
||||
{
|
||||
WARN_LOG(HLE, "%08x=sceKernelCreateEventFlag(): invalid name", SCE_KERNEL_ERROR_ERROR);
|
||||
return SCE_KERNEL_ERROR_ERROR;
|
||||
}
|
||||
|
||||
// These attributes aren't valid.
|
||||
if ((flag_attr & 0x100) != 0 || flag_attr >= 0x300)
|
||||
{
|
||||
WARN_LOG(HLE, "%08x=sceKernelCreateEventFlag(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, flag_attr);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
|
||||
}
|
||||
|
||||
EventFlag *e = new EventFlag();
|
||||
SceUID id = kernelObjects.Create(e);
|
||||
|
||||
e->nef.size = sizeof(NativeEventFlag);
|
||||
strncpy(e->nef.name, name, 32);
|
||||
strncpy(e->nef.name, name, 31);
|
||||
e->nef.name[31] = 0;
|
||||
e->nef.attr = flag_attr;
|
||||
e->nef.initPattern = flag_initPattern;
|
||||
e->nef.currentPattern = e->nef.initPattern;
|
||||
e->nef.numWaitThreads = 0;
|
||||
|
||||
DEBUG_LOG(HLE,"%i=sceKernelCreateEventFlag(\"%s\", %08x, %08x, %08x)", id, e->nef.name, e->nef.attr, e->nef.currentPattern, optPtr);
|
||||
DEBUG_LOG(HLE, "%i=sceKernelCreateEventFlag(\"%s\", %08x, %08x, %08x)", id, e->nef.name, e->nef.attr, e->nef.currentPattern, optPtr);
|
||||
|
||||
if (optPtr != 0)
|
||||
WARN_LOG(HLE, "sceKernelCreateEventFlag(%s) unsupported options parameter: %08x", name, optPtr);
|
||||
if ((flag_attr & ~PSP_EVENT_WAITMULTIPLE) != 0)
|
||||
WARN_LOG(HLE, "sceKernelCreateEventFlag(%s) unsupported attr parameter: %08x", name, flag_attr);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
u32 sceKernelCancelEventFlag(SceUID uid, u32 pattern, u32 numWaitThreadsPtr)
|
||||
{
|
||||
DEBUG_LOG(HLE, "sceKernelCancelEventFlag(%i, %08X, %08X)", uid, pattern, numWaitThreadsPtr);
|
||||
|
||||
u32 error;
|
||||
EventFlag *e = kernelObjects.Get<EventFlag>(uid, error);
|
||||
if (e)
|
||||
{
|
||||
if (Memory::IsValidAddress(numWaitThreadsPtr))
|
||||
Memory::Write_U32(e->nef.numWaitThreads, numWaitThreadsPtr);
|
||||
|
||||
e->nef.currentPattern = pattern;
|
||||
e->nef.numWaitThreads = 0;
|
||||
|
||||
if (__KernelClearEventFlagThreads(e, SCE_KERNEL_ERROR_WAIT_CANCEL))
|
||||
hleReSchedule("event flag canceled");
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return error;
|
||||
}
|
||||
|
||||
u32 sceKernelClearEventFlag(SceUID id, u32 bits)
|
||||
{
|
||||
u32 error;
|
||||
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
|
||||
if (e)
|
||||
{
|
||||
DEBUG_LOG(HLE,"sceKernelClearEventFlag(%i, %08x)", id, bits);
|
||||
DEBUG_LOG(HLE, "sceKernelClearEventFlag(%i, %08x)", id, bits);
|
||||
e->nef.currentPattern &= bits;
|
||||
// Note that it's not possible for threads to get woken up by this action.
|
||||
return 0;
|
||||
@ -123,30 +248,26 @@ u32 sceKernelClearEventFlag(SceUID id, u32 bits)
|
||||
|
||||
u32 sceKernelDeleteEventFlag(SceUID uid)
|
||||
{
|
||||
DEBUG_LOG(HLE,"sceKernelDeleteEventFlag(%i)", uid);
|
||||
return kernelObjects.Destroy<EventFlag>(uid);
|
||||
}
|
||||
DEBUG_LOG(HLE, "sceKernelDeleteEventFlag(%i)", uid);
|
||||
|
||||
u8 __KernelEventFlagMatches(u32 *pattern, u32 bits, u8 wait, u32 outAddr)
|
||||
{
|
||||
if ((wait & PSP_EVENT_WAITOR)
|
||||
? (bits & *pattern) /* one or more bits of the mask */
|
||||
: ((bits & *pattern) == bits)) /* all the bits of the mask */
|
||||
u32 error;
|
||||
EventFlag *e = kernelObjects.Get<EventFlag>(uid, error);
|
||||
if (e)
|
||||
{
|
||||
if (Memory::IsValidAddress(outAddr))
|
||||
Memory::Write_U32(*pattern, outAddr);
|
||||
bool wokeThreads = __KernelClearEventFlagThreads(e, SCE_KERNEL_ERROR_WAIT_DELETE);
|
||||
if (wokeThreads)
|
||||
hleReSchedule("event flag deleted");
|
||||
|
||||
if (wait & PSP_EVENT_WAITCLEAR)
|
||||
*pattern &= ~bits;
|
||||
return 1;
|
||||
return kernelObjects.Destroy<EventFlag>(uid);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return error;
|
||||
}
|
||||
|
||||
u32 sceKernelSetEventFlag(SceUID id, u32 bitsToSet)
|
||||
{
|
||||
u32 error;
|
||||
DEBUG_LOG(HLE,"sceKernelSetEventFlag(%i, %08x)", id, bitsToSet);
|
||||
DEBUG_LOG(HLE, "sceKernelSetEventFlag(%i, %08x)", id, bitsToSet);
|
||||
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
|
||||
if (e)
|
||||
{
|
||||
@ -154,19 +275,20 @@ u32 sceKernelSetEventFlag(SceUID id, u32 bitsToSet)
|
||||
|
||||
e->nef.currentPattern |= bitsToSet;
|
||||
|
||||
retry:
|
||||
for (size_t i = 0; i < e->waitingThreads.size(); i++)
|
||||
for (size_t i = 0; i < e->waitingThreads.size(); ++i)
|
||||
{
|
||||
EventFlagTh *t = &e->waitingThreads[i];
|
||||
if (__KernelEventFlagMatches(&e->nef.currentPattern, t->bits, t->wait, t->outAddr))
|
||||
if (__KernelUnlockEventFlagForThread(e, *t, error, 0, wokeThreads))
|
||||
{
|
||||
__KernelResumeThreadFromWait(t->tid);
|
||||
wokeThreads = true;
|
||||
e->nef.numWaitThreads--;
|
||||
e->waitingThreads.erase(e->waitingThreads.begin() + i);
|
||||
goto retry;
|
||||
// Try the one that used to be in this place next.
|
||||
--i;
|
||||
}
|
||||
}
|
||||
|
||||
if (wokeThreads)
|
||||
hleReSchedule("event flag set");
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
@ -175,43 +297,80 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
// Actually RETURNs a u32
|
||||
void sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr)
|
||||
void __KernelEventFlagTimeout(u64 userdata, int cycleslate)
|
||||
{
|
||||
DEBUG_LOG(HLE,"sceKernelWaitEventFlag(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr);
|
||||
SceUID threadID = (SceUID)userdata;
|
||||
|
||||
u32 error;
|
||||
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
|
||||
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
|
||||
if (timeoutPtr != 0)
|
||||
Memory::Write_U32(0, timeoutPtr);
|
||||
|
||||
SceUID flagID = __KernelGetWaitID(threadID, WAITTYPE_EVENTFLAG, error);
|
||||
EventFlag *e = kernelObjects.Get<EventFlag>(flagID, error);
|
||||
if (e)
|
||||
{
|
||||
EventFlagTh th;
|
||||
if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr))
|
||||
for (size_t i = 0; i < e->waitingThreads.size(); i++)
|
||||
{
|
||||
// No match - must wait.
|
||||
e->nef.numWaitThreads++;
|
||||
th.tid = __KernelGetCurThread();
|
||||
th.bits = bits;
|
||||
th.wait = wait;
|
||||
th.outAddr = outBitsPtr;
|
||||
e->waitingThreads.push_back(th);
|
||||
u32 timeout;
|
||||
if (Memory::IsValidAddress(timeoutPtr))
|
||||
timeout = Memory::Read_U32(timeoutPtr);
|
||||
EventFlagTh *t = &e->waitingThreads[i];
|
||||
if (t->tid == threadID)
|
||||
{
|
||||
bool wokeThreads;
|
||||
|
||||
__KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, 0, false); // sets RETURN
|
||||
// Do not set RETURN here; it's already set for us and we'd overwrite the wrong thread's RETURN
|
||||
// This thread isn't waiting anymore, but we'll remove it from waitingThreads later.
|
||||
// The reason is, if it times out, but what it was waiting on is DELETED prior to it
|
||||
// actually running, it will get a DELETE result instead of a TIMEOUT.
|
||||
// So, we need to remember it or we won't be able to mark it DELETE instead later.
|
||||
__KernelUnlockEventFlagForThread(e, *t, error, SCE_KERNEL_ERROR_WAIT_TIMEOUT, wokeThreads);
|
||||
e->nef.numWaitThreads--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN(error);
|
||||
}
|
||||
}
|
||||
|
||||
// Actually RETURNs a u32
|
||||
void sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr)
|
||||
void __KernelSetEventFlagTimeout(EventFlag *e, u32 timeoutPtr)
|
||||
{
|
||||
DEBUG_LOG(HLE,"sceKernelWaitEventFlagCB(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr);
|
||||
if (timeoutPtr == 0 || eventFlagWaitTimer == 0)
|
||||
return;
|
||||
|
||||
int micro = (int) Memory::Read_U32(timeoutPtr);
|
||||
|
||||
// This seems like the actual timing of timeouts on hardware.
|
||||
if (micro <= 1)
|
||||
micro = 5;
|
||||
else if (micro <= 209)
|
||||
micro = 240;
|
||||
|
||||
// This should call __KernelEventFlagTimeout() later, unless we cancel it.
|
||||
CoreTiming::ScheduleEvent(usToCycles(micro), eventFlagWaitTimer, __KernelGetCurThread());
|
||||
}
|
||||
|
||||
void __KernelEventFlagRemoveThread(EventFlag *e, SceUID threadID)
|
||||
{
|
||||
for (size_t i = 0; i < e->waitingThreads.size(); i++)
|
||||
{
|
||||
EventFlagTh *t = &e->waitingThreads[i];
|
||||
if (t->tid == threadID)
|
||||
{
|
||||
e->waitingThreads.erase(e->waitingThreads.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr)
|
||||
{
|
||||
DEBUG_LOG(HLE, "sceKernelWaitEventFlag(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr);
|
||||
|
||||
if ((wait & ~PSP_EVENT_WAITKNOWN) != 0)
|
||||
{
|
||||
WARN_LOG(HLE, "sceKernelWaitEventFlag(%i) invalid mode parameter: %08x", id, wait);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_MODE;
|
||||
}
|
||||
// Can't wait on 0, that's guaranteed to wait forever.
|
||||
if (bits == 0)
|
||||
return SCE_KERNEL_ERROR_EVF_ILPAT;
|
||||
|
||||
u32 error;
|
||||
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
|
||||
@ -220,29 +379,112 @@ void sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32
|
||||
EventFlagTh th;
|
||||
if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr))
|
||||
{
|
||||
// If this thread was left in waitingThreads after a timeout, remove it.
|
||||
// Otherwise we might write the outBitsPtr in the wrong place.
|
||||
__KernelEventFlagRemoveThread(e, __KernelGetCurThread());
|
||||
|
||||
u32 timeout = 0xFFFFFFFF;
|
||||
if (Memory::IsValidAddress(timeoutPtr))
|
||||
timeout = Memory::Read_U32(timeoutPtr);
|
||||
|
||||
// Do we allow more than one thread to wait?
|
||||
if (e->nef.numWaitThreads > 0 && (e->nef.attr & PSP_EVENT_WAITMULTIPLE) == 0)
|
||||
return SCE_KERNEL_ERROR_EVF_MULTI;
|
||||
|
||||
// No match - must wait.
|
||||
e->nef.numWaitThreads++;
|
||||
th.tid = __KernelGetCurThread();
|
||||
th.bits = bits;
|
||||
th.wait = wait;
|
||||
th.outAddr = outBitsPtr;
|
||||
// If < 5ms, sometimes hardware doesn't write this, but it's unpredictable.
|
||||
th.outAddr = timeout == 0 ? NULL : outBitsPtr;
|
||||
e->waitingThreads.push_back(th);
|
||||
u32 timeout;
|
||||
if (Memory::IsValidAddress(timeoutPtr))
|
||||
timeout = Memory::Read_U32(timeoutPtr);
|
||||
|
||||
__KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, 0, true); // sets RETURN
|
||||
__KernelSetEventFlagTimeout(e, timeoutPtr);
|
||||
__KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, timeoutPtr, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN(error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr)
|
||||
{
|
||||
DEBUG_LOG(HLE, "sceKernelWaitEventFlagCB(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr);
|
||||
|
||||
if ((wait & ~PSP_EVENT_WAITKNOWN) != 0)
|
||||
{
|
||||
WARN_LOG(HLE, "sceKernelWaitEventFlagCB(%i) invalid mode parameter: %08x", id, wait);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_MODE;
|
||||
}
|
||||
// Can't wait on 0, that's guaranteed to wait forever.
|
||||
if (bits == 0)
|
||||
return SCE_KERNEL_ERROR_EVF_ILPAT;
|
||||
|
||||
u32 error;
|
||||
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
|
||||
if (e)
|
||||
{
|
||||
EventFlagTh th;
|
||||
if (!__KernelEventFlagMatches(&e->nef.currentPattern, bits, wait, outBitsPtr))
|
||||
{
|
||||
// If this thread was left in waitingThreads after a timeout, remove it.
|
||||
// Otherwise we might write the outBitsPtr in the wrong place.
|
||||
__KernelEventFlagRemoveThread(e, __KernelGetCurThread());
|
||||
|
||||
u32 timeout = 0xFFFFFFFF;
|
||||
if (Memory::IsValidAddress(timeoutPtr))
|
||||
timeout = Memory::Read_U32(timeoutPtr);
|
||||
|
||||
// Do we allow more than one thread to wait?
|
||||
if (e->nef.numWaitThreads > 0 && (e->nef.attr & PSP_EVENT_WAITMULTIPLE) == 0)
|
||||
return SCE_KERNEL_ERROR_EVF_MULTI;
|
||||
|
||||
// No match - must wait.
|
||||
e->nef.numWaitThreads++;
|
||||
th.tid = __KernelGetCurThread();
|
||||
th.bits = bits;
|
||||
th.wait = wait;
|
||||
// If < 5ms, sometimes hardware doesn't write this, but it's unpredictable.
|
||||
th.outAddr = timeout == 0 ? NULL : outBitsPtr;
|
||||
e->waitingThreads.push_back(th);
|
||||
|
||||
__KernelSetEventFlagTimeout(e, timeoutPtr);
|
||||
__KernelWaitCurThread(WAITTYPE_EVENTFLAG, id, 0, timeoutPtr, true);
|
||||
}
|
||||
else
|
||||
hleCheckCurrentCallbacks();
|
||||
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
int sceKernelPollEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr)
|
||||
{
|
||||
DEBUG_LOG(HLE,"sceKernelPollEventFlag(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr);
|
||||
DEBUG_LOG(HLE, "sceKernelPollEventFlag(%i, %08x, %i, %08x, %08x)", id, bits, wait, outBitsPtr, timeoutPtr);
|
||||
|
||||
if ((wait & ~PSP_EVENT_WAITKNOWN) != 0)
|
||||
{
|
||||
WARN_LOG(HLE, "sceKernelPollEventFlag(%i) invalid mode parameter: %08x", id, wait);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_MODE;
|
||||
}
|
||||
// Poll seems to also fail when CLEAR and CLEARALL are used together, but not wait.
|
||||
if ((wait & PSP_EVENT_WAITCLEAR) != 0 && (wait & PSP_EVENT_WAITCLEARALL) != 0)
|
||||
{
|
||||
WARN_LOG(HLE, "sceKernelPollEventFlag(%i) invalid mode parameter: %08x", id, wait);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_MODE;
|
||||
}
|
||||
// Can't wait on 0, it never matches.
|
||||
if (bits == 0)
|
||||
return SCE_KERNEL_ERROR_EVF_ILPAT;
|
||||
|
||||
u32 error;
|
||||
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
|
||||
@ -252,6 +494,10 @@ int sceKernelPollEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti
|
||||
{
|
||||
if (Memory::IsValidAddress(outBitsPtr))
|
||||
Memory::Write_U32(e->nef.currentPattern, outBitsPtr);
|
||||
|
||||
if (e->nef.numWaitThreads > 0 && (e->nef.attr & PSP_EVENT_WAITMULTIPLE) == 0)
|
||||
return SCE_KERNEL_ERROR_EVF_MULTI;
|
||||
|
||||
// No match - return that, this is polling, not waiting.
|
||||
return SCE_KERNEL_ERROR_EVF_COND;
|
||||
}
|
||||
@ -269,7 +515,7 @@ int sceKernelPollEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti
|
||||
//int sceKernelReferEventFlagStatus(SceUID event, SceKernelEventFlagInfo *status);
|
||||
u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr)
|
||||
{
|
||||
DEBUG_LOG(HLE,"sceKernelReferEventFlagStatus(%i, %08x)", id, statusPtr);
|
||||
DEBUG_LOG(HLE, "sceKernelReferEventFlagStatus(%i, %08x)", id, statusPtr);
|
||||
u32 error;
|
||||
EventFlag *e = kernelObjects.Get<EventFlag>(id, error);
|
||||
if (e)
|
||||
@ -282,10 +528,3 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
// never seen this one
|
||||
u32 sceKernelCancelEventFlag()
|
||||
{
|
||||
ERROR_LOG(HLE,"UNIMPL: sceKernelCancelEventFlag()");
|
||||
return 0;
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ int sceKernelCreateEventFlag(const char *name, u32 flag_attr, u32 flag_initPatte
|
||||
u32 sceKernelClearEventFlag(SceUID id, u32 bits);
|
||||
u32 sceKernelDeleteEventFlag(SceUID uid);
|
||||
u32 sceKernelSetEventFlag(SceUID id, u32 bitsToSet);
|
||||
void sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr);
|
||||
void sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr);
|
||||
int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr);
|
||||
int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr);
|
||||
int sceKernelPollEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 timeoutPtr);
|
||||
u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr);
|
||||
u32 sceKernelCancelEventFlag();
|
||||
u32 sceKernelCancelEventFlag(SceUID uid, u32 pattern, u32 numWaitThreadsPtr);
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define PSP_MUTEX_ATTR_FIFO 0
|
||||
#define PSP_MUTEX_ATTR_PRIORITY 0x100
|
||||
#define PSP_MUTEX_ATTR_ALLOW_RECURSIVE 0x200
|
||||
#define PSP_MUTEX_ATTR_KNOWN (PSP_MUTEX_ATTR_PRIORITY | PSP_MUTEX_ATTR_ALLOW_RECURSIVE)
|
||||
|
||||
// Not sure about the names of these
|
||||
#define PSP_MUTEX_ERROR_NO_SUCH_MUTEX 0x800201C3
|
||||
@ -187,14 +188,21 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio
|
||||
__KernelMutexInit();
|
||||
|
||||
if (!name)
|
||||
{
|
||||
WARN_LOG(HLE, "%08x=sceKernelCreateMutex(): invalid name", SCE_KERNEL_ERROR_ERROR);
|
||||
return SCE_KERNEL_ERROR_ERROR;
|
||||
}
|
||||
if (attr >= 0xC00)
|
||||
{
|
||||
WARN_LOG(HLE, "%08x=sceKernelCreateMutex(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
|
||||
}
|
||||
|
||||
if (initialCount < 0)
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
|
||||
if ((attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1)
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
|
||||
|
||||
DEBUG_LOG(HLE, "sceKernelCreateMutex(%s, %08x, %d, %08x)", name, attr, initialCount, optionsPtr);
|
||||
|
||||
Mutex *mutex = new Mutex();
|
||||
SceUID id = kernelObjects.Create(mutex);
|
||||
|
||||
@ -210,8 +218,12 @@ int sceKernelCreateMutex(const char *name, u32 attr, int initialCount, u32 optio
|
||||
else
|
||||
__KernelMutexAcquireLock(mutex, initialCount);
|
||||
|
||||
DEBUG_LOG(HLE, "%i=sceKernelCreateMutex(%s, %08x, %d, %08x)", id, name, attr, initialCount, optionsPtr);
|
||||
|
||||
if (optionsPtr != 0)
|
||||
WARN_LOG(HLE, "sceKernelCreateMutex(%s) unsupported options parameter.", name);
|
||||
WARN_LOG(HLE, "sceKernelCreateMutex(%s) unsupported options parameter: %08x", name, optionsPtr);
|
||||
if ((attr & ~PSP_MUTEX_ATTR_KNOWN) != 0)
|
||||
WARN_LOG(HLE, "sceKernelCreateMutex(%s) unsupported attr parameter: %08x", name, attr);
|
||||
|
||||
return id;
|
||||
}
|
||||
@ -342,6 +354,11 @@ void __KernelMutexTimeout(u64 userdata, int cyclesLate)
|
||||
Memory::Write_U32(0, timeoutPtr);
|
||||
|
||||
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
|
||||
|
||||
// We intentionally don't remove from waitingThreads here yet.
|
||||
// The reason is, if it times out, but what it was waiting on is DELETED prior to it
|
||||
// actually running, it will get a DELETE result instead of a TIMEOUT.
|
||||
// So, we need to remember it or we won't be able to mark it DELETE instead later.
|
||||
}
|
||||
|
||||
void __KernelMutexThreadEnd(SceUID threadID)
|
||||
@ -486,13 +503,20 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init
|
||||
if (!mutexInitComplete)
|
||||
__KernelMutexInit();
|
||||
|
||||
DEBUG_LOG(HLE, "sceKernelCreateLwMutex(%08x, %s, %08x, %d, %08x)", workareaPtr, name, attr, initialCount, optionsPtr);
|
||||
|
||||
if (!name)
|
||||
{
|
||||
WARN_LOG(HLE, "%08x=sceKernelCreateLwMutex(): invalid name", SCE_KERNEL_ERROR_ERROR);
|
||||
return SCE_KERNEL_ERROR_ERROR;
|
||||
else if (initialCount < 0)
|
||||
}
|
||||
if (attr >= 0x400)
|
||||
{
|
||||
WARN_LOG(HLE, "%08x=sceKernelCreateLwMutex(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
|
||||
}
|
||||
|
||||
if (initialCount < 0)
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
|
||||
else if ((attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1)
|
||||
if ((attr & PSP_MUTEX_ATTR_ALLOW_RECURSIVE) == 0 && initialCount > 1)
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
|
||||
|
||||
LwMutex *mutex = new LwMutex();
|
||||
@ -515,8 +539,12 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init
|
||||
|
||||
Memory::WriteStruct(workareaPtr, &workarea);
|
||||
|
||||
DEBUG_LOG(HLE, "sceKernelCreateLwMutex(%08x, %s, %08x, %d, %08x)", workareaPtr, name, attr, initialCount, optionsPtr);
|
||||
|
||||
if (optionsPtr != 0)
|
||||
WARN_LOG(HLE, "sceKernelCreateLwMutex(%s) unsupported options parameter.", name);
|
||||
WARN_LOG(HLE, "sceKernelCreateLwMutex(%s) unsupported options parameter: %08x", name, optionsPtr);
|
||||
if ((attr & ~PSP_MUTEX_ATTR_KNOWN) != 0)
|
||||
WARN_LOG(HLE, "sceKernelCreateLwMutex(%s) unsupported attr parameter: %08x", name, attr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -669,6 +697,11 @@ void __KernelLwMutexTimeout(u64 userdata, int cyclesLate)
|
||||
Memory::Write_U32(0, timeoutPtr);
|
||||
|
||||
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
|
||||
|
||||
// We intentionally don't remove from waitingThreads here yet.
|
||||
// The reason is, if it times out, but what it was waiting on is DELETED prior to it
|
||||
// actually running, it will get a DELETE result instead of a TIMEOUT.
|
||||
// So, we need to remember it or we won't be able to mark it DELETE instead later.
|
||||
}
|
||||
|
||||
void __KernelWaitLwMutex(LwMutex *mutex, u32 timeoutPtr)
|
||||
|
@ -149,7 +149,7 @@ int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr)
|
||||
if (newCount > s->ns.maxCount)
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
|
||||
|
||||
if (numWaitThreadsPtr)
|
||||
if (Memory::IsValidAddress(numWaitThreadsPtr))
|
||||
Memory::Write_U32(s->ns.numWaitThreads, numWaitThreadsPtr);
|
||||
|
||||
if (newCount < 0)
|
||||
@ -177,7 +177,15 @@ int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32
|
||||
__KernelSemaInit();
|
||||
|
||||
if (!name)
|
||||
{
|
||||
WARN_LOG(HLE, "%08x=sceKernelCreateSema(): invalid name", SCE_KERNEL_ERROR_ERROR);
|
||||
return SCE_KERNEL_ERROR_ERROR;
|
||||
}
|
||||
if (attr >= 0x200)
|
||||
{
|
||||
WARN_LOG(HLE, "%08x=sceKernelCreateSema(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, attr);
|
||||
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
|
||||
}
|
||||
|
||||
Semaphore *s = new Semaphore;
|
||||
SceUID id = kernelObjects.Create(s);
|
||||
@ -194,7 +202,9 @@ int sceKernelCreateSema(const char* name, u32 attr, int initVal, int maxVal, u32
|
||||
DEBUG_LOG(HLE, "%i=sceKernelCreateSema(%s, %08x, %i, %i, %08x)", id, s->ns.name, s->ns.attr, s->ns.initCount, s->ns.maxCount, optionPtr);
|
||||
|
||||
if (optionPtr != 0)
|
||||
WARN_LOG(HLE, "sceKernelCreateSema(%s) unsupported options parameter.", name);
|
||||
WARN_LOG(HLE, "sceKernelCreateSema(%s) unsupported options parameter: %08x", name, optionPtr);
|
||||
if ((attr & ~PSP_SEMA_ATTR_PRIORITY) != 0)
|
||||
WARN_LOG(HLE, "sceKernelCreateSema(%s) unsupported attr parameter: %08x", name, attr);
|
||||
|
||||
return id;
|
||||
}
|
||||
@ -296,6 +306,9 @@ void __KernelSemaTimeout(u64 userdata, int cycleslate)
|
||||
if (s)
|
||||
{
|
||||
// This thread isn't waiting anymore, but we'll remove it from waitingThreads later.
|
||||
// The reason is, if it times out, but what it was waiting on is DELETED prior to it
|
||||
// actually running, it will get a DELETE result instead of a TIMEOUT.
|
||||
// So, we need to remember it or we won't be able to mark it DELETE instead later.
|
||||
s->ns.numWaitThreads--;
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,8 @@ u32 sceRtcGetTickResolution()
|
||||
|
||||
u32 sceRtcGetCurrentTick(u32 tickPtr)
|
||||
{
|
||||
DEBUG_LOG(HLE, "sceRtcGetCurrentTick(%08x)", tickPtr);
|
||||
//Don't spam the log
|
||||
//DEBUG_LOG(HLE, "sceRtcGetCurrentTick(%08x)", tickPtr);
|
||||
|
||||
u64 curTick = __RtcGetCurrentTick();
|
||||
if (Memory::IsValidAddress(tickPtr))
|
||||
|
@ -27,15 +27,15 @@
|
||||
#include "../Dialog/PSPSaveDialog.h"
|
||||
#include "../Dialog/PSPMsgDialog.h"
|
||||
#include "../Dialog/PSPPlaceholderDialog.h"
|
||||
#include "../Dialog/PSPOskDialog.h"
|
||||
|
||||
PSPSaveDialog saveDialog;
|
||||
PSPMsgDialog msgDialog;
|
||||
PSPPlaceholderDialog oskDialog;
|
||||
PSPOskDialog oskDialog;
|
||||
PSPPlaceholderDialog netDialog;
|
||||
|
||||
void __UtilityInit()
|
||||
{
|
||||
|
||||
SavedataParam::Init();
|
||||
}
|
||||
|
||||
@ -66,7 +66,6 @@ void sceUtilitySavedataUpdate(u32 unknown)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#define PSP_AV_MODULE_AVCODEC 0
|
||||
#define PSP_AV_MODULE_SASCORE 1
|
||||
#define PSP_AV_MODULE_ATRAC3PLUS 2 // Requires PSP_AV_MODULE_AVCODEC loading first
|
||||
@ -118,29 +117,34 @@ u32 sceUtilityMsgDialogGetStatus()
|
||||
|
||||
// On screen keyboard
|
||||
|
||||
int sceUtilityOskInitStart(unsigned int)
|
||||
int sceUtilityOskInitStart(u32 oskPtr)
|
||||
{
|
||||
DEBUG_LOG(HLE,"FAKE sceUtilityOskInitStart(%i)", PARAM(0));
|
||||
oskDialog.Init();
|
||||
return 0;
|
||||
ERROR_LOG(HLE,"FAKE sceUtilityOskInitStart(%i)", PARAM(0));
|
||||
return oskDialog.Init(oskPtr);
|
||||
}
|
||||
|
||||
int sceUtilityOskShutdownStart()
|
||||
{
|
||||
DEBUG_LOG(HLE,"FAKE sceUtilityOskShutdownStart(%i)", PARAM(0));
|
||||
ERROR_LOG(HLE,"FAKE sceUtilityOskShutdownStart(%i)", PARAM(0));
|
||||
oskDialog.Shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sceUtilityOskUpdate(unsigned int unknown)
|
||||
{
|
||||
DEBUG_LOG(HLE,"FAKE sceUtilityOskUpdate(%i)", unknown);
|
||||
ERROR_LOG(HLE,"FAKE sceUtilityOskUpdate(%i)", unknown);
|
||||
oskDialog.Update();
|
||||
}
|
||||
|
||||
int sceUtilityOskGetStatus()
|
||||
{
|
||||
return oskDialog.GetStatus();
|
||||
int status = oskDialog.GetStatus();
|
||||
// Seems that 4 is the cancelled status for OSK?
|
||||
if (status == 4)
|
||||
{
|
||||
status = 5;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
@ -246,7 +246,6 @@ inline void Write_Float(float f, u32 address)
|
||||
|
||||
// Reads a zero-terminated string from memory at the address.
|
||||
void GetString(std::string& _string, const u32 _Address);
|
||||
|
||||
u8* GetPointer(const u32 address);
|
||||
bool IsValidAddress(const u32 address);
|
||||
|
||||
|
@ -98,6 +98,7 @@ LOCAL_SRC_FILES := \
|
||||
$(SRC)/Core/Debugger/SymbolMap.cpp \
|
||||
$(SRC)/Core/Dialog/PSPDialog.cpp \
|
||||
$(SRC)/Core/Dialog/PSPMsgDialog.cpp \
|
||||
$(SRC)/Core/Dialog/PSPOskDialog.cpp \
|
||||
$(SRC)/Core/Dialog/PSPPlaceholderDialog.cpp \
|
||||
$(SRC)/Core/Dialog/PSPSaveDialog.cpp \
|
||||
$(SRC)/Core/Dialog/SavedataParam.cpp \
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit e9b4496e32bb592dfff14b93abf284ff20783d3c
|
||||
Subproject commit b41feca84bd700770791b17bde2d0a7bf910538d
|
80
test.py
80
test.py
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python
|
||||
# Automated script to run the pspautotests test suite in PPSSPP.
|
||||
|
||||
import sys
|
||||
@ -25,6 +25,7 @@ class Command(object):
|
||||
def target():
|
||||
self.process = subprocess.Popen(self.cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
self.output, _ = self.process.communicate()
|
||||
self.output = self.output.decode("utf-8")
|
||||
|
||||
thread = threading.Thread(target=target)
|
||||
thread.start()
|
||||
@ -57,6 +58,15 @@ tests_good = [
|
||||
"misc/testgp",
|
||||
"string/string",
|
||||
"gpu/callbacks/ge_callbacks",
|
||||
"threads/events/events",
|
||||
"threads/events/cancel/cancel",
|
||||
"threads/events/clear/clear",
|
||||
"threads/events/create/create",
|
||||
"threads/events/delete/delete",
|
||||
"threads/events/poll/poll",
|
||||
"threads/events/refer/refer",
|
||||
"threads/events/set/set",
|
||||
"threads/events/wait/wait",
|
||||
"threads/lwmutex/create/create",
|
||||
"threads/lwmutex/delete/delete",
|
||||
"threads/lwmutex/lock/lock",
|
||||
@ -83,6 +93,7 @@ tests_good = [
|
||||
"threads/semaphores/wait/wait",
|
||||
"power/power",
|
||||
"umd/callbacks/umd",
|
||||
"umd/wait/wait",
|
||||
"io/directory/directory",
|
||||
]
|
||||
|
||||
@ -114,7 +125,6 @@ tests_next = [
|
||||
"video/pmf_simple/pmf_simple",
|
||||
|
||||
# Currently hang or crash.
|
||||
"threads/events/events",
|
||||
"audio/atrac/atractest",
|
||||
]
|
||||
|
||||
@ -138,12 +148,12 @@ tests_ignored = [
|
||||
def init():
|
||||
global PPSSPP_EXE
|
||||
if not os.path.exists("pspautotests"):
|
||||
print "Please run git submodule init; git submodule update;"
|
||||
print("Please run git submodule init; git submodule update;")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.exists(TEST_ROOT + "cpu/cpu_alu/cpu_alu.prx"):
|
||||
print "Please install the pspsdk and run make in common/ and in all the tests"
|
||||
print "(checked for existence of cpu/cpu_alu/cpu_alu.prx)"
|
||||
print("Please install the pspsdk and run make in common/ and in all the tests")
|
||||
print("(checked for existence of cpu/cpu_alu/cpu_alu.prx)")
|
||||
sys.exit(1)
|
||||
|
||||
for p in PPSSPP_EXECUTABLES:
|
||||
@ -152,28 +162,28 @@ def init():
|
||||
break
|
||||
|
||||
if not PPSSPP_EXE:
|
||||
print "PPSSPP executable missing, please build one."
|
||||
print("PPSSPP executable missing, please build one.")
|
||||
sys.exit(1)
|
||||
|
||||
def tcprint(arg):
|
||||
global teamcity_mode
|
||||
if teamcity_mode:
|
||||
print arg
|
||||
print(arg)
|
||||
|
||||
def run_tests(test_list, args):
|
||||
global PPSSPP_EXE, TIMEOUT
|
||||
tests_passed = []
|
||||
tests_failed = []
|
||||
|
||||
|
||||
for test in test_list:
|
||||
# Try prx first
|
||||
expected_filename = TEST_ROOT + test + ".expected"
|
||||
|
||||
elf_filename = TEST_ROOT + test + ".prx"
|
||||
print elf_filename
|
||||
print(elf_filename)
|
||||
|
||||
if not os.path.exists(elf_filename):
|
||||
print "WARNING: no prx, trying elf"
|
||||
print("WARNING: no prx, trying elf")
|
||||
elf_filename = TEST_ROOT + test + ".elf"
|
||||
|
||||
if not os.path.exists(elf_filename):
|
||||
@ -189,7 +199,7 @@ def run_tests(test_list, args):
|
||||
continue
|
||||
|
||||
expected_output = open(expected_filename).read().strip()
|
||||
|
||||
|
||||
tcprint("##teamcity[testStarted name='%s' captureStandardOutput='true']" % test)
|
||||
|
||||
cmdline = [PPSSPP_EXE, elf_filename]
|
||||
@ -199,16 +209,16 @@ def run_tests(test_list, args):
|
||||
c.run(TIMEOUT)
|
||||
|
||||
output = c.output.strip()
|
||||
|
||||
|
||||
if c.timeout:
|
||||
print output
|
||||
print "Test exceded limit of %d seconds." % TIMEOUT
|
||||
print(output)
|
||||
print("Test exceded limit of %d seconds." % TIMEOUT)
|
||||
tcprint("##teamcity[testFailed name='%s' message='Test timeout']" % test)
|
||||
tcprint("##teamcity[testFinished name='%s']" % test)
|
||||
continue
|
||||
|
||||
if output.startswith("TESTERROR"):
|
||||
print "Failed to run test " + elf_filename + "!"
|
||||
print("Failed to run test " + elf_filename + "!")
|
||||
tests_failed.append(test)
|
||||
tcprint("##teamcity[testFailed name='%s' message='Failed to run test']" % test)
|
||||
tcprint("##teamcity[testFinished name='%s']" % test)
|
||||
@ -217,48 +227,48 @@ def run_tests(test_list, args):
|
||||
different = False
|
||||
expected_lines = [x.strip() for x in expected_output.splitlines()]
|
||||
output_lines = [x.strip() for x in output.splitlines()]
|
||||
|
||||
|
||||
for i in range(0, min(len(output_lines), len(expected_lines))):
|
||||
if output_lines[i] != expected_lines[i]:
|
||||
print "E%i < %s" % (i + 1, expected_lines[i])
|
||||
print "O%i > %s" % (i + 1, output_lines[i])
|
||||
print("E%i < %s" % (i + 1, expected_lines[i]))
|
||||
print("O%i > %s" % (i + 1, output_lines[i]))
|
||||
different = True
|
||||
|
||||
if len(output_lines) != len(expected_lines):
|
||||
for i in range(len(output_lines), len(expected_lines)):
|
||||
print "E%i < %s" % (i + 1, expected_lines[i])
|
||||
print("E%i < %s" % (i + 1, expected_lines[i]))
|
||||
for i in range(len(expected_lines), len(output_lines)):
|
||||
print "O%i > %s" % (i + 1, output_lines[i])
|
||||
print "*** Different number of lines!"
|
||||
print("O%i > %s" % (i + 1, output_lines[i]))
|
||||
print("*** Different number of lines!")
|
||||
different = True
|
||||
|
||||
if not different:
|
||||
if '-v' in args:
|
||||
print "++++++++++++++ The Equal Output +++++++++++++"
|
||||
print "\n".join(output_lines)
|
||||
print "+++++++++++++++++++++++++++++++++++++++++++++"
|
||||
print " " + test + " - passed!"
|
||||
print("++++++++++++++ The Equal Output +++++++++++++")
|
||||
print("\n".join(output_lines))
|
||||
print("+++++++++++++++++++++++++++++++++++++++++++++")
|
||||
print(" " + test + " - passed!")
|
||||
tests_passed.append(test)
|
||||
tcprint("##teamcity[testFinished name='%s']" % test)
|
||||
else:
|
||||
if '-v' in args:
|
||||
print "============== output from failed " + test + " :"
|
||||
print output
|
||||
print "============== expected output:"
|
||||
print expected_output
|
||||
print "==============================="
|
||||
print("============== output from failed " + test + " :")
|
||||
print(output)
|
||||
print("============== expected output:")
|
||||
print(expected_output)
|
||||
print("===============================")
|
||||
tests_failed.append(test)
|
||||
tcprint("##teamcity[testFailed name='%s' message='Output different from expected file']" % test)
|
||||
tcprint("##teamcity[testFinished name='%s']" % test)
|
||||
|
||||
print "%i tests passed, %i tests failed." % (
|
||||
len(tests_passed), len(tests_failed))
|
||||
print("%i tests passed, %i tests failed." % (
|
||||
len(tests_passed), len(tests_failed)))
|
||||
|
||||
if len(tests_failed):
|
||||
print "Failed tests:"
|
||||
print("Failed tests:")
|
||||
for t in tests_failed:
|
||||
print " " + t
|
||||
print "Ran " + PPSSPP_EXE
|
||||
print(" " + t)
|
||||
print("Ran " + PPSSPP_EXE)
|
||||
|
||||
|
||||
def main():
|
||||
|
Loading…
Reference in New Issue
Block a user