mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
Merge branch 'master' into armjit
Conflicts: CMakeLists.txt Core/MIPS/ARM/RegCache.cpp
This commit is contained in:
commit
7cb95bbc89
33
.gitignore
vendored
33
.gitignore
vendored
@ -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*/
|
||||
|
@ -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)
|
||||
|
@ -1 +0,0 @@
|
||||
#include "Action.h"
|
@ -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;
|
||||
};
|
@ -22,6 +22,9 @@
|
||||
|
||||
#include "Common.h"
|
||||
#include "MemoryUtil.h"
|
||||
#ifdef __SYMBIAN32__
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
namespace ArmGen
|
||||
{
|
||||
|
@ -1,5 +1,4 @@
|
||||
set(SRCS
|
||||
Action.cpp
|
||||
ColorUtil.cpp
|
||||
ConsoleListener.cpp
|
||||
ExtendedTrace.cpp
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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"
|
||||
|
@ -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] = "";
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#if defined(__APPLE__) || defined(__SYMBIAN32__)
|
||||
#define __thread
|
||||
#endif
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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_
|
||||
|
@ -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
|
||||
|
@ -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", ¤tDirectory, "");
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
80
Core/Dialog/PSPDialog.cpp
Normal 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
73
Core/Dialog/PSPDialog.h
Normal 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;
|
||||
};
|
249
Core/Dialog/PSPMsgDialog.cpp
Normal file
249
Core/Dialog/PSPMsgDialog.cpp
Normal 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");
|
||||
}
|
79
Core/Dialog/PSPMsgDialog.h
Normal file
79
Core/Dialog/PSPMsgDialog.h
Normal 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;
|
||||
};
|
||||
|
249
Core/Dialog/PSPOskDialog.cpp
Normal file
249
Core/Dialog/PSPOskDialog.cpp
Normal 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
164
Core/Dialog/PSPOskDialog.h
Normal 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;
|
||||
};
|
||||
|
50
Core/Dialog/PSPPlaceholderDialog.cpp
Normal file
50
Core/Dialog/PSPPlaceholderDialog.cpp
Normal 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;
|
||||
}
|
@ -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();
|
||||
};
|
||||
|
765
Core/Dialog/PSPSaveDialog.cpp
Normal file
765
Core/Dialog/PSPSaveDialog.cpp
Normal 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*)¶m.GetPspParam()->saveNameList));
|
||||
INFO_LOG(HLE,"fileName : %s",param.GetPspParam()->fileName);
|
||||
INFO_LOG(HLE,"dataBuf : %08x",*((unsigned int*)¶m.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*)¶m.GetPspParam()->icon0FileData.buf));
|
||||
INFO_LOG(HLE,"icon0 size : %u",param.GetPspParam()->icon0FileData.bufSize);
|
||||
|
||||
INFO_LOG(HLE,"icon1 data : %08x",*((unsigned int*)¶m.GetPspParam()->icon1FileData.buf));
|
||||
INFO_LOG(HLE,"icon1 size : %u",param.GetPspParam()->icon1FileData.bufSize);
|
||||
|
||||
INFO_LOG(HLE,"pic1 data : %08x",*((unsigned int*)¶m.GetPspParam()->pic1FileData.buf));
|
||||
INFO_LOG(HLE,"pic1 size : %u",param.GetPspParam()->pic1FileData.bufSize);
|
||||
|
||||
INFO_LOG(HLE,"snd0 data : %08x",*((unsigned int*)¶m.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
119
Core/Dialog/PSPSaveDialog.h
Normal 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;
|
||||
};
|
||||
|
841
Core/Dialog/SavedataParam.cpp
Normal file
841
Core/Dialog/SavedataParam.cpp
Normal 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
188
Core/Dialog/SavedataParam.h
Normal 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;
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -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);
|
@ -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)
|
||||
{
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
#include "../../Globals.h"
|
||||
|
||||
#define MISSING_KEY -10
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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;}
|
||||
};
|
||||
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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 ¤tDirectory, 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");
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
136
Core/HLE/HLE.cpp
136
Core/HLE/HLE.cpp
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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++)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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"},
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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()
|
||||
|
@ -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();
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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"},
|
||||
|
@ -1,3 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../Common/ChunkFile.h"
|
||||
|
||||
void Register_sceFont();
|
||||
|
||||
void __FontInit();
|
||||
void __FontDoState(PointerWrap &p);
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -17,4 +17,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../Common/ChunkFile.h"
|
||||
|
||||
void Register_sceImpose();
|
||||
void __ImposeInit();
|
||||
void __ImposeDoState(PointerWrap &p);
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
|
@ -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"},
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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"},
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user