Merge branch 'master' into armjit

Conflicts:
	CMakeLists.txt
	Core/MIPS/ARM/RegCache.cpp
This commit is contained in:
Henrik Rydgard 2013-01-07 10:25:02 +01:00
commit 7cb95bbc89
285 changed files with 25710 additions and 7913 deletions

33
.gitignore vendored
View File

@ -1,37 +1,46 @@
# For MSVC
*.lastcodeanalysissucceeded
*.pdb
*.ilk
*.obj
*.pch
Logs
*.log
*.dll
*.rar
*.exe
*.ini
*.map
*.lib
*.user
*.sdf
*.ncb
Debug
DebugFast
Release
*.opensdf
*.suo
*.aps
*.exp
Debug
DebugFast
Release
Windows/x64
Windows/ipch
# For ppsspp.ini, etc.
*.ini
Logs
Memstick
bin
gen
libs
obj
*.exp
build*/
.pspsh.hist
GameLogNotes.txt
Windows/x64
Windows/ipch
Memstick
android/ui_atlas.zim
__testoutput.txt
__testerror.txt
__testfinish.txt
GameLogNotes.txt
android/ui_atlas.zim
ppge_atlas.zim.png
local.properties
build*/

View File

@ -18,7 +18,7 @@ if(BLACKBERRY)
set(ARM ON)
endif()
if (ARM)
if(ARM)
set(USING_GLES2 ON)
else() # Assume x86
set(X86 ON)
@ -37,6 +37,7 @@ option(ANDROID "Set to ON if targeting an Android device" ${ANDROID})
option(BLACKBERRY "Set to ON if targeting a Blackberry device" ${BLACKBERRY})
option(IOS "Set to ON if targeting an iOS device" ${IOS})
option(USING_GLES2 "Set to ON if target device uses OpenGL ES 2.0" ${USING_GLES2})
option(USING_QT_UI "Set to ON if you wish to use the Qt frontend wrapper" ${USING_QT_UI})
option(HEADLESS "Set to OFF to not generate the PPSSPPHeadless target" ${HEADLESS})
option(DEBUG "Set to ON to enable full debug logging" ${DEBUG})
@ -98,7 +99,7 @@ endif()
if(ARM)
add_definitions(-DARM)
endif()
if (USING_GLES2)
if(USING_GLES2)
add_definitions(-DUSING_GLES2)
endif()
@ -108,6 +109,7 @@ endif()
if(NOT MSVC)
# Disable some warnings
add_definitions(-O2)
add_definitions(-Wno-multichar)
add_definitions(-fno-strict-aliasing)
if(NOT APPLE)
@ -192,8 +194,6 @@ endif()
add_library(Common STATIC
${CommonExtra}
Common/Action.cpp
Common/Action.h
Common/ColorUtil.cpp
Common/ColorUtil.h
Common/ConsoleListener.cpp
@ -272,6 +272,17 @@ add_library(zlib STATIC
)
include_directories(ext/zlib)
add_library(snappy STATIC
ext/snappy/snappy-c.cpp
ext/snappy/snappy-internal.h
ext/snappy/snappy-sinksource.h
ext/snappy/snappy-stubs-internal.h
ext/snappy/snappy-stubs-public.h
ext/snappy/snappy.cpp
ext/snappy/snappy.h
)
include_directories(ext/snappy)
add_library(etcpack STATIC
native/ext/etcpack/etcdec.cpp
native/ext/etcpack/etcdec.h
@ -386,12 +397,20 @@ if(ANDROID)
native/android/native-audio-so.h)
target_link_libraries(native_audio OpenSLES)
# No target
elseif(USING_QT_UI)
# Currently unused
find_package(Qt4 COMPONENTS QtMultimedia QtOpenGL QtGui QtCore)
include(${QT_USE_FILE})
qt4_wrap_cpp(nativeQtHeader native/base/QtMain.h)
set(nativeExtra ${nativeExtra} native/base/QtMain.cpp ${nativeQtHeader})
set(nativeExtraLibs ${nativeExtraLibs} ${QT_LIBRARIES})
set(TargetBin PPSSPPQt)
elseif(BLACKBERRY)
set(nativeExtra ${nativeExtra} native/base/BlackberryMain.cpp)
set(nativeExtraLibs ${nativeExtraLibs} asound bps screen socket EGL)
set(TargetBin PPSSPPBlackberry)
elseif(SDL_FOUND)
# Require SDL
# Require SDL
include_directories(${SDL_INCLUDE_DIR})
set(nativeExtra ${nativeExtra} native/base/PCMain.cpp)
set(nativeExtraLibs ${nativeExtraLibs} ${SDL_LIBRARY})
@ -526,6 +545,8 @@ add_library(native STATIC
native/util/random/perlin.cpp
native/util/random/perlin.h
native/util/random/rng.h
native/util/text/utf8.h
native/util/text/utf8.cpp
native/ext/rapidxml/rapidxml.hpp
native/ext/rapidxml/rapidxml_iterators.hpp
native/ext/rapidxml/rapidxml_print.hpp
@ -534,7 +555,7 @@ include_directories(native)
# rapidxml is headers only so we can't make a lib specific for it
include_directories(native/ext/rapidxml)
target_link_libraries(native ${LIBZIP} etcpack sha1 stb_image stb_vorbis #vjson
zlib ${GLEW_LIBRARIES})
zlib snappy ${GLEW_LIBRARIES})
if(ANDROID)
target_link_libraries(native log)
endif()
@ -606,6 +627,18 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/Debugger/DebugInterface.h
Core/Debugger/SymbolMap.cpp
Core/Debugger/SymbolMap.h
Core/Dialog/PSPDialog.cpp
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
Core/Dialog/PSPSaveDialog.h
Core/Dialog/SavedataParam.cpp
Core/Dialog/SavedataParam.h
Core/ELF/ElfReader.cpp
Core/ELF/ElfReader.h
Core/ELF/ElfTypes.h
@ -697,16 +730,20 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/HLE/sceSas.h
Core/HLE/sceSsl.cpp
Core/HLE/sceSsl.h
Core/HLE/scesupPreAcc.cpp
Core/HLE/scesupPreAcc.h
Core/HLE/sceUmd.cpp
Core/HLE/sceUmd.h
Core/HLE/sceUsb.cpp
Core/HLE/sceUsb.h
Core/HLE/sceUtility.cpp
Core/HLE/sceUtility.h
Core/HLE/sceVaudio.cpp
Core/HLE/sceVaudio.h
Core/HW/MediaEngine.cpp
Core/HW/MediaEngine.h
Core/HW/MemoryStick.cpp
Core/HW/MemoryStick.h
Core/HW/SasAudio.cpp
Core/HW/SasAudio.h
Core/Host.cpp
Core/Host.h
Core/Loaders.cpp
@ -740,6 +777,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/PSPLoaders.h
Core/PSPMixer.cpp
Core/PSPMixer.h
Core/SaveState.cpp
Core/SaveState.h
Core/System.cpp
Core/System.h
Core/Util/BlockAllocator.cpp
@ -762,6 +801,8 @@ add_library(GPU OBJECT
GPU/GLES/FragmentShaderGenerator.h
GPU/GLES/Framebuffer.cpp
GPU/GLES/Framebuffer.h
GPU/GLES/IndexGenerator.cpp
GPU/GLES/IndexGenerator.h
GPU/GLES/ShaderManager.cpp
GPU/GLES/ShaderManager.h
GPU/GLES/StateMapping.cpp
@ -775,12 +816,14 @@ add_library(GPU OBJECT
GPU/GLES/VertexShaderGenerator.cpp
GPU/GLES/VertexShaderGenerator.h
GPU/GPUInterface.h
GPU/GeDisasm.cpp
GPU/GeDisasm.h
GPU/GPUCommon.cpp
GPU/GPUCommon.h
GPU/GPUState.cpp
GPU/GPUState.h
GPU/Math3D.cpp
GPU/Math3D.h
# GPU/Null/NullDisplayListInterpreter.cpp
# GPU/Null/NullDisplayListInterpreter.h
GPU/Null/NullGpu.cpp
GPU/Null/NullGpu.h
GPU/ge_constants.h)
@ -855,7 +898,7 @@ if(WIN32)
endif()
if(HEADLESS)
add_executable(PPSSPPHeadless headless/Headless.cpp)
add_executable(PPSSPPHeadless headless/Headless.cpp headless/StubHost.h)
target_link_libraries(PPSSPPHeadless ${CoreLibName}
${COCOA_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
setup_target_project(PPSSPPHeadless headless)
@ -867,11 +910,11 @@ set(NativeAppSource
android/jni/MenuScreens.cpp
android/jni/GamepadEmu.cpp
android/jni/UIShader.cpp
android/jni/ui_atlas.cpp
android/jni/ArmEmitterTest.cpp)
set(AndroidAssets
android/jni/ArmEmitterTest.cpp
android/jni/ui_atlas.cpp)
set(NativeAssets
android/assets/ui_atlas.zim
android/assets/ppge_atlas.zim)
assets/ppge_atlas.zim)
set(LinkCommon ${CoreLibName} ${CMAKE_THREAD_LIBS_INIT} ${nativeExtraLibs})
if (TargetBin)
@ -879,6 +922,6 @@ if (TargetBin)
target_link_libraries(${TargetBin} ${LinkCommon})
endif()
file(INSTALL ${AndroidAssets} DESTINATION assets)
file(INSTALL ${NativeAssets} DESTINATION assets)
#include(CPack)

View File

@ -1 +0,0 @@
#include "Action.h"

View File

@ -1,20 +0,0 @@
#pragma once
#include <vector>
// Sometimes you want to set something to happen later, without that later place really needing
// to know about all the things that might happen. That's when you use an Action, and add it
// to the appropriate ActionSet.
//
// Unlike CoreTiming events, these are not timed in any way and do not care about the time.
// This code also doesn't depend on anything in PPSSPP so it can live in Common.
// Pretty much a Runnable. Similar to Action from JPCSP.
class Action
{
public:
virtual ~Action() {}
virtual void run() = 0;
};

View File

@ -22,6 +22,9 @@
#include "Common.h"
#include "MemoryUtil.h"
#ifdef __SYMBIAN32__
#include <signal.h>
#endif
namespace ArmGen
{

View File

@ -1,5 +1,4 @@
set(SRCS
Action.cpp
ColorUtil.cpp
ConsoleListener.cpp
ExtendedTrace.cpp

View File

@ -31,9 +31,12 @@
#include <vector>
#include <deque>
#include <string>
#include <list>
#include <set>
#include "Common.h"
#include "FileUtil.h"
#include "../ext/snappy/snappy-c.h"
template <class T>
struct LinkedListItem : public T
@ -112,14 +115,59 @@ public:
}
}
template<class K, class T>
void Do(std::multimap<K, T> &x)
{
unsigned int number = (unsigned int)x.size();
Do(number);
switch (mode) {
case MODE_READ:
{
x.clear();
while (number > 0)
{
K first;
Do(first);
T second;
Do(second);
x.insert(std::make_pair(first, second));
--number;
}
}
break;
case MODE_WRITE:
case MODE_MEASURE:
case MODE_VERIFY:
{
typename std::multimap<K, T>::iterator itr = x.begin();
while (number > 0)
{
Do(itr->first);
Do(itr->second);
--number;
++itr;
}
}
break;
}
}
// Store vectors.
template<class T>
void Do(std::vector<T> &x)
{
T dv;
Do(x, dv);
}
template<class T>
void Do(std::vector<T> &x, T &default_val)
{
u32 vec_size = (u32)x.size();
Do(vec_size);
x.resize(vec_size);
DoArray(&x[0], vec_size);
x.resize(vec_size, default_val);
if (vec_size > 0)
DoArray(&x[0], vec_size);
}
// Store deques.
@ -133,7 +181,62 @@ public:
for(i = 0; i < deq_size; i++)
DoVoid(&x[i],sizeof(T));
}
// Store STL lists.
template<class T>
void Do(std::list<T> &x)
{
T dv;
Do(x, dv);
}
template<class T>
void Do(std::list<T> &x, T &default_val)
{
u32 list_size = (u32)x.size();
Do(list_size);
x.resize(list_size, default_val);
typename std::list<T>::iterator itr, end;
for (itr = x.begin(), end = x.end(); itr != end; ++itr)
Do(*itr);
}
// Store STL sets.
template <class T>
void Do(std::set<T> &x)
{
unsigned int number = (unsigned int)x.size();
Do(number);
switch (mode)
{
case MODE_READ:
{
x.clear();
while (number-- > 0)
{
T it;
Do(it);
x.insert(it);
}
}
break;
case MODE_WRITE:
case MODE_MEASURE:
case MODE_VERIFY:
{
typename std::set<T>::iterator itr = x.begin();
while (number-- > 0)
Do(*itr++);
}
break;
default:
ERROR_LOG(COMMON, "Savestate error: invalid mode %d.", mode);
}
}
// Store strings.
void Do(std::string &x)
{
@ -317,9 +420,22 @@ public:
}
u8 *ptr = buffer;
u8 *buf = buffer;
if (header.Compress) {
u8 *uncomp_buffer = new u8[header.UncompressedSize];
size_t uncomp_size = header.UncompressedSize;
snappy_uncompress((const char *)buffer, sz, (char *)uncomp_buffer, &uncomp_size);
if (uncomp_size != header.UncompressedSize) {
ERROR_LOG(COMMON,"Size mismatch: file: %i calc: %i", (int)header.UncompressedSize, (int)uncomp_size);
}
ptr = uncomp_buffer;
buf = uncomp_buffer;
delete [] buffer;
}
PointerWrap p(&ptr, PointerWrap::MODE_READ);
_class.DoState(p);
delete[] buffer;
delete[] buf;
INFO_LOG(COMMON, "ChunkReader: Done loading %s" , _rFilename.c_str());
return true;
@ -337,33 +453,57 @@ public:
return false;
}
bool compress = true;
// Get data
u8 *ptr = 0;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
_class.DoState(p);
size_t const sz = (size_t)ptr;
std::vector<u8> buffer(sz);
u8 * buffer = new u8[sz];
ptr = &buffer[0];
p.SetMode(PointerWrap::MODE_WRITE);
_class.DoState(p);
// Create header
SChunkHeader header;
header.Compress = 0;
header.Compress = compress ? 1 : 0;
header.Revision = _Revision;
header.ExpectedSize = (int)sz;
header.UncompressedSize = (int)sz;
// Write to file
if (!pFile.WriteArray(&header, 1))
{
ERROR_LOG(COMMON,"ChunkReader: Failed writing header");
return false;
}
if (!pFile.WriteBytes(&buffer[0], sz))
{
ERROR_LOG(COMMON,"ChunkReader: Failed writing data");
return false;
if (compress) {
size_t comp_len = snappy_max_compressed_length(sz);
u8 *compressed_buffer = new u8[comp_len];
snappy_compress((const char *)buffer, sz, (char *)compressed_buffer, &comp_len);
delete [] buffer;
header.ExpectedSize = comp_len;
if (!pFile.WriteArray(&header, 1))
{
ERROR_LOG(COMMON,"ChunkReader: Failed writing header");
return false;
}
if (!pFile.WriteBytes(&compressed_buffer[0], comp_len)) {
ERROR_LOG(COMMON,"ChunkReader: Failed writing compressed data");
return false;
} else {
INFO_LOG(COMMON, "Savestate: Compressed %i bytes into %i", (int)sz, (int)comp_len);
}
delete [] compressed_buffer;
} else {
if (!pFile.WriteArray(&header, 1))
{
ERROR_LOG(COMMON,"ChunkReader: Failed writing header");
return false;
}
if (!pFile.WriteBytes(&buffer[0], sz))
{
ERROR_LOG(COMMON,"ChunkReader: Failed writing data");
return false;
}
delete [] buffer;
}
INFO_LOG(COMMON,"ChunkReader: Done writing %s",
@ -371,12 +511,37 @@ public:
return true;
}
template <class T>
static bool Verify(T& _class)
{
u8 *ptr = 0;
// Step 1: Measure the space required.
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
_class.DoState(p);
size_t const sz = (size_t)ptr;
std::vector<u8> buffer(sz);
// Step 2: Dump the state.
ptr = &buffer[0];
p.SetMode(PointerWrap::MODE_WRITE);
_class.DoState(p);
// Step 3: Verify the state.
ptr = &buffer[0];
p.SetMode(PointerWrap::MODE_VERIFY);
_class.DoState(p);
return true;
}
private:
struct SChunkHeader
{
int Revision;
int Compress;
int ExpectedSize;
int UncompressedSize;
};
};

View File

@ -160,6 +160,17 @@ private:
# define _M_SSE 0x402
#endif
#ifdef _MSC_VER
inline unsigned int bswap32(unsigned int x) { return _byteswap_ulong(x); }
inline unsigned int bswap16(unsigned int x) { return _byteswap_ushort(x); }
#else
// TODO: speedup
inline unsigned int bswap32(unsigned int x) { return (x >> 24) | ((x & 0xFF0000) >> 8) | ((x & 0xFF00) << 8) | (x << 24);}
inline unsigned short bswap16(unsigned short x) { return (x << 8) | (x >> 8); }
#endif
// Host communication.
enum HOST_COMM
{

View File

@ -140,7 +140,6 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ABI.h" />
<ClInclude Include="Action.h" />
<ClInclude Include="ArmABI.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@ -196,7 +195,6 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="ABI.cpp" />
<ClCompile Include="Action.cpp" />
<ClCompile Include="ArmABI.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>

View File

@ -43,7 +43,6 @@
<ClInclude Include="x64Emitter.h" />
<ClInclude Include="ArmEmitter.h" />
<ClInclude Include="ArmABI.h" />
<ClInclude Include="Action.h" />
<ClInclude Include="FixedSizeUnorderedSet.h" />
</ItemGroup>
<ItemGroup>
@ -72,7 +71,6 @@
<ClCompile Include="x64Emitter.cpp" />
<ClCompile Include="ArmEmitter.cpp" />
<ClCompile Include="ArmABI.cpp" />
<ClCompile Include="Action.cpp" />
<ClCompile Include="ThunkArm.cpp" />
<ClCompile Include="ArmCPUDetect.cpp" />
</ItemGroup>

View File

@ -23,7 +23,11 @@
// Directory seperators, do we need this?
#define DIR_SEP "/"
#define DIR_SEP_CHR '/'
#ifdef _WIN32
#define DIR_SEP_CHRS "/\\"
#else
#define DIR_SEP_CHRS "/"
#endif
// The user data dir
#define ROOT_DIR "."
@ -96,7 +100,7 @@
#define LOGGER_CONFIG "Logger.ini"
// Files in the directory returned by GetUserPath(D_LOGS_IDX)
#define MAIN_LOG "dolphin.log"
#define MAIN_LOG "ppsspp.log"
// Sys files
#define TOTALDB "totaldb.dsy"

View File

@ -318,9 +318,7 @@ void ConsoleListener::Log(LogTypes::LOG_LEVELS Level, const char *Text)
Text += 10;
}
SetConsoleTextAttribute(hConsole, Color);
size_t len = strlen(Text);
if (Text[len-1] == '\n' && Text[len-1] == '\r')
len--;
size_t len = strlen(Text);
WriteConsole(hConsole, Text, (DWORD)len, &cCharsWritten, NULL);
#else
char ColorAttr[16] = "";

View File

@ -102,7 +102,7 @@ void CFileSearch::FindFiles(const std::string& _searchString, const std::string&
((s.size() > ext.size()) && (!strcasecmp(s.substr(s.size() - ext.size()).c_str(), ext.c_str())) ))
{
std::string full_name;
if (_strPath.c_str()[_strPath.size()-1] == DIR_SEP_CHR)
if (strchr(DIR_SEP_CHRS, _strPath.c_str()[_strPath.size()-1]))
full_name = _strPath + s;
else
full_name = _strPath + DIR_SEP + s;

View File

@ -53,6 +53,33 @@
#define fstat64 fstat
#endif
// Hack
#if defined(__SYMBIAN32__)
static inline int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) {
struct dirent *readdir_entry;
readdir_entry = readdir(dirp);
if (readdir_entry == NULL) {
*result = NULL;
return errno;
}
*entry = *readdir_entry;
*result = entry;
return 0;
}
#endif
// No thread safe
#ifdef _WIN32
inline struct tm* localtime_r (const time_t *clock, struct tm *result) {
if (!clock || !result) return NULL;
memcpy(result,localtime(clock),sizeof(*result));
return result;
}
#endif
// This namespace has various generic functions related to files and paths.
// The code still needs a ton of cleanup.
// REMEMBER: strdup considered harmful!
@ -66,7 +93,7 @@ static void StripTailDirSlashes(std::string &fname)
if (fname.length() > 1)
{
size_t i = fname.length() - 1;
while (fname[i] == DIR_SEP_CHR)
while (strchr(DIR_SEP_CHRS, fname[i]))
fname[i--] = '\0';
}
return;
@ -188,17 +215,26 @@ bool CreateFullPath(const std::string &fullPath)
}
size_t position = 0;
#ifdef _WIN32
// Skip the drive letter, no need to create C:\.
position = 3;
#endif
while (true)
{
// Find next sub path
position = fullPath.find(DIR_SEP_CHR, position);
position = fullPath.find_first_of(DIR_SEP_CHRS, position);
// we're done, yay!
if (position == fullPath.npos)
{
if (!File::Exists(fullPath))
File::CreateDir(fullPath);
return true;
}
std::string subPath = fullPath.substr(0, position);
if (!File::IsDirectory(subPath))
if (!File::Exists(subPath))
File::CreateDir(subPath);
// A safety check
@ -299,6 +335,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
ERROR_LOG(COMMON,
"Copy: failed reading from source, %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
fclose(input);
fclose(output);
return false;
}
}
@ -310,6 +348,8 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
ERROR_LOG(COMMON,
"Copy: failed writing to output, %s --> %s: %s",
srcFilename.c_str(), destFilename.c_str(), GetLastErrorMsg());
fclose(input);
fclose(output);
return false;
}
}
@ -320,6 +360,34 @@ bool Copy(const std::string &srcFilename, const std::string &destFilename)
#endif
}
tm GetModifTime(const std::string &filename)
{
tm return_time = {0};
if (!Exists(filename))
{
WARN_LOG(COMMON, "GetCreateTime: failed %s: No such file", filename.c_str());
return return_time;
}
if (IsDirectory(filename))
{
WARN_LOG(COMMON, "GetCreateTime: failed %s: is a directory", filename.c_str());
return return_time;
}
struct stat64 buf;
if (stat64(filename.c_str(), &buf) == 0)
{
DEBUG_LOG(COMMON, "GetCreateTime: %s: %lld",
filename.c_str(), (long long)buf.st_mtime);
localtime_r((time_t*)&buf.st_mtime,&return_time);
return return_time;
}
ERROR_LOG(COMMON, "GetCreateTime: Stat failed %s: %s",
filename.c_str(), GetLastErrorMsg());
return return_time;
}
// Returns the size of filename (64bit)
u64 GetSize(const std::string &filename)
{
@ -504,7 +572,7 @@ bool DeleteDirRecursively(const std::string &directory)
(virtualName[2] == '\0')))
continue;
std::string newPath = directory + DIR_SEP_CHR + virtualName;
std::string newPath = directory + DIR_SEP + virtualName;
if (IsDirectory(newPath))
{
if (!DeleteDirRecursively(newPath))
@ -649,6 +717,8 @@ std::string &GetUserPath(const unsigned int DirIDX, const std::string &newPath)
#ifdef _WIN32
// TODO: use GetExeDirectory() here instead of ROOT_DIR so that if the cwd is changed we still have the correct paths?
paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
#elif defined(__SYMBIAN32__)
paths[D_USER_IDX] = "E:" DIR_SEP "PPSSPP" DIR_SEP;
#else
if (File::Exists(ROOT_DIR DIR_SEP USERDATA_DIR))
paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;

View File

@ -56,6 +56,9 @@ bool Exists(const std::string &filename);
// Returns true if filename is a directory
bool IsDirectory(const std::string &filename);
// Returns struct with modification date of file
tm GetModifTime(const std::string &filename);
// Returns the size of filename (64bit)
u64 GetSize(const std::string &filename);

View File

@ -87,13 +87,32 @@ public:
return count_;
}
size_t capacity() const {
return N;
}
int room() const {
return N - count_;
}
bool empty() {
return count_;
}
bool empty() {
return count_ == 0;
}
void DoState(PointerWrap &p) {
int size = N;
p.Do(size);
if (size != N)
{
ERROR_LOG(HLE, "Savestate failure: Incompatible queue size.");
return;
}
p.DoArray<T>(storage_, N);
p.Do(head_);
p.Do(tail_);
p.Do(count_);
p.DoMarker("FixedSizeQueue");
}
private:
T *storage_;

View File

@ -23,6 +23,10 @@
#define INFO_LEVEL 4 // General information.
#define DEBUG_LEVEL 5 // Detailed debugging - might make things slow.
#ifndef _WIN32
#include <signal.h>
#endif
namespace LogTypes
{
@ -41,11 +45,13 @@ enum LOG_TYPE {
INTC,
MEMMAP,
SOUND,
SAS,
HLE,
TIMER,
VIDEO,
DYNA_REC,
NETPLAY,
ME,
NUMBER_OF_LOGS, // Must be last
JIT = DYNA_REC,

View File

@ -22,6 +22,9 @@
#include "Timer.h"
#include "Thread.h"
#include "FileUtil.h"
#ifdef __SYMBIAN32__
#include <e32debug.h>
#endif
void GenericLog(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type,
const char *file, int line, const char* fmt, ...)
@ -53,17 +56,21 @@ LogManager::LogManager()
m_Log[LogTypes::INTC] = new LogContainer("INTC", "Interrupts");
m_Log[LogTypes::MEMMAP] = new LogContainer("MM", "Memory Map");
m_Log[LogTypes::SOUND] = new LogContainer("SND", "Sound");
m_Log[LogTypes::SAS] = new LogContainer("SAS", "Sound Mixer (Sas)");
m_Log[LogTypes::HLE] = new LogContainer("HLE", "HLE");
m_Log[LogTypes::TIMER] = new LogContainer("TMR", "Timer");
m_Log[LogTypes::VIDEO] = new LogContainer("VID", "Video");
m_Log[LogTypes::DYNA_REC] = new LogContainer("Jit", "JIT compiler");
m_Log[LogTypes::NETPLAY] = new LogContainer("NET", "Net play");
m_Log[LogTypes::ME] = new LogContainer("ME", "Media Engine");
// Remove file logging on small devices
#if !defined(ANDROID) && !defined(IOS) && !defined(BLACKBERRY)
m_fileLog = new FileLogListener(File::GetUserPath(F_MAINLOG_IDX).c_str());
m_consoleLog = new ConsoleListener();
m_debuggerLog = new DebuggerLogListener();
#else
m_fileLog = NULL;
#endif
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
@ -84,8 +91,9 @@ LogManager::~LogManager()
{
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
{
if (m_fileLog != NULL)
m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog);
#if !defined(ANDROID) && !defined(IOS) && !defined(BLACKBERRY)
m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog);
m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_consoleLog);
m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_debuggerLog);
#endif
@ -93,12 +101,30 @@ LogManager::~LogManager()
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
delete m_Log[i];
if (m_fileLog != NULL)
delete m_fileLog;
#if !defined(ANDROID) && !defined(IOS) && !defined(BLACKBERRY)
delete m_fileLog;
delete m_consoleLog;
#endif
}
void LogManager::ChangeFileLog(const char *filename)
{
if (m_fileLog != NULL)
{
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
m_logManager->RemoveListener((LogTypes::LOG_TYPE)i, m_fileLog);
delete m_fileLog;
}
if (filename != NULL)
{
m_fileLog = new FileLogListener(filename);
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; ++i)
m_Log[i]->AddListener(m_fileLog);
}
}
void LogManager::SaveConfig(IniFile::Section *section)
{
for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++)
@ -134,7 +160,7 @@ void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const
static const char level_to_char[7] = "-NEWID";
char formattedTime[13];
Common::Timer::GetTimeFormatted(formattedTime);
sprintf(msg, "%s %s:%u %c[%s]: %s\n",
sprintf(msg, "%s %s:%d %c[%s]: %s\n",
formattedTime,
file, line, level_to_char[(int)level],
log->GetShortName(), temp);
@ -197,7 +223,11 @@ void FileLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)
return;
std::lock_guard<std::mutex> lk(m_log_lock);
#ifdef __SYMBIAN32__
RDebug::Printf("%s",msg);
#else
m_logfile << msg << std::flush;
#endif
}
void DebuggerLogListener::Log(LogTypes::LOG_LEVELS, const char *msg)

View File

@ -179,6 +179,8 @@ public:
static void Init();
static void Shutdown();
void ChangeFileLog(const char *filename);
void SaveConfig(IniFile::Section *section);
void LoadConfig(IniFile::Section *section);
};

View File

@ -22,10 +22,10 @@
#include <numeric>
namespace {
#ifdef USE_SSE
static u32 saved_sse_state = _mm_getcsr();
static const u32 default_sse_state = _mm_getcsr();
#endif
}
namespace MathUtil
@ -116,19 +116,25 @@ namespace MathUtil
void LoadDefaultSSEState()
{
#ifdef USE_SSE
_mm_setcsr(default_sse_state);
#endif
}
void LoadSSEState()
{
#ifdef USE_SSE
_mm_setcsr(saved_sse_state);
#endif
}
void SaveSSEState()
{
#ifdef USE_SSE
saved_sse_state = _mm_getcsr();
#endif
}
inline void MatrixMul(int n, const float *a, const float *b, float *result)
@ -247,3 +253,28 @@ void Matrix44::Multiply(const Matrix44 &a, const Matrix44 &b, Matrix44 &result)
MatrixMul(4, a.data, b.data, result.data);
}
int Pow2roundup(int x)
{
if (x < 0)
return 0;
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x+1;
}
int GetPow2(int x)
{
int ret = 0;
int val = 1;
while(x > val)
{
ret++;
val *= 2;
}
return ret;
}

View File

@ -20,7 +20,14 @@
#include "Common.h"
#ifndef ARM
#define USE_SSE
#endif
#ifdef USE_SSE
#include <xmmintrin.h>
#endif
#include <vector>
namespace MathUtil
@ -146,6 +153,8 @@ struct Rectangle
inline float pow2f(float x) {return x * x;}
inline double pow2(double x) {return x * x;}
int Pow2roundup(int x);
int GetPow2(int x);
/*
@ -163,7 +172,6 @@ float MathFloatVectorSum(const std::vector<float>&);
#define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1))
#define ROUND_DOWN(x, a) ((x) & ~((a) - 1))
// Tiny matrix/vector library.
// Used for things like Free-Look in the gfx backend.

View File

@ -33,6 +33,11 @@
#endif
#endif
#if defined(__SYMBIAN32__)
// Also Xbox 360
#define UNUSABLE_MMAP 1
#endif
#ifdef ANDROID
@ -108,12 +113,12 @@ SYSTEM_INFO sysInfo;
// Windows mappings need to be on 64K boundaries, due to Alpha legacy.
#ifdef _WIN32
size_t roundup(size_t x) {
int gran = sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x10000;
return (x + gran - 1) & ~(gran - 1);
int gran = sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x10000;
return (x + gran - 1) & ~(gran - 1);
}
#else
size_t roundup(size_t x) {
return x;
return x;
}
#endif
@ -122,8 +127,8 @@ void MemArena::GrabLowMemSpace(size_t size)
{
#ifdef _WIN32
hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL);
GetSystemInfo(&sysInfo);
#elif ANDROID
GetSystemInfo(&sysInfo);
#elif defined(ANDROID)
// Use ashmem so we don't have to allocate a file on disk!
fd = ashmem_create_region("PPSSPP_RAM", size);
// Note that it appears that ashmem is pinned by default, so no need to pin.
@ -132,6 +137,8 @@ void MemArena::GrabLowMemSpace(size_t size)
ERROR_LOG(MEMMAP, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno));
return;
}
#elif defined(UNUSABLE_MMAP)
// Do nothing as we are using malloc()
#else
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
fd = open(ram_temp_file.c_str(), O_RDWR | O_CREAT, mode);
@ -156,6 +163,8 @@ void MemArena::ReleaseSpace()
#ifdef _WIN32
CloseHandle(hMemoryMapping);
hMemoryMapping = 0;
#elif defined(UNUSABLE_MMAP)
// Do nothing as we are using malloc()
#else
close(fd);
#endif
@ -165,30 +174,32 @@ void MemArena::ReleaseSpace()
void *MemArena::CreateView(s64 offset, size_t size, void *base)
{
#ifdef _WIN32
size = roundup(size);
void *ptr = MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base);
if (!ptr) {
//ERROR_LOG(MEMMAP, "Failed to map memory: %08x %08x %08x : %s", (u32)offset, (u32)size, (u32)base, GetLastErrorMsg());
} else {
//ERROR_LOG(MEMMAP, "Mapped memory: %08x %08x %08x : %s", (u32)offset, (u32)size, (u32)base, GetLastErrorMsg());
}
return ptr;
size = roundup(size);
void *ptr = MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base);
if (!ptr) {
//ERROR_LOG(MEMMAP, "Failed to map memory: %08x %08x %08x : %s", (u32)offset, (u32)size, (u32)base, GetLastErrorMsg());
} else {
//ERROR_LOG(MEMMAP, "Mapped memory: %08x %08x %08x : %s", (u32)offset, (u32)size, (u32)base, GetLastErrorMsg());
}
return ptr;
#elif defined(UNUSABLE_MMAP)
void *retval = malloc(size);
if (!retval)
{
NOTICE_LOG(MEMMAP, "malloc failed: %s", strerror(errno));
return 0;
}
return retval;
#else
void *retval = mmap(
base, size,
PROT_READ | PROT_WRITE,
MAP_SHARED | ((base == 0) ? 0 : MAP_FIXED),
fd, offset);
void *retval = mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED |
((base == 0) ? 0 : MAP_FIXED), fd, offset);
if (retval == MAP_FAILED)
{
NOTICE_LOG(MEMMAP, "mmap on %s (fd: %d) failed", ram_temp_file.c_str(), (int)fd);
return 0;
}
else
{
return retval;
}
return retval;
#endif
}
@ -197,6 +208,8 @@ void MemArena::ReleaseView(void* view, size_t size)
{
#ifdef _WIN32
UnmapViewOfFile(view);
#elif defined(UNUSABLE_MMAP)
free(view);
#else
munmap(view, size);
#endif
@ -221,11 +234,14 @@ u8* MemArena::Find4GBBase()
// 32 bit
#ifdef _WIN32
// The highest thing in any 1GB section of memory space is the locked cache. We only need to fit it.
u8* base = (u8*)VirtualAlloc(0, 0x31000000, MEM_RESERVE, PAGE_READWRITE);
u8* base = (u8*)VirtualAlloc(0, 0x10000000, MEM_RESERVE, PAGE_READWRITE);
if (base) {
VirtualFree(base, 0, MEM_RELEASE);
}
return base;
#elif defined(UNUSABLE_MMAP)
// We are unable to use relative addresses due to lack of mmap()
return NULL;
#else
void* base = mmap(0, 0x5000000, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_SHARED, -1, 0);
@ -267,7 +283,7 @@ static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32
int i;
for (i = 0; i < num_views; i++)
{
const MemoryView &view = views[i];
const MemoryView &view = views[i];
SKIP(flags, view.flags);
if (view.flags & MV_MIRROR_PREVIOUS) {
position = last_position;
@ -391,7 +407,7 @@ void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemAr
if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low))
arena->ReleaseView(*views[i].out_ptr, views[i].size);
*views[i].out_ptr = NULL;
if (views[i].out_ptr_low)
if (views[i].out_ptr_low)
*views[i].out_ptr_low = NULL;
}
}

View File

@ -53,6 +53,10 @@ void* AllocateExecutableMemory(size_t size, bool low)
{
#if defined(_WIN32)
void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#elif defined(__SYMBIAN32__)
// On Symbian, we will need to create an RChunk and allocate with ->CreateLocalCode(size, size);
static char *map_hint = 0;
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, -1, 0);
#else
static char *map_hint = 0;
#if defined(__x86_64__) && !defined(MAP_32BIT)
@ -111,7 +115,11 @@ void* AllocateMemoryPages(size_t size)
#ifdef _WIN32
void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
#else
void* ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
void* ptr = mmap(0, size, PROT_READ | PROT_WRITE,
#ifndef __SYMBIAN32__
MAP_ANON |
#endif
MAP_PRIVATE, -1, 0);
#endif
// printf("Mapped memory at %p (size %ld)\n", ptr,
@ -130,9 +138,12 @@ void* AllocateAlignedMemory(size_t size,size_t alignment)
#else
void* ptr = NULL;
#ifdef ANDROID
ptr = memalign(alignment, size);
ptr = memalign(alignment, size);
#elif defined(__SYMBIAN32__)
// On Symbian, we will want to create an RChunk.
ptr = malloc(size);
#else
posix_memalign(&ptr, alignment, size);
posix_memalign(&ptr, alignment, size);
#endif
#endif

View File

@ -17,7 +17,7 @@
#include "Common.h"
#ifdef __APPLE__
#if defined(__APPLE__) || defined(__SYMBIAN32__)
#define __thread
#endif

View File

@ -5,7 +5,7 @@
#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !defined(ANDROID)
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !defined(ANDROID) && !defined(__SYMBIAN32__)
// GCC 4.4 provides <condition_variable>
#include <condition_variable>
#else

View File

@ -5,7 +5,7 @@
#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !defined(ANDROID)
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !defined(ANDROID) && !defined(__SYMBIAN32__)
// GCC 4.4 provides <mutex>
#include <mutex>
#else

View File

@ -5,7 +5,7 @@
#define GCC_VER(x,y,z) ((x) * 10000 + (y) * 100 + (z))
#define GCC_VERSION GCC_VER(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !defined(ANDROID)
#if GCC_VERSION >= GCC_VER(4,4,0) && __GXX_EXPERIMENTAL_CXX0X__ && !defined(ANDROID) && !defined(__SYMBIAN32__)
// GCC 4.4 provides <thread>
#ifndef _GLIBCXX_USE_SCHED_YIELD
#define _GLIBCXX_USE_SCHED_YIELD

View File

@ -211,8 +211,8 @@ void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _P
_CompleteFilename = _Path;
// check for seperator
if (DIR_SEP_CHR != *_CompleteFilename.rbegin())
_CompleteFilename += DIR_SEP_CHR;
if (!strchr(DIR_SEP_CHRS, *_CompleteFilename.rbegin()))
_CompleteFilename += DIR_SEP;
// add the filename
_CompleteFilename += _Filename;
@ -258,31 +258,31 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
// Uri encode and decode.
// RFC1630, RFC1738, RFC2396
//#include <string>
//#include <assert.h>
const char HEX2DEC[256] =
// Some compilers don't like to assume (int)-1 will safely cast to (char)-1 as
// the MSBs aren't 0's. Workaround the issue while maintaining table spacing.
#define N1 (char)-1
const char HEX2DEC[256] =
{
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1, -1,-1,-1,-1,
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0 */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* 1 */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* 2 */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* 3 */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,N1,N1, N1,N1,N1,N1,
/* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 4 */ N1,10,11,12, 13,14,15,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* 5 */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* 6 */ N1,10,11,12, 13,14,15,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* 7 */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* 8 */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* 9 */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* A */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* B */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
/* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
/* C */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* D */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* E */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1,
/* F */ N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1, N1,N1,N1,N1
};
std::string UriDecode(const std::string & sSrc)
@ -376,3 +376,11 @@ std::string UriEncode(const std::string & sSrc)
delete [] pStart;
return sResult;
}
bool StringEndsWith(std::string const &fullString, std::string const &ending) {
if (fullString.length() >= ending.length()) {
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
} else {
return false;
}
}

View File

@ -129,4 +129,6 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
std::string UriDecode(const std::string & sSrc);
std::string UriEncode(const std::string & sSrc);
bool StringEndsWith(std::string const &fullString, std::string const &ending);
#endif // _STRINGUTIL_H_

View File

@ -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
@ -49,14 +55,16 @@ set(SRCS
HLE/sceRtc.cpp
HLE/sceSas.cpp
HLE/sceUmd.cpp
HLE/sceUsb.cpp
HLE/sceUtility.cpp
HLE/sceParseUri.cpp
HLE/sceSsl.cpp
HLE/sceParseUri.cpp
HLE/sceParseHttp.cpp
HLE/scesupPreAcc.cpp
HLE/sceVaudio.cpp
HW/MemoryStick.cpp
HW/MediaEngine.cpp
HW/SasAudio.cpp
FileSystems/BlockDevices.cpp
FileSystems/ISOFileSystem.cpp
FileSystems/DirectoryFileSystem.cpp

View File

@ -33,30 +33,37 @@ CConfig::~CConfig()
void CConfig::Load(const char *iniFileName)
{
iniFilename_ = iniFileName;
NOTICE_LOG(LOADER, "Loading config: %s", iniFileName);
INFO_LOG(LOADER, "Loading config: %s", iniFileName);
bSaveSettings = true;
IniFile iniFile;
iniFile.Load(iniFileName);
if (!iniFile.Load(iniFileName)) {
ERROR_LOG(LOADER, "Failed to read %s. Setting config to default.", iniFileName);
// Continue anyway to initialize the config.
}
IniFile::Section *general = iniFile.GetOrCreateSection("General");
bSpeedLimit = false;
general->Get("FirstRun", &bFirstRun, true);
general->Get("AutoLoadLast", &bAutoLoadLast, false);
general->Get("AutoRun", &bAutoRun, false);
general->Get("AutoRun", &bAutoRun, true);
general->Get("ConfirmOnQuit", &bConfirmOnQuit, false);
general->Get("IgnoreBadMemAccess", &bIgnoreBadMemAccess, true);
general->Get("CurrentDirectory", &currentDirectory, "");
general->Get("ShowDebuggerOnLoad", &bShowDebuggerOnLoad, false);
IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU");
cpu->Get("Core", &iCpuCore, 0);
cpu->Get("FastMemory", &bFastMemory, false);
IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics");
graphics->Get("ShowFPSCounter", &bShowFPSCounter, false);
graphics->Get("DisplayFramebuffer", &bDisplayFramebuffer, false);
graphics->Get("WindowZoom", &iWindowZoom, 1);
graphics->Get("BufferedRendering", &bBufferedRendering, true);
graphics->Get("HardwareTransform", &bHardwareTransform, true);
graphics->Get("LinearFiltering", &bLinearFiltering, false);
IniFile::Section *sound = iniFile.GetOrCreateSection("Sound");
sound->Get("Enable", &bEnableSound, true);
@ -64,14 +71,18 @@ void CConfig::Load(const char *iniFileName)
IniFile::Section *control = iniFile.GetOrCreateSection("Control");
control->Get("ShowStick", &bShowAnalogStick, false);
control->Get("ShowTouchControls", &bShowTouchControls, true);
// Ephemeral settings
bDrawWireframe = false;
}
void CConfig::Save()
{
if (g_Config.bSaveSettings && iniFilename_.size())
{
if (iniFilename_.size() && g_Config.bSaveSettings) {
IniFile iniFile;
iniFile.Load(iniFilename_.c_str());
if (!iniFile.Load(iniFilename_.c_str())) {
ERROR_LOG(LOADER, "Error saving config - can't read ini %s", iniFilename_.c_str());
}
IniFile::Section *general = iniFile.GetOrCreateSection("General");
general->Set("FirstRun", bFirstRun);
@ -83,12 +94,15 @@ void CConfig::Save()
general->Set("ShowDebuggerOnLoad", bShowDebuggerOnLoad);
IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU");
cpu->Set("Core", iCpuCore);
cpu->Set("FastMemory", bFastMemory);
IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics");
graphics->Set("ShowFPSCounter", bShowFPSCounter);
graphics->Set("DisplayFramebuffer", bDisplayFramebuffer);
graphics->Set("WindowZoom", iWindowZoom);
graphics->Set("BufferedRendering", bBufferedRendering);
graphics->Set("HardwareTransform", bHardwareTransform);
graphics->Set("LinearFiltering", bLinearFiltering);
IniFile::Section *sound = iniFile.GetOrCreateSection("Sound");
sound->Set("Enable", bEnableSound);
@ -97,9 +111,12 @@ void CConfig::Save()
control->Set("ShowStick", bShowAnalogStick);
control->Set("ShowTouchControls", bShowTouchControls);
iniFile.Save(iniFilename_.c_str());
NOTICE_LOG(LOADER, "Config saved: %s", iniFilename_.c_str());
if (!iniFile.Save(iniFilename_.c_str())) {
ERROR_LOG(LOADER, "Error saving config - can't write ini %s", iniFilename_.c_str());
return;
}
INFO_LOG(LOADER, "Config saved: %s", iniFilename_.c_str());
} else {
NOTICE_LOG(LOADER, "Error saving config: %s", iniFilename_.c_str());
INFO_LOG(LOADER, "Not saving config");
}
}

View File

@ -32,27 +32,42 @@ public:
CConfig();
~CConfig();
// Many of these are currently broken.
bool bEnableSound;
bool bAutoLoadLast;
// Whether to save the config on close.
bool bSaveSettings;
// These are broken
bool bAutoLoadLast;
bool bFirstRun;
bool bAutoRun;
bool bSpeedLimit;
bool bConfirmOnQuit;
bool bIgnoreBadMemAccess;
bool bDisplayFramebuffer;
bool bBufferedRendering;
bool bAutoRun; // start immediately
// Core
bool bIgnoreBadMemAccess;
bool bFastMemory;
int iCpuCore;
// GFX
bool bDisplayFramebuffer;
bool bHardwareTransform;
bool bBufferedRendering;
bool bDrawWireframe;
bool bLinearFiltering;
int iWindowZoom; // for Windows
// Sound
bool bEnableSound;
// UI
bool bShowTouchControls;
bool bShowDebuggerOnLoad;
bool bShowAnalogStick;
bool bShowFPSCounter;
bool bShowDebugStats;
int iWindowZoom; // for Windows
int iCpuCore;
std::string currentDirectory;
std::string memCardDirectory;
std::string flashDirectory;
void Load(const char *iniFileName = "ppsspp.ini");
void Save();

View File

@ -81,14 +81,14 @@ void Core_SingleStep()
// Some platforms, like Android, do not call this function but handle things on their own.
void Core_Run()
{
#if _DEBUG
#if defined(_DEBUG)
host->UpdateDisassembly();
#endif
while (true)
{
reswitch:
switch(coreState)
switch (coreState)
{
case CORE_RUNNING:
//1: enter a fast runloop
@ -126,24 +126,18 @@ void Core_EnableStepping(bool step)
{
if (step)
{
//PowerPC::Pause();
// Sleep(1);
sleep_ms(1);
#if _DEBUG
#if defined(_DEBUG)
host->SetDebugMode(true);
#endif
coreState=CORE_STEPPING;
coreState = CORE_STEPPING;
}
else
{
#if _DEBUG
#if defined(_DEBUG)
host->SetDebugMode(false);
#endif
coreState = CORE_RUNNING;
//PowerPC::Start();
///SetEvent(m_hStepEvent); //TODO: pulseevent is flawed and can be lost
m_hStepEvent.notify_one();
}
}

View File

@ -33,9 +33,10 @@ void Core_Halt(const char *msg);
bool Core_IsStepping();
// RUNNING must be at 0.
enum CoreState
{
CORE_RUNNING,
CORE_RUNNING = 0,
CORE_STEPPING,
CORE_POWERDOWN,
CORE_ERROR,

View File

@ -91,6 +91,9 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -113,12 +116,20 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\ext\snappy\snappy-c.cpp" />
<ClCompile Include="..\ext\snappy\snappy.cpp" />
<ClCompile Include="Config.cpp" />
<ClCompile Include="Core.cpp" />
<ClCompile Include="CoreTiming.cpp" />
<ClCompile Include="CPU.cpp" />
<ClCompile Include="Debugger\Breakpoints.cpp" />
<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" />
<ClCompile Include="ELF\ElfReader.cpp" />
<ClCompile Include="ELF\ParamSFO.cpp" />
<ClCompile Include="ELF\PrxDecrypter.cpp" />
@ -162,13 +173,15 @@
<ClCompile Include="HLE\sceRtc.cpp" />
<ClCompile Include="HLE\sceSas.cpp" />
<ClCompile Include="HLE\sceSsl.cpp" />
<ClCompile Include="HLE\scesupPreAcc.cpp" />
<ClCompile Include="HLE\sceUmd.cpp" />
<ClCompile Include="HLE\sceUsb.cpp" />
<ClCompile Include="HLE\sceUtility.cpp" />
<ClCompile Include="HLE\sceVaudio.cpp" />
<ClCompile Include="HLE\__sceAudio.cpp" />
<ClCompile Include="Host.cpp" />
<ClCompile Include="HW\MediaEngine.cpp" />
<ClCompile Include="HW\MemoryStick.cpp" />
<ClCompile Include="HW\SasAudio.cpp" />
<ClCompile Include="Loaders.cpp" />
<ClCompile Include="MemMap.cpp" />
<ClCompile Include="MemmapFunctions.cpp" />
@ -242,12 +255,18 @@
<ClCompile Include="MIPS\x86\RegCache.cpp" />
<ClCompile Include="PSPLoaders.cpp" />
<ClCompile Include="PSPMixer.cpp" />
<ClCompile Include="SaveState.cpp" />
<ClCompile Include="System.cpp" />
<ClCompile Include="Util\BlockAllocator.cpp" />
<ClCompile Include="Util\PPGeDraw.cpp" />
<ClCompile Include="Util\ppge_atlas.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\ext\snappy\snappy-internal.h" />
<ClInclude Include="..\ext\snappy\snappy-sinksource.h" />
<ClInclude Include="..\ext\snappy\snappy-stubs-internal.h" />
<ClInclude Include="..\ext\snappy\snappy-stubs-public.h" />
<ClInclude Include="..\ext\snappy\snappy.h" />
<ClInclude Include="Config.h" />
<ClInclude Include="Core.h" />
<ClInclude Include="CoreParameter.h" />
@ -256,6 +275,12 @@
<ClInclude Include="Debugger\Breakpoints.h" />
<ClInclude Include="Debugger\DebugInterface.h" />
<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" />
<ClInclude Include="ELF\ElfReader.h" />
<ClInclude Include="ELF\ElfTypes.h" />
<ClInclude Include="ELF\ParamSFO.h" />
@ -301,13 +326,15 @@
<ClInclude Include="HLE\sceRtc.h" />
<ClInclude Include="HLE\sceSas.h" />
<ClInclude Include="HLE\sceSsl.h" />
<ClInclude Include="HLE\scesupPreAcc.h" />
<ClInclude Include="HLE\sceUmd.h" />
<ClInclude Include="HLE\sceUsb.h" />
<ClInclude Include="HLE\sceUtility.h" />
<ClInclude Include="HLE\sceKernelVTimer.h" />
<ClInclude Include="HLE\sceVaudio.h" />
<ClInclude Include="HLE\__sceAudio.h" />
<ClInclude Include="Host.h" />
<ClInclude Include="HW\MediaEngine.h" />
<ClInclude Include="HW\SasAudio.h" />
<ClInclude Include="HW\MemoryStick.h" />
<ClInclude Include="Loaders.h" />
<ClInclude Include="MemMap.h" />
@ -352,6 +379,7 @@
<ClInclude Include="MIPS\x86\RegCache.h" />
<ClInclude Include="PSPLoaders.h" />
<ClInclude Include="PSPMixer.h" />
<ClInclude Include="SaveState.h" />
<ClInclude Include="System.h" />
<ClInclude Include="Util\BlockAllocator.h" />
<ClInclude Include="Util\Pool.h" />
@ -381,4 +409,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -40,6 +40,15 @@
<Filter Include="HW">
<UniqueIdentifier>{9696662f-7398-489a-a358-5ebbf4ad4d97}</UniqueIdentifier>
</Filter>
<Filter Include="Dialog">
<UniqueIdentifier>{41034c99-9b76-477f-8a77-bffaaffd5e82}</UniqueIdentifier>
</Filter>
<Filter Include="Ext">
<UniqueIdentifier>{1966d4a4-9a34-4a6c-946a-ebaf33633276}</UniqueIdentifier>
</Filter>
<Filter Include="Ext\Snappy">
<UniqueIdentifier>{0b77054f-7fc7-4c33-ada3-762aecde69e5}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ELF\ElfReader.cpp">
@ -291,6 +300,9 @@
<ClCompile Include="HLE\sceImpose.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="HW\MediaEngine.cpp">
<Filter>HW</Filter>
</ClCompile>
<ClCompile Include="Util\PPGeDraw.cpp">
<Filter>Util</Filter>
</ClCompile>
@ -315,12 +327,42 @@
<ClCompile Include="HLE\sceParseHttp.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="HLE\scesupPreAcc.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="HLE\sceVaudio.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="Dialog\PSPDialog.cpp">
<Filter>Dialog</Filter>
</ClCompile>
<ClCompile Include="Dialog\PSPMsgDialog.cpp">
<Filter>Dialog</Filter>
</ClCompile>
<ClCompile Include="Dialog\PSPPlaceholderDialog.cpp">
<Filter>Dialog</Filter>
</ClCompile>
<ClCompile Include="Dialog\PSPSaveDialog.cpp">
<Filter>Dialog</Filter>
</ClCompile>
<ClCompile Include="Dialog\SavedataParam.cpp">
<Filter>Dialog</Filter>
</ClCompile>
<ClCompile Include="Dialog\PSPOskDialog.cpp">
<Filter>Dialog</Filter>
</ClCompile>
<ClCompile Include="HW\SasAudio.cpp">
<Filter>HW</Filter>
</ClCompile>
<ClCompile Include="HLE\sceUsb.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="SaveState.cpp">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="..\ext\snappy\snappy-c.cpp">
<Filter>Ext\Snappy</Filter>
</ClCompile>
<ClCompile Include="..\ext\snappy\snappy.cpp">
<Filter>Ext\Snappy</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@ -560,6 +602,9 @@
<ClInclude Include="HLE\sceImpose.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="HW\MediaEngine.h">
<Filter>HW</Filter>
</ClInclude>
<ClInclude Include="Util\PPGeDraw.h">
<Filter>Util</Filter>
</ClInclude>
@ -584,12 +629,51 @@
<ClInclude Include="HLE\sceParseHttp.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="HLE\scesupPreAcc.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="HLE\sceVaudio.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="Dialog\PSPDialog.h">
<Filter>Dialog</Filter>
</ClInclude>
<ClInclude Include="Dialog\PSPMsgDialog.h">
<Filter>Dialog</Filter>
</ClInclude>
<ClInclude Include="Dialog\PSPPlaceholderDialog.h">
<Filter>Dialog</Filter>
</ClInclude>
<ClInclude Include="Dialog\PSPSaveDialog.h">
<Filter>Dialog</Filter>
</ClInclude>
<ClInclude Include="Dialog\SavedataParam.h">
<Filter>Dialog</Filter>
</ClInclude>
<ClInclude Include="Dialog\PSPOskDialog.h">
<Filter>Dialog</Filter>
</ClInclude>
<ClInclude Include="HW\SasAudio.h">
<Filter>HW</Filter>
</ClInclude>
<ClInclude Include="HLE\sceUsb.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="SaveState.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="..\ext\snappy\snappy.h">
<Filter>Ext\Snappy</Filter>
</ClInclude>
<ClInclude Include="..\ext\snappy\snappy-stubs-public.h">
<Filter>Ext\Snappy</Filter>
</ClInclude>
<ClInclude Include="..\ext\snappy\snappy-sinksource.h">
<Filter>Ext\Snappy</Filter>
</ClInclude>
<ClInclude Include="..\ext\snappy\snappy-stubs-internal.h">
<Filter>Ext\Snappy</Filter>
</ClInclude>
<ClInclude Include="..\ext\snappy\snappy-internal.h">
<Filter>Ext\Snappy</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
@ -597,4 +681,4 @@
<None Include="..\android\jni\Android.mk" />
<None Include="GameLogNotes.txt" />
</ItemGroup>
</Project>
</Project>

View File

@ -48,6 +48,7 @@ struct CoreParameter
bool enableDebugging; // enables breakpoints and other time-consuming debugger features
bool printfEmuLog; // writes "emulator:" logging to stdout
bool headLess; // Try to avoid messageboxes etc
bool useMediaEngine;
// Internal PSP resolution
int renderWidth;

View File

@ -24,6 +24,7 @@
#include "CoreTiming.h"
#include "Core.h"
#include "HLE/sceKernelThread.h"
#include "../Common/ChunkFile.h"
int CPU_HZ = 222000000;
@ -36,6 +37,11 @@ namespace CoreTiming
struct EventType
{
EventType() {}
EventType(TimedCallback cb, const char *n)
: callback(cb), name(n) {}
TimedCallback callback;
const char *name;
};
@ -50,12 +56,6 @@ struct BaseEvent
// Event *next;
};
template <class T>
struct LinkedListItem : public T
{
LinkedListItem<T> *next;
};
typedef LinkedListItem<BaseEvent> Event;
Event *first;
@ -74,11 +74,13 @@ s64 idledCycles;
static std::recursive_mutex externalEventSection;
// Warning: not included in save state.
void (*advanceCallback)(int cyclesExecuted) = NULL;
void SetClockFrequencyMHz(int cpuMhz)
{
CPU_HZ = cpuMhz * 1000000;
// TODO: Rescale times of scheduled events?
}
int GetClockFrequencyMHz()
@ -124,13 +126,24 @@ void FreeTsEvent(Event* ev)
int RegisterEvent(const char *name, TimedCallback callback)
{
EventType type;
type.name = name;
type.callback = callback;
event_types.push_back(type);
event_types.push_back(EventType(callback, name));
return (int)event_types.size() - 1;
}
void AntiCrashCallback(u64 userdata, int cyclesLate)
{
ERROR_LOG(CPU, "Savestate broken: an unregistered event was called.");
Core_Halt("invalid timing events");
}
void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback)
{
if (event_type >= (int) event_types.size())
event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT"));
event_types[event_type] = EventType(callback, name);
}
void UnregisterAllEvents()
{
if (first)
@ -181,7 +194,7 @@ u64 GetIdleTicks()
// This is to be called when outside threads, such as the graphics thread, wants to
// schedule things to be executed on the main thread.
void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata)
void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata)
{
std::lock_guard<std::recursive_mutex> lk(externalEventSection);
Event *ne = GetNewTsEvent();
@ -240,12 +253,12 @@ void AddEventToQueue(Event* ne)
// This must be run ONLY from within the cpu thread
// cyclesIntoFuture may be VERY inaccurate if called from anything else
// than Advance
void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata)
void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata)
{
Event *ne = GetNewEvent();
ne->userdata = userdata;
ne->type = event_type;
ne->time = globalTimer + cyclesIntoFuture;
ne->time = GetTicks() + cyclesIntoFuture;
AddEventToQueue(ne);
}
@ -294,6 +307,7 @@ u64 UnscheduleEvent(int event_type, u64 userdata)
return result;
}
// Warning: not included in save state.
void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted))
{
advanceCallback = callback;
@ -420,7 +434,7 @@ void ProcessFifoWaitEvents()
{
break;
}
}
}
}
void MoveEvents()
@ -437,7 +451,7 @@ void MoveEvents()
// Move free events to threadsafe pool
while(allocatedTsEvents > 0 && eventPool)
{
{
Event *ev = eventPool;
eventPool = ev->next;
ev->next = eventTsPool;
@ -454,7 +468,7 @@ void Advance()
ProcessFifoWaitEvents();
if (!first)
if (!first)
{
// WARN_LOG(CPU, "WARNING - no events in queue. Setting downcount to 10000");
downcount += 10000;
@ -486,24 +500,26 @@ void Idle(int maxIdle)
if (maxIdle != 0 && cyclesDown > maxIdle)
cyclesDown = maxIdle;
if (first && cyclesDown > 0)
{
int cyclesExecuted = slicelength - downcount;
int cyclesNextEvent = (int) (first->time - globalTimer);
if (first && cyclesDown > 0)
{
int cyclesExecuted = slicelength - downcount;
int cyclesNextEvent = (int) (first->time - globalTimer);
if (cyclesNextEvent < cyclesExecuted + cyclesDown)
{
cyclesDown = cyclesNextEvent - cyclesExecuted;
// Now, now... no time machines, please.
if (cyclesDown < 0)
cyclesDown = 0;
}
}
if (cyclesNextEvent < cyclesExecuted + cyclesDown)
{
cyclesDown = cyclesNextEvent - cyclesExecuted;
// Now, now... no time machines, please.
if (cyclesDown < 0)
cyclesDown = 0;
}
}
DEBUG_LOG(CPU, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(CPU_HZ * 0.001f));
idledCycles += cyclesDown;
downcount -= cyclesDown;
if (downcount == 0)
downcount = -1;
}
std::string GetScheduledEventsSummary()
@ -527,4 +543,29 @@ std::string GetScheduledEventsSummary()
return text;
}
void Event_DoState(PointerWrap &p, BaseEvent *ev)
{
p.Do(*ev);
}
void DoState(PointerWrap &p)
{
std::lock_guard<std::recursive_mutex> lk(externalEventSection);
int n = (int) event_types.size();
p.Do(n);
// These (should) be filled in later by the modules.
event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT"));
p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **) NULL);
p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
p.Do(CPU_HZ);
p.Do(downcount);
p.Do(slicelength);
p.Do(globalTimer);
p.Do(idledCycles);
p.DoMarker("CoreTiming");
}
} // namespace

View File

@ -32,33 +32,42 @@
// ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
#include "../Globals.h"
#include "../Common/ChunkFile.h"
#include <string>
//const int CPU_HZ = 222000000;
extern int CPU_HZ;
inline int msToCycles(int ms) {
inline s64 msToCycles(int ms) {
return CPU_HZ / 1000 * ms;
}
inline int msToCycles(float ms) {
return (int)(CPU_HZ * ms * (0.001f));
inline s64 msToCycles(float ms) {
return (s64)(CPU_HZ * ms * (0.001f));
}
inline int msToCycles(double ms) {
return (int)(CPU_HZ * ms * (0.001));
inline s64 msToCycles(double ms) {
return (s64)(CPU_HZ * ms * (0.001));
}
inline int usToCycles(float us) {
return (int)(CPU_HZ * us * (0.000001f));
inline s64 usToCycles(float us) {
return (s64)(CPU_HZ * us * (0.000001f));
}
inline int usToCycles(int us) {
return (int)(CPU_HZ / 1000000 * us);
inline s64 usToCycles(int us) {
return (CPU_HZ / 1000000 * (s64)us);
}
inline u64 cyclesToUs(u64 cycles) {
inline s64 usToCycles(s64 us) {
return (CPU_HZ / 1000000 * us);
}
inline s64 usToCycles(u64 us) {
return (s64)(CPU_HZ / 1000000 * us);
}
inline s64 cyclesToUs(s64 cycles) {
return cycles / (CPU_HZ / 1000000);
}
@ -74,12 +83,14 @@ namespace CoreTiming
// Returns the event_type identifier.
int RegisterEvent(const char *name, TimedCallback callback);
// For save states.
void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback);
void UnregisterAllEvents();
// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk,
// when we implement state saves.
void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata=0);
void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata=0);
void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata=0);
void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata=0);
void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata=0);
u64 UnscheduleEvent(int event_type, u64 userdata);
@ -99,10 +110,13 @@ namespace CoreTiming
void LogPendingEvents();
// Warning: not included in save states.
void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted));
std::string GetScheduledEventsSummary();
void DoState(PointerWrap &p);
void SetClockFrequencyMHz(int cpuMhz);
int GetClockFrequencyMHz();
extern int downcount;

View File

@ -40,7 +40,7 @@ void MemCheck::Action(u32 iValue, u32 addr, bool write, int size, u32 pc)
if (bLog)
{
char temp[256];
printf(temp,"CHK %08x %s%i at %08x (%s), PC=%08x (%s)",iValue,write?"Write":"Read",size*8,addr,symbolMap.GetDescription(addr),pc,symbolMap.GetDescription(pc));
sprintf(temp,"CHK %08x %s%i at %08x (%s), PC=%08x (%s)",iValue,write?"Write":"Read",size*8,addr,symbolMap.GetDescription(addr),pc,symbolMap.GetDescription(pc));
ERROR_LOG(MEMMAP,"%s",temp);
}
if (bBreak)

View File

@ -144,10 +144,9 @@ bool SymbolMap::LoadSymbolMap(const char *filename)
{
char line[512],temp[256];
fgets(line,511,f);
if (strlen(line)<4)
if (strlen(line) < 4 || sscanf(line, "%s", temp) != 1)
continue;
sscanf(line,"%s",temp);
if (strcmp(temp,"UNUSED")==0) continue;
if (strcmp(temp,".text")==0) {started=true;continue;};
if (strcmp(temp,".init")==0) {started=true;continue;};
@ -233,7 +232,7 @@ int SymbolMap::GetSymbolNum(unsigned int address, SymbolType symmask)
}
char temp[256];
char descriptionTemp[256];
char *SymbolMap::GetDescription(unsigned int address)
{
@ -244,8 +243,8 @@ char *SymbolMap::GetDescription(unsigned int address)
return entries[fun].name;
else
{
sprintf(temp, "(%08x)", address);
return temp;
sprintf(descriptionTemp, "(%08x)", address);
return descriptionTemp;
}
//}
//else
@ -438,11 +437,12 @@ void SymbolMap::UseFuncSignaturesFile(const char *filename, u32 maxAddress)
//#1: Read the signature file and put them in a fast data structure
FILE *f = fopen(filename, "r");
int count;
fscanf(f,"%08x\n",&count);
u32 inst,size,hash;
if (fscanf(f, "%08x\n", &count) != 1)
count = 0;
char name[256];
for (int a=0; a<count; a++)
{
u32 inst, size, hash;
if (fscanf(f,"%08x\t%08x\t%08x\t%s\n",&inst,&size,&hash,name)!=EOF)
sigs[numSigs++]=Sig(inst,size,hash,name);
else

80
Core/Dialog/PSPDialog.cpp Normal file
View File

@ -0,0 +1,80 @@
// 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 "../Util/PPGeDraw.h"
#include "PSPDialog.h"
PSPDialog::PSPDialog() : status(SCE_UTILITY_STATUS_SHUTDOWN)
, lastButtons(0)
, buttons(0)
{
}
PSPDialog::~PSPDialog() {
}
PSPDialog::DialogStatus PSPDialog::GetStatus()
{
PSPDialog::DialogStatus retval = status;
if (status == SCE_UTILITY_STATUS_SHUTDOWN)
status = SCE_UTILITY_STATUS_NONE;
if (status == SCE_UTILITY_STATUS_INITIALIZE)
status = SCE_UTILITY_STATUS_RUNNING;
return retval;
}
void PSPDialog::StartDraw()
{
PPGeBegin();
PPGeDraw4Patch(I_BUTTON, 0, 0, 480, 272, 0xcFFFFFFF);
}
void PSPDialog::EndDraw()
{
PPGeEnd();
}
void PSPDialog::DisplayMessage(std::string text)
{
PPGeDrawText(text.c_str(), 40, 30, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
}
int PSPDialog::Shutdown()
{
status = SCE_UTILITY_STATUS_SHUTDOWN;
return 0;
}
int PSPDialog::Update()
{
return 0;
}
void PSPDialog::DoState(PointerWrap &p)
{
p.Do(status);
p.Do(lastButtons);
p.Do(buttons);
p.DoMarker("PSPDialog");
}
bool PSPDialog::IsButtonPressed(int checkButton)
{
return (!(lastButtons & checkButton)) && (buttons & checkButton);
}

73
Core/Dialog/PSPDialog.h Normal file
View File

@ -0,0 +1,73 @@
// 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 "../Config.h"
#include "../../Common/ChunkFile.h"
#define SCE_UTILITY_DIALOG_RESULT_SUCCESS 0
#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
{
public:
PSPDialog();
virtual ~PSPDialog();
virtual int Update();
virtual int Shutdown();
virtual void DoState(PointerWrap &p);
enum DialogStatus
{
SCE_UTILITY_STATUS_NONE = 0,
SCE_UTILITY_STATUS_INITIALIZE = 1,
SCE_UTILITY_STATUS_RUNNING = 2,
SCE_UTILITY_STATUS_FINISHED = 3,
SCE_UTILITY_STATUS_SHUTDOWN = 4
};
DialogStatus GetStatus();
void StartDraw();
void EndDraw();
protected:
bool IsButtonPressed(int checkButton);
void DisplayMessage(std::string text);
DialogStatus status;
unsigned int lastButtons;
unsigned int buttons;
};

View File

@ -0,0 +1,249 @@
// 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 "PSPMsgDialog.h"
#include "../Util/PPGeDraw.h"
#include "../HLE/sceCtrl.h"
#include "../Core/MemMap.h"
PSPMsgDialog::PSPMsgDialog()
: PSPDialog()
, display(DS_NONE)
{
}
PSPMsgDialog::~PSPMsgDialog() {
}
int PSPMsgDialog::Init(unsigned int paramAddr)
{
// Ignore if already running
if (status != SCE_UTILITY_STATUS_NONE && status != SCE_UTILITY_STATUS_SHUTDOWN)
{
return 0;
}
messageDialogAddr = paramAddr;
if (!Memory::IsValidAddress(messageDialogAddr))
{
return 0;
}
Memory::ReadStruct(messageDialogAddr, &messageDialog);
// debug info
int optionsNotCoded = ((messageDialog.options | SCE_UTILITY_MSGDIALOG_DEBUG_OPTION_CODED) ^ SCE_UTILITY_MSGDIALOG_DEBUG_OPTION_CODED);
if(optionsNotCoded)
{
ERROR_LOG(HLE,"PSPMsgDialog options not coded : 0x%08x",optionsNotCoded);
}
yesnoChoice = 1;
if (messageDialog.type == 0) // number
{
INFO_LOG(HLE, "MsgDialog: %08x", messageDialog.errorNum);
display = DS_ERROR;
}
else
{
INFO_LOG(HLE, "MsgDialog: %s", messageDialog.string);
display = DS_MESSAGE;
if(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_YESNO)
display = DS_YESNO;
if(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_OK)
display = DS_OK;
if(messageDialog.options & SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO)
yesnoChoice = 0;
}
status = SCE_UTILITY_STATUS_INITIALIZE;
lastButtons = __CtrlPeekButtons();
return 0;
}
void PSPMsgDialog::DisplayBack()
{
PPGeDrawImage(cancelButtonImg, 250, 220, 20, 20, 0, 0xFFFFFFFF);
PPGeDrawText("Back", 270, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
}
void PSPMsgDialog::DisplayYesNo()
{
PPGeDrawText("Yes", 200, 150, PPGE_ALIGN_LEFT, 0.5f, (yesnoChoice == 1?0xFF0000FF:0xFFFFFFFF));
PPGeDrawText("No", 320, 150, PPGE_ALIGN_LEFT, 0.5f, (yesnoChoice == 0?0xFF0000FF:0xFFFFFFFF));
if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0)
{
yesnoChoice = 1;
}
else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1)
{
yesnoChoice = 0;
}
}
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);
PPGeDrawText("Back", 320, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
}
int PSPMsgDialog::Update()
{
switch (status) {
case SCE_UTILITY_STATUS_FINISHED:
status = SCE_UTILITY_STATUS_SHUTDOWN;
break;
}
if (status != SCE_UTILITY_STATUS_RUNNING)
{
return 0;
}
const char *text;
if (messageDialog.type == 0) {
char temp[256];
sprintf(temp, "Error code: %08x", messageDialog.errorNum);
text = temp;
} else {
text = messageDialog.string;
}
buttons = __CtrlPeekButtons();
okButtonImg = I_CIRCLE;
cancelButtonImg = I_CROSS;
okButtonFlag = CTRL_CIRCLE;
cancelButtonFlag = CTRL_CROSS;
if(messageDialog.common.buttonSwap == 1)
{
okButtonImg = I_CROSS;
cancelButtonImg = I_CIRCLE;
okButtonFlag = CTRL_CROSS;
cancelButtonFlag = CTRL_CIRCLE;
}
switch(display)
{
case DS_MESSAGE:
StartDraw();
DisplayMessage(text);
// TODO : Dialogs should take control over input and not send them to the game while displaying
DisplayBack();
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
messageDialog.buttonPressed = 0;
}
EndDraw();
break;
case DS_ERROR:
StartDraw();
DisplayMessage(text);
// TODO : Dialogs should take control over input and not send them to the game while displaying
DisplayBack();
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
messageDialog.buttonPressed = 0;
}
EndDraw();
break;
case DS_YESNO:
StartDraw();
DisplayMessage(text);
DisplayYesNo();
// TODO : Dialogs should take control over input and not send them to the game while displaying
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
messageDialog.buttonPressed = 3;
}
else if (IsButtonPressed(okButtonFlag))
{
if(yesnoChoice == 0)
{
status = SCE_UTILITY_STATUS_FINISHED;
messageDialog.buttonPressed = 2;
}
else
{
status = SCE_UTILITY_STATUS_FINISHED;
messageDialog.buttonPressed = 1;
}
}
EndDraw();
break;
case DS_OK:
StartDraw();
DisplayMessage(text);
// TODO : Dialogs should take control over input and not send them to the game while displaying
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
messageDialog.buttonPressed = 3;
}
else if (IsButtonPressed(okButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
messageDialog.buttonPressed = 1;
}
EndDraw();
break;
default:
status = SCE_UTILITY_STATUS_FINISHED;
return 0;
break;
}
lastButtons = buttons;
Memory::WriteStruct(messageDialogAddr, &messageDialog);
return 0;
}
int PSPMsgDialog::Shutdown()
{
return PSPDialog::Shutdown();
}
void PSPMsgDialog::DoState(PointerWrap &p)
{
PSPDialog::DoState(p);
p.Do(display);
p.Do(messageDialog);
p.Do(messageDialogAddr);
p.Do(yesnoChoice);
p.Do(okButtonImg);
p.Do(cancelButtonImg);
p.Do(okButtonFlag);
p.Do(cancelButtonFlag);
p.DoMarker("PSPMsgDialog");
}

View File

@ -0,0 +1,79 @@
// 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"
#define SCE_UTILITY_MSGDIALOG_OPTION_ERROR 0 // Do nothing
#define SCE_UTILITY_MSGDIALOG_OPTION_TEXT 0x00000001
#define SCE_UTILITY_MSGDIALOG_OPTION_YESNO 0x00000010
#define SCE_UTILITY_MSGDIALOG_OPTION_OK 0x00000020
#define SCE_UTILITY_MSGDIALOG_OPTION_DEFAULT_NO 0x00000100
#define SCE_UTILITY_MSGDIALOG_DEBUG_OPTION_CODED 0x00000131 // OR of all options coded to display warning
struct pspMessageDialog
{
pspUtilityDialogCommon common;
int result;
int type;
unsigned int errorNum;
char string[512];
unsigned int options;
unsigned int buttonPressed; // 0=?, 1=Yes/OK, 2=No, 3=Back
};
class PSPMsgDialog: public PSPDialog {
public:
PSPMsgDialog();
virtual ~PSPMsgDialog();
virtual int Init(unsigned int paramAddr);
virtual int Update();
virtual int Shutdown();
virtual void DoState(PointerWrap &p);
private :
void DisplayBack();
void DisplayYesNo();
void DisplayEnterBack();
enum DisplayState
{
DS_NONE,
DS_MESSAGE,
DS_ERROR,
DS_YESNO,
DS_OK
};
DisplayState display;
pspMessageDialog messageDialog;
int messageDialogAddr;
int yesnoChoice;
int okButtonImg;
int cancelButtonImg;
int okButtonFlag;
int cancelButtonFlag;
};

View File

@ -0,0 +1,249 @@
// 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"
#define NUMKEYROWS 4
#define KEYSPERROW 12
#define NUMBEROFVALIDCHARS (KEYSPERROW * NUMKEYROWS)
const char oskKeys[NUMKEYROWS][KEYSPERROW + 1] =
{
{'1','2','3','4','5','6','7','8','9','0','-','+','\0'},
{'Q','W','E','R','T','Y','U','I','O','P','[',']','\0'},
{'A','S','D','F','G','H','J','K','L',';','@','~','\0'},
{'Z','X','C','V','B','N','M',',','.','/','?','\\','\0'},
};
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 = (char)(Memory::Read_U16(addr))))
{
*string++ = c;
addr+=2;
}
*string++ = '\0';
_string = stringBuffer;
}
int PSPOskDialog::Init(u32 oskPtr)
{
// Ignore if already running
if (status != SCE_UTILITY_STATUS_NONE && status != SCE_UTILITY_STATUS_SHUTDOWN)
{
return -1;
}
status = SCE_UTILITY_STATUS_INITIALIZE;
memset(&oskParams, 0, sizeof(oskParams));
memset(&oskData, 0, sizeof(oskData));
// TODO: should this be init'd to oskIntext?
inputChars.clear();
oskParamsAddr = oskPtr;
selectedChar = 0;
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;
}
// Eat any keys pressed before the dialog inited.
__CtrlReadLatch();
return 0;
}
void PSPOskDialog::RenderKeyboard()
{
int selectedRow = selectedChar / KEYSPERROW;
int selectedExtra = selectedChar % KEYSPERROW;
char temp[2];
temp[1] = '\0';
int limit = oskData.outtextlimit;
// TODO: Test more thoroughly. Encountered a game where this was 0.
if (limit <= 0)
limit = 16;
const float keyboardLeftSide = (480.0f - (23.0f * KEYSPERROW)) / 2.0f;
float previewLeftSide = (480.0f - (15.0f * limit)) / 2.0f;
PPGeDrawText(oskDesc.c_str(), 480/2, 20, PPGE_ALIGN_CENTER, 0.5f, 0xFFFFFFFF);
for (int i = 0; i < limit; ++i)
{
u32 color = 0xFFFFFFFF;
if (i < (int) inputChars.size())
temp[0] = inputChars[i];
else if (i == inputChars.size())
{
temp[0] = oskKeys[selectedRow][selectedExtra];
color = 0xFF3060FF;
}
else
temp[0] = '_';
PPGeDrawText(temp, previewLeftSide + (i * 16.0f), 40.0f, NULL, 0.5f, color);
}
for (int row = 0; row < NUMKEYROWS; ++row)
{
for (int col = 0; col < KEYSPERROW; ++col)
{
u32 color = 0xFFFFFFFF;
if (selectedRow == row && col == selectedExtra)
color = 0xFF7f7f7f;
temp[0] = oskKeys[row][col];
PPGeDrawText(temp, keyboardLeftSide + (25.0f * col), 70.0f + (25.0f * row), NULL, 0.6f, color);
if (selectedRow == row && col == selectedExtra)
PPGeDrawText("_", keyboardLeftSide + (25.0f * col), 70.0f + (25.0f * row), NULL, 0.6f, 0xFFFFFFFF);
}
}
}
int PSPOskDialog::Update()
{
buttons = __CtrlReadLatch();
int selectedRow = selectedChar / KEYSPERROW;
int selectedExtra = selectedChar % KEYSPERROW;
int limit = oskData.outtextlimit;
// TODO: Test more thoroughly. Encountered a game where this was 0.
if (limit <= 0)
limit = 16;
if (status == SCE_UTILITY_STATUS_INITIALIZE)
{
status = SCE_UTILITY_STATUS_RUNNING;
}
else if (status == SCE_UTILITY_STATUS_RUNNING)
{
StartDraw();
RenderKeyboard();
PPGeDrawImage(I_CROSS, 100, 220, 20, 20, 0, 0xFFFFFFFF);
PPGeDrawText("Select", 130, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
PPGeDrawImage(I_CIRCLE, 200, 220, 20, 20, 0, 0xFFFFFFFF);
PPGeDrawText("Delete", 230, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
PPGeDrawImage(I_BUTTON, 290, 220, 50, 20, 0, 0xFFFFFFFF);
PPGeDrawText("Start", 305, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
PPGeDrawText("Finish", 350, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
if (IsButtonPressed(CTRL_UP))
{
selectedChar -= KEYSPERROW;
}
else if (IsButtonPressed(CTRL_DOWN))
{
selectedChar += KEYSPERROW;
}
else if (IsButtonPressed(CTRL_LEFT))
{
selectedChar--;
if (((selectedChar + KEYSPERROW) % KEYSPERROW) == KEYSPERROW - 1)
selectedChar += KEYSPERROW;
}
else if (IsButtonPressed(CTRL_RIGHT))
{
selectedChar++;
if ((selectedChar % KEYSPERROW) == 0)
selectedChar -= KEYSPERROW;
}
selectedChar = (selectedChar + NUMBEROFVALIDCHARS) % NUMBEROFVALIDCHARS;
// TODO : Dialogs should take control over input and not send them to the game while displaying
if (IsButtonPressed(CTRL_CROSS))
{
if ((int) inputChars.size() < limit)
inputChars += oskKeys[selectedRow][selectedExtra];
}
else if (IsButtonPressed(CTRL_CIRCLE))
{
if (inputChars.size() > 0)
inputChars.resize(inputChars.size() - 1);
}
else if (IsButtonPressed(CTRL_START))
{
status = SCE_UTILITY_STATUS_FINISHED;
}
EndDraw();
}
else if (status == SCE_UTILITY_STATUS_FINISHED)
{
status = SCE_UTILITY_STATUS_SHUTDOWN;
}
for (int i = 0; i < limit; ++i)
{
u16 value = 0;
if (i < (int) inputChars.size())
value = 0x0000 ^ inputChars[i];
Memory::Write_U16(value, oskData.outtextPtr + (2 * i));
}
oskData.outtextlength = inputChars.size();
oskParams.base.result= 0;
oskData.result = PSP_UTILITY_OSK_RESULT_CHANGED;
Memory::WriteStruct(oskParams.SceUtilityOskDataPtr, &oskData);
Memory::WriteStruct(oskParamsAddr, &oskParams);
return 0;
}
void PSPOskDialog::DoState(PointerWrap &p)
{
PSPDialog::DoState(p);
p.Do(oskParams);
p.Do(oskData);
p.Do(oskDesc);
p.Do(oskIntext);
p.Do(oskOuttext);
p.Do(oskParamsAddr);
p.Do(selectedChar);
p.Do(inputChars);
p.DoMarker("PSPOskDialog");
}

164
Core/Dialog/PSPOskDialog.h Normal file
View File

@ -0,0 +1,164 @@
// 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"
/**
* 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;
class PSPOskDialog: public PSPDialog {
public:
PSPOskDialog();
virtual ~PSPOskDialog();
virtual int Init(u32 oskPtr);
virtual int Update();
virtual void DoState(PointerWrap &p);
private:
void HackyGetStringWide(std::string& _string, const u32 em_address);
void RenderKeyboard();
SceUtilityOskParams oskParams;
SceUtilityOskData oskData;
std::string oskDesc;
std::string oskIntext;
std::string oskOuttext;
int oskParamsAddr;
int selectedChar;
std::string inputChars;
};

View File

@ -0,0 +1,50 @@
// 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 "PSPPlaceholderDialog.h"
PSPPlaceholderDialog::PSPPlaceholderDialog() : PSPDialog() {
}
PSPPlaceholderDialog::~PSPPlaceholderDialog() {
}
int PSPPlaceholderDialog::Init()
{
status = SCE_UTILITY_STATUS_INITIALIZE;
return 0;
}
int PSPPlaceholderDialog::Update()
{
//__UtilityUpdate();
if (status == SCE_UTILITY_STATUS_INITIALIZE)
{
status = SCE_UTILITY_STATUS_RUNNING;
}
else if (status == SCE_UTILITY_STATUS_RUNNING)
{
status = SCE_UTILITY_STATUS_FINISHED;
}
else if (status == SCE_UTILITY_STATUS_FINISHED)
{
status = SCE_UTILITY_STATUS_SHUTDOWN;
}
return 0;
}

View File

@ -17,15 +17,14 @@
#pragma once
#include "../../Globals.h"
#include "PSPDialog.h"
class ShaderManager;
class GPU
{
class PSPPlaceholderDialog: public PSPDialog {
public:
static u32 EnqueueList(u32 listpc, u32 stall);
static void UpdateStall(int listid, u32 newstall);
static void ExecuteOp(u32 op, u32 diff);
static bool InterpretList();
PSPPlaceholderDialog();
virtual ~PSPPlaceholderDialog();
virtual int Init();
virtual int Update();
};

View File

@ -0,0 +1,765 @@
// 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 "PSPSaveDialog.h"
#include "../Util/PPGeDraw.h"
#include "../HLE/sceCtrl.h"
#include "../Core/MemMap.h"
PSPSaveDialog::PSPSaveDialog()
: PSPDialog()
, display(DS_NONE)
, currentSelectedSave(0)
{
param.SetPspParam(0);
}
PSPSaveDialog::~PSPSaveDialog() {
}
int PSPSaveDialog::Init(int paramAddr)
{
// Ignore if already running
if (status != SCE_UTILITY_STATUS_NONE && status != SCE_UTILITY_STATUS_SHUTDOWN)
{
ERROR_LOG(HLE,"A save request is already running !");
return 0;
}
int size = Memory::Read_U32(paramAddr);
memset(&request,0,sizeof(request));
// Only copy the right size to support different save request format
Memory::Memcpy(&request,paramAddr,size);
requestAddr = paramAddr;
u32 retval = param.SetPspParam(&request);
INFO_LOG(HLE,"sceUtilitySavedataInitStart(%08x)", paramAddr);
INFO_LOG(HLE,"Mode: %i", param.GetPspParam()->mode);
switch(param.GetPspParam()->mode)
{
case SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD:
case SCE_UTILITY_SAVEDATA_TYPE_LOAD:
DEBUG_LOG(HLE, "Loading. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetSaveName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
display = DS_NONE;
break;
case SCE_UTILITY_SAVEDATA_TYPE_LISTLOAD:
DEBUG_LOG(HLE, "Loading. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
if(param.GetFilenameCount() == 0)
display = DS_LOAD_NODATA;
else
display = DS_LOAD_LIST_CHOICE;
break;
case SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE:
case SCE_UTILITY_SAVEDATA_TYPE_SAVE:
DEBUG_LOG(HLE, "Saving. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
display = DS_NONE;
break;
case SCE_UTILITY_SAVEDATA_TYPE_LISTSAVE:
DEBUG_LOG(HLE, "Saving. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
display = DS_SAVE_LIST_CHOICE;
break;
case SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE:
DEBUG_LOG(HLE, "Delete. Title: %s Save: %s File: %s", param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
if(param.GetFilenameCount() == 0)
display = DS_DELETE_NODATA;
else
display = DS_DELETE_LIST_CHOICE;
break;
case SCE_UTILITY_SAVEDATA_TYPE_SIZES:
case SCE_UTILITY_SAVEDATA_TYPE_LIST:
case SCE_UTILITY_SAVEDATA_TYPE_FILES:
case SCE_UTILITY_SAVEDATA_TYPE_SIZES22:
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE:
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE:
case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE:
display = DS_NONE;
break;
case SCE_UTILITY_SAVEDATA_TYPE_DELETE: // This run on PSP display a list of all save on the PSP. Weird. (Not really, it's to let you free up space)
display = DS_DELETE_LIST_CHOICE;
break;
default:
{
ERROR_LOG(HLE, "Load/Save function %d not coded. Title: %s Save: %s File: %s", param.GetPspParam()->mode, param.GetGameName(param.GetPspParam()).c_str(), param.GetGameName(param.GetPspParam()).c_str(), param.GetFileName(param.GetPspParam()).c_str());
param.GetPspParam()->result = 0;
status = SCE_UTILITY_STATUS_INITIALIZE;
display = DS_NONE;
return 0; // Return 0 should allow the game to continue, but missing function must be implemented and returning the right value or the game can block.
}
break;
}
status = (int)retval < 0 ? SCE_UTILITY_STATUS_SHUTDOWN : SCE_UTILITY_STATUS_INITIALIZE;
currentSelectedSave = 0;
lastButtons = __CtrlPeekButtons();
/*INFO_LOG(HLE,"Dump Param :");
INFO_LOG(HLE,"size : %d",param.GetPspParam()->size);
INFO_LOG(HLE,"language : %d",param.GetPspParam()->language);
INFO_LOG(HLE,"buttonSwap : %d",param.GetPspParam()->buttonSwap);
INFO_LOG(HLE,"result : %d",param.GetPspParam()->result);
INFO_LOG(HLE,"mode : %d",param.GetPspParam()->mode);
INFO_LOG(HLE,"bind : %d",param.GetPspParam()->bind);
INFO_LOG(HLE,"overwriteMode : %d",param.GetPspParam()->overwriteMode);
INFO_LOG(HLE,"gameName : %s",param.GetGameName(param.GetPspParam()).c_str());
INFO_LOG(HLE,"saveName : %s",param.GetPspParam()->saveName);
INFO_LOG(HLE,"saveNameList : %08x",*((unsigned int*)&param.GetPspParam()->saveNameList));
INFO_LOG(HLE,"fileName : %s",param.GetPspParam()->fileName);
INFO_LOG(HLE,"dataBuf : %08x",*((unsigned int*)&param.GetPspParam()->dataBuf));
INFO_LOG(HLE,"dataBufSize : %u",param.GetPspParam()->dataBufSize);
INFO_LOG(HLE,"dataSize : %u",param.GetPspParam()->dataSize);
INFO_LOG(HLE,"sfo title : %s",param.GetPspParam()->sfoParam.title);
INFO_LOG(HLE,"sfo savedataTitle : %s",param.GetPspParam()->sfoParam.savedataTitle);
INFO_LOG(HLE,"sfo detail : %s",param.GetPspParam()->sfoParam.detail);
INFO_LOG(HLE,"icon0 data : %08x",*((unsigned int*)&param.GetPspParam()->icon0FileData.buf));
INFO_LOG(HLE,"icon0 size : %u",param.GetPspParam()->icon0FileData.bufSize);
INFO_LOG(HLE,"icon1 data : %08x",*((unsigned int*)&param.GetPspParam()->icon1FileData.buf));
INFO_LOG(HLE,"icon1 size : %u",param.GetPspParam()->icon1FileData.bufSize);
INFO_LOG(HLE,"pic1 data : %08x",*((unsigned int*)&param.GetPspParam()->pic1FileData.buf));
INFO_LOG(HLE,"pic1 size : %u",param.GetPspParam()->pic1FileData.bufSize);
INFO_LOG(HLE,"snd0 data : %08x",*((unsigned int*)&param.GetPspParam()->snd0FileData.buf));
INFO_LOG(HLE,"snd0 size : %u",param.GetPspParam()->snd0FileData.bufSize);*/
return retval;
}
void PSPSaveDialog::DisplaySaveList(bool canMove)
{
int displayCount = 0;
for(int i = 0; i < param.GetFilenameCount(); i++)
{
int textureColor = 0xFFFFFFFF;
if(param.GetFileInfo(i).size == 0)
{
textureColor = 0xFF777777;
}
// Calc save image position on screen
float w = 150;
float h = 80;
float x = 20;
if(displayCount != currentSelectedSave)
{
w = 80;
h = 40;
x = 50;
}
float y = 80;
if(displayCount < currentSelectedSave)
y -= 50 * (currentSelectedSave - displayCount);
else if(displayCount > currentSelectedSave)
{
y += 90 + 50 * (displayCount - currentSelectedSave - 1);
}
int tw = 256;
int th = 256;
if(param.GetFileInfo(i).textureData != 0)
{
tw = param.GetFileInfo(i).textureWidth;
th = param.GetFileInfo(i).textureHeight;
PPGeSetTexture(param.GetFileInfo(i).textureData, param.GetFileInfo(i).textureWidth, param.GetFileInfo(i).textureHeight);
}
else
{
PPGeDisableTexture();
}
PPGeDrawImage(x, y, w, h, 0, 0 ,1 ,1 ,tw, th, textureColor);
PPGeSetDefaultTexture();
displayCount++;
}
if(canMove)
{
if (IsButtonPressed(CTRL_UP) && currentSelectedSave > 0)
{
currentSelectedSave--;
}
else if (IsButtonPressed(CTRL_DOWN) && currentSelectedSave < (param.GetFilenameCount()-1))
{
currentSelectedSave++;
}
}
}
void PSPSaveDialog::DisplaySaveIcon()
{
int textureColor = 0xFFFFFFFF;
if(param.GetFileInfo(currentSelectedSave).size == 0)
{
textureColor = 0xFF777777;
}
// Calc save image position on screen
float w = 150;
float h = 80;
float x = 20;
float y = 80;
int tw = 256;
int th = 256;
if(param.GetFileInfo(currentSelectedSave).textureData != 0)
{
tw = param.GetFileInfo(currentSelectedSave).textureWidth;
th = param.GetFileInfo(currentSelectedSave).textureHeight;
PPGeSetTexture(param.GetFileInfo(currentSelectedSave).textureData, param.GetFileInfo(currentSelectedSave).textureWidth, param.GetFileInfo(currentSelectedSave).textureHeight);
}
else
{
PPGeDisableTexture();
}
PPGeDrawImage(x, y, w, h, 0, 0 ,1 ,1 ,tw, th, textureColor);
if(param.GetFileInfo(currentSelectedSave).textureData != 0)
{
PPGeSetDefaultTexture();
}
}
void PSPSaveDialog::DisplaySaveDataInfo1()
{
if(param.GetFileInfo(currentSelectedSave).size == 0)
{
PPGeDrawText("New Save", 200, 110, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
}
else
{
char txt[2048];
_dbg_assert_msg_(HLE, sizeof(txt) > sizeof(SaveFileInfo), "Local buffer is too small.");
snprintf(txt,2048,"%s\n%02d/%02d/%d %02d:%02d %lld KB\n%s\n%s"
, param.GetFileInfo(currentSelectedSave).title
, param.GetFileInfo(currentSelectedSave).modif_time.tm_mday
, param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1
, param.GetFileInfo(currentSelectedSave).modif_time.tm_year + 1900
, param.GetFileInfo(currentSelectedSave).modif_time.tm_hour
, param.GetFileInfo(currentSelectedSave).modif_time.tm_min
, param.GetFileInfo(currentSelectedSave).size / 1024
, param.GetFileInfo(currentSelectedSave).saveTitle
, param.GetFileInfo(currentSelectedSave).saveDetail
);
std::string saveinfoTxt = txt;
PPGeDrawText(saveinfoTxt.c_str(), 200, 80, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
}
}
void PSPSaveDialog::DisplaySaveDataInfo2()
{
if(param.GetFileInfo(currentSelectedSave).size == 0)
{
}
else
{
char txt[1024];
snprintf(txt,1024,"%s\n%02d/%02d/%d %02d:%02d\n%lld KB"
, param.GetFileInfo(currentSelectedSave).saveTitle
, param.GetFileInfo(currentSelectedSave).modif_time.tm_mday
, param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1
, param.GetFileInfo(currentSelectedSave).modif_time.tm_year + 1900
, param.GetFileInfo(currentSelectedSave).modif_time.tm_hour
, param.GetFileInfo(currentSelectedSave).modif_time.tm_min
, param.GetFileInfo(currentSelectedSave).size / 1024
);
std::string saveinfoTxt = txt;
PPGeDrawText(saveinfoTxt.c_str(), 20, 180, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
}
}
void PSPSaveDialog::DisplayConfirmationYesNo(std::string text)
{
PPGeDrawText(text.c_str(), 200, 90, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
PPGeDrawText("Yes", 250, 150, PPGE_ALIGN_LEFT, 0.5f, (yesnoChoice == 1?0xFF0000FF:0xFFFFFFFF));
PPGeDrawText("No", 350, 150, PPGE_ALIGN_LEFT, 0.5f, (yesnoChoice == 0?0xFF0000FF:0xFFFFFFFF));
if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0)
{
yesnoChoice = 1;
}
else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1)
{
yesnoChoice = 0;
}
}
void PSPSaveDialog::DisplayInfo(std::string text)
{
PPGeDrawText(text.c_str(), 200, 90, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
}
void PSPSaveDialog::DisplayTitle(std::string name)
{
PPGeDrawText(name.c_str(), 10, 10, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
}
void PSPSaveDialog::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);
PPGeDrawText("Back", 320, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
}
void PSPSaveDialog::DisplayBack()
{
PPGeDrawImage(cancelButtonImg, 250, 220, 20, 20, 0, 0xFFFFFFFF);
PPGeDrawText("Back", 270, 220, PPGE_ALIGN_LEFT, 0.5f, 0xFFFFFFFF);
}
int PSPSaveDialog::Update()
{
switch (status) {
case SCE_UTILITY_STATUS_FINISHED:
status = SCE_UTILITY_STATUS_SHUTDOWN;
break;
default:
break;
}
if (status != SCE_UTILITY_STATUS_RUNNING)
{
return 0;
}
if (!param.GetPspParam()) {
status = SCE_UTILITY_STATUS_SHUTDOWN;
return 0;
}
buttons = __CtrlPeekButtons();
okButtonImg = I_CIRCLE;
cancelButtonImg = I_CROSS;
okButtonFlag = CTRL_CIRCLE;
cancelButtonFlag = CTRL_CROSS;
if(param.GetPspParam()->buttonSwap == 1)
{
okButtonImg = I_CROSS;
cancelButtonImg = I_CIRCLE;
okButtonFlag = CTRL_CROSS;
cancelButtonFlag = CTRL_CIRCLE;
}
switch(display)
{
case DS_SAVE_LIST_CHOICE:
StartDraw();
DisplayTitle("Save");
// TODO : use focus for selected save by default, and don't modify global selected save,use local var
DisplaySaveList();
DisplaySaveDataInfo1();
// TODO : Dialogs should take control over input and not send them to the game while displaying
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
}
else if (IsButtonPressed(okButtonFlag))
{
// Save exist, ask user confirm
if(param.GetFileInfo(currentSelectedSave).size > 0)
{
yesnoChoice = 0;
display = DS_SAVE_CONFIRM_OVERWRITE;
}
else
{
display = DS_SAVE_SAVING;
if(param.Save(param.GetPspParam(),currentSelectedSave))
{
param.SetPspParam(param.GetPspParam()); // Optim : Just Update modified save
display = DS_SAVE_DONE;
}
else
{
display = DS_SAVE_LIST_CHOICE; // This will probably need error message ?
}
}
}
EndDraw();
break;
case DS_SAVE_CONFIRM_OVERWRITE:
StartDraw();
DisplayTitle("Save");
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayConfirmationYesNo("Do you want to overwrite the data ?");
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag))
{
display = DS_SAVE_LIST_CHOICE;
}
else if (IsButtonPressed(okButtonFlag))
{
if(yesnoChoice == 0)
{
display = DS_SAVE_LIST_CHOICE;
}
else
{
display = DS_SAVE_SAVING;
if(param.Save(param.GetPspParam(),currentSelectedSave))
{
param.SetPspParam(param.GetPspParam()); // Optim : Just Update modified save
display = DS_SAVE_DONE;
}
else
{
display = DS_SAVE_LIST_CHOICE; // This will probably need error message ?
}
}
}
EndDraw();
break;
case DS_SAVE_SAVING:
StartDraw();
DisplayTitle("Save");
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayInfo("Saving\nPlease Wait...");
EndDraw();
break;
case DS_SAVE_DONE:
StartDraw();
DisplayTitle("Save");
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayBack();
DisplayInfo("Save completed");
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_SUCCESS;
// Set the save to use for autosave and autoload
param.SetSelectedSave(param.GetFileInfo(currentSelectedSave).idx);
}
EndDraw();
break;
case DS_LOAD_LIST_CHOICE:
StartDraw();
DisplayTitle("Load");
DisplaySaveList();
DisplaySaveDataInfo1();
// TODO : Dialogs should take control over input and not send them to the game while displaying
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
}
else if (IsButtonPressed(okButtonFlag))
{
display = DS_LOAD_LOADING;
if(param.Load(param.GetPspParam(),currentSelectedSave))
{
display = DS_LOAD_DONE;
}
}
EndDraw();
break;
case DS_LOAD_LOADING:
StartDraw();
DisplayTitle("Load");
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayInfo("Loading\nPlease Wait...");
EndDraw();
break;
case DS_LOAD_DONE:
StartDraw();
DisplayTitle("Load");
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayBack();
DisplayInfo("Load completed");
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_SUCCESS;
// Set the save to use for autosave and autoload
param.SetSelectedSave(param.GetFileInfo(currentSelectedSave).idx);
}
EndDraw();
break;
case DS_LOAD_NODATA:
StartDraw();
DisplayTitle("Load");
DisplayBack();
DisplayInfo("There is no data");
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA;
}
EndDraw();
break;
case DS_DELETE_LIST_CHOICE:
StartDraw();
DisplayTitle("Delete");
DisplaySaveList();
DisplaySaveDataInfo1();
// TODO : Dialogs should take control over input and not send them to the game while displaying
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
}
else if (IsButtonPressed(okButtonFlag))
{
yesnoChoice = 0;
display = DS_DELETE_CONFIRM;
}
EndDraw();
break;
case DS_DELETE_CONFIRM:
StartDraw();
DisplayTitle("Delete");
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayConfirmationYesNo("The data will be deleted.\nAre you sure you want to continue?");
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag))
{
display = DS_DELETE_LIST_CHOICE;
}
else if (IsButtonPressed(okButtonFlag))
{
if(yesnoChoice == 0)
{
display = DS_DELETE_LIST_CHOICE;
}
else
{
display = DS_DELETE_DELETING;
if(param.Delete(param.GetPspParam(),currentSelectedSave))
{
param.SetPspParam(param.GetPspParam()); // Optim : Just Update modified save
display = DS_DELETE_DONE;
}
else
{
display = DS_DELETE_LIST_CHOICE; // This will probably need error message ?
}
}
}
EndDraw();
break;
case DS_DELETE_DELETING:
StartDraw();
DisplayTitle("Delete");
DisplayInfo("Deleting\nPlease Wait...");
EndDraw();
break;
case DS_DELETE_DONE:
StartDraw();
DisplayTitle("Delete");
DisplayBack();
DisplayInfo("Delete completed");
if (IsButtonPressed(cancelButtonFlag))
{
if(param.GetFilenameCount() == 0)
display = DS_DELETE_NODATA;
else
display = DS_DELETE_LIST_CHOICE;
}
EndDraw();
break;
case DS_DELETE_NODATA:
StartDraw();
DisplayTitle("Delete");
DisplayBack();
DisplayInfo("There is no data");
if (IsButtonPressed(cancelButtonFlag))
{
status = SCE_UTILITY_STATUS_FINISHED;
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA;
}
EndDraw();
break;
case DS_NONE: // For action which display nothing
{
switch(param.GetPspParam()->mode)
{
case SCE_UTILITY_SAVEDATA_TYPE_LOAD: // Only load and exit
case SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD:
if(param.Load(param.GetPspParam(),param.GetSelectedSave()))
param.GetPspParam()->result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA;
status = SCE_UTILITY_STATUS_FINISHED;
break;
case SCE_UTILITY_SAVEDATA_TYPE_SAVE: // Only save and exit
case SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE:
if(param.Save(param.GetPspParam(),param.GetSelectedSave()))
param.GetPspParam()->result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE;
status = SCE_UTILITY_STATUS_FINISHED;
break;
case SCE_UTILITY_SAVEDATA_TYPE_SIZES:
if(param.GetSizes(param.GetPspParam()))
{
param.GetPspParam()->result = 0;
}
else
{
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA;
}
status = SCE_UTILITY_STATUS_FINISHED;
break;
case SCE_UTILITY_SAVEDATA_TYPE_LIST:
param.GetList(param.GetPspParam());
param.GetPspParam()->result = 0;
status = SCE_UTILITY_STATUS_FINISHED;
break;
case SCE_UTILITY_SAVEDATA_TYPE_FILES:
if(param.GetFilesList(param.GetPspParam()))
{
param.GetPspParam()->result = 0;
}
else
{
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
}
status = SCE_UTILITY_STATUS_FINISHED;
break;
case SCE_UTILITY_SAVEDATA_TYPE_SIZES22:
if(param.GetSizes22(param.GetPspParam()))
{
param.GetPspParam()->result = 0;
}
else
{
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
}
status = SCE_UTILITY_STATUS_FINISHED;
break;
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE:
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE:
if(param.Save(param.GetPspParam(),param.GetSelectedSave()))
param.GetPspParam()->result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
status = SCE_UTILITY_STATUS_FINISHED;
break;
case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE:
if(param.Load(param.GetPspParam(),param.GetSelectedSave()))
param.GetPspParam()->result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; // not sure if correct code
status = SCE_UTILITY_STATUS_FINISHED;
break;
default:
status = SCE_UTILITY_STATUS_FINISHED;
break;
}
}
break;
default:
status = SCE_UTILITY_STATUS_FINISHED;
break;
}
lastButtons = buttons;
if(status == SCE_UTILITY_STATUS_FINISHED)
{
Memory::Memcpy(requestAddr,&request,request.size);
}
return 0;
}
int PSPSaveDialog::Shutdown()
{
PSPDialog::Shutdown();
param.SetPspParam(0);
return 0;
}
void PSPSaveDialog::DoState(PointerWrap &p)
{
PSPDialog::DoState(p);
p.Do(display);
param.DoState(p);
p.Do(request);
// Just reset it.
bool hasParam = param.GetPspParam() != NULL;
p.Do(hasParam);
if (hasParam)
param.SetPspParam(&request);
p.Do(requestAddr);
p.Do(currentSelectedSave);
p.Do(yesnoChoice);
p.Do(okButtonImg);
p.Do(cancelButtonImg);
p.Do(okButtonFlag);
p.Do(cancelButtonFlag);
p.DoMarker("PSPSaveDialog");
}

119
Core/Dialog/PSPSaveDialog.h Normal file
View File

@ -0,0 +1,119 @@
// 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 "SavedataParam.h"
#define SCE_UTILITY_SAVEDATA_ERROR_TYPE (0x80110300)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_MS (0x80110301)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_EJECT_MS (0x80110302)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_ACCESS_ERROR (0x80110305)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_DATA_BROKEN (0x80110306)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA (0x80110307)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM (0x80110308)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_INTERNAL (0x8011030b)
#define SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA (0x80110327)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_NO_MS (0x80110381)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_EJECT_MS (0x80110382)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE (0x80110383)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_PROTECTED (0x80110384)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_ACCESS_ERROR (0x80110385)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_PARAM (0x80110388)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_NO_UMD (0x80110389)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_WRONG_UMD (0x8011038a)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_INTERNAL (0x8011038b)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_MS (0x80110341)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_EJECT_MS (0x80110342)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_MS_PROTECTED (0x80110344)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_ACCESS_ERROR (0x80110345)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA (0x80110347)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_PARAM (0x80110348)
#define SCE_UTILITY_SAVEDATA_ERROR_DELETE_INTERNAL (0x8011034b)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_MS (0x801103C1)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_EJECT_MS (0x801103C2)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_ACCESS_ERROR (0x801103C5)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA (0x801103C7)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_PARAM (0x801103C8)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_UMD (0x801103C9)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_WRONG_UMD (0x801103Ca)
#define SCE_UTILITY_SAVEDATA_ERROR_SIZES_INTERNAL (0x801103Cb)
class PSPSaveDialog: public PSPDialog {
public:
PSPSaveDialog();
virtual ~PSPSaveDialog();
virtual int Init(int paramAddr);
virtual int Update();
virtual int Shutdown();
virtual void DoState(PointerWrap &p);
private :
void DisplaySaveList(bool canMove = true);
void DisplaySaveIcon();
void DisplayTitle(std::string name);
void DisplayEnterBack();
void DisplayBack();
void DisplaySaveDataInfo1();
void DisplaySaveDataInfo2();
void DisplayConfirmationYesNo(std::string text);
void DisplayInfo(std::string text);
enum DisplayState
{
DS_NONE,
DS_SAVE_LIST_CHOICE,
DS_SAVE_CONFIRM_OVERWRITE,
DS_SAVE_SAVING,
DS_SAVE_DONE,
DS_LOAD_LIST_CHOICE,
DS_LOAD_LOADING,
DS_LOAD_DONE,
DS_LOAD_NODATA,
DS_DELETE_LIST_CHOICE,
DS_DELETE_CONFIRM,
DS_DELETE_DELETING,
DS_DELETE_DONE,
DS_DELETE_NODATA
};
DisplayState display;
SavedataParam param;
SceUtilitySavedataParam request;
int requestAddr;
int currentSelectedSave;
int yesnoChoice;
int okButtonImg;
int cancelButtonImg;
int okButtonFlag;
int cancelButtonFlag;
};

View File

@ -0,0 +1,841 @@
// 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 "SavedataParam.h"
#include "image/png_load.h"
#include "../HLE/sceKernelMemory.h"
#include "../ELF/ParamSFO.h"
#include "Core/HW/MemoryStick.h"
#include "PSPSaveDialog.h"
std::string icon0Name = "ICON0.PNG";
std::string icon1Name = "ICON1.PMF";
std::string pic1Name = "PIC1.PNG";
std::string snd0Name = "SND0.AT3";
std::string sfoName = "PARAM.SFO";
std::string savePath = "ms0:/PSP/SAVEDATA/";
namespace
{
int getSizeNormalized(int size)
{
int sizeCluster = (int)MemoryStick_SectorSize();
return ((int)((size + sizeCluster - 1) / sizeCluster)) * sizeCluster;
}
void SetStringFromSFO(ParamSFOData &sfoFile, const char *name, char *str, int strLength)
{
std::string value = sfoFile.GetValueString(name);
strncpy(str, value.c_str(), strLength - 1);
str[strLength - 1] = 0;
}
bool ReadPSPFile(std::string filename, u8 *data, s64 dataSize, s64 *readSize)
{
u32 handle = pspFileSystem.OpenFile(filename, FILEACCESS_READ);
if (handle == 0)
return false;
int result = pspFileSystem.ReadFile(handle, data, dataSize);
pspFileSystem.CloseFile(handle);
if(readSize)
*readSize = result;
return result != 0;
}
bool WritePSPFile(std::string filename, u8 *data, int dataSize)
{
u32 handle = pspFileSystem.OpenFile(filename, (FileAccess)(FILEACCESS_WRITE | FILEACCESS_CREATE));
if (handle == 0)
return false;
int result = pspFileSystem.WriteFile(handle, data, dataSize);
pspFileSystem.CloseFile(handle);
return result != 0;
}
struct EncryptFileInfo
{
int fileVersion;
u8 key[16];
int sdkVersion;
};
bool PSPMatch(std::string text, std::string regexp)
{
if(text.empty() && regexp.empty())
return true;
else if(regexp == "*")
return true;
else if(text.empty())
return false;
else if(regexp.empty())
return false;
else if(regexp == "?" && text.length() == 1)
return true;
else if(text == regexp)
return true;
else if(regexp.data()[0] == '*')
{
bool res = PSPMatch(text.substr(1),regexp.substr(1));
if(!res)
res = PSPMatch(text.substr(1),regexp);
return res;
}
else if(regexp.data()[0] == '?')
{
return PSPMatch(text.substr(1),regexp.substr(1));
}
else if(regexp.data()[0] == text.data()[0])
{
return PSPMatch(text.substr(1),regexp.substr(1));
}
return false;
}
}
SavedataParam::SavedataParam()
: pspParam(0)
, selectedSave(0)
, saveDataList(0)
, saveDataListCount(0)
, saveNameListDataCount(0)
{
}
void SavedataParam::Init()
{
if (!pspFileSystem.GetFileInfo(savePath).exists)
{
pspFileSystem.MkDir(savePath);
}
}
std::string SavedataParam::GetSaveDirName(SceUtilitySavedataParam* param, int saveId)
{
if (!param) {
return "";
}
std::string dirName = GetSaveName(param);
if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it
dirName = GetFilename(saveId);
return dirName;
}
std::string SavedataParam::GetSaveDir(SceUtilitySavedataParam* param, int saveId)
{
if (!param) {
return "";
}
std::string dirPath = GetGameName(param)+GetSaveName(param);
if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it
dirPath = std::string(GetGameName(param))+GetFilename(saveId);
return dirPath;
}
std::string SavedataParam::GetSaveFilePath(SceUtilitySavedataParam* param, int saveId)
{
if (!param) {
return "";
}
return savePath + GetSaveDir(param,saveId);
}
std::string SavedataParam::GetGameName(SceUtilitySavedataParam* param)
{
char gameName[14];
memcpy(gameName,param->gameName,13);
gameName[13] = 0;
return gameName;
}
std::string SavedataParam::GetSaveName(SceUtilitySavedataParam* param)
{
char saveName[21];
memcpy(saveName,param->saveName,20);
saveName[20] = 0;
if(strcmp(saveName,"<>") == 0)
return "";
return saveName;
}
std::string SavedataParam::GetFileName(SceUtilitySavedataParam* param)
{
char fileName[14];
memcpy(fileName,param->fileName,13);
fileName[13] = 0;
return fileName;
}
bool SavedataParam::Delete(SceUtilitySavedataParam* param, int saveId)
{
if (!param)
{
return false;
}
std::string dirPath = GetSaveFilePath(param,saveId);
if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it
{
if (saveDataList[saveId].size == 0) // don't delete no existing file
{
return false;
}
}
pspFileSystem.RmDir(dirPath);
return true;
}
bool SavedataParam::Save(SceUtilitySavedataParam* param, int saveId)
{
if (!param) {
return false;
}
std::string dirPath = GetSaveFilePath(param, saveId);
if (!pspFileSystem.GetFileInfo(dirPath).exists)
pspFileSystem.MkDir(dirPath);
if(param->dataBuf != 0) // Can launch save without save data in mode 13
{
std::string filePath = dirPath+"/"+GetFileName(param);
int saveSize = param->dataSize;
if(saveSize == 0 || saveSize > param->dataBufSize)
saveSize = param->dataBufSize; // fallback, should never use this
INFO_LOG(HLE,"Saving file with size %u in %s",saveSize,filePath.c_str());
u8 *data_ = (u8*)Memory::GetPointer(param->dataBuf);
// copy back save name in request
strncpy(param->saveName,GetSaveDirName(param, saveId).c_str(),20);
if (!WritePSPFile(filePath, data_, saveSize))
{
ERROR_LOG(HLE,"Error writing file %s",filePath.c_str());
return false;
}
}
// SAVE PARAM.SFO
ParamSFOData sfoFile;
std::string sfopath = dirPath+"/"+sfoName;
PSPFileInfo sfoInfo = pspFileSystem.GetFileInfo(sfopath);
if(sfoInfo.exists) // Read old sfo if exist
{
u8 *sfoData = new u8[(size_t)sfoInfo.size];
size_t sfoSize = (size_t)sfoInfo.size;
if(ReadPSPFile(sfopath,sfoData,sfoSize, NULL))
{
sfoFile.ReadSFO(sfoData,sfoSize);
delete[] sfoData;
}
}
// Update values
sfoFile.SetValue("TITLE",param->sfoParam.title,128);
sfoFile.SetValue("SAVEDATA_TITLE",param->sfoParam.savedataTitle,128);
sfoFile.SetValue("SAVEDATA_DETAIL",param->sfoParam.detail,1024);
sfoFile.SetValue("PARENTAL_LEVEL",param->sfoParam.parentalLevel,4);
sfoFile.SetValue("CATEGORY","MS",4);
sfoFile.SetValue("SAVEDATA_DIRECTORY",GetSaveDir(param,saveId),64);
// For each file, 13 bytes for filename, 16 bytes for file hash (0 in PPSSPP), 3 byte for padding
const int FILE_LIST_ITEM_SIZE = 13 + 16 + 3;
const int FILE_LIST_COUNT_MAX = 99;
const int FILE_LIST_TOTAL_SIZE = FILE_LIST_ITEM_SIZE * FILE_LIST_COUNT_MAX;
u32 tmpDataSize = 0;
u8* tmpDataOrig = sfoFile.GetValueData("SAVEDATA_FILE_LIST", &tmpDataSize);
u8* tmpData = new u8[FILE_LIST_TOTAL_SIZE];
if (tmpDataOrig != NULL)
memcpy(tmpData, tmpDataOrig, tmpDataSize > FILE_LIST_TOTAL_SIZE ? FILE_LIST_TOTAL_SIZE : tmpDataSize);
else
memset(tmpData, 0, FILE_LIST_TOTAL_SIZE);
if (param->dataBuf != 0)
{
char *fName = (char*)tmpData;
for(int i = 0; i < FILE_LIST_COUNT_MAX; i++)
{
if(fName[0] == 0)
break; // End of list
if(strncmp(fName,GetFileName(param).c_str(),20) == 0)
break; // File already in SFO
fName += FILE_LIST_ITEM_SIZE;
}
if (fName + 20 <= (char*)tmpData + FILE_LIST_TOTAL_SIZE)
snprintf(fName, 20, "%s",GetFileName(param).c_str());
}
sfoFile.SetValue("SAVEDATA_FILE_LIST", tmpData, FILE_LIST_TOTAL_SIZE, FILE_LIST_TOTAL_SIZE);
delete[] tmpData;
// No crypted save, so fill with 0
tmpData = new u8[128];
memset(tmpData, 0, 128);
sfoFile.SetValue("SAVEDATA_PARAMS", tmpData, 128, 128);
delete[] tmpData;
u8 *sfoData;
size_t sfoSize;
sfoFile.WriteSFO(&sfoData,&sfoSize);
WritePSPFile(sfopath, sfoData, sfoSize);
delete[] sfoData;
// SAVE ICON0
if (param->icon0FileData.buf)
{
u8* data_ = (u8*)Memory::GetPointer(param->icon0FileData.buf);
std::string icon0path = dirPath+"/"+icon0Name;
WritePSPFile(icon0path, data_, param->icon0FileData.bufSize);
}
// SAVE ICON1
if (param->icon1FileData.buf)
{
u8* data_ = (u8*)Memory::GetPointer(param->icon1FileData.buf);
std::string icon1path = dirPath+"/"+icon1Name;
WritePSPFile(icon1path, data_, param->icon1FileData.bufSize);
}
// SAVE PIC1
if (param->pic1FileData.buf)
{
u8* data_ = (u8*)Memory::GetPointer(param->pic1FileData.buf);
std::string pic1path = dirPath+"/"+pic1Name;
WritePSPFile(pic1path, data_, param->pic1FileData.bufSize);
}
// Save SND
if (param->snd0FileData.buf)
{
u8* data_ = (u8*)Memory::GetPointer(param->snd0FileData.buf);
std::string snd0path = dirPath+"/"+snd0Name;
WritePSPFile(snd0path, data_, param->snd0FileData.bufSize);
}
// Save Encryption Data
{
EncryptFileInfo encryptInfo;
int dataSize = sizeof(encryptInfo); // version + key + sdkVersion
memset(&encryptInfo,0,dataSize);
encryptInfo.fileVersion = 1;
encryptInfo.sdkVersion = sceKernelGetCompiledSdkVersion();
if(param->size > 1500)
memcpy(encryptInfo.key,param->key,16);
std::string encryptInfoPath = dirPath+"/"+"ENCRYPT_INFO.BIN";
WritePSPFile(encryptInfoPath, (u8*)&encryptInfo, dataSize);
}
return true;
}
bool SavedataParam::Load(SceUtilitySavedataParam *param, int saveId)
{
if (!param) {
return false;
}
u8 *data_ = (u8*)Memory::GetPointer(param->dataBuf);
std::string dirPath = GetSaveFilePath(param, saveId);
if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it
{
if (saveDataList[saveId].size == 0) // don't read no existing file
{
return false;
}
}
std::string filePath = dirPath+"/"+GetFileName(param);
s64 readSize;
INFO_LOG(HLE,"Loading file with size %u in %s",param->dataBufSize,filePath.c_str());
if (!ReadPSPFile(filePath, data_, param->dataBufSize, &readSize))
{
ERROR_LOG(HLE,"Error reading file %s",filePath.c_str());
return false;
}
param->dataSize = readSize;
// copy back save name in request
strncpy(param->saveName,GetSaveDirName(param, saveId).c_str(),20);
ParamSFOData sfoFile;
std::string sfopath = dirPath+"/"+sfoName;
PSPFileInfo sfoInfo = pspFileSystem.GetFileInfo(sfopath);
if(sfoInfo.exists) // Read sfo
{
u8 *sfoData = new u8[(size_t)sfoInfo.size];
size_t sfoSize = (size_t)sfoInfo.size;
if(ReadPSPFile(sfopath,sfoData,sfoSize, NULL))
{
sfoFile.ReadSFO(sfoData,sfoSize);
// copy back info in request
strncpy(param->sfoParam.title,sfoFile.GetValueString("TITLE").c_str(),128);
strncpy(param->sfoParam.savedataTitle,sfoFile.GetValueString("SAVEDATA_TITLE").c_str(),128);
strncpy(param->sfoParam.detail,sfoFile.GetValueString("SAVEDATA_DETAIL").c_str(),1024);
param->sfoParam.parentalLevel = sfoFile.GetValueInt("PARENTAL_LEVEL");
}
delete[] sfoData;
}
// Don't know what it is, but PSP always respond this and this unlock some game
param->bind = 1021;
return true;
}
std::string SavedataParam::GetSpaceText(int size)
{
char text[50];
if(size < 1024)
{
sprintf(text,"%d B",size);
return std::string(text);
}
size /= 1024;
if(size < 1024)
{
sprintf(text,"%d KB",size);
return std::string(text);
}
size /= 1024;
if(size < 1024)
{
sprintf(text,"%d MB",size);
return std::string(text);
}
size /= 1024;
sprintf(text,"%d GB",size);
return std::string(text);
}
bool SavedataParam::GetSizes(SceUtilitySavedataParam *param)
{
if (!param) {
return false;
}
bool ret = true;
if (Memory::IsValidAddress(param->msFree))
{
Memory::Write_U32((u32)MemoryStick_SectorSize(),param->msFree); // cluster Size
Memory::Write_U32((u32)(MemoryStick_FreeSpace() / MemoryStick_SectorSize()),param->msFree+4); // Free cluster
Memory::Write_U32((u32)(MemoryStick_FreeSpace() / 0x400),param->msFree+8); // Free space (in KB)
std::string spaceTxt = SavedataParam::GetSpaceText((int)MemoryStick_FreeSpace());
Memory::Memset(param->msFree+12,0,spaceTxt.size()+1);
Memory::Memcpy(param->msFree+12,spaceTxt.c_str(),spaceTxt.size()); // Text representing free space
}
if (Memory::IsValidAddress(param->msData))
{
std::string path = GetSaveFilePath(param,0);
PSPFileInfo finfo = pspFileSystem.GetFileInfo(path);
if(finfo.exists)
{
// TODO : fill correctly with the total save size
Memory::Write_U32(1,param->msData+36); //1
Memory::Write_U32(0x20,param->msData+40); // 0x20
Memory::Write_U8(0,param->msData+44); // "32 KB" // 8 u8
Memory::Write_U32(0x20,param->msData+52); // 0x20
Memory::Write_U8(0,param->msData+56); // "32 KB" // 8 u8
}
else
{
Memory::Write_U32(0,param->msData+36);
Memory::Write_U32(0,param->msData+40);
Memory::Write_U8(0,param->msData+44);
Memory::Write_U32(0,param->msData+52);
Memory::Write_U8(0,param->msData+56);
ret = false;
// this should return SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA
}
}
if (Memory::IsValidAddress(param->utilityData))
{
int total_size = 0;
total_size += getSizeNormalized(1); // SFO;
total_size += getSizeNormalized(param->dataSize); // Save Data
total_size += getSizeNormalized(param->icon0FileData.size);
total_size += getSizeNormalized(param->icon1FileData.size);
total_size += getSizeNormalized(param->pic1FileData.size);
total_size += getSizeNormalized(param->snd0FileData.size);
Memory::Write_U32(total_size / (u32)MemoryStick_SectorSize(),param->utilityData); // num cluster
Memory::Write_U32(total_size / 0x400,param->utilityData+4); // save size in KB
std::string spaceTxt = SavedataParam::GetSpaceText(total_size);
Memory::Memset(param->utilityData+8,0,spaceTxt.size()+1);
Memory::Memcpy(param->utilityData+8,spaceTxt.c_str(),spaceTxt.size()); // save size in text
Memory::Write_U32(total_size / 0x400,param->utilityData+16); // save size in KB
spaceTxt = SavedataParam::GetSpaceText(total_size);
Memory::Memset(param->utilityData+20,0,spaceTxt.size()+1);
Memory::Memcpy(param->utilityData+20,spaceTxt.c_str(),spaceTxt.size()); // save size in text
}
return ret;
}
bool SavedataParam::GetList(SceUtilitySavedataParam *param)
{
if (!param) {
return false;
}
if (Memory::IsValidAddress(param->idListAddr))
{
u32 outputBuffer = Memory::Read_U32(param->idListAddr + 8);
u32 maxFile = Memory::Read_U32(param->idListAddr + 0);
std::vector<PSPFileInfo> validDir;
std::vector<PSPFileInfo> allDir = pspFileSystem.GetDirListing(savePath);
if (Memory::IsValidAddress(outputBuffer))
{
std::string searchString = GetGameName(param)+GetSaveName(param);
for (size_t i = 0; i < allDir.size() && i < maxFile; i++)
{
std::string dirName = allDir[i].name;
if(PSPMatch(dirName, searchString))
{
validDir.push_back(allDir[i]);
}
}
for (size_t i = 0; i < validDir.size(); i++)
{
u32 baseAddr = outputBuffer + (i*72);
Memory::Write_U32(0x11FF,baseAddr + 0); // mode
Memory::Write_U64(0,baseAddr + 4); // TODO ctime
Memory::Write_U64(0,baseAddr + 12); // TODO unknow
Memory::Write_U64(0,baseAddr + 20); // TODO atime
Memory::Write_U64(0,baseAddr + 28); // TODO unknow
Memory::Write_U64(0,baseAddr + 36); // TODO mtime
Memory::Write_U64(0,baseAddr + 44); // TODO unknow
// folder name without gamename (max 20 u8)
std::string outName = validDir[i].name.substr(GetGameName(param).size());
Memory::Memset(baseAddr + 52,0,20);
Memory::Memcpy(baseAddr + 52, outName.c_str(), outName.size());
}
}
// Save num of folder found
Memory::Write_U32(validDir.size(),param->idListAddr+4);
}
return true;
}
bool SavedataParam::GetFilesList(SceUtilitySavedataParam *param)
{
if (!param)
{
return false;
}
u32 dataAddr = param->fileListAddr;
if (!Memory::IsValidAddress(dataAddr))
return false;
// TODO : Need to be checked against more game
u32 fileInfosAddr = Memory::Read_U32(dataAddr + 24);
//for Valkyria2, dataAddr+0 and dataAddr+12 has "5" for 5 files
int numFiles = Memory::Read_U32(dataAddr+12);
int foundFiles = 0;
for (int i = 0; i < numFiles; i++)
{
// for each file (80 bytes):
// u32 mode, u32 ??, u64 size, u64 ctime, u64 ??, u64 atime, u64 ???, u64 mtime, u64 ???
// u8[16] filename (or 13 + padding?)
u32 curFileInfoAddr = fileInfosAddr + i*80;
char fileName[16];
strncpy(fileName, Memory::GetCharPointer(curFileInfoAddr + 64),16);
std::string filePath = savePath + GetGameName(param) + GetSaveName(param) + "/" + fileName;
PSPFileInfo info = pspFileSystem.GetFileInfo(filePath);
if (info.exists)
{
Memory::Write_U32(0x21FF, curFileInfoAddr+0);
Memory::Write_U64(info.size, curFileInfoAddr+8);
Memory::Write_U64(0,curFileInfoAddr + 16); // TODO ctime
Memory::Write_U64(0,curFileInfoAddr + 24); // TODO unknow
Memory::Write_U64(0,curFileInfoAddr + 32); // TODO atime
Memory::Write_U64(0,curFileInfoAddr + 40); // TODO unknow
Memory::Write_U64(0,curFileInfoAddr + 48); // TODO mtime
Memory::Write_U64(0,curFileInfoAddr + 56); // TODO unknow
foundFiles++;
}
}
// TODO : verify if return true if at least 1 file found or only if all found
return foundFiles > 0;
}
bool SavedataParam::GetSizes22(SceUtilitySavedataParam *param)
{
if (!param)
{
return false;
}
// TODO code this
return false;
}
void SavedataParam::Clear()
{
if (saveDataList)
{
for (int i = 0; i < saveNameListDataCount; i++)
{
if (saveDataList[i].textureData != 0)
kernelMemory.Free(saveDataList[i].textureData);
saveDataList[i].textureData = 0;
}
delete[] saveDataList;
saveDataList = 0;
saveDataListCount = 0;
}
}
int SavedataParam::SetPspParam(SceUtilitySavedataParam *param)
{
pspParam = param;
if (!pspParam)
{
Clear();
return 0;
}
bool listEmptyFile = true;
if (param->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTLOAD ||
param->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE)
{
listEmptyFile = false;
}
char (*saveNameListData)[20];
bool hasMultipleFileName = false;
if (param->saveNameList != 0)
{
Clear();
saveNameListData = (char(*)[20])Memory::GetPointer(param->saveNameList);
// Get number of fileName in array
saveDataListCount = 0;
while(saveNameListData[saveDataListCount][0] != 0)
{
saveDataListCount++;
}
if(saveDataListCount > 0)
{
hasMultipleFileName = true;
saveDataList = new SaveFileInfo[saveDataListCount];
// get and stock file info for each file
int realCount = 0;
for (int i = 0; i < saveDataListCount; i++)
{
DEBUG_LOG(HLE,"Name : %s",saveNameListData[i]);
std::string fileDataPath = savePath+GetGameName(param)+saveNameListData[i]+"/"+param->fileName;
PSPFileInfo info = pspFileSystem.GetFileInfo(fileDataPath);
if (info.exists)
{
SetFileInfo(realCount, info, saveNameListData[i]);
DEBUG_LOG(HLE,"%s Exist",fileDataPath.c_str());
realCount++;
}
else
{
if (listEmptyFile)
{
saveDataList[realCount].size = 0;
saveDataList[realCount].saveName = saveNameListData[i];
saveDataList[realCount].idx = i;
saveDataList[realCount].textureData = 0;
DEBUG_LOG(HLE,"Don't Exist");
realCount++;
}
}
}
saveNameListDataCount = realCount;
}
}
if(!hasMultipleFileName) // Load info on only save
{
saveNameListData = 0;
Clear();
saveDataList = new SaveFileInfo[1];
saveDataListCount = 1;
// get and stock file info for each file
DEBUG_LOG(HLE,"Name : %s",GetSaveName(param).c_str());
std::string fileDataPath = savePath+GetGameName(param)+GetSaveName(param)+"/"+param->fileName;
PSPFileInfo info = pspFileSystem.GetFileInfo(fileDataPath);
if (info.exists)
{
SetFileInfo(0, info, GetSaveName(pspParam));
DEBUG_LOG(HLE,"%s Exist",fileDataPath.c_str());
saveNameListDataCount = 1;
}
else
{
if (listEmptyFile)
{
saveDataList[0].size = 0;
saveDataList[0].saveName = GetSaveName(param);
saveDataList[0].idx = 0;
saveDataList[0].textureData = 0;
DEBUG_LOG(HLE,"Don't Exist");
}
saveNameListDataCount = 0;
return 0;
}
}
return 0;
}
void SavedataParam::SetFileInfo(int idx, PSPFileInfo &info, std::string saveName)
{
saveDataList[idx].size = info.size;
saveDataList[idx].saveName = saveName;
saveDataList[idx].idx = 0;
saveDataList[idx].modif_time = info.mtime;
// Start with a blank slate.
saveDataList[idx].textureData = 0;
saveDataList[idx].title[0] = 0;
saveDataList[idx].saveTitle[0] = 0;
saveDataList[idx].saveDetail[0] = 0;
// Search save image icon0
// TODO : If icon0 don't exist, need to use icon1 which is a moving icon. Also play sound
std::string fileDataPath2 = savePath + GetGameName(pspParam) + saveName + "/" + icon0Name;
PSPFileInfo info2 = pspFileSystem.GetFileInfo(fileDataPath2);
if (info2.exists)
{
u8 *textureDataPNG = new u8[(size_t)info2.size];
ReadPSPFile(fileDataPath2, textureDataPNG, info2.size, NULL);
unsigned char *textureData;
int w,h;
int success = pngLoadPtr(textureDataPNG, (int)info2.size, &w, &h, &textureData, false);
delete[] textureDataPNG;
u32 texSize = w*h*4;
u32 atlasPtr;
if (success)
atlasPtr = kernelMemory.Alloc(texSize, true, "SaveData Icon");
if (success && atlasPtr > 0)
{
saveDataList[idx].textureData = atlasPtr;
Memory::Memcpy(atlasPtr, textureData, texSize);
free(textureData);
saveDataList[idx].textureWidth = w;
saveDataList[idx].textureHeight = h;
}
else
WARN_LOG(HLE, "Unable to load PNG data for savedata.");
}
// Load info in PARAM.SFO
fileDataPath2 = savePath + GetGameName(pspParam) + saveName + "/" + sfoName;
info2 = pspFileSystem.GetFileInfo(fileDataPath2);
if (info2.exists)
{
u8 *sfoParam = new u8[(size_t)info2.size];
ReadPSPFile(fileDataPath2, sfoParam, info2.size, NULL);
ParamSFOData sfoFile;
if (sfoFile.ReadSFO(sfoParam,(size_t)info2.size))
{
SetStringFromSFO(sfoFile, "TITLE", saveDataList[idx].title, sizeof(saveDataList[idx].title));
SetStringFromSFO(sfoFile, "SAVEDATA_TITLE", saveDataList[idx].saveTitle, sizeof(saveDataList[idx].saveTitle));
SetStringFromSFO(sfoFile, "SAVEDATA_DETAIL", saveDataList[idx].saveDetail, sizeof(saveDataList[idx].saveDetail));
}
delete [] sfoParam;
}
}
SceUtilitySavedataParam* SavedataParam::GetPspParam()
{
return pspParam;
}
int SavedataParam::GetFilenameCount()
{
return saveNameListDataCount;
}
const SaveFileInfo& SavedataParam::GetFileInfo(int idx)
{
return saveDataList[idx];
}
std::string SavedataParam::GetFilename(int idx)
{
return saveDataList[idx].saveName;
}
int SavedataParam::GetSelectedSave()
{
return selectedSave;
}
void SavedataParam::SetSelectedSave(int idx)
{
selectedSave = idx;
}
void SavedataParam::DoState(PointerWrap &p)
{
// pspParam is handled in PSPSaveDialog.
p.Do(selectedSave);
p.Do(saveDataListCount);
p.Do(saveNameListDataCount);
if (p.mode == p.MODE_READ)
{
if (saveDataList != NULL)
delete [] saveDataList;
saveDataList = new SaveFileInfo[saveDataListCount];
}
p.DoArray(saveDataList, saveDataListCount);
p.DoMarker("SavedataParam");
}

188
Core/Dialog/SavedataParam.h Normal file
View File

@ -0,0 +1,188 @@
// 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 "../HLE/sceKernel.h"
#include "../System.h"
enum SceUtilitySavedataType
{
SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD = 0,
SCE_UTILITY_SAVEDATA_TYPE_AUTOSAVE = 1,
SCE_UTILITY_SAVEDATA_TYPE_LOAD = 2,
SCE_UTILITY_SAVEDATA_TYPE_SAVE = 3,
SCE_UTILITY_SAVEDATA_TYPE_LISTLOAD = 4,
SCE_UTILITY_SAVEDATA_TYPE_LISTSAVE = 5,
SCE_UTILITY_SAVEDATA_TYPE_LISTDELETE = 6,
SCE_UTILITY_SAVEDATA_TYPE_DELETE = 7,
SCE_UTILITY_SAVEDATA_TYPE_SIZES = 8,
SCE_UTILITY_SAVEDATA_TYPE_LIST = 11,
SCE_UTILITY_SAVEDATA_TYPE_FILES = 12,
SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE = 13,
SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE = 15,
SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE = 17,
SCE_UTILITY_SAVEDATA_TYPE_SIZES22 = 22
} ;
// title, savedataTitle, detail: parts of the unencrypted SFO
// data, it contains what the VSH and standard load screen shows
struct PspUtilitySavedataSFOParam
{
char title[0x80];
char savedataTitle[0x80];
char detail[0x400];
unsigned char parentalLevel;
unsigned char unknown[3];
};
struct PspUtilitySavedataFileData {
int buf;
SceSize bufSize; // Size of the buffer pointed to by buf
SceSize size; // Actual file size to write / was read
int unknown;
};
// Structure to hold the parameters for the sceUtilitySavedataInitStart function.
struct SceUtilitySavedataParam
{
SceSize size; // Size of the structure
int language;
int buttonSwap;
int unknown[4];
int result;
int unknown2[4];
int mode; // 0 to load, 1 to save
int bind;
int overwriteMode; // use 0x10 ?
/** gameName: name used from the game for saves, equal for all saves */
char gameName[13];
char unused[3];
/** saveName: name of the particular save, normally a number */
char saveName[20];
int saveNameList;
/** fileName: name of the data file of the game for example DATA.BIN */
char fileName[13];
char unused2[3];
/** pointer to a buffer that will contain data file unencrypted data */
u32 dataBuf; // Initially void*, but void* in 64bit system take 8 bytes.
/** size of allocated space to dataBuf */
SceSize dataBufSize;
SceSize dataSize; // Size of the actual save data
PspUtilitySavedataSFOParam sfoParam;
PspUtilitySavedataFileData icon0FileData;
PspUtilitySavedataFileData icon1FileData;
PspUtilitySavedataFileData pic1FileData;
PspUtilitySavedataFileData snd0FileData;
int newData;
int focus;
int abortStatus;
// Function SCE_UTILITY_SAVEDATA_TYPE_SIZES
u32 msFree;
u32 msData;
u32 utilityData;
char key[16];
int secureVersion;
int multiStatus;
// Function 11 LIST
u32 idListAddr;
// Function 12 FILES
u32 fileListAddr;
// Function 22 GETSIZES
u32 sizeAddr;
};
// Non native, this one we can reorganize as we like
struct SaveFileInfo
{
s64 size;
std::string saveName;
int idx;
char title[128];
char saveTitle[128];
char saveDetail[1024];
tm modif_time;
u32 textureData;
int textureWidth;
int textureHeight;
};
class SavedataParam
{
public:
SavedataParam();
static void Init();
std::string GetSaveFilePath(SceUtilitySavedataParam* param, int saveId = -1);
std::string GetSaveDirName(SceUtilitySavedataParam* param, int saveId = -1);
std::string GetSaveDir(SceUtilitySavedataParam* param, int saveId = -1);
bool Delete(SceUtilitySavedataParam* param, int saveId = -1);
bool Save(SceUtilitySavedataParam* param, int saveId = -1);
bool Load(SceUtilitySavedataParam* param, int saveId = -1);
bool GetSizes(SceUtilitySavedataParam* param);
bool GetList(SceUtilitySavedataParam* param);
bool GetFilesList(SceUtilitySavedataParam* param);
bool GetSizes22(SceUtilitySavedataParam* param);
std::string GetGameName(SceUtilitySavedataParam* param);
std::string GetSaveName(SceUtilitySavedataParam* param);
std::string GetFileName(SceUtilitySavedataParam* param);
static std::string GetSpaceText(int size);
int SetPspParam(SceUtilitySavedataParam* param);
SceUtilitySavedataParam* GetPspParam();
int GetFilenameCount();
const SaveFileInfo& GetFileInfo(int idx);
std::string GetFilename(int idx);
int GetSelectedSave();
void SetSelectedSave(int idx);
void DoState(PointerWrap &p);
private:
void Clear();
void SetFileInfo(int idx, PSPFileInfo &info, std::string saveName);
SceUtilitySavedataParam* pspParam;
int selectedSave;
SaveFileInfo* saveDataList;
int saveDataListCount;
int saveNameListDataCount;
};

View File

@ -50,6 +50,118 @@ void addrToHiLo(u32 addr, u16 &hi, s16 &lo)
}
}
void ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs)
{
for (int r = 0; r < numRelocs; r++)
{
u32 info = rels[r].r_info;
u32 addr = rels[r].r_offset;
int type = info & 0xf;
int readwrite = (info>>8) & 0xff;
int relative = (info>>16) & 0xff;
//0 = code
//1 = data
addr += segmentVAddr[readwrite];
u32 op = Memory::ReadUnchecked_U32(addr);
const bool log = false;
//log=true;
if (log)
{
DEBUG_LOG(LOADER,"rel at: %08x type: %08x",addr,info);
}
u32 relocateTo = segmentVAddr[relative];
#define R_MIPS32 2
#define R_MIPS26 4
#define R_MIPS16_HI 5
#define R_MIPS16_LO 6
switch (type)
{
case R_MIPS32:
if (log)
DEBUG_LOG(LOADER,"Full address reloc %08x", addr);
//full address, no problemo
op += relocateTo;
break;
case R_MIPS26: //j, jal
//add on to put in correct address space
if (log)
DEBUG_LOG(LOADER,"j/jal reloc %08x", addr);
op = (op & 0xFC000000) | (((op&0x03FFFFFF)+(relocateTo>>2))&0x03FFFFFFF);
break;
case R_MIPS16_HI: //lui part of lui-addiu pairs
{
if (log)
DEBUG_LOG(LOADER,"HI reloc %08x", addr);
u32 cur = (op & 0xFFFF) << 16;
u16 hi = 0;
bool found = false;
for (int t = r + 1; t<numRelocs; t++)
{
if ((rels[t].r_info & 0xF) == R_MIPS16_LO)
{
u32 corrLoAddr = rels[t].r_offset + segmentVAddr[readwrite];
if (log)
{
DEBUG_LOG(LOADER,"Corresponding lo found at %08x", corrLoAddr);
}
s16 lo = (s32)(s16)(u16)(Memory::ReadUnchecked_U32(corrLoAddr) & 0xFFFF); //signed??
cur += lo;
cur += relocateTo;
addrToHiLo(cur, hi, lo);
found = true;
break;
}
}
if (!found)
ERROR_LOG(LOADER, "R_MIPS16: not found");
op = (op & 0xFFFF0000) | (hi);
}
break;
case R_MIPS16_LO: //addiu part of lui-addiu pairs
{
if (log)
DEBUG_LOG(LOADER,"LO reloc %08x", addr);
u32 cur = op & 0xFFFF;
cur += relocateTo;
cur &= 0xFFFF;
op = (op & 0xFFFF0000) | cur;
}
break;
case 7: //gp
if (log)
ERROR_LOG(LOADER,"ARGH IT'S A GP!!!!!!!! %08x", addr);
break;
case 0: // another GP reloc!
{
char temp[256];
MIPSDisAsm(op, 0, temp);
ERROR_LOG(LOADER,"WARNING: GP reloc? @ %08x : 0 : %s", addr, temp );
}
break;
default:
ERROR_LOG(LOADER,"ARGH IT'S A UNKNOWN RELOCATION!!!!!!!! %08x", addr);
break;
}
Memory::Write_U32(op, addr);
}
}
bool ElfReader::LoadInto(u32 loadAddress)
{
@ -89,10 +201,21 @@ bool ElfReader::LoadInto(u32 loadAddress)
}
}
u32 totalSize = totalEnd - totalStart;
if (loadAddress)
vaddr = userMemory.AllocAt(loadAddress, totalSize, "ELF");
if (!bRelocate)
{
// Binary is prerelocated, load it where the first segment starts
vaddr = userMemory.AllocAt(totalStart, totalSize, "ELF");
}
else if (loadAddress)
{
// Binary needs to be relocated: add loadAddress to the binary start address
vaddr = userMemory.AllocAt(loadAddress + totalStart, totalSize, "ELF");
}
else
{
// Just put it where there is room
vaddr = userMemory.Alloc(totalSize, false, "ELF");
}
if (vaddr == -1) {
ERROR_LOG(LOADER, "Failed to allocate memory for ELF!");
@ -109,9 +232,8 @@ bool ElfReader::LoadInto(u32 loadAddress)
DEBUG_LOG(LOADER,"%i segments:", header->e_phnum);
// First pass : Get the damn bits into RAM
u32 segmentVAddr[32];
u32 baseAddress = bRelocate?vaddr:0;
for (int i=0; i<header->e_phnum; i++)
{
Elf32_Phdr *p = segments + i;
@ -170,7 +292,6 @@ bool ElfReader::LoadInto(u32 loadAddress)
if (s->sh_type == SHT_PSPREL)
{
//We have a relocation table!
int symbolSection = s->sh_link;
int sectionToModify = s->sh_info;
if (!(sections[sectionToModify].sh_flags & SHF_ALLOC))
@ -184,128 +305,18 @@ bool ElfReader::LoadInto(u32 loadAddress)
Elf32_Rel *rels = (Elf32_Rel *)GetSectionDataPtr(i);
DEBUG_LOG(LOADER,"%s: Performing %i relocations on %s",name,numRelocs,GetSectionName(sectionToModify));
for (int r = 0; r < numRelocs; r++)
{
u32 info = rels[r].r_info;
u32 addr = rels[r].r_offset;
int type = info & 0xf;
int readwrite = (info>>8) & 0xff;
int relative = (info>>16) & 0xff;
//0 = code
//1 = data
addr += segmentVAddr[readwrite];
u32 op = Memory::ReadUnchecked_U32(addr);
const bool log = false;
//log=true;
if (log)
{
DEBUG_LOG(LOADER,"rel at: %08x type: %08x",addr,info);
}
u32 relocateTo = segmentVAddr[relative];
#define R_MIPS32 2
#define R_MIPS26 4
#define R_MIPS16_HI 5
#define R_MIPS16_LO 6
switch (type)
{
case R_MIPS32:
if (log)
DEBUG_LOG(LOADER,"Full address reloc %08x", addr);
//full address, no problemo
op += relocateTo;
break;
case R_MIPS26: //j, jal
//add on to put in correct address space
if (log)
DEBUG_LOG(LOADER,"j/jal reloc %08x", addr);
op = (op & 0xFC000000) | (((op&0x03FFFFFF)+(relocateTo>>2))&0x03FFFFFFF);
break;
case R_MIPS16_HI: //lui part of lui-addiu pairs
{
if (log)
DEBUG_LOG(LOADER,"HI reloc %08x", addr);
u32 cur = (op & 0xFFFF) << 16;
u16 hi = 0;
bool found = false;
for (int t = r + 1; t<numRelocs; t++)
{
if ((rels[t].r_info & 0xF) == R_MIPS16_LO)
{
u32 corrLoAddr = rels[t].r_offset + segmentVAddr[readwrite];
if (log)
{
DEBUG_LOG(LOADER,"Corresponding lo found at %08x", corrLoAddr);
}
s16 lo = (s32)(s16)(u16)(Memory::ReadUnchecked_U32(corrLoAddr) & 0xFFFF); //signed??
cur += lo;
cur += relocateTo;
addrToHiLo(cur, hi, lo);
found = true;
break;
}
}
if (!found)
ERROR_LOG(LOADER, "R_MIPS16: not found");
op = (op & 0xFFFF0000) | (hi);
}
break;
case R_MIPS16_LO: //addiu part of lui-addiu pairs
{
if (log)
DEBUG_LOG(LOADER,"LO reloc %08x", addr);
u32 cur = op & 0xFFFF;
cur += relocateTo;
cur &= 0xFFFF;
op = (op & 0xFFFF0000) | cur;
}
break;
case 7: //gp
if (log)
ERROR_LOG(LOADER,"ARGH IT'S A GP!!!!!!!! %08x", addr);
break;
case 0: // another GP reloc!
{
char temp[256];
MIPSDisAsm(op, 0, temp);
ERROR_LOG(LOADER,"WARNING: GP reloc? @ %08x : 0 : %s", addr, temp );
}
break;
default:
ERROR_LOG(LOADER,"ARGH IT'S A UNKNOWN RELOCATION!!!!!!!! %08x", addr);
break;
}
Memory::Write_U32(op, addr);
}
LoadRelocations(rels, numRelocs);
}
else if (s->sh_type == SHT_REL)
{
DEBUG_LOG(LOADER, "Traditional relocation section found.");
if (bRelocate)
if (!bRelocate)
{
DEBUG_LOG(LOADER, "Binary is prerelocated. Skipping relocations.");
}
else
{
//We have a relocation table!
int symbolSection = s->sh_link;
int sectionToModify = s->sh_info;
if (!(sections[sectionToModify].sh_flags & SHF_ALLOC))
{
@ -317,6 +328,24 @@ bool ElfReader::LoadInto(u32 loadAddress)
}
}
// Segment relocations (a few games use them)
if (GetNumSections() == 0)
{
for (int i=0; i<header->e_phnum; i++)
{
Elf32_Phdr *p = &segments[i];
if (p->p_type == 0x700000A0)
{
INFO_LOG(LOADER,"Loading segment relocations");
int numRelocs = p->p_filesz / sizeof(Elf32_Rel);
Elf32_Rel *rels = (Elf32_Rel *)GetSegmentPtr(i);
LoadRelocations(rels, numRelocs);
}
}
}
NOTICE_LOG(LOADER,"ELF loading completed successfully.");
return true;
}

View File

@ -42,6 +42,7 @@ class ElfReader
bool bRelocate;
u32 entryPoint;
u32 vaddr;
u32 segmentVAddr[32];
public:
ElfReader(void *ptr)
{
@ -104,6 +105,10 @@ public:
{
return segments[segment].p_offset;
}
u32 GetSegmentVaddr(int segment)
{
return segmentVAddr[segment];
}
bool DidRelocate() {
return bRelocate;
@ -117,4 +122,5 @@ public:
// More indepth stuff:)
bool LoadInto(u32 vaddr);
bool LoadSymbols();
void LoadRelocations(Elf32_Rel *rels, int numRelocs);
};

View File

@ -38,22 +38,54 @@ struct IndexTable
u32 data_table_offset; /* Offset of the param_data from start of data_table */
};
void ParseDataString(const char *key, const char *utfdata, ParamSFOData *sfodata, int maxlen = 0)
void ParamSFOData::SetValue(std::string key, unsigned int value, int max_size)
{
std::string data;
if (maxlen)
data = std::string(utfdata, maxlen);
else
data = std::string(utfdata);
if (!strcmp(key, "DISC_ID")) {
sfodata->discID = data;
} else if (!strcmp(key, "TITLE")) {
sfodata->title = data;
values[key].type = VT_INT;
values[key].i_value = value;
values[key].max_size = max_size;
}
void ParamSFOData::SetValue(std::string key, std::string value, int max_size)
{
values[key].type = VT_UTF8;
values[key].s_value = value;
values[key].max_size = max_size;
}
void ParamSFOData::SetValue(std::string key, const u8* value, unsigned int size, int max_size)
{
values[key].type = VT_UTF8_SPE;
values[key].SetData(value,size);
values[key].max_size = max_size;
}
int ParamSFOData::GetValueInt(std::string key)
{
std::map<std::string,ValueData>::iterator it = values.find(key);
if(it == values.end() || it->second.type != VT_INT)
return 0;
return it->second.i_value;
}
std::string ParamSFOData::GetValueString(std::string key)
{
std::map<std::string,ValueData>::iterator it = values.find(key);
if(it == values.end() || (it->second.type != VT_UTF8))
return "";
return it->second.s_value;
}
u8* ParamSFOData::GetValueData(std::string key, unsigned int *size)
{
std::map<std::string,ValueData>::iterator it = values.find(key);
if(it == values.end() || (it->second.type != VT_UTF8_SPE))
return 0;
if(size)
{
*size = it->second.u_size;
}
return it->second.u_value;
}
// I'm so sorry Ced but this is highly endian unsafe :(
bool ParseParamSFO(const u8 *paramsfo, size_t size, ParamSFOData *data)
bool ParamSFOData::ReadSFO(const u8 *paramsfo, size_t size)
{
const Header *header = (const Header *)paramsfo;
if (header->magic != 0x46535000)
@ -75,15 +107,16 @@ bool ParseParamSFO(const u8 *paramsfo, size_t size, ParamSFOData *data)
{
// Unsigned int
const u32 *data = (const u32 *)(data_start + indexTables[i].data_table_offset);
SetValue(key,*data,indexTables[i].param_max_len);
DEBUG_LOG(LOADER, "%s %08x", key, *data);
}
break;
case 0x0004:
// Special format UTF-8
{
const char *utfdata = (const char *)(data_start + indexTables[i].data_table_offset);
const u8 *utfdata = (const u8 *)(data_start + indexTables[i].data_table_offset);
DEBUG_LOG(LOADER, "%s %s", key, utfdata);
ParseDataString(key, utfdata, data, indexTables[i].param_len);
SetValue(key, utfdata, indexTables[i].param_len, indexTables[i].param_max_len);
}
break;
case 0x0204:
@ -91,7 +124,7 @@ bool ParseParamSFO(const u8 *paramsfo, size_t size, ParamSFOData *data)
{
const char *utfdata = (const char *)(data_start + indexTables[i].data_table_offset);
DEBUG_LOG(LOADER, "%s %s", key, utfdata);
ParseDataString(key, utfdata, data, indexTables[i].param_len);
SetValue(key,std::string(utfdata,indexTables[i].param_len),indexTables[i].param_max_len);
}
break;
}
@ -99,3 +132,92 @@ bool ParseParamSFO(const u8 *paramsfo, size_t size, ParamSFOData *data)
return true;
}
bool ParamSFOData::WriteSFO(u8 **paramsfo, size_t *size)
{
size_t total_size = 0;
size_t key_size = 0;
size_t data_size = 0;
Header header;
header.magic = 0x46535000;
header.version = 0x00000101;
header.index_table_entries = 0;
total_size += sizeof(Header);
// Get size info
for (std::map<std::string,ValueData>::iterator it = values.begin(); it != values.end(); it++)
{
key_size += it->first.size()+1;
data_size += it->second.max_size;
header.index_table_entries++;
}
// Padding
while((key_size%4)) key_size++;
header.key_table_start = sizeof(Header) + header.index_table_entries * sizeof(IndexTable);
header.data_table_start = header.key_table_start + key_size;
total_size += sizeof(IndexTable) * header.index_table_entries;
total_size += key_size;
total_size += data_size;
*size = total_size;
u8* data = new u8[total_size];
*paramsfo = data;
memset(data, 0, total_size);
memcpy(data, &header, sizeof(Header));
// Now fill
IndexTable *index_ptr = (IndexTable*)(data + sizeof(Header));
u8* key_ptr = data + header.key_table_start;
u8* data_ptr = data + header.data_table_start;
for (std::map<std::string,ValueData>::iterator it = values.begin(); it != values.end(); it++)
{
u16 offset = (u16)(key_ptr - (data+header.key_table_start));
index_ptr->key_table_offset = offset;
offset = (u16)(data_ptr - (data+header.data_table_start));
index_ptr->data_table_offset = offset;
index_ptr->param_max_len = it->second.max_size;
if (it->second.type == VT_INT)
{
index_ptr->param_fmt = 0x0404;
index_ptr->param_len = 4;
*(int*)data_ptr = it->second.i_value;
}
else if (it->second.type == VT_UTF8_SPE)
{
index_ptr->param_fmt = 0x0004;
index_ptr->param_len = it->second.u_size;
memset(data_ptr,0,index_ptr->param_max_len);
memcpy(data_ptr,it->second.u_value,index_ptr->param_len);
}
else if (it->second.type == VT_UTF8)
{
index_ptr->param_fmt = 0x0204;
index_ptr->param_len = it->second.s_value.size()+1;
memcpy(data_ptr,it->second.s_value.c_str(),index_ptr->param_len);
data_ptr[index_ptr->param_len] = 0;
}
memcpy(key_ptr,it->first.c_str(),it->first.size());
key_ptr[it->first.size()] = 0;
data_ptr += index_ptr->param_max_len;
key_ptr += it->first.size()+1;
index_ptr++;
}
return true;
}

View File

@ -18,11 +18,72 @@
#pragma once
#include <map>
struct ParamSFOData
class ParamSFOData
{
std::string discID;
std::string title; // utf-8
public:
void SetValue(std::string key, unsigned int value, int max_size);
void SetValue(std::string key, std::string value, int max_size);
void SetValue(std::string key, const u8* value, unsigned int size, int max_size);
int GetValueInt(std::string key);
std::string GetValueString(std::string key);
u8* GetValueData(std::string key, unsigned int *size);
bool ReadSFO(const u8 *paramsfo, size_t size);
bool WriteSFO(u8 **paramsfo, size_t *size);
private:
enum ValueType
{
VT_INT,
VT_UTF8,
VT_UTF8_SPE // raw data in u8
};
class ValueData
{
public:
ValueType type;
int max_size;
std::string s_value;
int i_value;
u8* u_value;
unsigned int u_size;
void SetData(const u8* data, int size)
{
if(u_value)
{
delete[] u_value;
u_value = 0;
}
if(size > 0)
{
u_value = new u8[size];
memcpy(u_value, data, size);
}
u_size = size;
}
ValueData()
{
u_value = 0;
u_size = 0;
type = VT_INT;
max_size = 0;
i_value = 0;
}
~ValueData()
{
if(u_value)
delete[] u_value;
}
};
std::map<std::string,ValueData> values;
};
bool ParseParamSFO(const u8 *paramsfo, size_t size, ParamSFOData *data);

View File

@ -6,252 +6,255 @@ extern "C"
}
#include "../../Globals.h"
#include "PrxDecrypter.h"
#define ROUNDUP16(x) (((x)+15)&~15)
// Thank you PSARDUMPER & JPCSP keys
// PRXDecrypter 16-byte tag keys.
u8 keys260_0[] = {0xC3, 0x24, 0x89, 0xD3, 0x80, 0x87, 0xB2, 0x4E, 0x4C, 0xD7, 0x49, 0xE4, 0x9D, 0x1D, 0x34, 0xD1};
u8 keys260_1[] = {0xF3, 0xAC, 0x6E, 0x7C, 0x04, 0x0A, 0x23, 0xE7, 0x0D, 0x33, 0xD8, 0x24, 0x73, 0x39, 0x2B, 0x4A};
u8 keys260_2[] = {0x72, 0xB4, 0x39, 0xFF, 0x34, 0x9B, 0xAE, 0x82, 0x30, 0x34, 0x4A, 0x1D, 0xA2, 0xD8, 0xB4, 0x3C};
u8 keys280_0[] = {0xCA, 0xFB, 0xBF, 0xC7, 0x50, 0xEA, 0xB4, 0x40, 0x8E, 0x44, 0x5C, 0x63, 0x53, 0xCE, 0x80, 0xB1};
u8 keys280_1[] = {0x40, 0x9B, 0xC6, 0x9B, 0xA9, 0xFB, 0x84, 0x7F, 0x72, 0x21, 0xD2, 0x36, 0x96, 0x55, 0x09, 0x74};
u8 keys280_2[] = {0x03, 0xA7, 0xCC, 0x4A, 0x5B, 0x91, 0xC2, 0x07, 0xFF, 0xFC, 0x26, 0x25, 0x1E, 0x42, 0x4B, 0xB5};
u8 keys300_0[] = {0x9F, 0x67, 0x1A, 0x7A, 0x22, 0xF3, 0x59, 0x0B, 0xAA, 0x6D, 0xA4, 0xC6, 0x8B, 0xD0, 0x03, 0x77};
u8 keys300_1[] = {0x15, 0x07, 0x63, 0x26, 0xDB, 0xE2, 0x69, 0x34, 0x56, 0x08, 0x2A, 0x93, 0x4E, 0x4B, 0x8A, 0xB2};
u8 keys300_2[] = {0x56, 0x3B, 0x69, 0xF7, 0x29, 0x88, 0x2F, 0x4C, 0xDB, 0xD5, 0xDE, 0x80, 0xC6, 0x5C, 0xC8, 0x73};
u8 keys303_0[] = {0x7b, 0xa1, 0xe2, 0x5a, 0x91, 0xb9, 0xd3, 0x13, 0x77, 0x65, 0x4a, 0xb7, 0xc2, 0x8a, 0x10, 0xaf};
u8 keys310_0[] = {0xa2, 0x41, 0xe8, 0x39, 0x66, 0x5b, 0xfa, 0xbb, 0x1b, 0x2d, 0x6e, 0x0e, 0x33, 0xe5, 0xd7, 0x3f};
u8 keys310_1[] = {0xA4, 0x60, 0x8F, 0xAB, 0xAB, 0xDE, 0xA5, 0x65, 0x5D, 0x43, 0x3A, 0xD1, 0x5E, 0xC3, 0xFF, 0xEA};
u8 keys310_2[] = {0xE7, 0x5C, 0x85, 0x7A, 0x59, 0xB4, 0xE3, 0x1D, 0xD0, 0x9E, 0xCE, 0xC2, 0xD6, 0xD4, 0xBD, 0x2B};
u8 keys310_3[] = {0x2E, 0x00, 0xF6, 0xF7, 0x52, 0xCF, 0x95, 0x5A, 0xA1, 0x26, 0xB4, 0x84, 0x9B, 0x58, 0x76, 0x2F};
u8 keys330_0[] = {0x3B, 0x9B, 0x1A, 0x56, 0x21, 0x80, 0x14, 0xED, 0x8E, 0x8B, 0x08, 0x42, 0xFA, 0x2C, 0xDC, 0x3A};
u8 keys330_1[] = {0xE8, 0xBE, 0x2F, 0x06, 0xB1, 0x05, 0x2A, 0xB9, 0x18, 0x18, 0x03, 0xE3, 0xEB, 0x64, 0x7D, 0x26};
u8 keys330_2[] = {0xAB, 0x82, 0x25, 0xD7, 0x43, 0x6F, 0x6C, 0xC1, 0x95, 0xC5, 0xF7, 0xF0, 0x63, 0x73, 0x3F, 0xE7};
u8 keys330_3[] = {0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F, 0x38, 0x4C, 0x4D, 0x96, 0xBD, 0x49, 0xEC, 0x9B};
u8 keys330_4[] = {0xEC, 0x3B, 0xD2, 0xC0, 0xFA, 0xC1, 0xEE, 0xB9, 0x9A, 0xBC, 0xFF, 0xA3, 0x89, 0xF2, 0x60, 0x1F};
u8 keys360_0[] = {0x3C, 0x2B, 0x51, 0xD4, 0x2D, 0x85, 0x47, 0xDA, 0x2D, 0xCA, 0x18, 0xDF, 0xFE, 0x54, 0x09, 0xED};
u8 keys360_1[] = {0x31, 0x1F, 0x98, 0xD5, 0x7B, 0x58, 0x95, 0x45, 0x32, 0xAB, 0x3A, 0xE3, 0x89, 0x32, 0x4B, 0x34};
u8 keys370_0[] = {0x26, 0x38, 0x0A, 0xAC, 0xA5, 0xD8, 0x74, 0xD1, 0x32, 0xB7, 0x2A, 0xBF, 0x79, 0x9E, 0x6D, 0xDB};
u8 keys370_1[] = {0x53, 0xE7, 0xAB, 0xB9, 0xC6, 0x4A, 0x4B, 0x77, 0x92, 0x17, 0xB5, 0x74, 0x0A, 0xDA, 0xA9, 0xEA};
u8 keys370_2[] = {0x71, 0x10, 0xF0, 0xA4, 0x16, 0x14, 0xD5, 0x93, 0x12, 0xFF, 0x74, 0x96, 0xDF, 0x1F, 0xDA, 0x89};
u8 keys390_0[] = {0x45, 0xEF, 0x5C, 0x5D, 0xED, 0x81, 0x99, 0x84, 0x12, 0x94, 0x8F, 0xAB, 0xE8, 0x05, 0x6D, 0x7D};
u8 keys390_1[] = {0x70, 0x1B, 0x08, 0x25, 0x22, 0xA1, 0x4D, 0x3B, 0x69, 0x21, 0xF9, 0x71, 0x0A, 0xA8, 0x41, 0xA9};
u8 keys500_0[] = {0xEB, 0x1B, 0x53, 0x0B, 0x62, 0x49, 0x32, 0x58, 0x1F, 0x83, 0x0A, 0xF4, 0x99, 0x3D, 0x75, 0xD0};
u8 keys500_1[] = {0xBA, 0xE2, 0xA3, 0x12, 0x07, 0xFF, 0x04, 0x1B, 0x64, 0xA5, 0x11, 0x85, 0xF7, 0x2F, 0x99, 0x5B};
u8 keys500_2[] = {0x2C, 0x8E, 0xAF, 0x1D, 0xFF, 0x79, 0x73, 0x1A, 0xAD, 0x96, 0xAB, 0x09, 0xEA, 0x35, 0x59, 0x8B};
u8 keys500_c[] = {0xA3, 0x5D, 0x51, 0xE6, 0x56, 0xC8, 0x01, 0xCA, 0xE3, 0x77, 0xBF, 0xCD, 0xFF, 0x24, 0xDA, 0x4D};
u8 keys505_a[] = {0x7B, 0x94, 0x72, 0x27, 0x4C, 0xCC, 0x54, 0x3B, 0xAE, 0xDF, 0x46, 0x37, 0xAC, 0x01, 0x4D, 0x87};
u8 keys505_0[] = {0x2E, 0x8E, 0x97, 0xA2, 0x85, 0x42, 0x70, 0x73, 0x18, 0xDA, 0xA0, 0x8A, 0xF8, 0x62, 0xA2, 0xB0};
u8 keys505_1[] = {0x58, 0x2A, 0x4C, 0x69, 0x19, 0x7B, 0x83, 0x3D, 0xD2, 0x61, 0x61, 0xFE, 0x14, 0xEE, 0xAA, 0x11};
u8 keys570_5k[] = {0x6D, 0x72, 0xA4, 0xBA, 0x7F, 0xBF, 0xD1, 0xF1, 0xA9, 0xF3, 0xBB, 0x07, 0x1B, 0xC0, 0xB3, 0x66};
u8 keys600_1[] = {0xE3, 0x52, 0x39, 0x97, 0x3B, 0x84, 0x41, 0x1C, 0xC3, 0x23, 0xF1, 0xB8, 0xA9, 0x09, 0x4B, 0xF0};
u8 keys600_2[] = {0xE1, 0x45, 0x93, 0x2C, 0x53, 0xE2, 0xAB, 0x06, 0x6F, 0xB6, 0x8F, 0x0B, 0x66, 0x91, 0xE7, 0x1E};
u8 keys620_0[] = {0xD6, 0xBD, 0xCE, 0x1E, 0x12, 0xAF, 0x9A, 0xE6, 0x69, 0x30, 0xDE, 0xDA, 0x88, 0xB8, 0xFF, 0xFB};
u8 keys620_1[] = {0x1D, 0x13, 0xE9, 0x50, 0x04, 0x73, 0x3D, 0xD2, 0xE1, 0xDA, 0xB9, 0xC1, 0xE6, 0x7B, 0x25, 0xA7};
u8 keys620_a[] = {0xAC, 0x34, 0xBA, 0xB1, 0x97, 0x8D, 0xAE, 0x6F, 0xBA, 0xE8, 0xB1, 0xD6, 0xDF, 0xDF, 0xF1, 0xA2};
u8 keys620_e[] = {0xB1, 0xB3, 0x7F, 0x76, 0xC3, 0xFB, 0x88, 0xE6, 0xF8, 0x60, 0xD3, 0x35, 0x3C, 0xA3, 0x4E, 0xF3};
u8 keys620_5[] = {0xF1, 0xBC, 0x17, 0x07, 0xAE, 0xB7, 0xC8, 0x30, 0xD8, 0x34, 0x9D, 0x40, 0x6A, 0x8E, 0xDF, 0x4E};
u8 keys620_5k[] = {0x41, 0x8A, 0x35, 0x4F, 0x69, 0x3A, 0xDF, 0x04, 0xFD, 0x39, 0x46, 0xA2, 0x5C, 0x2D, 0xF2, 0x21};
u8 keys620_5v[] = {0xF2, 0x8F, 0x75, 0xA7, 0x31, 0x91, 0xCE, 0x9E, 0x75, 0xBD, 0x27, 0x26, 0xB4, 0xB4, 0x0C, 0x32};
u8 keys630_k1[] = {0x36, 0xB0, 0xDC, 0xFC, 0x59, 0x2A, 0x95, 0x1D, 0x80, 0x2D, 0x80, 0x3F, 0xCD, 0x30, 0xA0, 0x1B};
u8 keys630_k2[] = {0xd4, 0x35, 0x18, 0x02, 0x29, 0x68, 0xfb, 0xa0, 0x6a, 0xa9, 0xa5, 0xed, 0x78, 0xfd, 0x2e, 0x9d};
u8 keys630_k3[] = {0x23, 0x8D, 0x3D, 0xAE, 0x41, 0x50, 0xA0, 0xFA, 0xF3, 0x2F, 0x32, 0xCE, 0xC7, 0x27, 0xCD, 0x50};
u8 keys630_k4[] = {0xAA, 0xA1, 0xB5, 0x7C, 0x93, 0x5A, 0x95, 0xBD, 0xEF, 0x69, 0x16, 0xFC, 0x2B, 0x92, 0x31, 0xDD};
u8 keys630_k5[] = {0x87, 0x37, 0x21, 0xCC, 0x65, 0xAE, 0xAA, 0x5F, 0x40, 0xF6, 0x6F, 0x2A, 0x86, 0xC7, 0xA1, 0xC8};
u8 keys630_k6[] = {0x8D, 0xDB, 0xDC, 0x5C, 0xF2, 0x70, 0x2B, 0x40, 0xB2, 0x3D, 0x00, 0x09, 0x61, 0x7C, 0x10, 0x60};
u8 keys630_k7[] = {0x77, 0x1C, 0x06, 0x5F, 0x53, 0xEC, 0x3F, 0xFC, 0x22, 0xCE, 0x5A, 0x27, 0xFF, 0x78, 0xA8, 0x48};
u8 keys630_k8[] = {0x81, 0xD1, 0x12, 0x89, 0x35, 0xC8, 0xEA, 0x8B, 0xE0, 0x02, 0x2D, 0x2D, 0x6A, 0x18, 0x67, 0xB8};
u8 keys636_k1[] = {0x07, 0xE3, 0x08, 0x64, 0x7F, 0x60, 0xA3, 0x36, 0x6A, 0x76, 0x21, 0x44, 0xC9, 0xD7, 0x06, 0x83};
u8 keys636_k2[] = {0x91, 0xF2, 0x02, 0x9E, 0x63, 0x32, 0x30, 0xA9, 0x1D, 0xDA, 0x0B, 0xA8, 0xB7, 0x41, 0xA3, 0xCC};
u8 keys638_k4[] = {0x98, 0x43, 0xFF, 0x85, 0x68, 0xB2, 0xDB, 0x3B, 0xD4, 0x22, 0xD0, 0x4F, 0xAB, 0x5F, 0x0A, 0x31};
u8 keys639_k3[] = {0x01, 0x7B, 0xF0, 0xE9, 0xBE, 0x9A, 0xDD, 0x54, 0x37, 0xEA, 0x0E, 0xC4, 0xD6, 0x4D, 0x8E, 0x9E};
u8 keys660_k1[] = {0x76, 0xF2, 0x6C, 0x0A, 0xCA, 0x3A, 0xBA, 0x4E, 0xAC, 0x76, 0xD2, 0x40, 0xF5, 0xC3, 0xBF, 0xF9};
u8 keys660_k2[] = {0x7A, 0x3E, 0x55, 0x75, 0xB9, 0x6A, 0xFC, 0x4F, 0x3E, 0xE3, 0xDF, 0xB3, 0x6C, 0xE8, 0x2A, 0x82};
u8 keys660_k3[] = {0xFA, 0x79, 0x09, 0x36, 0xE6, 0x19, 0xE8, 0xA4, 0xA9, 0x41, 0x37, 0x18, 0x81, 0x02, 0xE9, 0xB3};
u8 keys660_v1[] = {0xBA, 0x76, 0x61, 0x47, 0x8B, 0x55, 0xA8, 0x72, 0x89, 0x15, 0x79, 0x6D, 0xD7, 0x2F, 0x78, 0x0E};
u8 keys660_v2[] = {0xF9, 0x4A, 0x6B, 0x96, 0x79, 0x3F, 0xEE, 0x0A, 0x04, 0xC8, 0x8D, 0x7E, 0x5F, 0x38, 0x3A, 0xCF};
u8 keys660_v3[] = {0x88, 0xAF, 0x18, 0xE9, 0xC3, 0xAA, 0x6B, 0x56, 0xF7, 0xC5, 0xA8, 0xBF, 0x1A, 0x84, 0xE9, 0xF3};
u8 keys660_v4[] = {0xD1, 0xB0, 0xAE, 0xC3, 0x24, 0x36, 0x13, 0x49, 0xD6, 0x49, 0xD7, 0x88, 0xEA, 0xA4, 0x99, 0x86};
u8 keys660_v5[] = {0xCB, 0x93, 0x12, 0x38, 0x31, 0xC0, 0x2D, 0x2E, 0x7A, 0x18, 0x5C, 0xAC, 0x92, 0x93, 0xAB, 0x32};
u8 keys660_v6[] = {0x92, 0x8C, 0xA4, 0x12, 0xD6, 0x5C, 0x55, 0x31, 0x5B, 0x94, 0x23, 0x9B, 0x62, 0xB3, 0xDB, 0x47};
u8 keys660_k4[] = {0xC8, 0xA0, 0x70, 0x98, 0xAE, 0xE6, 0x2B, 0x80, 0xD7, 0x91, 0xE6, 0xCA, 0x4C, 0xA9, 0x78, 0x4E};
u8 keys660_k5[] = {0xBF, 0xF8, 0x34, 0x02, 0x84, 0x47, 0xBD, 0x87, 0x1C, 0x52, 0x03, 0x23, 0x79, 0xBB, 0x59, 0x81};
u8 keys660_k6[] = {0xD2, 0x83, 0xCC, 0x63, 0xBB, 0x10, 0x15, 0xE7, 0x7B, 0xC0, 0x6D, 0xEE, 0x34, 0x9E, 0x4A, 0xFA};
u8 keys660_k7[] = {0xEB, 0xD9, 0x1E, 0x05, 0x3C, 0xAE, 0xAB, 0x62, 0xE3, 0xB7, 0x1F, 0x37, 0xE5, 0xCD, 0x68, 0xC3};
u8 keys660_v7[] = {0xC5, 0x9C, 0x77, 0x9C, 0x41, 0x01, 0xE4, 0x85, 0x79, 0xC8, 0x71, 0x63, 0xA5, 0x7D, 0x4F, 0xFB};
u8 keys660_v8[] = {0x86, 0xA0, 0x7D, 0x4D, 0xB3, 0x6B, 0xA2, 0xFD, 0xF4, 0x15, 0x85, 0x70, 0x2D, 0x6A, 0x0D, 0x3A};
u8 keys660_k8[] = {0x85, 0x93, 0x1F, 0xED, 0x2C, 0x4D, 0xA4, 0x53, 0x59, 0x9C, 0x3F, 0x16, 0xF3, 0x50, 0xDE, 0x46};
u8 key_21C0[] = {0x6A, 0x19, 0x71, 0xF3, 0x18, 0xDE, 0xD3, 0xA2, 0x6D, 0x3B, 0xDE, 0xC7, 0xBE, 0x98, 0xE2, 0x4C};
u8 key_2250[] = {0x50, 0xCC, 0x03, 0xAC, 0x3F, 0x53, 0x1A, 0xFA, 0x0A, 0xA4, 0x34, 0x23, 0x86, 0x61, 0x7F, 0x97};
u8 key_22E0[] = {0x66, 0x0F, 0xCB, 0x3B, 0x30, 0x75, 0xE3, 0x10, 0x0A, 0x95, 0x65, 0xC7, 0x3C, 0x93, 0x87, 0x22};
u8 key_2D80[] = {0x40, 0x02, 0xC0, 0xBF, 0x20, 0x02, 0xC0, 0xBF, 0x5C, 0x68, 0x2B, 0x95, 0x5F, 0x40, 0x7B, 0xB8};
u8 key_2D90[] = {0x55, 0x19, 0x35, 0x10, 0x48, 0xD8, 0x2E, 0x46, 0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F};
u8 key_2DA8[] = {0x80, 0x02, 0xC0, 0xBF, 0x00, 0x0A, 0xC0, 0xBF, 0x40, 0x03, 0xC0, 0xBF, 0x40, 0x00, 0x00, 0x00};
u8 key_2DB8[] = {0x4C, 0x2D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xB8, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
u8 key_D91605F0[] = {0xB8, 0x8C, 0x45, 0x8B, 0xB6, 0xE7, 0x6E, 0xB8, 0x51, 0x59, 0xA6, 0x53, 0x7C, 0x5E, 0x86, 0x31};
u8 key_D91606F0[] = {0xED, 0x10, 0xE0, 0x36, 0xC4, 0xFE, 0x83, 0xF3, 0x75, 0x70, 0x5E, 0xF6, 0xA4, 0x40, 0x05, 0xF7};
u8 key_D91608F0[] = {0x5C, 0x77, 0x0C, 0xBB, 0xB4, 0xC2, 0x4F, 0xA2, 0x7E, 0x3B, 0x4E, 0xB4, 0xB4, 0xC8, 0x70, 0xAF};
u8 key_D91609F0[] = {0xD0, 0x36, 0x12, 0x75, 0x80, 0x56, 0x20, 0x43, 0xC4, 0x30, 0x94, 0x3E, 0x1C, 0x75, 0xD1, 0xBF};
u8 key_D9160AF0[] = {0x10, 0xA9, 0xAC, 0x16, 0xAE, 0x19, 0xC0, 0x7E, 0x3B, 0x60, 0x77, 0x86, 0x01, 0x6F, 0xF2, 0x63};
u8 key_D9160BF0[] = {0x83, 0x83, 0xF1, 0x37, 0x53, 0xD0, 0xBE, 0xFC, 0x8D, 0xA7, 0x32, 0x52, 0x46, 0x0A, 0xC2, 0xC2};
u8 key_D91611F0[] = {0x61, 0xB0, 0xC0, 0x58, 0x71, 0x57, 0xD9, 0xFA, 0x74, 0x67, 0x0E, 0x5C, 0x7E, 0x6E, 0x95, 0xB9};
u8 key_D91612F0[] = {0x9E, 0x20, 0xE1, 0xCD, 0xD7, 0x88, 0xDE, 0xC0, 0x31, 0x9B, 0x10, 0xAF, 0xC5, 0xB8, 0x73, 0x23};
u8 key_D91613F0[] = {0xEB, 0xFF, 0x40, 0xD8, 0xB4, 0x1A, 0xE1, 0x66, 0x91, 0x3B, 0x8F, 0x64, 0xB6, 0xFC, 0xB7, 0x12};
u8 key_D91614F0[] = {0xFD, 0xF7, 0xB7, 0x3C, 0x9F, 0xD1, 0x33, 0x95, 0x11, 0xB8, 0xB5, 0xBB, 0x54, 0x23, 0x73, 0x85};
u8 key_D91615F0[] = {0xC8, 0x03, 0xE3, 0x44, 0x50, 0xF1, 0xE7, 0x2A, 0x6A, 0x0D, 0xC3, 0x61, 0xB6, 0x8E, 0x5F, 0x51};
u8 key_D91616F0[] = {0x53, 0x03, 0xB8, 0x6A, 0x10, 0x19, 0x98, 0x49, 0x1C, 0xAF, 0x30, 0xE4, 0x25, 0x1B, 0x6B, 0x28};
u8 key_D91617F0[] = {0x02, 0xFA, 0x48, 0x73, 0x75, 0xAF, 0xAE, 0x0A, 0x67, 0x89, 0x2B, 0x95, 0x4B, 0x09, 0x87, 0xA3};
u8 key_D91618F0[] = {0x96, 0x96, 0x7C, 0xC3, 0xF7, 0x12, 0xDA, 0x62, 0x1B, 0xF6, 0x9A, 0x9A, 0x44, 0x44, 0xBC, 0x48};
u8 key_D91619F0[] = {0xE0, 0x32, 0xA7, 0x08, 0x6B, 0x2B, 0x29, 0x2C, 0xD1, 0x4D, 0x5B, 0xEE, 0xA8, 0xC8, 0xB4, 0xE9};
u8 key_D9161AF0[] = {0x27, 0xE5, 0xA7, 0x49, 0x52, 0xE1, 0x94, 0x67, 0x35, 0x66, 0x91, 0x0C, 0xE8, 0x9A, 0x25, 0x24};
u8 key_D91620F0[] = {0x52, 0x1C, 0xB4, 0x5F, 0x40, 0x3B, 0x9A, 0xDD, 0xAC, 0xFC, 0xEA, 0x92, 0xFD, 0xDD, 0xF5, 0x90};
u8 key_D91621F0[] = {0xD1, 0x91, 0x2E, 0xA6, 0x21, 0x14, 0x29, 0x62, 0xF6, 0xED, 0xAE, 0xCB, 0xDD, 0xA3, 0xBA, 0xFE};
u8 key_D91622F0[] = {0x59, 0x5D, 0x78, 0x4D, 0x21, 0xB2, 0x01, 0x17, 0x6C, 0x9A, 0xB5, 0x1B, 0xDA, 0xB7, 0xF9, 0xE6};
u8 key_D91623F0[] = {0xAA, 0x45, 0xEB, 0x4F, 0x62, 0xFB, 0xD1, 0x0D, 0x71, 0xD5, 0x62, 0xD2, 0xF5, 0xBF, 0xA5, 0x2F};
u8 key_D91624F0[] = {0x61, 0xB7, 0x26, 0xAF, 0x8B, 0xF1, 0x41, 0x58, 0x83, 0x6A, 0xC4, 0x92, 0x12, 0xCB, 0xB1, 0xE9};
u8 key_D91628F0[] = {0x49, 0xA4, 0xFC, 0x66, 0xDC, 0xE7, 0x62, 0x21, 0xDB, 0x18, 0xA7, 0x50, 0xD6, 0xA8, 0xC1, 0xB6};
u8 key_D91680F0[] = {0x2C, 0x22, 0x9B, 0x12, 0x36, 0x74, 0x11, 0x67, 0x49, 0xD1, 0xD1, 0x88, 0x92, 0xF6, 0xA1, 0xD8};
u8 key_D91681F0[] = {0x52, 0xB6, 0x36, 0x6C, 0x8C, 0x46, 0x7F, 0x7A, 0xCC, 0x11, 0x62, 0x99, 0xC1, 0x99, 0xBE, 0x98};
u8 key_2E5E10F0[] = {0x9D, 0x5C, 0x5B, 0xAF, 0x8C, 0xD8, 0x69, 0x7E, 0x51, 0x9F, 0x70, 0x96, 0xE6, 0xD5, 0xC4, 0xE8};
u8 key_2E5E12F0[] = {0x8A, 0x7B, 0xC9, 0xD6, 0x52, 0x58, 0x88, 0xEA, 0x51, 0x83, 0x60, 0xCA, 0x16, 0x79, 0xE2, 0x07};
u8 key_2E5E13F0[] = {0xFF, 0xA4, 0x68, 0xC3, 0x31, 0xCA, 0xB7, 0x4C, 0xF1, 0x23, 0xFF, 0x01, 0x65, 0x3D, 0x26, 0x36};
u8 key_2FD30BF0[] = {0xD8, 0x58, 0x79, 0xF9, 0xA4, 0x22, 0xAF, 0x86, 0x90, 0xAC, 0xDA, 0x45, 0xCE, 0x60, 0x40, 0x3F};
u8 key_2FD311F0[] = {0x3A, 0x6B, 0x48, 0x96, 0x86, 0xA5, 0xC8, 0x80, 0x69, 0x6C, 0xE6, 0x4B, 0xF6, 0x04, 0x17, 0x44};
u8 key_2FD312F0[] = {0xC5, 0xFB, 0x69, 0x03, 0x20, 0x7A, 0xCF, 0xBA, 0x2C, 0x90, 0xF8, 0xB8, 0x4D, 0xD2, 0xF1, 0xDE};
u8 keys02G_E[] = {0x9D, 0x09, 0xFD, 0x20, 0xF3, 0x8F, 0x10, 0x69, 0x0D, 0xB2, 0x6F, 0x00, 0xCC, 0xC5, 0x51, 0x2E};
u8 keys03G_E[] = {0x4F, 0x44, 0x5C, 0x62, 0xB3, 0x53, 0xC4, 0x30, 0xFC, 0x3A, 0xA4, 0x5B, 0xEC, 0xFE, 0x51, 0xEA};
u8 keys05G_E[] = {0x5D, 0xAA, 0x72, 0xF2, 0x26, 0x60, 0x4D, 0x1C, 0xE7, 0x2D, 0xC8, 0xA3, 0x2F, 0x79, 0xC5, 0x54};
u8 oneseg_310[] = {0xC7, 0x27, 0x72, 0x85, 0xAB, 0xA7, 0xF7, 0xF0, 0x4C, 0xC1, 0x86, 0xCC, 0xE3, 0x7F, 0x17, 0xCA};
u8 oneseg_300[] = {0x76, 0x40, 0x9E, 0x08, 0xDB, 0x9B, 0x3B, 0xA1, 0x47, 0x8A, 0x96, 0x8E, 0xF3, 0xF7, 0x62, 0x92};
u8 oneseg_280[] = {0x23, 0xDC, 0x3B, 0xB5, 0xA9, 0x82, 0xD6, 0xEA, 0x63, 0xA3, 0x6E, 0x2B, 0x2B, 0xE9, 0xE1, 0x54};
u8 oneseg_260_271[] = {0x22, 0x43, 0x57, 0x68, 0x2F, 0x41, 0xCE, 0x65, 0x4C, 0xA3, 0x7C, 0xC6, 0xC4, 0xAC, 0xF3, 0x60};
u8 oneseg_slim[] = {0x12, 0x57, 0x0D, 0x8A, 0x16, 0x6D, 0x87, 0x06, 0x03, 0x7D, 0xC8, 0x8B, 0x62, 0xA3, 0x32, 0xA9};
u8 ms_app_main[] = {0x1E, 0x2E, 0x38, 0x49, 0xDA, 0xD4, 0x16, 0x08, 0x27, 0x2E, 0xF3, 0xBC, 0x37, 0x75, 0x80, 0x93};
u8 demokeys_280[] = {0x12, 0x99, 0x70, 0x5E, 0x24, 0x07, 0x6C, 0xD0, 0x2D, 0x06, 0xFE, 0x7E, 0xB3, 0x0C, 0x11, 0x26};
u8 demokeys_3XX_1[] = {0x47, 0x05, 0xD5, 0xE3, 0x56, 0x1E, 0x81, 0x9B, 0x09, 0x2F, 0x06, 0xDB, 0x6B, 0x12, 0x92, 0xE0};
u8 demokeys_3XX_2[] = {0xF6, 0x62, 0x39, 0x6E, 0x26, 0x22, 0x4D, 0xCA, 0x02, 0x64, 0x16, 0x99, 0x7B, 0x9A, 0xE7, 0xB8};
u8 ebootbin_271_new[] = {0xF4, 0xAE, 0xF4, 0xE1, 0x86, 0xDD, 0xD2, 0x9C, 0x7C, 0xC5, 0x42, 0xA6, 0x95, 0xA0, 0x83, 0x88};
u8 ebootbin_280_new[] = {0xB8, 0x8C, 0x45, 0x8B, 0xB6, 0xE7, 0x6E, 0xB8, 0x51, 0x59, 0xA6, 0x53, 0x7C, 0x5E, 0x86, 0x31};
u8 ebootbin_300_new[] = {0xED, 0x10, 0xE0, 0x36, 0xC4, 0xFE, 0x83, 0xF3, 0x75, 0x70, 0x5E, 0xF6, 0xA4, 0x40, 0x05, 0xF7};
u8 ebootbin_310_new[] = {0x5C, 0x77, 0x0C, 0xBB, 0xB4, 0xC2, 0x4F, 0xA2, 0x7E, 0x3B, 0x4E, 0xB4, 0xB4, 0xC8, 0x70, 0xAF};
u8 gameshare_260_271[] = {0xF9, 0x48, 0x38, 0x0C, 0x96, 0x88, 0xA7, 0x74, 0x4F, 0x65, 0xA0, 0x54, 0xC2, 0x76, 0xD9, 0xB8};
u8 gameshare_280[] = {0x2D, 0x86, 0x77, 0x3A, 0x56, 0xA4, 0x4F, 0xDD, 0x3C, 0x16, 0x71, 0x93, 0xAA, 0x8E, 0x11, 0x43};
u8 gameshare_300[] = {0x78, 0x1A, 0xD2, 0x87, 0x24, 0xBD, 0xA2, 0x96, 0x18, 0x3F, 0x89, 0x36, 0x72, 0x90, 0x92, 0x85};
u8 gameshare_310[] = {0xC9, 0x7D, 0x3E, 0x0A, 0x54, 0x81, 0x6E, 0xC7, 0x13, 0x74, 0x99, 0x74, 0x62, 0x18, 0xE7, 0xDD};
u8 key_380210F0[] = {0x32, 0x2C, 0xFA, 0x75, 0xE4, 0x7E, 0x93, 0xEB, 0x9F, 0x22, 0x80, 0x85, 0x57, 0x08, 0x98, 0x48};
u8 key_380280F0[] = {0x97, 0x09, 0x12, 0xD3, 0xDB, 0x02, 0xBD, 0xD8, 0xE7, 0x74, 0x51, 0xFE, 0xF0, 0xEA, 0x6C, 0x5C};
u8 key_380283F0[] = {0x34, 0x20, 0x0C, 0x8E, 0xA1, 0x86, 0x79, 0x84, 0xAF, 0x13, 0xAE, 0x34, 0x77, 0x6F, 0xEA, 0x89};
u8 key_407810F0[] = {0xAF, 0xAD, 0xCA, 0xF1, 0x95, 0x59, 0x91, 0xEC, 0x1B, 0x27, 0xD0, 0x4E, 0x8A, 0xF3, 0x3D, 0xE7};
u8 drmkeys_6XX_1[] = {0x36, 0xEF, 0x82, 0x4E, 0x74, 0xFB, 0x17, 0x5B, 0x14, 0x14, 0x05, 0xF3, 0xB3, 0x8A, 0x76, 0x18};
u8 drmkeys_6XX_2[] = {0x21, 0x52, 0x5D, 0x76, 0xF6, 0x81, 0x0F, 0x15, 0x2F, 0x4A, 0x40, 0x89, 0x63, 0xA0, 0x10, 0x55};
static const u8 keys260_0[] = {0xC3, 0x24, 0x89, 0xD3, 0x80, 0x87, 0xB2, 0x4E, 0x4C, 0xD7, 0x49, 0xE4, 0x9D, 0x1D, 0x34, 0xD1};
static const u8 keys260_1[] = {0xF3, 0xAC, 0x6E, 0x7C, 0x04, 0x0A, 0x23, 0xE7, 0x0D, 0x33, 0xD8, 0x24, 0x73, 0x39, 0x2B, 0x4A};
static const u8 keys260_2[] = {0x72, 0xB4, 0x39, 0xFF, 0x34, 0x9B, 0xAE, 0x82, 0x30, 0x34, 0x4A, 0x1D, 0xA2, 0xD8, 0xB4, 0x3C};
static const u8 keys280_0[] = {0xCA, 0xFB, 0xBF, 0xC7, 0x50, 0xEA, 0xB4, 0x40, 0x8E, 0x44, 0x5C, 0x63, 0x53, 0xCE, 0x80, 0xB1};
static const u8 keys280_1[] = {0x40, 0x9B, 0xC6, 0x9B, 0xA9, 0xFB, 0x84, 0x7F, 0x72, 0x21, 0xD2, 0x36, 0x96, 0x55, 0x09, 0x74};
static const u8 keys280_2[] = {0x03, 0xA7, 0xCC, 0x4A, 0x5B, 0x91, 0xC2, 0x07, 0xFF, 0xFC, 0x26, 0x25, 0x1E, 0x42, 0x4B, 0xB5};
static const u8 keys300_0[] = {0x9F, 0x67, 0x1A, 0x7A, 0x22, 0xF3, 0x59, 0x0B, 0xAA, 0x6D, 0xA4, 0xC6, 0x8B, 0xD0, 0x03, 0x77};
static const u8 keys300_1[] = {0x15, 0x07, 0x63, 0x26, 0xDB, 0xE2, 0x69, 0x34, 0x56, 0x08, 0x2A, 0x93, 0x4E, 0x4B, 0x8A, 0xB2};
static const u8 keys300_2[] = {0x56, 0x3B, 0x69, 0xF7, 0x29, 0x88, 0x2F, 0x4C, 0xDB, 0xD5, 0xDE, 0x80, 0xC6, 0x5C, 0xC8, 0x73};
static const u8 keys303_0[] = {0x7b, 0xa1, 0xe2, 0x5a, 0x91, 0xb9, 0xd3, 0x13, 0x77, 0x65, 0x4a, 0xb7, 0xc2, 0x8a, 0x10, 0xaf};
static const u8 keys310_0[] = {0xa2, 0x41, 0xe8, 0x39, 0x66, 0x5b, 0xfa, 0xbb, 0x1b, 0x2d, 0x6e, 0x0e, 0x33, 0xe5, 0xd7, 0x3f};
static const u8 keys310_1[] = {0xA4, 0x60, 0x8F, 0xAB, 0xAB, 0xDE, 0xA5, 0x65, 0x5D, 0x43, 0x3A, 0xD1, 0x5E, 0xC3, 0xFF, 0xEA};
static const u8 keys310_2[] = {0xE7, 0x5C, 0x85, 0x7A, 0x59, 0xB4, 0xE3, 0x1D, 0xD0, 0x9E, 0xCE, 0xC2, 0xD6, 0xD4, 0xBD, 0x2B};
static const u8 keys310_3[] = {0x2E, 0x00, 0xF6, 0xF7, 0x52, 0xCF, 0x95, 0x5A, 0xA1, 0x26, 0xB4, 0x84, 0x9B, 0x58, 0x76, 0x2F};
static const u8 keys330_0[] = {0x3B, 0x9B, 0x1A, 0x56, 0x21, 0x80, 0x14, 0xED, 0x8E, 0x8B, 0x08, 0x42, 0xFA, 0x2C, 0xDC, 0x3A};
static const u8 keys330_1[] = {0xE8, 0xBE, 0x2F, 0x06, 0xB1, 0x05, 0x2A, 0xB9, 0x18, 0x18, 0x03, 0xE3, 0xEB, 0x64, 0x7D, 0x26};
static const u8 keys330_2[] = {0xAB, 0x82, 0x25, 0xD7, 0x43, 0x6F, 0x6C, 0xC1, 0x95, 0xC5, 0xF7, 0xF0, 0x63, 0x73, 0x3F, 0xE7};
static const u8 keys330_3[] = {0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F, 0x38, 0x4C, 0x4D, 0x96, 0xBD, 0x49, 0xEC, 0x9B};
static const u8 keys330_4[] = {0xEC, 0x3B, 0xD2, 0xC0, 0xFA, 0xC1, 0xEE, 0xB9, 0x9A, 0xBC, 0xFF, 0xA3, 0x89, 0xF2, 0x60, 0x1F};
static const u8 keys360_0[] = {0x3C, 0x2B, 0x51, 0xD4, 0x2D, 0x85, 0x47, 0xDA, 0x2D, 0xCA, 0x18, 0xDF, 0xFE, 0x54, 0x09, 0xED};
static const u8 keys360_1[] = {0x31, 0x1F, 0x98, 0xD5, 0x7B, 0x58, 0x95, 0x45, 0x32, 0xAB, 0x3A, 0xE3, 0x89, 0x32, 0x4B, 0x34};
static const u8 keys370_0[] = {0x26, 0x38, 0x0A, 0xAC, 0xA5, 0xD8, 0x74, 0xD1, 0x32, 0xB7, 0x2A, 0xBF, 0x79, 0x9E, 0x6D, 0xDB};
static const u8 keys370_1[] = {0x53, 0xE7, 0xAB, 0xB9, 0xC6, 0x4A, 0x4B, 0x77, 0x92, 0x17, 0xB5, 0x74, 0x0A, 0xDA, 0xA9, 0xEA};
static const u8 keys370_2[] = {0x71, 0x10, 0xF0, 0xA4, 0x16, 0x14, 0xD5, 0x93, 0x12, 0xFF, 0x74, 0x96, 0xDF, 0x1F, 0xDA, 0x89};
static const u8 keys390_0[] = {0x45, 0xEF, 0x5C, 0x5D, 0xED, 0x81, 0x99, 0x84, 0x12, 0x94, 0x8F, 0xAB, 0xE8, 0x05, 0x6D, 0x7D};
static const u8 keys390_1[] = {0x70, 0x1B, 0x08, 0x25, 0x22, 0xA1, 0x4D, 0x3B, 0x69, 0x21, 0xF9, 0x71, 0x0A, 0xA8, 0x41, 0xA9};
static const u8 keys500_0[] = {0xEB, 0x1B, 0x53, 0x0B, 0x62, 0x49, 0x32, 0x58, 0x1F, 0x83, 0x0A, 0xF4, 0x99, 0x3D, 0x75, 0xD0};
static const u8 keys500_1[] = {0xBA, 0xE2, 0xA3, 0x12, 0x07, 0xFF, 0x04, 0x1B, 0x64, 0xA5, 0x11, 0x85, 0xF7, 0x2F, 0x99, 0x5B};
static const u8 keys500_2[] = {0x2C, 0x8E, 0xAF, 0x1D, 0xFF, 0x79, 0x73, 0x1A, 0xAD, 0x96, 0xAB, 0x09, 0xEA, 0x35, 0x59, 0x8B};
static const u8 keys500_c[] = {0xA3, 0x5D, 0x51, 0xE6, 0x56, 0xC8, 0x01, 0xCA, 0xE3, 0x77, 0xBF, 0xCD, 0xFF, 0x24, 0xDA, 0x4D};
static const u8 keys505_a[] = {0x7B, 0x94, 0x72, 0x27, 0x4C, 0xCC, 0x54, 0x3B, 0xAE, 0xDF, 0x46, 0x37, 0xAC, 0x01, 0x4D, 0x87};
static const u8 keys505_0[] = {0x2E, 0x8E, 0x97, 0xA2, 0x85, 0x42, 0x70, 0x73, 0x18, 0xDA, 0xA0, 0x8A, 0xF8, 0x62, 0xA2, 0xB0};
static const u8 keys505_1[] = {0x58, 0x2A, 0x4C, 0x69, 0x19, 0x7B, 0x83, 0x3D, 0xD2, 0x61, 0x61, 0xFE, 0x14, 0xEE, 0xAA, 0x11};
static const u8 keys570_5k[] = {0x6D, 0x72, 0xA4, 0xBA, 0x7F, 0xBF, 0xD1, 0xF1, 0xA9, 0xF3, 0xBB, 0x07, 0x1B, 0xC0, 0xB3, 0x66};
static const u8 keys600_1[] = {0xE3, 0x52, 0x39, 0x97, 0x3B, 0x84, 0x41, 0x1C, 0xC3, 0x23, 0xF1, 0xB8, 0xA9, 0x09, 0x4B, 0xF0};
static const u8 keys600_2[] = {0xE1, 0x45, 0x93, 0x2C, 0x53, 0xE2, 0xAB, 0x06, 0x6F, 0xB6, 0x8F, 0x0B, 0x66, 0x91, 0xE7, 0x1E};
static const u8 keys620_0[] = {0xD6, 0xBD, 0xCE, 0x1E, 0x12, 0xAF, 0x9A, 0xE6, 0x69, 0x30, 0xDE, 0xDA, 0x88, 0xB8, 0xFF, 0xFB};
static const u8 keys620_1[] = {0x1D, 0x13, 0xE9, 0x50, 0x04, 0x73, 0x3D, 0xD2, 0xE1, 0xDA, 0xB9, 0xC1, 0xE6, 0x7B, 0x25, 0xA7};
static const u8 keys620_a[] = {0xAC, 0x34, 0xBA, 0xB1, 0x97, 0x8D, 0xAE, 0x6F, 0xBA, 0xE8, 0xB1, 0xD6, 0xDF, 0xDF, 0xF1, 0xA2};
static const u8 keys620_e[] = {0xB1, 0xB3, 0x7F, 0x76, 0xC3, 0xFB, 0x88, 0xE6, 0xF8, 0x60, 0xD3, 0x35, 0x3C, 0xA3, 0x4E, 0xF3};
static const u8 keys620_5[] = {0xF1, 0xBC, 0x17, 0x07, 0xAE, 0xB7, 0xC8, 0x30, 0xD8, 0x34, 0x9D, 0x40, 0x6A, 0x8E, 0xDF, 0x4E};
static const u8 keys620_5k[] = {0x41, 0x8A, 0x35, 0x4F, 0x69, 0x3A, 0xDF, 0x04, 0xFD, 0x39, 0x46, 0xA2, 0x5C, 0x2D, 0xF2, 0x21};
static const u8 keys620_5v[] = {0xF2, 0x8F, 0x75, 0xA7, 0x31, 0x91, 0xCE, 0x9E, 0x75, 0xBD, 0x27, 0x26, 0xB4, 0xB4, 0x0C, 0x32};
static const u8 keys630_k1[] = {0x36, 0xB0, 0xDC, 0xFC, 0x59, 0x2A, 0x95, 0x1D, 0x80, 0x2D, 0x80, 0x3F, 0xCD, 0x30, 0xA0, 0x1B};
static const u8 keys630_k2[] = {0xd4, 0x35, 0x18, 0x02, 0x29, 0x68, 0xfb, 0xa0, 0x6a, 0xa9, 0xa5, 0xed, 0x78, 0xfd, 0x2e, 0x9d};
static const u8 keys630_k3[] = {0x23, 0x8D, 0x3D, 0xAE, 0x41, 0x50, 0xA0, 0xFA, 0xF3, 0x2F, 0x32, 0xCE, 0xC7, 0x27, 0xCD, 0x50};
static const u8 keys630_k4[] = {0xAA, 0xA1, 0xB5, 0x7C, 0x93, 0x5A, 0x95, 0xBD, 0xEF, 0x69, 0x16, 0xFC, 0x2B, 0x92, 0x31, 0xDD};
static const u8 keys630_k5[] = {0x87, 0x37, 0x21, 0xCC, 0x65, 0xAE, 0xAA, 0x5F, 0x40, 0xF6, 0x6F, 0x2A, 0x86, 0xC7, 0xA1, 0xC8};
static const u8 keys630_k6[] = {0x8D, 0xDB, 0xDC, 0x5C, 0xF2, 0x70, 0x2B, 0x40, 0xB2, 0x3D, 0x00, 0x09, 0x61, 0x7C, 0x10, 0x60};
static const u8 keys630_k7[] = {0x77, 0x1C, 0x06, 0x5F, 0x53, 0xEC, 0x3F, 0xFC, 0x22, 0xCE, 0x5A, 0x27, 0xFF, 0x78, 0xA8, 0x48};
static const u8 keys630_k8[] = {0x81, 0xD1, 0x12, 0x89, 0x35, 0xC8, 0xEA, 0x8B, 0xE0, 0x02, 0x2D, 0x2D, 0x6A, 0x18, 0x67, 0xB8};
static const u8 keys636_k1[] = {0x07, 0xE3, 0x08, 0x64, 0x7F, 0x60, 0xA3, 0x36, 0x6A, 0x76, 0x21, 0x44, 0xC9, 0xD7, 0x06, 0x83};
static const u8 keys636_k2[] = {0x91, 0xF2, 0x02, 0x9E, 0x63, 0x32, 0x30, 0xA9, 0x1D, 0xDA, 0x0B, 0xA8, 0xB7, 0x41, 0xA3, 0xCC};
static const u8 keys638_k4[] = {0x98, 0x43, 0xFF, 0x85, 0x68, 0xB2, 0xDB, 0x3B, 0xD4, 0x22, 0xD0, 0x4F, 0xAB, 0x5F, 0x0A, 0x31};
static const u8 keys639_k3[] = {0x01, 0x7B, 0xF0, 0xE9, 0xBE, 0x9A, 0xDD, 0x54, 0x37, 0xEA, 0x0E, 0xC4, 0xD6, 0x4D, 0x8E, 0x9E};
static const u8 keys660_k1[] = {0x76, 0xF2, 0x6C, 0x0A, 0xCA, 0x3A, 0xBA, 0x4E, 0xAC, 0x76, 0xD2, 0x40, 0xF5, 0xC3, 0xBF, 0xF9};
static const u8 keys660_k2[] = {0x7A, 0x3E, 0x55, 0x75, 0xB9, 0x6A, 0xFC, 0x4F, 0x3E, 0xE3, 0xDF, 0xB3, 0x6C, 0xE8, 0x2A, 0x82};
static const u8 keys660_k3[] = {0xFA, 0x79, 0x09, 0x36, 0xE6, 0x19, 0xE8, 0xA4, 0xA9, 0x41, 0x37, 0x18, 0x81, 0x02, 0xE9, 0xB3};
static const u8 keys660_v1[] = {0xBA, 0x76, 0x61, 0x47, 0x8B, 0x55, 0xA8, 0x72, 0x89, 0x15, 0x79, 0x6D, 0xD7, 0x2F, 0x78, 0x0E};
static const u8 keys660_v2[] = {0xF9, 0x4A, 0x6B, 0x96, 0x79, 0x3F, 0xEE, 0x0A, 0x04, 0xC8, 0x8D, 0x7E, 0x5F, 0x38, 0x3A, 0xCF};
static const u8 keys660_v3[] = {0x88, 0xAF, 0x18, 0xE9, 0xC3, 0xAA, 0x6B, 0x56, 0xF7, 0xC5, 0xA8, 0xBF, 0x1A, 0x84, 0xE9, 0xF3};
static const u8 keys660_v4[] = {0xD1, 0xB0, 0xAE, 0xC3, 0x24, 0x36, 0x13, 0x49, 0xD6, 0x49, 0xD7, 0x88, 0xEA, 0xA4, 0x99, 0x86};
static const u8 keys660_v5[] = {0xCB, 0x93, 0x12, 0x38, 0x31, 0xC0, 0x2D, 0x2E, 0x7A, 0x18, 0x5C, 0xAC, 0x92, 0x93, 0xAB, 0x32};
static const u8 keys660_v6[] = {0x92, 0x8C, 0xA4, 0x12, 0xD6, 0x5C, 0x55, 0x31, 0x5B, 0x94, 0x23, 0x9B, 0x62, 0xB3, 0xDB, 0x47};
static const u8 keys660_k4[] = {0xC8, 0xA0, 0x70, 0x98, 0xAE, 0xE6, 0x2B, 0x80, 0xD7, 0x91, 0xE6, 0xCA, 0x4C, 0xA9, 0x78, 0x4E};
static const u8 keys660_k5[] = {0xBF, 0xF8, 0x34, 0x02, 0x84, 0x47, 0xBD, 0x87, 0x1C, 0x52, 0x03, 0x23, 0x79, 0xBB, 0x59, 0x81};
static const u8 keys660_k6[] = {0xD2, 0x83, 0xCC, 0x63, 0xBB, 0x10, 0x15, 0xE7, 0x7B, 0xC0, 0x6D, 0xEE, 0x34, 0x9E, 0x4A, 0xFA};
static const u8 keys660_k7[] = {0xEB, 0xD9, 0x1E, 0x05, 0x3C, 0xAE, 0xAB, 0x62, 0xE3, 0xB7, 0x1F, 0x37, 0xE5, 0xCD, 0x68, 0xC3};
static const u8 keys660_v7[] = {0xC5, 0x9C, 0x77, 0x9C, 0x41, 0x01, 0xE4, 0x85, 0x79, 0xC8, 0x71, 0x63, 0xA5, 0x7D, 0x4F, 0xFB};
static const u8 keys660_v8[] = {0x86, 0xA0, 0x7D, 0x4D, 0xB3, 0x6B, 0xA2, 0xFD, 0xF4, 0x15, 0x85, 0x70, 0x2D, 0x6A, 0x0D, 0x3A};
static const u8 keys660_k8[] = {0x85, 0x93, 0x1F, 0xED, 0x2C, 0x4D, 0xA4, 0x53, 0x59, 0x9C, 0x3F, 0x16, 0xF3, 0x50, 0xDE, 0x46};
static const u8 key_21C0[] = {0x6A, 0x19, 0x71, 0xF3, 0x18, 0xDE, 0xD3, 0xA2, 0x6D, 0x3B, 0xDE, 0xC7, 0xBE, 0x98, 0xE2, 0x4C};
static const u8 key_2250[] = {0x50, 0xCC, 0x03, 0xAC, 0x3F, 0x53, 0x1A, 0xFA, 0x0A, 0xA4, 0x34, 0x23, 0x86, 0x61, 0x7F, 0x97};
static const u8 key_22E0[] = {0x66, 0x0F, 0xCB, 0x3B, 0x30, 0x75, 0xE3, 0x10, 0x0A, 0x95, 0x65, 0xC7, 0x3C, 0x93, 0x87, 0x22};
static const u8 key_2D80[] = {0x40, 0x02, 0xC0, 0xBF, 0x20, 0x02, 0xC0, 0xBF, 0x5C, 0x68, 0x2B, 0x95, 0x5F, 0x40, 0x7B, 0xB8};
static const u8 key_2D90[] = {0x55, 0x19, 0x35, 0x10, 0x48, 0xD8, 0x2E, 0x46, 0xA8, 0xB1, 0x47, 0x77, 0xDC, 0x49, 0x6A, 0x6F};
static const u8 key_2DA8[] = {0x80, 0x02, 0xC0, 0xBF, 0x00, 0x0A, 0xC0, 0xBF, 0x40, 0x03, 0xC0, 0xBF, 0x40, 0x00, 0x00, 0x00};
static const u8 key_2DB8[] = {0x4C, 0x2D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xB8, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const u8 key_D91605F0[] = {0xB8, 0x8C, 0x45, 0x8B, 0xB6, 0xE7, 0x6E, 0xB8, 0x51, 0x59, 0xA6, 0x53, 0x7C, 0x5E, 0x86, 0x31};
static const u8 key_D91606F0[] = {0xED, 0x10, 0xE0, 0x36, 0xC4, 0xFE, 0x83, 0xF3, 0x75, 0x70, 0x5E, 0xF6, 0xA4, 0x40, 0x05, 0xF7};
static const u8 key_D91608F0[] = {0x5C, 0x77, 0x0C, 0xBB, 0xB4, 0xC2, 0x4F, 0xA2, 0x7E, 0x3B, 0x4E, 0xB4, 0xB4, 0xC8, 0x70, 0xAF};
static const u8 key_D91609F0[] = {0xD0, 0x36, 0x12, 0x75, 0x80, 0x56, 0x20, 0x43, 0xC4, 0x30, 0x94, 0x3E, 0x1C, 0x75, 0xD1, 0xBF};
static const u8 key_D9160AF0[] = {0x10, 0xA9, 0xAC, 0x16, 0xAE, 0x19, 0xC0, 0x7E, 0x3B, 0x60, 0x77, 0x86, 0x01, 0x6F, 0xF2, 0x63};
static const u8 key_D9160BF0[] = {0x83, 0x83, 0xF1, 0x37, 0x53, 0xD0, 0xBE, 0xFC, 0x8D, 0xA7, 0x32, 0x52, 0x46, 0x0A, 0xC2, 0xC2};
static const u8 key_D91611F0[] = {0x61, 0xB0, 0xC0, 0x58, 0x71, 0x57, 0xD9, 0xFA, 0x74, 0x67, 0x0E, 0x5C, 0x7E, 0x6E, 0x95, 0xB9};
static const u8 key_D91612F0[] = {0x9E, 0x20, 0xE1, 0xCD, 0xD7, 0x88, 0xDE, 0xC0, 0x31, 0x9B, 0x10, 0xAF, 0xC5, 0xB8, 0x73, 0x23};
static const u8 key_D91613F0[] = {0xEB, 0xFF, 0x40, 0xD8, 0xB4, 0x1A, 0xE1, 0x66, 0x91, 0x3B, 0x8F, 0x64, 0xB6, 0xFC, 0xB7, 0x12};
static const u8 key_D91614F0[] = {0xFD, 0xF7, 0xB7, 0x3C, 0x9F, 0xD1, 0x33, 0x95, 0x11, 0xB8, 0xB5, 0xBB, 0x54, 0x23, 0x73, 0x85};
static const u8 key_D91615F0[] = {0xC8, 0x03, 0xE3, 0x44, 0x50, 0xF1, 0xE7, 0x2A, 0x6A, 0x0D, 0xC3, 0x61, 0xB6, 0x8E, 0x5F, 0x51};
static const u8 key_D91616F0[] = {0x53, 0x03, 0xB8, 0x6A, 0x10, 0x19, 0x98, 0x49, 0x1C, 0xAF, 0x30, 0xE4, 0x25, 0x1B, 0x6B, 0x28};
static const u8 key_D91617F0[] = {0x02, 0xFA, 0x48, 0x73, 0x75, 0xAF, 0xAE, 0x0A, 0x67, 0x89, 0x2B, 0x95, 0x4B, 0x09, 0x87, 0xA3};
static const u8 key_D91618F0[] = {0x96, 0x96, 0x7C, 0xC3, 0xF7, 0x12, 0xDA, 0x62, 0x1B, 0xF6, 0x9A, 0x9A, 0x44, 0x44, 0xBC, 0x48};
static const u8 key_D91619F0[] = {0xE0, 0x32, 0xA7, 0x08, 0x6B, 0x2B, 0x29, 0x2C, 0xD1, 0x4D, 0x5B, 0xEE, 0xA8, 0xC8, 0xB4, 0xE9};
static const u8 key_D9161AF0[] = {0x27, 0xE5, 0xA7, 0x49, 0x52, 0xE1, 0x94, 0x67, 0x35, 0x66, 0x91, 0x0C, 0xE8, 0x9A, 0x25, 0x24};
static const u8 key_D91620F0[] = {0x52, 0x1C, 0xB4, 0x5F, 0x40, 0x3B, 0x9A, 0xDD, 0xAC, 0xFC, 0xEA, 0x92, 0xFD, 0xDD, 0xF5, 0x90};
static const u8 key_D91621F0[] = {0xD1, 0x91, 0x2E, 0xA6, 0x21, 0x14, 0x29, 0x62, 0xF6, 0xED, 0xAE, 0xCB, 0xDD, 0xA3, 0xBA, 0xFE};
static const u8 key_D91622F0[] = {0x59, 0x5D, 0x78, 0x4D, 0x21, 0xB2, 0x01, 0x17, 0x6C, 0x9A, 0xB5, 0x1B, 0xDA, 0xB7, 0xF9, 0xE6};
static const u8 key_D91623F0[] = {0xAA, 0x45, 0xEB, 0x4F, 0x62, 0xFB, 0xD1, 0x0D, 0x71, 0xD5, 0x62, 0xD2, 0xF5, 0xBF, 0xA5, 0x2F};
static const u8 key_D91624F0[] = {0x61, 0xB7, 0x26, 0xAF, 0x8B, 0xF1, 0x41, 0x58, 0x83, 0x6A, 0xC4, 0x92, 0x12, 0xCB, 0xB1, 0xE9};
static const u8 key_D91628F0[] = {0x49, 0xA4, 0xFC, 0x66, 0xDC, 0xE7, 0x62, 0x21, 0xDB, 0x18, 0xA7, 0x50, 0xD6, 0xA8, 0xC1, 0xB6};
static const u8 key_D91680F0[] = {0x2C, 0x22, 0x9B, 0x12, 0x36, 0x74, 0x11, 0x67, 0x49, 0xD1, 0xD1, 0x88, 0x92, 0xF6, 0xA1, 0xD8};
static const u8 key_D91681F0[] = {0x52, 0xB6, 0x36, 0x6C, 0x8C, 0x46, 0x7F, 0x7A, 0xCC, 0x11, 0x62, 0x99, 0xC1, 0x99, 0xBE, 0x98};
static const u8 key_2E5E10F0[] = {0x9D, 0x5C, 0x5B, 0xAF, 0x8C, 0xD8, 0x69, 0x7E, 0x51, 0x9F, 0x70, 0x96, 0xE6, 0xD5, 0xC4, 0xE8};
static const u8 key_2E5E12F0[] = {0x8A, 0x7B, 0xC9, 0xD6, 0x52, 0x58, 0x88, 0xEA, 0x51, 0x83, 0x60, 0xCA, 0x16, 0x79, 0xE2, 0x07};
static const u8 key_2E5E13F0[] = {0xFF, 0xA4, 0x68, 0xC3, 0x31, 0xCA, 0xB7, 0x4C, 0xF1, 0x23, 0xFF, 0x01, 0x65, 0x3D, 0x26, 0x36};
static const u8 key_2FD30BF0[] = {0xD8, 0x58, 0x79, 0xF9, 0xA4, 0x22, 0xAF, 0x86, 0x90, 0xAC, 0xDA, 0x45, 0xCE, 0x60, 0x40, 0x3F};
static const u8 key_2FD311F0[] = {0x3A, 0x6B, 0x48, 0x96, 0x86, 0xA5, 0xC8, 0x80, 0x69, 0x6C, 0xE6, 0x4B, 0xF6, 0x04, 0x17, 0x44};
static const u8 key_2FD312F0[] = {0xC5, 0xFB, 0x69, 0x03, 0x20, 0x7A, 0xCF, 0xBA, 0x2C, 0x90, 0xF8, 0xB8, 0x4D, 0xD2, 0xF1, 0xDE};
static const u8 keys02G_E[] = {0x9D, 0x09, 0xFD, 0x20, 0xF3, 0x8F, 0x10, 0x69, 0x0D, 0xB2, 0x6F, 0x00, 0xCC, 0xC5, 0x51, 0x2E};
static const u8 keys03G_E[] = {0x4F, 0x44, 0x5C, 0x62, 0xB3, 0x53, 0xC4, 0x30, 0xFC, 0x3A, 0xA4, 0x5B, 0xEC, 0xFE, 0x51, 0xEA};
static const u8 keys05G_E[] = {0x5D, 0xAA, 0x72, 0xF2, 0x26, 0x60, 0x4D, 0x1C, 0xE7, 0x2D, 0xC8, 0xA3, 0x2F, 0x79, 0xC5, 0x54};
static const u8 oneseg_310[] = {0xC7, 0x27, 0x72, 0x85, 0xAB, 0xA7, 0xF7, 0xF0, 0x4C, 0xC1, 0x86, 0xCC, 0xE3, 0x7F, 0x17, 0xCA};
static const u8 oneseg_300[] = {0x76, 0x40, 0x9E, 0x08, 0xDB, 0x9B, 0x3B, 0xA1, 0x47, 0x8A, 0x96, 0x8E, 0xF3, 0xF7, 0x62, 0x92};
static const u8 oneseg_280[] = {0x23, 0xDC, 0x3B, 0xB5, 0xA9, 0x82, 0xD6, 0xEA, 0x63, 0xA3, 0x6E, 0x2B, 0x2B, 0xE9, 0xE1, 0x54};
static const u8 oneseg_260_271[] = {0x22, 0x43, 0x57, 0x68, 0x2F, 0x41, 0xCE, 0x65, 0x4C, 0xA3, 0x7C, 0xC6, 0xC4, 0xAC, 0xF3, 0x60};
static const u8 oneseg_slim[] = {0x12, 0x57, 0x0D, 0x8A, 0x16, 0x6D, 0x87, 0x06, 0x03, 0x7D, 0xC8, 0x8B, 0x62, 0xA3, 0x32, 0xA9};
static const u8 ms_app_main[] = {0x1E, 0x2E, 0x38, 0x49, 0xDA, 0xD4, 0x16, 0x08, 0x27, 0x2E, 0xF3, 0xBC, 0x37, 0x75, 0x80, 0x93};
static const u8 demokeys_280[] = {0x12, 0x99, 0x70, 0x5E, 0x24, 0x07, 0x6C, 0xD0, 0x2D, 0x06, 0xFE, 0x7E, 0xB3, 0x0C, 0x11, 0x26};
static const u8 demokeys_3XX_1[] = {0x47, 0x05, 0xD5, 0xE3, 0x56, 0x1E, 0x81, 0x9B, 0x09, 0x2F, 0x06, 0xDB, 0x6B, 0x12, 0x92, 0xE0};
static const u8 demokeys_3XX_2[] = {0xF6, 0x62, 0x39, 0x6E, 0x26, 0x22, 0x4D, 0xCA, 0x02, 0x64, 0x16, 0x99, 0x7B, 0x9A, 0xE7, 0xB8};
static const u8 ebootbin_271_new[] = {0xF4, 0xAE, 0xF4, 0xE1, 0x86, 0xDD, 0xD2, 0x9C, 0x7C, 0xC5, 0x42, 0xA6, 0x95, 0xA0, 0x83, 0x88};
static const u8 ebootbin_280_new[] = {0xB8, 0x8C, 0x45, 0x8B, 0xB6, 0xE7, 0x6E, 0xB8, 0x51, 0x59, 0xA6, 0x53, 0x7C, 0x5E, 0x86, 0x31};
static const u8 ebootbin_300_new[] = {0xED, 0x10, 0xE0, 0x36, 0xC4, 0xFE, 0x83, 0xF3, 0x75, 0x70, 0x5E, 0xF6, 0xA4, 0x40, 0x05, 0xF7};
static const u8 ebootbin_310_new[] = {0x5C, 0x77, 0x0C, 0xBB, 0xB4, 0xC2, 0x4F, 0xA2, 0x7E, 0x3B, 0x4E, 0xB4, 0xB4, 0xC8, 0x70, 0xAF};
static const u8 gameshare_260_271[] = {0xF9, 0x48, 0x38, 0x0C, 0x96, 0x88, 0xA7, 0x74, 0x4F, 0x65, 0xA0, 0x54, 0xC2, 0x76, 0xD9, 0xB8};
static const u8 gameshare_280[] = {0x2D, 0x86, 0x77, 0x3A, 0x56, 0xA4, 0x4F, 0xDD, 0x3C, 0x16, 0x71, 0x93, 0xAA, 0x8E, 0x11, 0x43};
static const u8 gameshare_300[] = {0x78, 0x1A, 0xD2, 0x87, 0x24, 0xBD, 0xA2, 0x96, 0x18, 0x3F, 0x89, 0x36, 0x72, 0x90, 0x92, 0x85};
static const u8 gameshare_310[] = {0xC9, 0x7D, 0x3E, 0x0A, 0x54, 0x81, 0x6E, 0xC7, 0x13, 0x74, 0x99, 0x74, 0x62, 0x18, 0xE7, 0xDD};
static const u8 key_380210F0[] = {0x32, 0x2C, 0xFA, 0x75, 0xE4, 0x7E, 0x93, 0xEB, 0x9F, 0x22, 0x80, 0x85, 0x57, 0x08, 0x98, 0x48};
static const u8 key_380280F0[] = {0x97, 0x09, 0x12, 0xD3, 0xDB, 0x02, 0xBD, 0xD8, 0xE7, 0x74, 0x51, 0xFE, 0xF0, 0xEA, 0x6C, 0x5C};
static const u8 key_380283F0[] = {0x34, 0x20, 0x0C, 0x8E, 0xA1, 0x86, 0x79, 0x84, 0xAF, 0x13, 0xAE, 0x34, 0x77, 0x6F, 0xEA, 0x89};
static const u8 key_407810F0[] = {0xAF, 0xAD, 0xCA, 0xF1, 0x95, 0x59, 0x91, 0xEC, 0x1B, 0x27, 0xD0, 0x4E, 0x8A, 0xF3, 0x3D, 0xE7};
static const u8 drmkeys_6XX_1[] = {0x36, 0xEF, 0x82, 0x4E, 0x74, 0xFB, 0x17, 0x5B, 0x14, 0x14, 0x05, 0xF3, 0xB3, 0x8A, 0x76, 0x18};
static const u8 drmkeys_6XX_2[] = {0x21, 0x52, 0x5D, 0x76, 0xF6, 0x81, 0x0F, 0x15, 0x2F, 0x4A, 0x40, 0x89, 0x63, 0xA0, 0x10, 0x55};
// PRXDecrypter 144-byte tag keys.
u32 g_key0[] = {
static const u32 g_key0[] = {
0x7b21f3be, 0x299c5e1d, 0x1c9c5e71, 0x96cb4645, 0x3c9b1be0, 0xeb85de3d,
0x4a7f2022, 0xc2206eaa, 0xd50b3265, 0x55770567, 0x3c080840, 0x981d55f2,
0x5fd8f6f3, 0xee8eb0c5, 0x944d8152, 0xf8278651, 0x2705bafa, 0x8420e533,
0x27154ae9, 0x4819aa32, 0x59a3aa40, 0x2cb3cf65, 0xf274466d, 0x3a655605,
0x21b0f88f, 0xc5b18d26, 0x64c19051, 0xd669c94e, 0xe87035f2, 0x9d3a5909,
0x6f4e7102, 0xdca946ce, 0x8416881b, 0xbab097a5, 0x249125c6, 0xb34c0872};
u32 g_key2[] = {
static const u32 g_key2[] = {
0xccfda932, 0x51c06f76, 0x046dcccf, 0x49e1821e, 0x7d3b024c, 0x9dda5865,
0xcc8c9825, 0xd1e97db5, 0x6874d8cb, 0x3471c987, 0x72edb3fc, 0x81c8365d,
0xe161e33a, 0xfc92db59, 0x2009b1ec, 0xb1a94ce4, 0x2f03696b, 0x87e236d8,
0x3b2b8ce9, 0x0305e784, 0xf9710883, 0xb039db39, 0x893bea37, 0xe74d6805,
0x2a5c38bd, 0xb08dc813, 0x15b32375, 0x46be4525, 0x0103fd90, 0xa90e87a2,
0x52aba66a, 0x85bf7b80, 0x45e8ce63, 0x4dd716d3, 0xf5e30d2d, 0xaf3ae456};
u32 g_key3[] = {
static const u32 g_key3[] = {
0xa6c8f5ca, 0x6d67c080, 0x924f4d3a, 0x047ca06a, 0x08640297, 0x4fd4a758,
0xbd685a87, 0x9b2701c2, 0x83b62a35, 0x726b533c, 0xe522fa0c, 0xc24b06b4,
0x459d1cac, 0xa8c5417b, 0x4fea62a2, 0x0615d742, 0x30628d09, 0xc44fab14,
0x69ff715e, 0xd2d8837d, 0xbeed0b8b, 0x1e6e57ae, 0x61e8c402, 0xbe367a06,
0x543f2b5e, 0xdb3ec058, 0xbe852075, 0x1e7e4dcc, 0x1564ea55, 0xec7825b4,
0xc0538cad, 0x70f72c7f, 0x49e8c3d0, 0xeda97ec5, 0xf492b0a4, 0xe05eb02a};
u32 g_key44[] = {
static const u32 g_key44[] = {
0xef80e005, 0x3a54689f, 0x43c99ccd, 0x1b7727be, 0x5cb80038, 0xdd2efe62,
0xf369f92c, 0x160f94c5, 0x29560019, 0xbf3c10c5, 0xf2ce5566, 0xcea2c626,
0xb601816f, 0x64e7481e, 0x0c34debd, 0x98f29cb0, 0x3fc504d7, 0xc8fb39f0,
0x0221b3d8, 0x63f936a2, 0x9a3a4800, 0x6ecc32e3, 0x8e120cfd, 0xb0361623,
0xaee1e689, 0x745502eb, 0xe4a6c61c, 0x74f23eb4, 0xd7fa5813, 0xb01916eb,
0x12328457, 0xd2bc97d2, 0x646425d8, 0x328380a5, 0x43da8ab1, 0x4b122ac9};
u32 g_key20[] = {
static const u32 g_key20[] = {
0x33b50800, 0xf32f5fcd, 0x3c14881f, 0x6e8a2a95, 0x29feefd5, 0x1394eae3,
0xbd6bd443, 0x0821c083, 0xfab379d3, 0xe613e165, 0xf5a754d3, 0x108b2952,
0x0a4b1e15, 0x61eadeba, 0x557565df, 0x3b465301, 0xae54ecc3, 0x61423309,
0x70c9ff19, 0x5b0ae5ec, 0x989df126, 0x9d987a5f, 0x55bc750e, 0xc66eba27,
0x2de988e8, 0xf76600da, 0x0382dccb, 0x5569f5f2, 0x8e431262, 0x288fe3d3,
0x656f2187, 0x37d12e9c, 0x2f539eb4, 0xa492998e, 0xed3958f7, 0x39e96523};
u32 g_key3A[] = {
static const u32 g_key3A[] = {
0x67877069, 0x3abd5617, 0xc23ab1dc, 0xab57507d, 0x066a7f40, 0x24def9b9,
0x06f759e4, 0xdcf524b1, 0x13793e5e, 0x0359022d, 0xaae7e1a2, 0x76b9b2fa,
0x9a160340, 0x87822fba, 0x19e28fbb, 0x9e338a02, 0xd8007e9a, 0xea317af1,
0x630671de, 0x0b67ca7c, 0x865192af, 0xea3c3526, 0x2b448c8e, 0x8b599254,
0x4602e9cb, 0x4de16cda, 0xe164d5bb, 0x07ecd88e, 0x99ffe5f8, 0x768800c1,
0x53b091ed, 0x84047434, 0xb426dbbc, 0x36f948bb, 0x46142158, 0x749bb492};
u32 g_keyEBOOT1xx[] = {
static const u32 g_keyEBOOT1xx[] = {
0x18CB69EF, 0x158E8912, 0xDEF90EBB, 0x4CB0FB23, 0x3687EE18, 0x868D4A6E,
0x19B5C756, 0xEE16551D, 0xE7CB2D6C, 0x9747C660, 0xCE95143F, 0x2956F477,
0x03824ADE, 0x210C9DF1, 0x5029EB24, 0x81DFE69F, 0x39C89B00, 0xB00C8B91,
0xEF2DF9C2, 0xE13A93FC, 0x8B94A4A8, 0x491DD09D, 0x686A400D, 0xCED4C7E4,
0x96C8B7C9, 0x1EAADC28, 0xA4170B84, 0x505D5DDC, 0x5DA6C3CF, 0x0E5DFA2D,
0x6E7919B5, 0xCE5E29C7, 0xAAACDB94, 0x45F70CDD, 0x62A73725, 0xCCE6563D};
u32 g_keyEBOOT2xx[] = {
static const u32 g_keyEBOOT2xx[] = {
0xDA8E36FA, 0x5DD97447, 0x76C19874, 0x97E57EAF, 0x1CAB09BD, 0x9835BAC6,
0x03D39281, 0x03B205CF, 0x2882E734, 0xE714F663, 0xB96E2775, 0xBD8AAFC7,
0x1DD3EC29, 0xECA4A16C, 0x5F69EC87, 0x85981E92, 0x7CFCAE21, 0xBAE9DD16,
0xE6A97804, 0x2EEE02FC, 0x61DF8A3D, 0xDD310564, 0x9697E149, 0xC2453F3B,
0xF91D8456, 0x39DA6BC8, 0xB3E5FEF5, 0x89C593A3, 0xFB5C8ABC, 0x6C0B7212,
0xE10DD3CB, 0x98D0B2A8, 0x5FD61847, 0xF0DC2357, 0x7701166A, 0x0F5C3B68};
u32 g_keyUPDATER[] = {
static const u32 g_keyUPDATER[] = {
0xA5603CBF, 0xD7482441, 0xF65764CC, 0x1F90060B, 0x4EA73E45, 0xE551D192,
0xE7B75D8A, 0x465A506E, 0x40FB1022, 0x2C273350, 0x8096DA44, 0x9947198E,
0x278DEE77, 0x745D062E, 0xC148FA45, 0x832582AF, 0x5FDB86DA, 0xCB15C4CE,
0x2524C62F, 0x6C2EC3B1, 0x369BE39E, 0xF7EB1FC4, 0x1E51CE1A, 0xD70536F4,
0xC34D39D8, 0x7418FB13, 0xE3C84DE1, 0xB118F03C, 0xA2018D4E, 0xE6D8770D,
0x5720F390, 0x17F96341, 0x60A4A68F, 0x1327DD28, 0x05944C64, 0x0C2C4C12};
u32 g_keyMEIMG250[] = {
static const u32 g_keyMEIMG250[] = {
0xA381FEBC, 0x99B9D5C9, 0x6C560A8D, 0x30309F95, 0x792646CC, 0x82B64E5E,
0x1A3951AD, 0x0A182EC4, 0xC46131B4, 0x77C50C8A, 0x325F16C6, 0x02D1942E,
0x0AA38AC4, 0x2A940AC6, 0x67034726, 0xE52DB133, 0xD2EF2107, 0x85C81E90,
0xC8D164BA, 0xC38DCE1D, 0x948BA275, 0x0DB84603, 0xE2473637, 0xCD74FCDA,
0x588E3D66, 0x6D28E822, 0x891E548B, 0xF53CF56D, 0x0BBDDB66, 0xC4B286AA,
0x2BEBBC4B, 0xFC261FF4, 0x92B8E705, 0xDCEE6952, 0x5E0442E5, 0x8BEB7F21};
u32 g_keyMEIMG260[] = {
static const u32 g_keyMEIMG260[] = {
0x11BFD698, 0xD7F9B324, 0xDD524927, 0x16215B86, 0x504AC36D, 0x5843B217,
0xE5A0DA47, 0xBB73A1E7, 0x2915DB35, 0x375CFD3A, 0xBB70A905, 0x272BEFCA,
0x2E960791, 0xEA0799BB, 0xB85AE6C8, 0xC9CAF773, 0x250EE641, 0x06E74A9E,
0x5244895D, 0x466755A5, 0x9A84AF53, 0xE1024174, 0xEEBA031E, 0xED80B9CE,
0xBC315F72, 0x5821067F, 0xE8313058, 0xD2D0E706, 0xE6D8933E, 0xD7D17FB4,
0x505096C4, 0xFDA50B3B, 0x4635AE3D, 0xEB489C8A, 0x422D762D, 0x5A8B3231};
u32 g_keyDEMOS27X[] = {
static const u32 g_keyDEMOS27X[] = {
0x1ABF102F, 0xD596D071, 0x6FC552B2, 0xD4F2531F, 0xF025CDD9, 0xAF9AAF03,
0xE0CF57CF, 0x255494C4, 0x7003675E, 0x907BC884, 0x002D4EE4, 0x0B687A0D,
0x9E3AA44F, 0xF58FDA81, 0xEC26AC8C, 0x3AC9B49D, 0x3471C037, 0xB0F3834D,
0x10DC4411, 0xA232EA31, 0xE2E5FA6B, 0x45594B03, 0xE43A1C87, 0x31DAD9D1,
0x08CD7003, 0xFA9C2FDF, 0x5A891D25, 0x9B5C1934, 0x22F366E5, 0x5F084A32,
0x695516D5, 0x2245BE9F, 0x4F6DD705, 0xC4B8B8A1, 0xBC13A600, 0x77B7FC3B};
u32 g_keyUNK1[] = {
static const u32 g_keyUNK1[] = {
0x33B50800, 0xF32F5FCD, 0x3C14881F, 0x6E8A2A95, 0x29FEEFD5, 0x1394EAE3,
0xBD6BD443, 0x0821C083, 0xFAB379D3, 0xE613E165, 0xF5A754D3, 0x108B2952,
0x0A4B1E15, 0x61EADEBA, 0x557565DF, 0x3B465301, 0xAE54ECC3, 0x61423309,
0x70C9FF19, 0x5B0AE5EC, 0x989DF126, 0x9D987A5F, 0x55BC750E, 0xC66EBA27,
0x2DE988E8, 0xF76600DA, 0x0382DCCB, 0x5569F5F2, 0x8E431262, 0x288FE3D3,
0x656F2187, 0x37D12E9C, 0x2F539EB4, 0xA492998E, 0xED3958F7, 0x39E96523};
u32 g_key_GAMESHARE1xx[] = {
static const u32 g_key_GAMESHARE1xx[] = {
0x721B53E8, 0xFC3E31C6, 0xF85BA2A2, 0x3CF0AC72, 0x54EEA7AB, 0x5959BFCB,
0x54B8836B, 0xBC431313, 0x989EF2CF, 0xF0CE36B2, 0x98BA4CF8, 0xE971C931,
0xA0375DC8, 0x08E52FA0, 0xAC0DD426, 0x57E4D601, 0xC56E61C7, 0xEF1AB98A,
0xD1D9F8F4, 0x5FE9A708, 0x3EF09D07, 0xFA0C1A8C, 0xA91EEA5C, 0x58F482C5,
0x2C800302, 0x7EE6F6C3, 0xFF6ABBBB, 0x2110D0D0, 0xD3297A88, 0x980012D3,
0xDC59C87B, 0x7FDC5792, 0xDB3F5DA6, 0xFC23B787, 0x22698ED3, 0xB680E812};
u32 g_key_GAMESHARE2xx[] = {
static const u32 g_key_GAMESHARE2xx[] = {
0x94A757C7, 0x9FD39833, 0xF8508371, 0x328B0B29, 0x2CBCB9DA, 0x2918B9C6,
0x944C50BA, 0xF1DCE7D0, 0x640C3966, 0xC90B3D08, 0xF4AD17BA, 0x6CA0F84B,
0xF7767C67, 0xA4D3A55A, 0x4A085C6A, 0x6BB27071, 0xFA8B38FB, 0x3FDB31B8,
0x8B7196F2, 0xDB9BED4A, 0x51625B84, 0x4C1481B4, 0xF684F508, 0x30B44770,
0x93AA8E74, 0x90C579BC, 0x246EC88D, 0x2E051202, 0xC774842E, 0xA185D997,
0x7A2B3ADD, 0xFE835B6D, 0x508F184D, 0xEB4C4F13, 0x0E1993D3, 0xBA96DFD2};
u32 g_key_INDEXDAT1xx[] = {
static const u32 g_key_INDEXDAT1xx[] = {
0x76CB00AF, 0x111CE62F, 0xB7B27E36, 0x6D8DE8F9, 0xD54BF16A, 0xD9E90373,
0x7599D982, 0x51F82B0E, 0x636103AD, 0x8E40BC35, 0x2F332C94, 0xF513AAE9,
0xD22AFEE9, 0x04343987, 0xFC5BB80C, 0x12349D89, 0x14A481BB, 0x25ED3AE8,
@ -259,13 +262,13 @@ u32 g_key_INDEXDAT1xx[] = {
0xA34D8C80, 0x962B235D, 0x3E420548, 0x09CF9FFE, 0xD4883F5C, 0xD90E9CB5,
0x00AEF4E9, 0xF0886DE9, 0x62A58A5B, 0x52A55546, 0x971941B5, 0xF5B79FAC};
typedef struct
struct TAG_INFO
{
u32 tag; // 4 byte value at offset 0xD0 in the PRX file
u32* key; // "step1_result" use for XOR step
const u32 *key; // "step1_result" use for XOR step
u8 code;
u8 codeExtra;
} TAG_INFO;
};
static const TAG_INFO g_tagInfo[] =
{
@ -287,7 +290,23 @@ static const TAG_INFO g_tagInfo[] =
{ 0xBB67C59F, g_key_GAMESHARE2xx, 0x5E, 0x5E }
};
static TAG_INFO const* GetTagInfo(u32 tagFind)
bool HasKey(int key)
{
switch (key)
{
case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: case 0x0C: case 0x0D: case 0x0E: case 0x0F:
case 0x10: case 0x11: case 0x12:
case 0x38: case 0x39: case 0x3A: case 0x44: case 0x4B:
case 0x53: case 0x57: case 0x5D:
case 0x63: case 0x64:
return true;
default:
INFO_LOG(HLE, "Missing key %02X, cannot decrypt module", key);
return false;
}
}
static const TAG_INFO *GetTagInfo(u32 tagFind)
{
for (u32 iTag = 0; iTag < sizeof(g_tagInfo)/sizeof(TAG_INFO); iTag++)
if (g_tagInfo[iTag].tag == tagFind)
@ -297,21 +316,17 @@ static TAG_INFO const* GetTagInfo(u32 tagFind)
static void ExtraV2Mangle(u8* buffer1, u8 codeExtra)
{
#ifdef _MSC_VER
static u8 __declspec(align(64)) g_dataTmp[20+0xA0];
#else
static u8 g_dataTmp[20+0xA0] __attribute__((aligned(0x40)));
#endif
u8* buffer2 = g_dataTmp; // aligned
u8 buffer2[ROUNDUP16(0x14+0xA0)];
memcpy(buffer2+0x14, buffer1, 0xA0);
memcpy(buffer2+20, buffer1, 0xA0);
u32* pl2 = (u32*)buffer2;
pl2[0] = 5;
pl2[1] = pl2[2] = 0;
pl2[3] = codeExtra;
pl2[4] = 0xA0;
sceUtilsBufferCopyWithRange(buffer2, 20+0xA0, buffer2, 20+0xA0, 7);
sceUtilsBufferCopyWithRange(buffer2, 20+0xA0, buffer2, 20+0xA0, KIRK_CMD_DECRYPT_IV_0);
// copy result back
memcpy(buffer1, buffer2, 0xA0);
}
@ -323,7 +338,7 @@ static int Scramble(u32 *buf, u32 size, u32 code)
buf[3] = code;
buf[4] = size;
if (sceUtilsBufferCopyWithRange((u8*)buf, size+0x14, (u8*)buf, size+0x14, 7) < 0)
if (sceUtilsBufferCopyWithRange((u8*)buf, size+0x14, (u8*)buf, size+0x14, KIRK_CMD_DECRYPT_IV_0) < 0)
{
return -1;
}
@ -336,11 +351,16 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag)
int i, retsize;
u8 bD0[0x80], b80[0x50], b00[0x80], bB0[0x20];
TAG_INFO const* pti = GetTagInfo(tag);
const TAG_INFO *pti = GetTagInfo(tag);
if (pti == NULL)
{
return -1;
}
if (!HasKey(pti->code) ||
(pti->codeExtra != 0 && !HasKey(pti->codeExtra)))
{
return MISSING_KEY;
}
retsize = *(u32*)&pbIn[0xB0];
@ -350,9 +370,15 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag)
break;
}
// Scramble the key (!)
//
// NOTE: I can't make much sense out of this code. Scramble seems really odd, appears
// to write to stuff that should be before the actual key.
u8 key[0x90];
memcpy(key, pti->key, 0x90);
if (i == 0x14)
{
Scramble(pti->key, 0x90, pti->code);
Scramble((u32 *)key, 0x90, pti->code);
}
// build conversion into pbOut
@ -387,7 +413,7 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag)
int ret;
int iXOR;
for (iXOR = 0; iXOR < 0x70; iXOR++)
pbOut[0x40+iXOR] = pbOut[0x40+iXOR] ^ ((u8*)pti->key)[0x14+iXOR];
pbOut[0x40+iXOR] = pbOut[0x40+iXOR] ^ key[0x14+iXOR];
ret = sceUtilsBufferCopyWithRange(pbOut+0x2C, 20+0x70, pbOut+0x2C, 20+0x70, 7);
if (ret != 0)
@ -396,7 +422,7 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag)
}
for (iXOR = 0x6F; iXOR >= 0; iXOR--)
pbOut[0x40+iXOR] = pbOut[0x2C+iXOR] ^ ((u8*)pti->key)[0x20+iXOR];
pbOut[0x40+iXOR] = pbOut[0x2C+iXOR] ^ key[0x20+iXOR];
memset(pbOut+0x80, 0, 0x30); // $40 bytes kept, clean up
pbOut[0xA0] = 1;
@ -418,15 +444,15 @@ static int DecryptPRX1(const u8* pbIn, u8* pbOut, int cbTotal, u32 tag)
////////// Decryption 2 //////////
typedef struct
struct TAG_INFO2
{
u32 tag; // 4 byte value at offset 0xD0 in the PRX file
u8 *key; // 16 bytes keys
const u8 *key; // 16 bytes keys
u8 code; // code for scramble
u8 type;
} TAG_INFO2;
};
static TAG_INFO2 g_tagInfo2[] =
static const TAG_INFO2 g_tagInfo2[] =
{
{ 0x4C9494F0, keys660_k1, 0x43 },
{ 0x4C9495F0, keys660_k2, 0x43 },
@ -562,7 +588,7 @@ static TAG_INFO2 g_tagInfo2[] =
};
static TAG_INFO2 *GetTagInfo2(u32 tagFind)
static const TAG_INFO2 *GetTagInfo2(u32 tagFind)
{
for (u32 iTag = 0; iTag < sizeof(g_tagInfo2) / sizeof(TAG_INFO2); iTag++)
{
@ -575,26 +601,25 @@ static TAG_INFO2 *GetTagInfo2(u32 tagFind)
return NULL; // not found
}
// Moving these out here is a really ugly hack to avoid a stack corruption warning, warranted or not
static u8 padding1[0x100];
static u8 tmp1[0x150], tmp2[0x90+0x14], tmp3[0x60+0x14], tmp4[0x20];
static u8 padding2[0x100];
static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag)
{
TAG_INFO2 * pti = GetTagInfo2(tag);
const TAG_INFO2 *pti = GetTagInfo2(tag);
if (!pti)
{
return -1;
}
if (!HasKey(pti->code))
{
return MISSING_KEY;
}
int retsize = *(int *)&inbuf[0xB0];
memset(tmp1, 0, 0x150);
memset(tmp2, 0, 0x90+0x14);
memset(tmp3, 0, 0x60+0x14);
memset(tmp4, 0, 0x20);
int retsize = *(const int *)&inbuf[0xB0];
u8 tmp1[0x150] = {0};
u8 tmp2[ROUNDUP16(0x90+0x14)] = {0};
u8 tmp3[ROUNDUP16(0x90+0x14)] = {0};
u8 tmp4[ROUNDUP16(0x20)] = {0};
if (inbuf != outbuf)
memcpy(outbuf, inbuf, size);
@ -604,7 +629,7 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag)
return -2;
}
if ((size - 0x150) < retsize)
if (((int)size - 0x150) < retsize)
{
return -4;
}
@ -612,17 +637,16 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag)
memcpy(tmp1, outbuf, 0x150);
int i, j;
u8 *p = tmp2+0x14;
u8 *p = tmp2 + 0x14;
// Writes 0x90 bytes to tmp2 + 0x14.
for (i = 0; i < 9; i++)
{
for (j = 0; j < 0x10; j++)
{
p[(i << 4) + j] = pti->key[j];
}
p[(i << 4)] = i;
p[(i << 4)] = i; // really? this is very odd
}
if (Scramble((u32 *)tmp2, 0x90, pti->code) < 0)
@ -673,9 +697,7 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag)
return -8;
}
int iXOR;
for (iXOR = 0; iXOR < 0x40; iXOR++)
for (int iXOR = 0; iXOR < 0x40; iXOR++)
{
tmp3[iXOR+0x14] = outbuf[iXOR+0x80] ^ tmp2[iXOR+0x10];
}
@ -685,7 +707,7 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag)
return -9;
}
for (iXOR = 0x3F; iXOR >= 0; iXOR--)
for (int iXOR = 0x3F; iXOR >= 0; iXOR--)
{
outbuf[iXOR+0x40] = tmp3[iXOR] ^ tmp2[iXOR+0x50]; // uns 8
}
@ -708,7 +730,7 @@ static int DecryptPRX2(const u8 *inbuf, u8 *outbuf, u32 size, u32 tag)
memcpy(outbuf+0xD0, outbuf+0xD0, 0x80);
// The real decryption
if (sceUtilsBufferCopyWithRange(outbuf, size, outbuf+0x40, size-0x40, 0x1) != 0)
if (sceUtilsBufferCopyWithRange(outbuf, size, outbuf + 0x40, size - 0x40, 0x1) != 0)
{
return -1;
}
@ -726,6 +748,10 @@ int pspDecryptPRX(const u8 *inbuf, u8 *outbuf, u32 size)
{
kirk_init();
int retsize = DecryptPRX1(inbuf, outbuf, size, *(u32 *)&inbuf[0xD0]);
if (retsize == MISSING_KEY)
{
return MISSING_KEY;
}
if (retsize <= 0)
{

View File

@ -19,6 +19,8 @@
#include "../../Globals.h"
#define MISSING_KEY -10
#ifdef _MSC_VER
#pragma pack(push, 1)
#endif

View File

@ -122,11 +122,11 @@ bool CISOFileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr)
{
u32 idx = index[blockNumber];
u32 idx2 = index[blockNumber+1];
u8 inbuffer[4096]; //too big
z_stream z;
u8 inbuffer[4096]; //too big
z_stream z;
int plain = idx & 0x80000000;
idx = (idx & 0x7FFFFFFF) << indexShift;
idx2 = (idx2 & 0x7FFFFFFF) << indexShift;

View File

@ -38,28 +38,32 @@ public:
class CISOFileBlockDevice : public BlockDevice
{
public:
CISOFileBlockDevice(std::string _filename);
~CISOFileBlockDevice();
bool ReadBlock(int blockNumber, u8 *outPtr);
int GetNumBlocks() { return numBlocks;}
private:
std::string filename;
FILE *f;
u32 *index;
int indexShift;
u32 blockSize;
int numBlocks;
public:
CISOFileBlockDevice(std::string _filename);
~CISOFileBlockDevice();
bool ReadBlock(int blockNumber, u8 *outPtr);
int GetNumBlocks() { return numBlocks;}
};
class FileBlockDevice : public BlockDevice
{
std::string filename;
FILE *f;
size_t filesize;
public:
FileBlockDevice(std::string _filename);
~FileBlockDevice();
bool ReadBlock(int blockNumber, u8 *outPtr);
int GetNumBlocks() {return (int)(filesize/GetBlockSize());}
int GetNumBlocks() {return (int)(filesize / GetBlockSize());}
private:
std::string filename;
FILE *f;
size_t filesize;
};

View File

@ -18,29 +18,142 @@
#ifdef _WIN32
#include <windows.h>
#else
#include <dirent.h>
#include <unistd.h>
#include <sys/stat.h>
#include <ctype.h>
#endif
#include "FileUtil.h"
#include "DirectoryFileSystem.h"
DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath) : basePath(_basePath)
#if HOST_IS_CASE_SENSITIVE
static bool FixFilenameCase(const std::string &path, std::string &filename)
{
// Are we lucky?
if (File::Exists(path + filename))
return true;
size_t filenameSize = filename.size();
for (size_t i = 0; i < filenameSize; i++)
{
filename[i] = tolower(filename[i]);
}
//TODO: lookup filename in cache for "path"
struct dirent_large { struct dirent entry; char padding[FILENAME_MAX+1]; } diren;
struct dirent_large;
struct dirent *result = NULL;
DIR *dirp = opendir(path.c_str());
if (!dirp)
return false;
bool retValue = false;
while (!readdir_r(dirp, (dirent*) &diren, &result) && result)
{
// Hm, is this check UTF-8 compatible? (size vs strlen)
if (strlen(result->d_name) != filenameSize)
continue;
size_t i;
for (i = 0; i < filenameSize; i++)
{
if (filename[i] != tolower(result->d_name[i]))
break;
}
if (i < filenameSize)
continue;
filename = result->d_name;
retValue = true;
}
closedir(dirp);
return retValue;
}
bool DirectoryFileSystem::FixPathCase(std::string &path, FixPathCaseBehavior behavior)
{
size_t len = path.size();
if (len == 0)
return true;
if (path[len - 1] == '/')
{
len--;
if (len == 0)
return true;
}
std::string fullPath;
fullPath.reserve(basePath.size() + len + 1);
fullPath.append(basePath);
size_t start = 0;
while (start < len)
{
size_t i = path.find('/', start);
if (i == std::string::npos)
i = len;
if (i > start)
{
std::string component = path.substr(start, i - start);
// Fix case and stop on nonexistant path component
if (FixFilenameCase(fullPath, component) == false) {
// Still counts as success if partial matches allowed or if this
// is the last component and only the ones before it are required
return (behavior == FPC_PARTIAL_ALLOWED || (behavior == FPC_PATH_MUST_EXIST && i >= len));
}
path.replace(start, i - start, component);
fullPath.append(component);
fullPath.append(1, '/');
}
start = i + 1;
}
return true;
}
#endif
DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath) : basePath(_basePath) {
File::CreateFullPath(basePath);
hAlloc = _hAlloc;
}
std::string DirectoryFileSystem::GetLocalPath(std::string localpath)
{
DirectoryFileSystem::~DirectoryFileSystem() {
for (auto iter = entries.begin(); iter != entries.end(); ++iter) {
#ifdef _WIN32
CloseHandle((*iter).second.hFile);
#else
fclose((*iter).second.hFile);
#endif
}
}
std::string DirectoryFileSystem::GetLocalPath(std::string localpath) {
if (localpath.empty())
return basePath;
if (localpath[0] == '/')
localpath.erase(0,1);
//Convert slashes
//Convert slashes
#ifdef _WIN32
for (size_t i = 0; i < localpath.size(); i++)
{
for (size_t i = 0; i < localpath.size(); i++) {
if (localpath[i] == '/')
localpath[i] = '\\';
}
@ -48,104 +161,225 @@ std::string DirectoryFileSystem::GetLocalPath(std::string localpath)
return basePath + localpath;
}
bool DirectoryFileSystem::MkDir(const std::string &dirname) {
bool DirectoryFileSystem::MkDir(const std::string &dirname)
{
std::string fullName = GetLocalPath(dirname);
#ifdef _WIN32
return CreateDirectory(fullName.c_str(), NULL) == TRUE;
#if HOST_IS_CASE_SENSITIVE
// Must fix case BEFORE attempting, because MkDir would create
// duplicate (different case) directories
std::string fixedCase = dirname;
if ( ! FixPathCase(fixedCase, FPC_PARTIAL_ALLOWED) )
return false;
return File::CreateFullPath(GetLocalPath(fixedCase));
#else
mkdir(fullName.c_str(), 0777);
return true;
return File::CreateFullPath(GetLocalPath(dirname));
#endif
}
bool DirectoryFileSystem::RmDir(const std::string &dirname)
{
bool DirectoryFileSystem::RmDir(const std::string &dirname) {
std::string fullName = GetLocalPath(dirname);
#ifdef _WIN32
#if HOST_IS_CASE_SENSITIVE
// Maybe we're lucky?
if (File::DeleteDirRecursively(fullName))
return true;
// Nope, fix case and try again
fullName = dirname;
if ( ! FixPathCase(fullName, FPC_FILE_MUST_EXIST) )
return false; // or go on and attempt (for a better error code than just false?)
fullName = GetLocalPath(fullName);
#endif
/*#ifdef _WIN32
return RemoveDirectory(fullName.c_str()) == TRUE;
#else
return 0 == rmdir(fullName.c_str());
#endif
#endif*/
return File::DeleteDirRecursively(fullName);
}
bool DirectoryFileSystem::RenameFile(const std::string &from, const std::string &to)
{
std::string fullFrom = GetLocalPath(from);
bool DirectoryFileSystem::RenameFile(const std::string &from, const std::string &to) {
std::string fullTo = to;
// TO filename may not include path. Intention is that it uses FROM's path
if (to.find("/") != std::string::npos) {
int offset = from.find_last_of("/");
if (offset >= 0) {
size_t offset = from.find_last_of("/");
if (offset != std::string::npos) {
fullTo = from.substr(0, offset + 1) + to;
}
}
std::string fullFrom = GetLocalPath(from);
#if HOST_IS_CASE_SENSITIVE
// In case TO should overwrite a file with different case
if ( ! FixPathCase(fullTo, FPC_PATH_MUST_EXIST) )
return false; // or go on and attempt (for a better error code than just false?)
#endif
fullTo = GetLocalPath(fullTo);
const char * fullToC = fullTo.c_str();
#ifdef _WIN32
return MoveFile(fullFrom.c_str(), fullTo.c_str()) == TRUE;
bool retValue = (MoveFile(fullFrom.c_str(), fullToC) == TRUE);
#else
return 0 == rename(fullFrom.c_str(), fullTo.c_str());
bool retValue = (0 == rename(fullFrom.c_str(), fullToC));
#endif
#if HOST_IS_CASE_SENSITIVE
if (! retValue)
{
// May have failed due to case sensitivity on FROM, so try again
fullFrom = from;
if ( ! FixPathCase(fullFrom, FPC_FILE_MUST_EXIST) )
return false; // or go on and attempt (for a better error code than just false?)
fullFrom = GetLocalPath(fullFrom);
#ifdef _WIN32
retValue = (MoveFile(fullFrom.c_str(), fullToC) == TRUE);
#else
retValue = (0 == rename(fullFrom.c_str(), fullToC));
#endif
}
#endif
return retValue;
}
bool DirectoryFileSystem::DeleteFile(const std::string &filename)
{
bool DirectoryFileSystem::DeleteFile(const std::string &filename) {
std::string fullName = GetLocalPath(filename);
#ifdef _WIN32
return DeleteFile(fullName.c_str()) == TRUE;
bool retValue = (::DeleteFile(fullName.c_str()) == TRUE);
#else
return 0 == unlink(fullName.c_str());
bool retValue = (0 == unlink(fullName.c_str()));
#endif
#if HOST_IS_CASE_SENSITIVE
if (! retValue)
{
// May have failed due to case sensitivity, so try again
fullName = filename;
if ( ! FixPathCase(fullName, FPC_FILE_MUST_EXIST) )
return false; // or go on and attempt (for a better error code than just false?)
fullName = GetLocalPath(fullName);
#ifdef _WIN32
retValue = (::DeleteFile(fullName.c_str()) == TRUE);
#else
retValue = (0 == unlink(fullName.c_str()));
#endif
}
#endif
return retValue;
}
u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access)
{
u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access) {
#if HOST_IS_CASE_SENSITIVE
if (access & (FILEACCESS_APPEND|FILEACCESS_CREATE|FILEACCESS_WRITE))
{
DEBUG_LOG(HLE, "Checking case for path %s", filename.c_str());
if ( ! FixPathCase(filename, FPC_PATH_MUST_EXIST) )
return 0; // or go on and attempt (for a better error code than just 0?)
}
// else we try fopen first (in case we're lucky) before simulating case insensitivity
#endif
std::string fullName = GetLocalPath(filename);
INFO_LOG(HLE,"Actually opening %s (%s)", fullName.c_str(), filename.c_str());
const char *fullNameC = fullName.c_str();
INFO_LOG(HLE,"Actually opening %s (%s)", fullNameC, filename.c_str());
OpenFileEntry entry;
//TODO: tests, should append seek to end of file? seeking in a file opened for append?
#ifdef _WIN32
// Convert parameters to Windows permissions and access
DWORD desired = 0;
DWORD sharemode = 0;
DWORD openmode = 0;
if (access & FILEACCESS_READ)
{
if (access & FILEACCESS_READ) {
desired |= GENERIC_READ;
sharemode |= FILE_SHARE_READ;
}
if (access & FILEACCESS_WRITE)
{
if (access & FILEACCESS_WRITE) {
desired |= GENERIC_WRITE;
sharemode |= FILE_SHARE_WRITE;
}
if (access & FILEACCESS_CREATE)
{
if (access & FILEACCESS_CREATE) {
openmode = OPEN_ALWAYS;
}
else
} else {
openmode = OPEN_EXISTING;
}
//Let's do it!
entry.hFile = CreateFile(fullName.c_str(), desired, sharemode, 0, openmode, 0, 0);
entry.hFile = CreateFile(fullNameC, desired, sharemode, 0, openmode, 0, 0);
bool success = entry.hFile != INVALID_HANDLE_VALUE;
#else
entry.hFile = fopen(fullName.c_str(), access & FILEACCESS_WRITE ? "wb" : "rb");
bool success = entry.hFile != 0;
// Convert flags in access parameter to fopen access mode
const char *mode = NULL;
if (access & FILEACCESS_APPEND) {
if (access & FILEACCESS_READ)
mode = "ab+"; // append+read, create if needed
else
mode = "ab"; // append only, create if needed
} else if (access & FILEACCESS_WRITE) {
if (access & FILEACCESS_READ) {
// FILEACCESS_CREATE is ignored for read only, write only, and append
// because C++ standard fopen's nonexistant file creation can only be
// customized for files opened read+write
if (access & FILEACCESS_CREATE)
mode = "wb+"; // read+write, create if needed
else
mode = "rb+"; // read+write, but don't create
} else {
mode = "wb"; // write only, create if needed
}
} else { // neither write nor append, so default to read only
mode = "rb"; // read only, don't create
}
entry.hFile = fopen(fullNameC, mode);
bool success = entry.hFile != 0;
#endif
if (!success)
#if HOST_IS_CASE_SENSITIVE
if (!success &&
!(access & FILEACCESS_APPEND) &&
!(access & FILEACCESS_CREATE) &&
!(access & FILEACCESS_WRITE))
{
if ( ! FixPathCase(filename, FPC_PATH_MUST_EXIST) )
return 0; // or go on and attempt (for a better error code than just 0?)
fullName = GetLocalPath(filename);
fullNameC = fullName.c_str();
DEBUG_LOG(HLE, "Case may have been incorrect, second try opening %s (%s)", fullNameC, filename.c_str());
// And try again with the correct case this time
#ifdef _WIN32
ERROR_LOG(HLE, "DirectoryFileSystem::OpenFile: FAILED, %i", GetLastError());
entry.hFile = CreateFile(fullNameC, desired, sharemode, 0, openmode, 0, 0);
success = entry.hFile != INVALID_HANDLE_VALUE;
#else
entry.hFile = fopen(fullNameC, mode);
success = entry.hFile != 0;
#endif
}
#endif
if (!success) {
#ifdef _WIN32
ERROR_LOG(HLE, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", GetLastError(), (int)access);
#else
ERROR_LOG(HLE, "DirectoryFileSystem::OpenFile: FAILED, access = %i", (int)access);
#endif
//wwwwaaaaahh!!
return 0;
}
else
{
} else {
#ifdef _WIN32
if (access & FILEACCESS_APPEND)
SetFilePointer(entry.hFile, 0, NULL, FILE_END);
#endif
u32 newHandle = hAlloc->GetNewHandle();
entries[newHandle] = entry;
@ -153,55 +387,46 @@ u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access)
}
}
void DirectoryFileSystem::CloseFile(u32 handle)
{
void DirectoryFileSystem::CloseFile(u32 handle) {
EntryMap::iterator iter = entries.find(handle);
if (iter != entries.end())
{
if (iter != entries.end()) {
hAlloc->FreeHandle(handle);
#ifdef _WIN32
CloseHandle((*iter).second.hFile);
#else
fclose((*iter).second.hFile);
fclose((*iter).second.hFile);
#endif
entries.erase(iter);
}
else
{
} else {
//This shouldn't happen...
ERROR_LOG(HLE,"Cannot close file that hasn't been opened: %08x", handle);
}
}
bool DirectoryFileSystem::OwnsHandle(u32 handle)
{
bool DirectoryFileSystem::OwnsHandle(u32 handle) {
EntryMap::iterator iter = entries.find(handle);
return (iter != entries.end());
}
size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
{
size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
EntryMap::iterator iter = entries.find(handle);
if (iter != entries.end())
{
size_t bytesRead;
size_t bytesRead;
#ifdef _WIN32
::ReadFile(iter->second.hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesRead, 0);
#else
bytesRead = fread(pointer, 1, size, iter->second.hFile);
bytesRead = fread(pointer, 1, size, iter->second.hFile);
#endif
return bytesRead;
}
else
{
} else {
//This shouldn't happen...
ERROR_LOG(HLE,"Cannot read file that hasn't been opened: %08x", handle);
return 0;
}
}
size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
{
size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) {
EntryMap::iterator iter = entries.find(handle);
if (iter != entries.end())
{
@ -209,81 +434,88 @@ size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)
#ifdef _WIN32
::WriteFile(iter->second.hFile, (LPVOID)pointer, (DWORD)size, (LPDWORD)&bytesWritten, 0);
#else
bytesWritten = fwrite(pointer, 1, size, iter->second.hFile);
bytesWritten = fwrite(pointer, 1, size, iter->second.hFile);
#endif
return bytesWritten;
}
else
{
} else {
//This shouldn't happen...
ERROR_LOG(HLE,"Cannot write to file that hasn't been opened: %08x", handle);
return 0;
}
}
size_t DirectoryFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
{
size_t DirectoryFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {
EntryMap::iterator iter = entries.find(handle);
if (iter != entries.end())
{
if (iter != entries.end()) {
#ifdef _WIN32
DWORD moveMethod = 0;
switch (type)
{
case FILEMOVE_BEGIN: moveMethod = FILE_BEGIN; break;
case FILEMOVE_CURRENT: moveMethod = FILE_CURRENT; break;
case FILEMOVE_END: moveMethod = FILE_END; break;
switch (type) {
case FILEMOVE_BEGIN: moveMethod = FILE_BEGIN; break;
case FILEMOVE_CURRENT: moveMethod = FILE_CURRENT; break;
case FILEMOVE_END: moveMethod = FILE_END; break;
}
DWORD newPos = SetFilePointer((*iter).second.hFile, (LONG)position, 0, moveMethod);
return newPos;
return newPos;
#else
int moveMethod = 0;
switch (type) {
case FILEMOVE_BEGIN: moveMethod = SEEK_SET; break;
case FILEMOVE_CURRENT: moveMethod = SEEK_CUR; break;
case FILEMOVE_END: moveMethod = SEEK_END; break;
}
fseek(iter->second.hFile, position, moveMethod);
int moveMethod = 0;
switch (type) {
case FILEMOVE_BEGIN: moveMethod = SEEK_SET; break;
case FILEMOVE_CURRENT: moveMethod = SEEK_CUR; break;
case FILEMOVE_END: moveMethod = SEEK_END; break;
}
fseek(iter->second.hFile, position, moveMethod);
return ftell(iter->second.hFile);
#endif
}
else
{
} else {
//This shouldn't happen...
ERROR_LOG(HLE,"Cannot seek in file that hasn't been opened: %08x", handle);
return 0;
}
}
PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename)
{
PSPFileInfo x;
PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) {
PSPFileInfo x;
x.name = filename;
std::string fullName = GetLocalPath(filename);
if (!File::Exists(fullName)) {
if (! File::Exists(fullName)) {
#if HOST_IS_CASE_SENSITIVE
if (! FixPathCase(filename, FPC_FILE_MUST_EXIST))
return x;
fullName = GetLocalPath(filename);
if (! File::Exists(fullName))
return x;
#else
return x;
#endif
}
x.type = File::IsDirectory(fullName) ? FILETYPE_NORMAL : FILETYPE_DIRECTORY;
x.type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
x.exists = true;
if (x.type != FILETYPE_DIRECTORY)
{
#ifdef _WIN32
WIN32_FILE_ATTRIBUTE_DATA data;
GetFileAttributesEx(fullName.c_str(), GetFileExInfoStandard, &data);
WIN32_FILE_ATTRIBUTE_DATA data;
GetFileAttributesEx(fullName.c_str(), GetFileExInfoStandard, &data);
x.size = data.nFileSizeLow | ((u64)data.nFileSizeHigh<<32);
x.size = data.nFileSizeLow | ((u64)data.nFileSizeHigh<<32);
#else
x.size = File::GetSize(fullName);
//TODO
x.size = File::GetSize(fullName);
//TODO
#endif
x.mtime = File::GetModifTime(fullName);
}
return x;
}
std::vector<PSPFileInfo> DirectoryFileSystem::GetDirListing(std::string path)
{
bool DirectoryFileSystem::GetHostPath(const std::string &inpath, std::string &outpath) {
outpath = GetLocalPath(inpath);
return true;
}
std::vector<PSPFileInfo> DirectoryFileSystem::GetDirListing(std::string path) {
std::vector<PSPFileInfo> myVector;
#ifdef _WIN32
WIN32_FIND_DATA findData;
@ -293,33 +525,55 @@ std::vector<PSPFileInfo> DirectoryFileSystem::GetDirListing(std::string path)
hFind = FindFirstFile(w32path.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE)
{
if (hFind == INVALID_HANDLE_VALUE) {
return myVector; //the empty list
}
while (true)
{
while (true) {
PSPFileInfo entry;
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
entry.type = FILETYPE_DIRECTORY;
else
entry.type = FILETYPE_NORMAL;
if (!strcmp(findData.cFileName, "..") )// TODO: is this just for .. or all sub directories? Need to add a directory to the test to find out. Also why so different than the old test results?
// TODO: is this just for .. or all subdirectories? Need to add a directory to the test
// to find out. Also why so different than the old test results?
if (!strcmp(findData.cFileName, "..") )
entry.size = 4096;
else
entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32);
entry.name = findData.cFileName;
myVector.push_back(entry);
int retval = FindNextFile(hFind, &findData);
if (!retval)
break;
}
#else
DIR *dp;
dirent *dirp;
if((dp = opendir(GetLocalPath(path).c_str())) == NULL) {
ERROR_LOG(HLE,"Error opening directory %s\n",path.c_str());
return myVector;
}
while ((dirp = readdir(dp)) != NULL) {
PSPFileInfo entry;
struct stat s;
stat(dirp->d_name, &s);
if (S_ISDIR(s.st_mode))
entry.type = FILETYPE_DIRECTORY;
else
entry.type = FILETYPE_NORMAL;
entry.name = dirp->d_name;
myVector.push_back(entry);
}
closedir(dp);
#endif
return myVector;
}
void DirectoryFileSystem::DoState(PointerWrap &p) {
if (!entries.empty()) {
ERROR_LOG(FILESYS, "FIXME: Open files during savestate, could go badly.");
}
}

View File

@ -28,29 +28,32 @@
typedef void * HANDLE;
#endif
#if defined(__APPLE__)
class DirectoryFileSystem : public IFileSystem
{
struct OpenFileEntry
{
#ifdef _WIN32
HANDLE hFile;
#if TARGET_OS_IPHONE
#define HOST_IS_CASE_SENSITIVE 1
#elif TARGET_IPHONE_SIMULATOR
#define HOST_IS_CASE_SENSITIVE 0
#else
FILE *hFile;
// Mac OSX case sensitivity defaults off, but is user configurable (when
// creating a filesytem), so assume the worst:
#define HOST_IS_CASE_SENSITIVE 1
#endif
};
typedef std::map<u32,OpenFileEntry> EntryMap;
EntryMap entries;
std::string basePath;
IHandleAllocator *hAlloc;
#elif defined(_WIN32) || defined(__SYMBIAN32__)
#define HOST_IS_CASE_SENSITIVE 0
#else // Android, Linux, BSD (and the rest?)
#define HOST_IS_CASE_SENSITIVE 1
// In case of Windows: Translate slashes, etc.
std::string GetLocalPath(std::string localpath);
#endif
class DirectoryFileSystem : public IFileSystem {
public:
DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath);
~DirectoryFileSystem();
void DoState(PointerWrap &p);
std::vector<PSPFileInfo> GetDirListing(std::string path);
u32 OpenFile(std::string filename, FileAccess access);
void CloseFile(u32 handle);
@ -59,9 +62,36 @@ public:
size_t SeekFile(u32 handle, s32 position, FileMove type);
PSPFileInfo GetFileInfo(std::string filename);
bool OwnsHandle(u32 handle);
bool MkDir(const std::string &dirname);
bool RmDir(const std::string &dirname);
bool RenameFile(const std::string &from, const std::string &to);
bool DeleteFile(const std::string &filename);
bool GetHostPath(const std::string &inpath, std::string &outpath);
private:
struct OpenFileEntry {
#ifdef _WIN32
HANDLE hFile;
#else
FILE *hFile;
#endif
};
typedef std::map<u32, OpenFileEntry> EntryMap;
EntryMap entries;
std::string basePath;
IHandleAllocator *hAlloc;
// In case of Windows: Translate slashes, etc.
std::string GetLocalPath(std::string localpath);
#if HOST_IS_CASE_SENSITIVE
typedef enum {
FPC_FILE_MUST_EXIST, // all path components must exist (rmdir, move from)
FPC_PATH_MUST_EXIST, // all except the last one must exist - still tries to fix last one (fopen, move to)
FPC_PARTIAL_ALLOWED, // don't care how many exist (mkdir recursive)
} FixPathCaseBehavior;
bool FixPathCase(std::string &path, FixPathCaseBehavior behavior);
#endif
};

View File

@ -18,6 +18,7 @@
#pragma once
#include "../../Globals.h"
#include "../../Common/ChunkFile.h"
#include <string>
enum FileAccess
@ -56,12 +57,28 @@ struct PSPFileInfo
PSPFileInfo()
: size(0), access(0), exists(false), type(FILETYPE_NORMAL), isOnSectorSystem(false), startSector(0), numSectors(0) {}
void DoState(PointerWrap &p)
{
p.Do(name);
p.Do(size);
p.Do(access);
p.Do(exists);
p.Do(type);
p.Do(mtime);
p.Do(isOnSectorSystem);
p.Do(startSector);
p.Do(numSectors);
p.DoMarker("PSPFileInfo");
}
std::string name;
s64 size;
u32 access; //unix 777
bool exists;
FileType type;
tm mtime;
bool isOnSectorSystem;
u32 startSector;
u32 numSectors;
@ -73,6 +90,7 @@ class IFileSystem
public:
virtual ~IFileSystem() {}
virtual void DoState(PointerWrap &p) = 0;
virtual std::vector<PSPFileInfo> GetDirListing(std::string path) = 0;
virtual u32 OpenFile(std::string filename, FileAccess access) = 0;
virtual void CloseFile(u32 handle) = 0;
@ -85,12 +103,14 @@ public:
virtual bool RmDir(const std::string &dirname) = 0;
virtual bool RenameFile(const std::string &from, const std::string &to) = 0;
virtual bool DeleteFile(const std::string &filename) = 0;
virtual bool GetHostPath(const std::string &inpath, std::string &outpath) = 0;
};
class EmptyFileSystem : public IFileSystem
{
public:
virtual void DoState(PointerWrap &p) {}
std::vector<PSPFileInfo> GetDirListing(std::string path) {std::vector<PSPFileInfo> vec; return vec;}
u32 OpenFile(std::string filename, FileAccess access) {return 0;}
void CloseFile(u32 handle) {}
@ -103,6 +123,7 @@ public:
virtual bool RmDir(const std::string &dirname) {return false;}
virtual bool RenameFile(const std::string &from, const std::string &to) {return false;}
virtual bool DeleteFile(const std::string &filename) {return false;}
virtual bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;}
};

View File

@ -16,10 +16,12 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Globals.h"
#include "Log.h"
#include "Common.h"
#include "ISOFileSystem.h"
#include <cstring>
#include <cstdio>
#include <ctype.h>
const int sectorSize = 2048;
@ -27,11 +29,13 @@ static bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize)
{
if (filename.substr(0, 8) != "/sce_lbn")
return false;
std::string yo = filename;
std::string prev = filename;
filename.erase(0, 10);
sscanf(filename.c_str(), "%08x", sectorStart);
if (sscanf(filename.c_str(), "%08x", sectorStart) != 1)
WARN_LOG(FILESYS, "Invalid LBN reference: %s", prev.c_str());
filename.erase(0, filename.find("_size") + 7);
sscanf(filename.c_str(), "%08x", readSize);
if (sscanf(filename.c_str(), "%08x", readSize) != 1)
WARN_LOG(FILESYS, "Incomplete LBN reference: %s", prev.c_str());
return true;
}
@ -127,6 +131,7 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic
entireISO.size = _blockDevice->GetNumBlocks() * _blockDevice->GetBlockSize();
entireISO.isBlockSectorMode = true;
entireISO.flags = 0;
entireISO.parent = NULL;
if (!memcmp(desc.cd001, "CD001", 5))
{
@ -138,6 +143,12 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic
}
treeroot = new TreeEntry;
treeroot->isDirectory = true;
treeroot->startingPosition = 0;
treeroot->size = 0;
treeroot->isBlockSectorMode = false;
treeroot->flags = 0;
treeroot->parent = NULL;
u32 rootSector = desc.root.firstDataSectorLE;
u32 rootSize = desc.root.dataLengthLE;
@ -148,6 +159,7 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic
ISOFileSystem::~ISOFileSystem()
{
delete blockDevice;
delete treeroot;
}
void ISOFileSystem::ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root)
@ -197,7 +209,7 @@ nextblock:
if (strlen(name) == 1 && name[0] == '\x01') // ".." record
{
strcpy(name,"..");
relative=true;
relative = true;
}
TreeEntry *e = new TreeEntry;
@ -207,9 +219,10 @@ nextblock:
e->isDirectory = !isFile;
e->flags = dir.flags;
e->isBlockSectorMode = false;
e->parent = root;
// Let's not excessively spam the log - I commented this line out.
// DEBUG_LOG(FILESYS, "%s: %s %08x %08x %i", e->isDirectory?"D":"F", name, dir.firstDataSectorLE, e->startingPosition, e->startingPosition);
//DEBUG_LOG(FILESYS, "%s: %s %08x %08x %i", e->isDirectory?"D":"F", name, dir.firstDataSectorLE, e->startingPosition, e->startingPosition);
if (e->isDirectory && !relative)
{
@ -227,7 +240,7 @@ nextblock:
}
ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(std::string path)
ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(std::string path, bool catchError)
{
if (path.length() == 0)
{
@ -253,8 +266,15 @@ ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(std::string path)
{
for (size_t i=0; i<e->children.size(); i++)
{
std::string n = e->children[i]->name;
std::string n = (e->children[i]->name);
for (size_t j = 0; j < n.size(); j++) {
n[j] = tolower(n[j]);
}
std::string curPath = path.substr(0, path.find_first_of('/'));
for (size_t j = 0; j < curPath.size(); j++) {
curPath[j] = tolower(curPath[j]);
}
if (curPath == n)
{
//yay we got it
@ -277,7 +297,10 @@ ISOFileSystem::TreeEntry *ISOFileSystem::GetFromPath(std::string path)
}
else
{
ERROR_LOG(FILESYS,"File %s not found", path.c_str());
if (catchError)
{
ERROR_LOG(FILESYS,"File %s not found", path.c_str());
}
return 0;
}
}
@ -384,6 +407,8 @@ size_t ISOFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size)
}
else
{
_dbg_assert_msg_(HLE, e.file != 0, "Expecting non-raw fd to have a tree entry.");
//clamp read length
if ((s64)e.seekPos > e.file->size - (s64)size)
{
@ -480,7 +505,7 @@ PSPFileInfo ISOFileSystem::GetFileInfo(std::string filename)
return fileInfo;
}
TreeEntry *entry = GetFromPath(filename);
TreeEntry *entry = GetFromPath(filename, false);
PSPFileInfo x;
if (!entry)
{
@ -527,3 +552,70 @@ std::vector<PSPFileInfo> ISOFileSystem::GetDirListing(std::string path)
}
return myVector;
}
std::string ISOFileSystem::EntryFullPath(TreeEntry *e)
{
size_t fullLen = 0;
TreeEntry *cur = e;
while (cur != NULL && cur != treeroot)
{
// For the "/".
fullLen += 1 + cur->name.size();
cur = cur->parent;
}
std::string path;
path.resize(fullLen);
cur = e;
while (cur != NULL && cur != treeroot)
{
path.replace(fullLen - cur->name.size(), cur->name.size(), cur->name);
path.replace(fullLen - cur->name.size() - 1, 1, "/");
fullLen -= 1 + cur->name.size();
cur = cur->parent;
}
return path;
}
void ISOFileSystem::DoState(PointerWrap &p)
{
int n = (int) entries.size();
p.Do(n);
if (p.mode == p.MODE_READ)
{
entries.clear();
for (int i = 0; i < n; ++i)
{
u32 fd;
p.Do(fd);
std::string path;
p.Do(path);
OpenFileEntry of;
of.file = path.empty() ? NULL : GetFromPath(path);
p.Do(of.seekPos);
p.Do(of.isRawSector);
p.Do(of.sectorStart);
p.Do(of.openSize);
entries[fd] = of;
}
}
else
{
for (EntryMap::iterator it = entries.begin(), end = entries.end(); it != end; ++it)
{
p.Do(it->first);
std::string path = "";
if (it->second.file != NULL)
path = EntryFullPath(it->second.file);
p.Do(path);
p.Do(it->second.seekPos);
p.Do(it->second.isRawSector);
p.Do(it->second.sectorStart);
p.Do(it->second.openSize);
}
}
p.DoMarker("ISOFileSystem");
}

View File

@ -27,6 +27,26 @@
class ISOFileSystem : public IFileSystem
{
public:
ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice);
~ISOFileSystem();
void DoState(PointerWrap &p);
std::vector<PSPFileInfo> GetDirListing(std::string path);
u32 OpenFile(std::string filename, FileAccess access);
void CloseFile(u32 handle);
size_t ReadFile(u32 handle, u8 *pointer, s64 size);
size_t SeekFile(u32 handle, s32 position, FileMove type);
PSPFileInfo GetFileInfo(std::string filename);
bool OwnsHandle(u32 handle);
size_t WriteFile(u32 handle, const u8 *pointer, s64 size);
bool GetHostPath(const std::string &inpath, std::string &outpath) {return false;}
virtual bool MkDir(const std::string &dirname) {return false;}
virtual bool RmDir(const std::string &dirname) {return false;}
virtual bool RenameFile(const std::string &from, const std::string &to) {return false;}
virtual bool DeleteFile(const std::string &filename) {return false;}
private:
struct TreeEntry
{
TreeEntry(){}
@ -42,8 +62,9 @@ class ISOFileSystem : public IFileSystem
u32 startingPosition;
s64 size;
bool isDirectory;
bool isBlockSectorMode; // "umd:" mode: all sizes and offsets are in 2048 byte chunks
bool isBlockSectorMode; // "umd:" mode: all sizes and offsets are in 2048 byte chunks
TreeEntry *parent;
std::vector<TreeEntry*> children;
};
@ -66,22 +87,6 @@ class ISOFileSystem : public IFileSystem
TreeEntry entireISO;
void ReadDirectory(u32 startsector, u32 dirsize, TreeEntry *root);
TreeEntry *GetFromPath(std::string path);
public:
ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevice);
~ISOFileSystem();
std::vector<PSPFileInfo> GetDirListing(std::string path);
u32 OpenFile(std::string filename, FileAccess access);
void CloseFile(u32 handle);
size_t ReadFile(u32 handle, u8 *pointer, s64 size);
size_t WriteFile(u32 handle, const u8 *pointer, s64 size);
size_t SeekFile(u32 handle, s32 position, FileMove type);
PSPFileInfo GetFileInfo(std::string filename);
bool OwnsHandle(u32 handle);
virtual bool MkDir(const std::string &dirname) {return false;}
virtual bool RmDir(const std::string &dirname) {return false;}
virtual bool RenameFile(const std::string &from, const std::string &to) {return false;}
virtual bool DeleteFile(const std::string &filename) {return false;}
TreeEntry *GetFromPath(std::string path, bool catchError=true);
std::string EntryFullPath(TreeEntry *e);
};

View File

@ -16,8 +16,140 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <set>
#include "Common/StringUtil.h"
#include "MetaFileSystem.h"
static bool ApplyPathStringToComponentsVector(std::vector<std::string> &vector, const std::string &pathString)
{
size_t len = pathString.length();
size_t start = 0;
while (start < len)
{
size_t i = pathString.find('/', start);
if (i == std::string::npos)
i = len;
if (i > start)
{
std::string component = pathString.substr(start, i - start);
if (component != ".")
{
if (component == "..")
{
if (vector.size() != 0)
{
vector.pop_back();
}
else
{
// The PSP silently ignores attempts to .. to parent of root directory
WARN_LOG(HLE, "RealPath: ignoring .. beyond root - root directory is its own parent: \"%s\"", pathString.c_str());
}
}
else
{
vector.push_back(component);
}
}
}
start = i + 1;
}
return true;
}
/*
* Changes relative paths to absolute, removes ".", "..", and trailing "/"
* "drive:./blah" is absolute (ignore the dot) and "/blah" is relative (because it's missing "drive:")
* babel (and possibly other games) use "/directoryThatDoesNotExist/../directoryThatExists/filename"
*/
static bool RealPath(const std::string &currentDirectory, const std::string &inPath, std::string &outPath)
{
size_t inLen = inPath.length();
if (inLen == 0)
{
ERROR_LOG(HLE, "RealPath: inPath is empty");
return false;
}
size_t inColon = inPath.find(':');
if (inColon + 1 == inLen)
{
WARN_LOG(HLE, "RealPath: inPath is all prefix and no path: \"%s\"", inPath.c_str());
outPath = inPath;
return true;
}
bool relative = (inColon == std::string::npos);
std::string prefix, inAfterColon;
std::vector<std::string> cmpnts; // path components
size_t outPathCapacityGuess = inPath.length();
if (relative)
{
size_t curDirLen = currentDirectory.length();
if (curDirLen == 0)
{
ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory is empty", inPath.c_str());
return false;
}
size_t curDirColon = currentDirectory.find(':');
if (curDirColon == std::string::npos)
{
ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" has no prefix", inPath.c_str(), currentDirectory.c_str());
return false;
}
if (curDirColon + 1 == curDirLen)
{
ERROR_LOG(HLE, "RealPath: inPath \"%s\" is relative, but current directory \"%s\" is all prefix and no path. Using \"/\" as path for current directory.", inPath.c_str(), currentDirectory.c_str());
}
else
{
const std::string curDirAfter = currentDirectory.substr(curDirColon + 1);
if (! ApplyPathStringToComponentsVector(cmpnts, curDirAfter) )
{
ERROR_LOG(HLE,"RealPath: currentDirectory is not a valid path: \"%s\"", currentDirectory.c_str());
return false;
}
outPathCapacityGuess += curDirLen;
}
prefix = currentDirectory.substr(0, curDirColon + 1);
inAfterColon = inPath;
}
else
{
prefix = inPath.substr(0, inColon + 1);
inAfterColon = inPath.substr(inColon + 1);
}
if (! ApplyPathStringToComponentsVector(cmpnts, inAfterColon) )
{
WARN_LOG(HLE, "RealPath: inPath is not a valid path: \"%s\"", inPath.c_str());
return false;
}
outPath.clear();
outPath.reserve(outPathCapacityGuess);
outPath.append(prefix);
size_t numCmpnts = cmpnts.size();
for (size_t i = 0; i < numCmpnts; i++)
{
outPath.append(1, '/');
outPath.append(cmpnts[i]);
}
return true;
}
IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
{
for (size_t i = 0; i < fileSystems.size(); i++)
@ -29,23 +161,37 @@ IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
return 0;
}
bool MetaFileSystem::MapFilePath(std::string inpath, std::string &outpath, IFileSystem **system)
bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpath, IFileSystem **system)
{
// host0 HACK
// need to figure out what to do about xxx:./... paths - is there a current dir per drive?
if (!inpath.compare(0, 8, "host0:./"))
inpath = currentDirectory + inpath.substr(7);
//TODO: implement current directory per thread (NOT per drive)
std::string realpath;
for (size_t i = 0; i < fileSystems.size(); i++)
// Special handling: host0:command.txt (as seen in Super Monkey Ball Adventures, for example)
// appears to mean the current directory on the UMD. Let's just assume the current directory.
std::string inpath = _inpath;
if (inpath.substr(0, 6) == "host0:") {
INFO_LOG(HLE, "Host0 path detected, stripping: %s", inpath.c_str());
inpath = inpath.substr(6);
}
if ( RealPath(currentDirectory, inpath, realpath) )
{
int prefLen = fileSystems[i].prefix.size();
if (fileSystems[i].prefix == inpath.substr(0,prefLen))
for (size_t i = 0; i < fileSystems.size(); i++)
{
outpath = inpath.substr(prefLen);
*system = fileSystems[i].system;
return true;
size_t prefLen = fileSystems[i].prefix.size();
if (fileSystems[i].prefix == realpath.substr(0, prefLen))
{
outpath = realpath.substr(prefLen);
*system = fileSystems[i].system;
DEBUG_LOG(HLE, "MapFilePath: mapped \"%s\" to prefix: \"%s\", path: \"%s\"", inpath.c_str(), fileSystems[i].prefix.c_str(), outpath.c_str());
return true;
}
}
}
DEBUG_LOG(HLE, "MapFilePath: failed mapping \"%s\", returning false", inpath.c_str());
return false;
}
@ -80,11 +226,6 @@ void MetaFileSystem::UnmountAll()
u32 MetaFileSystem::OpenFile(std::string filename, FileAccess access)
{
std::string of;
if (filename.find(':') == std::string::npos)
{
filename = currentDirectory + "/" + filename;
DEBUG_LOG(HLE,"OpenFile: Expanded path to %s", filename.c_str());
}
IFileSystem *system;
if (MapFilePath(filename, of, &system))
{
@ -99,11 +240,6 @@ u32 MetaFileSystem::OpenFile(std::string filename, FileAccess access)
PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename)
{
std::string of;
if (filename.find(':') == std::string::npos)
{
filename = currentDirectory + "/" + filename;
DEBUG_LOG(HLE,"GetFileInfo: Expanded path to %s", filename.c_str());
}
IFileSystem *system;
if (MapFilePath(filename, of, &system))
{
@ -116,27 +252,20 @@ PSPFileInfo MetaFileSystem::GetFileInfo(std::string filename)
}
}
//TODO: Not sure where this should live. Seems a bit wrong putting it in common
bool stringEndsWith (std::string const &fullString, std::string const &ending)
bool MetaFileSystem::GetHostPath(const std::string &inpath, std::string &outpath)
{
if (fullString.length() >= ending.length()) {
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
} else {
return false;
}
std::string of;
IFileSystem *system;
if (MapFilePath(inpath, of, &system)) {
return system->GetHostPath(of, outpath);
} else {
return false;
}
}
std::vector<PSPFileInfo> MetaFileSystem::GetDirListing(std::string path)
{
std::string of;
if (path.find(':') == std::string::npos)
{
if (!stringEndsWith(currentDirectory, "/"))
{
path = currentDirectory + "/" + path;
}
DEBUG_LOG(HLE,"GetFileInfo: Expanded path to %s", path.c_str());
}
IFileSystem *system;
if (MapFilePath(path, of, &system))
{
@ -240,3 +369,22 @@ size_t MetaFileSystem::SeekFile(u32 handle, s32 position, FileMove type)
return 0;
}
void MetaFileSystem::DoState(PointerWrap &p)
{
p.Do(current);
p.Do(currentDirectory);
int n = (int) fileSystems.size();
p.Do(n);
if (n != fileSystems.size())
{
ERROR_LOG(FILESYS, "Savestate failure: number of filesystems doesn't match.");
return;
}
for (int i = 0; i < n; ++i)
fileSystems[i].system->DoState(p);
p.DoMarker("MetaFileSystem");
}

View File

@ -36,9 +36,14 @@ public:
u32 GetNewHandle() {return current++;}
void FreeHandle(u32 handle) {}
IFileSystem *GetHandleOwner(u32 handle);
bool MapFilePath(std::string inpath, std::string &outpath, IFileSystem **system);
virtual void DoState(PointerWrap &p);
IFileSystem *GetHandleOwner(u32 handle);
bool MapFilePath(const std::string &inpath, std::string &outpath, IFileSystem **system);
// Only possible if a file system is a DirectoryFileSystem or similar.
bool GetHostPath(const std::string &inpath, std::string &outpath);
std::vector<PSPFileInfo> GetDirListing(std::string path);
u32 OpenFile(std::string filename, FileAccess access);
void CloseFile(u32 handle);

View File

@ -29,6 +29,12 @@ template<u64 func()> void WrapU64_V() {
currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF;
}
template<u64 func(u32)> void WrapU64_U() {
u64 retval = func(PARAM(0));
currentMIPS->r[2] = retval & 0xFFFFFFFF;
currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF;
}
template<int func(u32, u64)> void WrapI_UU64() {
u64 param_one = currentMIPS->r[6];
param_one |= (u64)(currentMIPS->r[7]) << 32;
@ -36,6 +42,13 @@ template<int func(u32, u64)> void WrapI_UU64() {
RETURN(retval);
}
template<u32 func(u32, u64)> void WrapU_UU64() {
u64 param_one = currentMIPS->r[6];
param_one |= (u64)(currentMIPS->r[7]) << 32;
u32 retval = func(PARAM(0), param_one);
RETURN(retval);
}
template<int func(u32, u32, u64)> void WrapI_UUU64() {
u64 param_two = currentMIPS->r[6];
param_two |= (u64)(currentMIPS->r[7]) << 32;
@ -50,6 +63,21 @@ template<u32 func(int, s64, int)> void WrapU_II64I() {
RETURN(retval);
}
template<u32 func(u32, u64, u32, u32)> void WrapU_UU64UU() {
u64 param_one = currentMIPS->r[6];
param_one |= (u64)(currentMIPS->r[7]) << 32;
u32 retval = func(PARAM(0), param_one, PARAM(4), PARAM(5));
RETURN(retval);
}
template<s64 func(int, s64, int)> void WrapI64_II64I() {
s64 param_one = currentMIPS->r[6];
param_one |= (s64)(currentMIPS->r[7]) << 32;
s64 retval = func(PARAM(0), param_one, PARAM(4));
currentMIPS->r[2] = (retval >> 0) & 0xFFFFFFFF;
currentMIPS->r[3] = (retval >> 32) & 0xFFFFFFFF;
}
//32bit wrappers
template<void func()> void WrapV_V() {
func();
@ -83,6 +111,16 @@ template<int func(u32)> void WrapI_U() {
RETURN(retval);
}
template<int func(u32, int)> void WrapI_UI() {
int retval = func(PARAM(0), PARAM(1));
RETURN(retval);
}
template<int func(u32, int, int, u32)> void WrapI_UIIU() {
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
}
template<u32 func(int, u32, int)> void WrapU_IUI() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
@ -93,6 +131,26 @@ template<int func(u32, u32)> void WrapI_UU() {
RETURN(retval);
}
template<int func(u32, u32, u32)> void WrapI_UUU() {
int retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
}
template<int func(u32, u32, u32, int, int, int,int )> void WrapI_UUUIIII() {
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
RETURN(retval);
}
template<int func(u32, u32, u32, u32)> void WrapI_UUUU() {
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
}
template<int func(u32, u32, u32, u32, u32)> void WrapI_UUUUU() {
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
RETURN(retval);
}
template<int func()> void WrapI_V() {
int retval = func();
RETURN(retval);
@ -103,6 +161,11 @@ template<u32 func(int)> void WrapU_I() {
RETURN(retval);
}
template<u32 func(int, int, u32)> void WrapU_IIU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
}
template<int func(int)> void WrapI_I() {
int retval = func(PARAM(0));
RETURN(retval);
@ -128,6 +191,16 @@ template<void func(u32, const char *)> void WrapV_UC() {
func(PARAM(0), Memory::GetCharPointer(PARAM(1)));
}
template<int func(u32, const char *)> void WrapI_UC() {
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)));
RETURN(retval);
}
template<u32 func(u32, int , int , int, int, int)> void WrapU_UIIIII() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5));
RETURN(retval);
}
template<u32 func(u32, int , int , int, int, int, int)> void WrapU_UIIIIII() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
RETURN(retval);
@ -138,10 +211,20 @@ template<u32 func(u32, u32)> void WrapU_UU() {
RETURN(retval);
}
template<u32 func(const char *, u32, u32, u32)> void WrapU_CUUU() {
u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
}
template<void func(u32, int, u32, int, int)> void WrapV_UIUII() {
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
}
template<u32 func(u32, int, u32, int, int)> void WrapU_UIUII() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
RETURN(retval);
}
template<u32 func(u32, int, int)> void WrapU_UII() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
@ -184,6 +267,11 @@ template<int func(int, int, int)> void WrapI_III() {
RETURN(retval);
}
template<int func(int, int, int, int)> void WrapI_IIII() {
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
}
template<int func(int, int, u32)> void WrapI_IIU() {
int retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
@ -214,12 +302,23 @@ template<int func(const char *, u32)> void WrapI_CU() {
RETURN(retval);
}
template<int func(const char *, u32, u32)> void WrapI_CUU() {
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
RETURN(retval);
}
template<int func(const char *, u32, u32, u32)> void WrapI_CUUU() {
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
PARAM(3));
RETURN(retval);
}
template<int func(const char *, u32, u32, int, u32, u32)> void WrapI_CUUIUU() {
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
PARAM(3), PARAM(4), PARAM(5));
RETURN(retval);
}
template<u32 func(const char *, u32)> void WrapU_CU() {
u32 retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1));
RETURN((u32) retval);
@ -240,11 +339,31 @@ template<u32 func(int, int, int)> void WrapU_III() {
RETURN(retval);
}
template<u32 func(int, int)> void WrapU_II() {
u32 retval = func(PARAM(0), PARAM(1));
RETURN(retval);
}
template<u32 func(int, int, int, int)> void WrapU_IIII() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
}
template<u32 func(int, u32, u32)> void WrapU_IUU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
}
template<u32 func(int, u32, u32, u32)> void WrapU_IUUU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
}
template<u32 func(int, u32, u32, u32, u32)> void WrapU_IUUUU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
RETURN(retval);
}
template<u32 func(u32, u32, u32)> void WrapU_UUU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
@ -262,6 +381,11 @@ template<void func(u32, int, u32)> void WrapV_UIU() {
func(PARAM(0), PARAM(1), PARAM(2));
}
template<int func(u32, int, u32)> void WrapI_UIU() {
int retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
}
template<void func(int, u32, u32, u32, u32)> void WrapV_IUUUU() {
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
}
@ -278,16 +402,33 @@ template<void func(const char *, u32, int, u32)> void WrapV_CUIU() {
func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
}
template<int func(const char *, u32, int, u32)> void WrapI_CUIU() {
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
}
template<void func(u32, const char *, u32, int, u32)> void WrapV_UCUIU() {
func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), PARAM(3),
PARAM(4));
}
template<int func(u32, const char *, u32, int, u32)> void WrapI_UCUIU() {
int retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2),
PARAM(3), PARAM(4));
RETURN(retval);
}
template<void func(const char *, u32, int, int, u32)> void WrapV_CUIIU() {
func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3),
PARAM(4));
}
template<int func(const char *, u32, int, int, u32)> void WrapI_CUIIU() {
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
PARAM(3), PARAM(4));
RETURN(retval);
}
template<u32 func(u32, u32, u32, u32)> void WrapU_UUUU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
@ -298,6 +439,11 @@ template<u32 func(u32, u32, u32, int)> void WrapU_UUUI() {
RETURN(retval);
}
template<u32 func(u32, u32, int, u32)> void WrapU_UUIU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
}
template<u32 func(u32, int, int, int)> void WrapU_UIII() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
@ -308,6 +454,10 @@ template<int func(int, u32, u32, u32, u32)> void WrapI_IUUUU() {
RETURN(retval);
}
template<int func(int, u32, int, int)> void WrapI_IUII() {
int retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3));
RETURN(retval);
}
template<u32 func(u32, u32, u32, u32, u32)> void WrapU_UUUUU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
RETURN(retval);
@ -332,6 +482,11 @@ template<u32 func(const char *, int)> void WrapU_CI() {
RETURN(retval);
}
template<u32 func(const char *, int, int)> void WrapU_CII() {
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2));
RETURN(retval);
}
template<int func(const char *, int, u32, int, u32)> void WrapU_CIUIU() {
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2),
PARAM(3), PARAM(4));
@ -359,3 +514,8 @@ template<int func(int, u32, u32)> void WrapI_IUU() {
int retval = func(PARAM(0), PARAM(1), PARAM(2));
RETURN(retval);
}
template<u32 func(u32, u32, u32, u32, u32, u32, u32)> void WrapU_UUUUUUU() {
u32 retval = func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4), PARAM(5), PARAM(6));
RETURN(retval);
}

View File

@ -17,6 +17,7 @@
#include "HLE.h"
#include <map>
#include <vector>
#include "../MemMap.h"
#include "HLETables.h"
@ -25,19 +26,51 @@
#include "sceIo.h"
#include "sceAudio.h"
#include "sceKernelMemory.h"
#include "sceKernelThread.h"
#include "sceKernelInterrupt.h"
#include "../MIPS/MIPSCodeUtils.h"
#include "../Host.h"
enum
{
// Do nothing after the syscall.
HLE_AFTER_NOTHING = 0x00,
// Reschedule immediately after the syscall.
HLE_AFTER_RESCHED = 0x01,
// Call current thread's callbacks after the syscall.
HLE_AFTER_CURRENT_CALLBACKS = 0x02,
// Check all threads' callbacks after the syscall.
HLE_AFTER_ALL_CALLBACKS = 0x04,
// Reschedule and process current thread's callbacks after the syscall.
HLE_AFTER_RESCHED_CALLBACKS = 0x08,
// Run interrupts (and probably reschedule) after the syscall.
HLE_AFTER_RUN_INTERRUPTS = 0x10,
// Switch to CORE_STEPPING after the syscall (for debugging.)
HLE_AFTER_DEBUG_BREAK = 0x20,
};
static std::vector<HLEModule> moduleDB;
static std::vector<Syscall> unresolvedSyscalls;
static int hleAfterSyscall = HLE_AFTER_NOTHING;
static char hleAfterSyscallReschedReason[512];
void HLEInit()
{
RegisterAllModules();
}
void HLEDoState(PointerWrap &p)
{
Syscall sc = {0};
p.Do(unresolvedSyscalls, sc);
p.DoMarker("HLE");
}
void HLEShutdown()
{
hleAfterSyscall = HLE_AFTER_NOTHING;
moduleDB.clear();
unresolvedSyscalls.clear();
}
void RegisterModule(const char *name, int numFunctions, const HLEFunction *funcTable)
@ -158,7 +191,8 @@ void ResolveSyscall(const char *moduleName, u32 nib, u32 address)
{
INFO_LOG(HLE,"Resolving %s/%08x",moduleName,nib);
// Note: doing that, we can't trace external module calls, so maybe something else should be done to debug more efficiently
Memory::Write_U32(MIPS_MAKE_JAL(address), sysc->symAddr);
// Note that this should be J not JAL, as otherwise control will return to the stub..
Memory::Write_U32(MIPS_MAKE_J(address), sysc->symAddr);
Memory::Write_U32(MIPS_MAKE_NOP(), sysc->symAddr + 4);
}
}
@ -177,12 +211,107 @@ const char *GetFuncName(int moduleIndex, int func)
return "[unknown]";
}
void hleCheckAllCallbacks()
{
hleAfterSyscall |= HLE_AFTER_ALL_CALLBACKS;
}
void hleCheckCurrentCallbacks()
{
hleAfterSyscall |= HLE_AFTER_CURRENT_CALLBACKS;
}
void hleReSchedule(const char *reason)
{
_dbg_assert_msg_(HLE, reason != 0, "hleReSchedule: Expecting a valid reason.");
_dbg_assert_msg_(HLE, reason != 0 && strlen(reason) < 256, "hleReSchedule: Not too long reason.");
hleAfterSyscall |= HLE_AFTER_RESCHED;
if (!reason)
strcpy(hleAfterSyscallReschedReason, "Invalid reason");
// You can't seriously need a reason that long, can you?
else if (strlen(reason) >= sizeof(hleAfterSyscallReschedReason))
{
memcpy(hleAfterSyscallReschedReason, reason, sizeof(hleAfterSyscallReschedReason) - 1);
hleAfterSyscallReschedReason[sizeof(hleAfterSyscallReschedReason) - 1] = 0;
}
else
strcpy(hleAfterSyscallReschedReason, reason);
}
void hleReSchedule(bool callbacks, const char *reason)
{
hleReSchedule(reason);
if (callbacks)
hleAfterSyscall |= HLE_AFTER_RESCHED_CALLBACKS;
}
void hleRunInterrupts()
{
hleAfterSyscall |= HLE_AFTER_RUN_INTERRUPTS;
}
void hleDebugBreak()
{
hleAfterSyscall |= HLE_AFTER_DEBUG_BREAK;
}
// Pauses execution after an HLE call.
bool hleExecuteDebugBreak(const HLEFunction &func)
{
const u32 NID_SUSPEND_INTR = 0x092968F4, NID_RESUME_INTR = 0x5F10D406;
// Never break on these, they're noise.
u32 blacklistedNIDs[] = {NID_SUSPEND_INTR, NID_RESUME_INTR, NID_IDLE};
for (int i = 0; i < ARRAY_SIZE(blacklistedNIDs); ++i)
{
if (func.ID == blacklistedNIDs[i])
return false;
}
Core_EnableStepping(true);
host->SetDebugMode(true);
return true;
}
inline void hleFinishSyscall(int modulenum, int funcnum)
{
if ((hleAfterSyscall & HLE_AFTER_CURRENT_CALLBACKS) != 0)
__KernelForceCallbacks();
if ((hleAfterSyscall & HLE_AFTER_RUN_INTERRUPTS) != 0)
__RunOnePendingInterrupt();
// Rescheduling will also do HLE_AFTER_ALL_CALLBACKS.
if ((hleAfterSyscall & HLE_AFTER_RESCHED_CALLBACKS) != 0)
__KernelReSchedule(true, hleAfterSyscallReschedReason);
else if ((hleAfterSyscall & HLE_AFTER_RESCHED) != 0)
__KernelReSchedule(hleAfterSyscallReschedReason);
else if ((hleAfterSyscall & HLE_AFTER_ALL_CALLBACKS) != 0)
__KernelCheckCallbacks();
if ((hleAfterSyscall & HLE_AFTER_DEBUG_BREAK) != 0)
{
if (!hleExecuteDebugBreak(moduleDB[modulenum].funcTable[funcnum]))
{
// We'll do it next syscall.
hleAfterSyscall = HLE_AFTER_DEBUG_BREAK;
hleAfterSyscallReschedReason[0] = 0;
return;
}
}
hleAfterSyscall = HLE_AFTER_NOTHING;
hleAfterSyscallReschedReason[0] = 0;
}
void CallSyscall(u32 op)
{
u32 callno = (op >> 6) & 0xFFFFF; //20 bits
int funcnum = callno & 0xFFF;
int modulenum = (callno & 0xFF000) >> 12;
if (funcnum == 0xfff)
if (funcnum == 0xfff || op == 0xffff)
{
_dbg_assert_msg_(HLE,0,"Unknown syscall");
ERROR_LOG(HLE,"Unknown syscall: Module: %s", moduleDB[modulenum].name);
@ -192,6 +321,9 @@ void CallSyscall(u32 op)
if (func)
{
func();
if (hleAfterSyscall != HLE_AFTER_NOTHING)
hleFinishSyscall(modulenum, funcnum);
}
else
{

View File

@ -73,8 +73,21 @@ int GetModuleIndex(const char *modulename);
void RegisterModule(const char *name, int numFunctions, const HLEFunction *funcTable);
// Run the current thread's callbacks after the syscall finishes.
void hleCheckCurrentCallbacks();
// Check and potentially run all thread's callbacks after the syscall finishes.
void hleCheckAllCallbacks();
// Reschedule after the syscall finishes.
void hleReSchedule(const char *reason);
// Reschedule and go into a callback processing state after the syscall finishes.
void hleReSchedule(bool callbacks, const char *reason);
// Run interrupts after the syscall finishes.
void hleRunInterrupts();
// Pause emulation after the syscall finishes.
void hleDebugBreak();
void HLEInit();
void HLEDoState(PointerWrap &p);
void HLEShutdown();
u32 GetNibByName(const char *module, const char *function);
u32 GetSyscallOp(const char *module, u32 nib);
@ -82,7 +95,3 @@ void WriteSyscall(const char *module, u32 nib, u32 address);
void CallSyscall(u32 op);
void ResolveSyscall(const char *moduleName, u32 nib, u32 address);
// Need to be able to save entire kernel state
int GetStateSize();
void SaveState(u8 *ptr);
void LoadState(const u8 *ptr);

View File

@ -51,8 +51,8 @@
#include "sceParseUri.h"
#include "sceSsl.h"
#include "sceParseHttp.h"
#include "scesupPreAcc.h"
#include "sceVaudio.h"
#include "sceUsb.h"
#define N(s) s
@ -74,9 +74,9 @@ const HLEFunction FakeSysCalls[] =
const HLEFunction UtilsForUser[] =
{
{0x91E4F6A7, WrapU_V<sceKernelLibcClock>, "sceKernelLibcClock"},
{0x27CC57F0, sceKernelLibcTime, "sceKernelLibcTime"},
{0x27CC57F0, WrapU_U<sceKernelLibcTime>, "sceKernelLibcTime"},
{0x71EC4271, sceKernelLibcGettimeofday, "sceKernelLibcGettimeofday"},
{0xBFA98062, 0, "sceKernelDcacheInvalidateRange"},
{0xBFA98062, WrapI_UI<sceKernelDcacheInvalidateRange>, "sceKernelDcacheInvalidateRange"},
{0xC8186A58, 0, "sceKernelUtilsMd5Digest"},
{0x9E5C5086, 0, "sceKernelUtilsMd5BlockInit"},
{0x61E1E525, 0, "sceKernelUtilsMd5BlockUpdate"},
@ -89,10 +89,10 @@ const HLEFunction UtilsForUser[] =
{0x06FB8A63, 0, "sceKernelUtilsMt19937UInt"},
{0x37FB5C42, sceKernelGetGPI, "sceKernelGetGPI"},
{0x6AD345D7, sceKernelSetGPO, "sceKernelSetGPO"},
{0x79D1C3FA, sceKernelDcacheWritebackAll, "sceKernelDcacheWritebackAll"},
{0xB435DEC5, sceKernelDcacheWritebackInvalidateAll, "sceKernelDcacheWritebackInvalidateAll"},
{0x3EE30821, sceKernelDcacheWritebackRange, "sceKernelDcacheWritebackRange"},
{0x34B9FA9E, sceKernelDcacheWritebackInvalidateRange, "sceKernelDcacheWritebackInvalidateRange"},
{0x79D1C3FA, WrapI_V<sceKernelDcacheWritebackAll>, "sceKernelDcacheWritebackAll"},
{0xB435DEC5, WrapI_V<sceKernelDcacheWritebackInvalidateAll>, "sceKernelDcacheWritebackInvalidateAll"},
{0x3EE30821, WrapI_UI<sceKernelDcacheWritebackRange>, "sceKernelDcacheWritebackRange"},
{0x34B9FA9E, WrapI_UI<sceKernelDcacheWritebackInvalidateRange>, "sceKernelDcacheWritebackInvalidateRange"},
{0xC2DF770E, 0, "sceKernelIcacheInvalidateRange"},
{0x80001C4C, 0, "sceKernelDcacheProbe"},
{0x16641D70, 0, "sceKernelDcacheReadTag"},
@ -185,43 +185,12 @@ const HLEFunction pspeDebug[] =
};
const HLEFunction sceUsb[] =
{
{0xae5de6af, 0, "sceUsbStart"},
{0xc2464fa0, 0, "sceUsbStop"},
{0xc21645a4, 0, "sceUsbGetState"},
{0x4e537366, 0, "sceUsbGetDrvList"},
{0x112cc951, 0, "sceUsbGetDrvState"},
{0x586db82c, 0, "sceUsbActivate"},
{0xc572a9c8, 0, "sceUsbDeactivate"},
{0x5be0e002, 0, "sceUsbWaitState"},
{0x1c360735, 0, "sceUsbWaitCancel"},
};
const HLEFunction sceUsbstor[] =
{
{0x60066CFE, 0, "sceUsbstorGetStatus"},
};
const HLEFunction sceUsbstorBoot[] =
{
{0xE58818A8, 0, "sceUsbstorBootSetCapacity"},
{0x594BBF95, 0, "sceUsbstorBootSetLoadAddr"},
{0x6D865ECD, 0, "sceUsbstorBootGetDataSize"},
{0xA1119F0D, 0, "sceUsbstorBootSetStatus"},
{0x1F080078, 0, "sceUsbstorBootRegisterNotify"},
{0xA55C9E16, 0, "sceUsbstorBootUnregisterNotify"},
};
const HLEModule moduleList[] =
{
{"FakeSysCalls", SZ(FakeSysCalls), FakeSysCalls},
{"UtilsForUser",SZ(UtilsForUser),UtilsForUser},
{"KDebugForKernel",SZ(KDebugForKernel),KDebugForKernel},
{"sceSAScore"},
{"sceUsbstor",SZ(sceUsbstor),sceUsbstor},
{"sceUsbstorBoot",SZ(sceUsbstorBoot),sceUsbstorBoot},
{"sceUsb", SZ(sceUsb), sceUsb},
{"SceBase64_Library"},
{"sceCert_Loader"},
{"SceFont_Library"},
@ -273,8 +242,8 @@ void RegisterAllModules() {
Register_sceParseUri();
Register_sceSsl();
Register_sceParseHttp();
Register_scesupPreAcc();
Register_sceVaudio();
Register_sceUsb();
for (int i = 0; i < numModules; i++)
{

View File

@ -28,41 +28,36 @@
#include "FixedSizeQueue.h"
#include "Common/Thread.h"
// While buffers == MAX_BUFFERS, block on blocking write
// non-blocking writes will return busy, I guess
#define MAX_BUFFERS 2
#define MIN_BUFFERS 1
std::recursive_mutex section;
int eventAudioUpdate = -1;
int eventHostAudioUpdate = -1;
int mixFrequency = 44100;
const int hwSampleRate = 44100;
const int hwBlockSize = 480;
const int hostAttemptBlockSize = 64;
const int hwBlockSize = 60;
const int hostAttemptBlockSize = 256;
const int audioIntervalUs = (int)(1000000ULL * hwBlockSize / hwSampleRate);
const int audioHostIntervalUs = (int)(1000000ULL * hostAttemptBlockSize / hwSampleRate);
// High and low watermarks, basically.
const int chanQueueMaxSizeFactor = 2;
const int chanQueueMaxSizeFactor = 4;
const int chanQueueMinSizeFactor = 1;
FixedSizeQueue<s16, hwBlockSize * 8> outAudioQueue;
FixedSizeQueue<s16, hostAttemptBlockSize * 16> outAudioQueue;
void hleAudioUpdate(u64 userdata, int cyclesLate)
{
__AudioUpdate();
CoreTiming::ScheduleEvent(usToCycles(audioIntervalUs), eventAudioUpdate, 0);
CoreTiming::ScheduleEvent(usToCycles(audioIntervalUs) - cyclesLate, eventAudioUpdate, 0);
}
void hleHostAudioUpdate(u64 userdata, int cyclesLate)
{
host->UpdateSound();
CoreTiming::ScheduleEvent(usToCycles(audioHostIntervalUs), eventHostAudioUpdate, 0);
CoreTiming::ScheduleEvent(usToCycles(audioHostIntervalUs) - cyclesLate, eventHostAudioUpdate, 0);
}
void __AudioInit()
@ -78,6 +73,33 @@ void __AudioInit()
chans[i].clear();
}
void __AudioDoState(PointerWrap &p)
{
section.lock();
p.Do(eventAudioUpdate);
CoreTiming::RestoreRegisterEvent(eventAudioUpdate, "AudioUpdate", &hleAudioUpdate);
p.Do(eventHostAudioUpdate);
CoreTiming::RestoreRegisterEvent(eventHostAudioUpdate, "AudioUpdateHost", &hleAudioUpdate);
p.Do(mixFrequency);
outAudioQueue.DoState(p);
int chanCount = ARRAY_SIZE(chans);
p.Do(chanCount);
if (chanCount != ARRAY_SIZE(chans))
{
ERROR_LOG(HLE, "Savestate failure: different number of audio channels.");
section.unlock();
return;
}
for (int i = 0; i < chanCount; ++i)
chans[i].DoState(p);
section.unlock();
p.DoMarker("sceAudio");
}
void __AudioShutdown()
{
for (int i = 0; i < 8; i++)
@ -95,8 +117,8 @@ u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking)
chan.waitingThread = __KernelGetCurThread();
// WARNING: This changes currentThread so must grab waitingThread before (line above).
__KernelWaitCurThread(WAITTYPE_AUDIOCHANNEL, (SceUID)chanNum, 0, 0, false);
section.unlock();
return 0;
// Fall through to the sample queueing, don't want to lose the samples even though
// we're getting full.
}
else
{
@ -137,12 +159,12 @@ void __AudioUpdate()
s32 mixBuffer[hwBlockSize * 2];
memset(mixBuffer, 0, sizeof(mixBuffer));
for (int i = 0; i < MAX_CHANNEL; i++)
for (int i = 0; i < PSP_AUDIO_CHANNEL_MAX; i++)
{
if (!chans[i].reserved)
continue;
if (!chans[i].sampleQueue.size()) {
// DEBUG_LOG(HLE, "No queued samples, skipping channel %i", i);
// ERROR_LOG(HLE, "No queued samples, skipping channel %i", i);
continue;
}
@ -151,8 +173,8 @@ void __AudioUpdate()
if (chans[i].sampleQueue.size() >= 2)
{
s16 sampleL = chans[i].sampleQueue.front();
s16 sampleR = chans[i].sampleQueue.front();
chans[i].sampleQueue.pop();
s16 sampleR = chans[i].sampleQueue.front();
chans[i].sampleQueue.pop();
mixBuffer[s * 2] += sampleL;
mixBuffer[s * 2 + 1] += sampleR;
@ -178,14 +200,20 @@ void __AudioUpdate()
section.lock();
if (g_Config.bEnableSound && outAudioQueue.room() >= hwBlockSize * 2) {
// Push the mixed samples onto the output audio queue.
for (int i = 0; i < hwBlockSize; i++) {
s32 sampleL = mixBuffer[i * 2] >> 2; // TODO - what factor?
s32 sampleR = mixBuffer[i * 2 + 1] >> 2;
if (g_Config.bEnableSound) {
if (outAudioQueue.room() >= hwBlockSize * 2) {
// Push the mixed samples onto the output audio queue.
for (int i = 0; i < hwBlockSize; i++) {
s32 sampleL = mixBuffer[i * 2] >> 2; // TODO - what factor?
s32 sampleR = mixBuffer[i * 2 + 1] >> 2;
outAudioQueue.push((s16)sampleL);
outAudioQueue.push((s16)sampleR);
outAudioQueue.push((s16)sampleL);
outAudioQueue.push((s16)sampleR);
}
} else {
// This happens quite a lot. There's still something slightly off
// about the amount of audio we produce.
DEBUG_LOG(HLE, "Audio outbuffer overrun! room = %i / %i", outAudioQueue.room(), (u32)outAudioQueue.capacity());
}
}
@ -194,6 +222,7 @@ void __AudioUpdate()
void __AudioSetOutputFrequency(int freq)
{
WARN_LOG(HLE, "Switching audio frequency to %i", freq);
mixFrequency = freq;
}

View File

@ -22,6 +22,7 @@
// Easy interface for sceAudio to write to, to keep the complexity in check.
void __AudioInit();
void __AudioDoState(PointerWrap &p);
void __AudioUpdate();
void __AudioShutdown();
void __AudioSetOutputFrequency(int freq);

View File

@ -25,19 +25,59 @@
#include "sceKernel.h"
#include "sceUtility.h"
#define ATRAC_ERROR_API_FAIL 0x80630002
#define ATRAC_ERROR_ALL_DATA_DECODED 0x80630024
#define ATRAC_ERROR_API_FAIL 0x80630002
#define ATRAC_ERROR_ALL_DATA_DECODED 0x80630024
int sceAtracAddStreamData(int atracID, u32 bytesToAdd)
{
ERROR_LOG(HLE, "UNIMPL sceAtracAddStreamData(%i, %i)", atracID, bytesToAdd);
#define AT3_MAGIC 0x0270
#define AT3_PLUS_MAGIC 0xFFFE
#define PSP_MODE_AT_3_PLUS 0x00001000
#define PSP_MODE_AT_3 0x00001001
struct Atrac {
};
Atrac globalAtrac;
// TODO: Properly.
Atrac *getAtrac(int atracID) {
if (atracID == 1) {
return &globalAtrac;
} else {
return 0;
}
}
int getCodecType(int addr) {
int at3magic = Memory::Read_U16(addr+20);
if (at3magic == AT3_MAGIC) {
return PSP_MODE_AT_3;
} else if (at3magic == AT3_PLUS_MAGIC) {
return PSP_MODE_AT_3_PLUS;
}
return 0;
}
int sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishFlagAddr, u32 remainAddr)
u32 sceAtracGetAtracID(int codecType)
{
ERROR_LOG(HLE, "FAKE sceAtracGetAtracID(%i)", codecType);
return 1;
}
u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd)
{
ERROR_LOG(HLE, "UNIMPL sceAtracAddStreamData(%i, %08x)", atracID, bytesToAdd);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
// TODO
return 0;
}
u32 sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishFlagAddr, u32 remainAddr)
{
ERROR_LOG(HLE, "FAKE sceAtracDecodeData(%i, %08x, %08x, %08x, %08x)", atracID, outAddr, numSamplesAddr, finishFlagAddr, remainAddr);
Memory::Write_U16(0, outAddr); // Write a single 16-bit stereo
Memory::Write_U16(0, outAddr + 2);
@ -48,187 +88,295 @@ int sceAtracDecodeData(int atracID, u32 outAddr, u32 numSamplesAddr, u32 finishF
return 0;
}
void sceAtracEndEntry()
u32 sceAtracEndEntry()
{
ERROR_LOG(HLE, "UNIMPL sceAtracEndEntry");
RETURN(0);
}
void sceAtracGetAtracID()
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetAtracID");
RETURN(0);
}
void sceAtracGetBufferInfoForReseting()
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetBufferInfoForReseting");
RETURN(0);
}
int sceAtracGetBitrate(int atracID, u32 outBitrateAddr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetBitrate");
ERROR_LOG(HLE, "UNIMPL sceAtracEndEntry(.)");
return 0;
}
void sceAtracGetChannel()
u32 sceAtracGetBufferInfoForReseting(int atracID, int sample, u32 bufferInfoAddr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetChannel");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracGetBufferInfoForReseting(%i, %i, %08x)",atracID, sample, bufferInfoAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
// TODO: Write the right stuff instead.
Memory::Memset(bufferInfoAddr, 0, 32);
return 0;
}
void sceAtracGetLoopStatus()
u32 sceAtracGetBitrate(int atracID, u32 outBitrateAddr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetLoopStatus");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracGetBitrate(%i, %08x)", atracID, outBitrateAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
if (Memory::IsValidAddress(outBitrateAddr))
Memory::Write_U32(64, outBitrateAddr);
return 0;
}
void sceAtracGetInternalErrorInfo()
u32 sceAtracGetChannel(int atracID, u32 channelAddr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetInternalErrorInfo");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracGetChannel(%i, %08x)", atracID, channelAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
if (Memory::IsValidAddress(channelAddr))
Memory::Write_U32(2, channelAddr);
return 0;
}
void sceAtracGetMaxSample()
u32 sceAtracGetLoopStatus(int atracID, u32 loopNbr, u32 statusAddr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetMaxSample");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracGetLoopStatus(%i, %08x, %08x)", atracID, loopNbr, statusAddr );
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
return 0;
}
int sceAtracGetNextDecodePosition(int atracID, u32 outposAddr)
u32 sceAtracGetInternalErrorInfo(int atracID, u32 errorAddr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetInternalErrorInfo(%i, %08x)", atracID, errorAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
if (Memory::IsValidAddress(errorAddr))
Memory::Write_U32(0, errorAddr);
return 0;
}
u32 sceAtracGetMaxSample(int atracID, u32 maxSamplesAddr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetMaxSample(%i, %08x)", atracID, maxSamplesAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
if (Memory::IsValidAddress(maxSamplesAddr))
Memory::Write_U32(1024, maxSamplesAddr);
return 0;
}
u32 sceAtracGetNextDecodePosition(int atracID, u32 outposAddr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetNextDecodePosition(%i, %08x)", atracID, outposAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
Memory::Write_U32(1, outposAddr); // outpos
return 0;
}
int sceAtracGetNextSample(int atracID, u32 outNAddr)
u32 sceAtracGetNextSample(int atracID, u32 outNAddr)
{
ERROR_LOG(HLE, "FAKE sceAtracGetNextSample(%i, %08x)", atracID, outNAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
Memory::Write_U32(0, outNAddr);
return 0;
}
int sceAtracGetRemainFrame(int atracID, u32 outposAddr)
u32 sceAtracGetRemainFrame(int atracID, u32 outposAddr)
{
ERROR_LOG(HLE, "sceAtracGetRemainFrame(%i, %08x)", atracID, outposAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
Memory::Write_U32(12, outposAddr); // outpos
return 0;
}
int sceAtracGetSecondBufferInfo(int atracID, u32 outposAddr, u32 outBytesAddr)
u32 sceAtracGetSecondBufferInfo(int atracID, u32 outposAddr, u32 outBytesAddr)
{
ERROR_LOG(HLE, "sceAtracGetSecondBufferInfo(%i, %08x, %08x)", atracID, outposAddr, outBytesAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
Memory::Write_U32(0, outposAddr); // outpos
Memory::Write_U32(0x10000, outBytesAddr); // outBytes
return 0;
}
int sceAtracGetSoundSample(int atracID, u32 outEndSampleAddr, u32 outLoopStartSampleAddr, u32 outLoopEndSampleAddr)
u32 sceAtracGetSoundSample(int atracID, u32 outEndSampleAddr, u32 outLoopStartSampleAddr, u32 outLoopEndSampleAddr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetSoundSample(%i, %08x, %08x, %08x)", atracID, outEndSampleAddr, outLoopStartSampleAddr, outLoopEndSampleAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
Memory::Write_U32(0x10000, outEndSampleAddr); // outEndSample
Memory::Write_U32(-1, outLoopStartSampleAddr); // outLoopStartSample
Memory::Write_U32(-1, outLoopEndSampleAddr); // outLoopEndSample
return 0;
}
int sceAtracGetStreamDataInfo(int atracID, u32 writePointerAddr, u32 availableBytesAddr, u32 readOffsetAddr)
u32 sceAtracGetStreamDataInfo(int atracID, u32 writePointerAddr, u32 availableBytesAddr, u32 readOffsetAddr)
{
ERROR_LOG(HLE, "FAKE sceAtracGetStreamDataInfo(%i, %08x, %08x, %08x)", atracID, writePointerAddr, availableBytesAddr, readOffsetAddr);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
Memory::Write_U32(0, readOffsetAddr);
Memory::Write_U32(0, availableBytesAddr);
Memory::Write_U32(0, writePointerAddr);
return 0;
}
void sceAtracReleaseAtracID()
u32 sceAtracReleaseAtracID(int atracID)
{
ERROR_LOG(HLE, "UNIMPL sceAtracReleaseAtracID");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracReleaseAtracID(%i)", atracID);
return 0;
}
void sceAtracResetPlayPosition()
u32 sceAtracResetPlayPosition(int atracID, int sample, int bytesWrittenFirstBuf, int bytesWrittenSecondBuf)
{
ERROR_LOG(HLE, "UNIMPL sceAtracResetPlayPosition");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracResetPlayPosition(%i, %i, %i, %i)", atracID, sample, bytesWrittenFirstBuf, bytesWrittenSecondBuf);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
return 0;
}
void sceAtracSetHalfwayBuffer()
u32 sceAtracSetHalfwayBuffer(int atracID, u32 halfBuffer, u32 readSize, u32 halfBufferSize)
{
ERROR_LOG(HLE, "UNIMPL sceAtracSetHalfwayBuffer");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracSetHalfwayBuffer(%i, %08x, %8x, %8x)", atracID, halfBuffer, readSize, halfBufferSize);
return 0;
}
void sceAtracSetSecondBuffer()
u32 sceAtracSetSecondBuffer(int atracID, u32 secondBuffer, u32 secondBufferSize)
{
ERROR_LOG(HLE, "UNIMPL sceAtracSetSecondBuffer(%i, %08x, %i)", PARAM(0),PARAM(1),PARAM(2));
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracSetSecondBuffer(%i, %08x, %8x)", atracID, secondBuffer, secondBufferSize);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
return 0;
}
void sceAtracSetData()
u32 sceAtracSetData(int atracID, u32 buffer, u32 bufferSize)
{
ERROR_LOG(HLE, "UNIMPL sceAtracSetData");
RETURN(0);
} //?
ERROR_LOG(HLE, "UNIMPL sceAtracSetData(%i, %08x, %08x)", atracID, buffer, bufferSize);
return 0;
}
void sceAtracSetDataAndGetID()
{
ERROR_LOG(HLE, "UNIMPL sceAtracSetDataAndGetID(%08x, %i)", PARAM(0), PARAM(1));
RETURN(1);
int sceAtracSetDataAndGetID(u32 buffer, u32 bufferSize)
{
ERROR_LOG(HLE, "UNIMPL sceAtracSetDataAndGetID(%08x, %08x)", buffer, bufferSize);
int codecType = getCodecType(buffer);
return 0;
}
void sceAtracSetHalfwayBufferAndGetID()
int sceAtracSetHalfwayBufferAndGetID(int atracID, u32 halfBuffer, u32 readSize, u32 halfBufferSize)
{
ERROR_LOG(HLE, "UNIMPL sceAtracSetHalfwayBufferAndGetID");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracSetHalfwayBufferAndGetID(%i, %08x, %08x, %08x)", atracID, halfBuffer, readSize, halfBufferSize);
int codecType = getCodecType(halfBuffer);
return 0;
}
void sceAtracStartEntry()
u32 sceAtracStartEntry()
{
ERROR_LOG(HLE, "UNIMPL sceAtracStartEntry");
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracStartEntry(.)");
return 0;
}
void sceAtracSetLoopNum()
u32 sceAtracSetLoopNum(int atracID, int loopNum)
{
ERROR_LOG(HLE, "UNIMPL sceAtracSetLoopNum(%i, %i)", PARAM(0), PARAM(1));
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceAtracSetLoopNum(%i, %i)", atracID, loopNum);
return 0;
}
int sceAtracReinit()
{
ERROR_LOG(HLE, "UNIMPL sceAtracReinit(..)");
return 0;
}
int sceAtracGetOutputChannel(int atracID, u32 outputChanPtr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracGetOutputChannel(%i, %08x)", atracID, outputChanPtr);
if (Memory::IsValidAddress(outputChanPtr))
Memory::Write_U32(2, outputChanPtr);
return 0;
}
int sceAtracIsSecondBufferNeeded(int atracID)
{
ERROR_LOG(HLE, "UNIMPL sceAtracIsSecondBufferNeeded(%i)", atracID);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
return 0;
}
int sceAtracSetMOutHalfwayBuffer(int atracID, u32 MOutHalfBuffer, int readSize, int MOutHalfBufferSize)
{
ERROR_LOG(HLE, "UNIMPL sceAtracSetMOutHalfwayBuffer(%i, %08x, %i, %i)", atracID, MOutHalfBuffer, readSize, MOutHalfBufferSize);
Atrac *atrac = getAtrac(atracID);
if (!atrac) {
//return -1;
}
return 0;
}
int sceAtracSetAA3DataAndGetID(u32 buffer, int bufferSize, int fileSize, u32 metadataSizeAddr)
{
ERROR_LOG(HLE, "UNIMPL sceAtracSetAA3DataAndGetID(%08x, %i, %i, %08x)", buffer, bufferSize, fileSize, metadataSizeAddr);
int codecType = getCodecType(buffer);
return 0;
}
const HLEFunction sceAtrac3plus[] =
{
{0x7db31251,WrapI_IU<sceAtracAddStreamData>,"sceAtracAddStreamData"},
{0x6a8c3cd5,WrapI_IUUUU<sceAtracDecodeData>,"sceAtracDecodeData"},
{0xd5c28cc0,sceAtracEndEntry,"sceAtracEndEntry"},
{0x780f88d1,sceAtracGetAtracID,"sceAtracGetAtracID"},
{0xca3ca3d2,sceAtracGetBufferInfoForReseting,"sceAtracGetBufferInfoForReseting"},
{0xa554a158,WrapI_IU<sceAtracGetBitrate>,"sceAtracGetBitrate"},
{0x31668baa,sceAtracGetChannel,"sceAtracGetChannel"},
{0xfaa4f89b,sceAtracGetLoopStatus,"sceAtracGetLoopStatus"},
{0xe88f759b,sceAtracGetInternalErrorInfo,"sceAtracGetInternalErrorInfo"},
{0xd6a5f2f7,sceAtracGetMaxSample,"sceAtracGetMaxSample"},
{0xe23e3a35,WrapI_IU<sceAtracGetNextDecodePosition>,"sceAtracGetNextDecodePosition"},
{0x36faabfb,WrapI_IU<sceAtracGetNextSample>,"sceAtracGetNextSample"},
{0x9ae849a7,WrapI_IU<sceAtracGetRemainFrame>,"sceAtracGetRemainFrame"},
{0x83e85ea0,WrapI_IUU<sceAtracGetSecondBufferInfo>,"sceAtracGetSecondBufferInfo"},
{0xa2bba8be,WrapI_IUUU<sceAtracGetSoundSample>,"sceAtracGetSoundSample"},
{0x5d268707,WrapI_IUUU<sceAtracGetStreamDataInfo>,"sceAtracGetStreamDataInfo"},
{0x61eb33f5,sceAtracReleaseAtracID,"sceAtracReleaseAtracID"},
{0x644e5607,sceAtracResetPlayPosition,"sceAtracResetPlayPosition"},
{0x3f6e26b5,sceAtracSetHalfwayBuffer,"sceAtracSetHalfwayBuffer"},
{0x83bf7afd,sceAtracSetSecondBuffer,"sceAtracSetSecondBuffer"},
{0x0E2A73AB,sceAtracSetData,"sceAtracSetData"}, //?
{0x7a20e7af,sceAtracSetDataAndGetID,"sceAtracSetDataAndGetID"},
{0x0eb8dc38,sceAtracSetHalfwayBufferAndGetID,"sceAtracSetHalfwayBufferAndGetID"},
{0xd1f59fdb,sceAtracStartEntry,"sceAtracStartEntry"},
{0x868120b5,sceAtracSetLoopNum,"sceAtracSetLoopNum"},
{0x132f1eca,0,"sceAtracReinit"},
{0xeca32a99,0,"sceAtracIsSecondBufferNeeded"},
{0x0fae370e,0,"sceAtracSetHalfwayBufferAndGetID"},
{0x2DD3E298,0,"sceAtrac3plus_2DD3E298"},
{0x7db31251,WrapU_IU<sceAtracAddStreamData>,"sceAtracAddStreamData"},
{0x6a8c3cd5,WrapU_IUUUU<sceAtracDecodeData>,"sceAtracDecodeData"},
{0xd5c28cc0,WrapU_V<sceAtracEndEntry>,"sceAtracEndEntry"},
{0x780f88d1,WrapU_I<sceAtracGetAtracID>,"sceAtracGetAtracID"},
{0xca3ca3d2,WrapU_IIU<sceAtracGetBufferInfoForReseting>,"sceAtracGetBufferInfoForReseting"},
{0xa554a158,WrapU_IU<sceAtracGetBitrate>,"sceAtracGetBitrate"},
{0x31668baa,WrapU_IU<sceAtracGetChannel>,"sceAtracGetChannel"},
{0xfaa4f89b,WrapU_IUU<sceAtracGetLoopStatus>,"sceAtracGetLoopStatus"},
{0xe88f759b,WrapU_IU<sceAtracGetInternalErrorInfo>,"sceAtracGetInternalErrorInfo"},
{0xd6a5f2f7,WrapU_IU<sceAtracGetMaxSample>,"sceAtracGetMaxSample"},
{0xe23e3a35,WrapU_IU<sceAtracGetNextDecodePosition>,"sceAtracGetNextDecodePosition"},
{0x36faabfb,WrapU_IU<sceAtracGetNextSample>,"sceAtracGetNextSample"},
{0x9ae849a7,WrapU_IU<sceAtracGetRemainFrame>,"sceAtracGetRemainFrame"},
{0x83e85ea0,WrapU_IUU<sceAtracGetSecondBufferInfo>,"sceAtracGetSecondBufferInfo"},
{0xa2bba8be,WrapU_IUUU<sceAtracGetSoundSample>,"sceAtracGetSoundSample"},
{0x5d268707,WrapU_IUUU<sceAtracGetStreamDataInfo>,"sceAtracGetStreamDataInfo"},
{0x61eb33f5,WrapU_I<sceAtracReleaseAtracID>,"sceAtracReleaseAtracID"},
{0x644e5607,WrapU_IIII<sceAtracResetPlayPosition>,"sceAtracResetPlayPosition"},
{0x3f6e26b5,WrapU_IUUU<sceAtracSetHalfwayBuffer>,"sceAtracSetHalfwayBuffer"},
{0x83bf7afd,WrapU_IUU<sceAtracSetSecondBuffer>,"sceAtracSetSecondBuffer"},
{0x0E2A73AB,WrapU_IUU<sceAtracSetData>,"sceAtracSetData"}, //?
{0x7a20e7af,WrapI_UU<sceAtracSetDataAndGetID>,"sceAtracSetDataAndGetID"},
{0xd1f59fdb,WrapU_V<sceAtracStartEntry>,"sceAtracStartEntry"},
{0x868120b5,WrapU_II<sceAtracSetLoopNum>,"sceAtracSetLoopNum"},
{0x132f1eca,WrapI_V<sceAtracReinit>,"sceAtracReinit"},
{0xeca32a99,WrapI_I<sceAtracIsSecondBufferNeeded>,"sceAtracIsSecondBufferNeeded"},
{0x0fae370e,WrapI_IUUU<sceAtracSetHalfwayBufferAndGetID>,"sceAtracSetHalfwayBufferAndGetID"},
{0x2DD3E298,WrapU_IIU<sceAtracGetBufferInfoForReseting>,"sceAtracGetBufferInfoForResetting"},
{0x5CF9D852,WrapI_IUII<sceAtracSetMOutHalfwayBuffer>,"sceAtracSetMOutHalfwayBuffer"},
{0xB3B5D042,WrapI_IU<sceAtracGetOutputChannel>,"sceAtracGetOutputChannel"},
{0xF6837A1A,0,"sceAtracSetMOutData"},
{0x472E3825,0,"sceAtracSetMOutDataAndGetID"},
{0x9CD7DE03,0,"sceAtracSetMOutHalfwayBufferAndGetID"},
{0x5622B7C1,WrapI_UIIU<sceAtracSetAA3DataAndGetID>,"sceAtracSetAA3DataAndGetID"},
{0x5DD66588,0,"sceAtracSetAA3HalfwayBufferAndGetID"},
};

View File

@ -6,7 +6,7 @@
// 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
// 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.
@ -36,74 +36,48 @@ AudioChannel chans[8];
// Not sure about the range of volume, I often see 0x800 so that might be either
// max or 50%?
void sceAudioOutputBlocking(u32 chan, u32 vol, u32 samplePtr)
{
if (samplePtr == 0)
{
u32 sceAudioOutputBlocking(u32 chan, u32 vol, u32 samplePtr) {
if (samplePtr == 0) {
ERROR_LOG(HLE, "sceAudioOutputBlocking - Sample pointer null");
RETURN(0);
return 0;
}
if (chan < 0 || chan >= MAX_CHANNEL)
{
if (chan >= PSP_AUDIO_CHANNEL_MAX) {
ERROR_LOG(HLE,"sceAudioOutputBlocking() - BAD CHANNEL");
RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL);
}
else if (!chans[chan].reserved)
{
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
} else if (!chans[chan].reserved) {
ERROR_LOG(HLE,"sceAudioOutputBlocking() - channel not reserved");
RETURN(SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED);
}
else
{
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
} else {
DEBUG_LOG(HLE, "sceAudioOutputBlocking(%d, %d, %08x )",chan,vol,samplePtr);
chans[chan].leftVolume = vol;
chans[chan].rightVolume = vol;
chans[chan].sampleAddress = samplePtr;
RETURN(0);
int retval = __AudioEnqueue(chans[chan], chan, true);
if (retval != 0) {
// There was an error and didn't block (block always returns 0 here). Fine to RETURN.
RETURN(retval);
}
return __AudioEnqueue(chans[chan], chan, true);
}
}
void sceAudioOutputPannedBlocking(u32 chan, u32 volume1, u32 volume2, u32 samplePtr)
{
if (samplePtr == 0)
{
ERROR_LOG(HLE, "sceAudioOutputPannedBlocking - Sample pointer null");
RETURN(0);
}
else if (chan < 0 || chan >= MAX_CHANNEL)
{
u32 sceAudioOutputPannedBlocking(u32 chan, u32 volume1, u32 volume2, u32 samplePtr) {
if (samplePtr == 0) {
ERROR_LOG(HLE, "sceAudioOutputPannedBlocking - Sample pointer null");
return 0;
} else if (chan >= PSP_AUDIO_CHANNEL_MAX) {
ERROR_LOG(HLE,"sceAudioOutputPannedBlocking() - BAD CHANNEL");
RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL);
}
else if (!chans[chan].reserved)
{
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
} else if (!chans[chan].reserved) {
ERROR_LOG(HLE,"sceAudioOutputPannedBlocking() - CHANNEL NOT RESERVED");
RETURN(SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED);
}
else
{
DEBUG_LOG(HLE, "sceAudioOutputPannedBlocking(%d,%d,%d, %08x )", chan, volume1, volume2, samplePtr);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
} else {
DEBUG_LOG(HLE, "sceAudioOutputPannedBlocking(%d,%d,%d, %08x )", chan, volume1, volume2, samplePtr);
chans[chan].leftVolume = volume1;
chans[chan].rightVolume = volume2;
chans[chan].sampleAddress = samplePtr;
RETURN(0);
int retval = __AudioEnqueue(chans[chan], chan, true);
if (retval != 0) {
// There was an error and didn't block (block always returns 0 here). Fine to RETURN.
RETURN(retval);
}
return __AudioEnqueue(chans[chan], chan, true);
}
}
u32 sceAudioOutput(u32 chan, u32 vol, u32 samplePtr)
{
if (chan < 0 || chan >= MAX_CHANNEL)
{
if (chan >= PSP_AUDIO_CHANNEL_MAX) {
ERROR_LOG(HLE,"sceAudioOutput() - BAD CHANNEL");
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}
@ -125,7 +99,7 @@ u32 sceAudioOutput(u32 chan, u32 vol, u32 samplePtr)
u32 sceAudioOutputPanned(u32 chan, u32 leftVol, u32 rightVol, u32 samplePtr)
{
if (chan < 0 || chan >= MAX_CHANNEL)
if (chan >= PSP_AUDIO_CHANNEL_MAX)
{
ERROR_LOG(HLE,"sceAudioOutputPanned() - BAD CHANNEL");
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
@ -148,7 +122,7 @@ u32 sceAudioOutputPanned(u32 chan, u32 leftVol, u32 rightVol, u32 samplePtr)
int sceAudioGetChannelRestLen(u32 chan)
{
if (chan < 0 || chan >= MAX_CHANNEL)
if (chan >= PSP_AUDIO_CHANNEL_MAX)
{
ERROR_LOG(HLE, "sceAudioGetChannelRestLen(%i) - BAD CHANNEL", chan);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
@ -161,7 +135,7 @@ int sceAudioGetChannelRestLen(u32 chan)
int sceAudioGetChannelRestLength(u32 chan)
{
if (chan < 0 || chan >= MAX_CHANNEL)
if (chan >= PSP_AUDIO_CHANNEL_MAX)
{
ERROR_LOG(HLE, "sceAudioGetChannelRestLength(%i) - BAD CHANNEL", chan);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
@ -174,13 +148,9 @@ int sceAudioGetChannelRestLength(u32 chan)
static int GetFreeChannel()
{
for (int i = 0; i < MAX_CHANNEL; i++)
{
for (int i = 0; i < PSP_AUDIO_CHANNEL_MAX ; i++)
if (!chans[i].reserved)
{
return i;
}
}
return -1;
}
@ -196,7 +166,7 @@ u32 sceAudioChReserve(u32 channel, u32 sampleCount, u32 format) //.Allocate soun
return SCE_ERROR_AUDIO_NO_CHANNELS_AVAILABLE;
}
if (channel < 0 || channel >= MAX_CHANNEL)
if (channel >= PSP_AUDIO_CHANNEL_MAX)
{
ERROR_LOG(HLE ,"sceAudioChReserve(channel = %d, sampleCount = %d, format = %d) - BAD CHANNEL", channel, sampleCount, format);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
@ -212,7 +182,7 @@ u32 sceAudioChReserve(u32 channel, u32 sampleCount, u32 format) //.Allocate soun
{
WARN_LOG(HLE, "WARNING: Reserving already reserved channel. Error?");
}
DEBUG_LOG(HLE, "%i = sceAudioChReserve(%i, %i, %i)", channel, sampleCount, format);
DEBUG_LOG(HLE, "sceAudioChReserve(channel = %d, sampleCount = %d, format = %d)", channel, sampleCount, format);
chans[channel].sampleCount = sampleCount;
chans[channel].reserved = true;
@ -221,7 +191,7 @@ u32 sceAudioChReserve(u32 channel, u32 sampleCount, u32 format) //.Allocate soun
u32 sceAudioChRelease(u32 chan)
{
if (chan < 0 || chan >= MAX_CHANNEL)
if (chan >= PSP_AUDIO_CHANNEL_MAX)
{
ERROR_LOG(HLE, "sceAudioChRelease(%i) - BAD CHANNEL", chan);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
@ -240,66 +210,65 @@ u32 sceAudioChRelease(u32 chan)
u32 sceAudioSetChannelDataLen(u32 chan, u32 len)
{
if (chan < 0 || chan >= MAX_CHANNEL)
{
ERROR_LOG(HLE,"sceAudioSetChannelDataLen(%i, %i) - BAD CHANNEL", chan, len);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioSetChannelDataLen(%i, %i) - channel not reserved", chan, len);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
else
{
DEBUG_LOG(HLE, "sceAudioSetChannelDataLen(%i, %i)", chan, len);
//chans[chan].dataLen = len;
if (chan >= PSP_AUDIO_CHANNEL_MAX)
{
ERROR_LOG(HLE,"sceAudioSetChannelDataLen(%i, %i) - BAD CHANNEL", chan, len);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioSetChannelDataLen(%i, %i) - channel not reserved", chan, len);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
else
{
DEBUG_LOG(HLE, "sceAudioSetChannelDataLen(%i, %i)", chan, len);
chans[chan].sampleCount = len;
return 0;
}
return 0;
}
}
u32 sceAudioChangeChannelConfig(u32 chan, u32 format)
{
if (chan < 0 || chan >= MAX_CHANNEL)
{
ERROR_LOG(HLE,"sceAudioChangeChannelConfig(%i, %i) - invalid channel number", chan, format);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioChangeChannelConfig(%i, %i) - channel not reserved", chan, format);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
else
{
DEBUG_LOG(HLE, "sceAudioChangeChannelConfig(%i, %i)", chan, format);
chans[chan].format = format;
return 0;
}
if (chan >= PSP_AUDIO_CHANNEL_MAX)
{
ERROR_LOG(HLE,"sceAudioChangeChannelConfig(%i, %i) - invalid channel number", chan, format);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioChangeChannelConfig(%i, %i) - channel not reserved", chan, format);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
else
{
DEBUG_LOG(HLE, "sceAudioChangeChannelConfig(%i, %i)", chan, format);
chans[chan].format = format;
return 0;
}
}
u32 sceAudioChangeChannelVolume(u32 chan, u32 lvolume, u32 rvolume)
{
if (chan < 0 || chan >= MAX_CHANNEL)
{
ERROR_LOG(HLE,"sceAudioChangeChannelVolume(%i, %i, %i) - invalid channel number", chan, lvolume, rvolume);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioChangeChannelVolume(%i, %i, %i) - channel not reserved", chan, lvolume, rvolume);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
else
{
DEBUG_LOG(HLE, "sceAudioChangeChannelVolume(%i, %i, %i)", chan, lvolume, rvolume);
chans[chan].leftVolume = lvolume;
chans[chan].rightVolume = rvolume;
return 0;
}
if (chan >= PSP_AUDIO_CHANNEL_MAX)
{
ERROR_LOG(HLE,"sceAudioChangeChannelVolume(%i, %i, %i) - invalid channel number", chan, lvolume, rvolume);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioChangeChannelVolume(%i, %i, %i) - channel not reserved", chan, lvolume, rvolume);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
else
{
DEBUG_LOG(HLE, "sceAudioChangeChannelVolume(%i, %i, %i)", chan, lvolume, rvolume);
chans[chan].leftVolume = lvolume;
chans[chan].rightVolume = rvolume;
return 0;
}
}
u32 sceAudioInit()
{
DEBUG_LOG(HLE,"sceAudioInit()");
@ -315,40 +284,37 @@ u32 sceAudioEnd()
u32 sceAudioOutput2Reserve(u32 sampleCount)
{
ERROR_LOG(HLE,"sceAudioOutput2Reserve(%i)", sampleCount);
DEBUG_LOG(HLE,"sceAudioOutput2Reserve(%i)", sampleCount);
chans[0].sampleCount = sampleCount;
chans[0].reserved = true;
return 0;
}
void sceAudioOutput2OutputBlocking(u32 vol, u32 dataPtr)
u32 sceAudioOutput2OutputBlocking(u32 vol, u32 dataPtr)
{
DEBUG_LOG(HLE,"FAKE sceAudioOutput2OutputBlocking(%i, %08x)", vol, dataPtr);
chans[0].leftVolume = vol;
chans[0].rightVolume = vol;
chans[0].sampleAddress = dataPtr;
RETURN(0);
u32 retval = __AudioEnqueue(chans[0], 0, true);
if (retval < 0)
RETURN(retval);
return __AudioEnqueue(chans[0], 0, true);
}
u32 sceAudioOutput2ChangeLength(u32 sampleCount)
{
WARN_LOG(HLE,"sceAudioOutput2ChangeLength(%i)", sampleCount);
DEBUG_LOG(HLE,"sceAudioOutput2ChangeLength(%i)", sampleCount);
chans[0].sampleCount = sampleCount;
return 0;
}
u32 sceAudioOutput2GetRestSample()
{
WARN_LOG(HLE,"UNTESTED sceAudioOutput2GetRestSample()");
DEBUG_LOG(HLE,"UNTESTED sceAudioOutput2GetRestSample()");
return chans[0].sampleQueue.size() * 2;
}
u32 sceAudioOutput2Release()
{
WARN_LOG(HLE,"sceAudioOutput2Release()");
DEBUG_LOG(HLE,"sceAudioOutput2Release()");
chans[0].reserved = false;
return 0;
}
@ -364,34 +330,39 @@ u32 sceAudioSetFrequency(u32 freq) {
}
}
u32 sceAudioSetVolumeOffset(u32 unknown) {
ERROR_LOG(HLE, "UNIMPL sceAudioSetVolumeOffset(%i)", unknown);
return 0;
}
const HLEFunction sceAudio[] =
{
// Newer simplified single channel audio output. Presumably for games that use Atrac3
// directly from Sas instead of playing it on a separate audio channel.
{0x01562ba3, WrapU_U<sceAudioOutput2Reserve>, "sceAudioOutput2Reserve"},
{0x2d53f36e, WrapV_UU<sceAudioOutput2OutputBlocking>, "sceAudioOutput2OutputBlocking"},
{0x63f2889c, WrapU_U<sceAudioOutput2ChangeLength>, "sceAudioOutput2ChangeLength"},
{0x647cef33, WrapU_V<sceAudioOutput2GetRestSample>, "sceAudioOutput2GetRestSample"},
{0x43196845, WrapU_V<sceAudioOutput2Release>, "sceAudioOutput2Release"},
{0x01562ba3, WrapU_U<sceAudioOutput2Reserve>, "sceAudioOutput2Reserve"},
{0x2d53f36e, WrapU_UU<sceAudioOutput2OutputBlocking>, "sceAudioOutput2OutputBlocking"},
{0x63f2889c, WrapU_U<sceAudioOutput2ChangeLength>, "sceAudioOutput2ChangeLength"},
{0x647cef33, WrapU_V<sceAudioOutput2GetRestSample>, "sceAudioOutput2GetRestSample"},
{0x43196845, WrapU_V<sceAudioOutput2Release>, "sceAudioOutput2Release"},
{0x80F1F7E0, WrapU_V<sceAudioInit>, "sceAudioInit"},
{0x210567F7, WrapU_V<sceAudioEnd>, "sceAudioEnd"},
{0xA2BEAA6C, WrapU_U<sceAudioSetFrequency>, "sceAudioSetFrequency"},
{0x927AC32B, 0, "sceAudioSetVolumeOffset"},
{0xA2BEAA6C, WrapU_U<sceAudioSetFrequency>, "sceAudioSetFrequency"},
{0x927AC32B, WrapU_U<sceAudioSetVolumeOffset>, "sceAudioSetVolumeOffset"},
// The oldest and standard audio interface. Supports 8 channels, most games use 1-2.
{0x8c1009b2, WrapU_UUU<sceAudioOutput>, "sceAudioOutput"},
{0x136CAF51, WrapV_UUU<sceAudioOutputBlocking>, "sceAudioOutputBlocking"},
{0xE2D56B2D, WrapU_UUUU<sceAudioOutputPanned>, "sceAudioOutputPanned"},
{0x13F592BC, WrapV_UUUU<sceAudioOutputPannedBlocking>, "sceAudioOutputPannedBlocking"}, //(u32, u32, u32, void *)Output sound, blocking
{0x5EC81C55, WrapU_UUU<sceAudioChReserve>, "sceAudioChReserve"}, //(u32, u32 samplecount, u32) Initialize channel and allocate buffer long, long samplecount, long);//init buffer? returns handle, minus if error
{0x6FC46853, WrapU_U<sceAudioChRelease>, "sceAudioChRelease"}, //(long handle)Terminate channel and deallocate buffer //free buffer?
{0xE9D97901, WrapI_U<sceAudioGetChannelRestLen>, "sceAudioGetChannelRestLen"},
{0xB011922F, WrapI_U<sceAudioGetChannelRestLen>, "sceAudioGetChannelRestLength"}, // Is there a difference between this and sceAudioGetChannelRestLen?
{0x8c1009b2, WrapU_UUU<sceAudioOutput>, "sceAudioOutput"},
{0x136CAF51, WrapU_UUU<sceAudioOutputBlocking>, "sceAudioOutputBlocking"},
{0xE2D56B2D, WrapU_UUUU<sceAudioOutputPanned>, "sceAudioOutputPanned"},
{0x13F592BC, WrapU_UUUU<sceAudioOutputPannedBlocking>, "sceAudioOutputPannedBlocking"}, //(u32, u32, u32, void *)Output sound, blocking
{0x5EC81C55, WrapU_UUU<sceAudioChReserve>, "sceAudioChReserve"}, //(u32, u32 samplecount, u32) Initialize channel and allocate buffer long, long samplecount, long);//init buffer? returns handle, minus if error
{0x6FC46853, WrapU_U<sceAudioChRelease>, "sceAudioChRelease"}, //(long handle)Terminate channel and deallocate buffer //free buffer?
{0xE9D97901, WrapI_U<sceAudioGetChannelRestLen>, "sceAudioGetChannelRestLen"},
{0xB011922F, WrapI_U<sceAudioGetChannelRestLen>, "sceAudioGetChannelRestLength"}, // Is there a difference between this and sceAudioGetChannelRestLen?
{0xCB2E439E, WrapU_UU<sceAudioSetChannelDataLen>, "sceAudioSetChannelDataLen"}, //(u32, u32)
{0x95FD0C2D, WrapU_UU<sceAudioChangeChannelConfig>, "sceAudioChangeChannelConfig"},
{0xB7E1D8E7, WrapU_UUU<sceAudioChangeChannelVolume>, "sceAudioChangeChannelVolume"},
{0x95FD0C2D, WrapU_UU<sceAudioChangeChannelConfig>, "sceAudioChangeChannelConfig"},
{0xB7E1D8E7, WrapU_UUU<sceAudioChangeChannelVolume>, "sceAudioChangeChannelVolume"},
// I guess these are like the others but do sample rate conversion?
{0x38553111, 0, "sceAudioSRCChReserve"},
@ -405,9 +376,9 @@ const HLEFunction sceAudio[] =
// Microphone interface
{0x7de61688, 0, "sceAudioInputInit"},
{0xE926D3FB, 0, "sceAudioInputInitEx"},
{0x6d4bec68, 0, "sceAudioInput"},
{0x086e5895, 0, "sceAudioInputBlocking"},
{0xa708c6a6, 0, "sceAudioGetInputLength"},
{0x6d4bec68, 0, "sceAudioInput"},
{0x086e5895, 0, "sceAudioInputBlocking"},
{0xa708c6a6, 0, "sceAudioGetInputLength"},
{0xA633048E, 0, "sceAudioPollInputEnd"},
{0x87b2e651, 0, "sceAudioWaitInputEnd"},
};
@ -416,5 +387,5 @@ const HLEFunction sceAudio[] =
void Register_sceAudio()
{
RegisterModule("sceAudio", ARRAY_SIZE(sceAudio), sceAudio);
RegisterModule("sceAudio", ARRAY_SIZE(sceAudio), sceAudio);
}

View File

@ -36,46 +36,57 @@ enum PspAudioFrequencies { PSP_AUDIO_FREQ_44K = 44100, PSP_AUDIO_FREQ_48K = 48
#define SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED 0x80260008
#define SCE_ERROR_AUDIO_NOT_OUTPUT 0x80260009
#define MAX_CHANNEL 8
#define PSP_AUDIO_CHANNEL_MAX 8
struct AudioChannel
{
AudioChannel() {
AudioChannel() {
clear();
}
// PSP side
// PSP side
bool reserved;
bool reserved;
// last sample address
u32 sampleAddress;
u32 sampleCount; // Number of samples written in each OutputBlocking
int dataLen; // Probably internal queue size. Not currently used.
// last sample address
u32 sampleAddress;
u32 sampleCount; // Number of samples written in each OutputBlocking
int leftVolume;
int rightVolume;
int leftVolume;
int rightVolume;
int format;
int format;
SceUID waitingThread;
// PC side - should probably split out
// PC side - should probably split out
// We copy samples as they are written into this simple ring buffer.
// Might try something more efficient later.
FixedSizeQueue<s16, 32768> sampleQueue;
// We copy samples as they are written into this simple ring buffer.
// Might try something more efficient later.
FixedSizeQueue<s16, 32768> sampleQueue;
void clear() {
reserved = false;
void DoState(PointerWrap &p) {
p.Do(reserved);
p.Do(sampleAddress);
p.Do(sampleCount);
p.Do(leftVolume);
p.Do(rightVolume);
p.Do(format);
p.Do(waitingThread);
sampleQueue.DoState(p);
p.DoMarker("AudioChannel");
}
void clear() {
reserved = false;
waitingThread = 0;
leftVolume = 0;
rightVolume = 0;
format = 0;
sampleAddress = 0;
sampleCount = 0;
sampleQueue.clear();
}
sampleQueue.clear();
}
};
extern AudioChannel chans[8];

View File

@ -32,7 +32,7 @@
#define CTRL_MODE_ANALOG 1
const int PSP_CTRL_ERROR_INVALID_MODE = 0x80000107;
const int PSP_CTRL_ERROR_INVALID_NUM_BUFFERS = 0x80000104;
const int PSP_CTRL_ERROR_INVALID_IDLE_PTR = 0x80000023;
const int NUM_CTRL_BUFFERS = 64;
@ -61,7 +61,6 @@ struct CtrlLatch {
//////////////////////////////////////////////////////////////////////////
// STATE BEGIN
static bool ctrlInited = false;
static bool analogEnabled = false;
static int ctrlLatchBufs = 0;
static u32 ctrlOldButtons = 0;
@ -72,9 +71,16 @@ static int ctrlBuf = 0;
static int ctrlBufRead = 0;
static CtrlLatch latch;
static int ctrlIdleReset = -1;
static int ctrlIdleBack = -1;
static int ctrlCycle = 0;
static std::vector<SceUID> waitingThreads;
static std::recursive_mutex ctrlMutex;
static int ctrlTimer = -1;
// STATE END
//////////////////////////////////////////////////////////////////////////
@ -125,6 +131,13 @@ u32 __CtrlPeekButtons()
return ctrlCurrent.buttons;
}
u32 __CtrlReadLatch()
{
u32 ret = latch.btnMake;
__CtrlResetLatch();
return ret;
}
// Functions so that the rest of the emulator can control what the sceCtrl interface should return
// to the game:
@ -149,7 +162,7 @@ void __CtrlSetAnalog(float x, float y)
if (x < -1.0f) x = -1.0f;
if (y < -1.0f) y = -1.0f;
ctrlCurrent.analog[0] = (u8)(x * 127.f + 128.f);
ctrlCurrent.analog[1] = (u8)(y * 127.f + 128.f);
ctrlCurrent.analog[1] = (u8)(-y * 127.f + 128.f);
}
int __CtrlReadSingleBuffer(u32 ctrlDataPtr, bool negative)
@ -173,7 +186,7 @@ int __CtrlReadSingleBuffer(u32 ctrlDataPtr, bool negative)
int __CtrlReadBuffer(u32 ctrlDataPtr, u32 nBufs, bool negative, bool peek)
{
if (nBufs > NUM_CTRL_BUFFERS)
return PSP_CTRL_ERROR_INVALID_NUM_BUFFERS;
return SCE_KERNEL_ERROR_INVALID_SIZE;
int resetRead = ctrlBufRead;
@ -202,9 +215,9 @@ int __CtrlReadBuffer(u32 ctrlDataPtr, u32 nBufs, bool negative, bool peek)
return done;
}
void __CtrlVblank()
void __CtrlDoSample()
{
// When in vblank sampling mode, this samples the ctrl data into the buffers and updates the latch.
// This samples the ctrl data into the buffers and updates the latch.
__CtrlUpdateLatch();
// Wake up a single thread that was waiting for the buffer.
@ -226,15 +239,32 @@ retry:
}
}
void __CtrlVblank()
{
// This always runs, so make sure we're in vblank mode.
if (ctrlCycle == 0)
__CtrlDoSample();
}
void __CtrlTimerUpdate(u64 userdata, int cyclesLate)
{
// This only runs in timer mode (ctrlCycle > 0.)
_dbg_assert_msg_(HLE, ctrlCycle > 0, "Ctrl: sampling cycle should be > 0");
__CtrlDoSample();
CoreTiming::ScheduleEvent(usToCycles(ctrlCycle), ctrlTimer, 0);
}
void __CtrlInit()
{
std::lock_guard<std::recursive_mutex> guard(ctrlMutex);
ctrlTimer = CoreTiming::RegisterEvent("CtrlSampleTimer", __CtrlTimerUpdate);
__DisplayListenVblank(__CtrlVblank);
if (!ctrlInited)
{
__DisplayListenVblank(__CtrlVblank);
ctrlInited = true;
}
ctrlIdleReset = -1;
ctrlIdleBack = -1;
ctrlCycle = 0;
std::lock_guard<std::recursive_mutex> guard(ctrlMutex);
ctrlBuf = 1;
ctrlBufRead = 0;
@ -253,31 +283,64 @@ void __CtrlInit()
memcpy(&ctrlBufs[i], &ctrlCurrent, sizeof(_ctrl_data));
}
void sceCtrlInit()
void __CtrlDoState(PointerWrap &p)
{
__CtrlInit();
std::lock_guard<std::recursive_mutex> guard(ctrlMutex);
DEBUG_LOG(HLE,"sceCtrlInit");
RETURN(0);
p.Do(analogEnabled);
p.Do(ctrlLatchBufs);
p.Do(ctrlOldButtons);
p.DoVoid(ctrlBufs, sizeof(ctrlBufs));
p.Do(ctrlCurrent);
p.Do(ctrlBuf);
p.Do(ctrlBufRead);
p.Do(latch);
p.Do(ctrlIdleReset);
p.Do(ctrlIdleBack);
p.Do(ctrlCycle);
SceUID dv = 0;
p.Do(waitingThreads, dv);
p.Do(ctrlTimer);
CoreTiming::RestoreRegisterEvent(ctrlTimer, "CtrlSampleTimer", __CtrlTimerUpdate);
p.DoMarker("sceCtrl");
}
void __CtrlShutdown()
{
waitingThreads.clear();
}
u32 sceCtrlSetSamplingCycle(u32 cycle)
{
if (cycle == 0)
DEBUG_LOG(HLE, "sceCtrlSetSamplingCycle(%u)", cycle);
if ((cycle > 0 && cycle < 5555) || cycle > 20000)
{
// TODO: Change to vblank when we support something else.
DEBUG_LOG(HLE, "sceCtrlSetSamplingCycle(%u)", cycle);
WARN_LOG(HLE, "SCE_KERNEL_ERROR_INVALID_VALUE=sceCtrlSetSamplingCycle(%u)", cycle);
return SCE_KERNEL_ERROR_INVALID_VALUE;
}
else
{
ERROR_LOG(HLE, "UNIMPL sceCtrlSetSamplingCycle(%u)", cycle);
}
return 0;
u32 prev = ctrlCycle;
ctrlCycle = cycle;
if (prev > 0)
CoreTiming::UnscheduleEvent(ctrlTimer, 0);
if (cycle > 0)
CoreTiming::ScheduleEvent(usToCycles(ctrlCycle), ctrlTimer, 0);
return prev;
}
int sceCtrlGetSamplingCycle(u32 cyclePtr)
{
ERROR_LOG(HLE, "UNIMPL sceCtrlSetSamplingCycle(%08x)", cyclePtr);
DEBUG_LOG(HLE, "sceCtrlSetSamplingCycle(%08x)", cyclePtr);
if (Memory::IsValidAddress(cyclePtr))
Memory::Write_U32(ctrlCycle, cyclePtr);
return 0;
}
@ -297,6 +360,7 @@ u32 sceCtrlSetSamplingMode(u32 mode)
int sceCtrlGetSamplingMode(u32 modePtr)
{
u32 retVal = analogEnabled == true ? CTRL_MODE_ANALOG : CTRL_MODE_DIGITAL;
DEBUG_LOG(HLE, "%d=sceCtrlGetSamplingMode(%08x)", retVal, modePtr);
if (Memory::IsValidAddress(modePtr))
Memory::Write_U32(retVal, modePtr);
@ -304,10 +368,33 @@ int sceCtrlGetSamplingMode(u32 modePtr)
return 0;
}
void sceCtrlSetIdleCancelThreshold()
int sceCtrlSetIdleCancelThreshold(int idleReset, int idleBack)
{
ERROR_LOG(HLE,"UNIMPL sceCtrlSetIdleCancelThreshold");
RETURN(0);
DEBUG_LOG(HLE, "FAKE sceCtrlSetIdleCancelThreshold(%d, %d)", idleReset, idleBack);
if (idleReset < -1 || idleBack < -1 || idleReset > 128 || idleBack > 128)
return SCE_KERNEL_ERROR_INVALID_VALUE;
ctrlIdleReset = idleReset;
ctrlIdleBack = idleBack;
return 0;
}
int sceCtrlGetIdleCancelThreshold(u32 idleResetPtr, u32 idleBackPtr)
{
DEBUG_LOG(HLE, "sceCtrlSetIdleCancelThreshold(%08x, %08x)", idleResetPtr, idleBackPtr);
if (idleResetPtr && !Memory::IsValidAddress(idleResetPtr))
return PSP_CTRL_ERROR_INVALID_IDLE_PTR;
if (idleBackPtr && !Memory::IsValidAddress(idleBackPtr))
return PSP_CTRL_ERROR_INVALID_IDLE_PTR;
if (idleResetPtr)
Memory::Write_U32(ctrlIdleReset, idleResetPtr);
if (idleBackPtr)
Memory::Write_U32(ctrlIdleBack, idleBackPtr);
return 0;
}
void sceCtrlReadBufferPositive(u32 ctrlDataPtr, u32 nBufs)
@ -378,7 +465,7 @@ u32 sceCtrlReadLatch(u32 latchDataPtr)
static const HLEFunction sceCtrl[] =
{
{0x3E65A0EA, WrapV_V<sceCtrlInit>, "sceCtrlInit"}, //(int unknown), init with 0
{0x3E65A0EA, 0, "sceCtrlInit"}, //(int unknown), init with 0
{0x1f4011e6, WrapU_U<sceCtrlSetSamplingMode>, "sceCtrlSetSamplingMode"}, //(int on);
{0x6A2774F3, WrapU_U<sceCtrlSetSamplingCycle>, "sceCtrlSetSamplingCycle"},
{0x02BAAD91, WrapI_U<sceCtrlGetSamplingCycle>,"sceCtrlGetSamplingCycle"},
@ -393,8 +480,8 @@ static const HLEFunction sceCtrl[] =
{0xAF5960F3, 0, "sceCtrl_AF5960F3"},
{0xA68FD260, 0, "sceCtrlClearRapidFire"},
{0x6841BE1A, 0, "sceCtrlSetRapidFire"},
{0xa7144800, WrapV_V<sceCtrlSetIdleCancelThreshold>, "sceCtrlSetIdleCancelThreshold"},
{0x687660fa, 0, "sceCtrlGetIdleCancelThreshold"},
{0xa7144800, WrapI_II<sceCtrlSetIdleCancelThreshold>, "sceCtrlSetIdleCancelThreshold"},
{0x687660fa, WrapI_UU<sceCtrlGetIdleCancelThreshold>, "sceCtrlGetIdleCancelThreshold"},
};
void Register_sceCtrl()

View File

@ -17,6 +17,8 @@
#pragma once
#include "../../Common/ChunkFile.h"
void Register_sceCtrl();
#define CTRL_SQUARE 0x8000
@ -33,6 +35,8 @@ void Register_sceCtrl();
#define CTRL_RTRIGGER 0x0200
void __CtrlInit();
void __CtrlDoState(PointerWrap &p);
void __CtrlShutdown();
void __CtrlButtonDown(u32 buttonBit);
void __CtrlButtonUp(u32 buttonBit);
@ -40,4 +44,5 @@ void __CtrlButtonUp(u32 buttonBit);
void __CtrlSetAnalog(float x, float y);
// For use by internal UI like MsgDialog
u32 __CtrlPeekButtons();
u32 __CtrlPeekButtons();
u32 __CtrlReadLatch();

View File

@ -46,15 +46,19 @@
// Internal drawing library
#include "../Util/PPGeDraw.h"
extern ShaderManager shaderManager;
struct FrameBufferState
{
struct FrameBufferState {
u32 topaddr;
PspDisplayPixelFormat pspFramebufFormat;
int pspFramebufLinesize;
};
struct WaitVBlankInfo
{
WaitVBlankInfo(u32 tid) : threadID(tid), vcountUnblock(0) {}
u32 threadID;
int vcountUnblock; // what was this for again?
};
// STATE BEGIN
static FrameBufferState framebuf;
static FrameBufferState latchedFramebuf;
@ -63,15 +67,18 @@ static bool framebufIsLatched;
static int enterVblankEvent = -1;
static int leaveVblankEvent = -1;
static int hCount = 0;
static int hCountTotal = 0; //unused
static int vCount = 0;
static int isVblank = 0;
static bool hasSetMode = false;
double lastFrameTime = 0;
static int hCount;
static int hCountTotal; //unused
static int vCount;
static int isVblank;
static bool hasSetMode;
double lastFrameTime;
std::vector<WaitVBlankInfo> vblankWaitingThreads;
// STATE END
// Called when vblank happens (like an internal interrupt.) Not part of state, should be static.
std::vector<VblankCallback> vblankListeners;
// The vblank period is 731.5 us (0.7315 ms)
@ -83,19 +90,10 @@ enum {
PSP_DISPLAY_SETBUF_NEXTFRAME = 1
};
struct WaitVBlankInfo
{
u32 threadID;
int vcountUnblock;
};
std::vector<WaitVBlankInfo> vblankWaitingThreads;
void hleEnterVblank(u64 userdata, int cyclesLate);
void hleLeaveVblank(u64 userdata, int cyclesLate);
void __DisplayInit()
{
void __DisplayInit() {
gpuStats.reset();
hasSetMode = false;
framebufIsLatched = false;
@ -109,31 +107,66 @@ void __DisplayInit()
CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs), enterVblankEvent, 0);
isVblank = 0;
vCount = 0;
hCount = 0;
hCountTotal = 0;
lastFrameTime = 0;
InitGfxState();
}
void __DisplayShutdown()
{
void __DisplayDoState(PointerWrap &p) {
p.Do(framebuf);
p.Do(latchedFramebuf);
p.Do(framebufIsLatched);
p.Do(hCount);
p.Do(hCountTotal);
p.Do(vCount);
p.Do(isVblank);
p.Do(hasSetMode);
p.Do(lastFrameTime);
WaitVBlankInfo wvi(0);
p.Do(vblankWaitingThreads, wvi);
p.Do(enterVblankEvent);
CoreTiming::RestoreRegisterEvent(enterVblankEvent, "EnterVBlank", &hleEnterVblank);
p.Do(leaveVblankEvent);
CoreTiming::RestoreRegisterEvent(leaveVblankEvent, "LeaveVBlank", &hleLeaveVblank);
p.Do(gstate);
p.Do(gstate_c);
p.Do(gpuStats);
gpu->DoState(p);
ReapplyGfxState();
if (p.mode == p.MODE_READ) {
if (hasSetMode) {
gpu->InitClear();
}
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
}
p.DoMarker("sceDisplay");
}
void __DisplayShutdown() {
vblankListeners.clear();
vblankWaitingThreads.clear();
ShutdownGfxState();
}
void __DisplayListenVblank(VblankCallback callback)
{
void __DisplayListenVblank(VblankCallback callback) {
vblankListeners.push_back(callback);
}
void __DisplayFireVblank()
{
for (std::vector<VblankCallback>::iterator iter = vblankListeners.begin(), end = vblankListeners.end(); iter != end; ++iter)
{
void __DisplayFireVblank() {
for (std::vector<VblankCallback>::iterator iter = vblankListeners.begin(), end = vblankListeners.end(); iter != end; ++iter) {
VblankCallback cb = *iter;
cb();
}
}
void hleEnterVblank(u64 userdata, int cyclesLate)
{
void hleEnterVblank(u64 userdata, int cyclesLate) {
int vbCount = userdata;
DEBUG_LOG(HLE, "Enter VBlank %i", vbCount);
@ -144,16 +177,18 @@ void hleEnterVblank(u64 userdata, int cyclesLate)
__DisplayFireVblank();
// Wake up threads waiting for VBlank
__KernelTriggerWait(WAITTYPE_VBLANK, 0, true);
for (size_t i = 0; i < vblankWaitingThreads.size(); i++) {
__KernelResumeThreadFromWait(vblankWaitingThreads[i].threadID, 0);
}
vblankWaitingThreads.clear();
// Trigger VBlank interrupt handlers.
__TriggerInterrupt(PSP_VBLANK_INTR);
__TriggerInterrupt(PSP_INTR_IMMEDIATE | PSP_INTR_ONLY_IF_ENABLED, PSP_VBLANK_INTR);
CoreTiming::ScheduleEvent(msToCycles(vblankMs) - cyclesLate, leaveVblankEvent, vbCount+1);
// TODO: Should this be done here or in hleLeaveVblank?
if (framebufIsLatched)
{
if (framebufIsLatched) {
DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr);
framebuf = latchedFramebuf;
framebufIsLatched = false;
@ -171,37 +206,40 @@ void hleEnterVblank(u64 userdata, int cyclesLate)
// Now we can subvert the Ge engine in order to draw custom overlays like stat counters etc.
// Here we will be drawing to the non buffered front surface.
if (g_Config.bShowDebugStats)
{
if (g_Config.bShowDebugStats && gpuStats.numDrawCalls) {
gpu->UpdateStats();
char stats[512];
sprintf(stats,
"Frames: %i\n"
"Draw calls: %i\n"
"Draw flushes: %i\n"
"Vertices Transformed: %i\n"
"Textures active: %i\n"
"Textures decoded: %i\n"
"Texture invalidations: %i\n"
"Vertex shaders loaded: %i\n"
"Fragment shaders loaded: %i\n"
"Combined shaders loaded: %i\n",
gpuStats.numFrames,
gpuStats.numDrawCalls,
gpuStats.numFlushes,
gpuStats.numVertsTransformed,
gpuStats.numTextures,
gpuStats.numTexturesDecoded,
gpuStats.numTextureInvalidations,
gpuStats.numVertexShaders,
gpuStats.numFragmentShaders,
gpuStats.numShaders
);
float zoom = 0.7f * sqrtf(g_Config.iWindowZoom);
float zoom = 0.5f; /// g_Config.iWindowZoom;
PPGeBegin();
PPGeDrawText(stats, 2, 2, 0, zoom, 0x90000000);
PPGeDrawText(stats, 0, 0, 0, zoom);
PPGeDrawText(stats, 0, 0, 0, zoom, 0xFFc0c0c0);
PPGeEnd();
gpuStats.resetFrame();
}
host->EndFrame();
#ifdef _WIN32
@ -211,20 +249,23 @@ void hleEnterVblank(u64 userdata, int cyclesLate)
if (lastFrameTime == 0.0)
lastFrameTime = time_now_d();
if (!GetAsyncKeyState(VK_TAB)) {
while (time_now_d() < lastFrameTime + 1.0 / 60.0f) {
while (time_now_d() < lastFrameTime + 1.0 / 60.0) {
Common::SleepCurrentThread(1);
time_update();
}
lastFrameTime = time_now_d();
// Advance lastFrameTime by a constant amount each frame,
// but don't let it get too far behind.
lastFrameTime = std::max(lastFrameTime + 1.0 / 60.0, time_now_d() - 1.5 / 60.0);
}
// We are going to have to do something about audio timing for platforms that
// are vsynced to something that's not exactly 60fps..
#endif
host->BeginFrame();
gpu->BeginFrame();
shaderManager.DirtyShader();
shaderManager.DirtyUniform(DIRTY_ALL);
// Tell the emu core that it's time to stop emulating
// Win32 doesn't need this.
#ifndef _WIN32
@ -232,9 +273,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate)
#endif
}
void hleLeaveVblank(u64 userdata, int cyclesLate)
{
void hleLeaveVblank(u64 userdata, int cyclesLate) {
isVblank = 0;
DEBUG_LOG(HLE,"Leave VBlank %i", (int)userdata - 1);
vCount++;
@ -242,19 +281,16 @@ void hleLeaveVblank(u64 userdata, int cyclesLate)
CoreTiming::ScheduleEvent(msToCycles(frameMs - vblankMs) - cyclesLate, enterVblankEvent, userdata);
}
void sceDisplayIsVblank()
{
void sceDisplayIsVblank() {
DEBUG_LOG(HLE,"%i=sceDisplayIsVblank()",isVblank);
RETURN(isVblank);
}
u32 sceDisplaySetMode(u32 unknown, u32 xres, u32 yres)
{
u32 sceDisplaySetMode(u32 unknown, u32 xres, u32 yres) {
DEBUG_LOG(HLE,"sceDisplaySetMode(%d,%d,%d)",unknown,xres,yres);
host->BeginFrame();
if (!hasSetMode)
{
if (!hasSetMode) {
gpu->InitClear();
hasSetMode = true;
}
@ -262,9 +298,7 @@ u32 sceDisplaySetMode(u32 unknown, u32 xres, u32 yres)
return 0;
}
void sceDisplaySetFramebuf()
{
//host->EndFrame();
u32 sceDisplaySetFramebuf() {
u32 topaddr = PARAM(0);
int linesize = PARAM(1);
int pixelformat = PARAM(2);
@ -272,39 +306,36 @@ void sceDisplaySetFramebuf()
FrameBufferState fbstate;
DEBUG_LOG(HLE,"sceDisplaySetFramebuf(topaddr=%08x,linesize=%d,pixelsize=%d,sync=%d)",topaddr,linesize,pixelformat,sync);
if (topaddr == 0)
{
if (topaddr == 0) {
DEBUG_LOG(HLE,"- screen off");
}
else
{
} else {
fbstate.topaddr = topaddr;
fbstate.pspFramebufFormat = (PspDisplayPixelFormat)pixelformat;
fbstate.pspFramebufLinesize = linesize;
}
if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE)
{
if (sync == PSP_DISPLAY_SETBUF_IMMEDIATE) {
// Write immediately to the current framebuffer parameters
framebuf = fbstate;
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
}
else if (topaddr != 0)
{
if (topaddr != 0)
{
framebuf = fbstate;
gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat);
}
else
WARN_LOG(HLE, "%s: PSP_DISPLAY_SETBUF_IMMEDIATE without topaddr?", __FUNCTION__);
} else if (topaddr != 0) {
// Delay the write until vblank
latchedFramebuf = fbstate;
framebufIsLatched = true;
}
RETURN(0);
return 0;
}
u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, int mode)
{
u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, int mode) {
const FrameBufferState &fbState = mode == 1 ? latchedFramebuf : framebuf;
DEBUG_LOG(HLE,"sceDisplayGetFramebuf(*%08x = %08x, *%08x = %08x, *%08x = %08x, %i)",
topaddrPtr, fbState.topaddr, linesizePtr, fbState.pspFramebufLinesize, pixelFormatPtr, fbState.pspFramebufFormat, mode);
if (Memory::IsValidAddress(topaddrPtr))
Memory::Write_U32(fbState.topaddr, topaddrPtr);
if (Memory::IsValidAddress(linesizePtr))
@ -315,100 +346,94 @@ u32 sceDisplayGetFramebuf(u32 topaddrPtr, u32 linesizePtr, u32 pixelFormatPtr, i
return 0;
}
void sceDisplayWaitVblankStart()
{
void sceDisplayWaitVblankStart() {
DEBUG_LOG(HLE,"sceDisplayWaitVblankStart()");
vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread()));
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false);
}
void sceDisplayWaitVblank()
{
void sceDisplayWaitVblank() {
DEBUG_LOG(HLE,"sceDisplayWaitVblank()");
vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread()));
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false);
}
void sceDisplayWaitVblankStartMulti()
{
void sceDisplayWaitVblankStartMulti() {
DEBUG_LOG(HLE,"sceDisplayWaitVblankStartMulti()");
vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread()));
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false);
}
void sceDisplayWaitVblankCB()
{
DEBUG_LOG(HLE,"sceDisplayWaitVblankCB()");
void sceDisplayWaitVblankCB() {
DEBUG_LOG(HLE,"sceDisplayWaitVblankCB()");
vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread()));
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true);
__KernelCheckCallbacks();
}
void sceDisplayWaitVblankStartCB()
{
DEBUG_LOG(HLE,"sceDisplayWaitVblankStartCB()");
void sceDisplayWaitVblankStartCB() {
DEBUG_LOG(HLE,"sceDisplayWaitVblankStartCB()");
vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread()));
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true);
__KernelCheckCallbacks();
}
void sceDisplayWaitVblankStartMultiCB()
{
DEBUG_LOG(HLE,"sceDisplayWaitVblankStartMultiCB()");
void sceDisplayWaitVblankStartMultiCB() {
DEBUG_LOG(HLE,"sceDisplayWaitVblankStartMultiCB()");
vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread()));
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true);
__KernelCheckCallbacks();
}
void sceDisplayGetVcount()
{
u32 sceDisplayGetVcount() {
// Too spammy
// DEBUG_LOG(HLE,"%i=sceDisplayGetVcount()", vCount);
// Games like Puyo Puyo call this in a tight loop at end-of-frame. We could have it consume some time from CoreTiming?
// DEBUG_LOG(HLE,"%i=sceDisplayGetVcount()", vCount);
// Puyo Puyo Fever polls this as a substitute for waiting for vblank.
// As a result, the game never gets to reschedule so it doesn't mix audio and things break.
// Need to find a better hack as this breaks games like Project Diva.
// hleReSchedule("sceDisplayGetVcount hack"); // Puyo puyo hack?
CoreTiming::Idle(1000000);
RETURN(vCount);
return vCount;
}
void sceDisplayGetCurrentHcount()
{
void sceDisplayGetCurrentHcount() {
RETURN(hCount++);
}
void sceDisplayGetAccumulatedHcount()
{
void sceDisplayGetAccumulatedHcount() {
// Just do an estimate
u32 accumHCount = CoreTiming::GetTicks() / (222000000 / 60 / 272);
DEBUG_LOG(HLE,"%i=sceDisplayGetAccumulatedHcount()", accumHCount);
DEBUG_LOG(HLE,"%i=sceDisplayGetAccumulatedHcount()", accumHCount);
RETURN(accumHCount);
}
float sceDisplayGetFramePerSec()
{
float sceDisplayGetFramePerSec() {
float fps = 59.9400599f;
DEBUG_LOG(HLE,"%f=sceDisplayGetFramePerSec()", fps);
return fps; // (9MHz * 1)/(525 * 286)
return fps; // (9MHz * 1)/(525 * 286)
}
const HLEFunction sceDisplay[] =
{
{0x0E20F177,&WrapU_UUU<sceDisplaySetMode>, "sceDisplaySetMode"},
{0x289D82FE,sceDisplaySetFramebuf, "sceDisplaySetFramebuf"},
{0xEEDA2E54,&WrapU_UUUI<sceDisplayGetFramebuf>,"sceDisplayGetFrameBuf"},
const HLEFunction sceDisplay[] = {
{0x0E20F177,WrapU_UUU<sceDisplaySetMode>, "sceDisplaySetMode"},
{0x289D82FE,WrapU_V<sceDisplaySetFramebuf>, "sceDisplaySetFramebuf"},
{0xEEDA2E54,WrapU_UUUI<sceDisplayGetFramebuf>,"sceDisplayGetFrameBuf"},
{0x36CDFADE,sceDisplayWaitVblank, "sceDisplayWaitVblank"},
{0x984C27E7,sceDisplayWaitVblankStart, "sceDisplayWaitVblankStart"},
{0x40f1469c,sceDisplayWaitVblankStartMulti, "sceDisplayWaitVblankStartMulti"},
{0x8EB9EC49,sceDisplayWaitVblankCB, "sceDisplayWaitVblankCB"},
{0x46F186C3,sceDisplayWaitVblankStartCB, "sceDisplayWaitVblankStartCB"},
{0x77ed8b3a,sceDisplayWaitVblankStartMultiCB,"sceDisplayWaitVblankStartMultiCB"},
{0xdba6c4c4,&WrapF_V<sceDisplayGetFramePerSec>,"sceDisplayGetFramePerSec"},
{0xdba6c4c4,WrapF_V<sceDisplayGetFramePerSec>,"sceDisplayGetFramePerSec"},
{0x773dd3a3,sceDisplayGetCurrentHcount,"sceDisplayGetCurrentHcount"},
{0x210eab3a,sceDisplayGetAccumulatedHcount,"sceDisplayGetAccumulatedHcount"},
{0x9C6EAAD7,sceDisplayGetVcount,"sceDisplayGetVcount"},
{0x9C6EAAD7,WrapU_V<sceDisplayGetVcount>,"sceDisplayGetVcount"},
{0xDEA197D4,0,"sceDisplayGetMode"},
{0x7ED59BC4,0,"sceDisplaySetHoldMode"},
{0xA544C486,0,"sceDisplaySetResumeMode"},
{0xB4F378FA,0,"sceDisplayIsForeground"},
{0x31C4BAA8,0,"sceDisplayGetBrightness"},
{0x4D4E10EC,sceDisplayIsVblank,"sceDisplayIsVblank"},
};
void Register_sceDisplay()
{
void Register_sceDisplay() {
RegisterModule("sceDisplay", ARRAY_SIZE(sceDisplay), sceDisplay);
}

View File

@ -18,6 +18,8 @@
#pragma once
void __DisplayInit();
void __DisplayDoState(PointerWrap &p);
void __DisplayShutdown();
void Register_sceDisplay();
@ -25,4 +27,5 @@ void Register_sceDisplay();
bool __DisplayFrameDone();
typedef void (*VblankCallback)();
// Listen for vblank events. Only register during init.
void __DisplayListenVblank(VblankCallback callback);

View File

@ -8,27 +8,385 @@
#include "sceFont.h"
typedef u32 FontLibraryHandle;
typedef u32 FontHandle;
typedef struct {
u32 userDataAddr;
u32 numFonts;
u32 cacheDataAddr;
// Driver callbacks.
u32 allocFuncAddr;
u32 freeFuncAddr;
u32 openFuncAddr;
u32 closeFuncAddr;
u32 readFuncAddr;
u32 seekFuncAddr;
u32 errorFuncAddr;
u32 ioFinishFuncAddr;
} FontNewLibParams;
typedef enum {
FONT_FAMILY_SANS_SERIF = 1,
FONT_FAMILY_SERIF = 2,
} Family;
typedef enum {
FONT_STYLE_REGULAR = 1,
FONT_STYLE_ITALIC = 2,
FONT_STYLE_BOLD = 5,
FONT_STYLE_BOLD_ITALIC = 6,
FONT_STYLE_DB = 103, // Demi-Bold / semi-bold
} Style;
typedef enum {
FONT_LANGUAGE_JAPANESE = 1,
FONT_LANGUAGE_LATIN = 2,
FONT_LANGUAGE_KOREAN = 3,
} Language;
typedef struct {
float fontH;
float fontV;
float fontHRes;
float fontVRes;
float fontWeight;
u16 fontFamily;
u16 fontStyle;
// Check.
u16 fontStyleSub;
u16 fontLanguage;
u16 fontRegion;
u16 fontCountry;
char fontName[64];
char fontFileName[64];
u32 fontAttributes;
u32 fontExpire;
} FontStyle;
typedef struct {
// Glyph metrics (in 26.6 signed fixed-point).
u32 maxGlyphWidthI;
u32 maxGlyphHeightI;
u32 maxGlyphAscenderI;
u32 maxGlyphDescenderI;
u32 maxGlyphLeftXI;
u32 maxGlyphBaseYI;
u32 minGlyphCenterXI;
u32 maxGlyphTopYI;
u32 maxGlyphAdvanceXI;
u32 maxGlyphAdvanceYI;
// Glyph metrics (replicated as float).
float maxGlyphWidthF;
float maxGlyphHeightF;
float maxGlyphAscenderF;
float maxGlyphDescenderF;
float maxGlyphLeftXF;
float maxGlyphBaseYF;
float minGlyphCenterXF;
float maxGlyphTopYF;
float maxGlyphAdvanceXF;
float maxGlyphAdvanceYF;
// Bitmap dimensions.
short maxGlyphWidth;
short maxGlyphHeight;
u32 charMapLength; // Number of elements in the font's charmap.
u32 shadowMapLength; // Number of elements in the font's shadow charmap.
// Font style (used by font comparison functions).
FontStyle fontStyle;
u8 BPP; // Font's BPP.
u8 pad[3];
} FontInfo;
typedef struct {
u32 bitmapWidth;
u32 bitmapHeight;
u32 bitmapLeft;
u32 bitmapTop;
// Glyph metrics (in 26.6 signed fixed-point).
u32 spf26Width;
u32 spf26Height;
s32 spf26Ascender;
s32 spf26Descender;
s32 spf26BearingHX;
s32 spf26BearingHY;
s32 spf26BearingVX;
s32 spf26BearingVY;
s32 spf26AdvanceH;
s32 spf26AdvanceV;
u8 pad[4];
} CharInfo;
typedef enum {
PSP_FONT_PIXELFORMAT_4 = 0,
PSP_FONT_PIXELFORMAT_4_REV = 1,
PSP_FONT_PIXELFORMAT_8 = 2,
PSP_FONT_PIXELFORMAT_24 = 3,
PSP_FONT_PIXELFORMAT_32 = 4
} FontPixelFormat;
typedef struct {
FontPixelFormat pixelFormat;
s32 xPos64;
s32 yPos64;
u16 bufWidth;
u16 bufHeight;
u16 bytesPerLine;
u16 pad;
u32 bufferPtr;
} GlyphImage;
FontNewLibParams fontLib;
void __FontInit()
{
memset(&fontLib, 0, sizeof(fontLib));
}
void __FontDoState(PointerWrap &p)
{
p.Do(fontLib);
p.DoMarker("sceFont");
}
u32 sceFontNewLib(u32 FontNewLibParamsPtr, u32 errorCodePtr)
{
ERROR_LOG(HLE, "sceFontNewLib %x, %x", FontNewLibParamsPtr, errorCodePtr);
if (Memory::IsValidAddress(FontNewLibParamsPtr)&&Memory::IsValidAddress(errorCodePtr))
{
Memory::Write_U32(0, errorCodePtr);
Memory::ReadStruct(FontNewLibParamsPtr, &fontLib);
}
return 1;
}
int sceFontDoneLib(u32 FontLibraryHandlePtr)
{
ERROR_LOG(HLE, "sceFontDoneLib %x", FontLibraryHandlePtr);
return 0;
}
u32 sceFontOpen(u32 libHandle, u32 index, u32 mode, u32 errorCodePtr)
{
ERROR_LOG(HLE, "sceFontDoneLib %x, %x, %x, %x", libHandle, index, mode, errorCodePtr);
if (Memory::IsValidAddress(errorCodePtr))
{
Memory::Write_U32(0, errorCodePtr);
}
return 1;
}
u32 sceFontOpenUserMemory(u32 libHandle, u32 memoryFontAddrPtr, u32 memoryFontLength, u32 errorCodePtr)
{
ERROR_LOG(HLE, "sceFontOpenUserMemory %x, %x, %x, %x", libHandle, memoryFontAddrPtr, memoryFontLength, errorCodePtr);
if (Memory::IsValidAddress(errorCodePtr))
{
Memory::Write_U32(0, errorCodePtr);
}
return 1;
}
u32 sceFontOpenUserFile(u32 libHandle, u32 fileNamePtr, u32 mode, u32 errorCodePtr)
{
ERROR_LOG(HLE, "sceFontOpenUserFile %x, %x, %x, %x", libHandle, fileNamePtr, mode, errorCodePtr);
if (Memory::IsValidAddress(errorCodePtr))
{
Memory::Write_U32(0, errorCodePtr);
}
return 1;
}
int sceFontClose(u32 fontHandle)
{
ERROR_LOG(HLE, "sceFontClose %x", fontHandle);
return 0;
}
int sceFontGetNumFontList(u32 libHandle, u32 errorCodePtr)
{
ERROR_LOG(HLE, "sceFontGetNumFontList %x, %x", libHandle, errorCodePtr);
if (Memory::IsValidAddress(errorCodePtr))
{
Memory::Write_U32(0, errorCodePtr);
}
return 1;
}
int sceFontFindOptimumFont(u32 libHandlePtr, u32 fontStylePtr, u32 errorCodePtr)
{
ERROR_LOG(HLE, "sceFontFindOptimumFont %x, %x, %x", libHandlePtr, fontStylePtr, errorCodePtr);
if (Memory::IsValidAddress(errorCodePtr))
{
Memory::Write_U32(0, errorCodePtr);
}
return 1;
}
int sceFontFindFont(u32 libHandlePtr, u32 fontStylePtr, u32 errorCodePtr)
{
ERROR_LOG(HLE, "sceFontFindFont %x, %x, %x", libHandlePtr, fontStylePtr, errorCodePtr);
return 1;
}
int sceFontGetFontInfo(u32 fontHandle, u32 fontInfoPtr)
{
ERROR_LOG(HLE, "sceFontGetFontInfo %x, %x", fontHandle, fontInfoPtr);
FontInfo fi;
memset (&fi, 0, sizeof(fi));
if (Memory::IsValidAddress(fontInfoPtr))
{
fi.BPP = 4;
fi.charMapLength = 255;
fi.maxGlyphAdvanceXF = 2.0;
fi.maxGlyphAdvanceXI = 2;
fi.maxGlyphAdvanceYF = 2.0;
fi.maxGlyphAdvanceYI = 32 << 6;
fi.maxGlyphAscenderF = 32 << 6;
fi.maxGlyphAscenderI = 32 << 6;
fi.maxGlyphBaseYF = 0.0;
fi.maxGlyphBaseYI = 0;
fi.maxGlyphDescenderF = 0;
fi.maxGlyphDescenderI = 0;
fi.maxGlyphHeight = 32;
fi.maxGlyphHeightF = 32;
fi.maxGlyphHeightI = 32;
fi.maxGlyphLeftXF = 0;
fi.maxGlyphLeftXI = 0;
fi.maxGlyphTopYF = 0;
fi.maxGlyphTopYI = 0;
fi.maxGlyphWidth = 32;
fi.maxGlyphWidthF = 32;
fi.maxGlyphWidthI = 32;
fi.minGlyphCenterXF = 16;
fi.minGlyphCenterXI = 16;
fi.shadowMapLength = 0;
fi.fontStyle.fontAttributes=1;
fi.fontStyle.fontCountry= 1;
fi.fontStyle.fontExpire= 1;
fi.fontStyle.fontFamily= 1;
//fi.fontStyle.fontFileName="asd";
fi.fontStyle.fontH=32;
fi.fontStyle.fontHRes=32;
fi.fontStyle.fontLanguage=1;
// fi.fontStyle.fontName="ppsspp";
fi.fontStyle.fontRegion=9;
fi.fontStyle.fontV=32;
fi.fontStyle.fontVRes=32;
fi.fontStyle.fontWeight= 32;
Memory::WriteStruct(fontInfoPtr, &fi);
}
return 0;
}
int sceFontGetFontInfoByIndexNumber(u32 libHandle, u32 fontInfoPtr, u32 unknown, u32 fontIndex)
{
ERROR_LOG(HLE, "sceFontGetFontInfoByIndexNumber %x, %x, %x, %x", libHandle, fontInfoPtr, unknown, fontIndex);
// clearly wrong..
return sceFontGetFontInfo(libHandle, fontInfoPtr);
}
int sceFontGetCharInfo(u32 libHandler, u32 charCode, u32 charInfoPtr)
{
ERROR_LOG(HLE, "sceFontGetCharInfo %x, %x, %x", libHandler, charCode, charInfoPtr);
if (Memory::IsValidAddress(charInfoPtr))
{
CharInfo pspCharInfo;
memset(&pspCharInfo, 0, sizeof(pspCharInfo));
pspCharInfo.bitmapWidth = 32;
pspCharInfo.bitmapHeight = 32;
pspCharInfo.spf26Width = pspCharInfo.bitmapWidth << 6;
pspCharInfo.spf26Height = pspCharInfo.bitmapHeight << 6;
pspCharInfo.spf26AdvanceH = pspCharInfo.bitmapWidth << 6;
pspCharInfo.spf26AdvanceV = pspCharInfo.bitmapHeight << 6;
Memory::WriteStruct(charInfoPtr, &pspCharInfo);
}
return 0;
}
int sceFontGetCharGlyphImage(u32 libHandler, u32 charCode, u32 glyphImagePtr)
{
ERROR_LOG(HLE, "sceFontGetCharGlyphImage %x, %x, %x (%c)", libHandler, charCode, glyphImagePtr, charCode);
int pixelFormat = Memory::Read_U32(glyphImagePtr);
int xPos64 = Memory::Read_U32(glyphImagePtr+4);
int yPos64 = Memory::Read_U32(glyphImagePtr+8);
int bufWidth = Memory::Read_U16(glyphImagePtr+12);
int bufHeight = Memory::Read_U16(glyphImagePtr+14);
int bytesPerLine = Memory::Read_U16(glyphImagePtr+16);
int buffer =Memory::Read_U32(glyphImagePtr+20);
for (int y= 0; y < bufHeight; y++)
{
for (int x=0; x<bytesPerLine; x++)
{
Memory::Write_U8(0xff, buffer + (x * y));
}
}
return 0;
}
int sceFontGetCharGlyphImage_Clip(u32 libHandler, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight)
{
ERROR_LOG(HLE, "sceFontGetCharGlyphImage_Clip %x, %x, %x (%c)", libHandler, charCode, glyphImagePtr, charCode);
//sceFontGetCharGlyphImage(libHandler, charCode, glyphImagePtr);
return 0;
}
int sceFontGetFontList(u32 fontLibHandle, u32 fontStylePtr, u32 numFonts)
{
ERROR_LOG(HLE, "sceFontGetFontList %x, %x, %x", fontLibHandle, fontStylePtr, numFonts);
FontStyle style;
memset(&style, 0, sizeof (style));
style.fontH = 20 / 64.f;
style.fontV = 20 / 64.f;
style.fontHRes = 20 / 64.f;
style.fontVRes = 20 / 64.f;
style.fontStyle = 1;
for (u32 i = 0; i < numFonts; i++)
{
Memory::WriteStruct(fontStylePtr+ (sizeof(style)), &style);
}
return 0;
}
const HLEFunction sceLibFont[] =
{
{0x67f17ed7, 0, "sceFontNewLib"},
{0x574b6fbc, 0, "sceFontDoneLib"},
{0x67f17ed7, WrapU_UU<sceFontNewLib>, "sceFontNewLib"},
{0x574b6fbc, WrapI_U<sceFontDoneLib>, "sceFontDoneLib"},
{0x48293280, 0, "sceFontSetResolution"},
{0x27f6e642, 0, "sceFontGetNumFontList"},
{0xbc75d85b, 0, "sceFontGetFontList"},
{0x099ef33c, 0, "sceFontFindOptimumFont"},
{0x681e61a7, 0, "sceFontFindFont"},
{0x27f6e642, WrapI_UU<sceFontGetNumFontList>, "sceFontGetNumFontList"},
{0xbc75d85b, WrapI_UUU<sceFontGetFontList>, "sceFontGetFontList"},
{0x099ef33c, WrapI_UUU<sceFontFindOptimumFont>, "sceFontFindOptimumFont"},
{0x681e61a7, WrapI_UUU<sceFontFindFont>, "sceFontFindFont"},
{0x2f67356a, 0, "sceFontCalcMemorySize"},
{0x5333322d, 0, "sceFontGetFontInfoByIndexNumber"},
{0xa834319d, 0, "sceFontOpen"},
{0x57fcb733, 0, "sceFontOpenUserFile"},
{0xbb8e7fe6, 0, "sceFontOpenUserMemory"},
{0x3aea8cb6, 0, "sceFontClose"},
{0x0da7535e, 0, "sceFontGetFontInfo"},
{0xdcc80c2f, 0, "sceFontGetCharInfo"},
{0x5333322d, WrapI_UUUU<sceFontGetFontInfoByIndexNumber>, "sceFontGetFontInfoByIndexNumber"},
{0xa834319d, WrapU_UUUU<sceFontOpen>, "sceFontOpen"},
{0x57fcb733, WrapU_UUUU<sceFontOpenUserFile>, "sceFontOpenUserFile"},
{0xbb8e7fe6, WrapU_UUUU<sceFontOpenUserMemory>, "sceFontOpenUserMemory"},
{0x3aea8cb6, WrapI_U<sceFontClose>, "sceFontClose"},
{0x0da7535e, WrapI_UU<sceFontGetFontInfo>, "sceFontGetFontInfo"},
{0xdcc80c2f, WrapI_UUU<sceFontGetCharInfo>, "sceFontGetCharInfo"},
{0x5c3e4a9e, 0, "sceFontGetCharImageRect"},
{0x980f4895, 0, "sceFontGetCharGlyphImage"},
{0xca1e6945, 0, "sceFontGetCharGlyphImage_Clip"},
{0x980f4895, WrapI_UUU<sceFontGetCharGlyphImage>, "sceFontGetCharGlyphImage"},
{0xca1e6945, WrapI_UUUIIII<sceFontGetCharGlyphImage_Clip>, "sceFontGetCharGlyphImage_Clip"},
{0x74b21701, 0, "sceFontPixelToPointH"},
{0xf8f0752e, 0, "sceFontPixelToPointV"},
{0x472694cd, 0, "sceFontPointToPixelH"},

View File

@ -1,3 +1,8 @@
#pragma once
#include "../../Common/ChunkFile.h"
void Register_sceFont();
void __FontInit();
void __FontDoState(PointerWrap &p);

View File

@ -25,12 +25,20 @@
#include "../GPU/GPUState.h"
#include "../GPU/GPUInterface.h"
// TODO: This doesn't really belong here
static int state;
static PspGeCallbackData ge_callback_data[16];
static bool ge_used_callbacks[16] = {0};
void __GeInit()
{
state = 0;
memset(&ge_used_callbacks, 0, sizeof(ge_used_callbacks));
}
void __GeDoState(PointerWrap &p)
{
p.DoArray(ge_callback_data, ARRAY_SIZE(ge_callback_data));
p.DoArray(ge_used_callbacks, ARRAY_SIZE(ge_used_callbacks));
// Everything else is done in sceDisplay.
p.DoMarker("sceGe");
}
void __GeShutdown()
@ -55,7 +63,29 @@ u32 sceGeEdramGetSize()
return retVal;
}
u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId,
// TODO: Probably shouldn't use an interrupt?
int __GeSubIntrBase(int callbackId)
{
// Negative means don't use.
if (callbackId < 0)
return 0;
if (callbackId >= (int)(ARRAY_SIZE(ge_used_callbacks)))
{
WARN_LOG(HLE, "Unexpected (too high) GE callback id %d, ignoring", callbackId);
return 0;
}
if (!ge_used_callbacks[callbackId])
{
WARN_LOG(HLE, "Unregistered GE callback id %d, ignoring", callbackId);
return 0;
}
return (callbackId + 1) << 16;
}
u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, int callbackId,
u32 optParamAddr)
{
DEBUG_LOG(HLE,
@ -63,19 +93,14 @@ u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId,
listAddress, stallAddress, callbackId, optParamAddr);
//if (!stallAddress)
// stallAddress = listAddress;
u32 listID = gpu->EnqueueList(listAddress, stallAddress);
// HACKY
if (listID)
state = SCE_GE_LIST_STALLING;
else
state = SCE_GE_LIST_COMPLETED;
u32 listID = gpu->EnqueueList(listAddress, stallAddress, __GeSubIntrBase(callbackId), false);
DEBUG_LOG(HLE, "List %i enqueued.", listID);
//return display list ID
return listID;
}
u32 sceGeListEnQueueHead(u32 listAddress, u32 stallAddress, u32 callbackId,
u32 sceGeListEnQueueHead(u32 listAddress, u32 stallAddress, int callbackId,
u32 optParamAddr)
{
DEBUG_LOG(HLE,
@ -83,29 +108,35 @@ u32 sceGeListEnQueueHead(u32 listAddress, u32 stallAddress, u32 callbackId,
listAddress, stallAddress, callbackId, optParamAddr);
//if (!stallAddress)
// stallAddress = listAddress;
u32 listID = gpu->EnqueueList(listAddress, stallAddress);
// HACKY
if (listID)
state = SCE_GE_LIST_STALLING;
else
state = SCE_GE_LIST_COMPLETED;
u32 listID = gpu->EnqueueList(listAddress, stallAddress, __GeSubIntrBase(callbackId), true);
DEBUG_LOG(HLE, "List %i enqueued.", listID);
//return display list ID
return listID;
}
void sceGeListUpdateStallAddr(u32 displayListID, u32 stallAddress)
int sceGeListDeQueue(u32 listID)
{
ERROR_LOG(HLE, "UNIMPL sceGeListDeQueue(%08x)", listID);
return 0;
}
int sceGeListUpdateStallAddr(u32 displayListID, u32 stallAddress)
{
DEBUG_LOG(HLE, "sceGeListUpdateStallAddr(dlid=%i,stalladdr=%08x)",
displayListID, stallAddress);
gpu->UpdateStall(displayListID, stallAddress);
return 0;
}
void sceGeListSync(u32 displayListID, u32 mode) //0 : wait for completion 1:check and return
int sceGeListSync(u32 displayListID, u32 mode) //0 : wait for completion 1:check and return
{
DEBUG_LOG(HLE, "sceGeListSync(dlid=%08x, mode=%08x)", displayListID, mode);
if(mode == 1) {
return gpu->listStatus(displayListID);
}
return 0;
}
u32 sceGeDrawSync(u32 mode)
@ -117,47 +148,82 @@ u32 sceGeDrawSync(u32 mode)
return 0;
}
void sceGeContinue()
int sceGeContinue()
{
ERROR_LOG(HLE, "UNIMPL sceGeContinue");
// no arguments
return 0;
}
void sceGeBreak(u32 mode)
int sceGeBreak(u32 mode)
{
//mode => 0 : current dlist 1: all drawing
ERROR_LOG(HLE, "UNIMPL sceGeBreak(mode=%d)", mode);
return 0;
}
u32 sceGeSetCallback(u32 structAddr)
{
DEBUG_LOG(HLE, "sceGeSetCallback(struct=%08x)", structAddr);
PspGeCallbackData ge_callback_data;
Memory::ReadStruct(structAddr, &ge_callback_data);
int cbID = -1;
for (int i = 0; i < ARRAY_SIZE(ge_used_callbacks); ++i)
if (!ge_used_callbacks[i])
{
cbID = i;
break;
}
if (ge_callback_data.finish_func)
if (cbID == -1)
{
sceKernelRegisterSubIntrHandler(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH,
ge_callback_data.finish_func, ge_callback_data.finish_arg);
sceKernelEnableSubIntr(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH);
}
if (ge_callback_data.signal_func)
{
sceKernelRegisterSubIntrHandler(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL,
ge_callback_data.signal_func, ge_callback_data.signal_arg);
sceKernelEnableSubIntr(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL);
WARN_LOG(HLE, "sceGeSetCallback(): out of callback ids");
return SCE_KERNEL_ERROR_OUT_OF_MEMORY;
}
// TODO: This should return a callback ID
return 0;
ge_used_callbacks[cbID] = true;
Memory::ReadStruct(structAddr, &ge_callback_data[cbID]);
int subIntrBase = __GeSubIntrBase(cbID);
if (ge_callback_data[cbID].finish_func)
{
sceKernelRegisterSubIntrHandler(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_FINISH,
ge_callback_data[cbID].finish_func, ge_callback_data[cbID].finish_arg);
sceKernelEnableSubIntr(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_FINISH);
}
if (ge_callback_data[cbID].signal_func)
{
sceKernelRegisterSubIntrHandler(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_SIGNAL,
ge_callback_data[cbID].signal_func, ge_callback_data[cbID].signal_arg);
sceKernelEnableSubIntr(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_SIGNAL);
}
return cbID;
}
void sceGeUnsetCallback(u32 cbID) {
int sceGeUnsetCallback(u32 cbID)
{
DEBUG_LOG(HLE, "sceGeUnsetCallback(cbid=%08x)", cbID);
sceKernelReleaseSubIntrHandler(PSP_GE_INTR, PSP_GE_SUBINTR_FINISH);
sceKernelReleaseSubIntrHandler(PSP_GE_INTR, PSP_GE_SUBINTR_SIGNAL);
if (cbID >= ARRAY_SIZE(ge_used_callbacks))
{
WARN_LOG(HLE, "sceGeUnsetCallback(cbid=%08x): invalid callback id", cbID);
return SCE_KERNEL_ERROR_INVALID_ID;
}
if (ge_used_callbacks[cbID])
{
int subIntrBase = __GeSubIntrBase(cbID);
sceKernelReleaseSubIntrHandler(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_FINISH);
sceKernelReleaseSubIntrHandler(PSP_GE_INTR, subIntrBase | PSP_GE_SUBINTR_SIGNAL);
}
else
WARN_LOG(HLE, "sceGeUnsetCallback(cbid=%08x): ignoring unregistered callback id", cbID);
ge_used_callbacks[cbID] = false;
return 0;
}
// Points to 512 32-bit words, where we can probably layout the context however we want
@ -165,7 +231,7 @@ void sceGeUnsetCallback(u32 cbID) {
u32 sceGeSaveContext(u32 ctxAddr)
{
DEBUG_LOG(HLE, "sceGeSaveContext(%08x)", ctxAddr);
gpu->Flush();
if (sizeof(gstate) > 512 * 4)
{
ERROR_LOG(HLE, "AARGH! sizeof(gstate) has grown too large!");
@ -186,6 +252,7 @@ u32 sceGeSaveContext(u32 ctxAddr)
u32 sceGeRestoreContext(u32 ctxAddr)
{
DEBUG_LOG(HLE, "sceGeRestoreContext(%08x)", ctxAddr);
gpu->Flush();
if (sizeof(gstate) > 512 * 4)
{
@ -202,9 +269,48 @@ u32 sceGeRestoreContext(u32 ctxAddr)
return 0;
}
void sceGeGetMtx()
int sceGeGetMtx(int type, u32 matrixPtr)
{
ERROR_LOG(HLE, "UNIMPL sceGeGetMtx()");
if (!Memory::IsValidAddress(matrixPtr)) {
ERROR_LOG(HLE, "sceGeGetMtx(%d, %08x) - bad matrix ptr", type, matrixPtr);
return -1;
}
INFO_LOG(HLE, "sceGeGetMtx(%d, %08x)", type, matrixPtr);
switch (type) {
case GE_MTX_BONE0:
case GE_MTX_BONE1:
case GE_MTX_BONE2:
case GE_MTX_BONE3:
case GE_MTX_BONE4:
case GE_MTX_BONE5:
case GE_MTX_BONE6:
case GE_MTX_BONE7:
{
int n = type - GE_MTX_BONE0;
Memory::Memcpy(matrixPtr, gstate.boneMatrix + n * 12, 12 * sizeof(float));
}
break;
case GE_MTX_TEXGEN:
Memory::Memcpy(matrixPtr, gstate.tgenMatrix, 12 * sizeof(float));
break;
case GE_MTX_WORLD:
Memory::Memcpy(matrixPtr, gstate.worldMatrix, 12 * sizeof(float));
break;
case GE_MTX_VIEW:
Memory::Memcpy(matrixPtr, gstate.viewMatrix, 12 * sizeof(float));
break;
case GE_MTX_PROJECTION:
Memory::Memcpy(matrixPtr, gstate.projMatrix, 16 * sizeof(float));
break;
}
return 0;
}
u32 sceGeGetCmd(int cmd)
{
INFO_LOG(HLE, "sceGeGetCmd(%i)", cmd);
return gstate.cmdmem[cmd]; // Does not mask away the high bits.
}
u32 sceGeEdramSetAddrTranslation(int new_size)
@ -218,23 +324,23 @@ u32 sceGeEdramSetAddrTranslation(int new_size)
const HLEFunction sceGe_user[] =
{
{0xE47E40E4,&WrapU_V<sceGeEdramGetAddr>, "sceGeEdramGetAddr"},
{0xAB49E76A,&WrapU_UUUU<sceGeListEnQueue>, "sceGeListEnQueue"},
{0x1C0D95A6,&WrapU_UUUU<sceGeListEnQueueHead>, "sceGeListEnQueueHead"},
{0xE0D68148,&WrapV_UU<sceGeListUpdateStallAddr>, "sceGeListUpdateStallAddr"},
{0x03444EB4,&WrapV_UU<sceGeListSync>, "sceGeListSync"},
{0xB287BD61,&WrapU_U<sceGeDrawSync>, "sceGeDrawSync"},
{0xB448EC0D,&WrapV_U<sceGeBreak>, "sceGeBreak"},
{0x4C06E472,sceGeContinue, "sceGeContinue"},
{0xA4FC06A4,&WrapU_U<sceGeSetCallback>, "sceGeSetCallback"},
{0x05DB22CE,&WrapV_U<sceGeUnsetCallback>, "sceGeUnsetCallback"},
{0x1F6752AD,&WrapU_V<sceGeEdramGetSize>, "sceGeEdramGetSize"},
{0xB77905EA,&WrapU_I<sceGeEdramSetAddrTranslation>,"sceGeEdramSetAddrTranslation"},
{0xDC93CFEF,0,"sceGeGetCmd"},
{0x57C8945B,&sceGeGetMtx,"sceGeGetMtx"},
{0x438A385A,&WrapU_U<sceGeSaveContext>,"sceGeSaveContext"},
{0x0BF608FB,&WrapU_U<sceGeRestoreContext>,"sceGeRestoreContext"},
{0x5FB86AB0,0,"sceGeListDeQueue"},
{0xE47E40E4, WrapU_V<sceGeEdramGetAddr>, "sceGeEdramGetAddr"},
{0xAB49E76A, WrapU_UUIU<sceGeListEnQueue>, "sceGeListEnQueue"},
{0x1C0D95A6, WrapU_UUIU<sceGeListEnQueueHead>, "sceGeListEnQueueHead"},
{0xE0D68148, WrapI_UU<sceGeListUpdateStallAddr>, "sceGeListUpdateStallAddr"},
{0x03444EB4, WrapI_UU<sceGeListSync>, "sceGeListSync"},
{0xB287BD61, WrapU_U<sceGeDrawSync>, "sceGeDrawSync"},
{0xB448EC0D, WrapI_U<sceGeBreak>, "sceGeBreak"},
{0x4C06E472, WrapI_V<sceGeContinue>, "sceGeContinue"},
{0xA4FC06A4, WrapU_U<sceGeSetCallback>, "sceGeSetCallback"},
{0x05DB22CE, WrapI_U<sceGeUnsetCallback>, "sceGeUnsetCallback"},
{0x1F6752AD, WrapU_V<sceGeEdramGetSize>, "sceGeEdramGetSize"},
{0xB77905EA, WrapU_I<sceGeEdramSetAddrTranslation>, "sceGeEdramSetAddrTranslation"},
{0xDC93CFEF, WrapU_I<sceGeGetCmd>, "sceGeGetCmd"},
{0x57C8945B, WrapI_IU<sceGeGetMtx>, "sceGeGetMtx"},
{0x438A385A, WrapU_U<sceGeSaveContext>, "sceGeSaveContext"},
{0x0BF608FB, WrapU_U<sceGeRestoreContext>, "sceGeRestoreContext"},
{0x5FB86AB0, WrapI_U<sceGeListDeQueue>, "sceGeListDeQueue"},
};
void Register_sceGe_user()

View File

@ -37,6 +37,7 @@ typedef struct PspGeCallbackData
void Register_sceGe_user();
void __GeInit();
void __GeDoState(PointerWrap &p);
void __GeShutdown();
@ -44,4 +45,4 @@ void __GeShutdown();
u32 sceGeRestoreContext(u32 ctxAddr);
u32 sceGeSaveContext(u32 ctxAddr);
u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, u32 callbackId, u32 optParamAddr);
u32 sceGeListEnQueue(u32 listAddress, u32 stallAddress, int callbackId, u32 optParamAddr);

View File

@ -20,29 +20,44 @@
#include "sceCtrl.h"
u32 sceHprmPeekCurrentKey(u32 keyAddress)
{
u32 sceHprmPeekCurrentKey(u32 keyAddress) {
INFO_LOG(HLE,"0=sceHprmPeekCurrentKey(ptr)");
Memory::Write_U32(0, keyAddress);
return 0;
}
// TODO: Might make sense to reflect the headphone status of the host here,
// if the games adjust their sound.
u32 sceHprmIsHeadphoneExist() {
DEBUG_LOG(HLE, "sceHprmIsHeadphoneExist()");
return 0;
}
u32 sceHprmIsMicrophoneExist() {
DEBUG_LOG(HLE, "sceHprmIsMicrophoneExist()");
return 0;
}
u32 sceHprmIsRemoteExist() {
DEBUG_LOG(HLE, "sceHprmIsRemoteExist()");
return 0;
}
const HLEFunction sceHprm[] =
{
{0x089fdfa4, 0, "sceHprm_0x089fdfa4"},
{0x1910B327, &WrapU_U<sceHprmPeekCurrentKey>, "sceHprmPeekCurrentKey"},
{0x208DB1BD, 0, "sceHprmIsRemoteExist"},
{0x7E69EDA4, 0, "sceHprmIsHeadphoneExist"},
{0x219C58F1, 0, "sceHprmIsMicrophoneExist"},
{0x208DB1BD, WrapU_V<sceHprmIsRemoteExist>, "sceHprmIsRemoteExist"},
{0x7E69EDA4, WrapU_V<sceHprmIsHeadphoneExist>, "sceHprmIsHeadphoneExist"},
{0x219C58F1, WrapU_V<sceHprmIsMicrophoneExist>, "sceHprmIsMicrophoneExist"},
{0xC7154136, 0, "sceHprmRegisterCallback"},
{0x444ED0B7, 0, "sceHprmUnregisterCallback"},
{0x1910B327, 0, "sceHprmPeekCurrentKey"},
{0x2BCEC83E, 0, "sceHprmPeekLatch"},
{0x40D2F9F0, 0, "sceHprmReadLatch"},
};
const int sceHprmCount = ARRAY_SIZE(sceHprm);
void Register_sceHprm()
{
RegisterModule("sceHprm", sceHprmCount, sceHprm);
RegisterModule("sceHprm", ARRAY_SIZE(sceHprm), sceHprm);
}

View File

@ -33,9 +33,24 @@ const int PSP_LANGUAGE_KOREAN = 9;
const int PSP_LANGUAGE_TRADITIONAL_CHINESE = 10;
const int PSP_LANGUAGE_SIMPLIFIED_CHINESE = 11;
static u32 iLanguage = PSP_LANGUAGE_ENGLISH;
static u32 iButtonValue = 0;
static u32 language = PSP_LANGUAGE_ENGLISH;
static u32 buttonValue = 0;
static u32 umdPopup = 0;
void __ImposeInit()
{
language = PSP_LANGUAGE_ENGLISH;
buttonValue = 0;
umdPopup = 0;
}
void __ImposeDoState(PointerWrap &p)
{
p.Do(language);
p.Do(buttonValue);
p.Do(umdPopup);
p.DoMarker("sceImpose");
}
u32 sceImposeGetBatteryIconStatus(u32 chargingPtr, u32 iconStatusPtr)
{
@ -50,8 +65,8 @@ u32 sceImposeGetBatteryIconStatus(u32 chargingPtr, u32 iconStatusPtr)
u32 sceImposeSetLanguageMode(u32 languageVal, u32 buttonVal)
{
DEBUG_LOG(HLE, "sceImposeSetLanguageMode(%08x, %08x)", languageVal, buttonVal);
iLanguage = languageVal;
iButtonValue = buttonVal;
language = languageVal;
buttonValue = buttonVal;
return 0;
}
@ -59,20 +74,32 @@ u32 sceImposeGetLanguageMode(u32 languagePtr, u32 btnPtr)
{
DEBUG_LOG(HLE, "sceImposeGetLanguageMode(%08x, %08x)", languagePtr, btnPtr);
if (Memory::IsValidAddress(languagePtr))
Memory::Write_U32(iLanguage, languagePtr);
Memory::Write_U32(language, languagePtr);
if (Memory::IsValidAddress(btnPtr))
Memory::Write_U32(iButtonValue, btnPtr);
Memory::Write_U32(buttonValue, btnPtr);
return 0;
}
u32 sceImposeSetUMDPopup(int value) {
DEBUG_LOG(HLE, "sceImposeSetUMDPopup(%i)", value);
umdPopup = value;
return 0;
}
u32 sceImposeGetUMDPopup() {
DEBUG_LOG(HLE, "sceImposeGetUMDPopup()");
return umdPopup;
}
//OSD stuff? home button?
const HLEFunction sceImpose[] =
{
{0x36aa6e91, &WrapU_UU<sceImposeSetLanguageMode>, "sceImposeSetLanguageMode"}, // Seen
{0x36aa6e91, WrapU_UU<sceImposeSetLanguageMode>, "sceImposeSetLanguageMode"}, // Seen
{0x381bd9e7, 0, "sceImposeHomeButton"},
{0x24fd7bcf, &WrapU_UU<sceImposeGetLanguageMode>, "sceImposeGetLanguageMode"},
{0x8c943191, &WrapU_UU<sceImposeGetBatteryIconStatus>, "sceImposeGetBatteryIconStatus"},
{0x72189C48, 0, "sceImposeSetUMDPopup"},
{0x24fd7bcf, WrapU_UU<sceImposeGetLanguageMode>, "sceImposeGetLanguageMode"},
{0x8c943191, WrapU_UU<sceImposeGetBatteryIconStatus>, "sceImposeGetBatteryIconStatus"},
{0x72189C48, WrapU_I<sceImposeSetUMDPopup>, "sceImposeSetUMDPopup"},
{0xE0887BC8, WrapU_V<sceImposeGetUMDPopup>, "sceImposeGetUMDPopup"},
};
void Register_sceImpose()

View File

@ -17,4 +17,8 @@
#pragma once
#include "../../Common/ChunkFile.h"
void Register_sceImpose();
void __ImposeInit();
void __ImposeDoState(PointerWrap &p);

View File

@ -20,7 +20,9 @@
#undef DeleteFile
#endif
#include "../System.h"
#include "../Config.h"
#include "../Host.h"
#include "../SaveState.h"
#include "HLE.h"
#include "../MIPS/MIPS.h"
#include "../HW/MemoryStick.h"
@ -36,11 +38,10 @@
#include "sceKernelMemory.h"
#include "sceKernelThread.h"
#define ERROR_ERRNO_FILE_NOT_FOUND 0x80010002
#define ERROR_ERRNO_FILE_NOT_FOUND 0x80010002
#define ERROR_MEMSTICK_DEVCTL_BAD_PARAMS 0x80220081
#define ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS 0x80220082
#define ERROR_MEMSTICK_DEVCTL_BAD_PARAMS 0x80220081
#define ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS 0x80220082
/*
@ -76,15 +77,9 @@ typedef s32 SceMode;
typedef s64 SceOff;
typedef u64 SceIores;
std::string emuDebugOutput;
const std::string &EmuDebugOutput() {
return emuDebugOutput;
}
typedef u32 (*DeferredAction)(SceUID id, int param);
DeferredAction defAction = 0;
u32 defParam;
u32 defParam = 0;
#define SCE_STM_FDIR 0x1000
#define SCE_STM_FREG 0x2000
@ -94,6 +89,12 @@ enum {
TYPE_FILE=0x20
};
#ifdef __SYMBIAN32__
#undef st_ctime
#undef st_atime
#undef st_mtime
#endif
struct SceIoStat {
SceMode st_mode;
unsigned int st_attr;
@ -109,7 +110,7 @@ struct SceIoDirEnt {
char d_name[256];
u32 d_private;
};
#ifndef __SYMBIAN32__
struct dirent {
u32 unk0;
u32 type;
@ -117,6 +118,7 @@ struct dirent {
u32 unk[19];
char name[0x108];
};
#endif
class FileNode : public KernelObject {
public:
@ -130,7 +132,18 @@ public:
sprintf(ptr, "Seekpos: %08x", (u32)pspFileSystem.GetSeekPos(handle));
}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_BADF; }
int GetIDType() const { return 0; }
int GetIDType() const { return PPSSPP_KERNEL_TMID_File; }
virtual void DoState(PointerWrap &p) {
p.Do(fullpath);
p.Do(handle);
p.Do(callbackID);
p.Do(callbackArg);
p.Do(asyncResult);
p.Do(pendingAsyncResult);
p.Do(sectorBlockMode);
p.DoMarker("File");
}
std::string fullpath;
u32 handle;
@ -142,15 +155,20 @@ public:
bool pendingAsyncResult;
bool sectorBlockMode;
PSPFileInfo info;
};
void __IoInit() {
INFO_LOG(HLE, "Starting up I/O...");
MemoryStick_SetFatState(PSP_FAT_MEMORYSTICK_STATE_ASSIGNED);
#ifdef _WIN32
char path_buffer[_MAX_PATH], drive[_MAX_DRIVE] ,dir[_MAX_DIR], file[_MAX_FNAME], ext[_MAX_EXT];
char mypath[_MAX_PATH];
char memstickpath[_MAX_PATH];
char flashpath[_MAX_PATH];
GetModuleFileName(NULL,path_buffer,sizeof(path_buffer));
@ -162,24 +180,47 @@ void __IoInit() {
_splitpath_s(path_buffer, drive, dir, file, ext );
// Mount a couple of filesystems
sprintf(mypath, "%s%sMemStick\\", drive, dir);
sprintf(memstickpath, "%s%sMemStick\\", drive, dir);
sprintf(flashpath, "%s%sFlash\\", drive, dir);
#else
// TODO
char mypath[256] = "/mount/sdcard/memstick";
std::string memstickpath = g_Config.memCardDirectory;
std::string flashpath = g_Config.flashDirectory;
#endif
DirectoryFileSystem *memstick;
memstick = new DirectoryFileSystem(&pspFileSystem, mypath);
DirectoryFileSystem *flash;
memstick = new DirectoryFileSystem(&pspFileSystem, memstickpath);
flash = new DirectoryFileSystem(&pspFileSystem, flashpath);
pspFileSystem.Mount("ms0:", memstick);
pspFileSystem.Mount("fatms0:", memstick);
pspFileSystem.Mount("fatms:", memstick);
pspFileSystem.Mount("flash0:", new EmptyFileSystem());
pspFileSystem.Mount("flash1:", new EmptyFileSystem());
pspFileSystem.Mount("flash0:", flash);
pspFileSystem.Mount("flash1:", flash);
}
void __IoDoState(PointerWrap &p) {
// TODO: defAction is hard to save, and not the right way anyway.
// Should probbly be an enum and on the FileNode anyway.
if (defAction != NULL) {
WARN_LOG(HLE, "FIXME: Savestate failure: deferred IO not saved yet.");
}
}
void __IoShutdown() {
defAction = 0;
defParam = 0;
}
u32 __IoGetFileHandleFromId(u32 id, u32 &outError)
{
FileNode *f = kernelObjects.Get < FileNode > (id, outError);
if (!f) {
return -1;
}
return f->handle;
}
u32 sceIoAssign(const char *aliasname, const char *physname, const char *devname, u32 flag) {
@ -208,7 +249,7 @@ void __IoCompleteAsyncIO(SceUID id) {
FileNode *f = kernelObjects.Get < FileNode > (id, error);
if (f) {
if (f->callbackID) {
// __KernelNotifyCallbackType(THREAD_CALLBACK_IO, __KernelGetCurThread(), f->callbackID, f->callbackArg);
__KernelNotifyCallback(THREAD_CALLBACK_IO, f->callbackID, f->callbackArg);
}
}
}
@ -232,13 +273,21 @@ void __IoGetStat(SceIoStat *stat, PSPFileInfo &info) {
u32 sceIoGetstat(const char *filename, u32 addr) {
SceIoStat stat;
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
__IoGetStat(&stat, info);
Memory::WriteStruct(addr, &stat);
DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr,
info.startSector);
return 0;
if (info.exists) {
__IoGetStat(&stat, info);
if (Memory::IsValidAddress(addr)) {
Memory::WriteStruct(addr, &stat);
DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr,
info.startSector);
return 0;
} else {
ERROR_LOG(HLE, "sceIoGetstat(%s, %08x) : bad address", filename, addr);
return -1;
}
} else {
DEBUG_LOG(HLE, "sceIoGetstat(%s, %08x) : FILE NOT FOUND", filename, addr);
return SCE_KERNEL_ERROR_NOFILE;
}
}
//Not sure about wrapping it or not, since the log seems to take the address of the data var
@ -251,7 +300,7 @@ u32 sceIoRead(int id, u32 data_addr, int size) {
u32 error;
FileNode *f = kernelObjects.Get < FileNode > (id, error);
if (f) {
if (data_addr) {
if (Memory::IsValidAddress(data_addr)) {
u8 *data = (u8*) Memory::GetPointer(data_addr);
f->asyncResult = (u32) pspFileSystem.ReadFile(f->handle, data,
size);
@ -259,7 +308,7 @@ u32 sceIoRead(int id, u32 data_addr, int size) {
data_addr, size);
return f->asyncResult;
} else {
ERROR_LOG(HLE, "sceIoRead Reading into zero pointer");
ERROR_LOG(HLE, "sceIoRead Reading into bad pointer %08x", data_addr);
return -1;
}
} else {
@ -296,7 +345,7 @@ u32 sceIoWrite(int id, void *data_ptr, int size) //(int fd, void *data, int size
}
}
u32 sceIoLseek(int id, s64 offset, int whence) {
s64 sceIoLseek(int id, s64 offset, int whence) {
u32 error;
FileNode *f = kernelObjects.Get < FileNode > (id, error);
if (f) {
@ -351,24 +400,25 @@ u32 sceIoLseek32(int id, int offset, int whence) {
}
}
u32 sceIoOpen(const char* filename, int mode) {
u32 sceIoOpen(const char* filename, int flags, int mode) {
//memory stick filename
int access = FILEACCESS_NONE;
if (mode & O_RDONLY)
if (flags & O_RDONLY)
access |= FILEACCESS_READ;
if (mode & O_WRONLY)
if (flags & O_WRONLY)
access |= FILEACCESS_WRITE;
if (mode & O_APPEND)
if (flags & O_APPEND)
access |= FILEACCESS_APPEND;
if (mode & O_CREAT)
if (flags & O_CREAT)
access |= FILEACCESS_CREATE;
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
u32 h = pspFileSystem.OpenFile(filename, (FileAccess) access);
if (h == 0)
{
ERROR_LOG(HLE,
"ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x) - file not found",
filename, mode);
"ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x, %08x) - file not found",
filename, flags, mode);
return ERROR_ERRNO_FILE_NOT_FOUND;
}
@ -377,7 +427,8 @@ u32 sceIoOpen(const char* filename, int mode) {
f->handle = h;
f->fullpath = filename;
f->asyncResult = id;
DEBUG_LOG(HLE, "%i=sceIoOpen(%s, %08x)", id, filename, mode);
f->info = info;
DEBUG_LOG(HLE, "%i=sceIoOpen(%s, %08x, %08x)", id, filename, flags, mode);
return id;
}
@ -415,15 +466,14 @@ void sceIoSync() {
}
struct DeviceSize {
u32 maxClusters;
u32 freeClusters;
u32 maxSectors;
u32 sectorSize;
u32 sectorsPerCluster;
u32 totalClusters;
u32 freeClusters;
u32 sectorCount;
};
u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, int outLen) {
if (strcmp(name, "emulator:")) {
DEBUG_LOG(HLE,"sceIoDevctl(\"%s\", %08x, %08x, %i, %08x, %i)", name, cmd,argAddr,argLen,outPtr,outLen);
}
@ -482,9 +532,16 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr,
}
break;
case 0x02025806: // Memory stick inserted?
case 0x02025801: // Memstick Driver status?
if (Memory::IsValidAddress(outPtr)) {
if (Memory::IsValidAddress(outPtr) && outLen >= 4) {
Memory::Write_U32(4, outPtr); // JPSCP: The right return value is 4 for some reason
return 0;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
}
case 0x02025806: // Memory stick inserted?
if (Memory::IsValidAddress(outPtr) && outLen >= 4) {
Memory::Write_U32(1, outPtr);
return 0;
} else {
@ -493,20 +550,23 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr,
case 0x02425818: // Get memstick size etc
// Pretend we have a 2GB memory stick.
if (Memory::IsValidAddress(argAddr)) { // "Should" be outPtr but isn't
if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // "Should" be outPtr but isn't
u32 pointer = Memory::Read_U32(argAddr);
u64 totalSize = (u32)2 * 1024 * 1024 * 1024;
u64 freeSize = 1 * 1024 * 1024 * 1024;
u32 sectorSize = 0x200;
u32 memStickSectorSize = 32 * 1024;
u32 sectorCount = memStickSectorSize / sectorSize;
u64 freeSize = 1 * 1024 * 1024 * 1024;
DeviceSize deviceSize;
deviceSize.maxSectors = 512;
deviceSize.sectorSize = 0x200;
deviceSize.sectorsPerCluster = 0x08;
deviceSize.totalClusters = (u32)((totalSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.freeClusters = (u32)((freeSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount));
deviceSize.freeClusters = deviceSize.maxClusters;
deviceSize.maxSectors = deviceSize.maxClusters;
deviceSize.sectorSize = sectorSize;
deviceSize.sectorCount = sectorCount;
Memory::WriteStruct(pointer, &deviceSize);
DEBUG_LOG(HLE, "Returned memstick size: maxSectors=%i", deviceSize.maxSectors);
return 0;
} else {
ERROR_LOG(HLE, "memstick size query: bad params");
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
}
}
@ -561,17 +621,18 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr,
case 0x02425818: // Get memstick size etc
// Pretend we have a 2GB memory stick.
{
if (Memory::IsValidAddress(argAddr)) { // "Should" be outPtr but isn't
if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // NOTE: not outPtr
u32 pointer = Memory::Read_U32(argAddr);
u64 totalSize = (u32)2 * 1024 * 1024 * 1024;
u64 freeSize = 1 * 1024 * 1024 * 1024;
u32 sectorSize = 0x200;
u32 memStickSectorSize = 32 * 1024;
u32 sectorCount = memStickSectorSize / sectorSize;
u64 freeSize = 1 * 1024 * 1024 * 1024;
DeviceSize deviceSize;
deviceSize.maxSectors = 512;
deviceSize.sectorSize = 0x200;
deviceSize.sectorsPerCluster = 0x08;
deviceSize.totalClusters = (u32)((totalSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.freeClusters = (u32)((freeSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount));
deviceSize.freeClusters = deviceSize.maxClusters;
deviceSize.maxSectors = deviceSize.maxClusters;
deviceSize.sectorSize = sectorSize;
deviceSize.sectorCount = sectorCount;
Memory::WriteStruct(pointer, &deviceSize);
return 0;
} else {
@ -595,12 +656,7 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr,
std::string data(Memory::GetCharPointer(argAddr), argLen);
if (PSP_CoreParameter().printfEmuLog)
{
printf("%s", data.c_str());
#ifdef _WIN32
OutputDebugString(data.c_str());
#endif
// Also collect the debug output
emuDebugOutput += data;
host->SendDebugOutput(data.c_str());
}
else
{
@ -610,7 +666,12 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr,
}
case 3: // EMULATOR_DEVCTL__IS_EMULATOR
if (Memory::IsValidAddress(outPtr))
Memory::Write_U32(1, outPtr); // TODO: Make a headless mode for running tests!
Memory::Write_U32(1, outPtr);
return 0;
case 4: // EMULATOR_DEVCTL__VERIFY_STATE
// Note that this is async, and makes sure the save state matches up.
SaveState::Verify();
// TODO: Maybe save/load to a file just to be sure?
return 0;
}
@ -644,26 +705,27 @@ u32 sceIoChdir(const char *dirname) {
return 1;
}
void sceIoChangeAsyncPriority()
int sceIoChangeAsyncPriority(int id, int priority)
{
ERROR_LOG(HLE, "UNIMPL sceIoChangeAsyncPriority(%d)", PARAM(0));
RETURN(0);
ERROR_LOG(HLE, "UNIMPL sceIoChangeAsyncPriority(%d, %d)", id, priority);
return 0;
}
u32 __IoClose(SceUID id, int param)
u32 __IoClose(SceUID actedFd, int closedFd)
{
DEBUG_LOG(HLE, "Deferred IoClose(%d)", id);
__IoCompleteAsyncIO(id);
return kernelObjects.Destroy < FileNode > (id);
DEBUG_LOG(HLE, "Deferred IoClose(%d, %d)", actedFd, closedFd);
__IoCompleteAsyncIO(closedFd);
return kernelObjects.Destroy < FileNode > (closedFd);
}
//TODO Not really sure if this should be wrapped nor how
void sceIoCloseAsync()
int sceIoCloseAsync(SceUID id)
{
DEBUG_LOG(HLE, "sceIoCloseAsync(%d)", PARAM(0));
DEBUG_LOG(HLE, "sceIoCloseAsync(%d)", id);
//sceIoClose();
// TODO: Not sure this is a good solution. Seems like you can defer one per fd.
defAction = &__IoClose;
RETURN(0);
defParam = id;
return 0;
}
u32 sceIoLseekAsync(int id, s64 offset, int whence)
@ -682,6 +744,7 @@ u32 sceIoSetAsyncCallback(int id, u32 clbckId, u32 clbckArg)
FileNode *f = kernelObjects.Get < FileNode > (id, error);
if (f)
{
// TODO: Check replacing / updating?
f->callbackID = clbckId;
f->callbackArg = clbckArg;
return 0;
@ -700,13 +763,14 @@ u32 sceIoLseek32Async(int id, int offset, int whence)
return 0;
}
void sceIoOpenAsync(const char *filename, int mode)
u32 sceIoOpenAsync(const char *filename, int flags, int mode)
{
DEBUG_LOG(HLE, "sceIoOpenAsync() sorta implemented");
RETURN(sceIoOpen(filename, mode));
// __IoCompleteAsyncIO(currentMIPS->r[2]); // The return value
// We have to return a UID here, which may have been destroyed when we reach Wait if it failed.
// Now that we're just faking it, we just don't RETURN(0) here.
u32 fd = sceIoOpen(filename, flags, mode);
// TODO: This can't actually have a callback yet, but if it's set before waiting, it should be called.
__IoCompleteAsyncIO(fd);
// We have to return an fd here, which may have been destroyed when we reach Wait if it failed.
return fd;
}
u32 sceIoReadAsync(int id, u32 data_addr, int size)
@ -717,7 +781,7 @@ u32 sceIoReadAsync(int id, u32 data_addr, int size)
return 0;
}
u32 sceIoGetAsyncStat(int id, u32 address, u32 uknwn)
u32 sceIoGetAsyncStat(int id, u32 poll, u32 address)
{
u32 error;
FileNode *f = kernelObjects.Get < FileNode > (id, error);
@ -725,7 +789,9 @@ u32 sceIoGetAsyncStat(int id, u32 address, u32 uknwn)
{
Memory::Write_U64(f->asyncResult, address);
DEBUG_LOG(HLE, "%i = sceIoGetAsyncStat(%i, %i, %08x) (HACK)",
(u32) f->asyncResult, id, address, uknwn);
(u32) f->asyncResult, id, poll, address);
if (!poll)
hleReSchedule("io waited");
return 0; //completed
}
else
@ -735,7 +801,7 @@ u32 sceIoGetAsyncStat(int id, u32 address, u32 uknwn)
}
}
void sceIoWaitAsync(int id, u32 address, u32 uknwn) {
int sceIoWaitAsync(int id, u32 address) {
u32 error;
FileNode *f = kernelObjects.Get < FileNode > (id, error);
if (f) {
@ -746,15 +812,16 @@ void sceIoWaitAsync(int id, u32 address, u32 uknwn) {
}
Memory::Write_U64(res, address);
DEBUG_LOG(HLE, "%i = sceIoWaitAsync(%i, %08x) (HACK)", (u32) res, id,
uknwn);
RETURN(0); //completed
address);
hleReSchedule("io waited");
return 0; //completed
} else {
ERROR_LOG(HLE, "ERROR - sceIoWaitAsync waiting for invalid id %i", id);
RETURN(-1);
return -1;
}
}
void sceIoWaitAsyncCB(int id, u32 address) {
int sceIoWaitAsyncCB(int id, u32 address) {
// Should process callbacks here
u32 error;
FileNode *f = kernelObjects.Get < FileNode > (id, error);
@ -767,10 +834,13 @@ void sceIoWaitAsyncCB(int id, u32 address) {
Memory::Write_U64(res, address);
DEBUG_LOG(HLE, "%i = sceIoWaitAsyncCB(%i, %08x) (HACK)", (u32) res, id,
address);
RETURN(0); //completed
hleCheckCurrentCallbacks();
hleReSchedule(true, "io waited");
return 0; //completed
} else {
ERROR_LOG(HLE, "ERROR - sceIoWaitAsyncCB waiting for invalid id %i",
id);
return -1;
}
}
@ -798,7 +868,21 @@ public:
const char *GetName() {return name.c_str();}
const char *GetTypeName() {return "DirListing";}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_BADF; }
int GetIDType() const { return 0; }
int GetIDType() const { return PPSSPP_KERNEL_TMID_DirList; }
virtual void DoState(PointerWrap &p) {
p.Do(name);
p.Do(index);
// TODO: Is this the right way for it to wake up?
int count = listing.size();
p.Do(count);
listing.resize(count);
for (int i = 0; i < count; ++i) {
listing[i].DoState(p);
}
p.DoMarker("DirListing");
}
std::string name;
std::vector<PSPFileInfo> listing;
@ -855,29 +939,86 @@ u32 sceIoDclose(int id) {
return kernelObjects.Destroy<DirListing>(id);
}
u32 sceIoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen)
{
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (error) {
return error;
}
//KD Hearts:
//56:46:434 HLE\sceIo.cpp:886 E[HLE]: UNIMPL 0=sceIoIoctrl id: 0000011f, cmd 04100001, indataPtr 08b313d8, inlen 00000010, outdataPtr 00000000, outLen 0
// 0000000
// TODO: This kind of stuff should be moved to the devices (wherever that would be)
// and does not belong in this file. Same thing with Devctl.
switch (cmd) {
case 0x04100001: // Define decryption key (amctrl.prx DRM)
if (Memory::IsValidAddress(indataPtr) && inlen == 16) {
u8 keybuf[16];
memcpy(keybuf, Memory::GetPointer(indataPtr), 16);
ERROR_LOG(HLE, "PGD DRM not yet supported, sorry.");
}
break;
// Get start sector of file.
case 0x01020006:
INFO_LOG(HLE, "sceIoIoCtl: Asked for start sector of file %i", id);
if (Memory::IsValidAddress(outdataPtr) && outlen >= 4) {
Memory::Write_U32(f->info.startSector, outdataPtr);
}
break;
// Get size in bytes of file.
case 0x01020007:
INFO_LOG(HLE, "sceIoIoCtl: Asked for size of file %i", id);
if (Memory::IsValidAddress(outdataPtr) && outlen >= 8) {
s64 size = f->info.size;
Memory::Write_U64(size, outdataPtr);
}
break;
default:
ERROR_LOG(HLE, "UNIMPL 0=sceIoIoctl id: %08x, cmd %08x, indataPtr %08x, inlen %08x, outdataPtr %08x, outLen %08x", id,cmd,indataPtr,inlen,outdataPtr,outlen);
break;
}
return 0;
}
KernelObject *__KernelFileNodeObject() {
return new FileNode;
}
KernelObject *__KernelDirListingObject() {
return new DirListing;
}
const HLEFunction IoFileMgrForUser[] = {
{ 0xb29ddf9c, &WrapU_C<sceIoDopen>, "sceIoDopen" },
{ 0xe3eb004c, &WrapU_IU<sceIoDread>, "sceIoDread" },
{ 0xeb092469, &WrapU_I<sceIoDclose>, "sceIoDclose" },
{ 0xe95a012b, 0, "sceIoIoctlAsync" },
{ 0x63632449, 0, "sceIoIoctl" },
{ 0x63632449, &WrapU_UUUUUU<sceIoIoctl>, "sceIoIoctl" },
{ 0xace946e8, &WrapU_CU<sceIoGetstat>, "sceIoGetstat" },
{ 0xb8a740f4, 0, "sceIoChstat" },
{ 0x55f4717d, &WrapU_C<sceIoChdir>, "sceIoChdir" },
{ 0x08bd7374, 0, "sceIoGetDevType" },
{ 0xB2A628C1, &WrapU_CCCU<sceIoAssign>, "sceIoAssign" },
{ 0xe8bc6571, 0, "sceIoCancel" },
{ 0xb293727f, sceIoChangeAsyncPriority, "sceIoChangeAsyncPriority" },
{ 0xb293727f, &WrapI_II<sceIoChangeAsyncPriority>, "sceIoChangeAsyncPriority" },
{ 0x810C4BC3, &WrapU_I<sceIoClose>, "sceIoClose" }, //(int fd);
{ 0xff5940b6, sceIoCloseAsync, "sceIoCloseAsync" },
{ 0xff5940b6, &WrapI_I<sceIoCloseAsync>, "sceIoCloseAsync" },
{ 0x54F5FB11, &WrapU_CIUIUI<sceIoDevctl>, "sceIoDevctl" }, //(const char *name int cmd, void *arg, size_t arglen, void *buf, size_t *buflen);
{ 0xcb05f8d6, &WrapU_IUU<sceIoGetAsyncStat>, "sceIoGetAsyncStat" },
{ 0x27EB27B8, &WrapU_II64I<sceIoLseek>, "sceIoLseek" }, //(int fd, int offset, int whence);
{ 0x27EB27B8, &WrapI64_II64I<sceIoLseek>, "sceIoLseek" }, //(int fd, int offset, int whence);
{ 0x68963324, &WrapU_III<sceIoLseek32>, "sceIoLseek32" },
{ 0x1b385d8f, &WrapU_III<sceIoLseek32Async>, "sceIoLseek32Async" },
{ 0x71b19e77, &WrapU_II64I<sceIoLseekAsync>, "sceIoLseekAsync" },
{ 0x109F50BC, &WrapU_CI<sceIoOpen>, "sceIoOpen" }, //(const char* file, int mode);
{ 0x89AA9906, &WrapV_CI<sceIoOpenAsync>, "sceIoOpenAsync" },
{ 0x109F50BC, &WrapU_CII<sceIoOpen>, "sceIoOpen" }, //(const char* file, int mode);
{ 0x89AA9906, &WrapU_CII<sceIoOpenAsync>, "sceIoOpenAsync" },
{ 0x06A70004, &WrapU_CI<sceIoMkdir>, "sceIoMkdir" }, //(const char *dir, int mode);
{ 0x3251ea56, &WrapU_IU<sceIoPollAsync>, "sceIoPollAsync" },
{ 0x6A638D83, &WrapU_IUI<sceIoRead>, "sceIoRead" }, //(int fd, void *data, int size);
@ -890,8 +1031,8 @@ const HLEFunction IoFileMgrForUser[] = {
{ 0x6d08a871, 0, "sceIoUnassign" },
{ 0x42EC03AC, &WrapU_IVI<sceIoWrite>, "sceIoWrite" }, //(int fd, void *data, int size);
{ 0x0facab19, 0, "sceIoWriteAsync" },
{ 0x35dbd746, &WrapV_IU<sceIoWaitAsyncCB>, "sceIoWaitAsyncCB" },
{ 0xe23eec33, &WrapV_IUU<sceIoWaitAsync>, "sceIoWaitAsync" },
{ 0x35dbd746, &WrapI_IU<sceIoWaitAsyncCB>, "sceIoWaitAsyncCB" },
{ 0xe23eec33, &WrapI_IU<sceIoWaitAsync>, "sceIoWaitAsync" },
};
void Register_IoFileMgrForUser() {

View File

@ -18,12 +18,17 @@
#pragma once
#include <string>
#include "../System.h"
#include "HLE.h"
#include "sceKernel.h"
void __IoInit();
void __IoDoState(PointerWrap &p);
void __IoShutdown();
u32 __IoGetFileHandleFromId(u32 id, u32 &outError);
KernelObject *__KernelFileNodeObject();
KernelObject *__KernelDirListingObject();
void Register_IoFileMgrForUser();
void Register_StdioForUser();
const std::string &EmuDebugOutput();

View File

@ -24,13 +24,17 @@
#include "../FileSystems/MetaFileSystem.h"
#include "../PSPLoaders.h"
#include "../../Core/CoreTiming.h"
#include "../../Core/SaveState.h"
#include "../../Core/System.h"
#include "../../GPU/GPUInterface.h"
#include "../../GPU/GPUState.h"
#include "__sceAudio.h"
#include "sceAudio.h"
#include "sceCtrl.h"
#include "sceDisplay.h"
#include "sceFont.h"
#include "sceGe.h"
#include "sceIo.h"
#include "sceKernel.h"
@ -46,15 +50,18 @@
#include "sceKernelEventFlag.h"
#include "sceKernelVTimer.h"
#include "sceKernelTime.h"
#include "sceMpeg.h"
#include "scePower.h"
#include "sceUtility.h"
#include "sceUmd.h"
#include "sceSsl.h"
#include "sceSas.h"
#include "scePsmf.h"
#include "sceImpose.h"
#include "sceUsb.h"
#include "../Util/PPGeDraw.h"
extern MetaFileSystem pspFileSystem;
/*
17: [MIPS32 R4K 00000000 ]: Loader: Type: 1 Vaddr: 00000000 Filesz: 2856816 Memsz: 2856816
18: [MIPS32 R4K 00000000 ]: Loader: Loadable Segment Copied to 0898dab0, size 002b9770
@ -63,6 +70,7 @@ extern MetaFileSystem pspFileSystem;
*/
static bool kernelRunning = false;
KernelObjectPool kernelObjects;
void __KernelInit()
{
@ -72,19 +80,32 @@ void __KernelInit()
return;
}
__KernelTimeInit();
__InterruptsInit();
__KernelMemoryInit();
__KernelThreadingInit();
__KernelAlarmInit();
__KernelEventFlagInit();
__KernelMbxInit();
__KernelMutexInit();
__KernelSemaInit();
__IoInit();
__AudioInit();
__SasInit();
__DisplayInit();
__InterruptsInit();
__GeInit();
__PowerInit();
__UtilityInit();
__UmdInit();
__MpegInit(PSP_CoreParameter().useMediaEngine);
__PsmfInit();
__CtrlInit();
__SslInit();
__ImposeInit();
__UsbInit();
__FontInit();
SaveState::Init(); // Must be after IO, as it may create a directory
// "Internal" PSP libraries
__PPGeInit();
@ -103,14 +124,21 @@ void __KernelShutdown()
INFO_LOG(HLE, "Shutting down kernel - %i kernel objects alive", kernelObjects.GetCount());
kernelObjects.Clear();
__MpegShutdown();
__PsmfShutdown();
__PPGeShutdown();
__CtrlShutdown();
__UtilityShutdown();
__GeShutdown();
__SasShutdown();
__DisplayShutdown();
__AudioShutdown();
__IoShutdown();
__InterruptsShutdown();
__KernelMutexShutdown();
__KernelThreadingShutdown();
__KernelMemoryShutdown();
__InterruptsShutdown();
CoreTiming::ClearPendingEvents();
CoreTiming::UnregisterAllEvents();
@ -118,6 +146,45 @@ void __KernelShutdown()
kernelRunning = false;
}
void __KernelDoState(PointerWrap &p)
{
p.Do(kernelRunning);
kernelObjects.DoState(p);
p.DoMarker("KernelObjects");
__InterruptsDoState(p);
__KernelMemoryDoState(p);
__KernelThreadingDoState(p);
__KernelAlarmDoState(p);
__KernelEventFlagDoState(p);
__KernelMbxDoState(p);
__KernelModuleDoState(p);
__KernelMutexDoState(p);
__KernelSemaDoState(p);
__KernelTimeDoState(p);
__AudioDoState(p);
__CtrlDoState(p);
__DisplayDoState(p);
__FontDoState(p);
__GeDoState(p);
__ImposeDoState(p);
__IoDoState(p);
__MpegDoState(p);
__PowerDoState(p);
__PsmfDoState(p);
__SasDoState(p);
__SslDoState(p);
__UmdDoState(p);
__UtilityDoState(p);
__UsbDoState(p);
__PPGeDoState(p);
__InterruptsDoStateLate(p);
__KernelThreadingDoStateLate(p);
}
bool __KernelIsRunning() {
return kernelRunning;
}
@ -125,20 +192,18 @@ bool __KernelIsRunning() {
void sceKernelExitGame()
{
INFO_LOG(HLE,"sceKernelExitGame");
if (PSP_CoreParameter().headLess)
exit(0);
else
if (!PSP_CoreParameter().headLess)
PanicAlert("Game exited");
__KernelSwitchOffThread("game exited");
Core_Stop();
}
void sceKernelExitGameWithStatus()
{
INFO_LOG(HLE,"sceKernelExitGameWithStatus");
if (PSP_CoreParameter().headLess)
exit(0);
else
if (!PSP_CoreParameter().headLess)
PanicAlert("Game exited (with status)");
__KernelSwitchOffThread("game exited");
Core_Stop();
}
@ -180,21 +245,55 @@ void sceKernelGetGPI()
}
// Don't even log these, they're spammy and we probably won't
// need to emulate them.
void sceKernelDcacheWritebackAll()
// need to emulate them. Might be useful for invalidating cached
// textures, and in the future display lists, in some cases though.
int sceKernelDcacheInvalidateRange(u32 addr, int size)
{
}
void sceKernelDcacheWritebackRange()
{
}
void sceKernelDcacheWritebackInvalidateRange()
{
}
void sceKernelDcacheWritebackInvalidateAll()
{
}
if (size < 0 || (int) addr + size < 0)
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
KernelObjectPool kernelObjects;
if (size > 0)
{
if ((addr % 64) != 0 || (size % 64) != 0)
return SCE_KERNEL_ERROR_CACHE_ALIGNMENT;
if (addr != 0)
gpu->InvalidateCache(addr, size);
}
return 0;
}
int sceKernelDcacheWritebackAll()
{
// Some games seem to use this a lot, it doesn't make sense
// to zap the whole texture cache.
gpu->InvalidateCacheHint(0, -1);
return 0;
}
int sceKernelDcacheWritebackRange(u32 addr, int size)
{
if (size < 0)
return SCE_KERNEL_ERROR_INVALID_SIZE;
if (size > 0 && addr != 0) {
gpu->InvalidateCache(addr, size);
}
return 0;
}
int sceKernelDcacheWritebackInvalidateRange(u32 addr, int size)
{
if (size < 0)
return SCE_KERNEL_ERROR_INVALID_SIZE;
if (size > 0 && addr != 0) {
gpu->InvalidateCache(addr, size);
}
return 0;
}
int sceKernelDcacheWritebackInvalidateAll()
{
gpu->InvalidateCacheHint(0, -1);
return 0;
}
KernelObjectPool::KernelObjectPool()
{
@ -258,12 +357,12 @@ void KernelObjectPool::List()
if (pool[i])
{
pool[i]->GetQuickInfo(buffer,256);
INFO_LOG(HLE, "KO %i: %s \"%s\": %s", i + handleOffset, pool[i]->GetTypeName(), pool[i]->GetName(), buffer);
}
else
{
strcpy(buffer,"WTF? Zero Pointer");
}
INFO_LOG(HLE, "KO %i: %s \"%s\": %s", i + handleOffset, pool[i]->GetTypeName(), pool[i]->GetName(), buffer);
}
}
}
@ -279,6 +378,88 @@ int KernelObjectPool::GetCount()
return count;
}
void KernelObjectPool::DoState(PointerWrap &p)
{
int _maxCount = maxCount;
p.Do(_maxCount);
if (_maxCount != maxCount)
ERROR_LOG(HLE, "Unable to load state: different kernel object storage.");
if (p.mode == p.MODE_READ)
kernelObjects.Clear();
p.DoArray(occupied, maxCount);
for (int i = 0; i < maxCount; ++i)
{
if (!occupied[i])
continue;
int type;
if (p.mode == p.MODE_READ)
{
p.Do(type);
pool[i] = CreateByIDType(type);
pool[i]->uid = i + handleOffset;
// Already logged an error.
if (pool[i] == NULL)
return;
}
else
{
type = pool[i]->GetIDType();
p.Do(type);
}
pool[i]->DoState(p);
}
p.DoMarker("KernelObjectPool");
}
KernelObject *KernelObjectPool::CreateByIDType(int type)
{
// Used for save states. This is ugly, but what other way is there?
switch (type)
{
case SCE_KERNEL_TMID_Alarm:
return __KernelAlarmObject();
case SCE_KERNEL_TMID_EventFlag:
return __KernelEventFlagObject();
case SCE_KERNEL_TMID_Mbox:
return __KernelMbxObject();
case SCE_KERNEL_TMID_Fpl:
return __KernelMemoryFPLObject();
case SCE_KERNEL_TMID_Vpl:
return __KernelMemoryVPLObject();
case PPSSPP_KERNEL_TMID_PMB:
return __KernelMemoryPMBObject();
case PPSSPP_KERNEL_TMID_Module:
return __KernelModuleObject();
case SCE_KERNEL_TMID_Mpipe:
return __KernelMsgPipeObject();
case SCE_KERNEL_TMID_Mutex:
return __KernelMutexObject();
case SCE_KERNEL_TMID_LwMutex:
return __KernelLwMutexObject();
case SCE_KERNEL_TMID_Semaphore:
return __KernelSemaphoreObject();
case SCE_KERNEL_TMID_Callback:
return __KernelCallbackObject();
case SCE_KERNEL_TMID_Thread:
return __KernelThreadObject();
case SCE_KERNEL_TMID_VTimer:
return __KernelVTimerObject();
case PPSSPP_KERNEL_TMID_File:
return __KernelFileNodeObject();
case PPSSPP_KERNEL_TMID_DirList:
return __KernelDirListingObject();
default:
ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type);
return NULL;
}
}
void sceKernelIcacheInvalidateAll()
{
DEBUG_LOG(CPU, "Icache invalidated - should clear JIT someday");
@ -326,38 +507,38 @@ 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,&WrapV_IIU<sceKernelCancelSema>, "sceKernelCancelSema"},
{0xD6DA4BA1,&WrapV_CUIIU<sceKernelCreateSema>, "sceKernelCreateSema"},
{0x28b6489c,&WrapV_I<sceKernelDeleteSema>, "sceKernelDeleteSema"},
{0x58b1f937,&WrapV_II<sceKernelPollSema>, "sceKernelPollSema"},
{0xBC6FEBC5,&WrapV_IU<sceKernelReferSemaStatus>, "sceKernelReferSemaStatus"},
{0x3F53E640,&WrapV_II<sceKernelSignalSema>, "sceKernelSignalSema"},
{0x4E3A1105,&WrapV_IIU<sceKernelWaitSema>, "sceKernelWaitSema"},
{0x6d212bac,&WrapV_IIU<sceKernelWaitSemaCB>, "sceKernelWaitSemaCB"},
{0x8FFDF9A2,&WrapI_IIU<sceKernelCancelSema>, "sceKernelCancelSema"},
{0xD6DA4BA1,&WrapI_CUIIU<sceKernelCreateSema>, "sceKernelCreateSema"},
{0x28b6489c,&WrapI_I<sceKernelDeleteSema>, "sceKernelDeleteSema"},
{0x58b1f937,&WrapI_II<sceKernelPollSema>, "sceKernelPollSema"},
{0xBC6FEBC5,&WrapI_IU<sceKernelReferSemaStatus>, "sceKernelReferSemaStatus"},
{0x3F53E640,&WrapI_II<sceKernelSignalSema>, "sceKernelSignalSema"},
{0x4E3A1105,&WrapI_IIU<sceKernelWaitSema>, "sceKernelWaitSema"},
{0x6d212bac,&WrapI_IIU<sceKernelWaitSemaCB>, "sceKernelWaitSemaCB"},
{0x60107536,&WrapV_U<sceKernelDeleteLwMutex>, "sceKernelDeleteLwMutex"},
{0x19CFF145,&WrapV_UCUIU<sceKernelCreateLwMutex>, "sceKernelCreateLwMutex"},
{0xf8170fbe,&WrapV_I<sceKernelDeleteMutex>, "sceKernelDeleteMutex"},
{0xB011B11F,&WrapV_IIU<sceKernelLockMutex>, "sceKernelLockMutex"},
{0x5bf4dd27,&WrapV_IIU<sceKernelLockMutexCB>, "sceKernelLockMutexCB"},
{0x6b30100f,&WrapV_II<sceKernelUnlockMutex>, "sceKernelUnlockMutex"},
{0xb7d098c6,&WrapV_CUIU<sceKernelCreateMutex>, "sceKernelCreateMutex"},
{0x0DDCD2C9,&WrapV_II<sceKernelTryLockMutex>, "sceKernelTryLockMutex"},
{0x60107536,&WrapI_U<sceKernelDeleteLwMutex>, "sceKernelDeleteLwMutex"},
{0x19CFF145,&WrapI_UCUIU<sceKernelCreateLwMutex>, "sceKernelCreateLwMutex"},
{0xf8170fbe,&WrapI_I<sceKernelDeleteMutex>, "sceKernelDeleteMutex"},
{0xB011B11F,&WrapI_IIU<sceKernelLockMutex>, "sceKernelLockMutex"},
{0x5bf4dd27,&WrapI_IIU<sceKernelLockMutexCB>, "sceKernelLockMutexCB"},
{0x6b30100f,&WrapI_II<sceKernelUnlockMutex>, "sceKernelUnlockMutex"},
{0xb7d098c6,&WrapI_CUIU<sceKernelCreateMutex>, "sceKernelCreateMutex"},
{0x0DDCD2C9,&WrapI_II<sceKernelTryLockMutex>, "sceKernelTryLockMutex"},
// NOTE: LockLwMutex and UnlockLwMutex are in Kernel_Library, see sceKernelInterrupt.cpp.
{0xFCCFAD26,sceKernelCancelWakeupThread,"sceKernelCancelWakeupThread"},
{0xea748e31,sceKernelChangeCurrentThreadAttr,"sceKernelChangeCurrentThreadAttr"},
{0x71bc9871,sceKernelChangeThreadPriority,"sceKernelChangeThreadPriority"},
{0x446D8DE6,sceKernelCreateThread,"sceKernelCreateThread"},
{0x9fa03cd3,sceKernelDeleteThread,"sceKernelDeleteThread"},
{0xBD123D9E,0,"sceKernelDelaySysClockThread"},
{0x1181E963,0,"sceKernelDelaySysClockThreadCB"},
{0x446D8DE6,WrapI_CUUIUU<sceKernelCreateThread>,"sceKernelCreateThread"},
{0x9fa03cd3,WrapI_I<sceKernelDeleteThread>,"sceKernelDeleteThread"},
{0xBD123D9E,sceKernelDelaySysClockThread,"sceKernelDelaySysClockThread"},
{0x1181E963,sceKernelDelaySysClockThreadCB,"sceKernelDelaySysClockThreadCB"},
{0xceadeb47,sceKernelDelayThread,"sceKernelDelayThread"},
{0x68da9e36,sceKernelDelayThreadCB,"sceKernelDelayThreadCB"},
{0xaa73c935,sceKernelExitThread,"sceKernelExitThread"},
@ -366,8 +547,8 @@ const HLEFunction ThreadManForUser[] =
{0x293b45b8,sceKernelGetThreadId,"sceKernelGetThreadId"},
{0x3B183E26,sceKernelGetThreadExitStatus,"sceKernelGetThreadExitStatus"},
{0x52089CA1,sceKernelGetThreadStackFreeSize,"sceKernelGetThreadStackFreeSize"},
{0xFFC36A14,0,"sceKernelReferThreadRunStatus"},
{0x17c1684e,sceKernelReferThreadStatus,"sceKernelReferThreadStatus"},
{0xFFC36A14,WrapU_UU<sceKernelReferThreadRunStatus>,"sceKernelReferThreadRunStatus"},
{0x17c1684e,WrapU_UU<sceKernelReferThreadStatus>,"sceKernelReferThreadStatus"},
{0x2C34E053,0,"sceKernelReleaseWaitThread"},
{0x75156e8f,sceKernelResumeThread,"sceKernelResumeThread"},
{0x3ad58b8c,&WrapU_V<sceKernelSuspendDispatchThread>,"sceKernelSuspendDispatchThread"},
@ -375,30 +556,15 @@ const HLEFunction ThreadManForUser[] =
{0x912354a7,sceKernelRotateThreadReadyQueue,"sceKernelRotateThreadReadyQueue"},
{0x9ACE131E,sceKernelSleepThread,"sceKernelSleepThread"},
{0x82826f70,sceKernelSleepThreadCB,"sceKernelSleepThreadCB"},
{0xF475845D,&WrapV_IUU<sceKernelStartThread>,"sceKernelStartThread"},
{0xF475845D,&WrapI_IUU<sceKernelStartThread>,"sceKernelStartThread"},
{0x9944f31f,sceKernelSuspendThread,"sceKernelSuspendThread"},
{0x616403ba,WrapV_U<sceKernelTerminateThread>,"sceKernelTerminateThread"},
{0x383f7bcc,sceKernelTerminateDeleteThread,"sceKernelTerminateDeleteThread"},
{0x616403ba,WrapI_U<sceKernelTerminateThread>,"sceKernelTerminateThread"},
{0x383f7bcc,WrapI_I<sceKernelTerminateDeleteThread>,"sceKernelTerminateDeleteThread"},
{0x840E8133,sceKernelWaitThreadEndCB,"sceKernelWaitThreadEndCB"},
{0xd13bde95,sceKernelCheckThreadStack,"sceKernelCheckThreadStack"},
{0x94416130,0,"sceKernelGetThreadmanIdList"},
{0x57CF62DD,sceKernelGetThreadmanIdType,"sceKernelGetThreadmanIdType"},
{0x20fff560,sceKernelCreateVTimer,"sceKernelCreateVTimer"},
{0x328F9E52,0,"sceKernelDeleteVTimer"},
{0xc68d9437,sceKernelStartVTimer,"sceKernelStartVTimer"},
{0xD0AEEE87,0,"sceKernelStopVTimer"},
{0xD2D615EF,0,"sceKernelCancelVTimerHandler"},
{0xB3A59970,0,"sceKernelGetVTimerBase"},
{0xB7C18B77,0,"sceKernelGetVTimerBaseWide"},
{0x034A921F,0,"sceKernelGetVTimerTime"},
{0xC0B3FFD2,0,"sceKernelGetVTimerTimeWide"},
{0x5F32BEAA,0,"sceKernelReferVTimerStatus"},
{0x542AD630,0,"sceKernelSetVTimerTime"},
{0xFB6425C3,0,"sceKernelSetVTimerTimeWide"},
{0xd8b299ae,sceKernelSetVTimerHandler,"sceKernelSetVTimerHandler"},
{0x53B00E9A,0,"sceKernelSetVTimerHandlerWide"},
{0x94416130,WrapU_UUUU<sceKernelGetThreadmanIdList>,"sceKernelGetThreadmanIdList"},
{0x57CF62DD,WrapU_U<sceKernelGetThreadmanIdType>,"sceKernelGetThreadmanIdType"},
{0x82BC5777,sceKernelGetSystemTimeWide,"sceKernelGetSystemTimeWide"},
{0xdb738f35,sceKernelGetSystemTime,"sceKernelGetSystemTime"},
@ -409,10 +575,10 @@ const HLEFunction ThreadManForUser[] =
{0x64D4540E,0,"sceKernelReferThreadProfiler"},
//Fifa Street 2 uses alarms
{0x6652b8ca,sceKernelSetAlarm,"sceKernelSetAlarm"},
{0xB2C25152,sceKernelSetSysClockAlarm,"sceKernelSetSysClockAlarm"},
{0x7e65b999,sceKernelCancelAlarm,"sceKernelCancelAlarm"},
{0xDAA3F564,sceKernelReferAlarmStatus,"sceKernelReferAlarmStatus"},
{0x6652b8ca,WrapI_UUU<sceKernelSetAlarm>,"sceKernelSetAlarm"},
{0xB2C25152,WrapI_UUU<sceKernelSetSysClockAlarm>,"sceKernelSetSysClockAlarm"},
{0x7e65b999,WrapI_I<sceKernelCancelAlarm>,"sceKernelCancelAlarm"},
{0xDAA3F564,WrapI_IU<sceKernelReferAlarmStatus>,"sceKernelReferAlarmStatus"},
{0xba6b92e2,sceKernelSysClock2USec,"sceKernelSysClock2USec"},
{0x110DEC9A,0,"sceKernelUSec2SysClock"},
@ -435,11 +601,11 @@ const HLEFunction ThreadManForUser[] =
{0x2A3D44FF,sceKernelGetCallbackCount,"sceKernelGetCallbackCount"},
{0x730ED8BC,sceKernelReferCallbackStatus,"sceKernelReferCallbackStatus"},
{0x8125221D,&WrapU_CIUIU<sceKernelCreateMbx>,"sceKernelCreateMbx"},
{0x8125221D,&WrapI_CUU<sceKernelCreateMbx>,"sceKernelCreateMbx"},
{0x86255ADA,&WrapI_I<sceKernelDeleteMbx>,"sceKernelDeleteMbx"},
{0xE9B3061E,&WrapV_IU<sceKernelSendMbx>,"sceKernelSendMbx"},
{0x18260574,&WrapV_IUU<sceKernelReceiveMbx>,"sceKernelReceiveMbx"},
{0xF3986382,&WrapV_IUU<sceKernelReceiveMbxCB>,"sceKernelReceiveMbxCB"},
{0xE9B3061E,&WrapI_IU<sceKernelSendMbx>,"sceKernelSendMbx"},
{0x18260574,&WrapI_IUU<sceKernelReceiveMbx>,"sceKernelReceiveMbx"},
{0xF3986382,&WrapI_IUU<sceKernelReceiveMbxCB>,"sceKernelReceiveMbxCB"},
{0x0D81716A,&WrapI_IU<sceKernelPollMbx>,"sceKernelPollMbx"},
{0x87D4DD36,&WrapI_IU<sceKernelCancelReceiveMbx>,"sceKernelCancelReceiveMbx"},
{0xA8E8C846,&WrapI_IU<sceKernelReferMbxStatus>,"sceKernelReferMbxStatus"},
@ -473,6 +639,21 @@ const HLEFunction ThreadManForUser[] =
{0xA8AA591F,sceKernelCancelFpl,"sceKernelCancelFpl"},
{0xD8199E4C,sceKernelReferFplStatus,"sceKernelReferFplStatus"},
{0x20fff560,WrapU_CU<sceKernelCreateVTimer>,"sceKernelCreateVTimer"},
{0x328F9E52,WrapU_U<sceKernelDeleteVTimer>,"sceKernelDeleteVTimer"},
{0xc68d9437,WrapU_U<sceKernelStartVTimer>,"sceKernelStartVTimer"},
{0xD0AEEE87,WrapU_U<sceKernelStopVTimer>,"sceKernelStopVTimer"},
{0xD2D615EF,WrapU_U<sceKernelCancelVTimerHandler>,"sceKernelCancelVTimerHandler"},
{0xB3A59970,WrapU_UU<sceKernelGetVTimerBase>,"sceKernelGetVTimerBase"},
{0xB7C18B77,WrapU64_U<sceKernelGetVTimerBaseWide>,"sceKernelGetVTimerBaseWide"},
{0x034A921F,WrapU_UU<sceKernelGetVTimerTime>,"sceKernelGetVTimerTime"},
{0xC0B3FFD2,WrapU64_U<sceKernelGetVTimerTimeWide>,"sceKernelGetVTimerTimeWide"},
{0x5F32BEAA,WrapU_UU<sceKernelReferVTimerStatus>,"sceKernelReferVTimerStatus"},
{0x542AD630,WrapU_UU<sceKernelSetVTimerTime>,"sceKernelSetVTimerTime"},
{0xFB6425C3,WrapU_UU64<sceKernelSetVTimerTimeWide>,"sceKernelSetVTimerTimeWide"},
{0xd8b299ae,WrapU_UUUU<sceKernelSetVTimerHandler>,"sceKernelSetVTimerHandler"},
{0x53B00E9A,WrapU_UU64UU<sceKernelSetVTimerHandlerWide>,"sceKernelSetVTimerHandlerWide"},
// Not sure if these should be hooked up. See below.
{0x0E927AED, _sceKernelReturnFromTimerHandler, "_sceKernelReturnFromTimerHandler"},
{0x532A522E, _sceKernelExitThread,"_sceKernelExitThread"},

View File

@ -18,11 +18,15 @@
#pragma once
#include "../../Globals.h"
#include "../../Common/ChunkFile.h"
#include <cstring>
enum
{
SCE_KERNEL_ERROR_OK = 0,
SCE_KERNEL_ERROR_OK = 0,
SCE_KERNEL_ERROR_OUT_OF_MEMORY = 0x80000022,
SCE_KERNEL_ERROR_INVALID_ID = 0x80000100,
SCE_KERNEL_ERROR_INVALID_SIZE = 0x80000104,
SCE_KERNEL_ERROR_INVALID_VALUE = 0x800001fe,
SCE_KERNEL_ERROR_INVALID_ARGUMENT = 0x800001ff,
SCE_KERNEL_ERROR_ERROR = 0x80020001,
@ -223,6 +227,7 @@ enum
SCE_KERNEL_ERROR_ERRORMAX = 0x8002044d,
};
// If you add to this, make sure to check KernelObjectPool::CreateByIDType().
enum TMIDPurpose
{
SCE_KERNEL_TMID_Thread = 1,
@ -242,6 +247,12 @@ enum TMIDPurpose
SCE_KERNEL_TMID_DelayThread = 65,
SCE_KERNEL_TMID_SuspendThread = 66,
SCE_KERNEL_TMID_DormantThread = 67,
// Not official, but need ids for save states.
PPSSPP_KERNEL_TMID_Module = 0x100001,
PPSSPP_KERNEL_TMID_PMB = 0x100002,
PPSSPP_KERNEL_TMID_File = 0x100003,
PPSSPP_KERNEL_TMID_DirList = 0x100004,
};
typedef int SceUID;
@ -263,6 +274,7 @@ struct SceKernelLoadExecParam
void __KernelInit();
void __KernelShutdown();
void __KernelDoState(PointerWrap &p);
bool __KernelIsRunning();
bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param);
@ -283,10 +295,11 @@ void sceKernelFindModuleByName();
void sceKernelSetGPO();
void sceKernelGetGPI();
void sceKernelDcacheWritebackAll();
void sceKernelDcacheWritebackRange();
void sceKernelDcacheWritebackInvalidateRange();
void sceKernelDcacheWritebackInvalidateAll();
int sceKernelDcacheInvalidateRange(u32 addr, int size);
int sceKernelDcacheWritebackAll();
int sceKernelDcacheWritebackRange(u32 addr, int size);
int sceKernelDcacheWritebackInvalidateRange(u32 addr, int size);
int sceKernelDcacheWritebackInvalidateAll();
void sceKernelGetThreadStackFreeSize();
void sceKernelIcacheInvalidateAll();
void sceKernelIcacheClearAll();
@ -310,9 +323,10 @@ public:
// Implement this in all subclasses:
// static u32 GetMissingErrorCode()
// Future
// void Serialize(ChunkFile)
// void DeSerialize(ChunkFile)
virtual void DoState(PointerWrap &p)
{
_dbg_assert_msg_(HLE, false, "Unable to save state: bad kernel object.");
}
};
@ -324,7 +338,8 @@ public:
// Allocates a UID within the range and inserts the object into the map.
SceUID Create(KernelObject *obj, int rangeBottom = 16, int rangeTop = 0x7fffffff);
// TODO: How will we ever save/restore this pool?
void DoState(PointerWrap &p);
static KernelObject *CreateByIDType(int type);
template <class T>
u32 Destroy(SceUID handle)
@ -362,6 +377,24 @@ public:
return t;
}
}
template <class T>
T* GetByModuleByEntryAddr(u32 entryAddr)
{
for (int i=0; i <4096; i++)
{
T* t = dynamic_cast<T*>(pool[i]);
if (t)
{
if (t->nm.entry_addr == entryAddr)
{
return t;
}
}
}
return 0;
}
static u32 GetMissingErrorCode() { return -1; } // TODO
bool GetIDType(SceUID handle, int *type) const

View File

@ -17,28 +17,212 @@
#include "sceKernel.h"
#include "sceKernelAlarm.h"
#include "sceKernelInterrupt.h"
#include "HLE.h"
#include "../../Core/CoreTiming.h"
void sceKernelSetAlarm()
const int NATIVEALARM_SIZE = 20;
struct NativeAlarm
{
ERROR_LOG(HLE,"UNIMPL sceKernelSetAlarm");
RETURN(-1);
SceSize size;
u64 schedule;
u32 handlerPtr;
u32 commonPtr;
};
struct Alarm : public KernelObject
{
const char *GetName() {return "[Alarm]";}
const char *GetTypeName() {return "Alarm";}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_ALMID; }
int GetIDType() const { return SCE_KERNEL_TMID_Alarm; }
virtual void DoState(PointerWrap &p)
{
p.Do(alm);
p.DoMarker("Alarm");
}
NativeAlarm alm;
};
void __KernelScheduleAlarm(Alarm *alarm, u64 ticks);
class AlarmIntrHandler : public SubIntrHandler
{
public:
static SubIntrHandler *Create()
{
return new AlarmIntrHandler();
}
void setAlarm(Alarm *alarm)
{
alarmID = alarm->GetUID();
handlerAddress = alarm->alm.handlerPtr;
enabled = true;
}
virtual void copyArgsToCPU(const PendingInterrupt &pend)
{
SubIntrHandler::copyArgsToCPU(pend);
u32 error;
Alarm *alarm = kernelObjects.Get<Alarm>(alarmID, error);
if (alarm)
currentMIPS->r[MIPS_REG_A0] = alarm->alm.commonPtr;
else
ERROR_LOG(HLE, "sceKernelAlarm: Unable to send interrupt args: alarm deleted?");
}
virtual void handleResult(int result)
{
// A non-zero result means to reschedule.
if (result > 0)
{
u32 error;
Alarm *alarm = kernelObjects.Get<Alarm>(alarmID, error);
__KernelScheduleAlarm(alarm, (u64) usToCycles(result));
}
else
{
if (result < 0)
WARN_LOG(HLE, "Alarm requested reschedule for negative value %u, ignoring", (unsigned) result);
// Delete the alarm if it's not rescheduled.
kernelObjects.Destroy<Alarm>(alarmID);
__ReleaseSubIntrHandler(PSP_SYSTIMER0_INTR, alarmID);
}
}
virtual void DoState(PointerWrap &p)
{
SubIntrHandler::DoState(p);
p.Do(alarmID);
p.DoMarker("AlarmIntrHandler");
}
SceUID alarmID;
};
static int alarmTimer = 0;
void __KernelTriggerAlarm(u64 userdata, int cyclesLate)
{
int uid = (int) userdata;
u32 error;
Alarm *alarm = kernelObjects.Get<Alarm>(uid, error);
if (alarm)
__TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_SYSTIMER0_INTR, uid);
}
void sceKernelSetSysClockAlarm()
void __KernelAlarmInit()
{
ERROR_LOG(HLE,"UNIMPL sceKernelSetSysClockAlarm");
RETURN(-1);
alarmTimer = CoreTiming::RegisterEvent("Alarm", __KernelTriggerAlarm);
__RegisterSubIntrCreator(PSP_SYSTIMER0_INTR, AlarmIntrHandler::Create);
}
void sceKernelCancelAlarm()
void __KernelAlarmDoState(PointerWrap &p)
{
ERROR_LOG(HLE,"UNIMPL sceKernelCancelAlarm");
RETURN(-1);
p.Do(alarmTimer);
CoreTiming::RestoreRegisterEvent(alarmTimer, "Alarm", __KernelTriggerAlarm);
p.DoMarker("sceKernelAlarm");
}
void sceKernelReferAlarmStatus()
KernelObject *__KernelAlarmObject()
{
ERROR_LOG(HLE,"UNIMPL sceKernelReferAlarmStatus");
RETURN(-1);
// Default object to load from state.
return new Alarm;
}
void __KernelScheduleAlarm(Alarm *alarm, u64 ticks)
{
alarm->alm.schedule = (CoreTiming::GetTicks() + ticks) / (u64) CoreTiming::GetClockFrequencyMHz();
CoreTiming::ScheduleEvent((int) ticks, alarmTimer, alarm->GetUID());
}
SceUID __KernelSetAlarm(u64 ticks, u32 handlerPtr, u32 commonPtr)
{
if (!Memory::IsValidAddress(handlerPtr))
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
Alarm *alarm = new Alarm;
SceUID uid = kernelObjects.Create(alarm);
alarm->alm.size = NATIVEALARM_SIZE;
alarm->alm.handlerPtr = handlerPtr;
alarm->alm.commonPtr = commonPtr;
u32 error;
AlarmIntrHandler *handler = (AlarmIntrHandler *) __RegisterSubIntrHandler(PSP_SYSTIMER0_INTR, uid, error);
if (error != 0)
{
kernelObjects.Destroy<Alarm>(uid);
return error;
}
handler->setAlarm(alarm);
__KernelScheduleAlarm(alarm, ticks);
return uid;
}
SceUID sceKernelSetAlarm(SceUInt micro, u32 handlerPtr, u32 commonPtr)
{
DEBUG_LOG(HLE, "sceKernelSetAlarm(%d, %08x, %08x)", micro, handlerPtr, commonPtr);
return __KernelSetAlarm(usToCycles((u64) micro), handlerPtr, commonPtr);
}
SceUID sceKernelSetSysClockAlarm(u32 microPtr, u32 handlerPtr, u32 commonPtr)
{
u64 micro;
if (Memory::IsValidAddress(microPtr))
micro = Memory::Read_U64(microPtr);
else
return -1;
DEBUG_LOG(HLE, "sceKernelSetSysClockAlarm(%lld, %08x, %08x)", micro, handlerPtr, commonPtr);
return __KernelSetAlarm(usToCycles(micro), handlerPtr, commonPtr);
}
int sceKernelCancelAlarm(SceUID uid)
{
DEBUG_LOG(HLE, "sceKernelCancelAlarm(%08x)", uid);
CoreTiming::UnscheduleEvent(alarmTimer, uid);
__ReleaseSubIntrHandler(PSP_SYSTIMER0_INTR, uid);
return kernelObjects.Destroy<Alarm>(uid);
}
int sceKernelReferAlarmStatus(SceUID uid, u32 infoPtr)
{
u32 error;
Alarm *alarm = kernelObjects.Get<Alarm>(uid, error);
if (!alarm)
{
ERROR_LOG(HLE, "sceKernelReferAlarmStatus(%08x, %08x): invalid alarm", uid, infoPtr);
return error;
}
DEBUG_LOG(HLE, "sceKernelReferAlarmStatus(%08x, %08x)", uid, infoPtr);
if (!Memory::IsValidAddress(infoPtr))
return -1;
u32 size = Memory::Read_U32(infoPtr);
// Alarms actually respect size and write (kinda) what it can hold.
if (size > 0)
Memory::Write_U32(alarm->alm.size, infoPtr);
if (size > 4)
Memory::Write_U64(alarm->alm.schedule, infoPtr + 4);
if (size > 12)
Memory::Write_U32(alarm->alm.handlerPtr, infoPtr + 12);
if (size > 16)
Memory::Write_U32(alarm->alm.commonPtr, infoPtr + 16);
return 0;
}

View File

@ -17,7 +17,11 @@
#pragma once
void sceKernelSetAlarm();
void sceKernelSetSysClockAlarm();
void sceKernelCancelAlarm();
void sceKernelReferAlarmStatus();
SceUID sceKernelSetAlarm(SceUInt clock, u32 handlerPtr, u32 commonPtr);
SceUID sceKernelSetSysClockAlarm(u32 sysClockPtr, u32 handlerPtr, u32 commonPtr);
int sceKernelCancelAlarm(SceUID uid);
int sceKernelReferAlarmStatus(SceUID uid, u32 infoPtr);
void __KernelAlarmInit();
void __KernelAlarmDoState(PointerWrap &p);
KernelObject *__KernelAlarmObject();

View File

@ -19,18 +19,18 @@
#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
{
u32 size;
char name[32];
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
u32 attr;
u32 initPattern;
u32 currentPattern;
@ -57,12 +57,20 @@ public:
nef.currentPattern,
nef.numWaitThreads);
}
static u32 GetMissingErrorCode() {
return SCE_KERNEL_ERROR_UNKNOWN_EVFID;
}
int GetIDType() const { return SCE_KERNEL_TMID_EventFlag; }
virtual void DoState(PointerWrap &p)
{
p.Do(nef);
EventFlagTh eft = {0};
p.Do(waitingThreads, eft);
p.DoMarker("EventFlag");
}
NativeEventFlag nef;
std::vector<EventFlagTh> waitingThreads;
};
@ -79,37 +87,170 @@ 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,
};
int eventFlagWaitTimer = 0;
void __KernelEventFlagInit()
{
eventFlagWaitTimer = CoreTiming::RegisterEvent("EventFlagTimeout", __KernelEventFlagTimeout);
}
void __KernelEventFlagDoState(PointerWrap &p)
{
p.Do(eventFlagWaitTimer);
CoreTiming::RestoreRegisterEvent(eventFlagWaitTimer, "EventFlagTimeout", __KernelEventFlagTimeout);
p.DoMarker("sceKernelEventFlag");
}
KernelObject *__KernelEventFlagObject()
{
// Default object to load from state.
return new EventFlag;
}
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 (!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, KERNELOBJECT_MAX_NAME_LENGTH);
e->nef.name[KERNELOBJECT_MAX_NAME_LENGTH] = 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 +264,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 +291,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 +313,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,30 +395,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 ? 0 : 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
__KernelCheckCallbacks();
__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 ? 0 : 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);
@ -253,6 +510,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;
}
@ -270,7 +531,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)
@ -283,10 +544,3 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr)
return error;
}
}
// never seen this one
u32 sceKernelCancelEventFlag()
{
ERROR_LOG(HLE,"UNIMPL: sceKernelCancelEventFlag()");
return 0;
}

View File

@ -21,8 +21,12 @@ 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);
void __KernelEventFlagInit();
void __KernelEventFlagDoState(PointerWrap &p);
KernelObject *__KernelEventFlagObject();

View File

@ -22,46 +22,14 @@
#include "HLE.h"
#include "../MIPS/MIPS.h"
#include "Action.h"
#include "sceKernel.h"
#include "sceKernelThread.h"
#include "sceKernelInterrupt.h"
#include "sceKernelMutex.h"
struct Interrupt
{
PSPInterrupt intno;
};
// Yeah, this bit is a bit silly.
static int interruptsEnabled = 1;
static bool inInterrupt;
void __InterruptsInit()
{
interruptsEnabled = 1;
}
void __InterruptsShutdown()
{
}
void __DisableInterrupts()
{
interruptsEnabled = 0;
}
void __EnableInterrupts()
{
interruptsEnabled = 1;
}
bool __InterruptsEnabled()
{
return interruptsEnabled != 0;
}
void __DisableInterrupts();
void __EnableInterrupts();
bool __InterruptsEnabled();
// InterruptsManager
//////////////////////////////////////////////////////////////////////////
@ -89,6 +57,7 @@ void sceKernelCpuResumeIntr(u32 enable)
if (enable)
{
__EnableInterrupts();
hleRunInterrupts();
}
else
{
@ -115,86 +84,31 @@ void sceKernelCpuResumeIntrWithSync(u32 enable)
sceKernelCpuResumeIntr(enable);
}
bool __IsInInterrupt()
{
return inInterrupt;
}
bool __CanExecuteInterrupt()
{
return !inInterrupt;
}
class AllegrexInterruptHandler;
struct PendingInterrupt {
AllegrexInterruptHandler *handler;
int arg;
bool hasArg;
};
class AllegrexInterruptHandler
{
public:
virtual ~AllegrexInterruptHandler() {}
virtual void copyArgsToCPU(const PendingInterrupt &pend) = 0;
virtual void queueUp() = 0;
virtual void queueUpWithArg(int arg) = 0;
};
std::list<PendingInterrupt> pendingInterrupts;
class SubIntrHandler : public AllegrexInterruptHandler
{
public:
SubIntrHandler() {}
virtual void queueUp()
{
if (!enabled)
return;
PendingInterrupt pend;
pend.handler = this;
pend.hasArg = false;
pendingInterrupts.push_back(pend);
}
virtual void queueUpWithArg(int arg)
{
if (!enabled)
return;
PendingInterrupt pend;
pend.handler = this;
pend.arg = arg;
pend.hasArg = true;
pendingInterrupts.push_back(pend);
}
virtual void copyArgsToCPU(const PendingInterrupt &pend)
{
DEBUG_LOG(CPU, "Entering interrupt handler %08x", handlerAddress);
currentMIPS->pc = handlerAddress;
currentMIPS->r[MIPS_REG_A0] = pend.hasArg ? pend.arg : number;
currentMIPS->r[MIPS_REG_A1] = handlerArg;
// RA is already taken care of
}
bool enabled;
int number;
u32 handlerAddress;
u32 handlerArg;
};
class IntrHandler {
public:
void add(int subIntrNum, SubIntrHandler handler)
IntrHandler()
: subIntrCreator(NULL)
{
}
SubIntrHandler *add(int subIntrNum)
{
SubIntrHandler *handler;
if (subIntrCreator != NULL)
handler = subIntrCreator();
else
handler = new SubIntrHandler();
subIntrHandlers[subIntrNum] = handler;
return handler;
}
void remove(int subIntrNum)
{
subIntrHandlers.erase(subIntrNum);
if (has(subIntrNum))
{
delete subIntrHandlers[subIntrNum];
subIntrHandlers.erase(subIntrNum);
}
}
bool has(int subIntrNum) const
{
@ -203,20 +117,26 @@ public:
SubIntrHandler *get(int subIntrNum)
{
if (has(subIntrNum))
return &subIntrHandlers[subIntrNum];
return subIntrHandlers[subIntrNum];
else
return 0;
// what to do, what to do...
return NULL;
}
void clear()
{
std::map<int, SubIntrHandler *>::iterator it, end;
for (it = subIntrHandlers.begin(), end = subIntrHandlers.end(); it != end; ++it)
delete it->second;
subIntrHandlers.clear();
}
void queueUp(int subintr)
{
// Just call execute on all the subintr handlers for this interrupt.
// They will get queued up.
for (std::map<int, SubIntrHandler>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
for (std::map<int, SubIntrHandler *>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
{
if (subintr == -1 || iter->first == subintr)
iter->second.queueUp();
iter->second->queueUp();
}
}
@ -224,31 +144,71 @@ public:
{
// Just call execute on all the subintr handlers for this interrupt.
// They will get queued up.
for (std::map<int, SubIntrHandler>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
for (std::map<int, SubIntrHandler *>::iterator iter = subIntrHandlers.begin(); iter != subIntrHandlers.end(); ++iter)
{
if (subintr == -1 || iter->first == subintr)
iter->second.queueUpWithArg(arg);
iter->second->queueUpWithArg(arg);
}
}
void setCreator(SubIntrCreator creator)
{
subIntrCreator = creator;
}
void DoState(PointerWrap &p)
{
// We assume that the same creator has already been registered.
bool hasCreator = subIntrCreator != NULL;
p.Do(hasCreator);
if (hasCreator != (subIntrCreator != NULL))
{
ERROR_LOG(HLE, "Savestate failure: incompatible sub interrupt handler.");
return;
}
int n = (int) subIntrHandlers.size();
p.Do(n);
if (p.mode == p.MODE_READ)
{
clear();
for (int i = 0; i < n; ++i)
{
int subIntrNum;
p.Do(subIntrNum);
SubIntrHandler *handler = add(subIntrNum);
handler->DoState(p);
}
}
else
{
std::map<int, SubIntrHandler *>::iterator it, end;
for (it = subIntrHandlers.begin(), end = subIntrHandlers.end(); it != end; ++it)
{
p.Do(it->first);
it->second->DoState(p);
}
}
}
private:
std::map<int, SubIntrHandler> subIntrHandlers;
SubIntrCreator subIntrCreator;
std::map<int, SubIntrHandler *> subIntrHandlers;
};
class InterruptState
{
public:
void save()
{
insideInterrupt = __IsInInterrupt();
__KernelSaveContext(&savedCpu);
}
void save();
void restore();
void clear();
void restore()
void DoState(PointerWrap &p)
{
::inInterrupt = insideInterrupt;
__KernelLoadContext(&savedCpu);
p.Do(insideInterrupt);
p.Do(savedCpu);
p.DoMarker("InterruptState");
}
bool insideInterrupt;
@ -261,27 +221,152 @@ public:
InterruptState intState;
IntrHandler intrHandlers[PSP_NUMBER_INTERRUPTS];
std::list<PendingInterrupt> pendingInterrupts;
// Yeah, this bit is a bit silly.
static int interruptsEnabled = 1;
static bool inInterrupt;
void __InterruptsInit()
{
interruptsEnabled = 1;
inInterrupt = false;
intState.clear();
}
void __InterruptsDoState(PointerWrap &p)
{
int numInterrupts = PSP_NUMBER_INTERRUPTS;
p.Do(numInterrupts);
if (numInterrupts != PSP_NUMBER_INTERRUPTS)
{
ERROR_LOG(HLE, "Savestate failure: wrong number of interrupts, can't load.");
return;
}
intState.DoState(p);
PendingInterrupt pi(0, 0);
p.Do(pendingInterrupts, pi);
p.Do(interruptsEnabled);
p.Do(inInterrupt);
p.DoMarker("sceKernelInterrupt");
}
void __InterruptsDoStateLate(PointerWrap &p)
{
// We do these later to ensure the handlers have been registered.
for (int i = 0; i < PSP_NUMBER_INTERRUPTS; ++i)
intrHandlers[i].DoState(p);
p.DoMarker("sceKernelInterrupt Late");
}
void __InterruptsShutdown()
{
for (int i = 0; i < PSP_NUMBER_INTERRUPTS; ++i)
intrHandlers[i].clear();
pendingInterrupts.clear();
}
void __DisableInterrupts()
{
interruptsEnabled = 0;
}
void __EnableInterrupts()
{
interruptsEnabled = 1;
}
bool __InterruptsEnabled()
{
return interruptsEnabled != 0;
}
bool __IsInInterrupt()
{
return inInterrupt;
}
bool __CanExecuteInterrupt()
{
return !inInterrupt;
}
void InterruptState::save()
{
insideInterrupt = __IsInInterrupt();
__KernelSaveContext(&savedCpu);
}
void InterruptState::restore()
{
::inInterrupt = insideInterrupt;
__KernelLoadContext(&savedCpu);
}
void InterruptState::clear()
{
insideInterrupt = false;
}
// http://forums.ps2dev.org/viewtopic.php?t=5687
// http://www.google.se/url?sa=t&rct=j&q=&esrc=s&source=web&cd=7&ved=0CFYQFjAG&url=http%3A%2F%2Fdev.psnpt.com%2Fredmine%2Fprojects%2Fuofw%2Frepository%2Frevisions%2F65%2Fraw%2Ftrunk%2Finclude%2Finterruptman.h&ei=J4pCUKvyK4nl4QSu-YC4Cg&usg=AFQjCNFxJcgzQnv6dK7aiQlht_BM9grfQQ&sig2=GGk5QUEWI6qouYDoyE07YQ
void SubIntrHandler::queueUp()
{
if (!enabled)
return;
pendingInterrupts.push_back(PendingInterrupt(intrNumber, number));
};
void SubIntrHandler::queueUpWithArg(int arg)
{
if (!enabled)
return;
pendingInterrupts.push_back(PendingInterrupt(intrNumber, number, arg));
}
void SubIntrHandler::copyArgsToCPU(const PendingInterrupt &pend)
{
DEBUG_LOG(CPU, "Entering interrupt handler %08x", handlerAddress);
currentMIPS->pc = handlerAddress;
currentMIPS->r[MIPS_REG_A0] = pend.hasArg ? pend.arg : number;
currentMIPS->r[MIPS_REG_A1] = handlerArg;
// RA is already taken care of
}
// Returns true if anything was executed.
bool __RunOnePendingInterrupt()
{
if (inInterrupt)
if (inInterrupt || !interruptsEnabled)
{
// Already in an interrupt! We'll keep going when it's done.
return false;
}
// Can easily prioritize between different kinds of interrupts if necessary.
retry:
if (pendingInterrupts.size())
{
// If we came from CoreTiming::Advance(), we might've come from a waiting thread's callback.
// To avoid "injecting" return values into our saved state, we context switch here.
__KernelSwitchOffThread("interrupt");
PendingInterrupt pend = pendingInterrupts.front();
pendingInterrupts.pop_front();
SubIntrHandler *handler = intrHandlers[pend.intr].get(pend.subintr);
if (handler == NULL)
{
WARN_LOG(HLE, "Ignoring interrupt, already been released.");
pendingInterrupts.pop_front();
goto retry;
}
intState.save();
pend.handler->copyArgsToCPU(pend);
handler->copyArgsToCPU(pend);
currentMIPS->r[MIPS_REG_RA] = __KernelInterruptReturnAddress();
inInterrupt = true;
@ -294,37 +379,92 @@ bool __RunOnePendingInterrupt()
}
}
void __TriggerInterrupt(PSPInterrupt intno, int subintr)
void __TriggerRunInterrupts(int type)
{
intrHandlers[intno].queueUp(subintr);
DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i (%i in queue)", intno, subintr, pendingInterrupts.size());
if (!inInterrupt)
__RunOnePendingInterrupt();
// If interrupts aren't enabled, we run them later.
if (interruptsEnabled && !inInterrupt)
{
if ((type & PSP_INTR_HLE) != 0)
hleRunInterrupts();
else
__RunOnePendingInterrupt();
}
}
void __TriggerInterruptWithArg(PSPInterrupt intno, int subintr, int arg)
void __TriggerInterrupt(int type, PSPInterrupt intno, int subintr)
{
intrHandlers[intno].queueUpWithArg(subintr, arg);
DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i with arg %i (%i in queue)", intno, subintr, arg, pendingInterrupts.size());
if (!inInterrupt)
__RunOnePendingInterrupt();
if (interruptsEnabled || (type & PSP_INTR_ONLY_IF_ENABLED) == 0)
{
intrHandlers[intno].queueUp(subintr);
DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i (%i in queue)", intno, subintr, (u32)pendingInterrupts.size());
__TriggerRunInterrupts(type);
}
}
void __TriggerInterruptWithArg(int type, PSPInterrupt intno, int subintr, int arg)
{
if (interruptsEnabled || (type & PSP_INTR_ONLY_IF_ENABLED) == 0)
{
intrHandlers[intno].queueUpWithArg(subintr, arg);
DEBUG_LOG(HLE, "Triggering subinterrupts for interrupt %i sub %i with arg %i (%i in queue)", intno, subintr, arg,
(u32)pendingInterrupts.size());
__TriggerRunInterrupts(type);
}
}
void __KernelReturnFromInterrupt()
{
DEBUG_LOG(CPU, "Left interrupt handler at %08x", currentMIPS->pc);
inInterrupt = false;
// This is what we just ran.
PendingInterrupt pend = pendingInterrupts.front();
pendingInterrupts.pop_front();
SubIntrHandler *handler = intrHandlers[pend.intr].get(pend.subintr);
if (handler != NULL)
handler->handleResult(currentMIPS->r[MIPS_REG_V0]);
else
ERROR_LOG(HLE, "Interrupt released itself? Should not happen.");
// Restore context after running the interrupt.
intState.restore();
// All should now be back to normal, including PC.
// Alright, let's see if there's any more interrupts queued...
if (!__RunOnePendingInterrupt())
__KernelReSchedule("return from interrupt");
}
void __RegisterSubIntrCreator(u32 intrNumber, SubIntrCreator creator)
{
intrHandlers[intrNumber].setCreator(creator);
}
SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 &error)
{
SubIntrHandler *subIntrHandler = intrHandlers[intrNumber].add(subIntrNumber);
subIntrHandler->number = subIntrNumber;
subIntrHandler->intrNumber = intrNumber;
error = 0;
return subIntrHandler;
}
u32 __ReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber)
{
if (!intrHandlers[intrNumber].has(subIntrNumber))
return -1;
for (std::list<PendingInterrupt>::iterator it = pendingInterrupts.begin(); it != pendingInterrupts.end(); )
{
// Hmmm...
//__KernelReSchedule("return from interrupt");
if (it->intr == intrNumber && it->subintr == subIntrNumber)
pendingInterrupts.erase(it++);
else
++it;
}
intrHandlers[intrNumber].remove(subIntrNumber);
return 0;
}
u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg)
@ -334,35 +474,31 @@ u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handl
if (intrNumber >= PSP_NUMBER_INTERRUPTS)
return -1;
SubIntrHandler subIntrHandler;
subIntrHandler.number = subIntrNumber;
subIntrHandler.enabled = false;
subIntrHandler.handlerAddress = handler;
subIntrHandler.handlerArg = handlerArg;
intrHandlers[intrNumber].add(subIntrNumber, subIntrHandler);
return 0;
u32 error;
SubIntrHandler *subIntrHandler = __RegisterSubIntrHandler(intrNumber, subIntrNumber, error);
if (subIntrHandler)
{
subIntrHandler->enabled = false;
subIntrHandler->handlerAddress = handler;
subIntrHandler->handlerArg = handlerArg;
}
return error;
}
u32 sceKernelReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber)
{
DEBUG_LOG(HLE,"sceKernelReleaseSubIntrHandler(%i, %i)", PARAM(0), PARAM(1));
// TODO: should check if it's pending and remove it from pending list! (although that's probably unlikely)
if (intrNumber >= PSP_NUMBER_INTERRUPTS)
return -1;
if (!intrHandlers[intrNumber].has(subIntrNumber))
return -1;
intrHandlers[intrNumber].remove(subIntrNumber);
return 0;
return __ReleaseSubIntrHandler(intrNumber, subIntrNumber);
}
u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber)
{
DEBUG_LOG(HLE,"sceKernelEnableSubIntr(%i, %i)", intrNumber, subIntrNumber);
if (intrNumber < 0 || intrNumber >= PSP_NUMBER_INTERRUPTS)
if (intrNumber >= PSP_NUMBER_INTERRUPTS)
return -1;
if (!intrHandlers[intrNumber].has(subIntrNumber))
@ -375,7 +511,7 @@ u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber)
u32 sceKernelDisableSubIntr(u32 intrNumber, u32 subIntrNumber)
{
DEBUG_LOG(HLE,"sceKernelDisableSubIntr(%i, %i)", intrNumber, subIntrNumber);
if (intrNumber < 0 || intrNumber >= PSP_NUMBER_INTERRUPTS)
if (intrNumber >= PSP_NUMBER_INTERRUPTS)
return -1;
if (!intrHandlers[intrNumber].has(subIntrNumber))
@ -410,42 +546,54 @@ void QueryIntrHandlerInfo()
RETURN(0);
}
void sceKernelMemset()
u32 sceKernelMemset(u32 addr, u32 fillc, u32 n)
{
u32 addr = PARAM(0);
u8 c = PARAM(1) & 0xff;
u32 n = PARAM(2);
u8 c = fillc & 0xff;
DEBUG_LOG(HLE, "sceKernelMemset(ptr = %08x, c = %02x, n = %08x)", addr, c, n);
for (size_t i = 0; i < n; i++)
Memory::Write_U8((u8)c, addr + i);
RETURN(0); /* TODO: verify it should return this */
Memory::Memset(addr, c, n);
return addr;
}
u32 sceKernelMemcpy(u32 dst, u32 src, u32 size)
{
DEBUG_LOG(HLE, "sceKernelMemcpy(dest=%08x, src=%08x, size=%i)", dst, src, size);
if (Memory::IsValidAddress(dst) && Memory::IsValidAddress(src+size)) // a bit of bound checking. Wrong??
// Technically should crash if these are invalid and size > 0...
if (Memory::IsValidAddress(dst) && Memory::IsValidAddress(src + size - 1))
{
Memory::Memcpy(dst, Memory::GetPointer(src), size);
u8 *dstp = Memory::GetPointer(dst);
u8 *srcp = Memory::GetPointer(src);
u32 size64 = size / 8;
u32 size8 = size % 8;
// Try to handle overlapped copies with similar properties to hardware, just in case.
// Not that anyone ought to rely on it.
while (size64-- > 0)
{
*(u64 *) dstp = *(u64 *) srcp;
srcp += 8;
dstp += 8;
}
while (size8-- > 0)
*dstp++ = *srcp++;
}
return 0;
return dst;
}
const HLEFunction Kernel_Library[] =
const HLEFunction Kernel_Library[] =
{
{0x092968F4,sceKernelCpuSuspendIntr,"sceKernelCpuSuspendIntr"},
{0x5F10D406,WrapV_U<sceKernelCpuResumeIntr>, "sceKernelCpuResumeIntr"}, //int oldstat
{0x3b84732d,WrapV_U<sceKernelCpuResumeIntrWithSync>, "sceKernelCpuResumeIntrWithSync"},
{0x47a0b729,sceKernelIsCpuIntrSuspended, "sceKernelIsCpuIntrSuspended"}, //flags
{0xb55249d2,sceKernelIsCpuIntrEnable, "sceKernelIsCpuIntrEnable"},
{0xa089eca4,sceKernelMemset, "sceKernelMemset"},
{0xDC692EE3,&WrapV_UI<sceKernelTryLockLwMutex>, "sceKernelTryLockLwMutex"},
{0x37431849,&WrapV_UI<sceKernelTryLockLwMutex_600>, "sceKernelTryLockLwMutex_600"},
{0xbea46419,&WrapV_UIU<sceKernelLockLwMutex>, "sceKernelLockLwMutex"},
{0x1FC64E09,&WrapV_UIU<sceKernelLockLwMutexCB>, "sceKernelLockLwMutexCB"},
{0x15b6446b,&WrapV_UI<sceKernelUnlockLwMutex>, "sceKernelUnlockLwMutex"},
{0x293b45b8,sceKernelGetThreadId, "sceKernelGetThreadId"},
{0x1839852A,&WrapU_UUU<sceKernelMemcpy>,"sce_paf_private_memcpy"},
{0xb55249d2,sceKernelIsCpuIntrEnable, "sceKernelIsCpuIntrEnable"},
{0xa089eca4,WrapU_UUU<sceKernelMemset>, "sceKernelMemset"},
{0xDC692EE3,WrapI_UI<sceKernelTryLockLwMutex>, "sceKernelTryLockLwMutex"},
{0x37431849,WrapI_UI<sceKernelTryLockLwMutex_600>, "sceKernelTryLockLwMutex_600"},
{0xbea46419,WrapI_UIU<sceKernelLockLwMutex>, "sceKernelLockLwMutex"},
{0x1FC64E09,WrapI_UIU<sceKernelLockLwMutexCB>, "sceKernelLockLwMutexCB"},
{0x15b6446b,WrapI_UI<sceKernelUnlockLwMutex>, "sceKernelUnlockLwMutex"},
{0x293b45b8,sceKernelGetThreadId, "sceKernelGetThreadId"},
{0x1839852A,WrapU_UUU<sceKernelMemcpy>,"sce_paf_private_memcpy"},
};
void Register_Kernel_Library()
@ -454,7 +602,7 @@ void Register_Kernel_Library()
}
const HLEFunction InterruptManager[] =
const HLEFunction InterruptManager[] =
{
{0xCA04A2B9, WrapU_UUUU<sceKernelRegisterSubIntrHandler>, "sceKernelRegisterSubIntrHandler"},
{0xD61E6961, WrapU_UU<sceKernelReleaseSubIntrHandler>, "sceKernelReleaseSubIntrHandler"},
@ -463,7 +611,7 @@ const HLEFunction InterruptManager[] =
{0x5CB5A78B, 0, "sceKernelSuspendSubIntr"},
{0x7860E0DC, 0, "sceKernelResumeSubIntr"},
{0xFC4374B8, 0, "sceKernelIsSubInterruptOccurred"},
{0xD2E8363F, 0, "QueryIntrHandlerInfo"}, // No sce prefix for some reason
{0xD2E8363F, QueryIntrHandlerInfo, "QueryIntrHandlerInfo"}, // No sce prefix for some reason
{0xEEE43F47, 0, "sceKernelRegisterUserSpaceIntrStack"},
};

View File

@ -17,6 +17,8 @@
#pragma once
#include "../../Common/ChunkFile.h"
enum PSPInterrupt {
PSP_GPIO_INTR = 4,
PSP_ATA_INTR = 5,
@ -54,14 +56,70 @@ enum PSPGeSubInterrupts {
PSP_GE_SUBINTR_SIGNAL = 15
};
enum PSPInterruptTriggerType {
// Trigger immediately, for CoreTiming events.
PSP_INTR_IMMEDIATE = 0x0,
// Trigger after the HLE syscall finishes.
PSP_INTR_HLE = 0x1,
// Only trigger (as above) if interrupts are not suspended.
PSP_INTR_ONLY_IF_ENABLED = 0x2,
};
struct PendingInterrupt {
PendingInterrupt(int intr_, int subintr_)
: intr(intr_), subintr(subintr_), hasArg(false) {}
PendingInterrupt(int intr_, int subintr_, int arg_)
: intr(intr_), subintr(subintr_), hasArg(true), arg(arg_) {}
u32 intr;
u32 subintr;
bool hasArg;
int arg;
};
class SubIntrHandler
{
public:
SubIntrHandler() {}
virtual ~SubIntrHandler() {}
virtual void queueUp();
virtual void queueUpWithArg(int arg);
virtual void copyArgsToCPU(const PendingInterrupt &pend);
virtual void handleResult(int result) {}
virtual void DoState(PointerWrap &p)
{
p.Do(enabled);
p.Do(intrNumber);
p.Do(number);
p.Do(handlerAddress);
p.Do(handlerArg);
p.DoMarker("SubIntrHandler");
}
bool enabled;
int intrNumber;
int number;
u32 handlerAddress;
u32 handlerArg;
};
typedef SubIntrHandler *(*SubIntrCreator)();
bool __IsInInterrupt();
void __InterruptsInit();
void __InterruptsDoState(PointerWrap &p);
void __InterruptsDoStateLate(PointerWrap &p);
void __InterruptsShutdown();
void __TriggerInterrupt(PSPInterrupt intno, int subInterrupts = -1);
void __TriggerInterruptWithArg(PSPInterrupt intno, int subintr, int arg); // For GE "callbacks"
void __TriggerInterrupt(int type, PSPInterrupt intno, int subInterrupts = -1);
void __TriggerInterruptWithArg(int type, PSPInterrupt intno, int subintr, int arg); // For GE "callbacks"
bool __RunOnePendingInterrupt();
void __KernelReturnFromInterrupt();
void __RegisterSubIntrCreator(u32 intrNumber, SubIntrCreator creator);
SubIntrHandler *__RegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 &error);
u32 __ReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber);
u32 sceKernelRegisterSubIntrHandler(u32 intrNumber, u32 subIntrNumber, u32 handler, u32 handlerArg);
u32 sceKernelReleaseSubIntrHandler(u32 intrNumber, u32 subIntrNumber);
u32 sceKernelEnableSubIntr(u32 intrNumber, u32 subIntrNumber);

View File

@ -19,16 +19,23 @@
#include "sceKernelThread.h"
#include "sceKernelMbx.h"
#include "HLE.h"
#include "../../Core/CoreTiming.h"
#define SCE_KERNEL_MBA_THPRI 0x100
#define SCE_KERNEL_MBA_MSPRI 0x400
#define SCE_KERNEL_MBA_ATTR_KNOWN (SCE_KERNEL_MBA_THPRI | SCE_KERNEL_MBA_MSPRI)
// TODO: when a thread is being resumed (message received or cancellation), sceKernelReceiveMbx() always returns 0
const int PSP_MBX_ERROR_DUPLICATE_MSG = 0x800201C9;
typedef std::pair<SceUID, u32> MbxWaitingThread;
void __KernelMbxTimeout(u64 userdata, int cyclesLate);
static int mbxWaitTimer = 0;
struct NativeMbx
{
SceSize size;
char name[32];
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
SceUInt attr;
int numWaitThreads;
int numMessages;
@ -44,43 +51,265 @@ struct Mbx : public KernelObject
void AddWaitingThread(SceUID id, u32 addr)
{
bool inserted = false;
if (nmb.attr & SCE_KERNEL_MBA_THPRI)
{
for (std::vector<std::pair<SceUID, u32> >::iterator it = waitingThreads.begin(); it != waitingThreads.end(); it++)
for (std::vector<MbxWaitingThread>::iterator it = waitingThreads.begin(); it != waitingThreads.end(); it++)
{
if (__KernelGetThreadPrio(id) >= __KernelGetThreadPrio((*it).first))
if (__KernelGetThreadPrio(id) < __KernelGetThreadPrio((*it).first))
{
waitingThreads.insert(it, std::make_pair(id, addr));
inserted = true;
break;
}
}
}
if (!inserted)
waitingThreads.push_back(std::make_pair(id, addr));
}
inline void AddInitialMessage(u32 ptr)
{
nmb.numMessages++;
Memory::Write_U32(ptr, ptr);
nmb.packetListHead = ptr;
}
inline void AddFirstMessage(u32 endPtr, u32 ptr)
{
nmb.numMessages++;
Memory::Write_U32(nmb.packetListHead, ptr);
Memory::Write_U32(ptr, endPtr);
nmb.packetListHead = ptr;
}
inline void AddLastMessage(u32 endPtr, u32 ptr)
{
nmb.numMessages++;
Memory::Write_U32(ptr, endPtr);
Memory::Write_U32(nmb.packetListHead, ptr);
}
inline void AddMessage(u32 beforePtr, u32 afterPtr, u32 ptr)
{
nmb.numMessages++;
Memory::Write_U32(afterPtr, ptr);
Memory::Write_U32(ptr, beforePtr);
}
int ReceiveMessage(u32 receivePtr)
{
u32 ptr = nmb.packetListHead;
if (nmb.numMessages == 991)
{
u32 next = Memory::Read_U32(nmb.packetListHead);
u32 next2 = Memory::Read_U32(next);
if (next2 == ptr && next != ptr)
{
Memory::Write_U32(next, next);
nmb.packetListHead = next;
}
else
nmb.packetListHead = 0;
}
else
{
waitingThreads.push_back(std::make_pair(id, addr));
// Check over the linked list and reset the head.
int c = 0;
while (true)
{
u32 next = Memory::Read_U32(nmb.packetListHead);
if (!Memory::IsValidAddress(next))
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
if (next == ptr)
{
if (nmb.packetListHead != ptr)
{
next = Memory::Read_U32(next);
Memory::Write_U32(next, nmb.packetListHead);
nmb.packetListHead = next;
break;
}
else
{
if (c < nmb.numMessages - 1)
return PSP_MBX_ERROR_DUPLICATE_MSG;
nmb.packetListHead = 0;
break;
}
}
nmb.packetListHead = next;
c++;
}
}
// Tell the receiver about the message.
Memory::Write_U32(ptr, receivePtr);
nmb.numMessages--;
return 0;
}
virtual void DoState(PointerWrap &p)
{
p.Do(nmb);
MbxWaitingThread mwt(0,0);
p.Do(waitingThreads, mwt);
p.DoMarker("Mbx");
}
NativeMbx nmb;
std::vector<std::pair<SceUID, u32> > waitingThreads;
std::vector<u32> messageQueue;
std::vector<MbxWaitingThread> waitingThreads;
};
SceUID sceKernelCreateMbx(const char *name, int memoryPartition, SceUInt attr, int size, u32 optAddr)
void __KernelMbxInit()
{
DEBUG_LOG(HLE, "sceKernelCreateMbx(%s, %i, %08x, %i, %08x)", name, memoryPartition, attr, size, optAddr);
mbxWaitTimer = CoreTiming::RegisterEvent("MbxTimeout", __KernelMbxTimeout);
}
void __KernelMbxDoState(PointerWrap &p)
{
p.Do(mbxWaitTimer);
CoreTiming::RestoreRegisterEvent(mbxWaitTimer, "MbxTimeout", __KernelMbxTimeout);
p.DoMarker("sceKernelMbx");
}
KernelObject *__KernelMbxObject()
{
return new Mbx;
}
bool __KernelUnlockMbxForThread(Mbx *m, MbxWaitingThread &th, u32 &error, int result, bool &wokeThreads)
{
SceUID waitID = __KernelGetWaitID(th.first, WAITTYPE_MBX, error);
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(th.first, error);
// The waitID may be different after a timeout.
if (waitID != m->GetUID())
return true;
if (timeoutPtr != 0 && mbxWaitTimer != 0)
{
// Remove any event for this thread.
u64 cyclesLeft = CoreTiming::UnscheduleEvent(mbxWaitTimer, th.first);
Memory::Write_U32((u32) cyclesToUs(cyclesLeft), timeoutPtr);
}
__KernelResumeThreadFromWait(th.first, result);
wokeThreads = true;
return true;
}
void __KernelMbxTimeout(u64 userdata, int cyclesLate)
{
SceUID threadID = (SceUID)userdata;
u32 error;
u32 timeoutPtr = __KernelGetWaitTimeoutPtr(threadID, error);
if (timeoutPtr != 0)
Memory::Write_U32(0, timeoutPtr);
SceUID mbxID = __KernelGetWaitID(threadID, WAITTYPE_MBX, error);
Mbx *m = kernelObjects.Get<Mbx>(mbxID, error);
if (m)
{
// 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.
// TODO: Should numWaitThreads be decreased yet?
}
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
}
void __KernelWaitMbx(Mbx *m, u32 timeoutPtr)
{
if (timeoutPtr == 0 || mbxWaitTimer == 0)
return;
int micro = (int) Memory::Read_U32(timeoutPtr);
// This seems to match the actual timing.
if (micro <= 2)
micro = 10;
else if (micro <= 209)
micro = 250;
// This should call __KernelMbxTimeout() later, unless we cancel it.
CoreTiming::ScheduleEvent(usToCycles(micro), mbxWaitTimer, __KernelGetCurThread());
}
void __KernelMbxRemoveThread(Mbx *m, SceUID threadID)
{
for (size_t i = 0; i < m->waitingThreads.size(); i++)
{
MbxWaitingThread *t = &m->waitingThreads[i];
if (t->first == threadID)
{
m->waitingThreads.erase(m->waitingThreads.begin() + i);
break;
}
}
}
std::vector<MbxWaitingThread>::iterator __KernelMbxFindPriority(std::vector<MbxWaitingThread> &waiting)
{
_dbg_assert_msg_(HLE, !waiting.empty(), "__KernelMutexFindPriority: Trying to find best of no threads.");
std::vector<MbxWaitingThread>::iterator iter, end, best = waiting.end();
u32 best_prio = 0xFFFFFFFF;
for (iter = waiting.begin(), end = waiting.end(); iter != end; ++iter)
{
u32 iter_prio = __KernelGetThreadPrio(iter->first);
if (iter_prio < best_prio)
{
best = iter;
best_prio = iter_prio;
}
}
_dbg_assert_msg_(HLE, best != waiting.end(), "__KernelMutexFindPriority: Returning invalid best thread.");
return best;
}
SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr)
{
if (!name)
{
WARN_LOG(HLE, "%08x=%s(): invalid name", SCE_KERNEL_ERROR_ERROR, __FUNCTION__);
return SCE_KERNEL_ERROR_ERROR;
}
// Accepts 0x000 - 0x0FF, 0x100 - 0x1FF, and 0x400 - 0x4FF.
if (((attr & ~SCE_KERNEL_MBA_ATTR_KNOWN) & ~0xFF) != 0)
{
WARN_LOG(HLE, "%08x=%s(): invalid attr parameter: %08x", SCE_KERNEL_ERROR_ILLEGAL_ATTR, __FUNCTION__, attr);
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
}
Mbx *m = new Mbx();
SceUID id = kernelObjects.Create(m);
m->nmb.size = sizeof(NativeMbx);
strncpy(m->nmb.name, name, sizeof(m->nmb.name));
strncpy(m->nmb.name, name, KERNELOBJECT_MAX_NAME_LENGTH);
m->nmb.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
m->nmb.attr = attr;
m->nmb.numWaitThreads = 0;
m->nmb.numMessages = 0;
m->nmb.packetListHead = 0;
DEBUG_LOG(HLE, "%i=sceKernelCreateMbx(%s, %08x, %08x)", id, name, attr, optAddr);
if (optAddr != 0)
WARN_LOG(HLE, "%s(%s) unsupported options parameter: %08x", __FUNCTION__, name, optAddr);
if ((attr & ~SCE_KERNEL_MBA_ATTR_KNOWN) != 0)
WARN_LOG(HLE, "%s(%s) unsupported attr parameter: %08x", __FUNCTION__, name, attr);
return id;
}
@ -91,11 +320,14 @@ int sceKernelDeleteMbx(SceUID id)
if (m)
{
DEBUG_LOG(HLE, "sceKernelDeleteMbx(%i)", id);
bool wokeThreads = false;
for (size_t i = 0; i < m->waitingThreads.size(); i++)
{
Memory::Write_U32(0, m->waitingThreads[i].second);
__KernelResumeThreadFromWait(m->waitingThreads[i].first);
}
__KernelUnlockMbxForThread(m, m->waitingThreads[i], error, SCE_KERNEL_ERROR_WAIT_DELETE, wokeThreads);
m->waitingThreads.clear();
if (wokeThreads)
hleReSchedule("mbx deleted");
}
else
{
@ -104,63 +336,99 @@ int sceKernelDeleteMbx(SceUID id)
return kernelObjects.Destroy<Mbx>(id);
}
void sceKernelSendMbx(SceUID id, u32 packetAddr)
int sceKernelSendMbx(SceUID id, u32 packetAddr)
{
u32 error;
Mbx *m = kernelObjects.Get<Mbx>(id, error);
if (!m)
{
ERROR_LOG(HLE, "sceKernelSendMbx(%i, %08x): invalid mbx id", id, packetAddr);
return error;
}
NativeMbxPacket *addPacket = (NativeMbxPacket*)Memory::GetPointer(packetAddr);
if (addPacket == 0)
{
ERROR_LOG(HLE, "sceKernelSendMbx(%i, %08x): invalid packet address", id, packetAddr);
RETURN(-1);
return;
return -1;
}
if (!m)
// If the queue is empty, maybe someone is waiting.
// We have to check them first, they might've timed out.
if (m->nmb.numMessages == 0)
{
ERROR_LOG(HLE, "sceKernelSendMbx(%i, %08x): invalid mbx id", id, packetAddr);
RETURN(error);
return;
}
if (m->waitingThreads.empty())
{
DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): no threads currently waiting, adding message to queue", id, packetAddr);
if (m->nmb.attr & SCE_KERNEL_MBA_MSPRI)
bool wokeThreads = false;
std::vector<MbxWaitingThread>::iterator iter;
while (!wokeThreads && !m->waitingThreads.empty())
{
for (std::vector<u32>::iterator it = m->messageQueue.begin(); it != m->messageQueue.end(); it++)
if ((m->nmb.attr & SCE_KERNEL_MBA_THPRI) != 0)
iter = __KernelMbxFindPriority(m->waitingThreads);
else
iter = m->waitingThreads.begin();
MbxWaitingThread t = *iter;
__KernelUnlockMbxForThread(m, t, error, 0, wokeThreads);
m->waitingThreads.erase(iter);
if (wokeThreads)
{
NativeMbxPacket *p = (NativeMbxPacket*)Memory::GetPointer(*it);
if (addPacket->priority >= p->priority)
{
m->messageQueue.insert(it, packetAddr);
break;
}
DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): threads waiting, resuming %d", id, packetAddr, t.first);
Memory::Write_U32(packetAddr, t.second);
hleReSchedule("mbx sent");
// We don't need to do anything else, finish here.
return 0;
}
}
else
{
m->messageQueue.push_back(packetAddr);
}
RETURN(0);
}
else if (m->messageQueue.empty())
{
Memory::Write_U32(packetAddr, m->waitingThreads.front().second);
__KernelResumeThreadFromWait(m->waitingThreads.front().first);
DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): threads waiting, resuming %d", id, packetAddr, m->waitingThreads.front().first);
m->waitingThreads.erase(m->waitingThreads.begin());
RETURN(0);
__KernelReSchedule();
}
DEBUG_LOG(HLE, "sceKernelSendMbx(%i, %08x): no threads currently waiting, adding message to queue", id, packetAddr);
if (m->nmb.numMessages == 0)
m->AddInitialMessage(packetAddr);
else
{
ERROR_LOG(HLE, "sceKernelSendMbx(%i, %08x): WTF!? thread waiting while there is a message in the queue?", id, packetAddr);
RETURN(-1);
u32 next = m->nmb.packetListHead, prev;
for (int i = 0, n = m->nmb.numMessages; i < n; i++)
{
if (next == packetAddr)
return PSP_MBX_ERROR_DUPLICATE_MSG;
if (!Memory::IsValidAddress(next))
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
prev = next;
next = Memory::Read_U32(next);
}
bool inserted = false;
if (m->nmb.attr & SCE_KERNEL_MBA_MSPRI)
{
NativeMbxPacket p;
for (int i = 0, n = m->nmb.numMessages; i < n; i++)
{
Memory::ReadStruct<NativeMbxPacket>(next, &p);
if (addPacket->priority < p.priority)
{
if (i == 0)
m->AddFirstMessage(prev, packetAddr);
else
m->AddMessage(prev, next, packetAddr);
inserted = true;
break;
}
prev = next;
next = Memory::Read_U32(next);
}
}
if (!inserted)
m->AddLastMessage(prev, packetAddr);
}
return 0;
}
void sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr)
int sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr)
{
u32 error;
Mbx *m = kernelObjects.Get<Mbx>(id, error);
@ -168,52 +436,50 @@ void sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr)
if (!m)
{
ERROR_LOG(HLE, "sceKernelReceiveMbx(%i, %08x, %08x): invalid mbx id", id, packetAddrPtr, timeoutPtr);
RETURN(error);
return;
return error;
}
if (!m->messageQueue.empty())
if (m->nmb.numMessages > 0)
{
DEBUG_LOG(HLE, "sceKernelReceiveMbx(%i, %08x, %08x): sending first queue message", id, packetAddrPtr, timeoutPtr);
Memory::Write_U32(m->messageQueue.front(), packetAddrPtr);
m->messageQueue.erase(m->messageQueue.begin());
RETURN(0);
return m->ReceiveMessage(packetAddrPtr);
}
else
{
DEBUG_LOG(HLE, "sceKernelReceiveMbx(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr);
__KernelMbxRemoveThread(m, __KernelGetCurThread());
m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr);
RETURN(0);
__KernelWaitCurThread(WAITTYPE_MBX, 0, 0, 0, false); // ?
__KernelWaitMbx(m, timeoutPtr);
__KernelWaitCurThread(WAITTYPE_MBX, id, 0, timeoutPtr, false);
return 0;
}
}
void sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr)
int sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr)
{
u32 error;
Mbx *m = kernelObjects.Get<Mbx>(id, error);
__KernelCheckCallbacks();
if (!m)
{
ERROR_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): invalid mbx id", id, packetAddrPtr, timeoutPtr);
RETURN(error);
return;
return error;
}
if (!m->messageQueue.empty())
if (m->nmb.numMessages > 0)
{
DEBUG_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): sending first queue message", id, packetAddrPtr, timeoutPtr);
Memory::Write_U32(m->messageQueue.front(), packetAddrPtr);
m->messageQueue.erase(m->messageQueue.begin());
RETURN(0);
hleCheckCurrentCallbacks();
return m->ReceiveMessage(packetAddrPtr);
}
else
{
DEBUG_LOG(HLE, "sceKernelReceiveMbxCB(%i, %08x, %08x): no message in queue, waiting", id, packetAddrPtr, timeoutPtr);
m->AddWaitingThread(id, packetAddrPtr);
RETURN(0);
__KernelWaitCurThread(WAITTYPE_MBX, 0, 0, 0, true); // ?
__KernelMbxRemoveThread(m, __KernelGetCurThread());
m->AddWaitingThread(__KernelGetCurThread(), packetAddrPtr);
__KernelWaitMbx(m, timeoutPtr);
__KernelWaitCurThread(WAITTYPE_MBX, id, 0, timeoutPtr, true);
return 0;
}
}
@ -228,12 +494,10 @@ int sceKernelPollMbx(SceUID id, u32 packetAddrPtr)
return error;
}
if (!m->messageQueue.empty())
if (m->nmb.numMessages > 0)
{
DEBUG_LOG(HLE, "sceKernelPollMbx(%i, %08x): sending first queue message", id, packetAddrPtr);
Memory::Write_U32(m->messageQueue.front(), packetAddrPtr);
m->messageQueue.erase(m->messageQueue.begin());
return 0;
return m->ReceiveMessage(packetAddrPtr);
}
else
{
@ -255,13 +519,15 @@ int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr)
u32 count = m->waitingThreads.size();
DEBUG_LOG(HLE, "sceKernelCancelReceiveMbx(%i, %08x): cancelling %d threads", id, numWaitingThreadsAddr, count);
bool wokeThreads = false;
for (size_t i = 0; i < m->waitingThreads.size(); i++)
{
Memory::Write_U32(0, m->waitingThreads[i].second);
__KernelResumeThreadFromWait(m->waitingThreads[i].first);
}
__KernelUnlockMbxForThread(m, m->waitingThreads[i], error, SCE_KERNEL_ERROR_WAIT_CANCEL, wokeThreads);
m->waitingThreads.clear();
if (wokeThreads)
hleReSchedule("mbx canceled");
if (numWaitingThreadsAddr)
Memory::Write_U32(count, numWaitingThreadsAddr);
return 0;
@ -277,34 +543,19 @@ int sceKernelReferMbxStatus(SceUID id, u32 infoAddr)
return error;
}
SceKernelMbxInfo *info = (SceKernelMbxInfo*)Memory::GetPointer(infoAddr);
DEBUG_LOG(HLE, "sceKernelReferMbxStatus(%i, %08x)", id, infoAddr);
if (info)
{
strncpy(info->name, m->nmb.name, 32);
info->attr = m->nmb.attr;
info->numWaitThreads = m->waitingThreads.size();
info->numMessage = m->messageQueue.size();
// Fill the 'next' parameter of packets which we don't use by default but could be used by a game
if (m->messageQueue.size() != 0)
{
info->topPacketAddr = m->messageQueue[0];
for (u32 i = 0; i < m->messageQueue.size() - 1; i++)
{
Memory::Write_U32(m->messageQueue[i + 1], Memory::Read_U32(m->messageQueue[i]));
}
Memory::Write_U32(m->messageQueue[m->messageQueue.size() - 1], 0);
}
else
{
info->topPacketAddr = 0;
}
}
else
{
// Should we crash the thread somehow?
if (!Memory::IsValidAddress(infoAddr))
return -1;
for (int i = 0, n = m->nmb.numMessages; i < n; ++i)
m->nmb.packetListHead = Memory::Read_U32(m->nmb.packetListHead);
// For whatever reason, it won't write if the size (first member) is 0.
if (Memory::Read_U32(infoAddr) != 0)
{
m->nmb.numWaitThreads = m->waitingThreads.size();
Memory::WriteStruct<NativeMbx>(infoAddr, &m->nmb);
}
return 0;
}

View File

@ -24,22 +24,15 @@ struct NativeMbxPacket
u8 padding[3];
};
struct SceKernelMbxInfo
{
SceSize size;
char name[KERNELOBJECT_MAX_NAME_LENGTH+1];
SceUInt attr;
int numWaitThreads;
int numMessage;
u32 topPacketAddr;
};
SceUID sceKernelCreateMbx(const char *name, int memoryPartition, SceUInt attr, int size, u32 optAddr);
SceUID sceKernelCreateMbx(const char *name, u32 attr, u32 optAddr);
int sceKernelDeleteMbx(SceUID id);
void sceKernelSendMbx(SceUID id, u32 addPacketAddr);
void sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr);
void sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr);
int sceKernelSendMbx(SceUID id, u32 addPacketAddr);
int sceKernelReceiveMbx(SceUID id, u32 packetAddrPtr, u32 timeoutPtr);
int sceKernelReceiveMbxCB(SceUID id, u32 packetAddrPtr, u32 timeoutPtr);
int sceKernelPollMbx(SceUID id, u32 packetAddrPtr);
int sceKernelCancelReceiveMbx(SceUID id, u32 numWaitingThreadsAddr);
int sceKernelReferMbxStatus(SceUID id, u32 infoAddr);
void __KernelMbxInit();
void __KernelMbxDoState(PointerWrap &p);
KernelObject *__KernelMbxObject();

Some files were not shown because too many files have changed in this diff Show More