Merge branch 'master' of github.com:hrydgard/ppsspp

This commit is contained in:
Daniel Dressler 2013-06-13 20:31:09 -07:00
commit cd69ec3acb
261 changed files with 61247 additions and 46033 deletions

7
.gitignore vendored
View File

@ -18,7 +18,6 @@
*.aps
*.exp
Debug
DebugFast
Release
Windows/x64
Windows/ipch
@ -35,6 +34,8 @@ PPSSPPControls.dat
Logs
Memstick
memstick
Cheats
bin
gen
@ -55,3 +56,7 @@ android/assets/lang
android/assets/flash0
ppge_atlas.zim.png
local.properties
# For vim
*.swp
tags

View File

@ -129,9 +129,15 @@ if(NOT MSVC)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
add_definitions(-Wno-multichar)
add_definitions(-fno-strict-aliasing)
add_definitions(-ffast-math)
if (CMAKE_C_COMPILER_ID STREQUAL "Intel")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -parallel -fopenmp")
else()
add_definitions(-ffast-math)
endif()
if(NOT APPLE)
add_definitions(-Wno-psabi)
if (NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
add_definitions(-Wno-psabi)
endif()
add_definitions(-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED -D__BSD_VISIBLE=1)
add_definitions(-D_LARGEFILE64_SOURCE=1 -D_FILE_OFFSET_BITS=64)
endif()
@ -140,7 +146,7 @@ if(NOT MSVC)
endif()
if(BLACKBERRY)
add_definitions(-D_QNX_SOURCE=1 -D_C99=1 -O2 -mfpu=neon -mtune=cortex-a9)
add_definitions(-D_QNX_SOURCE=1 -D_C99=1 -O3 -mfpu=neon -mtune=cortex-a9)
endif()
if(X86 AND NOT MIPS)
@ -437,8 +443,16 @@ elseif(IOS)
ios/ViewController.mm
ios/ViewController.h
ios/AudioEngine.mm
ios/AudioEngine.h)
ios/AudioEngine.h
ios/iCade/iCadeReaderView.h
ios/iCade/iCadeReaderView.m
ios/iCade/iCadeState.h)
set(nativeExtraLibs ${nativeExtraLibs} "-framework Foundation -framework AudioToolbox -framework CoreGraphics -framework QuartzCore -framework OpenGLES -framework UIKit -framework GLKit -framework OpenAL")
# FFMPEG
add_definitions(-DUSE_FFMPEG)
include_directories(ffmpeg/ios/universal/include)
link_directories(ffmpeg/ios/universal/lib)
set(nativeExtraLibs ${nativeExtraLibs} iconv bz2 libavformat.a libavcodec.a libswresample.a libavutil.a libswscale.a)
set(TargetBin PPSSPP)
elseif(USING_QT_UI)
# Currently unused
@ -449,13 +463,13 @@ elseif(USING_QT_UI)
set(nativeExtraLibs ${nativeExtraLibs} ${QT_LIBRARIES})
set(TargetBin PPSSPPQt)
elseif(BLACKBERRY)
set(nativeExtra ${nativeExtra} native/base/BlackberryMain.cpp)
set(nativeExtra ${nativeExtra} native/base/BlackberryMain.cpp native/base/BlackberryDisplay.cpp)
set(nativeExtraLibs ${nativeExtraLibs} OpenAL bps screen socket EGL)
# FFMPEG
add_definitions(-DUSE_FFMPEG)
include_directories(ffmpeg/blackberry/armv7/include)
link_directories(ffmpeg/blackberry/armv7/lib)
set(nativeExtraLibs ${nativeExtraLibs} iconv libavformat.a libavcodec.a libswresample.a libavutil.a)
set(nativeExtraLibs ${nativeExtraLibs} iconv libavformat.a libavcodec.a libswresample.a libavutil.a libswscale.a)
set(TargetBin PPSSPPBlackberry)
elseif(SDL_FOUND)
# Require SDL
@ -468,6 +482,9 @@ elseif(SDL_FOUND)
elseif(PANDORA OR MAEMO)
set(nativeExtraLibs ${nativeExtraLibs} pthread EGL X11)
endif()
# FFMPEG
add_definitions(-DUSE_FFMPEG)
set(nativeExtraLibs ${nativeExtraLibs} avformat avcodec swresample swscale)
set(TargetBin PPSSPPSDL)
else()
message(FATAL_ERROR "Could not find SDL. Failing.")
@ -497,6 +514,7 @@ add_library(native STATIC
native/base/error_context.h
native/base/fastlist.h
native/base/fastlist_test.cpp
native/base/functional.h
native/base/linked_ptr.h
native/base/logging.h
native/base/mutex.h
@ -506,6 +524,8 @@ add_library(native STATIC
native/base/stringutil.h
native/base/timeutil.cpp
native/base/timeutil.h
native/data/compression.cpp
native/data/compression.h
native/file/chunk_file.cpp
native/file/chunk_file.h
native/file/dialog.cpp
@ -555,7 +575,6 @@ add_library(native STATIC
native/input/input_state.h
native/json/json_writer.cpp
native/json/json_writer.h
native/math/compression.h
native/math/curves.cpp
native/math/curves.h
native/math/lin/aabb.cpp
@ -577,6 +596,8 @@ add_library(native STATIC
native/net/http_client.h
native/net/resolve.cpp
native/net/resolve.h
native/net/url.cpp
native/net/url.h
native/profiler/profiler.cpp
native/profiler/profiler.h
native/thread/prioritizedworkqueue.cpp
@ -591,6 +612,12 @@ add_library(native STATIC
native/ui/ui.h
native/ui/ui_context.cpp
native/ui/ui_context.h
native/ui/ui_screen.cpp
native/ui/ui_screen.h
native/ui/view.cpp
native/ui/view.h
native/ui/viewgroup.cpp
native/ui/viewgroup.h
native/ui/virtual_input.cpp
native/ui/virtual_input.h
native/util/bits/bits.cpp
@ -608,7 +635,11 @@ add_library(native STATIC
native/ext/rapidxml/rapidxml.hpp
native/ext/rapidxml/rapidxml_iterators.hpp
native/ext/rapidxml/rapidxml_print.hpp
native/ext/rapidxml/rapidxml_utils.hpp)
native/ext/rapidxml/rapidxml_utils.hpp
native/ext/vjson/json.cpp
native/ext/vjson/json.h
native/ext/vjson/block_allocator.cpp
native/ext/vjson/block_allocator.h)
include_directories(native)
# rapidxml is headers only so we can't make a lib specific for it
include_directories(native/ext/rapidxml)
@ -643,6 +674,7 @@ add_library(xbrz STATIC
include_directories(ext/xbrz)
set(CoreExtra)
set(CoreExtraLibs)
if(ARM)
set(CoreExtra ${CoreExtra}
Core/MIPS/ARM/ArmAsm.cpp
@ -676,6 +708,12 @@ elseif(X86)
Core/MIPS/x86/RegCacheFPU.h)
endif()
# atrac3plus.cpp used dl* functions on non-win32 platforms
# Core needs to be linked with dl on these platforms
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(CoreExtraLibs ${CoreExtraLibs} ${CMAKE_DL_LIBS})
endif()
# 'ppsspp_jni' on ANDROID, 'Core' everywhere else
# SHARED on ANDROID, STATIC everywhere else
add_library(${CoreLibName} ${CoreLinkType}
@ -689,6 +727,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/CoreParameter.h
Core/CoreTiming.cpp
Core/CoreTiming.h
Core/CwCheat.cpp
Core/CwCheat.h
Core/Debugger/Breakpoints.cpp
Core/Debugger/Breakpoints.h
Core/Debugger/DebugInterface.h
@ -737,6 +777,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/HLE/sceAtrac.cpp
Core/HLE/sceAtrac.h
Core/HLE/sceAudio.cpp
Core/HLE/sceAudiocodec.cpp
Core/HLE/sceAudiocodec.h
Core/HLE/sceAudio.h
Core/HLE/sceChnnlsv.cpp
Core/HLE/sceChnnlsv.h
@ -832,10 +874,16 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/HLE/sceNp.h
Core/HLE/scePauth.cpp
Core/HLE/scePauth.h
Core/HW/atrac3plus.cpp
Core/HW/atrac3plus.h
Core/HW/MediaEngine.cpp
Core/HW/MediaEngine.h
Core/HW/MpegDemux.cpp
Core/HW/MpegDemux.h
Core/HW/MemoryStick.cpp
Core/HW/MemoryStick.h
Core/HW/OMAConvert.cpp
Core/HW/OMAConvert.h
Core/HW/SasAudio.cpp
Core/HW/SasAudio.h
Core/Host.cpp
@ -889,7 +937,7 @@ add_library(${CoreLibName} ${CoreLinkType}
Globals.h
git-version.cpp)
target_link_libraries(${CoreLibName} Common native kirk cityhash xbrz
${GLEW_LIBRARIES} ${OPENGL_LIBRARIES})
${CoreExtraLibs} ${GLEW_LIBRARIES} ${OPENGL_LIBRARIES})
setup_target_project(${CoreLibName} Core)
# Generate git-version.cpp at build time.
@ -1026,9 +1074,12 @@ set(NativeAppSource
android/jni/TestRunner.cpp
UI/GameInfoCache.cpp
UI/MenuScreens.cpp
UI/GameScreen.cpp
UI/GameSettingsScreen.cpp
UI/GamepadEmu.cpp
UI/UIShader.cpp
UI/OnScreenDisplay.cpp
UI/PluginScreen.cpp
UI/ui_atlas.cpp)
if(ANDROID)
set(NativeAppSource ${NativeAppSource} android/jni/ArmEmitterTest.cpp)

View File

@ -117,14 +117,33 @@ bool ARMXEmitter::TrySetValue_TwoOp(ARMReg reg, u32 val)
return true;
}
void ARMXEmitter::MOVI2F(ARMReg dest, float val, ARMReg tempReg)
void ARMXEmitter::MOVI2F(ARMReg dest, float val, ARMReg tempReg, bool negate)
{
union {float f; u32 u;} conv;
conv.f = val;
conv.f = negate ? -val : val;
// Try moving directly first if mantisse is empty
if (cpu_info.bVFPv3 && ((conv.u & 0x7FFFF) == 0))
{
// VFP Encoding for Imms: <7> Not(<6>) Repeat(<6>,5) <5:0> Zeros(19)
bool bit6 = (conv.u & 0x40000000) == 0x40000000;
bool canEncode = true;
for (u32 mask = 0x20000000; mask >= 0x2000000; mask >>= 1)
{
if (((conv.u & mask) == mask) == bit6)
canEncode = false;
}
if (canEncode)
{
u32 imm8 = (conv.u & 0x80000000) >> 24; // sign bit
imm8 |= (!bit6 << 6);
imm8 |= (conv.u & 0x1F80000) >> 19;
VMOV(dest, IMM(imm8));
return;
}
}
MOVI2R(tempReg, conv.u);
VMOV(dest, tempReg);
// TODO: VMOV an IMM directly if possible
// Otherwise, use a literal pool and VLDR directly (+- 1020)
// Otherwise, possible to use a literal pool and VLDR directly (+- 1020)
}
void ARMXEmitter::ADDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch)
@ -648,11 +667,11 @@ void ARMXEmitter::RBIT(ARMReg dest, ARMReg src)
}
void ARMXEmitter::REV (ARMReg dest, ARMReg src)
{
Write32(condition | (0x6B << 20) | (0xF << 16) | (dest << 12) | (0xF3 << 4) | src);
Write32(condition | (0x6BF << 16) | (dest << 12) | (0xF3 << 4) | src);
}
void ARMXEmitter::REV16(ARMReg dest, ARMReg src)
{
Write32(condition | (0x3DF << 16) | (dest << 12) | (0xFD << 4) | src);
Write32(condition | (0x6BF << 16) | (dest << 12) | (0xFB << 4) | src);
}
void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, Operand2 op2)
@ -850,6 +869,21 @@ ARMReg ARMXEmitter::SubBase(ARMReg Reg)
}
// NEON Specific
void ARMXEmitter::VABD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
{
_assert_msg_(DYNA_REC, Vd >= D0, "Pass invalid register to VABD(float)");
_assert_msg_(DYNA_REC, cpu_info.bNEON, "Can't use VABD(float) when CPU doesn't support it");
bool register_quad = Vd >= Q0;
// Gets encoded as a double register
Vd = SubBase(Vd);
Vn = SubBase(Vn);
Vm = SubBase(Vm);
Write32((0xF3 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \
| ((Vd & 0xF) << 12) | (0xD << 8) | ((Vn & 0x10) << 3) | (register_quad << 6) \
| ((Vm & 0x10) << 2) | (Vm & 0xF));
}
void ARMXEmitter::VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
{
_assert_msg_(DYNA_REC, Vd >= D0, "Pass invalid register to VADD(integer)");
@ -864,7 +898,7 @@ void ARMXEmitter::VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
Write32((0xF2 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \
| ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (register_quad << 6) \
| ((Vm & 0x10) << 2) | (Vm & 0xF));
| ((Vm & 0x10) << 2) | (Vm & 0xF));
}
void ARMXEmitter::VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
@ -879,7 +913,7 @@ void ARMXEmitter::VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm)
Write32((0xF3 << 24) | ((Vd & 0x10) << 18) | (Size << 20) | ((Vn & 0xF) << 16) \
| ((Vd & 0xF) << 12) | (0x8 << 8) | ((Vn & 0x10) << 3) | (1 << 6) \
| ((Vm & 0x10) << 2) | (Vm & 0xF));
| ((Vm & 0x10) << 2) | (Vm & 0xF));
}
// VFP Specific
@ -891,24 +925,30 @@ struct VFPEnc
// Double/single, Neon
const VFPEnc VFPOps[][2] = {
{{0xE0, 0xA0}, {0x20, 0xD1}}, // 0: VMLA
{{0xE0, 0xA4}, {0x22, 0xD1}}, // 1: VMLS
{{0xE3, 0xA0}, {0x20, 0xD0}}, // 2: VADD
{{0xE3, 0xA4}, {0x22, 0xD0}}, // 3: VSUB
{{0xE2, 0xA0}, {0x30, 0xD1}}, // 4: VMUL
{{0xEB, 0xAC}, { -1 /* 0x3B */, -1 /* 0x70 */}}, // 5: VABS(Vn(0x0) used for encoding)
{{0xE8, 0xA0}, { -1, -1}}, // 6: VDIV
{{0xEB, 0xA4}, { -1 /* 0x3B */, -1 /* 0x78 */}}, // 7: VNEG(Vn(0x1) used for encoding)
{{0xEB, 0xAC}, { -1, -1}}, // 8: VSQRT (Vn(0x1) used for encoding)
{{0xEB, 0xA4}, { -1, -1}}, // 9: VCMP (Vn(0x4 | #0 ? 1 : 0) used for encoding)
{{0xEB, 0xAC}, { -1, -1}}, // 10: VCMPE (Vn(0x4 | #0 ? 1 : 0) used for encoding)
{{ -1, -1}, {0x3B, 0x30}}, // 11: VABSi
{{0xE1, 0xA4}, { -1, -1}}, // 1: VNMLA
{{0xE0, 0xA4}, {0x22, 0xD1}}, // 2: VMLS
{{0xE1, 0xA0}, { -1, -1}}, // 3: VNMLS
{{0xE3, 0xA0}, {0x20, 0xD0}}, // 4: VADD
{{0xE3, 0xA4}, {0x22, 0xD0}}, // 5: VSUB
{{0xE2, 0xA0}, {0x30, 0xD1}}, // 6: VMUL
{{0xE2, 0xA4}, { -1, -1}}, // 7: VNMUL
{{0xEB, 0xAC}, { -1 /* 0x3B */, -1 /* 0x70 */}}, // 8: VABS(Vn(0x0) used for encoding)
{{0xE8, 0xA0}, { -1, -1}}, // 9: VDIV
{{0xEB, 0xA4}, { -1 /* 0x3B */, -1 /* 0x78 */}}, // 10: VNEG(Vn(0x1) used for encoding)
{{0xEB, 0xAC}, { -1, -1}}, // 11: VSQRT (Vn(0x1) used for encoding)
{{0xEB, 0xA4}, { -1, -1}}, // 12: VCMP (Vn(0x4 | #0 ? 1 : 0) used for encoding)
{{0xEB, 0xAC}, { -1, -1}}, // 13: VCMPE (Vn(0x4 | #0 ? 1 : 0) used for encoding)
{{ -1, -1}, {0x3B, 0x30}}, // 14: VABSi
};
const char *VFPOpNames[] = {
"VMLA",
"VNMLA",
"VMLS",
"VNMLS",
"VADD",
"VSUB",
"VMUL",
"VNMUL",
"VABS",
"VDIV",
"VNEG",
@ -962,6 +1002,7 @@ u32 ARMXEmitter::EncodeVm(ARMReg Vm)
else
return ((Reg & 0x1) << 5) | (Reg >> 1);
}
void ARMXEmitter::WriteVFPDataOp(u32 Op, ARMReg Vd, ARMReg Vn, ARMReg Vm)
{
bool quad_reg = Vd >= Q0;
@ -978,18 +1019,21 @@ void ARMXEmitter::WriteVFPDataOp(u32 Op, ARMReg Vd, ARMReg Vn, ARMReg Vm)
Write32(cond | (enc.opc1 << 20) | VnEnc | VdEnc | (enc.opc2 << 4) | (quad_reg << 6) | (double_reg << 8) | VmEnc);
}
void ARMXEmitter::VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(0, Vd, Vn, Vm); }
void ARMXEmitter::VMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(1, Vd, Vn, Vm); }
void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(2, Vd, Vn, Vm); }
void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(3, Vd, Vn, Vm); }
void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(4, Vd, Vn, Vm); }
void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(5, Vd, D0, Vm); }
void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(6, Vd, Vn, Vm); }
void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(7, Vd, D1, Vm); }
void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(8, Vd, D1, Vm); }
void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(9, Vd, D4, Vm); }
void ARMXEmitter::VCMPE(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(10, Vd, D4, Vm); }
void ARMXEmitter::VCMP(ARMReg Vd){ WriteVFPDataOp(9, Vd, D5, D0); }
void ARMXEmitter::VCMPE(ARMReg Vd){ WriteVFPDataOp(10, Vd, D5, D0); }
void ARMXEmitter::VNMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(1, Vd, Vn, Vm); }
void ARMXEmitter::VMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(2, Vd, Vn, Vm); }
void ARMXEmitter::VNMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(3, Vd, Vn, Vm); }
void ARMXEmitter::VADD(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(4, Vd, Vn, Vm); }
void ARMXEmitter::VSUB(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(5, Vd, Vn, Vm); }
void ARMXEmitter::VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(6, Vd, Vn, Vm); }
void ARMXEmitter::VNMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(7, Vd, Vn, Vm); }
void ARMXEmitter::VABS(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(8, Vd, D0, Vm); }
void ARMXEmitter::VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm){ WriteVFPDataOp(9, Vd, Vn, Vm); }
void ARMXEmitter::VNEG(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(10, Vd, D1, Vm); }
void ARMXEmitter::VSQRT(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(11, Vd, D1, Vm); }
void ARMXEmitter::VCMP(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(12, Vd, D4, Vm); }
void ARMXEmitter::VCMPE(ARMReg Vd, ARMReg Vm){ WriteVFPDataOp(13, Vd, D4, Vm); }
void ARMXEmitter::VCMP(ARMReg Vd){ WriteVFPDataOp(12, Vd, D5, D0); }
void ARMXEmitter::VCMPE(ARMReg Vd){ WriteVFPDataOp(13, Vd, D5, D0); }
void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, s16 offset)
{
@ -1059,6 +1103,11 @@ void ARMXEmitter::VMSR(ARMReg Rt) {
}
// VFP and ASIMD
void ARMXEmitter::VMOV(ARMReg Dest, Operand2 op2)
{
_assert_msg_(DYNA_REC, cpu_info.bVFPv3, "VMOV #imm requires VFPv3");
Write32(condition | (0xEB << 20) | EncodeVd(Dest) | (0xA << 8) | op2.Imm8VFP());
}
void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high)
{
_assert_msg_(DYNA_REC, Src < S0, "This VMOV doesn't support SRC other than ARM Reg");
@ -1067,7 +1116,7 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src, bool high)
Dest = SubBase(Dest);
Write32(condition | (0xE << 24) | (high << 21) | ((Dest & 0xF) << 16) | (Src << 12) \
| (11 << 8) | ((Dest & 0x10) << 3) | (1 << 4));
| (0xB << 8) | ((Dest & 0x10) << 3) | (1 << 4));
}
void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src)

View File

@ -530,6 +530,7 @@ public:
// Subtracts the base from the register to give us the real one
ARMReg SubBase(ARMReg Reg);
// NEON Only
void VABD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm);
void VADD(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm);
void VSUB(IntegerSize Size, ARMReg Vd, ARMReg Vn, ARMReg Vm);
@ -541,6 +542,10 @@ public:
// Compares against zero
void VCMP(ARMReg Vd);
void VCMPE(ARMReg Vd);
void VNMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm);
void VNMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm);
void VNMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm);
void VDIV(ARMReg Vd, ARMReg Vn, ARMReg Vm);
void VSQRT(ARMReg Vd, ARMReg Vm);
@ -552,6 +557,7 @@ public:
void VMUL(ARMReg Vd, ARMReg Vn, ARMReg Vm);
void VMLA(ARMReg Vd, ARMReg Vn, ARMReg Vm);
void VMLS(ARMReg Vd, ARMReg Vn, ARMReg Vm);
void VMOV(ARMReg Dest, Operand2 op2);
void VMOV(ARMReg Dest, ARMReg Src, bool high);
void VMOV(ARMReg Dest, ARMReg Src);
void VCVT(ARMReg Dest, ARMReg Src, int flags);
@ -564,7 +570,7 @@ public:
// Wrapper around MOVT/MOVW with fallbacks.
void MOVI2R(ARMReg reg, u32 val, bool optimize = true);
void MOVI2F(ARMReg dest, float val, ARMReg tempReg);
void MOVI2F(ARMReg dest, float val, ARMReg tempReg, bool negate = false);
void ADDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch);
void ANDI2R(ARMReg rd, ARMReg rs, u32 val, ARMReg scratch);

View File

@ -72,6 +72,9 @@
<AdditionalIncludeDirectories>../native</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -93,6 +96,9 @@
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -116,6 +122,8 @@
<FloatingPointModel>Fast</FloatingPointModel>
<AdditionalIncludeDirectories>../native</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -142,6 +150,8 @@
<FloatingPointModel>Fast</FloatingPointModel>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<OmitFramePointers>false</OmitFramePointers>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

View File

@ -37,6 +37,15 @@ const int LOG_PENDING_MAX = 120 * 10000;
const int LOG_LATENCY_DELAY_MS = 20;
const int LOG_SHUTDOWN_DELAY_MS = 250;
const int LOG_MAX_DISPLAY_LINES = 4000;
int ConsoleListener::refCount = 0;
HANDLE ConsoleListener::hThread = NULL;
HANDLE ConsoleListener::hTriggerEvent = NULL;
CRITICAL_SECTION ConsoleListener::criticalSection;
char *ConsoleListener::logPending = NULL;
volatile u32 ConsoleListener::logPendingReadPos = 0;
volatile u32 ConsoleListener::logPendingWritePos = 0;
#endif
ConsoleListener::ConsoleListener()
@ -45,12 +54,12 @@ ConsoleListener::ConsoleListener()
hConsole = NULL;
bUseColor = true;
hThread = NULL;
hTriggerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
InitializeCriticalSection(&criticalSection);
logPending = NULL;
logPendingReadPos = 0;
logPendingWritePos = 0;
if (hTriggerEvent == NULL)
{
hTriggerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
InitializeCriticalSection(&criticalSection);
}
++refCount;
#else
bUseColor = isatty(fileno(stdout));
#endif
@ -59,13 +68,6 @@ ConsoleListener::ConsoleListener()
ConsoleListener::~ConsoleListener()
{
Close();
#ifdef _WIN32
DeleteCriticalSection(&criticalSection);
if (logPending != NULL)
delete [] logPending;
#endif
}
// 100, 100, "Dolphin Log Console"
@ -96,7 +98,7 @@ void ConsoleListener::Open(bool Hidden, int Width, int Height, const char *Title
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
}
if (hTriggerEvent != NULL)
if (hTriggerEvent != NULL && hThread == NULL)
{
logPending = new char[LOG_PENDING_MAX];
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &ConsoleListener::RunThread, this, 0, NULL);
@ -135,17 +137,30 @@ void ConsoleListener::Close()
if (hConsole == NULL)
return;
if (hThread != NULL)
if (--refCount <= 0)
{
Common::AtomicStoreRelease(logPendingWritePos, (u32) -1);
if (hThread != NULL)
{
Common::AtomicStoreRelease(logPendingWritePos, (u32) -1);
SetEvent(hTriggerEvent);
WaitForSingleObject(hThread, LOG_SHUTDOWN_DELAY_MS);
CloseHandle(hThread);
hThread = NULL;
SetEvent(hTriggerEvent);
WaitForSingleObject(hThread, LOG_SHUTDOWN_DELAY_MS);
CloseHandle(hThread);
hThread = NULL;
}
if (hTriggerEvent != NULL)
{
DeleteCriticalSection(&criticalSection);
CloseHandle(hTriggerEvent);
hTriggerEvent = NULL;
}
if (logPending != NULL)
{
delete [] logPending;
logPending = NULL;
}
refCount = 0;
}
if (hTriggerEvent != NULL)
CloseHandle(hTriggerEvent);
FreeConsole();
hConsole = NULL;

View File

@ -55,13 +55,14 @@ private:
void SendToThread(LogTypes::LOG_LEVELS Level, const char *Text);
void WriteToConsole(LogTypes::LOG_LEVELS Level, const char *Text, size_t Len);
HANDLE hThread;
HANDLE hTriggerEvent;
CRITICAL_SECTION criticalSection;
static int refCount;
static HANDLE hThread;
static HANDLE hTriggerEvent;
static CRITICAL_SECTION criticalSection;
char *logPending;
volatile u32 logPendingReadPos;
volatile u32 logPendingWritePos;
static char *logPending;
static volatile u32 logPendingReadPos;
static volatile u32 logPendingWritePos;
#endif
bool bHidden;
bool bUseColor;

View File

@ -28,7 +28,6 @@
#ifdef __APPLE__
#include <strings.h>
#endif
#include <string>
#include "FileSearch.h"

View File

@ -15,9 +15,6 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <map>
#include <string>
#include "input/input_state.h"
#include "Core/Config.h"
#include "KeyMap.h"
@ -30,7 +27,7 @@ std::map<int,int> *platform_keymap = NULL;
// Default key mapping
// Ugly, yet the cleanest way
// I could find to create a
// I could find to create a
// static map.
// Still nicer than what
// I once did in C.
@ -299,7 +296,7 @@ std::string NamePspButtonFromKey(KeyMap::Key key)
std::string NameKeyFromPspButton(int btn)
{
// We drive our iteration
// We drive our iteration
// with the list of key names.
for (int i = 0; i < key_names_count; i++) {
const struct KeyMap_IntStrPair key_name = key_names[i];
@ -319,6 +316,7 @@ int SetKeyMapping(KeyMap::Key key, int btn)
return KEYMAP_ERROR_KEY_ALREADY_USED;
g_Config.iMappingMap[key] = btn;
return btn;
}
int RegisterPlatformDefaultKeyMap(std::map<int,int> *overriding_map)

View File

@ -15,7 +15,6 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Common.h"
#include "MemoryUtil.h"
#include "MemArena.h"

View File

@ -22,17 +22,24 @@
#include "CommonPaths.h"
#include "StringUtils.h"
// faster than sscanf
bool AsciiToHex(const char* _szValue, u32& result)
{
char *endptr = NULL;
const u32 value = strtoul(_szValue, &endptr, 16);
long parseHexLong(std::string s) {
long value = 0;
if (!endptr || *endptr)
return false;
result = value;
return true;
if (s.substr(0,2) == "0x") {
//s = s.substr(2);
}
value = strtoul(s.c_str(),0, 0);
return value;
}
long parseLong(std::string s) {
long value = 0;
if (s.substr(0,2) == "0x") {
s = s.substr(2);
value = strtol(s.c_str(),NULL, 16);
} else {
value = strtol(s.c_str(),NULL, 10);
}
return value;
}
bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args)
@ -93,160 +100,4 @@ void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _P
// add the filename
_CompleteFilename += _Filename;
}
std::string TabsToSpaces(int tab_size, const std::string &in)
{
const std::string spaces(tab_size, ' ');
std::string out(in);
size_t i = 0;
while (out.npos != (i = out.find('\t')))
out.replace(i, 1, spaces);
return out;
}
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest)
{
while(1)
{
const size_t pos = result.find(src);
if (pos == result.npos) break;
result.replace(pos, src.size(), dest);
}
return result;
}
// UriDecode and UriEncode are from http://www.codeguru.com/cpp/cpp/string/conversions/print.php/c12759
// by jinq0123 (November 2, 2006)
// Uri encode and decode.
// RFC1630, RFC1738, RFC2396
// 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 */ 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 */ 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 */ 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 */ 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)
{
// Note from RFC1630: "Sequences which start with a percent sign
// but are not followed by two hexadecimal characters (0-9, A-F) are reserved
// for future extension"
const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
const size_t SRC_LEN = sSrc.length();
const unsigned char * const SRC_END = pSrc + SRC_LEN;
const unsigned char * const SRC_LAST_DEC = SRC_END - 2; // last decodable '%'
char * const pStart = new char[SRC_LEN];
char * pEnd = pStart;
while (pSrc < SRC_LAST_DEC)
{
if (*pSrc == '%')
{
char dec1, dec2;
if (-1 != (dec1 = HEX2DEC[*(pSrc + 1)])
&& -1 != (dec2 = HEX2DEC[*(pSrc + 2)]))
{
*pEnd++ = (dec1 << 4) + dec2;
pSrc += 3;
continue;
}
}
*pEnd++ = *pSrc++;
}
// the last 2- chars
while (pSrc < SRC_END)
*pEnd++ = *pSrc++;
std::string sResult(pStart, pEnd);
delete [] pStart;
return sResult;
}
// Only alphanum is safe.
const char SAFE[256] =
{
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* 1 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* 2 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* 3 */ 1,1,1,1, 1,1,1,1, 1,1,0,0, 0,0,0,0,
/* 4 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* 5 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
/* 6 */ 0,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1,
/* 7 */ 1,1,1,1, 1,1,1,1, 1,1,1,0, 0,0,0,0,
/* 8 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* 9 */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* A */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* B */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* C */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* D */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* E */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
/* F */ 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
};
std::string UriEncode(const std::string & sSrc)
{
const char DEC2HEX[16 + 1] = "0123456789ABCDEF";
const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
const size_t SRC_LEN = sSrc.length();
unsigned char * const pStart = new unsigned char[SRC_LEN * 3];
unsigned char * pEnd = pStart;
const unsigned char * const SRC_END = pSrc + SRC_LEN;
for (; pSrc < SRC_END; ++pSrc)
{
if (SAFE[*pSrc])
*pEnd++ = *pSrc;
else
{
// escape this char
*pEnd++ = '%';
*pEnd++ = DEC2HEX[*pSrc >> 4];
*pEnd++ = DEC2HEX[*pSrc & 0x0F];
}
}
std::string sResult((char *)pStart, (char *)pEnd);
delete [] pStart;
return sResult;
}
bool StringEndsWith(std::string const &fullString, std::string const &ending) {
if (fullString.length() >= ending.length()) {
return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
} else {
return false;
}
}
}

View File

@ -22,7 +22,8 @@
#include <base/stringutil.h>
#include "Common.h"
long parseHexLong(std::string s);
long parseLong(std::string s);
std::string StringFromFormat(const char* format, ...);
// Cheap!
bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args);
@ -51,19 +52,11 @@ std::string ThousandSeparate(I value, int spaces = 0)
return oss.str();
}
// TODO: kill this
bool AsciiToHex(const char* _szValue, u32& result);
std::string TabsToSpaces(int tab_size, const std::string &in);
// "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe"
bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension);
void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename);
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest);
std::string UriDecode(const std::string & sSrc);
std::string UriEncode(const std::string & sSrc);
bool StringEndsWith(std::string const &fullString, std::string const &ending);
#endif // _STRINGUTIL_H_

View File

@ -2,10 +2,10 @@
#include "../Core/Config.h"
shared_ptr<ThreadPool> GlobalThreadPool::pool;
std::shared_ptr<ThreadPool> GlobalThreadPool::pool;
bool GlobalThreadPool::initialized = false;
void GlobalThreadPool::Loop(const function<void(int,int)>& loop, int lower, int upper) {
void GlobalThreadPool::Loop(const std::function<void(int,int)>& loop, int lower, int upper) {
Inititialize();
pool->ParallelLoop(loop, lower, upper);
}

View File

@ -6,10 +6,10 @@ class GlobalThreadPool {
public:
// will execute slices of "loop" from "lower" to "upper"
// in parallel on the global thread pool
static void Loop(const function<void(int,int)>& loop, int lower, int upper);
static void Loop(const std::function<void(int,int)>& loop, int lower, int upper);
private:
static shared_ptr<ThreadPool> pool;
static std::shared_ptr<ThreadPool> pool;
static bool initialized;
static void Inititialize();
};

View File

@ -72,6 +72,7 @@ set(SRCS
HLE/sceParseUri.cpp
HLE/sceParseHttp.cpp
HLE/sceVaudio.cpp
HLE/sceAudiocodec.cpp
HW/MemoryStick.cpp
HW/MediaEngine.cpp
HW/SasAudio.cpp

View File

@ -63,7 +63,9 @@ void Config::Load(const char *iniFileName)
general->Get("ShowDebuggerOnLoad", &bShowDebuggerOnLoad, false);
general->Get("Language", &languageIni, "en_US");
general->Get("NumWorkerThreads", &iNumWorkerThreads, cpu_info.num_cores);
general->Get("EnableCheats", &bEnableCheats, false);
general->Get("MaxRecent", &iMaxRecent, 12);
// Fix issue from switching from uint (hex in .ini) to int (dec)
if (iMaxRecent == 0)
iMaxRecent = 12;
@ -74,6 +76,9 @@ void Config::Load(const char *iniFileName)
general->Get("WindowX", &iWindowX, 40);
general->Get("WindowY", &iWindowY, 100);
general->Get("AutoSaveSymbolMap", &bAutoSaveSymbolMap, false);
#ifdef _WIN32
general->Get("TopMost", &bTopMost);
#endif
if (recentIsos.size() > iMaxRecent)
recentIsos.resize(iMaxRecent);
@ -102,7 +107,6 @@ void Config::Load(const char *iniFileName)
graphics->Get("VBO", &bUseVBO, false);
graphics->Get("FrameSkip", &iFrameSkip, 0);
graphics->Get("FrameRate", &iFpsLimit, 60);
graphics->Get("UseMediaEngine", &bUseMediaEngine, true);
#ifdef USING_GLES2
graphics->Get("AnisotropyLevel", &iAnisotropyLevel, 0);
#else
@ -126,8 +130,8 @@ void Config::Load(const char *iniFileName)
IniFile::Section *sound = iniFile.GetOrCreateSection("Sound");
sound->Get("Enable", &bEnableSound, true);
sound->Get("AutoLoadDShow", &bAutoLoadDShow, false);
sound->Get("EnableAtrac3plus", &bEnableAtrac3plus, true);
IniFile::Section *control = iniFile.GetOrCreateSection("Control");
control->Get("ShowStick", &bShowAnalogStick, false);
#ifdef BLACKBERRY10
@ -141,6 +145,7 @@ void Config::Load(const char *iniFileName)
control->Get("KeyMapping",iMappingMap);
control->Get("AccelerometerToAnalogHoriz", &bAccelerometerToAnalogHoriz, false);
control->Get("ForceInputDevice", &iForceInputDevice, -1);
control->Get("RightStickBind", &iRightStickBind, 0);
IniFile::Section *pspConfig = iniFile.GetOrCreateSection("SystemParam");
pspConfig->Get("NickName", &sNickName, "shadow");
@ -168,7 +173,11 @@ void Config::Save()
}
IniFile::Section *general = iniFile.GetOrCreateSection("General");
// Need to do this somewhere...
bFirstRun = false;
general->Set("FirstRun", bFirstRun);
general->Set("AutoLoadLast", bAutoLoadLast);
general->Set("AutoRun", bAutoRun);
general->Set("Browse", bBrowse);
@ -181,9 +190,13 @@ void Config::Save()
general->Set("WindowX", iWindowX);
general->Set("WindowY", iWindowY);
general->Set("AutoSaveSymbolMap", bAutoSaveSymbolMap);
#ifdef _WIN32
general->Set("TopMost", bTopMost);
#endif
general->Set("Language", languageIni);
general->Set("NumWorkerThreads", iNumWorkerThreads);
general->Set("MaxRecent", iMaxRecent);
general->Set("EnableCheats", bEnableCheats);
IniFile::Section *cpu = iniFile.GetOrCreateSection("CPU");
cpu->Set("Jit", bJit);
@ -200,7 +213,6 @@ void Config::Save()
graphics->Set("VBO", bUseVBO);
graphics->Set("FrameSkip", iFrameSkip);
graphics->Set("FrameRate", iFpsLimit);
graphics->Set("UseMediaEngine", bUseMediaEngine);
graphics->Set("AnisotropyLevel", iAnisotropyLevel);
graphics->Set("VertexCache", bVertexCache);
graphics->Set("FullScreen", bFullScreen);
@ -216,8 +228,8 @@ void Config::Save()
IniFile::Section *sound = iniFile.GetOrCreateSection("Sound");
sound->Set("Enable", bEnableSound);
sound->Set("AutoLoadDShow", bAutoLoadDShow);
sound->Set("EnableAtrac3plus", bEnableAtrac3plus);
IniFile::Section *control = iniFile.GetOrCreateSection("Control");
control->Set("ShowStick", bShowAnalogStick);
control->Set("ShowTouchControls", bShowTouchControls);
@ -225,7 +237,8 @@ void Config::Save()
control->Set("KeyMapping",iMappingMap);
control->Set("AccelerometerToAnalogHoriz", bAccelerometerToAnalogHoriz);
control->Set("ForceInputDevice", iForceInputDevice);
control->Set("RightStickBind", iRightStickBind);
IniFile::Section *pspConfig = iniFile.GetOrCreateSection("SystemParam");
pspConfig->Set("NickName", sNickName.c_str());

View File

@ -33,13 +33,17 @@ public:
// Whether to save the config on close.
bool bSaveSettings;
bool bFirstRun;
// These are broken
bool bAutoLoadLast;
bool bFirstRun;
bool bSpeedLimit;
bool bConfirmOnQuit;
bool bAutoRun; // start immediately
bool bBrowse;
#ifdef _WIN32
bool bTopMost;
#endif
// General
int iNumWorkerThreads;
@ -64,7 +68,6 @@ public:
#endif
bool bStretchToDisplay;
int iFrameSkip;
bool bUseMediaEngine;
int iWindowX;
int iWindowY;
@ -80,11 +83,13 @@ public:
bool bTexDeposterize;
int iFpsLimit;
int iMaxRecent;
bool bEnableCheats;
bool bReloadCheats;
// Sound
bool bEnableSound;
bool bAutoLoadDShow;
bool bEnableAtrac3plus;
// UI
bool bShowTouchControls;
bool bShowDebuggerOnLoad;
@ -93,6 +98,12 @@ public:
bool bShowDebugStats;
bool bLargeControls;
bool bAccelerometerToAnalogHoriz;
// Temporary until control mapping rewrite
// 0 = none
// 1 = arrow buttons
// 2 = face buttons
// 3 = L/R
int iRightStickBind;
// Control
std::map<int,int> iMappingMap; // Can be used differently depending on systems
@ -114,6 +125,7 @@ public:
std::string currentDirectory;
std::string memCardDirectory;
std::string flashDirectory;
std::string internalDataDirectory;
void Load(const char *iniFileName = "ppsspp.ini");
void Save();

View File

@ -36,7 +36,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<WholeProgramOptimization>false</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
@ -67,9 +67,12 @@
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86\include;../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>USE_FFMPEG;_USE_DSHOW_;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<PreprocessorDefinitions>USE_FFMPEG;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -83,11 +86,14 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<PreprocessorDefinitions>USE_FFMPEG;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<MinimalRebuild>false</MinimalRebuild>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -107,8 +113,11 @@
<BufferSecurityCheck>false</BufferSecurityCheck>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<PreprocessorDefinitions>USE_FFMPEG;_USE_DSHOW_;_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<PreprocessorDefinitions>USE_FFMPEG;_MBCS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -130,13 +139,16 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../native;../native/ext/glew;../ext/zlib</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<BufferSecurityCheck>false</BufferSecurityCheck>
<OmitFramePointers>false</OmitFramePointers>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<WholeProgramOptimization>true</WholeProgramOptimization>
<WholeProgramOptimization>false</WholeProgramOptimization>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<PreprocessorDefinitions>USE_FFMPEG;_CRT_SECURE_NO_WARNINGS;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
@ -157,6 +169,7 @@
<ClCompile Include="Core.cpp" />
<ClCompile Include="CoreTiming.cpp" />
<ClCompile Include="CPU.cpp" />
<ClCompile Include="Cwcheat.cpp" />
<ClCompile Include="Debugger\Breakpoints.cpp" />
<ClCompile Include="Debugger\SymbolMap.cpp" />
<ClCompile Include="Dialog\PSPDialog.cpp" />
@ -179,6 +192,7 @@
<ClCompile Include="HLE\HLETables.cpp" />
<ClCompile Include="HLE\sceAtrac.cpp" />
<ClCompile Include="HLE\sceAudio.cpp" />
<ClCompile Include="HLE\sceAudiocodec.cpp" />
<ClCompile Include="HLE\sceChnnlsv.cpp" />
<ClCompile Include="HLE\sceCtrl.cpp" />
<ClCompile Include="HLE\sceDeflt.cpp" />
@ -228,9 +242,10 @@
<ClCompile Include="HLE\sceVaudio.cpp" />
<ClCompile Include="HLE\__sceAudio.cpp" />
<ClCompile Include="Host.cpp" />
<ClCompile Include="HW\audioPlayer.cpp" />
<ClCompile Include="HW\atrac3plus.cpp" />
<ClCompile Include="HW\MediaEngine.cpp" />
<ClCompile Include="HW\MemoryStick.cpp" />
<ClCompile Include="HW\MpegDemux.cpp" />
<ClCompile Include="HW\OMAConvert.cpp" />
<ClCompile Include="HW\SasAudio.cpp" />
<ClCompile Include="Loaders.cpp" />
@ -332,6 +347,7 @@
<ClInclude Include="CoreParameter.h" />
<ClInclude Include="CoreTiming.h" />
<ClInclude Include="CPU.h" />
<ClInclude Include="Cwcheat.h" />
<ClInclude Include="Debugger\Breakpoints.h" />
<ClInclude Include="Debugger\DebugInterface.h" />
<ClInclude Include="Debugger\SymbolMap.h" />
@ -357,6 +373,7 @@
<ClInclude Include="HLE\HLETables.h" />
<ClInclude Include="HLE\sceAtrac.h" />
<ClInclude Include="HLE\sceAudio.h" />
<ClInclude Include="HLE\sceAudiocodec.h" />
<ClInclude Include="HLE\sceCtrl.h" />
<ClInclude Include="HLE\sceChnnlsv.h" />
<ClInclude Include="HLE\sceDeflt.h" />
@ -406,10 +423,10 @@
<ClInclude Include="HLE\sceVaudio.h" />
<ClInclude Include="HLE\__sceAudio.h" />
<ClInclude Include="Host.h" />
<ClInclude Include="HW\audioPlayer.h" />
<ClInclude Include="HW\atrac3plus.h" />
<ClInclude Include="HW\MediaEngine.h" />
<ClInclude Include="HW\MpegDemux.h" />
<ClInclude Include="HW\OMAConvert.h" />
<ClInclude Include="HW\qeditsimple.h" />
<ClInclude Include="HW\SasAudio.h" />
<ClInclude Include="HW\MemoryStick.h" />
<ClInclude Include="Loaders.h" />

View File

@ -421,12 +421,21 @@
<ClCompile Include="HW\OMAConvert.cpp">
<Filter>HW</Filter>
</ClCompile>
<ClCompile Include="HW\audioPlayer.cpp">
<Filter>HW</Filter>
</ClCompile>
<ClCompile Include="MIPS\JitCommon\JitBlockCache.cpp">
<Filter>MIPS\JitCommon</Filter>
</ClCompile>
<ClCompile Include="Cwcheat.cpp">
<Filter>Core</Filter>
</ClCompile>
<ClCompile Include="HW\atrac3plus.cpp">
<Filter>HW</Filter>
</ClCompile>
<ClCompile Include="HW\MpegDemux.cpp">
<Filter>HW</Filter>
</ClCompile>
<ClCompile Include="HLE\sceAudiocodec.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
@ -780,18 +789,24 @@
<ClInclude Include="HLE\sceMp3.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="HW\qeditsimple.h">
<Filter>HW</Filter>
</ClInclude>
<ClInclude Include="HW\OMAConvert.h">
<Filter>HW</Filter>
</ClInclude>
<ClInclude Include="HW\audioPlayer.h">
<Filter>HW</Filter>
</ClInclude>
<ClInclude Include="MIPS\JitCommon\JitBlockCache.h">
<Filter>MIPS\JitCommon</Filter>
</ClInclude>
<ClInclude Include="Cwcheat.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="HW\atrac3plus.h">
<Filter>HW</Filter>
</ClInclude>
<ClInclude Include="HW\MpegDemux.h">
<Filter>HW</Filter>
</ClInclude>
<ClInclude Include="HLE\sceAudiocodec.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />

View File

@ -33,7 +33,7 @@ enum GPUCore {
struct CoreParameter
{
CoreParameter() : collectEmuLog(0), unthrottle(false), updateRecent(true) {}
CoreParameter() : collectEmuLog(0), unthrottle(false), fpsLimit(0), updateRecent(true) {}
// 0 = Interpreter
// 1 = Jit
// 2 = JitIL
@ -50,7 +50,6 @@ struct CoreParameter
bool printfEmuLog; // writes "emulator:" logging to stdout
std::string *collectEmuLog;
bool headLess; // Try to avoid messageboxes etc
bool useMediaEngine;
// Internal PSP resolution
int renderWidth;

View File

@ -33,8 +33,6 @@
#include "../Globals.h"
#include <string>
class PointerWrap;
//const int CPU_HZ = 222000000;

494
Core/CwCheat.cpp Normal file
View File

@ -0,0 +1,494 @@
#include "CwCheat.h"
#include "../Core/CoreTiming.h"
#include "../Core/CoreParameter.h"
#include "StringUtils.h"
#include "Common/FileUtil.h"
#include "Config.h"
#include "MIPS/MIPS.h"
#include "Core/Config.h"
const static std::string CHEATS_DIR = "Cheats";
static std::string activeCheatFile;
static int CheatEvent = -1;
static CWCheatEngine *cheatEngine;
void hleCheat(u64 userdata, int cyclesLate);
void trim2(std::string& str);
void __CheatInit() {
//Moved createFullPath to CheatInit from the constructor because it spams the log and constantly checks if exists. In here, only checks once.
activeCheatFile = CHEATS_DIR + "/" + g_paramSFO.GetValueString("DISC_ID").c_str() + ".ini";
File::CreateFullPath(CHEATS_DIR);
if (g_Config.bEnableCheats) {
if (!File::Exists(activeCheatFile)) {
File::CreateEmptyFile(activeCheatFile);
}
cheatEngine = new CWCheatEngine();
cheatEngine->CreateCodeList();
g_Config.bReloadCheats = false;
CheatEvent = CoreTiming::RegisterEvent("CheatEvent", &hleCheat);
CoreTiming::ScheduleEvent(msToCycles(77), CheatEvent, 0);
}
}
void __CheatShutdown() {
if (cheatEngine != 0) {
cheatEngine->Exit();
delete cheatEngine;
cheatEngine = 0;
}
}
void hleCheat(u64 userdata, int cyclesLate) {
CoreTiming::ScheduleEvent(msToCycles(77), CheatEvent, 0);
if (g_Config.bReloadCheats == true) {
cheatEngine->CreateCodeList();
g_Config.bReloadCheats = false;
}
if (g_Config.bEnableCheats && cheatEngine) {
cheatEngine->Run();
}
}
CWCheatEngine::CWCheatEngine() {
}
void CWCheatEngine::Exit() {
exit2 = true;
}
void CWCheatEngine::CreateCodeList() {
parts = makeCodeParts();
}
std::vector<int> CWCheatEngine::GetNextCode() {
std::string code1;
std::string code2;
std::string modifier = "_L";
std::vector<std::string> splitCode;
std::vector<int> finalCode;
std::string modifier2 = "0";
while (true) {
if (currentCode >= parts.size()) {
code1.clear();
code2.clear();
break;
}
code1 = parts[currentCode++];
trim2(code1);
code2 = parts[currentCode++];
trim2(code2);
splitCode.push_back(code1);
splitCode.push_back(code2);
int var1 = (int) parseHexLong(splitCode[0]);
int var2 = (int) parseHexLong(splitCode[1]);
finalCode.push_back(var1);
finalCode.push_back(var2);
if (splitCode[0].substr(0,2) == modifier) {
splitCode[0] = splitCode[0].substr(3);
break;
}
else if (splitCode[0].substr(0,1) == modifier2) {
break;
}
}
return finalCode;
}
void CWCheatEngine::SkipCodes(int count) {
for (int i = 0; i < count; i ++) {
if (GetNextCode()[0] == 0) {
break;
}
}
}
void CWCheatEngine::SkipAllCodes() {
currentCode = codes.size();
}
int CWCheatEngine::GetAddress(int value) {
// The User space base address has to be added to given value
return (value + 0x08800000) & 0x3FFFFFFF;
}
void CWCheatEngine::AddCheatLine(std::string& line) {
std::string cheatCodes;
if (cheatCodes.length() <= 0) {
cheatCodes = line;
} else {
cheatCodes += "\n" + line;
}
}
inline void trim2(std::string& str) {
size_t pos = str.find_last_not_of(' ');
if(pos != std::string::npos) {
str.erase(pos + 1);
pos = str.find_first_not_of(' ');
if(pos != std::string::npos) str.erase(0, pos);
}
else str.erase(str.begin(), str.end());
}
inline std::vector<std::string> makeCodeParts() {
CWCheatEngine cheats;
std::string currentcode;
std::vector<std::string> finalList;
std::vector<std::string> CodesList = cheats.GetCodesList();
char split_char = '\n';
char empty = ' ';
for (size_t i = 0; i < CodesList.size(); i++) {
currentcode = CodesList[i];
for (size_t j=0; j < currentcode.length(); j++) {
if (currentcode[j] == empty) {
currentcode[j] = '\n';
}
}
trim2(currentcode);
std::istringstream iss(currentcode);
std::string each;
while (std::getline(iss, each, split_char)) {
finalList.push_back(each);
}
}
return finalList;
}
std::vector<std::string> CWCheatEngine::GetCodesList() {
std::string line;
std::string skip = "//";
std::vector<std::string> codesList; // Read from INI here
std::ifstream list(activeCheatFile.c_str());
for (int i = 0; !list.eof(); i ++) {
getline(list, line, '\n');
if (line.substr(0,2) == skip) {
line.clear();
} else {
codesList.push_back(line);
}
}
for(size_t i = 0; i < codesList.size(); i++) {
trim2(codesList[i]);
}
return codesList;
}
void CWCheatEngine::Run() {
CWCheatEngine cheats;
exit2 = false;
while (!exit2) {
codes = cheats.GetCodesList(); // UI Member
currentCode = 0;
while (true) {
std::vector<int> code = GetNextCode();
if (code.size() < 2) {
Exit();
break;
}
int value;
unsigned int comm = code[0];
int arg = code[1];
int addr = GetAddress(comm & 0x0FFFFFFF);
switch (comm >> 28) {
case 0: // 8-bit write.
if (Memory::IsValidAddress(addr)){
Memory::Write_U8((u8) arg, addr);
}
break;
case 0x1: // 16-bit write
if (Memory::IsValidAddress(addr)){
Memory::Write_U16((u16) arg, addr);
}
break;
case 0x2: // 32-bit write
if (Memory::IsValidAddress(addr)){
Memory::Write_U32((u32) arg, addr);
}
break;
case 0x3: // Increment/Decrement
{
addr = GetAddress(arg);
value = 0;
int increment = 0;
// Read value from memory
switch ((comm >> 20) & 0xF) {
case 1:
case 2: // 8-bit
value = Memory::Read_U8(addr);
increment = comm & 0xFF;
break;
case 3:
case 4: // 16-bit
value = Memory::Read_U16(addr);
increment = comm & 0xFFFF;
break;
case 5:
case 6: // 32-bit
value = Memory::Read_U32(addr);
code = GetNextCode();
if ( code[0] != NULL) {
increment = code[0];
}
break;
}
// Increment/Decrement value
switch ((comm >> 20) & 0xF) {
case 1:
case 3:
case 5: // increment
value += increment;
break;
case 2:
case 4:
case 6: // Decrement
value -= increment;
break;
}
// Write value back to memory
switch ((comm >> 20) & 0xF) {
case 1:
case 2: // 8-bit
Memory::Write_U8((u8) value, addr);
break;
case 3:
case 4: // 16-bit
Memory::Write_U16((u16) value, addr);
break;
case 5:
case 6: // 32-bit
Memory::Write_U32((u32) value, addr);
break;
}
break;
}
case 0x4: // 32-bit patch code
code = GetNextCode();
if (code[0] != NULL) {
int data = code[0];
int dataAdd = code[1];
int maxAddr = (arg >> 16) & 0xFFFF;
int stepAddr = (arg & 0xFFFF) * 4;
for (int a = 0; a < maxAddr; a++) {
if (Memory::IsValidAddress(addr)) {
Memory::Write_U32((u32) data, addr);
}
addr += stepAddr;
data += dataAdd;
}
}
break;
case 0x5: // Memcpy command
code = GetNextCode();
if (code[0] != NULL) {
int destAddr = code[0];
if (Memory::IsValidAddress(addr) && Memory::IsValidAddress(destAddr)) {
Memory::Memcpy(destAddr, Memory::GetPointer(addr), arg);
}
}
break;
case 0x6: // Pointer commands
code = GetNextCode();
if (code[0] != NULL) {
int arg2 = code[0];
int offset = code[1];
int baseOffset = (arg2 >> 20) * 4;
int base = Memory::Read_U32(addr + baseOffset);
int count = arg2 & 0xFFFF;
int type = (arg2 >> 16) & 0xF;
for (int i = 1; i < count; i ++ ) {
if (i+1 < count) {
code = GetNextCode();
int arg3 = code[0];
int arg4 = code[1];
int comm3 = arg3 >> 28;
switch (comm3) {
case 0x1: // type copy byte
{
int srcAddr = Memory::Read_U32(addr) + offset;
int dstAddr = Memory::Read_U16(addr + baseOffset) + (arg3 & 0x0FFFFFFF);
Memory::Memcpy(dstAddr, Memory::GetPointer(srcAddr), arg);
type = -1; //Done
break; }
case 0x2:
case 0x3: // type pointer walk
{
int walkOffset = arg3 & 0x0FFFFFFF;
if (comm3 == 0x3) {
walkOffset = -walkOffset;
}
base = Memory::Read_U32(base + walkOffset);
int comm4 = arg4 >> 28;
switch (comm4) {
case 0x2:
case 0x3: // type pointer walk
walkOffset = arg4 & 0x0FFFFFFF;
if (comm4 == 0x3) {
walkOffset = -walkOffset;
}
base = Memory::Read_U32(base + walkOffset);
break;
}
break; }
case 0x9: // type multi address write
base += arg3 & 0x0FFFFFFF;
arg += arg4;
break;
}
}
}
switch (type) {
case 0: // 8 bit write
Memory::Write_U8((u8) arg, base + offset);
break;
case 1: // 16-bit write
Memory::Write_U16((u16) arg, base + offset);
break;
case 2: // 32-bit write
Memory::Write_U32((u32) arg, base + offset);
break;
case 3: // 8 bit inverse write
Memory::Write_U8((u8) arg, base - offset);
break;
case 4: // 16-bit inverse write
Memory::Write_U16((u16) arg, base - offset);
break;
case 5: // 32-bit inverse write
Memory::Write_U32((u32) arg, base - offset);
break;
case -1: // Operation already performed, nothing to do
break;
}
}
break;
case 0x7: // Boolean commands.
switch (arg >> 16) {
case 0x0000: // 8-bit OR.
if (Memory::IsValidAddress(addr)) {
int val1 = (int) (arg & 0xFF);
int val2 = (int) Memory::Read_U8(addr);
Memory::Write_U8((u8) (val1 | val2), addr);
}
break;
case 0x0002: // 8-bit AND.
if (Memory::IsValidAddress(addr)) {
int val1 = (int) (arg & 0xFF);
int val2 = (int) Memory::Read_U8(addr);
Memory::Write_U8((u8) (val1 & val2), addr);
}
break;
case 0x0004: // 8-bit XOR.
if (Memory::IsValidAddress(addr)) {
int val1 = (int) (arg & 0xFF);
int val2 = (int) Memory::Read_U8(addr);
Memory::Write_U8((u8) (val1 ^ val2), addr);
}
break;
case 0x0001: // 16-bit OR.
if (Memory::IsValidAddress(addr)) {
short val1 = (short) (arg & 0xFFFF);
short val2 = (short) Memory::Read_U16(addr);
Memory::Write_U16((u16) (val1 | val2), addr);
}
break;
case 0x0003: // 16-bit AND.
if (Memory::IsValidAddress(addr)) {
short val1 = (short) (arg & 0xFFFF);
short val2 = (short) Memory::Read_U16(addr);
Memory::Write_U16((u16) (val1 & val2), addr);
}
break;
case 0x0005: // 16-bit OR.
if (Memory::IsValidAddress(addr)) {
short val1 = (short) (arg & 0xFFFF);
short val2 = (short) Memory::Read_U16(addr);
Memory::Write_U16((u16) (val1 ^ val2), addr);
}
break;
}
break;
case 0x8: // 8-bit and 16-bit patch code
code = GetNextCode();
if (code[0] != NULL) {
int data = code[0];
int dataAdd = code[1];
bool is8Bit = (data >> 16) == 0x0000;
int maxAddr = (arg >> 16) & 0xFFFF;
int stepAddr = (arg & 0xFFFF) * (is8Bit ? 1 : 2);
for (int a = 0; a < maxAddr; a++) {
if (Memory::IsValidAddress(addr)) {
if (is8Bit) {
Memory::Write_U8((u8) (data & 0xFF), addr);
}
else {
Memory::Write_U16((u16) (data & 0xFFFF), addr);
}
}
addr += stepAddr;
data += dataAdd;
}
}
break;
case 0xB: // Time command (not sure what to do?)
break;
case 0xC: // Code stopper
if (Memory::IsValidAddress(addr)) {
value = Memory::Read_U32(addr);
if (value != arg) {
SkipAllCodes();
}
}
break;
case 0xD: // Test commands & Jocker codes ( Someone will have to help me with these)
break;
case 0xE: // Test commands, multiple skip
{
bool is8Bit = (comm >> 24) == 0x1;
addr = GetAddress(arg & 0x0FFFFFFF);
if (Memory::IsValidAddress(addr)) {
int memoryValue = is8Bit ? Memory::Read_U8(addr) : Memory::Read_U16(addr);
int testValue = comm & (is8Bit ? 0xFF : 0xFFFF);
bool executeNextLines = false;
switch ( arg >> 28) {
case 0x0: // Equal
executeNextLines = memoryValue == testValue;
break;
case 0x1: // Not Equal
executeNextLines = memoryValue != testValue;
break;
case 0x2: // Less Than
executeNextLines = memoryValue < testValue;
break;
case 0x3: // Greater Than
executeNextLines = memoryValue > testValue;
break;
}
if (!executeNextLines) {
int skip = (comm >> 16) & (is8Bit ? 0xFF : 0xFFF);
SkipCodes(skip);
}
}
break;
}
}
}
}
// exiting...
Exit();
}

42
Core/CwCheat.h Normal file
View File

@ -0,0 +1,42 @@
// Rough and ready CwCheats implementation, disabled by default.
// Will not enable by default until the TOOD:s have been addressed.
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include "base/basictypes.h"
#include "Core/MemMap.h"
void __CheatInit();
void __CheatShutdown();
std::vector<std::string> makeCodeParts();
class CWCheatEngine {
public:
CWCheatEngine();
std::string String();
void AddCheatLine(std::string& line);
std::vector<std::string> GetCodesList();
void CreateCodeList();
void Exit();
void Run();
std::vector<int> GetNextCode();
private:
void SkipCodes(int count);
void SkipAllCodes();
int GetAddress(int value);
static uint64_t const serialVersionUID = 6791588139795694296ULL;
static const int cheatsThreadSleepMillis = 5;
bool cheatsOn;
std::vector<std::string> codes;
size_t currentCode;
bool exit2;
std::vector<std::string> parts;
};

View File

@ -22,14 +22,12 @@
#include <unistd.h>
#endif
#include <cstring>
#include <algorithm>
#include "../../Globals.h"
#include "../../Core/MemMap.h"
#include "SymbolMap.h"
#include <vector>
#include <map>
SymbolMap symbolMap;
@ -262,6 +260,8 @@ void SymbolMap::FillSymbolListBox(HWND listbox,SymbolType symmask)
//ListBox_AddString(listbox,"(0x80002000)");
//ListBox_SetItemData(listbox,1,0x80002000);
SendMessage(listbox, WM_SETREDRAW, FALSE, 0);
SendMessage(listbox, LB_INITSTORAGE, (WPARAM)entries.size(), (LPARAM)entries.size() * 30);
for (size_t i = 0; i < entries.size(); i++)
{
if (entries[i].type & symmask)
@ -272,6 +272,8 @@ void SymbolMap::FillSymbolListBox(HWND listbox,SymbolType symmask)
ListBox_SetItemData(listbox,index,entries[i].vaddress);
}
}
SendMessage(listbox, WM_SETREDRAW, TRUE, 0);
RedrawWindow(listbox, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
ShowWindow(listbox,SW_SHOW);
}
@ -289,6 +291,8 @@ void SymbolMap::FillSymbolComboBox(HWND listbox,SymbolType symmask)
//ListBox_AddString(listbox,"(0x80002000)");
//ListBox_SetItemData(listbox,1,0x80002000);
SendMessage(listbox, WM_SETREDRAW, FALSE, 0);
SendMessage(listbox, CB_INITSTORAGE, (WPARAM)entries.size(), (LPARAM)entries.size() * 30);
for (size_t i = 0; i < entries.size(); i++)
{
if (entries[i].type & symmask)
@ -299,6 +303,8 @@ void SymbolMap::FillSymbolComboBox(HWND listbox,SymbolType symmask)
ComboBox_SetItemData(listbox,index,entries[i].vaddress);
}
}
SendMessage(listbox, WM_SETREDRAW, TRUE, 0);
RedrawWindow(listbox, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN);
ShowWindow(listbox,SW_SHOW);
}

View File

@ -18,8 +18,10 @@
#include "../Util/PPGeDraw.h"
#include "PSPDialog.h"
#include "ChunkFile.h"
#include "i18n/i18n.h"
#define FADE_TIME 0.5
const float FONT_SCALE = 0.55f;
PSPDialog::PSPDialog() : status(SCE_UTILITY_STATUS_SHUTDOWN)
, lastButtons(0)
@ -44,21 +46,15 @@ PSPDialog::DialogStatus PSPDialog::GetStatus()
void PSPDialog::StartDraw()
{
PPGeBegin();
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x70606060));
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x80000000));
}
void PSPDialog::EndDraw()
{
PPGeEnd();
}
void PSPDialog::DisplayMessage(std::string text)
{
PPGeDrawRect(30, 30, 450, 31, CalcFadedColor(0xFFFFFFFF));
PPGeDrawRect(30, 200, 450, 201, CalcFadedColor(0xFFFFFFFF));
PPGeDrawTextWrapped(text.c_str(), 40, 50, 420, PPGE_ALIGN_LEFT, 0.55f, CalcFadedColor(0xFFFFFFFF));
}
int PSPDialog::Shutdown()
int PSPDialog::Shutdown(bool force)
{
status = SCE_UTILITY_STATUS_SHUTDOWN;
return 0;
@ -75,7 +71,7 @@ void PSPDialog::UpdateFade()
{
if(isFading)
{
fadeTimer += 1.0f/30; // Probably need a more real value of delta time
fadeTimer += 1.0f/30.0f; // Probably need a more real value of delta time
if(fadeTimer < FADE_TIME)
{
if(fadeIn)
@ -112,13 +108,42 @@ void PSPDialog::DoState(PointerWrap &p)
p.Do(status);
p.Do(lastButtons);
p.Do(buttons);
p.Do(fadeTimer);
p.Do(isFading);
p.Do(fadeIn);
p.Do(fadeValue);
p.Do(okButtonImg);
p.Do(cancelButtonImg);
p.Do(okButtonFlag);
p.Do(cancelButtonFlag);
p.DoMarker("PSPDialog");
}
pspUtilityDialogCommon *PSPDialog::GetCommonParam()
{
// FIXME
return 0;
}
bool PSPDialog::IsButtonPressed(int checkButton)
{
if(isFading) return false;
return (!(lastButtons & checkButton)) && (buttons & checkButton);
return !isFading && !(lastButtons & checkButton) && (buttons & checkButton);
}
void PSPDialog::DisplayButtons(int flags)
{
I18NCategory *d = GetI18NCategory("Dialog");
float x1 = 183.5f, x2 = 261.5f;
if (GetCommonParam()->buttonSwap == 1) {
x1 = 261.5f;
x2 = 183.5f;
}
if (flags & DS_BUTTON_OK) {
PPGeDrawImage(okButtonImg, x2, 256, 11.5f, 11.5f, 0, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(d->T("Enter"), x2 + 14.5f, 252, PPGE_ALIGN_LEFT, FONT_SCALE, CalcFadedColor(0xFFFFFFFF));
}
if (flags & DS_BUTTON_CANCEL) {
PPGeDrawText(d->T("Back"), x1 + 14.5f, 252, PPGE_ALIGN_LEFT, FONT_SCALE, CalcFadedColor(0xFFFFFFFF));
PPGeDrawImage(cancelButtonImg, x1, 256, 11.5f, 11.5f, 0, CalcFadedColor(0xFFFFFFFF));
}
}

View File

@ -52,8 +52,9 @@ public:
virtual ~PSPDialog();
virtual int Update();
virtual int Shutdown();
virtual int Shutdown(bool force = false);
virtual void DoState(PointerWrap &p);
virtual pspUtilityDialogCommon *GetCommonParam();
enum DialogStatus
{
@ -64,13 +65,21 @@ public:
SCE_UTILITY_STATUS_SHUTDOWN = 4
};
enum DialogStockButton
{
DS_BUTTON_NONE = 0x00,
DS_BUTTON_OK = 0x01,
DS_BUTTON_CANCEL = 0x02,
DS_BUTTON_BOTH = 0x03,
};
DialogStatus GetStatus();
void StartDraw();
void EndDraw();
protected:
bool IsButtonPressed(int checkButton);
void DisplayMessage(std::string text);
void DisplayButtons(int flags);
void StartFade(bool fadeIn_);
void UpdateFade();
@ -85,4 +94,9 @@ protected:
bool isFading;
bool fadeIn;
u32 fadeValue;
int okButtonImg;
int cancelButtonImg;
int okButtonFlag;
int cancelButtonFlag;
};

View File

@ -23,6 +23,8 @@
#include "ChunkFile.h"
#include "i18n/i18n.h"
const float FONT_SCALE = 0.55f;
PSPMsgDialog::PSPMsgDialog()
: PSPDialog()
, flag(0)
@ -128,45 +130,61 @@ int PSPMsgDialog::Init(unsigned int paramAddr)
return 0;
}
void PSPMsgDialog::DisplayBack()
void PSPMsgDialog::DisplayMessage(std::string text, bool hasYesNo)
{
I18NCategory *d = GetI18NCategory("Dialog");
PPGeDrawImage(cancelButtonImg, 290, 220, 12, 12, 0, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(d->T("Back"), 310, 218, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF));
}
void PSPMsgDialog::DisplayYesNo()
{
I18NCategory *d = GetI18NCategory("Dialog");
PPGeDrawText(d->T("Yes"), 200, 150, PPGE_ALIGN_LEFT, 0.55f, CalcFadedColor(yesnoChoice == 1?0xFF0000FF:0xFFFFFFFF));
PPGeDrawText(d->T("No"), 320, 150, PPGE_ALIGN_LEFT, 0.55f, CalcFadedColor(yesnoChoice == 0?0xFF0000FF:0xFFFFFFFF));
if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0)
const float WRAP_WIDTH = 350.0f;
float y = 136.0f, h;
int n;
PPGeMeasureText(0, &h, &n, text.c_str(), FONT_SCALE, PPGE_LINE_WRAP_WORD, WRAP_WIDTH);
float h2 = h * (float)n / 2.0f;
if (hasYesNo)
{
yesnoChoice = 1;
I18NCategory *d = GetI18NCategory("Dialog");
const char *choiceText;
u32 yesColor, noColor;
float x, w;
if (yesnoChoice == 1) {
choiceText = d->T("Yes");
x = 208.0f;
yesColor = 0xFF0FFFFF;
noColor = 0xFFFFFFFF;
}
else {
choiceText = d->T("No");
x = 272.0f;
yesColor = 0xFFFFFFFF;
noColor = 0xFF0FFFFF;
}
PPGeMeasureText(&w, &h, 0, choiceText, FONT_SCALE);
w = w / 2.0f + 5.5f;
h /= 2.0f;
float y2 = y + h2 + 4.0f;
h2 += h + 4.0f;
y = 132.0f - h;
PPGeDrawRect(x - w, y2 - h, x + w, y2 + h, CalcFadedColor(0x6DCFCFCF));
PPGeDrawText(d->T("Yes"), 208.0f, y2, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(yesColor));
PPGeDrawText(d->T("No"), 272.0f, y2, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(noColor));
if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0) {
yesnoChoice = 1;
}
else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1) {
yesnoChoice = 0;
}
}
else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1)
{
yesnoChoice = 0;
}
}
void PSPMsgDialog::DisplayOk()
{
I18NCategory *d = GetI18NCategory("Dialog");
PPGeDrawImage(okButtonImg, 200, 220, 12, 12, 0, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(d->T("Enter"), 220, 218, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF));
PPGeDrawTextWrapped(text.c_str(), 240.0f, y, WRAP_WIDTH, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0xFFFFFFFF));
float sy = 122.0f - h2, ey = 150.0f + h2;
PPGeDrawRect(60.0f, sy, 420.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF));
PPGeDrawRect(60.0f, ey, 420.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF));
}
int PSPMsgDialog::Update()
{
if (status != SCE_UTILITY_STATUS_RUNNING)
{
return 0;
}
if((flag & DS_ERROR))
if ((flag & DS_ERROR))
{
status = SCE_UTILITY_STATUS_FINISHED;
}
@ -180,7 +198,7 @@ int PSPMsgDialog::Update()
cancelButtonImg = I_CROSS;
okButtonFlag = CTRL_CIRCLE;
cancelButtonFlag = CTRL_CROSS;
if(messageDialog.common.buttonSwap == 1)
if (messageDialog.common.buttonSwap == 1)
{
okButtonImg = I_CROSS;
cancelButtonImg = I_CIRCLE;
@ -189,17 +207,21 @@ int PSPMsgDialog::Update()
}
StartDraw();
// white -> RGB(168,173,189), black -> RGB(129,134,150)
// (255 - a) + (x * a / 255) = 173, x * a / 255 = 134
// a = 255 - w + b = 158, x = b * 255 / a = ?
// but is not drawn using x * a + y * (255 - a) here?
//PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x9EF2D8D0));
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC));
if((flag & DS_MSG) || (flag & DS_ERRORMSG))
DisplayMessage(msgText);
if ((flag & DS_MSG) || (flag & DS_ERRORMSG))
DisplayMessage(msgText, (flag & DS_YESNO) != 0);
if(flag & DS_YESNO)
DisplayYesNo();
if (flag & (DS_OK | DS_VALIDBUTTON))
DisplayOk();
DisplayButtons(DS_BUTTON_OK);
if(flag & DS_CANCELBUTTON)
DisplayBack();
if (flag & DS_CANCELBUTTON)
DisplayButtons(DS_BUTTON_CANCEL);
if (IsButtonPressed(cancelButtonFlag) && (flag & DS_CANCELBUTTON))
{
@ -210,9 +232,9 @@ int PSPMsgDialog::Update()
messageDialog.buttonPressed = 0;
StartFade(false);
}
else if(IsButtonPressed(okButtonFlag) && (flag & DS_VALIDBUTTON))
else if (IsButtonPressed(okButtonFlag) && (flag & DS_VALIDBUTTON))
{
if(yesnoChoice == 0)
if (yesnoChoice == 0)
{
messageDialog.buttonPressed = 2;
}
@ -239,7 +261,7 @@ int PSPMsgDialog::Abort()
return PSPDialog::Shutdown();
}
int PSPMsgDialog::Shutdown()
int PSPMsgDialog::Shutdown(bool force)
{
return PSPDialog::Shutdown();
}
@ -252,9 +274,10 @@ void PSPMsgDialog::DoState(PointerWrap &p)
p.Do(messageDialogAddr);
p.DoArray(msgText, sizeof(msgText));
p.Do(yesnoChoice);
p.Do(okButtonImg);
p.Do(cancelButtonImg);
p.Do(okButtonFlag);
p.Do(cancelButtonFlag);
p.DoMarker("PSPMsgDialog");
}
pspUtilityDialogCommon *PSPMsgDialog::GetCommonParam()
{
return &messageDialog.common;
}

View File

@ -59,15 +59,14 @@ public:
virtual int Init(unsigned int paramAddr);
virtual int Update();
virtual int Shutdown();
virtual int Shutdown(bool force = false);
virtual void DoState(PointerWrap &p);
virtual pspUtilityDialogCommon *GetCommonParam();
int Abort();
private :
void DisplayBack();
void DisplayYesNo();
void DisplayEnter();
void DisplayOk();
void DisplayMessage(std::string text, bool hasYesNo = false);
enum Flags
{
@ -89,10 +88,5 @@ private :
char msgText[512];
int yesnoChoice;
int okButtonImg;
int cancelButtonImg;
int okButtonFlag;
int cancelButtonFlag;
};

View File

@ -21,6 +21,7 @@
#include "Core/Dialog/PSPOskDialog.h"
#include "Core/Util/PPGeDraw.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "Common/ChunkFile.h"
#include "GPU/GPUState.h"
@ -124,20 +125,20 @@ PSPOskDialog::PSPOskDialog() : PSPDialog() {
PSPOskDialog::~PSPOskDialog() {
}
void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const u32 em_address)
void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const PSPPointer<u16> em_address)
{
char stringBuffer[2048];
char *string = stringBuffer;
if (em_address == 0)
if (!em_address.Valid())
{
_string = "";
return;
}
u16 *src = (u16 *) Memory::GetPointer(em_address);
char stringBuffer[2048];
char *string = stringBuffer;
auto input = em_address;
int c;
while (c = *src++)
while ((c = *input++) != 0)
{
if (c < 0x80)
*string++ = c;
@ -154,13 +155,13 @@ void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const u32 em_address)
_string = stringBuffer;
}
void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, wchar_t* input)
void PSPOskDialog::ConvertUCS2ToUTF8(std::string& _string, const wchar_t *input)
{
char stringBuffer[2048];
char *string = stringBuffer;
int c;
while (c = *input++)
while ((c = *input++) != 0)
{
if (c < 0x80)
*string++ = c;
@ -189,16 +190,16 @@ int PSPOskDialog::Init(u32 oskPtr)
return -1;
}
oskParams = Memory::GetStruct<SceUtilityOskParams>(oskPtr);
oskParams = oskPtr;
if (oskParams->base.size != sizeof(SceUtilityOskParams))
{
ERROR_LOG(HLE, "sceUtilityOskInitStart: invalid size (%d)", oskParams->base.size);
return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE;
}
// Also seems to crash.
if (!Memory::IsValidAddress(oskParams->fieldPtr))
if (!oskParams->fields.Valid())
{
ERROR_LOG_REPORT(HLE, "sceUtilityOskInitStart: invalid field data (%08x)", oskParams->fieldPtr);
ERROR_LOG_REPORT(HLE, "sceUtilityOskInitStart: invalid field data (%08x)", oskParams->fields.ptr);
return -1;
}
@ -211,26 +212,19 @@ int PSPOskDialog::Init(u32 oskPtr)
selectedChar = 0;
currentKeyboard = OSK_KEYBOARD_LATIN_LOWERCASE;
Memory::ReadStruct(oskParams->fieldPtr, &oskData);
ConvertUCS2ToUTF8(oskDesc, oskData.descPtr);
ConvertUCS2ToUTF8(oskIntext, oskData.intextPtr);
ConvertUCS2ToUTF8(oskOuttext, oskData.outtextPtr);
ConvertUCS2ToUTF8(oskDesc, oskParams->fields[0].desc);
ConvertUCS2ToUTF8(oskIntext, oskParams->fields[0].intext);
ConvertUCS2ToUTF8(oskOuttext, oskParams->fields[0].outtext);
i_level = 0;
inputChars = L"";
if (oskData.intextPtr) {
u16 *src = (u16 *) Memory::GetPointer(oskData.intextPtr);
if (oskParams->fields[0].intext.Valid()) {
auto src = oskParams->fields[0].intext;
int c;
while (c = *src++)
{
while ((c = *src++) != 0)
inputChars += c;
if(c == 0x00)
{
break;
}
}
}
// Eat any keys pressed before the dialog inited.
@ -307,10 +301,10 @@ std::wstring PSPOskDialog::CombinationKorean(bool isInput)
}
}
} else if(i_level == 2) {
u32 tmp = GetIndex(kor_vowel, sw);
int tmp = GetIndex(kor_vowel, sw);
if(tmp != -1) {
int tmp2 = -1;
for(int j = 0; j < sizeof(kor_vowelCom) / 4; j+=3) {
for(size_t j = 0; j < sizeof(kor_vowelCom) / 4; j+=3) {
if(kor_vowelCom[j] == tmp && kor_vowelCom[j + 1] == i_value[1]) {
tmp2 = kor_vowelCom[j + 2];
break;
@ -337,7 +331,7 @@ std::wstring PSPOskDialog::CombinationKorean(bool isInput)
}
}
} else {
u32 tmp = GetIndex(kor_lcons, sw);
int tmp = GetIndex(kor_lcons, sw);
if(tmp == -1) {
string += inputChars[i];
@ -367,10 +361,10 @@ std::wstring PSPOskDialog::CombinationKorean(bool isInput)
}
}
} else if(i_level == 3) {
u32 tmp = GetIndex(kor_lcons, sw);
int tmp = GetIndex(kor_lcons, sw);
if(tmp != -1) {
int tmp2 = -1;
for(int j = 0; j < sizeof(kor_lconsCom) / 4; j+=3) {
for(size_t j = 0; j < sizeof(kor_lconsCom) / 4; j+=3) {
if(kor_lconsCom[j] == tmp && kor_lconsCom[j + 1] == i_value[2]) {
tmp2 = kor_lconsCom[j + 2];
break;
@ -402,7 +396,7 @@ std::wstring PSPOskDialog::CombinationKorean(bool isInput)
}
}
} else {
u32 tmp = GetIndex(kor_vowel, sw);
int tmp = GetIndex(kor_vowel, sw);
if(tmp == -1) {
string += inputChars[i];
if (inputChars.size() < FieldMaxLength()) {
@ -422,9 +416,9 @@ std::wstring PSPOskDialog::CombinationKorean(bool isInput)
} else {
if (inputChars.size() < FieldMaxLength()) {
int tmp2 = -1;
for(int j = 0; j < sizeof(kor_lconsSpr) / 4; j+=3) {
for(size_t j = 0; j < sizeof(kor_lconsSpr) / 4; j+=3) {
if(kor_lconsSpr[j] == i_value[2]) {
tmp2 = j;
tmp2 = (int)j;
break;
}
}
@ -441,7 +435,7 @@ std::wstring PSPOskDialog::CombinationKorean(bool isInput)
i_level = 2;
}
} else {
u32 tmp2 = GetIndex(kor_cons, kor_lcons[i_value[2]]);
int tmp2 = GetIndex(kor_cons, kor_lcons[i_value[2]]);
if(tmp2 != -1) {
u16 code = 0xAC00 + i_value[0] * 0x24C + i_value[1] * 0x1C;
@ -583,7 +577,7 @@ void PSPOskDialog::RemoveKorean()
else if(i_level == 2)
{
int tmp = -1;
for(int i = 2; i < sizeof(kor_vowelCom) / 4; i+=3)
for(size_t i = 2; i < sizeof(kor_vowelCom) / 4; i+=3)
{
if(kor_vowelCom[i] == i_value[1])
{
@ -607,11 +601,11 @@ void PSPOskDialog::RemoveKorean()
else if(i_level == 3)
{
int tmp = -1;
for(int i = 2; i < sizeof(kor_lconsCom) / 4; i+=3)
for(size_t i = 2; i < sizeof(kor_lconsCom) / 4; i+=3)
{
if(kor_lconsCom[i] == i_value[2])
{
tmp = kor_vowelCom[i - 1];
tmp = kor_lconsCom[i - 1];
break;
}
}
@ -631,9 +625,9 @@ void PSPOskDialog::RemoveKorean()
}
}
u32 PSPOskDialog::GetIndex(const wchar_t* src, wchar_t ch)
int PSPOskDialog::GetIndex(const wchar_t* src, wchar_t ch)
{
for(u32 i = 0; i < wcslen(src); i++)
for(int i = 0, end = (int)wcslen(src); i < end; i++)
{
if(src[i] == ch)
{
@ -646,9 +640,9 @@ u32 PSPOskDialog::GetIndex(const wchar_t* src, wchar_t ch)
u32 PSPOskDialog::FieldMaxLength()
{
if (oskData.outtextlimit > oskData.outtextlength - 1 || oskData.outtextlimit == 0)
return oskData.outtextlength - 1;
return oskData.outtextlimit;
if (oskParams->fields[0].outtextlimit > oskParams->fields[0].outtextlength - 1 || oskParams->fields[0].outtextlimit == 0)
return oskParams->fields[0].outtextlength - 1;
return oskParams->fields[0].outtextlimit;
}
void PSPOskDialog::RenderKeyboard()
@ -754,9 +748,18 @@ int PSPOskDialog::Update()
UpdateFade();
StartDraw();
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363));
RenderKeyboard();
PPGeDrawImage(I_CROSS, 30, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF));
PPGeDrawImage(I_CIRCLE, 150, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF));
if (g_Config.bButtonPreference)
{
PPGeDrawImage(I_CROSS, 30, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF));
PPGeDrawImage(I_CIRCLE, 150, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF));
}
else
{
PPGeDrawImage(I_CIRCLE, 30, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF));
PPGeDrawImage(I_CROSS, 150, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF));
}
//PPGeDrawImage(I_BUTTON, 230, 220, 50, 20, 0, CalcFadedColor(0xFFFFFFFF));
//PPGeDrawImage(I_BUTTON, 350, 220, 55, 20, 0, CalcFadedColor(0xFFFFFFFF));
@ -792,8 +795,8 @@ int PSPOskDialog::Update()
selectedChar = (selectedChar + (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard])) % (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard]);
if (IsButtonPressed(CTRL_CROSS))
{
if (IsButtonPressed(CTRL_CROSS) && g_Config.bButtonPreference || IsButtonPressed(CTRL_CIRCLE) && !g_Config.bButtonPreference)
{
inputChars = CombinationString(true);
}
else if (IsButtonPressed(CTRL_SELECT))
@ -813,7 +816,7 @@ int PSPOskDialog::Update()
selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra;
}
else if (IsButtonPressed(CTRL_CIRCLE))
else if (IsButtonPressed(CTRL_CIRCLE) && g_Config.bButtonPreference || IsButtonPressed(CTRL_CROSS) && !g_Config.bButtonPreference)
{
if (inputChars.size() > 0)
{
@ -835,38 +838,35 @@ int PSPOskDialog::Update()
status = SCE_UTILITY_STATUS_SHUTDOWN;
}
for (u32 i = 0; i < oskData.outtextlength; ++i)
u16 *outText = oskParams->fields[0].outtext;
for (u32 i = 0, end = oskParams->fields[0].outtextlength; i < end; ++i)
{
u16 value = 0;
if (i < inputChars.size())
value = inputChars[i];
Memory::Write_U16(value, oskData.outtextPtr + (2 * i));
outText[i] = value;
}
oskParams->base.result = 0;
oskData.result = PSP_UTILITY_OSK_RESULT_CHANGED;
Memory::WriteStruct(oskParams->fieldPtr, &oskData);
oskParams->fields[0].result = PSP_UTILITY_OSK_RESULT_CHANGED;
return 0;
}
template <typename T>
static void DoBasePointer(PointerWrap &p, T **ptr)
int PSPOskDialog::Shutdown(bool force)
{
u32 addr = *ptr == NULL ? 0 : (u8 *) *ptr - Memory::base;
p.Do(addr);
if (addr == 0)
*ptr = NULL;
else
*ptr = Memory::GetStruct<T>(addr);
if (status != SCE_UTILITY_STATUS_FINISHED && !force)
return SCE_ERROR_UTILITY_INVALID_STATUS;
PSPDialog::Shutdown();
return 0;
}
void PSPOskDialog::DoState(PointerWrap &p)
{
PSPDialog::DoState(p);
DoBasePointer(p, &oskParams);
p.Do(oskData);
p.Do(oskParams);
p.Do(oskDesc);
p.Do(oskIntext);
p.Do(oskOuttext);
@ -874,3 +874,8 @@ void PSPOskDialog::DoState(PointerWrap &p)
p.Do(inputChars);
p.DoMarker("PSPOskDialog");
}
pspUtilityDialogCommon *PSPOskDialog::GetCommonParam()
{
return &oskParams->base;
}

View File

@ -94,7 +94,7 @@ enum SceUtilityOskInputType
/**
* OSK Field data
*/
typedef struct _SceUtilityOskData
struct SceUtilityOskData
{
/** Unknown. Pass 0. */
int unk_00;
@ -111,19 +111,18 @@ typedef struct _SceUtilityOskData
/** Unknown. Pass 0. */
int unk_24;
/** Description text */
u32 descPtr;
PSPPointer<u16> desc;
/** Initial text */
u32 intextPtr;
PSPPointer<u16> intext;
// Length, in unsigned shorts, including the terminator.
u32 outtextlength;
/** Pointer to the output text */
u32 outtextPtr;
PSPPointer<u16> outtext;
/** Result. One of ::SceUtilityOskResult */
int result;
// Number of characters to allow, not including terminator (if less than outtextlength - 1.)
u32 outtextlimit;
} SceUtilityOskData;
};
// Parameters to sceUtilityOskInitStart
struct SceUtilityOskParams
@ -132,7 +131,7 @@ struct SceUtilityOskParams
// Number of fields.
int fieldCount;
// Pointer to an array of fields (see SceUtilityOskData.)
u32 fieldPtr;
PSPPointer<SceUtilityOskData> fields;
SceUtilityOskState state;
// Maybe just padding?
int unk_60;
@ -161,10 +160,13 @@ public:
virtual int Init(u32 oskPtr);
virtual int Update();
virtual int Shutdown(bool force = false);
virtual void DoState(PointerWrap &p);
virtual pspUtilityDialogCommon *GetCommonParam();
private:
void ConvertUCS2ToUTF8(std::string& _string, const u32 em_address);
void ConvertUCS2ToUTF8(std::string& _string, wchar_t* input);
void ConvertUCS2ToUTF8(std::string& _string, const PSPPointer<u16> em_address);
void ConvertUCS2ToUTF8(std::string& _string, const wchar_t *input);
void RenderKeyboard();
std::wstring CombinationString(bool isInput); // for Japanese, Korean
@ -172,10 +174,9 @@ private:
void RemoveKorean(); // for Korean character removal
u32 FieldMaxLength();
u32 GetIndex(const wchar_t* src, wchar_t ch);
int GetIndex(const wchar_t* src, wchar_t ch);
SceUtilityOskParams *oskParams;
SceUtilityOskData oskData;
PSPPointer<SceUtilityOskParams> oskParams;
std::string oskDesc;
std::string oskIntext;
std::string oskOuttext;

View File

@ -18,12 +18,15 @@
#include "PSPSaveDialog.h"
#include "../Util/PPGeDraw.h"
#include "../HLE/sceCtrl.h"
#include "../HLE/sceUtility.h"
#include "../Core/MemMap.h"
#include "../Config.h"
#include "Core/Reporting.h"
#include "Core/HW/MemoryStick.h"
#include "i18n/i18n.h"
const float FONT_SCALE = 0.55f;
PSPSaveDialog::PSPSaveDialog()
: PSPDialog()
, display(DS_NONE)
@ -58,6 +61,7 @@ int PSPSaveDialog::Init(int paramAddr)
switch (param.GetPspParam()->focus)
{
case SCE_UTILITY_SAVEDATA_FOCUS_NAME:
// TODO: This should probably force not using the list?
currentSelectedSave = 0;
break;
case SCE_UTILITY_SAVEDATA_FOCUS_FIRSTLIST:
@ -160,7 +164,7 @@ int PSPSaveDialog::Init(int paramAddr)
default:
{
ERROR_LOG_REPORT(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;
param.GetPspParam()->common.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.
@ -177,7 +181,7 @@ int PSPSaveDialog::Init(int paramAddr)
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,"result : %d",param.GetPspParam()->common.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);
@ -219,7 +223,21 @@ const std::string PSPSaveDialog::GetSelectedSaveDirName()
return param.GetSaveDirName(param.GetPspParam());
// Intentional fallthrough when saveName not valid.
// TODO: Maybe also WRITEDATA/READDATA/MAKEDATA/DELETEDATA/SINGLEDELETE?
// TODO: Test these to see what they use for the filename.
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE:
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATA:
case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE:
case SCE_UTILITY_SAVEDATA_TYPE_READDATA:
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE:
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA:
case SCE_UTILITY_SAVEDATA_TYPE_ERASESECURE:
case SCE_UTILITY_SAVEDATA_TYPE_ERASE:
case SCE_UTILITY_SAVEDATA_TYPE_DELETEDATA:
if (param.GetSaveDirName(param.GetPspParam(), currentSelectedSave) == "<>")
return param.GetSaveDirName(param.GetPspParam());
// Intentional fallthrough when saveName not valid.
// TODO: Maybe also SINGLEDELETE/etc?
default:
return param.GetSaveDirName(param.GetPspParam(), currentSelectedSave);
@ -227,45 +245,69 @@ const std::string PSPSaveDialog::GetSelectedSaveDirName()
}
}
void PSPSaveDialog::DisplayBanner(int which)
{
I18NCategory *d = GetI18NCategory("Dialog");
PPGeDrawRect(0, 0, 480, 23, CalcFadedColor(0x65636358));
const char *title;
switch (which)
{
case DB_SAVE:
title = d->T("Save");
break;
case DB_LOAD:
title = d->T("Load");
break;
case DB_DELETE:
title = d->T("Delete");
break;
default:
title = "";
break;
}
// TODO: Draw a hexagon icon
PPGeDrawText(title, 30, 11, PPGE_ALIGN_VCENTER, 0.6f, CalcFadedColor(0xFFFFFFFF));
}
void PSPSaveDialog::DisplaySaveList(bool canMove)
{
int displayCount = 0;
for(int i = 0; i < param.GetFilenameCount(); i++)
for (int i = 0; i < param.GetFilenameCount(); i++)
{
int textureColor = CalcFadedColor(0xFFFFFFFF);
if(param.GetFileInfo(i).size == 0 && param.GetFileInfo(i).textureData == 0)
if (param.GetFileInfo(i).size == 0 && param.GetFileInfo(i).textureData == 0)
textureColor = CalcFadedColor(0xFF777777);
// Calc save image position on screen
float w = 150;
float w = 144;
float h = 80;
float x = 20;
if(displayCount != currentSelectedSave) {
w = 80;
h = 40;
x = 55;
float x = 27;
if (displayCount != currentSelectedSave) {
w = 81;
h = 45;
x = 58.5f;
}
float y = 96;
if(displayCount < currentSelectedSave)
y -= 10 + 40 * (currentSelectedSave - displayCount );
else if(displayCount > currentSelectedSave)
y += 91 + 40 * (displayCount - currentSelectedSave - 1);
float y = 97;
if (displayCount < currentSelectedSave)
y -= 13 + 45 * (currentSelectedSave - displayCount);
else if (displayCount > currentSelectedSave)
y += 48 + 45 * (displayCount - currentSelectedSave);
int tw = 256;
int th = 256;
if(param.GetFileInfo(i).textureData != 0) {
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);
PPGeDrawImage(x, y, w, h, 0, 0, 1, 1, tw, th, textureColor);
PPGeSetDefaultTexture();
displayCount++;
}
if(canMove) {
if (canMove) {
if (IsButtonPressed(CTRL_UP) && currentSelectedSave > 0)
currentSelectedSave--;
else if (IsButtonPressed(CTRL_DOWN) && currentSelectedSave < (param.GetFilenameCount()-1))
@ -277,33 +319,33 @@ void PSPSaveDialog::DisplaySaveIcon()
{
int textureColor = CalcFadedColor(0xFFFFFFFF);
if(param.GetFileInfo(currentSelectedSave).size == 0)
if (param.GetFileInfo(currentSelectedSave).size == 0)
textureColor = CalcFadedColor(0xFF777777);
// Calc save image position on screen
float w = 150;
float w = 144;
float h = 80;
float x = 20;
float y = 80;
float x = 27;
float y = 97;
int tw = 256;
int th = 256;
if(param.GetFileInfo(currentSelectedSave).textureData != 0) {
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)
if (param.GetFileInfo(currentSelectedSave).textureData != 0)
PPGeSetDefaultTexture();
}
void PSPSaveDialog::DisplaySaveDataInfo1()
{
if(param.GetFileInfo(currentSelectedSave).size == 0) {
if (param.GetFileInfo(currentSelectedSave).size == 0) {
I18NCategory *d = GetI18NCategory("Dialog");
PPGeDrawText(d->T("New Save"), 180, 100, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(d->T("New Save"), 180, 136, PPGE_ALIGN_VCENTER, FONT_SCALE, CalcFadedColor(0xFFFFFFFF));
} else {
char title[512];
char time[512];
@ -312,10 +354,10 @@ void PSPSaveDialog::DisplaySaveDataInfo1()
char am_pm[] = "AM";
char hour_time[10] ;
int hour = param.GetFileInfo(currentSelectedSave).modif_time.tm_hour ;
int min = param.GetFileInfo(currentSelectedSave).modif_time.tm_min ;
if (g_Config.itimeformat) {
if( hour > 12 ) {
int hour = param.GetFileInfo(currentSelectedSave).modif_time.tm_hour;
int min = param.GetFileInfo(currentSelectedSave).modif_time.tm_min;
if (g_Config.itimeformat == PSP_SYSTEMPARAM_TIME_FORMAT_12HR) {
if (hour > 12) {
strcpy(am_pm, "PM");
hour -= 12;
}
@ -323,42 +365,51 @@ void PSPSaveDialog::DisplaySaveDataInfo1()
} else
snprintf(hour_time,10,"%02d:%02d", hour, min);
snprintf(title,512,"%s", param.GetFileInfo(currentSelectedSave).title);
snprintf(time,512,"%02d/%02d/%d %s %lld KB"
, param.GetFileInfo(currentSelectedSave).modif_time.tm_mday
, param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1
, param.GetFileInfo(currentSelectedSave).modif_time.tm_year + 1900
, hour_time
, param.GetFileInfo(currentSelectedSave).size / 1024
);
snprintf(saveTitle,512,"%s", param.GetFileInfo(currentSelectedSave).saveTitle);
snprintf(saveDetail,512,"%s", param.GetFileInfo(currentSelectedSave).saveDetail);
snprintf(title, 512, "%s", param.GetFileInfo(currentSelectedSave).title);
int day = param.GetFileInfo(currentSelectedSave).modif_time.tm_mday;
int month = param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1;
int year = param.GetFileInfo(currentSelectedSave).modif_time.tm_year + 1900;
s64 sizeK = param.GetFileInfo(currentSelectedSave).size / 1024;
switch (g_Config.iDateFormat) {
case PSP_SYSTEMPARAM_DATE_FORMAT_DDMMYYYY:
snprintf(time, 512, "%02d/%02d/%d %s %lld KB", day, month, year, hour_time, sizeK);
break;
case PSP_SYSTEMPARAM_DATE_FORMAT_MMDDYYYY:
snprintf(time, 512, "%02d/%02d/%d %s %lld KB", month, day, year, hour_time, sizeK);
break;
case PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD:
// fall through
default:
snprintf(time, 512, "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK);
}
snprintf(saveTitle, 512, "%s", param.GetFileInfo(currentSelectedSave).saveTitle);
snprintf(saveDetail, 512, "%s", param.GetFileInfo(currentSelectedSave).saveDetail);
PPGeDrawRect(180, 139, 980, 140, CalcFadedColor(0xFFFFFFFF));
PPGeDrawRect(180, 136, 980, 137, CalcFadedColor(0xFFFFFFFF));
std::string titleTxt = title;
std::string timeTxt = time;
std::string saveTitleTxt = saveTitle;
std::string saveDetailTxt = saveDetail;
PPGeDrawText(titleTxt.c_str(), 180, 120, PPGE_ALIGN_LEFT, 0.6f, CalcFadedColor(0xFFC0C0C0));
PPGeDrawText(timeTxt.c_str(), 180, 141, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(saveTitleTxt.c_str(), 175, 163, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(saveDetailTxt.c_str(), 175, 185, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(titleTxt.c_str(), 180, 136, PPGE_ALIGN_BOTTOM, 0.6f, CalcFadedColor(0xFFC0C0C0));
PPGeDrawText(timeTxt.c_str(), 180, 137, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(saveTitleTxt.c_str(), 175, 159, PPGE_ALIGN_LEFT, FONT_SCALE, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(saveDetailTxt.c_str(), 175, 181, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
}
}
void PSPSaveDialog::DisplaySaveDataInfo2()
{
if(param.GetFileInfo(currentSelectedSave).size == 0) {
if (param.GetFileInfo(currentSelectedSave).size == 0) {
} else {
char txt[1024];
char date[256];
char am_pm[] = "AM";
char hour_time[10] ;
int hour = param.GetFileInfo(currentSelectedSave).modif_time.tm_hour ;
int min = param.GetFileInfo(currentSelectedSave).modif_time.tm_min ;
if (g_Config.itimeformat) {
if( hour > 12 ) {
int hour = param.GetFileInfo(currentSelectedSave).modif_time.tm_hour;
int min = param.GetFileInfo(currentSelectedSave).modif_time.tm_min;
if (g_Config.itimeformat == PSP_SYSTEMPARAM_TIME_FORMAT_12HR) {
if (hour > 12) {
strcpy(am_pm, "PM");
hour -= 12;
}
@ -366,38 +417,74 @@ void PSPSaveDialog::DisplaySaveDataInfo2()
} else
snprintf(hour_time,10,"%02d:%02d", hour, min);
snprintf(txt,1024,"%s\n%02d/%02d/%d %s\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
, hour_time
, param.GetFileInfo(currentSelectedSave).size / 1024
);
const char *saveTitle = param.GetFileInfo(currentSelectedSave).saveTitle;
int day = param.GetFileInfo(currentSelectedSave).modif_time.tm_mday;
int month = param.GetFileInfo(currentSelectedSave).modif_time.tm_mon + 1;
int year = param.GetFileInfo(currentSelectedSave).modif_time.tm_year + 1900;
s64 sizeK = param.GetFileInfo(currentSelectedSave).size / 1024;
switch (g_Config.iDateFormat) {
case PSP_SYSTEMPARAM_DATE_FORMAT_DDMMYYYY:
snprintf(date, 256, "%02d/%02d/%d", day, month, year);
break;
case PSP_SYSTEMPARAM_DATE_FORMAT_MMDDYYYY:
snprintf(date, 256, "%02d/%02d/%d", month, day, year);
break;
case PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD:
// fall through
default:
snprintf(date, 256, "%d/%02d/%02d", year, month, day);
}
snprintf(txt, 1024, "%s\n%s %s\n%lld KB", saveTitle, date, hour_time, sizeK);
std::string saveinfoTxt = txt;
PPGeDrawText(saveinfoTxt.c_str(), 10, 180, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(saveinfoTxt.c_str(), 8, 200, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
}
}
void PSPSaveDialog::DisplayConfirmationYesNo(std::string text)
void PSPSaveDialog::DisplayMessage(std::string text, bool hasYesNo)
{
I18NCategory *d = GetI18NCategory("Dialog");
PPGeDrawRect(200, 85, 460, 86, CalcFadedColor(0xFFFFFFFF));
PPGeDrawRect(200, 160, 460, 161, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(text.c_str(), 320, 100, PPGE_ALIGN_HCENTER, 0.5f, 0xFFFFFFFF);
PPGeDrawText(d->T("Yes"), 275, 130, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(yesnoChoice == 1?0xFF0000FF:0xFFFFFFFF));
PPGeDrawText(d->T("No"), 340, 130, PPGE_ALIGN_LEFT, 0.5f, CalcFadedColor(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)
{
PPGeDrawRect(200, 100, 460, 101, CalcFadedColor(0xFFFFFFFF));
PPGeDrawRect(200, 140, 460, 141, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(text.c_str(), 320, 110, PPGE_ALIGN_HCENTER, 0.5f, CalcFadedColor(0xFFFFFFFF));
const float WRAP_WIDTH = 254.0f;
float y = 136.0f, h;
int n;
PPGeMeasureText(0, &h, &n, text.c_str(), FONT_SCALE, PPGE_LINE_WRAP_WORD, WRAP_WIDTH);
float h2 = h * (float)n / 2.0f;
if (hasYesNo)
{
I18NCategory *d = GetI18NCategory("Dialog");
const char *choiceText;
u32 yesColor, noColor;
float x, w;
if (yesnoChoice == 1) {
choiceText = d->T("Yes");
x = 302.0f;
yesColor = 0xFF0FFFFF;
noColor = 0xFFFFFFFF;
}
else {
choiceText = d->T("No");
x = 366.0f;
yesColor = 0xFFFFFFFF;
noColor = 0xFF0FFFFF;
}
PPGeMeasureText(&w, &h, 0, choiceText, FONT_SCALE);
w = w / 2.0f + 5.5f;
h /= 2.0f;
float y2 = y + h2 + 4.0f;
h2 += h + 4.0f;
y = 132.0f - h;
PPGeDrawRect(x - w, y2 - h, x + w, y2 + h, CalcFadedColor(0x6DCFCFCF));
PPGeDrawText(d->T("Yes"), 302.0f, y2, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(yesColor));
PPGeDrawText(d->T("No"), 366.0f, y2, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(noColor));
if (IsButtonPressed(CTRL_LEFT) && yesnoChoice == 0) {
yesnoChoice = 1;
}
else if (IsButtonPressed(CTRL_RIGHT) && yesnoChoice == 1) {
yesnoChoice = 0;
}
}
PPGeDrawTextWrapped(text.c_str(), 334.0f, y, WRAP_WIDTH, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0xFFFFFFFF));
float sy = 122.0f - h2, ey = 150.0f + h2;
PPGeDrawRect(202.0f, sy, 466.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF));
PPGeDrawRect(202.0f, ey, 466.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF));
}
void PSPSaveDialog::DisplayTitle(std::string name)
@ -405,22 +492,6 @@ void PSPSaveDialog::DisplayTitle(std::string name)
PPGeDrawText(name.c_str(), 10, 10, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
}
void PSPSaveDialog::DisplayEnterBack()
{
I18NCategory *d = GetI18NCategory("Dialog");
PPGeDrawImage(cancelButtonImg, 180, 257, 11, 11, 0, CalcFadedColor(0xFFFFFFFF));
PPGeDrawImage(okButtonImg, 270, 257, 11, 11, 0, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(d->T("Back"), 195, 255, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(d->T("Enter"), 285, 255, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
}
void PSPSaveDialog::DisplayBack()
{
I18NCategory *d = GetI18NCategory("Dialog");
PPGeDrawImage(cancelButtonImg, 180, 257, 11, 11, 0, CalcFadedColor(0xFFFFFFFF));
PPGeDrawText(d->T("Back"), 195, 255, PPGE_ALIGN_LEFT, 0.45f, CalcFadedColor(0xFFFFFFFF));
}
int PSPSaveDialog::Update()
{
switch (status) {
@ -446,7 +517,7 @@ int PSPSaveDialog::Update()
cancelButtonImg = I_CROSS;
okButtonFlag = CTRL_CIRCLE;
cancelButtonFlag = CTRL_CROSS;
if (param.GetPspParam()->buttonSwap == 1) {
if (param.GetPspParam()->common.buttonSwap == 1) {
okButtonImg = I_CROSS;
cancelButtonImg = I_CIRCLE;
okButtonFlag = CTRL_CROSS;
@ -459,15 +530,15 @@ int PSPSaveDialog::Update()
{
case DS_SAVE_LIST_CHOICE:
StartDraw();
// TODO : use focus param for selected save by default
DisplaySaveList();
DisplaySaveDataInfo1();
DisplayEnterBack();
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
DisplayBanner(DB_SAVE);
if (IsButtonPressed(cancelButtonFlag)) {
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
StartFade(false);
} else if (IsButtonPressed(okButtonFlag)) {
// Save exist, ask user confirm
@ -491,11 +562,13 @@ int PSPSaveDialog::Update()
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayConfirmationYesNo(d->T("Confirm Save", "Do you want to save this data?"));
DisplayMessage(d->T("Confirm Save", "Do you want to save this data?"), true);
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
DisplayBanner(DB_SAVE);
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag) || (IsButtonPressed(okButtonFlag) && yesnoChoice == 0)) {
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
StartFade(false);
} else if (IsButtonPressed(okButtonFlag)) {
display = DS_SAVE_SAVING;
@ -516,14 +589,16 @@ int PSPSaveDialog::Update()
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayConfirmationYesNo(d->T("Do you want to overwrite the data?"));
DisplayMessage(d->T("Do you want to overwrite the data?"), true);
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
DisplayBanner(DB_SAVE);
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag) || (IsButtonPressed(okButtonFlag) && yesnoChoice == 0)) {
if (param.GetPspParam()->mode != SCE_UTILITY_SAVEDATA_TYPE_SAVE)
display = DS_SAVE_LIST_CHOICE;
else {
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
StartFade(false);
}
} else if (IsButtonPressed(okButtonFlag)) {
@ -548,7 +623,9 @@ int PSPSaveDialog::Update()
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayInfo(d->T("Saving","Saving\nPlease Wait..."));
DisplayMessage(d->T("Saving","Saving\nPlease Wait..."));
DisplayBanner(DB_SAVE);
EndDraw();
break;
@ -557,12 +634,14 @@ int PSPSaveDialog::Update()
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayBack();
DisplayInfo(d->T("Save completed"));
DisplayMessage(d->T("Save completed"));
DisplayButtons(DS_BUTTON_CANCEL);
DisplayBanner(DB_SAVE);
if (IsButtonPressed(cancelButtonFlag)) {
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_SUCCESS;
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_SUCCESS;
// Set the save to use for autosave and autoload
param.SetSelectedSave(param.GetFileInfo(currentSelectedSave).idx);
StartFade(false);
@ -577,9 +656,11 @@ int PSPSaveDialog::Update()
DisplaySaveList();
DisplaySaveDataInfo1();
DisplayEnterBack();
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
DisplayBanner(DB_LOAD);
if (IsButtonPressed(cancelButtonFlag)) {
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
StartFade(false);
} else if (IsButtonPressed(okButtonFlag)) {
display = DS_LOAD_LOADING;
@ -595,18 +676,20 @@ int PSPSaveDialog::Update()
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayConfirmationYesNo(d->T("ConfirmLoad", "Load this data?"));
DisplayMessage(d->T("ConfirmLoad", "Load this data?"), true);
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
DisplayBanner(DB_LOAD);
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag) || (IsButtonPressed(okButtonFlag) && yesnoChoice == 0)) {
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
StartFade(false);
} else if (IsButtonPressed(okButtonFlag)) {
display = DS_LOAD_LOADING;
if (param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave))
display = DS_LOAD_DONE;
else {
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
StartFade(false);
}
}
@ -619,7 +702,9 @@ int PSPSaveDialog::Update()
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayInfo(d->T("Loading","Loading\nPlease Wait..."));
DisplayMessage(d->T("Loading","Loading\nPlease Wait..."));
DisplayBanner(DB_LOAD);
EndDraw();
break;
@ -628,12 +713,14 @@ int PSPSaveDialog::Update()
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayBack();
DisplayInfo(d->T("Load completed"));
DisplayMessage(d->T("Load completed"));
DisplayButtons(DS_BUTTON_CANCEL);
DisplayBanner(DB_LOAD);
if (IsButtonPressed(cancelButtonFlag)) {
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_SUCCESS;
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_SUCCESS;
// Set the save to use for autosave and autoload
param.SetSelectedSave(param.GetFileInfo(currentSelectedSave).idx);
StartFade(false);
@ -644,12 +731,13 @@ int PSPSaveDialog::Update()
case DS_LOAD_NODATA:
StartDraw();
DisplayBack();
DisplayMessage(d->T("There is no data"));
DisplayInfo(d->T("There is no data"));
DisplayButtons(DS_BUTTON_CANCEL);
DisplayBanner(DB_LOAD);
if (IsButtonPressed(cancelButtonFlag)) {
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA;
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA;
StartFade(false);
}
@ -662,9 +750,11 @@ int PSPSaveDialog::Update()
DisplaySaveList();
DisplaySaveDataInfo1();
DisplayEnterBack();
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
DisplayBanner(DB_DELETE);
if (IsButtonPressed(cancelButtonFlag)) {
param.GetPspParam()->result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
param.GetPspParam()->common.result = SCE_UTILITY_DIALOG_RESULT_CANCEL;
StartFade(false);
} else if (IsButtonPressed(okButtonFlag)) {
yesnoChoice = 0;
@ -679,9 +769,13 @@ int PSPSaveDialog::Update()
DisplaySaveIcon();
DisplaySaveDataInfo2();
DisplayConfirmationYesNo(d->T("DeleteConfirm", " This save data will be deleted.\nAre you sure you want to continue?"));
DisplayMessage(d->T("DeleteConfirm",
"This save data will be deleted.\nAre you sure you want to continue?"),
true);
DisplayButtons(DS_BUTTON_OK | DS_BUTTON_CANCEL);
DisplayBanner(DB_DELETE);
DisplayEnterBack();
if (IsButtonPressed(cancelButtonFlag))
display = DS_DELETE_LIST_CHOICE;
else if (IsButtonPressed(okButtonFlag)) {
@ -702,16 +796,19 @@ int PSPSaveDialog::Update()
case DS_DELETE_DELETING:
StartDraw();
DisplayInfo(d->T("Deleting","Deleting\nPlease Wait..."));
DisplayMessage(d->T("Deleting","Deleting\nPlease Wait..."));
DisplayBanner(DB_DELETE);
EndDraw();
break;
case DS_DELETE_DONE:
StartDraw();
DisplayBack();
DisplayMessage(d->T("Delete completed"));
DisplayInfo(d->T("Delete completed"));
DisplayButtons(DS_BUTTON_CANCEL);
DisplayBanner(DB_DELETE);
if (IsButtonPressed(cancelButtonFlag)) {
if (param.GetFilenameCount() == 0)
@ -725,12 +822,13 @@ int PSPSaveDialog::Update()
case DS_DELETE_NODATA:
StartDraw();
DisplayBack();
DisplayMessage(d->T("There is no data"));
DisplayInfo(d->T("There is no data"));
DisplayButtons(DS_BUTTON_CANCEL);
DisplayBanner(DB_DELETE);
if (IsButtonPressed(cancelButtonFlag)) {
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA;
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA;
StartFade(false);
}
@ -744,36 +842,33 @@ int PSPSaveDialog::Update()
case SCE_UTILITY_SAVEDATA_TYPE_LOAD: // Only load and exit
case SCE_UTILITY_SAVEDATA_TYPE_AUTOLOAD:
if (param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave))
param.GetPspParam()->result = 0;
param.GetPspParam()->common.result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_LOAD_NO_DATA;
param.GetPspParam()->common.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(), GetSelectedSaveDirName()))
param.GetPspParam()->result = 0;
param.GetPspParam()->common.result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_SAVE_MS_NOSPACE;
param.GetPspParam()->common.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;
param.GetPspParam()->common.result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA;
param.GetPspParam()->common.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;
param.GetPspParam()->common.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;
param.GetPspParam()->common.result = param.GetFilesList(param.GetPspParam());
status = SCE_UTILITY_STATUS_FINISHED;
break;
case SCE_UTILITY_SAVEDATA_TYPE_GETSIZE:
@ -781,11 +876,11 @@ int PSPSaveDialog::Update()
bool result = param.GetSize(param.GetPspParam());
// TODO: According to JPCSP, should test/verify this part but seems edge casey.
if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_DRIVER_READY)
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_MEMSTICK;
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_MEMSTICK;
else if (result)
param.GetPspParam()->result = 0;
param.GetPspParam()->common.result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
status = SCE_UTILITY_STATUS_FINISHED;
}
break;
@ -793,39 +888,40 @@ int PSPSaveDialog::Update()
// TODO: This should probably actually delete something.
// For now, always say it couldn't be deleted.
WARN_LOG(HLE, "FAKE sceUtilitySavedata DELETEDATA: %s", param.GetPspParam()->saveName);
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_STATUS;
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_STATUS;
status = SCE_UTILITY_STATUS_FINISHED;
break;
//case SCE_UTILITY_SAVEDATA_TYPE_AUTODELETE:
case SCE_UTILITY_SAVEDATA_TYPE_SINGLEDELETE:
if (param.Delete(param.GetPspParam(), param.GetSelectedSave()))
param.GetPspParam()->result = 0;
param.GetPspParam()->common.result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA;
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_DELETE_NO_DATA;
status = SCE_UTILITY_STATUS_FINISHED;
break;
// TODO: Should reset the directory's other files.
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATA:
case SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE:
if (param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_MAKEDATASECURE))
param.GetPspParam()->result = 0;
param.GetPspParam()->common.result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
status = SCE_UTILITY_STATUS_FINISHED;
break;
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATA:
case SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE:
if (param.Save(param.GetPspParam(), GetSelectedSaveDirName(), param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_WRITEDATASECURE))
param.GetPspParam()->result = 0;
param.GetPspParam()->common.result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
status = SCE_UTILITY_STATUS_FINISHED;
break;
case SCE_UTILITY_SAVEDATA_TYPE_READDATA:
case SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE:
if (param.Load(param.GetPspParam(), GetSelectedSaveDirName(), currentSelectedSave, param.GetPspParam()->mode == SCE_UTILITY_SAVEDATA_TYPE_READDATASECURE))
param.GetPspParam()->result = 0;
param.GetPspParam()->common.result = 0;
else
param.GetPspParam()->result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; // not sure if correct code
param.GetPspParam()->common.result = SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA; // not sure if correct code
status = SCE_UTILITY_STATUS_FINISHED;
break;
default:
@ -842,14 +938,14 @@ int PSPSaveDialog::Update()
lastButtons = buttons;
if (status == SCE_UTILITY_STATUS_FINISHED)
Memory::Memcpy(requestAddr,&request,request.size);
Memory::Memcpy(requestAddr,&request,request.common.size);
return 0;
}
int PSPSaveDialog::Shutdown()
int PSPSaveDialog::Shutdown(bool force)
{
if (status != SCE_UTILITY_STATUS_FINISHED)
if (status != SCE_UTILITY_STATUS_FINISHED && !force)
return SCE_ERROR_UTILITY_INVALID_STATUS;
PSPDialog::Shutdown();
@ -872,9 +968,10 @@ void PSPSaveDialog::DoState(PointerWrap &p)
p.Do(requestAddr);
p.Do(currentSelectedSave);
p.Do(yesnoChoice);
p.Do(okButtonImg);
p.Do(cancelButtonImg);
p.Do(okButtonFlag);
p.Do(cancelButtonFlag);
p.DoMarker("PSPSaveDialog");
}
pspUtilityDialogCommon *PSPSaveDialog::GetCommonParam()
{
return &param.GetPspParam()->common;
}

View File

@ -30,9 +30,10 @@
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_PARAM (0x80110308)
#define SCE_UTILITY_SAVEDATA_ERROR_LOAD_INTERNAL (0x8011030b)
#define SCE_UTILITY_SAVEDATA_ERROR_RW_NO_MEMSTICK (0x80110321)
#define SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA (0x80110327)
#define SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_STATUS (0x8011032c)
#define SCE_UTILITY_SAVEDATA_ERROR_RW_NO_MEMSTICK (0x80110321)
#define SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA (0x80110327)
#define SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS (0x80110328)
#define SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_STATUS (0x8011032c)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_NO_MS (0x80110381)
#define SCE_UTILITY_SAVEDATA_ERROR_SAVE_EJECT_MS (0x80110382)
@ -68,20 +69,19 @@ public:
virtual int Init(int paramAddr);
virtual int Update();
virtual int Shutdown();
virtual int Shutdown(bool force = false);
virtual void DoState(PointerWrap &p);
virtual pspUtilityDialogCommon *GetCommonParam();
private :
void DisplayBanner(int which);
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);
void DisplayMessage(std::string text, bool hasYesNo = false);
const std::string GetSelectedSaveDirName();
enum DisplayState
@ -107,6 +107,14 @@ private :
DS_DELETE_NODATA
};
enum DialogBanner
{
DB_NONE,
DB_SAVE,
DB_LOAD,
DB_DELETE
};
DisplayState display;
SavedataParam param;
@ -115,10 +123,5 @@ private :
int currentSelectedSave;
int yesnoChoice;
int okButtonImg;
int cancelButtonImg;
int okButtonFlag;
int cancelButtonFlag;
};

View File

@ -15,19 +15,21 @@
// 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 "../HLE/sceChnnlsv.h"
#include "Core/Reporting.h"
#include "Core/Dialog/SavedataParam.h"
#include "Core/Dialog/PSPSaveDialog.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceChnnlsv.h"
#include "Core/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";
#include "image/png_load.h"
static const std::string ICON0_FILENAME = "ICON0.PNG";
static const std::string ICON1_FILENAME = "ICON1.PMF";
static const std::string PIC1_FILENAME = "PIC1.PNG";
static const std::string SND0_FILENAME = "SND0.AT3";
static const std::string SFO_FILENAME = "PARAM.SFO";
std::string savePath = "ms0:/PSP/SAVEDATA/";
@ -78,13 +80,6 @@ namespace
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())
@ -294,7 +289,7 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save
// SAVE PARAM.SFO
ParamSFOData sfoFile;
std::string sfopath = dirPath+"/"+sfoName;
std::string sfopath = dirPath+"/" + SFO_FILENAME;
PSPFileInfo sfoInfo = pspFileSystem.GetFileInfo(sfopath);
if(sfoInfo.exists) // Read old sfo if exist
{
@ -316,40 +311,43 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save
sfoFile.SetValue("SAVEDATA_DIRECTORY", GetSaveDir(param, saveDirName), 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)
if (secureMode)
{
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;
fName += FILE_LIST_ITEM_SIZE;
}
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 (fName + 13 <= (char*)tmpData + FILE_LIST_TOTAL_SIZE)
snprintf(fName, 13, "%s",GetFileName(param).c_str());
if (fName + 13 + 16 <= (char*)tmpData + FILE_LIST_TOTAL_SIZE)
memcpy(fName+13, cryptedHash, 16);
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;
fName += FILE_LIST_ITEM_SIZE;
}
if (fName + 13 <= (char*)tmpData + FILE_LIST_TOTAL_SIZE)
snprintf(fName, 13, "%s",GetFileName(param).c_str());
if (fName + 13 + 16 <= (char*)tmpData + FILE_LIST_TOTAL_SIZE)
memcpy(fName+13, cryptedHash, 16);
}
sfoFile.SetValue("SAVEDATA_FILE_LIST", tmpData, FILE_LIST_TOTAL_SIZE, FILE_LIST_TOTAL_SIZE);
delete[] tmpData;
}
sfoFile.SetValue("SAVEDATA_FILE_LIST", tmpData, FILE_LIST_TOTAL_SIZE, FILE_LIST_TOTAL_SIZE);
delete[] tmpData;
// Init param with 0. This will be used to detect crypted save or not on loading
tmpData = new u8[128];
u8 *tmpData = new u8[128];
memset(tmpData, 0, 128);
sfoFile.SetValue("SAVEDATA_PARAMS", tmpData, 128, 128);
delete[] tmpData;
@ -363,7 +361,7 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save
{
int offset = sfoFile.GetDataOffset(sfoData,"SAVEDATA_PARAMS");
if(offset >= 0)
UpdateHash(sfoData, sfoSize, offset, (param->key[0]?3:1));
UpdateHash(sfoData, (int)sfoSize, offset, (param->key[0] ? 3 : 1));
}
WritePSPFile(sfopath, sfoData, (SceSize)sfoSize);
delete[] sfoData;
@ -409,21 +407,21 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save
if (param->icon0FileData.buf)
{
u8* data_ = (u8*)Memory::GetPointer(param->icon0FileData.buf);
std::string icon0path = dirPath+"/"+icon0Name;
std::string icon0path = dirPath + "/" + ICON0_FILENAME;
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;
std::string icon1path = dirPath + "/" + ICON1_FILENAME;
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;
std::string pic1path = dirPath + "/" + PIC1_FILENAME;
WritePSPFile(pic1path, data_, param->pic1FileData.bufSize);
}
@ -431,24 +429,10 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save
if (param->snd0FileData.buf)
{
u8* data_ = (u8*)Memory::GetPointer(param->snd0FileData.buf);
std::string snd0path = dirPath+"/"+snd0Name;
std::string snd0path = dirPath + "/" + SND0_FILENAME;
WritePSPFile(snd0path, data_, param->snd0FileData.bufSize);
}
// Save Encryption Data
{
EncryptFileInfo encryptInfo;
SceSize 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;
}
@ -485,7 +469,7 @@ bool SavedataParam::Load(SceUtilitySavedataParam *param, const std::string &save
strncpy(param->saveName, saveDirName.c_str(), 20);
ParamSFOData sfoFile;
std::string sfopath = dirPath+"/"+sfoName;
std::string sfopath = dirPath+"/" + SFO_FILENAME;
PSPFileInfo sfoInfo = pspFileSystem.GetFileInfo(sfopath);
if(sfoInfo.exists) // Read sfo
{
@ -854,51 +838,126 @@ bool SavedataParam::GetList(SceUtilitySavedataParam *param)
return true;
}
bool SavedataParam::GetFilesList(SceUtilitySavedataParam *param)
int SavedataParam::GetFilesList(SceUtilitySavedataParam *param)
{
if (!param) {
return false;
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_STATUS;
}
// TODO : Need to be checked against more game
u32 dataAddr = param->fileListAddr;
int foundFiles = 0;
if (Memory::IsValidAddress(dataAddr)) {
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);
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;
if (Memory::IsValidAddress(curFileInfoAddr)) {
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) {
bool isCrypted = IsSaveEncrypted(param, GetSaveDirName(param, 0));
Memory::Write_U32(0x21FF, curFileInfoAddr+0);
if(isCrypted) // Crypted save are 16 bytes bigger
Memory::Write_U64(info.size - 0x10, curFileInfoAddr+8);
else
Memory::Write_U64(info.size, curFileInfoAddr+8);
if (!param->fileList.Valid()) {
ERROR_LOG_REPORT(HLE, "SavedataParam::GetFilesList(): bad fileList address %08x", param->fileList.ptr);
// Should crash.
return -1;
}
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++;
}
auto &fileList = param->fileList;
if (fileList->secureEntries.Valid() && fileList->maxSecureEntries > 99) {
ERROR_LOG_REPORT(HLE, "SavedataParam::GetFilesList(): too many secure entries, %d", fileList->maxSecureEntries);
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS;
}
if (fileList->normalEntries.Valid() && fileList->maxNormalEntries > 8192) {
ERROR_LOG_REPORT(HLE, "SavedataParam::GetFilesList(): too many normal entries, %d", fileList->maxNormalEntries);
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS;
}
if (fileList->systemEntries.Valid() && fileList->maxSystemEntries > 5) {
ERROR_LOG_REPORT(HLE, "SavedataParam::GetFilesList(): too many system entries, %d", fileList->maxSystemEntries);
return SCE_UTILITY_SAVEDATA_ERROR_RW_BAD_PARAMS;
}
std::string dirPath = savePath + GetGameName(param) + GetSaveName(param);
if (!pspFileSystem.GetFileInfo(dirPath).exists) {
DEBUG_LOG(HLE, "SavedataParam::GetFilesList(): directory %s does not exist", dirPath.c_str());
return SCE_UTILITY_SAVEDATA_ERROR_RW_NO_DATA;
}
// Even if there are no files, initialize to 0.
fileList->resultNumSecureEntries = 0;
fileList->resultNumNormalEntries = 0;
fileList->resultNumSystemEntries = 0;
// We need PARAMS.SFO's SAVEDATA_FILE_LIST to determine which entries are secure.
PSPFileInfo sfoFileInfo = pspFileSystem.GetFileInfo(dirPath + "/" + SFO_FILENAME);
std::set<std::string> secureFilenames;
// TODO: Error code if not?
if (sfoFileInfo.exists) {
ParamSFOData sfoFile;
size_t sfoSize = (size_t)sfoFileInfo.size;
u8 *sfoData = new u8[sfoSize];
if (ReadPSPFile(dirPath + "/" + SFO_FILENAME, &sfoData, sfoSize, NULL)){
sfoFile.ReadSFO(sfoData, sfoSize);
}
delete[] sfoData;
u32 sfoFileListSize = 0;
char *sfoFileList = (char *)sfoFile.GetValueData("SAVEDATA_FILE_LIST", &sfoFileListSize);
const int FILE_LIST_ITEM_SIZE = 13 + 16 + 3;
const int FILE_LIST_COUNT_MAX = 99;
// Filenames are 13 bytes long at most. Add a NULL so there's no surprises.
char temp[14];
temp[13] = '\0';
for (u32 i = 0; i < FILE_LIST_COUNT_MAX; ++i) {
// Ends at a NULL filename.
if (i * FILE_LIST_ITEM_SIZE >= sfoFileListSize || sfoFileList[i * FILE_LIST_ITEM_SIZE] == '\0') {
break;
}
strncpy(temp, &sfoFileList[i * FILE_LIST_ITEM_SIZE], 13);
secureFilenames.insert(temp);
}
}
// TODO : verify if return true if at least 1 file found or only if all found
return foundFiles > 0;
// Does not list directories, nor recurse into them, and ignores files not ALL UPPERCASE.
auto files = pspFileSystem.GetDirListing(dirPath);
for (auto file = files.begin(), end = files.end(); file != end; ++file) {
if (file->type == FILETYPE_DIRECTORY) {
continue;
}
// TODO: What are the exact rules? It definitely skips lowercase, and allows FILE or FILE.EXT.
if (file->name.find_first_of("abcdefghijklmnopqrstuvwxyz") != file->name.npos) {
DEBUG_LOG(HLE, "SavedataParam::GetFilesList(): skipping file %s with lowercase", file->name.c_str());
continue;
}
bool isSystemFile = file->name == ICON0_FILENAME || file->name == ICON1_FILENAME || file->name == PIC1_FILENAME;
isSystemFile = isSystemFile || file->name == SND0_FILENAME || file->name == SFO_FILENAME;
SceUtilitySavedataFileListEntry *entry = NULL;
int sizeOffset = 0;
if (isSystemFile) {
if (fileList->systemEntries.Valid() && fileList->resultNumSystemEntries < fileList->maxSystemEntries) {
entry = &fileList->systemEntries[fileList->resultNumSystemEntries++];
}
} else if (secureFilenames.find(file->name) != secureFilenames.end()) {
if (fileList->secureEntries.Valid() && fileList->resultNumSecureEntries < fileList->maxSecureEntries) {
entry = &fileList->secureEntries[fileList->resultNumSecureEntries++];
}
// Secure files are slightly bigger.
bool isCrypted = IsSaveEncrypted(param, GetSaveDirName(param, 0));
if (isCrypted) {
sizeOffset = -0x10;
}
} else {
if (fileList->normalEntries.Valid() && fileList->resultNumNormalEntries < fileList->maxNormalEntries) {
entry = &fileList->normalEntries[fileList->resultNumNormalEntries++];
}
}
// Out of space for this file in the list.
if (entry == NULL) {
continue;
}
entry->st_mode = 0x21FF;
entry->st_size = file->size + sizeOffset;
// TODO: ctime, atime, mtime
// TODO: Probably actually 13 + 3 pad...
strncpy(entry->name, file->name.c_str(), 16);
entry->name[15] = '\0';
}
return 0;
}
bool SavedataParam::GetSize(SceUtilitySavedataParam *param)
@ -1006,6 +1065,10 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param)
int realCount = 0;
for (int i = 0; i < saveDataListCount; i++)
{
// TODO: Maybe we should fill the list with existing files instead?
if (strcmp(saveNameListData[i], "<>") == 0)
continue;
DEBUG_LOG(HLE,"Name : %s",saveNameListData[i]);
std::string fileDataPath = savePath+GetGameName(param) + saveNameListData[i] + "/" + param->fileName;
@ -1106,7 +1169,7 @@ void SavedataParam::SetFileInfo(SaveFileInfo &saveInfo, PSPFileInfo &info, std::
// 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;
std::string fileDataPath2 = savePath + GetGameName(pspParam) + saveName + "/" + ICON0_FILENAME;
PSPFileInfo info2 = pspFileSystem.GetFileInfo(fileDataPath2);
if (info2.exists)
{
@ -1117,7 +1180,7 @@ void SavedataParam::SetFileInfo(SaveFileInfo &saveInfo, PSPFileInfo &info, std::
}
// Load info in PARAM.SFO
fileDataPath2 = savePath + GetGameName(pspParam) + saveName + "/" + sfoName;
fileDataPath2 = savePath + GetGameName(pspParam) + saveName + "/" + SFO_FILENAME;
info2 = pspFileSystem.GetFileInfo(fileDataPath2);
if (info2.exists)
{
@ -1242,7 +1305,7 @@ int SavedataParam::GetFirstDataSave()
{
if (saveDataList[i].size != 0)
{
idx = i;;
idx = i;
break;
}
}
@ -1322,7 +1385,7 @@ bool SavedataParam::IsSaveEncrypted(SceUtilitySavedataParam* param, const std::s
ParamSFOData sfoFile;
std::string dirPath = GetSaveFilePath(param, GetSaveDir(param, saveDirName));
std::string sfopath = dirPath+"/"+sfoName;
std::string sfopath = dirPath + "/" + SFO_FILENAME;
PSPFileInfo sfoInfo = pspFileSystem.GetFileInfo(sfopath);
if(sfoInfo.exists) // Read sfo
{

View File

@ -17,8 +17,13 @@
#pragma once
#include "../HLE/sceKernel.h"
#include "../System.h"
#include "Core/HLE/sceKernel.h"
#include "Core/HLE/sceRtc.h"
#include "Core/System.h"
#include "Core/Dialog/PSPDialog.h"
#undef st_ctime
#undef st_atime
#undef st_mtime
enum SceUtilitySavedataType
{
@ -50,10 +55,10 @@ enum SceUtilitySavedataType
enum SceUtilitySavedataFocus
{
SCE_UTILITY_SAVEDATA_FOCUS_NAME = 0, // specified by saveName[]
SCE_UTILITY_SAVEDATA_FOCUS_FIRSTLIST = 1, // first listed (on screen or of all)?
SCE_UTILITY_SAVEDATA_FOCUS_LASTLIST = 2, // last listed (on screen or of all)?
SCE_UTILITY_SAVEDATA_FOCUS_FIRSTLIST = 1, // first listed (on screen or of all?)
SCE_UTILITY_SAVEDATA_FOCUS_LASTLIST = 2, // last listed (on screen or of all?)
SCE_UTILITY_SAVEDATA_FOCUS_LATEST = 3, // latest by modification date (first if none)
SCE_UTILITY_SAVEDATA_FOCUS_OLDEST = 4, // doldest by modification date (first if none)
SCE_UTILITY_SAVEDATA_FOCUS_OLDEST = 4, // oldest by modification date (first if none)
SCE_UTILITY_SAVEDATA_FOCUS_FIRSTDATA = 5, // first non-empty (first if none)
SCE_UTILITY_SAVEDATA_FOCUS_LASTDATA = 6, // last non-empty (first if none)
SCE_UTILITY_SAVEDATA_FOCUS_FIRSTEMPTY = 7, // first empty (what if no empty?)
@ -100,18 +105,33 @@ struct PspUtilitySavedataSizeInfo {
char overwriteString[8];
};
struct SceUtilitySavedataFileListEntry
{
int st_mode;
u64 st_size;
ScePspDateTime st_ctime;
ScePspDateTime st_atime;
ScePspDateTime st_mtime;
char name[16];
};
struct SceUtilitySavedataFileListInfo
{
u32 maxSecureEntries;
u32 maxNormalEntries;
u32 maxSystemEntries;
u32 resultNumSecureEntries;
u32 resultNumNormalEntries;
u32 resultNumSystemEntries;
PSPPointer<SceUtilitySavedataFileListEntry> secureEntries;
PSPPointer<SceUtilitySavedataFileListEntry> normalEntries;
PSPPointer<SceUtilitySavedataFileListEntry> systemEntries;
};
// 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];
pspUtilityDialogCommon common;
int mode; // 0 to load, 1 to save
int bind;
@ -159,7 +179,7 @@ struct SceUtilitySavedataParam
u32 idListAddr;
// Function 12 FILES
u32 fileListAddr;
PSPPointer<SceUtilitySavedataFileListInfo> fileList;
// Function 22 GETSIZES
u32 sizeAddr;
@ -216,7 +236,7 @@ public:
bool Load(SceUtilitySavedataParam* param, const std::string &saveDirName, int saveId = -1, bool secureMode = true);
bool GetSizes(SceUtilitySavedataParam* param);
bool GetList(SceUtilitySavedataParam* param);
bool GetFilesList(SceUtilitySavedataParam* param);
int GetFilesList(SceUtilitySavedataParam* param);
bool GetSize(SceUtilitySavedataParam* param);
bool IsSaveEncrypted(SceUtilitySavedataParam* param, const std::string &saveDirName);

View File

@ -15,7 +15,8 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "../MemMap.h"
#include "Core/MemMap.h"
#include "Core/Reporting.h"
#include "../MIPS/MIPSTables.h"
#include "ElfReader.h"
#include "../Debugger/SymbolMap.h"
@ -46,7 +47,7 @@ void addrToHiLo(u32 addr, u16 &hi, s16 &lo)
u32 test = (hi<<16) + lo;
if (test != addr)
{
WARN_LOG_REPORT(LOADER, "HI16/LO16 relocation failure?");
}
}
@ -77,28 +78,23 @@ void ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs)
}
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:
case R_MIPS_32:
if (log)
DEBUG_LOG(LOADER,"Full address reloc %08x", addr);
//full address, no problemo
op += relocateTo;
break;
case R_MIPS26: //j, jal
case R_MIPS_26: //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
case R_MIPS_HI16: //lui part of lui-addiu pairs
{
if (log)
DEBUG_LOG(LOADER,"HI reloc %08x", addr);
@ -108,7 +104,7 @@ void ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs)
bool found = false;
for (int t = r + 1; t<numRelocs; t++)
{
if ((rels[t].r_info & 0xF) == R_MIPS16_LO)
if ((rels[t].r_info & 0xF) == R_MIPS_LO16)
{
u32 corrLoAddr = rels[t].r_offset + segmentVAddr[readwrite];
if (log)
@ -125,13 +121,13 @@ void ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs)
}
}
if (!found)
ERROR_LOG(LOADER, "R_MIPS16: not found");
ERROR_LOG_REPORT(LOADER, "R_MIPS_HI16: could not find R_MIPS_LO16");
op = (op & 0xFFFF0000) | (hi);
}
break;
case R_MIPS16_LO: //addiu part of lui-addiu pairs
case R_MIPS_LO16: //addiu part of lui-addiu pairs
{
if (log)
DEBUG_LOG(LOADER,"LO reloc %08x", addr);
@ -142,27 +138,180 @@ void ElfReader::LoadRelocations(Elf32_Rel *rels, int numRelocs)
}
break;
case 7: //gp
if (log)
ERROR_LOG(LOADER,"ARGH IT'S A GP!!!!!!!! %08x", addr);
case R_MIPS_GPREL16: //gp
{
char temp[256];
MIPSDisAsm(op, 0, temp);
ERROR_LOG_REPORT(LOADER, "ARGH IT'S A GP!!!!!!!! %08x : %s", addr, temp);
}
break;
case 0: // another GP reloc!
{
char temp[256];
MIPSDisAsm(op, 0, temp);
ERROR_LOG(LOADER,"WARNING: GP reloc? @ %08x : 0 : %s", addr, temp );
ERROR_LOG_REPORT(LOADER, "WARNING: GP reloc? @ %08x : 0 : %s", addr, temp);
}
break;
default:
ERROR_LOG(LOADER,"ARGH IT'S A UNKNOWN RELOCATION!!!!!!!! %08x", addr);
{
char temp[256];
MIPSDisAsm(op, 0, temp);
ERROR_LOG_REPORT(LOADER,"ARGH IT'S A UNKNOWN RELOCATION!!!!!!!! %08x, type=%d : %s", addr, type, temp);
}
break;
}
Memory::Write_U32(op, addr);
}
}
void ElfReader::LoadRelocations2(int rel_seg)
{
Elf32_Phdr *ph;
u8 *buf, *end, *flag_table, *type_table;
int flag_table_size, type_table_size;
int flag_bits, seg_bits, type_bits;
int cmd, flag, seg, type;
int off_seg = 0, addr_seg, rel_base, rel_offset;
int relocate_to, last_type, lo16;
u32 op, addr;
int rcount = 0;
ph = segments + rel_seg;
buf = (u8*)GetSegmentPtr(rel_seg);
end = buf+ph->p_filesz;
flag_bits = buf[2];
type_bits = buf[3];
seg_bits = 1;
while((1<<seg_bits)<rel_seg)
seg_bits += 1;
buf += 4;
flag_table = buf;
flag_table_size = flag_table[0];
buf += flag_table_size;
type_table = buf;
type_table_size = flag_table[0];
buf += type_table_size;
rel_base = 0;
last_type = -1;
while(buf<end){
cmd = *(u16*)(buf);
buf += 2;
flag = ( cmd<<(16-flag_bits))&0xffff;
flag = (flag>>(16-flag_bits))&0xffff;
flag = flag_table[flag];
seg = (cmd<<(16-seg_bits-flag_bits))&0xffff;
seg = (seg>>(16-seg_bits))&0xffff;
type = ( cmd<<(16-type_bits-seg_bits-flag_bits))&0xffff;
type = (type>>(16-type_bits))&0xffff;
type = type_table[type];
if((flag&0x01)==0){
off_seg = seg;
if((flag&0x06)==0){
rel_base = cmd>>(seg_bits+flag_bits);
}else if((flag&0x06)==4){
rel_base = buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);
buf += 4;
}else{
ERROR_LOG_REPORT(LOADER, "Rel2: invalid size flag! %x", flag);
rel_base = 0;
}
}else{
addr_seg = seg;
relocate_to = segmentVAddr[addr_seg];
if((flag&0x06)==0x00){
rel_offset = cmd;
if(cmd&0x8000){
rel_offset |= 0xffff0000;
rel_offset >>= type_bits+seg_bits+flag_bits;
rel_offset |= 0xffff0000;
}else{
rel_offset >>= type_bits+seg_bits+flag_bits;
}
rel_base += rel_offset;
}else if((flag&0x06)==0x02){
rel_offset = cmd;
if(cmd&0x8000)
rel_offset |= 0xffff0000;
rel_offset >>= type_bits+seg_bits+flag_bits;
rel_offset = (rel_offset<<16) | (buf[0]) | (buf[1]<<8);
buf += 2;
rel_base += rel_offset;
}else if((flag&0x06)==0x04){
rel_base = buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24);;
buf += 4;
}else{
ERROR_LOG_REPORT(LOADER, "Rel2: invalid relocat size flag! %x", flag);
}
rel_offset = rel_base+segmentVAddr[off_seg];
if((flag&0x38)==0x00){
lo16 = 0;
}else if((flag&0x38)==0x08){
if(last_type!=0x04)
lo16 = 0;
}else if((flag&0x38)==0x10){
lo16 = (buf[0]) | (buf[1]<<8);
if(lo16&0x8000)
lo16 |= 0xffff0000;
buf += 2;
}else{
ERROR_LOG_REPORT(LOADER, "Rel2: invalid lo16 type! %x", flag);
}
op = Memory::ReadUnchecked_U32(rel_offset);
DEBUG_LOG(LOADER, "Rel2: %5d: CMD=0x%04X type=%d off_seg=%d offset=%08x addr_seg=%d op=%08x\n", rcount, cmd, type, off_seg, rel_base, addr_seg, op);
switch(type){
case 0:
continue;
case 2: // R_MIPS_32
op += relocate_to;
break;
case 3: // R_MIPS_26
case 6: // R_MIPS_J26
case 7: // R_MIPS_JAL26
op = (op&0xFC000000) | (((op&0x03FFFFFF)+(relocate_to>>2))&0x03FFFFFFF);
break;
case 4: // R_MIPS_HI16
addr = ((op<<16)+lo16)+relocate_to;
if(addr&0x8000)
addr += 0x00010000;
op = (op&0xffff0000) | (addr>>16 );
break;
case 1:
case 5: // R)MIPS_LO16
op = (op&0xffff0000) | (((op&0xffff)+relocate_to)&0xffff);
break;
default:
break;
}
Memory::Write_U32(op, rel_offset);
rcount += 1;
}
}
}
bool ElfReader::LoadInto(u32 loadAddress)
{
DEBUG_LOG(LOADER,"String section: %i", header->e_shstrndx);
@ -218,7 +367,7 @@ bool ElfReader::LoadInto(u32 loadAddress)
}
if (vaddr == (u32)-1) {
ERROR_LOG(LOADER, "Failed to allocate memory for ELF!");
ERROR_LOG_REPORT(LOADER, "Failed to allocate memory for ELF!");
return false;
}
@ -237,7 +386,7 @@ bool ElfReader::LoadInto(u32 loadAddress)
for (int i=0; i<header->e_phnum; i++)
{
Elf32_Phdr *p = segments + i;
DEBUG_LOG(LOADER, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", (int)p->p_type, (u32)p->p_vaddr, (int)p->p_filesz, (int)p->p_memsz);
DEBUG_LOG(LOADER, "Type: %08x Vaddr: %08x Filesz: %08x Memsz: %08x ", (int)p->p_type, (u32)p->p_vaddr, (int)p->p_filesz, (int)p->p_memsz);
if (p->p_type == PT_LOAD)
{
@ -298,7 +447,7 @@ bool ElfReader::LoadInto(u32 loadAddress)
{
if (!(sections[sectionToModify].sh_flags & SHF_ALLOC))
{
ERROR_LOG(LOADER,"Trying to relocate non-loaded section %s",GetSectionName(sectionToModify));
ERROR_LOG_REPORT(LOADER, "Trying to relocate non-loaded section %s", GetSectionName(sectionToModify));
continue;
}
@ -311,7 +460,7 @@ bool ElfReader::LoadInto(u32 loadAddress)
}
else
{
WARN_LOG(LOADER, "sectionToModify = %i - ignoring PSP relocation sector %i", sectionToModify, i);
WARN_LOG_REPORT(LOADER, "sectionToModify = %i - ignoring PSP relocation sector %i", sectionToModify, i);
}
}
else if (s->sh_type == SHT_REL)
@ -329,15 +478,15 @@ bool ElfReader::LoadInto(u32 loadAddress)
{
if (!(sections[sectionToModify].sh_flags & SHF_ALLOC))
{
ERROR_LOG(LOADER,"Trying to relocate non-loaded section %s, ignoring",GetSectionName(sectionToModify));
ERROR_LOG_REPORT(LOADER, "Trying to relocate non-loaded section %s, ignoring", GetSectionName(sectionToModify));
continue;
}
}
else
{
WARN_LOG(LOADER, "sectionToModify = %i - ignoring relocation sector %i", sectionToModify, i);
WARN_LOG_REPORT(LOADER, "sectionToModify = %i - ignoring relocation sector %i", sectionToModify, i);
}
ERROR_LOG(LOADER,"Traditional relocations unsupported.");
ERROR_LOG_REPORT(LOADER, "Traditional relocations unsupported.");
}
}
}
@ -356,6 +505,10 @@ bool ElfReader::LoadInto(u32 loadAddress)
Elf32_Rel *rels = (Elf32_Rel *)GetSegmentPtr(i);
LoadRelocations(rels, numRelocs);
} else if (p->p_type == 0x700000A1)
{
INFO_LOG(LOADER,"Loading segment relocations2");
LoadRelocations2(i);
}
}
}

View File

@ -21,6 +21,22 @@
#include "ElfTypes.h"
enum {
R_MIPS_NONE,
R_MIPS_16,
R_MIPS_32,
R_MIPS_REL32,
R_MIPS_26,
R_MIPS_HI16,
R_MIPS_LO16,
R_MIPS_GPREL16,
R_MIPS_LITERAL,
R_MIPS_GOT16,
R_MIPS_PC16,
R_MIPS_CALL16,
R_MIPS_GPREL32
};
enum KnownElfTypes
{
KNOWNELF_PSP = 0,
@ -120,6 +136,7 @@ public:
bool LoadInto(u32 vaddr);
bool LoadSymbols();
void LoadRelocations(Elf32_Rel *rels, int numRelocs);
void LoadRelocations2(int rel_seg);
private:

View File

@ -17,7 +17,6 @@
#include <stdio.h>
#include <string.h>
#include "../Globals.h"
#include "ParamSFO.h"
struct Header
@ -124,7 +123,7 @@ bool ParamSFOData::ReadSFO(const u8 *paramsfo, size_t size)
{
const char *utfdata = (const char *)(data_start + indexTables[i].data_table_offset);
DEBUG_LOG(LOADER, "%s %s", key, utfdata);
SetValue(key,std::string(utfdata,indexTables[i].param_len),indexTables[i].param_max_len);
SetValue(key,std::string(utfdata /*, indexTables[i].param_len*/), indexTables[i].param_max_len);
}
break;
}

View File

@ -18,10 +18,10 @@
#pragma once
#include <string>
#include <map>
#include "Common/CommonTypes.h"
#include "../Globals.h"
class ParamSFOData
{

View File

@ -1,5 +1,3 @@
#include <cstring>
extern "C"
{
#include "ext/libkirk/kirk_engine.h"

View File

@ -68,7 +68,7 @@ bool FileBlockDevice::ReadBlock(int blockNumber, u8 *outPtr)
// .CSO format
// complessed ISO(9660) header format
// compressed ISO(9660) header format
typedef struct ciso_header
{
unsigned char magic[4]; // +00 : 'C','I','S','O'

View File

@ -23,8 +23,6 @@
// The ISOFileSystemReader reads from a BlockDevice, so it automatically works
// with CISO images.
#include <string>
#include "../../Globals.h"
#include "Core/ELF/PBPReader.h"

View File

@ -20,7 +20,6 @@
// TODO: Remove the Windows-specific code, FILE is fine there too.
#include <map>
#include <string>
#include "../Core/FileSystems/FileSystem.h"

View File

@ -19,7 +19,6 @@
#include "../../Globals.h"
#include "ChunkFile.h"
#include <string>
enum FileAccess
{

View File

@ -16,8 +16,9 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Globals.h"
#include "Common.h"
#include "ISOFileSystem.h"
#include "Common/Common.h"
#include "Core/FileSystems/ISOFileSystem.h"
#include "Core/Reporting.h"
#include <cstring>
#include <cstdio>
#include <ctype.h>
@ -36,20 +37,20 @@ static bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize)
int offset = 0;
if (sscanf(filename_c + pos, "%x%n", sectorStart, &offset) != 1)
WARN_LOG(FILESYS, "Invalid LBN reference: %s", filename_c);
WARN_LOG_REPORT(FILESYS, "Invalid LBN reference: %s", filename_c);
pos += offset;
if (filename.compare(pos, sizeof("_size") - 1, "_size") != 0)
WARN_LOG(FILESYS, "Invalid LBN reference: %s", filename_c);
WARN_LOG_REPORT(FILESYS, "Invalid LBN reference: %s", filename_c);
pos += sizeof("_size") - 1;
offset = 0;
if (sscanf(filename_c + pos, "%x%n", readSize, &offset) != 1)
WARN_LOG(FILESYS, "Invalid LBN reference: %s", filename_c);
WARN_LOG_REPORT(FILESYS, "Invalid LBN reference: %s", filename_c);
pos += offset;
if (filename.size() > pos)
WARN_LOG(FILESYS, "Incomplete LBN reference: %s", filename_c);
WARN_LOG_REPORT(FILESYS, "Incomplete LBN reference: %s", filename_c);
return true;
}
@ -163,7 +164,7 @@ ISOFileSystem::ISOFileSystem(IHandleAllocator *_hAlloc, BlockDevice *_blockDevic
if (!memcmp(desc.cd001, "CD001", 5))
{
INFO_LOG(FILESYS, "Looks like a valid ISO!");
DEBUG_LOG(FILESYS, "Looks like a valid ISO!");
}
else
{

View File

@ -18,9 +18,7 @@
#pragma once
#include <map>
#include <string>
#include <list>
#include <vector>
#include "FileSystem.h"

View File

@ -17,8 +17,9 @@
#include <set>
#include "Common/StringUtils.h"
#include "../HLE/sceKernelThread.h"
#include "MetaFileSystem.h"
#include "Core/FileSystems/MetaFileSystem.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/Reporting.h"
static bool ApplyPathStringToComponentsVector(std::vector<std::string> &vector, const std::string &pathString)
{
@ -170,6 +171,7 @@ IFileSystem *MetaFileSystem::GetHandleOwner(u32 handle)
return 0;
}
extern u32 ioErrorCode;
bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpath, MountPoint **system)
{
std::string realpath;
@ -186,11 +188,14 @@ bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpat
int currentThread = __KernelGetCurThread();
currentDir_t::iterator it = currentDir.find(currentThread);
if (it == currentDir.end())
if (it == currentDir.end())
{
//TODO: emulate PSP's error 8002032C: "no current working directory" if relative... may break things requiring fixes elsewhere
if (inpath.find(':') == std::string::npos /* means path is relative */)
WARN_LOG(HLE, "Path is relative, but current directory not set for thread %i. Should give error, instead falling back to %s", currentThread, startingDirectory.c_str());
//Attempt to emulate SCE_KERNEL_ERROR_NOCWD / 8002032C: may break things requiring fixes elsewhere
if (inpath.find(':') == std::string::npos /* means path is relative */)
{
ioErrorCode = SCE_KERNEL_ERROR_NOCWD;
WARN_LOG_REPORT(HLE, "Path is relative, but current directory not set for thread %i. returning 8002032C(SCE_KERNEL_ERROR_NOCWD) instead.", currentThread);
}
}
else
{
@ -336,7 +341,7 @@ int MetaFileSystem::ChDir(const std::string &dir)
}
}
WARN_LOG(HLE, "ChDir failed to map device for \"%s\", failing", dir.c_str());
WARN_LOG_REPORT(HLE, "ChDir failed to map device for \"%s\", failing", dir.c_str());
return SCE_KERNEL_ERROR_NODEV;
}
}

View File

@ -18,7 +18,6 @@
#pragma once
#include "../../Globals.h"
// For easy parameter parsing and return value processing.
// 64bit wrappers
@ -92,6 +91,16 @@ template<u32 func(int, void *, int)> void WrapU_IVI() {
RETURN(retval);
}
template<int func(int, const char *, u32, void *, void *, u32, int)> void WrapI_ICUVVUI() {
u32 retval = func(PARAM(0), Memory::GetCharPointer(PARAM(1)), PARAM(2), Memory::GetPointer(PARAM(3)),Memory::GetPointer(PARAM(4)), PARAM(5), PARAM(6) );
RETURN(retval);
}
template<u32 func(int, void *)> void WrapU_IV() {
u32 retval = func(PARAM(0), Memory::GetPointer(PARAM(1)));
RETURN(retval);
}
template<float func()> void WrapF_V() {
RETURNF(func());
}

View File

@ -64,7 +64,7 @@ void hleDelayResultFinish(u64 userdata, int cycleslate)
{
u32 error;
SceUID threadID = (SceUID) userdata;
SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_DELAY, error);
SceUID verify = __KernelGetWaitID(threadID, WAITTYPE_HLEDELAY, error);
// The top 32 bits of userdata are the top 32 bits of the 64 bit result.
// We can't just put it all in userdata because we need to know the threadID...
u64 result = (userdata & 0xFFFFFFFF00000000ULL) | __KernelGetWaitValue(threadID, error);
@ -335,7 +335,7 @@ u32 hleDelayResult(u32 result, const char *reason, int usec)
if (__KernelIsDispatchEnabled())
{
CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, __KernelGetCurThread());
__KernelWaitCurThread(WAITTYPE_DELAY, 1, result, 0, false, reason);
__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, result, 0, false, reason);
}
else
WARN_LOG(HLE, "Dispatch disabled, not delaying HLE result (right thing to do?)");
@ -348,7 +348,7 @@ u64 hleDelayResult(u64 result, const char *reason, int usec)
{
u64 param = (result & 0xFFFFFFFF00000000) | __KernelGetCurThread();
CoreTiming::ScheduleEvent(usToCycles(usec), delayedResultEvent, param);
__KernelWaitCurThread(WAITTYPE_DELAY, 1, (u32) result, 0, false, reason);
__KernelWaitCurThread(WAITTYPE_HLEDELAY, 1, (u32) result, 0, false, reason);
}
else
WARN_LOG(HLE, "Dispatch disabled, not delaying HLE result (right thing to do?)");

View File

@ -64,6 +64,7 @@
#include "sceNp.h"
#include "sceMd5.h"
#include "sceJpeg.h"
#include "sceAudiocodec.h"
#define N(s) s
@ -79,6 +80,7 @@ const HLEFunction FakeSysCalls[] = {
{NID_CALLBACKRETURN, __KernelReturnFromMipsCall, "__KernelReturnFromMipsCall"},
{NID_INTERRUPTRETURN, __KernelReturnFromInterrupt, "__KernelReturnFromInterrupt"},
{NID_EXTENDRETURN, __KernelReturnFromExtendStack, "__KernelReturnFromExtendStack"},
{NID_MODULERETURN, __KernelReturnFromModuleFunc, "__KernelReturnFromModuleFunc"},
{NID_IDLE, __KernelIdle, "_sceKernelIdle"},
};
@ -268,6 +270,7 @@ void RegisterAllModules() {
Register_sceNpAuth();
Register_sceMd5();
Register_sceJpeg();
Register_sceAudiocodec();
for (int i = 0; i < numModules; i++)
{

View File

@ -23,6 +23,7 @@
#define NID_CALLBACKRETURN 0xbadc0fee
#define NID_INTERRUPTRETURN 0xbadd00d5
#define NID_EXTENDRETURN 0xbad0b0c9
#define NID_MODULERETURN 0xbad0d318
#define NID_IDLE 0x1d7e1d7e
void RegisterAllModules();

View File

@ -19,7 +19,7 @@
#include "sceAudio.h"
#include "sceKernel.h"
#include "sceKernelThread.h"
#include "StdMutex.h"
#include "base/mutex.h"
#include "CommonTypes.h"
#include "../CoreTiming.h"
#include "../MemMap.h"
@ -30,7 +30,7 @@
#include "Common/Thread.h"
// Should be used to lock anything related to the outAudioQueue.
std::recursive_mutex section;
recursive_mutex section;
int eventAudioUpdate = -1;
int eventHostAudioUpdate = -1;
@ -47,9 +47,19 @@ const int audioHostIntervalUs = (int)(1000000ULL * hostAttemptBlockSize / hwSamp
const int chanQueueMaxSizeFactor = 2;
const int chanQueueMinSizeFactor = 1;
FixedSizeQueue<s16, hostAttemptBlockSize * 16> outAudioQueue;
static inline s16 clamp_s16(int i) {
if (i > 32767)
return 32767;
if (i < -32768)
return -32768;
return i;
}
static inline s16 adjustvolume(s16 sample, int vol) {
return clamp_s16((sample * vol) >> 15);
}
void hleAudioUpdate(u64 userdata, int cyclesLate)
{
@ -73,7 +83,7 @@ void __AudioInit()
CoreTiming::ScheduleEvent(usToCycles(audioIntervalUs), eventAudioUpdate, 0);
CoreTiming::ScheduleEvent(usToCycles(audioHostIntervalUs), eventHostAudioUpdate, 0);
for (int i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++)
for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++)
chans[i].clear();
}
@ -86,9 +96,10 @@ void __AudioDoState(PointerWrap &p)
p.Do(mixFrequency);
section.lock();
outAudioQueue.DoState(p);
section.unlock();
{
lock_guard guard(section);
outAudioQueue.DoState(p);
}
int chanCount = ARRAY_SIZE(chans);
p.Do(chanCount);
@ -105,7 +116,7 @@ void __AudioDoState(PointerWrap &p)
void __AudioShutdown()
{
for (int i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++)
for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++)
chans[i].clear();
}
@ -121,10 +132,11 @@ u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking)
}
// If there's anything on the queue at all, it should be busy, but we try to be a bit lax.
if (chan.sampleQueue.size() > chan.sampleCount * 2 * chanQueueMaxSizeFactor || chan.sampleAddress == 0) {
//if (chan.sampleQueue.size() > chan.sampleCount * 2 * chanQueueMaxSizeFactor || chan.sampleAddress == 0) {
if (chan.sampleQueue.size() > 0 || chan.sampleAddress == 0) {
if (blocking) {
// TODO: Regular multichannel audio seems to block for 64 samples less? Or enqueue the first 64 sync?
int blockSamples = chan.sampleQueue.size() / 2 / chanQueueMinSizeFactor;
int blockSamples = (int)chan.sampleQueue.size() / 2 / chanQueueMinSizeFactor;
AudioChannelWaitInfo waitInfo = {__KernelGetCurThread(), blockSamples};
chan.waitingThreads.push_back(waitInfo);
@ -154,14 +166,23 @@ u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking)
// Walking a pointer for speed. But let's make sure we wouldn't trip on an invalid ptr.
if (Memory::IsValidAddress(chan.sampleAddress + (totalSamples - 1) * sizeof(s16)))
{
for (u32 i = 0; i < totalSamples; i++)
chan.sampleQueue.push(*sampleData++);
for (u32 i = 0; i < totalSamples; i += 2) {
chan.sampleQueue.push(adjustvolume(*sampleData++, chan.leftVolume));
chan.sampleQueue.push(adjustvolume(*sampleData++, chan.rightVolume));
}
}
}
else
{
for (u32 i = 0; i < totalSamples; i++)
chan.sampleQueue.push((s16)Memory::Read_U16(chan.sampleAddress + sizeof(s16) * i));
for (u32 i = 0; i < totalSamples; i++) {
s16 sampleL = (s16)Memory::Read_U16(chan.sampleAddress + sizeof(s16) * i);
sampleL = adjustvolume(sampleL, chan.leftVolume);
chan.sampleQueue.push(sampleL);
i++;
s16 sampleR = (s16)Memory::Read_U16(chan.sampleAddress + sizeof(s16) * i);
sampleR = adjustvolume(sampleR, chan.rightVolume);
chan.sampleQueue.push(sampleR);
}
}
}
else if (chan.format == PSP_AUDIO_FORMAT_MONO)
@ -170,44 +191,40 @@ u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking)
{
// Expand to stereo
s16 sample = (s16)Memory::Read_U16(chan.sampleAddress + 2 * i);
chan.sampleQueue.push(sample);
chan.sampleQueue.push(sample);
chan.sampleQueue.push(adjustvolume(sample, chan.leftVolume));
chan.sampleQueue.push(adjustvolume(sample, chan.rightVolume));
}
}
return ret;
}
static inline s16 clamp_s16(int i) {
if (i > 32767)
return 32767;
if (i < -32768)
return -32768;
return i;
}
inline void __AudioWakeThreads(AudioChannel &chan, int step)
inline void __AudioWakeThreads(AudioChannel &chan, int result, int step)
{
u32 error;
for (size_t w = 0; w < chan.waitingThreads.size(); ++w)
{
AudioChannelWaitInfo &waitInfo = chan.waitingThreads[w];
waitInfo.numSamples -= hwBlockSize;
waitInfo.numSamples -= step;
// If it's done (there will still be samples on queue) and actually still waiting, wake it up.
if (waitInfo.numSamples <= 0 && __KernelGetWaitID(waitInfo.threadID, WAITTYPE_AUDIOCHANNEL, error) != 0)
u32 waitID = __KernelGetWaitID(waitInfo.threadID, WAITTYPE_AUDIOCHANNEL, error);
if (waitInfo.numSamples <= 0 && waitID != 0)
{
// DEBUG_LOG(HLE, "Woke thread %i for some buffer filling", waitingThread);
u32 ret = __KernelGetWaitValue(waitInfo.threadID, error);
u32 ret = result == 0 ? __KernelGetWaitValue(waitInfo.threadID, error) : SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
__KernelResumeThreadFromWait(waitInfo.threadID, ret);
chan.waitingThreads.erase(chan.waitingThreads.begin() + w--);
}
// This means the thread stopped waiting, so stop trying to wake it.
else if (waitID == 0)
chan.waitingThreads.erase(chan.waitingThreads.begin() + w--);
}
}
void __AudioWakeThreads(AudioChannel &chan)
void __AudioWakeThreads(AudioChannel &chan, int result)
{
__AudioWakeThreads(chan, 0x7FFFFFFF);
__AudioWakeThreads(chan, result, 0x7FFFFFFF);
}
// Mix samples from the various audio channels into a single sample queue.
@ -222,11 +239,11 @@ void __AudioUpdate()
s32 mixBuffer[hwBlockSize * 2];
memset(mixBuffer, 0, sizeof(mixBuffer));
for (int i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++)
for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++)
{
if (!chans[i].reserved)
continue;
__AudioWakeThreads(chans[i], hwBlockSize);
__AudioWakeThreads(chans[i], 0, hwBlockSize);
if (!chans[i].sampleQueue.size()) {
// ERROR_LOG(HLE, "No queued samples, skipping channel %i", i);
@ -239,9 +256,8 @@ void __AudioUpdate()
{
s16 sampleL = chans[i].sampleQueue.pop_front();
s16 sampleR = chans[i].sampleQueue.pop_front();
// The channel volume should be done here?
mixBuffer[s * 2 + 0] += sampleL * (s32)chans[i].leftVolume >> 15;
mixBuffer[s * 2 + 1] += sampleR * (s32)chans[i].rightVolume >> 15;
mixBuffer[s * 2 + 0] += sampleL;
mixBuffer[s * 2 + 1] += sampleR;
}
else
{
@ -252,7 +268,7 @@ void __AudioUpdate()
}
if (g_Config.bEnableSound) {
section.lock();
lock_guard guard(section);
if (outAudioQueue.room() >= hwBlockSize * 2) {
// Push the mixed samples onto the output audio queue.
for (int i = 0; i < hwBlockSize; i++) {
@ -267,7 +283,6 @@ void __AudioUpdate()
// about the amount of audio we produce.
DEBUG_LOG(HLE, "Audio outbuffer overrun! room = %i / %i", outAudioQueue.room(), (u32)outAudioQueue.capacity());
}
section.unlock();
}
}
@ -283,15 +298,13 @@ void __AudioSetOutputFrequency(int freq)
int __AudioMix(short *outstereo, int numFrames)
{
// TODO: if mixFrequency != the actual output frequency, resample!
section.lock();
lock_guard guard(section);
int underrun = -1;
s16 sampleL = 0;
s16 sampleR = 0;
bool anythingToPlay = false;
for (int i = 0; i < numFrames; i++) {
if (outAudioQueue.size() >= 2)
{
if (outAudioQueue.size() >= 2) {
sampleL = outAudioQueue.pop_front();
sampleR = outAudioQueue.pop_front();
outstereo[i * 2 + 0] = sampleL;
@ -308,6 +321,5 @@ int __AudioMix(short *outstereo, int numFrames)
} else {
// DEBUG_LOG(HLE, "No underrun, mixed %i samples fine", numFrames);
}
section.unlock();
return underrun >= 0 ? underrun : numFrames;
}

View File

@ -29,7 +29,7 @@ void __AudioSetOutputFrequency(int freq);
// May return SCE_ERROR_AUDIO_CHANNEL_BUSY if buffer too large
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking);
void __AudioWakeThreads(AudioChannel &chan, int step);
void __AudioWakeThreads(AudioChannel &chan);
void __AudioWakeThreads(AudioChannel &chan, int result, int step);
void __AudioWakeThreads(AudioChannel &chan, int result);
int __AudioMix(short *outstereo, int numSamples);

File diff suppressed because it is too large Load Diff

View File

@ -17,9 +17,55 @@
#pragma once
#include "sceAudiocodec.h"
class PointerWrap;
void Register_sceAtrac3plus();
void __AtracInit();
void __AtracDoState(PointerWrap &p);
void __AtracShutdown();
void __AtracShutdown();
typedef struct
{
u32 decodePos; // 0
u32 endSample; // 4
u32 loopStart; // 8
u32 loopEnd; // 12
int samplesPerChan; // 16
char numFrame; // 20
// 2: all the stream data on the buffer
// 6: looping -> second buffer needed
char state; // 21
char unk22;
char numChan; // 23
u16 sampleSize; // 24
u16 codec; // 26
u32 dataOff; // 28
u32 curOff; // 32
u32 dataEnd; // 36
int loopNum; // 40
u32 streamDataByte; // 44
u32 unk48;
u32 unk52;
u32 buffer; // 56
u32 secondBuffer; // 60
u32 bufferByte; // 64
u32 secondBufferByte; // 68
// make sure the size is 128
u8 unk[56];
} SceAtracIdInfo;
typedef struct
{
// size 128
SceAudiocodecCodec codec;
// size 128
SceAtracIdInfo info;
} SceAtracId;
// provide some decoder interface
u32 _AtracAddStreamData(int atracID, u8 *buf, u32 bytesToAdd);
u32 _AtracDecodeData(int atracID, u8* outbuf, u32 *SamplesNum, u32* finish, int *remains);
int _AtracGetIDByContext(u32 contextAddr);

View File

@ -24,7 +24,7 @@
#include "Core/HLE/sceAudio.h"
#include "Core/HLE/__sceAudio.h"
const int PSP_AUDIO_SAMPLE_MAX = 65536 - 64;
const u32 PSP_AUDIO_SAMPLE_MAX = 65536 - 64;
const int PSP_AUDIO_ERROR_SRC_FORMAT_4 = 0x80000003;
void AudioChannel::DoState(PointerWrap &p)
@ -42,7 +42,7 @@ void AudioChannel::DoState(PointerWrap &p)
void AudioChannel::reset()
{
__AudioWakeThreads(*this);
__AudioWakeThreads(*this, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED);
clear();
}
@ -192,7 +192,7 @@ u32 sceAudioChReserve(int chan, u32 sampleCount, u32 format) {
return SCE_ERROR_AUDIO_NO_CHANNELS_AVAILABLE;
}
}
if (chan >= PSP_AUDIO_CHANNEL_MAX) {
if ((u32)chan >= PSP_AUDIO_CHANNEL_MAX) {
ERROR_LOG(HLE, "sceAudioChReserve(%08x, %08x, %08x) - bad channel", chan, sampleCount, format);
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}

View File

@ -42,7 +42,7 @@ enum PspAudioFrequencies { PSP_AUDIO_FREQ_44K = 44100, PSP_AUDIO_FREQ_48K = 48
#define SCE_ERROR_AUDIO_CHANNEL_ALREADY_RESERVED 0x80268002
const int PSP_AUDIO_CHANNEL_MAX = 8;
const u32 PSP_AUDIO_CHANNEL_MAX = 8;
const int PSP_AUDIO_CHANNEL_SRC = 8;
const int PSP_AUDIO_CHANNEL_OUTPUT2 = 8;

View File

@ -0,0 +1,45 @@
// 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 "Core/HLE/HLE.h"
#include "sceAudiocodec.h"
#include "Core/Reporting.h"
int sceAudiocodecInit(u32 audioCodec, int codeType) {
ERROR_LOG_REPORT(HLE, "UNIMPL sceAudiocodecInit(%08x, %x)", audioCodec, codeType);
return 0;
}
int sceAudiocodecDecode(u32 audioCodec, int codeType) {
ERROR_LOG_REPORT(HLE, "UNIMPL sceAudiocodecDecode(%08x, %x)", audioCodec, codeType);
return 0;
}
const HLEFunction sceAudiocodec[] =
{
{0x70A703F8, WrapI_UI<sceAudiocodecDecode>, "sceAudiocodecDecode"},
{0x5B37EB1D, WrapI_UI<sceAudiocodecInit>, "sceAudiocodecInit"},
{0x8ACA11D5, 0, "sceAudiocodecGetInfo"},
{0x3A20A200, 0, "sceAudiocodecGetEDRAM"},
{0x29681260, 0, "sceAudiocodecReleaseEDRAM"},
{0x9D3F790C, 0, "sceAudiocodeCheckNeedMem"},
};
void Register_sceAudiocodec()
{
RegisterModule("sceAudiocodec", ARRAY_SIZE(sceAudiocodec), sceAudiocodec);
}

59
Core/HLE/sceAudiocodec.h Normal file
View File

@ -0,0 +1,59 @@
// 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
typedef struct
{
s32 unk0;
s32 unk4;
s32 err; // 8
s32 edramAddr; // 12
s32 neededMem; // 16
s32 unk20;
u32 inBuf; // 24
s32 unk28;
u32 outBuf; // 32
s32 unk36;
s8 unk40;
s8 unk41;
s8 unk42;
s8 unk43;
s8 unk44;
s8 unk45;
s8 unk46;
s8 unk47;
s32 unk48;
s32 unk52;
s32 unk56;
s32 unk60;
s32 unk64;
s32 unk68;
s32 unk72;
s32 unk76;
s32 unk80;
s32 unk84;
s32 unk88;
s32 unk92;
s32 unk96;
s32 unk100;
u32 allocMem; // 104
// make sure the size is 128
u8 unk[20];
} SceAudiocodecCodec;
void Register_sceAudiocodec();

View File

@ -254,6 +254,8 @@ void __DisplayGetDebugStats(char stats[2048])
{
gpu->UpdateStats();
float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f;
sprintf(stats,
"Frames: %i\n"
"DL processing time: %0.2f ms\n"
@ -263,6 +265,7 @@ void __DisplayGetDebugStats(char stats[2048])
"Draw calls: %i, flushes %i\n"
"Cached Draw calls: %i\n"
"Num Tracked Vertex Arrays: %i\n"
"Cycles executed: %d (%f per vertex)\n"
"Vertices Submitted: %i\n"
"Cached Vertices Drawn: %i\n"
"Uncached Vertices Drawn: %i\n"
@ -283,6 +286,8 @@ void __DisplayGetDebugStats(char stats[2048])
gpuStats.numFlushes,
gpuStats.numCachedDrawCalls,
gpuStats.numTrackedVertexArrays,
gpuStats.vertexGPUCycles + gpuStats.otherGPUCycles,
vertexAverageCycles,
gpuStats.numVertsSubmitted,
gpuStats.numCachedVertsDrawn,
gpuStats.numUncachedVertsDrawn,
@ -561,7 +566,11 @@ u32 sceDisplayWaitVblank() {
}
u32 sceDisplayWaitVblankStartMulti(int vblanks) {
VERBOSE_LOG(HLE,"sceDisplayWaitVblankStartMulti()");
if (vblanks <= 0) {
WARN_LOG(HLE, "sceDisplayWaitVblankStartMulti(%d): invalid number of vblanks", vblanks);
return SCE_KERNEL_ERROR_INVALID_VALUE;
}
VERBOSE_LOG(HLE, "sceDisplayWaitVblankStartMulti(%d)", vblanks);
vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread(), vblanks));
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, false, "vblank start multi waited");
return 0;
@ -588,7 +597,11 @@ u32 sceDisplayWaitVblankStartCB() {
}
u32 sceDisplayWaitVblankStartMultiCB(int vblanks) {
VERBOSE_LOG(HLE,"sceDisplayWaitVblankStartMultiCB()");
if (vblanks <= 0) {
WARN_LOG(HLE, "sceDisplayWaitVblankStartMultiCB(%d): invalid number of vblanks", vblanks);
return SCE_KERNEL_ERROR_INVALID_VALUE;
}
VERBOSE_LOG(HLE,"sceDisplayWaitVblankStartMultiCB(%d)", vblanks);
vblankWaitingThreads.push_back(WaitVBlankInfo(__KernelGetCurThread(), vblanks));
__KernelWaitCurThread(WAITTYPE_VBLANK, 0, 0, 0, true, "vblank start multi waited");
return 0;

View File

@ -16,13 +16,27 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Globals.h"
#include "HLE.h"
#include "Core/Reporting.h"
#include "Core/HLE/HLE.h"
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
u32 sceDmacMemcpy(u32 dst, u32 src, u32 size)
{
if (!Memory::IsValidAddress(dst) || !Memory::IsValidAddress(src))
{
ERROR_LOG(HLE, "sceDmacMemcpy(dest=%08x, src=%08x, size=%i): invalid address", dst, src, size);
return 0;
}
DEBUG_LOG(HLE, "sceDmacMemcpy(dest=%08x, src=%08x, size=%i)", dst, src, size);
// TODO: check the addresses.
Memory::Memcpy(dst, Memory::GetPointer(src), size);
src &= ~0x40000000;
dst &= ~0x40000000;
if ((src >= PSP_GetVidMemBase() && src < PSP_GetVidMemEnd()) || (dst >= PSP_GetVidMemBase() && dst < PSP_GetVidMemEnd()))
gpu->UpdateMemory(dst, src, size);
return 0;
}

View File

@ -15,6 +15,7 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <cstdlib>
#include "Core/Config.h"
#include "Core/System.h"
#include "Core/Host.h"
@ -97,6 +98,7 @@ typedef s64 SceOff;
typedef u64 SceIores;
int asyncNotifyEvent = -1;
u32 ioErrorCode = 0;
#define SCE_STM_FDIR 0x1000
#define SCE_STM_FREG 0x2000
@ -143,6 +145,7 @@ public:
FileNode() : callbackID(0), callbackArg(0), asyncResult(0), hasAsyncResult(false), pendingAsyncResult(false), sectorBlockMode(false), closePending(false), npdrm(0), pgdInfo(NULL) {}
~FileNode() {
pspFileSystem.CloseFile(handle);
pgd_close(pgdInfo);
}
const char *GetName() {return fullpath.c_str();}
const char *GetTypeName() {return "OpenFile";}
@ -165,7 +168,19 @@ public:
p.Do(info);
p.Do(openMode);
// TODO: Savestate PGD files?
p.Do(npdrm);
p.Do(pgd_offset);
bool hasPGD = pgdInfo != NULL;
p.Do(hasPGD);
if (hasPGD) {
if (p.mode == p.MODE_READ) {
pgdInfo = (PGD_DESC*) malloc(sizeof(PGD_DESC));
}
p.DoVoid(pgdInfo, sizeof(PGD_DESC));
if (p.mode == p.MODE_READ) {
pgdInfo->block_buf = (u8 *)malloc(pgdInfo->block_size * 2);
}
}
p.DoMarker("File");
}
@ -728,6 +743,9 @@ FileNode *__IoOpen(const char* filename, int flags, int mode) {
access |= FILEACCESS_CREATE;
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
ioErrorCode = 0;
u32 h = pspFileSystem.OpenFile(filename, (FileAccess) access);
if (h == 0) {
return NULL;
@ -752,10 +770,19 @@ u32 sceIoOpen(const char* filename, int flags, int mode) {
return -1;
FileNode *f = __IoOpen(filename, flags, mode);
if (f == NULL) {
ERROR_LOG(HLE, "ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x, %08x) - file not found", filename, flags, mode);
// Timing is not accurate, aiming low for now.
return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND, "file opened", 100);
if (f == NULL)
{
//Timing is not accurate, aiming low for now.
if (ioErrorCode == SCE_KERNEL_ERROR_NOCWD)
{
ERROR_LOG(HLE, "SCE_KERNEL_ERROR_NOCWD=sceIoOpen(%s, %08x, %08x) - no current working directory", filename, flags, mode);
return hleDelayResult(SCE_KERNEL_ERROR_NOCWD , "no cwd", 10000);
}
else
{
ERROR_LOG(HLE, "ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x, %08x) - file not found", filename, flags, mode);
return hleDelayResult(ERROR_ERRNO_FILE_NOT_FOUND , "file opened", 10000);
}
}
SceUID id = f->GetUID();
@ -768,9 +795,6 @@ u32 sceIoClose(int id) {
u32 error;
DEBUG_LOG(HLE, "sceIoClose(%d)", id);
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if(f && f->npdrm){
pgd_close(f->pgdInfo);
}
// Timing is not accurate, aiming low for now.
return hleDelayResult(kernelObjects.Destroy<FileNode>(id), "file closed", 100);
}
@ -1042,12 +1066,19 @@ u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr,
break;
case 0x02425823:
// Check if FAT enabled
if (Memory::IsValidAddress(outPtr) && outLen == 4) {
hleEatCycles(23500);
// If the values added together are >= 0x80000000, or less than outPtr, invalid address.
if (((int)outPtr + outLen) < (int)outPtr) {
ERROR_LOG(HLE, "sceIoDevctl: fatms0: 0x02425823 command, bad address");
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
} else if (!Memory::IsValidAddress(outPtr)) {
// Technically, only checks for NULL, crashes for many bad addresses.
ERROR_LOG(HLE, "sceIoDevctl: fatms0: 0x02425823 command, no output address");
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
} else {
// Does not care about outLen, even if it's 0.
Memory::Write_U32(MemoryStick_FatState(), outPtr);
return 0;
} else {
ERROR_LOG(HLE, "Failed 0x02425823 fat");
return -1;
}
break;
case 0x02425824:
@ -1532,7 +1563,7 @@ int __IoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 out
//Unknown command, always expects return value of 1 according to JPCSP, used by Pangya Fantasy Golf.
case 0x1f30003:
INFO_LOG(HLE, "sceIoioCtl: Unknown cmd %08x always returns 1", cmd);
INFO_LOG(HLE, "sceIoIoCtl: Unknown cmd %08x always returns 1", cmd);
if(inlen != 4 || outlen != 1 || Memory::Read_U32(indataPtr) != outlen) {
INFO_LOG(HLE, "sceIoIoCtl id: %08x, cmd %08x, indataPtr %08x, inlen %08x, outdataPtr %08x, outlen %08x has invalid parameters", id, cmd, indataPtr, inlen, outdataPtr, outlen);
return SCE_KERNEL_ERROR_INVALID_ARGUMENT;

View File

@ -16,6 +16,7 @@
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Core/Config.h"
#include "Core/CwCheat.h"
#include "Core/HLE/HLE.h"
#include "Core/MIPS/MIPS.h"
#include "Core/MIPS/MIPSCodeUtils.h"
@ -109,7 +110,7 @@ void __KernelInit()
__PowerInit();
__UtilityInit();
__UmdInit();
__MpegInit(PSP_CoreParameter().useMediaEngine);
__MpegInit();
__PsmfInit();
__CtrlInit();
__RtcInit();
@ -119,6 +120,7 @@ void __KernelInit()
__FontInit();
__NetInit();
__VaudioInit();
__CheatInit();
SaveState::Init(); // Must be after IO, as it may create a directory
@ -160,6 +162,7 @@ void __KernelShutdown()
__KernelThreadingShutdown();
__KernelMemoryShutdown();
__InterruptsShutdown();
__CheatShutdown();
__KernelModuleShutdown();
CoreTiming::ClearPendingEvents();
@ -673,10 +676,10 @@ const HLEFunction ThreadManForUser[] =
{0xA9C2CB9A,&WrapI_IU<sceKernelReferMutexStatus>, "sceKernelReferMutexStatus"},
{0x87D9223C,0, "sceKernelCancelMutex"},
{0xFCCFAD26,sceKernelCancelWakeupThread,"sceKernelCancelWakeupThread"},
{0xFCCFAD26,WrapI_I<sceKernelCancelWakeupThread>,"sceKernelCancelWakeupThread"},
{0x1AF94D03,0,"sceKernelDonateWakeupThread"},
{0xea748e31,sceKernelChangeCurrentThreadAttr,"sceKernelChangeCurrentThreadAttr"},
{0x71bc9871,sceKernelChangeThreadPriority,"sceKernelChangeThreadPriority"},
{0xea748e31,WrapI_UU<sceKernelChangeCurrentThreadAttr>,"sceKernelChangeCurrentThreadAttr"},
{0x71bc9871,WrapI_II<sceKernelChangeThreadPriority>,"sceKernelChangeThreadPriority"},
{0x446D8DE6,WrapI_CUUIUU<sceKernelCreateThread>,"sceKernelCreateThread"},
{0x9fa03cd3,WrapI_I<sceKernelDeleteThread>,"sceKernelDeleteThread"},
{0xBD123D9E,sceKernelDelaySysClockThread,"sceKernelDelaySysClockThread"},
@ -687,19 +690,19 @@ const HLEFunction ThreadManForUser[] =
{0x809ce29b,WrapV_I<sceKernelExitDeleteThread>,"sceKernelExitDeleteThread"},
{0x94aa61ee,sceKernelGetThreadCurrentPriority,"sceKernelGetThreadCurrentPriority"},
{0x293b45b8,WrapI_V<sceKernelGetThreadId>,"sceKernelGetThreadId"},
{0x3B183E26,sceKernelGetThreadExitStatus,"sceKernelGetThreadExitStatus"},
{0x3B183E26,WrapI_I<sceKernelGetThreadExitStatus>,"sceKernelGetThreadExitStatus"},
{0x52089CA1,sceKernelGetThreadStackFreeSize,"sceKernelGetThreadStackFreeSize"},
{0xFFC36A14,WrapU_UU<sceKernelReferThreadRunStatus>,"sceKernelReferThreadRunStatus"},
{0x17c1684e,WrapU_UU<sceKernelReferThreadStatus>,"sceKernelReferThreadStatus"},
{0x2C34E053,WrapI_I<sceKernelReleaseWaitThread>,"sceKernelReleaseWaitThread"},
{0x75156e8f,sceKernelResumeThread,"sceKernelResumeThread"},
{0x75156e8f,WrapI_I<sceKernelResumeThread>,"sceKernelResumeThread"},
{0x3ad58b8c,&WrapU_V<sceKernelSuspendDispatchThread>,"sceKernelSuspendDispatchThread"},
{0x27e22ec2,&WrapU_U<sceKernelResumeDispatchThread>,"sceKernelResumeDispatchThread"},
{0x912354a7,&WrapI_I<sceKernelRotateThreadReadyQueue>,"sceKernelRotateThreadReadyQueue"},
{0x9ACE131E,sceKernelSleepThread,"sceKernelSleepThread"},
{0x82826f70,sceKernelSleepThreadCB,"sceKernelSleepThreadCB"},
{0x9ACE131E,WrapI_V<sceKernelSleepThread>,"sceKernelSleepThread"},
{0x82826f70,WrapI_V<sceKernelSleepThreadCB>,"sceKernelSleepThreadCB"},
{0xF475845D,&WrapI_IIU<sceKernelStartThread>,"sceKernelStartThread"},
{0x9944f31f,sceKernelSuspendThread,"sceKernelSuspendThread"},
{0x9944f31f,WrapI_I<sceKernelSuspendThread>,"sceKernelSuspendThread"},
{0x616403ba,WrapI_I<sceKernelTerminateThread>,"sceKernelTerminateThread"},
{0x383f7bcc,WrapI_I<sceKernelTerminateDeleteThread>,"sceKernelTerminateDeleteThread"},
{0x840E8133,WrapI_IU<sceKernelWaitThreadEndCB>,"sceKernelWaitThreadEndCB"},
@ -731,7 +734,7 @@ const HLEFunction ThreadManForUser[] =
{0xE1619D7C,WrapI_UUUU<sceKernelSysClock2USecWide>,"sceKernelSysClock2USecWide"},
{0x278C0DF5,WrapI_IU<sceKernelWaitThreadEnd>,"sceKernelWaitThreadEnd"},
{0xd59ead2f,sceKernelWakeupThread,"sceKernelWakeupThread"}, //AI Go, audio?
{0xd59ead2f,WrapI_I<sceKernelWakeupThread>,"sceKernelWakeupThread"}, //AI Go, audio?
{0x0C106E53,0,"sceKernelRegisterThreadEventHandler"},
{0x72F3C145,0,"sceKernelReleaseThreadEventHandler"},

View File

@ -19,7 +19,6 @@
#include "../../Globals.h"
#include "Common.h"
#include <cstring>
#include <map>
class PointerWrap;
@ -368,9 +367,6 @@ void sceKernelExitGameWithStatus();
int LoadExecForUser_362A956B();
void sceKernelRegisterExitCallback();
void sceKernelSleepThread();
void sceKernelSleepThreadCB();
u32 sceKernelDevkitVersion();
u32 sceKernelRegisterKprintfHandler();
@ -452,7 +448,10 @@ public:
}
else
{
T* t = dynamic_cast<T*>(pool[handle - handleOffset]);
// Previously we had a dynamic_cast here, but since RTTI was disabled traditionally,
// it just acted as a static case and everything worked. This means that we will never
// see the Wrong type object error below, but we'll just have to live with that danger.
T* t = static_cast<T*>(pool[handle - handleOffset]);
if (t == 0)
{
ERROR_LOG(HLE, "Kernel: Wrong type object %i (%08x)", handle, handle);
@ -478,15 +477,14 @@ public:
}
template <class T, typename ArgT>
void Iterate(bool func(T *, ArgT), ArgT arg)
void Iterate(bool func(T *, ArgT), ArgT arg, int type)
{
for (int i = 0; i < maxCount; i++)
{
if (!occupied[i])
continue;
T *t = dynamic_cast<T *>(pool[i]);
if (t)
{
T *t = static_cast<T *>(pool[i]);
if (t->GetIDType() == type) {
if (!func(t, arg))
break;
}
@ -497,6 +495,11 @@ public:
bool GetIDType(SceUID handle, int *type) const
{
if (handle < handleOffset || handle >= handleOffset+maxCount || !occupied[handle-handleOffset])
{
ERROR_LOG(HLE, "Kernel: Bad object handle %i (%08x)", handle, handle);
return false;
}
KernelObject *t = pool[handle - handleOffset];
*type = t->GetIDType();
return true;

View File

@ -160,8 +160,6 @@ bool __KernelUnlockEventFlagForThread(EventFlag *e, EventFlagTh &th, u32 &error,
{
if (!__KernelEventFlagMatches(&e->nef.currentPattern, th.bits, th.wait, th.outAddr))
return false;
e->nef.numWaitThreads--;
}
else
{
@ -219,7 +217,6 @@ void __KernelEventFlagBeginCallback(SceUID threadID, SceUID prevCallbackId)
waitData = *t;
// TODO: Hmm, what about priority/fifo order? Does it lose its place in line?
flag->waitingThreads.erase(flag->waitingThreads.begin() + i);
flag->nef.numWaitThreads--;
break;
}
}
@ -267,7 +264,6 @@ void __KernelEventFlagEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &r
EventFlagTh waitData = flag->pausedWaits[pauseKey];
u64 waitDeadline = waitData.pausedTimeout;
flag->pausedWaits.erase(pauseKey);
flag->nef.numWaitThreads++;
// TODO: Don't wake up if __KernelCurHasReadyCallbacks()?
@ -342,11 +338,11 @@ u32 sceKernelCancelEventFlag(SceUID uid, u32 pattern, u32 numWaitThreadsPtr)
EventFlag *e = kernelObjects.Get<EventFlag>(uid, error);
if (e)
{
e->nef.numWaitThreads = (int) e->waitingThreads.size();
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");
@ -451,7 +447,6 @@ void __KernelEventFlagTimeout(u64 userdata, int cycleslate)
// 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;
}
}
@ -520,11 +515,10 @@ int sceKernelWaitEventFlag(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32 ti
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)
if (e->waitingThreads.size() > 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;
@ -584,11 +578,10 @@ int sceKernelWaitEventFlagCB(SceUID id, u32 bits, u32 wait, u32 outBitsPtr, u32
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)
if (e->waitingThreads.size() > 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;
@ -641,7 +634,7 @@ 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)
if (e->waitingThreads.size() > 0 && (e->nef.attr & PSP_EVENT_WAITMULTIPLE) == 0)
return SCE_KERNEL_ERROR_EVF_MULTI;
// No match - return that, this is polling, not waiting.
@ -670,6 +663,16 @@ u32 sceKernelReferEventFlagStatus(SceUID id, u32 statusPtr)
if (!Memory::IsValidAddress(statusPtr))
return -1;
u32 error;
for (auto iter = e->waitingThreads.begin(); iter != e->waitingThreads.end(); ++iter)
{
SceUID waitID = __KernelGetWaitID(iter->tid, WAITTYPE_EVENTFLAG, error);
// The thread is no longer waiting for this, clean it up.
if (waitID != id)
e->waitingThreads.erase(iter--);
}
e->nef.numWaitThreads = (int) e->waitingThreads.size();
if (Memory::Read_U32(statusPtr) != 0)
Memory::WriteStruct(statusPtr, &e->nef);
return 0;

View File

@ -539,23 +539,27 @@ u32 sceKernelMemcpy(u32 dst, u32 src, u32 size)
{
DEBUG_LOG(HLE, "sceKernelMemcpy(dest=%08x, src=%08x, size=%i)", dst, src, size);
// Technically should crash if these are invalid and size > 0...
if (Memory::IsValidAddress(dst) && Memory::IsValidAddress(src + size - 1))
if (Memory::IsValidAddress(dst) && Memory::IsValidAddress(src) && Memory::IsValidAddress(dst + size - 1) && Memory::IsValidAddress(src + size - 1))
{
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)
// If it's non-overlapping, just do it in one go.
if (dst + size < src || src + size < dst)
memcpy(dstp, srcp, size);
else
{
*(u64 *) dstp = *(u64 *) srcp;
srcp += 8;
dstp += 8;
// Try to handle overlapped copies with similar properties to hardware, just in case.
// Not that anyone ought to rely on it.
for (u32 size64 = size / 8; size64 > 0; --size64)
{
memmove(dstp, srcp, 8);
dstp += 8;
srcp += 8;
}
for (u32 size8 = size % 8; size8 > 0; --size8)
*dstp++ = *srcp++;
}
while (size8-- > 0)
*dstp++ = *srcp++;
}
return dst;
}

View File

@ -890,8 +890,6 @@ bool __KernelUnlockVplForThread(VPL *vpl, VplWaitingThread &threadInfo, u32 &err
Memory::Write_U32(addr, threadInfo.addrPtr);
else
return false;
vpl->nv.numWaitThreads--;
}
if (timeoutPtr != 0 && vplWaitTimer != -1)
@ -1073,8 +1071,6 @@ void __KernelVplTimeout(u64 userdata, int cyclesLate)
// 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.
vpl->nv.numWaitThreads--;
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
}
}
@ -1108,8 +1104,6 @@ int sceKernelAllocateVpl(SceUID uid, u32 size, u32 addrPtr, u32 timeoutPtr)
{
if (vpl)
{
vpl->nv.numWaitThreads++;
SceUID threadID = __KernelGetCurThread();
__KernelVplRemoveThread(vpl, threadID);
VplWaitingThread waiting = {threadID, addrPtr};
@ -1209,9 +1203,9 @@ int sceKernelCancelVpl(SceUID uid, u32 numWaitThreadsPtr)
VPL *vpl = kernelObjects.Get<VPL>(uid, error);
if (vpl)
{
vpl->nv.numWaitThreads = (int) vpl->waitingThreads.size();
if (Memory::IsValidAddress(numWaitThreadsPtr))
Memory::Write_U32(vpl->nv.numWaitThreads, numWaitThreadsPtr);
vpl->nv.numWaitThreads = 0;
bool wokeThreads = __KernelClearVplThreads(vpl, SCE_KERNEL_ERROR_WAIT_CANCEL);
if (wokeThreads)
@ -1230,6 +1224,17 @@ int sceKernelReferVplStatus(SceUID uid, u32 infoPtr)
if (vpl)
{
DEBUG_LOG(HLE, "sceKernelReferVplStatus(%i, %08x)", uid, infoPtr);
u32 error;
for (auto iter = vpl->waitingThreads.begin(); iter != vpl->waitingThreads.end(); ++iter)
{
SceUID waitID = __KernelGetWaitID(iter->threadID, WAITTYPE_VPL, error);
// The thread is no longer waiting for this, clean it up.
if (waitID != uid)
vpl->waitingThreads.erase(iter--);
}
vpl->nv.numWaitThreads = (int) vpl->waitingThreads.size();
vpl->nv.freeSize = vpl->alloc.GetTotalFreeBytes();
if (Memory::IsValidAddress(infoPtr) && Memory::Read_U32(infoPtr))
Memory::WriteStruct(infoPtr, &vpl->nv);
@ -1326,7 +1331,7 @@ struct TLS : public KernelObject
const char *GetName() {return ntls.name;}
const char *GetTypeName() {return "TLS";}
static u32 GetMissingErrorCode() { return PSP_ERROR_UNKNOWN_TLS_ID; }
int GetIDType() const { return SCE_KERNEL_TMID_Vpl; }
int GetIDType() const { return SCE_KERNEL_TMID_Tls; }
TLS() : next(0) {}
@ -1458,7 +1463,7 @@ int sceKernelDeleteTls(SceUID uid)
int sceKernelAllocateTls(SceUID uid)
{
// TODO: Allocate downward if PSP_TLS_ATTR_HIGHMEM?
WARN_LOG(HLE, "UNIMPL sceKernelAllocateTls(%08x)", uid);
DEBUG_LOG(HLE, "sceKernelAllocateTls(%08x)", uid);
u32 error;
TLS *tls = kernelObjects.Get<TLS>(uid, error);
if (tls)
@ -1493,7 +1498,7 @@ int sceKernelAllocateTls(SceUID uid)
if (allocBlock == -1)
{
// TODO: Wait here, wake when one is free.
ERROR_LOG(HLE, "sceKernelAllocateTls: should wait");
ERROR_LOG_REPORT(HLE, "sceKernelAllocateTls: should wait");
return -1;
}

View File

@ -17,9 +17,9 @@
#include <fstream>
#include <algorithm>
#include <string>
#include "HLE.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/HLETables.h"
#include "Core/Reporting.h"
#include "Common/FileUtil.h"
#include "../Host.h"
@ -48,22 +48,6 @@ enum {
PSP_THREAD_ATTR_USER = 0x80000000
};
enum {
R_MIPS_NONE,
R_MIPS_16,
R_MIPS_32,
R_MIPS_REL32,
R_MIPS_26,
R_MIPS_HI16,
R_MIPS_LO16,
R_MIPS_GPREL16,
R_MIPS_LITERAL,
R_MIPS_GOT16,
R_MIPS_PC16,
R_MIPS_CALL16,
R_MIPS_GPREL32
};
enum {
// Function exports.
NID_MODULE_START = 0xD632ACDB,
@ -80,6 +64,12 @@ enum {
NID_MODULE_SDK_VERSION = 0x11B97506,
};
// This is a workaround for misbehaving homebrew (like TBL's Suicide Barbie (Final)).
static const char *lieAboutSuccessModules[] = {
"flash0:/kd/audiocodec.prx",
"flash0:/kd/libatrac3plus.prx",
};
static const char *blacklistedModules[] = {
"sceATRAC3plus_Library",
"sceFont_Library",
@ -161,11 +151,16 @@ struct ModuleInfo {
char name[28];
};
struct ModuleWaitingThread
{
SceUID threadID;
u32 statusPtr;
};
class Module : public KernelObject
{
public:
Module() : memoryBlockAddr(0), isFake(false) {}
Module() : memoryBlockAddr(0), isFake(false), isStarted(false) {}
~Module() {
if (memoryBlockAddr) {
userMemory.Free(memoryBlockAddr);
@ -190,14 +185,21 @@ public:
p.Do(nm);
p.Do(memoryBlockAddr);
p.Do(memoryBlockSize);
p.Do(isFake);
p.Do(isStarted);
ModuleWaitingThread mwt = {0};
p.Do(waitingThreads, mwt);
p.DoMarker("Module");
}
NativeModule nm;
std::vector<ModuleWaitingThread> waitingThreads;
u32 memoryBlockAddr;
u32 memoryBlockSize;
bool isFake;
// Probably one of the NativeModule fields, but not sure...
bool isStarted;
};
KernelObject *__KernelModuleObject()
@ -283,12 +285,15 @@ void __KernelModuleShutdown()
{
unresolvedVars.clear();
exportedVars.clear();
MIPSAnalyst::Shutdown();
}
void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type)
{
static u32 lastHI16RelocAddress = 0;
static u32 lastHI16ExportAddress = 0;
static bool lastHI16Processed = false;
static u32 lastHILO16Address = 0;
u32 relocData = Memory::Read_Instruction(relocAddress);
@ -313,10 +318,14 @@ void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type)
*/
case R_MIPS_HI16:
if (!lastHI16Processed)
WARN_LOG_REPORT(LOADER, "Unsafe unpaired HI16 variable relocation @ %08x / %08x", lastHI16RelocAddress, relocAddress);
// After this will be an R_MIPS_LO16. If that addition overflows, we need to account for it in HI16.
// The R_MIPS_LO16 and R_MIPS_HI16 will often be *different* relocAddress values.
lastHI16RelocAddress = relocAddress;
lastHI16ExportAddress = exportAddress;
lastHI16Processed = false;
break;
case R_MIPS_LO16:
@ -327,18 +336,24 @@ void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type)
ERROR_LOG_REPORT(LOADER, "HI16 and LO16 imports do not match for %08x => %08x/%08x (hi16 export: %08x)", exportAddress, lastHI16RelocAddress, relocAddress, lastHI16ExportAddress);
break;
}
u32 relocDataHi = Memory::Read_Instruction(lastHI16RelocAddress);
// Sign extend the existing low value (e.g. from addiu.)
u32 full = (relocDataHi << 16) + (s16)(u16)(relocData & 0xFFFF) + exportAddress;
// The low instruction will be a signed add, which means (full & 0x8000) will subtract.
// We add 1 in that case so that it ends up the right value.
u16 high = (full >> 16) + ((full & 0x8000) ? 1 : 0);
Memory::Write_U32((relocDataHi & ~0xFFFF) | high, lastHI16RelocAddress);
if (!lastHI16Processed)
{
// The low instruction will be a signed add, which means (full & 0x8000) will subtract.
// We add 1 in that case so that it ends up the right value.
u16 high = (full >> 16) + ((full & 0x8000) ? 1 : 0);
Memory::Write_U32((relocDataHi & ~0xFFFF) | high, lastHI16RelocAddress);
lastHI16Processed = true;
}
else if (lastHILO16Address != full)
WARN_LOG_REPORT(LOADER, "Potentially unsafe unpaired LO16 variable relocation @ %08x / %08x", lastHI16RelocAddress, relocAddress);
// And then this is the low relocation, hurray.
relocData = (relocData & ~0xFFFF) | (full & 0xFFFF);
lastHILO16Address = full;
}
break;
@ -402,8 +417,7 @@ void ExportVarSymbol(const char *moduleName, u32 nid, u32 address)
}
}
Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *error_string)
{
Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *error_string, u32 *magic) {
Module *module = new Module;
kernelObjects.Create(module);
memset(&module->nm, 0, sizeof(module->nm));
@ -413,9 +427,8 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
INFO_LOG(HLE, "~SCE module, skipping header");
ptr += *(u32*)(ptr + 4);
}
if (*(u32*)ptr == 0x5053507e) { // "~PSP"
// Decrypt module! YAY!
*magic = *(u32*)ptr;
if (*magic == 0x5053507e) { // "~PSP"
INFO_LOG(HLE, "Decrypting ~PSP file");
PSP_Header *head = (PSP_Header*)ptr;
const u8 *in = ptr;
@ -442,29 +455,26 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
{
ERROR_LOG(HLE, "Failed decrypting PRX! That's not normal! ret = %i\n", ret);
Reporting::ReportMessage("Failed decrypting the PRX (ret = %i, size = %i, psp_size = %i)!", ret, head->elf_size, head->psp_size);
// Fall through to safe exit in the next check.
}
}
if (*(u32*)ptr != 0x464c457f)
{
// DO NOT change to else if, see above.
if (*(u32*)ptr != 0x464c457f) {
ERROR_LOG_REPORT(HLE, "Wrong magic number %08x", *(u32*)ptr);
*error_string = "File corrupt";
if (newptr) {
if (newptr)
delete [] newptr;
}
kernelObjects.Destroy<Module>(module->GetUID());
return 0;
}
// Open ELF reader
ElfReader reader((void*)ptr);
if (!reader.LoadInto(loadAddress))
{
if (!reader.LoadInto(loadAddress)) {
ERROR_LOG(HLE, "LoadInto failed");
if (newptr)
{
delete [] newptr;
}
kernelObjects.Destroy<Module>(module->GetUID());
return 0;
}
@ -516,8 +526,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
SectionID textSection = reader.GetSectionByName(".text");
if (textSection != -1)
{
if (textSection != -1) {
u32 textStart = reader.GetSectionAddr(textSection);
u32 textSize = reader.GetSectionSize(textSection);
@ -801,9 +810,8 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
module->nm.entry_addr = module->nm.module_start_func;
if (newptr)
{
delete [] newptr;
}
return module;
}
@ -824,7 +832,8 @@ bool __KernelLoadPBP(const char *filename, std::string *error_string)
size_t elfSize;
u8 *elfData = pbp.GetSubFile(PBP_EXECUTABLE_PSP, &elfSize);
Module *module = __KernelLoadELFFromPtr(elfData, PSP_GetDefaultLoadAddress(), error_string);
u32 magic;
Module *module = __KernelLoadELFFromPtr(elfData, PSP_GetDefaultLoadAddress(), error_string, &magic);
if (!module) {
delete [] elfData;
return false;
@ -851,11 +860,13 @@ Module *__KernelLoadModule(u8 *fileptr, SceKernelLMOption *options, std::string
offsets[0] = offset0;
for (int i = 1; i < numfiles; i++)
memcpy(&offsets[i], fileptr + 12 + 4*i, 4);
module = __KernelLoadELFFromPtr(fileptr + offsets[5], PSP_GetDefaultLoadAddress(), error_string);
u32 magic = 0;
module = __KernelLoadELFFromPtr(fileptr + offsets[5], PSP_GetDefaultLoadAddress(), error_string, &magic);
}
else
{
module = __KernelLoadELFFromPtr(fileptr, PSP_GetDefaultLoadAddress(), error_string);
u32 magic = 0;
module = __KernelLoadELFFromPtr(fileptr, PSP_GetDefaultLoadAddress(), error_string, &magic);
}
return module;
@ -863,14 +874,15 @@ Module *__KernelLoadModule(u8 *fileptr, SceKernelLMOption *options, std::string
void __KernelStartModule(Module *m, int args, const char *argp, SceKernelSMOption *options)
{
m->isStarted = true;
if (m->nm.module_start_func != 0 && m->nm.module_start_func != (u32)-1)
{
if (m->nm.module_start_func != m->nm.entry_addr)
WARN_LOG_REPORT(LOADER, "Main module has start func (%08x) different from entry (%08x)?", m->nm.module_start_func, m->nm.entry_addr);
}
__KernelSetupRootThread(m->GetUID(), args, argp, options->priority, options->stacksize, options->attribute);
//TODO: if current thread, put it in wait state, waiting for the new thread
SceUID threadID = __KernelSetupRootThread(m->GetUID(), args, argp, options->priority, options->stacksize, options->attribute);
__KernelSetThreadRA(threadID, NID_MODULERETURN);
}
@ -995,6 +1007,18 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr)
return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
}
for (size_t i = 0; i < ARRAY_SIZE(lieAboutSuccessModules); i++) {
if (!strcmp(name, lieAboutSuccessModules[i])) {
INFO_LOG(LOADER, "Tries to load module %s. We return a fake module.", lieAboutSuccessModules[i]);
Module *module = new Module;
kernelObjects.Create(module);
memset(&module->nm, 0, sizeof(module->nm));
module->isFake = true;
return module->GetUID();
}
}
PSPFileInfo info = pspFileSystem.GetFileInfo(name);
std::string error_string;
s64 size = (s64)info.size;
@ -1022,11 +1046,17 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr)
u8 *temp = new u8[(int)size];
u32 handle = pspFileSystem.OpenFile(name, FILEACCESS_READ);
pspFileSystem.ReadFile(handle, temp, (size_t)size);
module = __KernelLoadELFFromPtr(temp, 0, &error_string);
u32 magic;
module = __KernelLoadELFFromPtr(temp, 0, &error_string, &magic);
delete [] temp;
pspFileSystem.CloseFile(handle);
if (!module) {
if (magic == 0x46535000) {
ERROR_LOG(LOADER, "Game tried to load an SFO as a module. Go figure? Magic = %08x", magic);
return -1;
}
// Module was blacklisted or couldn't be decrypted, which means it's a kernel module we don't want to run.
// Let's just act as if it worked.
NOTICE_LOG(LOADER, "Module %s is blacklisted or undecryptable - we lie about success", name);
@ -1045,6 +1075,13 @@ u32 sceKernelLoadModule(const char *name, u32 flags, u32 optionAddr)
return hleDelayResult(module->GetUID(), "module loaded", 500);
}
u32 sceKernelLoadModuleNpDrm(const char *name, u32 flags, u32 optionAddr)
{
DEBUG_LOG(LOADER, "sceKernelLoadModuleNpDrm(%s, %08x)", name, flags);
return sceKernelLoadModule(name, flags, optionAddr);
}
void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr)
{
u32 priority = 0x20;
@ -1053,7 +1090,7 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
int stackPartition = 0;
SceKernelSMOption smoption;
if (optionAddr) {
Memory::ReadStruct(optionAddr, &smoption);;
Memory::ReadStruct(optionAddr, &smoption);
}
u32 error;
Module *module = kernelObjects.Get<Module>(moduleId, error);
@ -1063,8 +1100,17 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
} else if (module->isFake) {
INFO_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): faked (undecryptable module)",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
if (returnValueAddr)
Memory::Write_U32(0, returnValueAddr);
RETURN(moduleId);
return;
} else if (module->isStarted) {
ERROR_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x) : already started",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
// TODO: Maybe should be SCE_KERNEL_ERROR_ALREADY_STARTED, but I get SCE_KERNEL_ERROR_ERROR.
// But I also get crashes...
RETURN(SCE_KERNEL_ERROR_ERROR);
return;
} else {
INFO_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
@ -1072,20 +1118,23 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
int attribute = module->nm.attribute;
u32 entryAddr = module->nm.entry_addr;
if ((entryAddr == -1) || entryAddr == module->memoryBlockAddr - 1)
if (module->nm.module_start_func != 0 && module->nm.module_start_func != (u32)-1)
{
if (module->nm.module_start_func != 0 && module->nm.module_start_func != (u32)-1)
{
entryAddr = module->nm.module_start_func;
attribute = module->nm.module_start_thread_attr;
}
else if (optionAddr)
entryAddr = module->nm.module_start_func;
attribute = module->nm.module_start_thread_attr;
}
else if ((entryAddr == (u32)-1) || entryAddr == module->memoryBlockAddr - 1)
{
if (optionAddr)
{
// TODO: Does sceKernelStartModule() really give an error when no entry only if you pass options?
attribute = smoption.attribute;
}
else
{
// TODO: Fix, check return value? Or do we call nothing?
// TODO: Why are we just returning the module ID in this case?
WARN_LOG(HLE, "sceKernelStartModule(): module has no start or entry func");
module->isStarted = true;
RETURN(moduleId);
return;
}
@ -1106,39 +1155,105 @@ void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValu
}
SceUID threadID = __KernelCreateThread(module->nm.name, moduleId, entryAddr, priority, stacksize, attribute, 0);
sceKernelStartThread(threadID, argsize, argAddr);
// TODO: This will probably return the wrong value?
sceKernelWaitThreadEnd(threadID, 0);
__KernelSetThreadRA(threadID, NID_MODULERETURN);
__KernelWaitCurThread(WAITTYPE_MODULE, moduleId, 1, 0, false, "started module");
const ModuleWaitingThread mwt = {__KernelGetCurThread(), returnValueAddr};
module->waitingThreads.push_back(mwt);
}
else if (entryAddr == 0)
{
INFO_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)",
INFO_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): no entry address",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
WARN_LOG(HLE, "No Entry Address");
module->isStarted = true;
}
else
{
ERROR_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)",
ERROR_LOG(HLE, "sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x): invalid entry address",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
ERROR_LOG(HLE, "Invalid Entry Address");
RETURN(-1);
return;
}
}
// TODO: Is this the correct return value?
// JPCSP returns this value as well.
RETURN(moduleId);
}
u32 sceKernelStopModule(u32 moduleId, u32 argSize, u32 argAddr, u32 returnValueAddr, u32 optionAddr)
{
ERROR_LOG(HLE,"UNIMPL sceKernelStopModule(%08x, %08x, %08x, %08x, %08x)", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
u32 priority = 0x20;
u32 stacksize = 0x40000;
u32 attr = 0;
// TODO: In a lot of cases (even for errors), this should resched. Needs testing.
u32 error;
Module *module = kernelObjects.Get<Module>(moduleId, error);
if (!module)
{
ERROR_LOG(HLE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): invalid module id", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
return error;
}
if (module->isFake)
{
INFO_LOG(HLE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x) - faking", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
if (returnValueAddr)
Memory::Write_U32(0, returnValueAddr);
return 0;
}
if (!module->isStarted)
{
ERROR_LOG(HLE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): already stopped", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
return SCE_KERNEL_ERROR_ALREADY_STOPPED;
}
u32 stopFunc = module->nm.module_stop_func;
if (module->nm.module_stop_thread_priority != 0)
priority = module->nm.module_stop_thread_priority;
if (module->nm.module_stop_thread_stacksize != 0)
stacksize = module->nm.module_stop_thread_stacksize;
if (module->nm.module_stop_thread_attr != 0)
attr = module->nm.module_stop_thread_attr;
// TODO: Need to test how this really works. Let's assume it's an override.
if (Memory::IsValidAddress(optionAddr))
{
auto options = Memory::GetStruct<SceKernelSMOption>(optionAddr);
// TODO: Check how size handling actually works.
if (options->size != 0 && options->priority != 0)
priority = options->priority;
if (options->size != 0 && options->stacksize != 0)
stacksize = options->stacksize;
if (options->size != 0 && options->attribute != 0)
attr = options->attribute;
// TODO: Maybe based on size?
else if (attr != 0)
WARN_LOG_REPORT(HLE, "Stopping module with attr=%x, but options specify 0", attr);
}
if (Memory::IsValidAddress(stopFunc))
{
SceUID threadID = __KernelCreateThread(module->nm.name, moduleId, stopFunc, priority, stacksize, attr, 0);
sceKernelStartThread(threadID, argSize, argAddr);
__KernelSetThreadRA(threadID, NID_MODULERETURN);
__KernelWaitCurThread(WAITTYPE_MODULE, moduleId, 1, 0, false, "stopped module");
const ModuleWaitingThread mwt = {__KernelGetCurThread(), returnValueAddr};
module->waitingThreads.push_back(mwt);
}
else if (stopFunc == 0)
{
INFO_LOG(HLE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): no stop func, skipping", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
module->isStarted = false;
}
else
{
ERROR_LOG_REPORT(HLE, "sceKernelStopModule(%08x, %08x, %08x, %08x, %08x): bad stop func address", moduleId, argSize, argAddr, returnValueAddr, optionAddr);
module->isStarted = false;
}
// We should call the "stop" entry point and return the value in returnValueAddr. See StartModule.
// Possibly also kill all its threads?
return 0;
}
@ -1151,7 +1266,7 @@ u32 sceKernelUnloadModule(u32 moduleId)
return error;
kernelObjects.Destroy<Module>(moduleId);
return 0;
return moduleId;
}
u32 sceKernelStopUnloadSelfModuleWithStatus(u32 exitCode, u32 argSize, u32 argp, u32 statusAddr, u32 optionAddr)
@ -1162,6 +1277,43 @@ u32 sceKernelStopUnloadSelfModuleWithStatus(u32 exitCode, u32 argSize, u32 argp,
return 0;
}
void __KernelReturnFromModuleFunc()
{
// Return from the thread as normal.
__KernelReturnFromThread();
SceUID leftModuleID = __KernelGetCurThreadModuleId();
SceUID leftThreadID = __KernelGetCurThread();
int exitStatus = sceKernelGetThreadExitStatus(leftThreadID);
// Reschedule immediately (to leave the thread) and delete it and its stack.
__KernelReSchedule("returned from module");
sceKernelDeleteThread(leftThreadID);
u32 error;
Module *module = kernelObjects.Get<Module>(leftModuleID, error);
if (!module)
{
ERROR_LOG_REPORT(HLE, "Returned from deleted module start/stop func");
return;
}
// We can't be starting and stopping at the same time, so no need to differentiate.
module->isStarted = !module->isStarted;
for (auto it = module->waitingThreads.begin(), end = module->waitingThreads.end(); it < end; ++it)
{
// Still waiting?
SceUID waitingModuleID = __KernelGetWaitID(it->threadID, WAITTYPE_MODULE, error);
if (waitingModuleID == leftModuleID)
{
if (it->statusPtr != 0)
Memory::Write_U32(exitStatus, it->statusPtr);
__KernelResumeThreadFromWait(it->threadID, 0);
}
}
module->waitingThreads.clear();
}
struct GetModuleIdByAddressArg
{
u32 addr;
@ -1185,7 +1337,7 @@ u32 sceKernelGetModuleIdByAddress(u32 moduleAddr)
state.addr = moduleAddr;
state.result = SCE_KERNEL_ERROR_UNKNOWN_MODULE;
kernelObjects.Iterate(&__GetModuleIdByAddressIterator, &state);
kernelObjects.Iterate(&__GetModuleIdByAddressIterator, &state, PPSSPP_KERNEL_TMID_Module);
if (state.result == SCE_KERNEL_ERROR_UNKNOWN_MODULE)
ERROR_LOG(HLE, "sceKernelGetModuleIdByAddress(%08x): module not found", moduleAddr)
else
@ -1224,12 +1376,21 @@ u32 sceKernelLoadModuleByID(u32 id, u32 flags, u32 lmoptionPtr)
Module *module = 0;
u8 *temp = new u8[size];
pspFileSystem.ReadFile(handle, temp, size);
module = __KernelLoadELFFromPtr(temp, 0, &error_string);
u32 magic;
module = __KernelLoadELFFromPtr(temp, 0, &error_string, &magic);
delete [] temp;
if (!module) {
// Some games try to load strange stuff as PARAM.SFO as modules and expect it to fail.
// This checks for the SFO magic number.
if (magic == 0x46535000) {
ERROR_LOG(LOADER, "Game tried to load an SFO as a module. Go figure? Magic = %08x", magic);
return -1;
}
// Module was blacklisted or couldn't be decrypted, which means it's a kernel module we don't want to run.
// Let's just act as if it worked.
NOTICE_LOG(LOADER, "Module %d is blacklisted or undecryptable - we lie about success", id);
return 1;
}
@ -1297,7 +1458,7 @@ const HLEFunction ModuleMgrForUser[] =
{0x8f2df740,WrapU_UUUUU<sceKernelStopUnloadSelfModuleWithStatus>,"sceKernelStopUnloadSelfModuleWithStatus"},
{0xfef27dc1,&WrapU_CU<sceKernelLoadModuleDNAS> , "sceKernelLoadModuleDNAS"},
{0x644395e2,0,"sceKernelGetModuleIdList"},
{0xf2d8d1b4,0,"ModuleMgrForUser_F2D8D1B4"},
{0xf2d8d1b4,&WrapU_CUU<sceKernelLoadModuleNpDrm>,"sceKernelLoadModuleNpDrm"},
};

View File

@ -26,5 +26,6 @@ void __KernelModuleShutdown();
u32 __KernelGetModuleGP(SceUID module);
bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std::string *error_string);
void __KernelReturnFromModuleFunc();
void Register_ModuleMgrForUser();

View File

@ -82,22 +82,6 @@ struct Mutex : public KernelObject
};
struct NativeLwMutex
{
SceSize size;
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
SceUInt attr;
SceUID uid;
u32 workareaPtr;
int initialCount;
// Not kept up to date.
int currentCount;
// Not kept up to date.
SceUID lockThread;
// Not kept up to date.
int numWaitThreads;
};
struct NativeLwMutexWorkarea
{
int lockLevel;
@ -120,6 +104,22 @@ struct NativeLwMutexWorkarea
}
};
struct NativeLwMutex
{
SceSize size;
char name[KERNELOBJECT_MAX_NAME_LENGTH + 1];
SceUInt attr;
SceUID uid;
PSPPointer<NativeLwMutexWorkarea> workarea;
int initialCount;
// Not kept up to date.
int currentCount;
// Not kept up to date.
SceUID lockThread;
// Not kept up to date.
int numWaitThreads;
};
struct LwMutex : public KernelObject
{
const char *GetName() {return nm.name;}
@ -692,7 +692,15 @@ int sceKernelReferMutexStatus(SceUID id, u32 infoAddr)
// Don't write if the size is 0. Anything else is A-OK, though, apparently.
if (Memory::Read_U32(infoAddr) != 0)
{
// Refresh and write
u32 error;
for (auto iter = m->waitingThreads.begin(); iter != m->waitingThreads.end(); ++iter)
{
SceUID waitID = __KernelGetWaitID(*iter, WAITTYPE_MUTEX, error);
// The thread is no longer waiting for this, clean it up.
if (waitID != id)
m->waitingThreads.erase(iter--);
}
m->nm.numWaitThreads = (int) m->waitingThreads.size();
Memory::WriteStruct(infoAddr, &m->nm);
}
@ -724,7 +732,7 @@ int sceKernelCreateLwMutex(u32 workareaPtr, const char *name, u32 attr, int init
mutex->nm.name[KERNELOBJECT_MAX_NAME_LENGTH] = 0;
mutex->nm.attr = attr;
mutex->nm.uid = id;
mutex->nm.workareaPtr = workareaPtr;
mutex->nm.workarea = workareaPtr;
mutex->nm.initialCount = initialCount;
auto workarea = Memory::GetStruct<NativeLwMutexWorkarea>(workareaPtr);
workarea->init();
@ -982,8 +990,7 @@ void __KernelLwMutexEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &ret
// TODO: Don't wake up if __KernelCurHasReadyCallbacks()?
// Attempt to unlock.
auto workarea = Memory::GetStruct<NativeLwMutexWorkarea>(mutex->nm.workareaPtr);
if (mutex->nm.lockThread == -1 && __KernelUnlockLwMutexForThread(mutex, workarea, threadID, error, 0))
if (mutex->nm.lockThread == -1 && __KernelUnlockLwMutexForThread(mutex, mutex->nm.workarea, threadID, error, 0))
return;
// We only check if it timed out if it couldn't unlock.
@ -1142,7 +1149,16 @@ int __KernelReferLwMutexStatus(SceUID uid, u32 infoPtr)
if (Memory::Read_U32(infoPtr) != 0)
{
auto workarea = Memory::GetStruct<NativeLwMutexWorkarea>(m->nm.workareaPtr);
auto workarea = m->nm.workarea;
u32 error;
for (auto iter = m->waitingThreads.begin(); iter != m->waitingThreads.end(); ++iter)
{
SceUID waitID = __KernelGetWaitID(*iter, WAITTYPE_LWMUTEX, error);
// The thread is no longer waiting for this, clean it up.
if (waitID != uid)
m->waitingThreads.erase(iter--);
}
// Refresh and write
m->nm.currentCount = workarea->lockLevel;

View File

@ -115,7 +115,6 @@ bool __KernelUnlockSemaForThread(Semaphore *s, SceUID threadID, u32 &error, int
return false;
s->ns.currentCount -= wVal;
s->ns.numWaitThreads--;
}
if (timeoutPtr != 0 && semaWaitTimer != -1)
@ -157,7 +156,6 @@ void __KernelSemaBeginCallback(SceUID threadID, SceUID prevCallbackId)
// TODO: Hmm, what about priority/fifo order? Does it lose its place in line?
s->waitingThreads.erase(std::remove(s->waitingThreads.begin(), s->waitingThreads.end(), threadID), s->waitingThreads.end());
s->ns.numWaitThreads--;
DEBUG_LOG(HLE, "sceKernelWaitSemaCB: Suspending sema wait for callback");
}
@ -188,7 +186,6 @@ void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &return
u64 waitDeadline = s->pausedWaitTimeouts[pauseKey];
s->pausedWaitTimeouts.erase(pauseKey);
s->ns.numWaitThreads++;
// TODO: Don't wake up if __KernelCurHasReadyCallbacks()?
@ -205,7 +202,6 @@ void __KernelSemaEndCallback(SceUID threadID, SceUID prevCallbackId, u32 &return
Memory::Write_U32(0, timeoutPtr);
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
s->ns.numWaitThreads--;
}
else
{
@ -244,6 +240,7 @@ int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr)
if (newCount > s->ns.maxCount)
return SCE_KERNEL_ERROR_ILLEGAL_COUNT;
s->ns.numWaitThreads = (int) s->waitingThreads.size();
if (Memory::IsValidAddress(numWaitThreadsPtr))
Memory::Write_U32(s->ns.numWaitThreads, numWaitThreadsPtr);
@ -251,7 +248,6 @@ int sceKernelCancelSema(SceUID id, int newCount, u32 numWaitThreadsPtr)
s->ns.currentCount = s->ns.initCount;
else
s->ns.currentCount = newCount;
s->ns.numWaitThreads = 0;
if (__KernelClearSemaThreads(s, SCE_KERNEL_ERROR_WAIT_CANCEL))
hleReSchedule("semaphore canceled");
@ -336,6 +332,16 @@ int sceKernelReferSemaStatus(SceUID id, u32 infoPtr)
if (!Memory::IsValidAddress(infoPtr))
return -1;
u32 error;
for (auto iter = s->waitingThreads.begin(); iter != s->waitingThreads.end(); ++iter)
{
SceUID waitID = __KernelGetWaitID(*iter, WAITTYPE_SEMA, error);
// The thread is no longer waiting for this, clean it up.
if (waitID != id)
s->waitingThreads.erase(iter--);
}
s->ns.numWaitThreads = (int) s->waitingThreads.size();
if (Memory::Read_U32(infoPtr) != 0)
Memory::WriteStruct(infoPtr, &s->ns);
return 0;
@ -353,7 +359,7 @@ int sceKernelSignalSema(SceUID id, int signal)
Semaphore *s = kernelObjects.Get<Semaphore>(id, error);
if (s)
{
if (s->ns.currentCount + signal - s->ns.numWaitThreads > s->ns.maxCount)
if (s->ns.currentCount + signal - (int) s->waitingThreads.size() > s->ns.maxCount)
return SCE_KERNEL_ERROR_SEMA_OVF;
int oldval = s->ns.currentCount;
@ -403,8 +409,6 @@ void __KernelSemaTimeout(u64 userdata, int cycleslate)
// The reason is, if it times out, but what it was waiting on is DELETED prior to it
// actually running, it will get a DELETE result instead of a TIMEOUT.
// So, we need to remember it or we won't be able to mark it DELETE instead later.
s->ns.numWaitThreads--;
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
}
}
@ -437,14 +441,10 @@ int __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, const char *bad
// If there are any callbacks, we always wait, and wake after the callbacks.
bool hasCallbacks = processCallbacks && __KernelCurHasReadyCallbacks();
if (s->ns.currentCount >= wantedCount && s->ns.numWaitThreads == 0 && !hasCallbacks)
if (s->ns.currentCount >= wantedCount && s->waitingThreads.size() == 0 && !hasCallbacks)
{
if (hasCallbacks)
{
// __KernelSemaBeginCallback() will decrement this, so increment it here.
// TODO: Clean this up a bit better.
s->ns.numWaitThreads++;
// Might actually end up having to wait, so set the timeout.
__KernelSetSemaTimeout(s, timeoutPtr);
__KernelWaitCallbacksCurThread(WAITTYPE_SEMA, id, wantedCount, timeoutPtr);
@ -454,8 +454,6 @@ int __KernelWaitSema(SceUID id, int wantedCount, u32 timeoutPtr, const char *bad
}
else
{
s->ns.numWaitThreads++;
SceUID threadID = __KernelGetCurThread();
// May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates.
if (std::find(s->waitingThreads.begin(), s->waitingThreads.end(), threadID) == s->waitingThreads.end())
@ -499,7 +497,7 @@ int sceKernelPollSema(SceUID id, int wantedCount)
Semaphore *s = kernelObjects.Get<Semaphore>(id, error);
if (s)
{
if (s->ns.currentCount >= wantedCount && s->ns.numWaitThreads == 0)
if (s->ns.currentCount >= wantedCount && s->waitingThreads.size() == 0)
{
s->ns.currentCount -= wantedCount;
return 0;

View File

@ -728,7 +728,7 @@ struct WaitTypeFuncs
void __KernelExecuteMipsCallOnCurrentThread(u32 callId, bool reschedAfter);
Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr);
void __KernelResetThread(Thread *t);
void __KernelResetThread(Thread *t, int lowestPriority);
void __KernelCancelWakeup(SceUID threadID);
void __KernelCancelThreadEndTimeout(SceUID threadID);
bool __KernelCheckThreadCallbacks(Thread *thread, bool force);
@ -746,6 +746,7 @@ u32 threadReturnHackAddr;
u32 cbReturnHackAddr;
u32 intReturnHackAddr;
u32 extendReturnHackAddr;
u32 moduleReturnHackAddr;
std::vector<ThreadCallback> threadEndListeners;
// Lists all thread ids that aren't deleted/etc.
@ -844,18 +845,52 @@ u32 __KernelInterruptReturnAddress()
return intReturnHackAddr;
}
u32 __KernelSetThreadRA(SceUID threadID, u32 nid)
{
u32 newRA;
switch (nid)
{
case NID_MODULERETURN:
newRA = moduleReturnHackAddr;
break;
default:
ERROR_LOG_REPORT(HLE, "__KernelSetThreadRA(): invalid RA address");
return -1;
}
if (threadID == currentThread)
currentMIPS->r[MIPS_REG_RA] = newRA;
else
{
u32 error;
Thread *thread = kernelObjects.Get<Thread>(threadID, error);
if (!thread)
return error;
thread->context.r[MIPS_REG_RA] = newRA;
}
return 0;
}
void hleScheduledWakeup(u64 userdata, int cyclesLate);
void hleThreadEndTimeout(u64 userdata, int cyclesLate);
void __KernelWriteFakeSysCall(u32 nid, u32 &ptr, u32 &pos)
void __KernelWriteFakeSysCall(u32 nid, u32 *ptr, u32 &pos)
{
ptr = pos;
*ptr = pos;
pos += 8;
WriteSyscall("FakeSysCalls", nid, ptr);
WriteSyscall("FakeSysCalls", nid, *ptr);
}
void __KernelThreadingInit()
{
struct ThreadHack
{
u32 nid;
u32 *addr;
};
// Yeah, this is straight out of JPCSP, I should be ashamed.
const static u32 idleThreadCode[] = {
MIPS_MAKE_ADDIU(MIPS_REG_A0, MIPS_REG_ZERO, 0),
@ -865,7 +900,16 @@ void __KernelThreadingInit()
MIPS_MAKE_SYSCALL("FakeSysCalls", "_sceKernelIdle"),
MIPS_MAKE_BREAK(),
};
u32 blockSize = sizeof(idleThreadCode) + 4 * 2 * 4; // The thread code above plus 4 8-byte "hacks"
// If you add another func here, don't forget __KernelThreadingDoState() below.
static ThreadHack threadHacks[] = {
{NID_THREADRETURN, &threadReturnHackAddr},
{NID_CALLBACKRETURN, &cbReturnHackAddr},
{NID_INTERRUPTRETURN, &intReturnHackAddr},
{NID_EXTENDRETURN, &extendReturnHackAddr},
{NID_MODULERETURN, &moduleReturnHackAddr},
};
u32 blockSize = sizeof(idleThreadCode) + ARRAY_SIZE(threadHacks) * 2 * 4; // The thread code above plus 8 bytes per "hack"
dispatchEnabled = true;
memset(waitTypeFuncs, 0, sizeof(waitTypeFuncs));
@ -879,11 +923,9 @@ void __KernelThreadingInit()
Memory::Memcpy(idleThreadHackAddr, idleThreadCode, sizeof(idleThreadCode));
u32 pos = idleThreadHackAddr + sizeof(idleThreadCode);
// IF you add another func here, add it to the allocation above, and also to DoState below.
__KernelWriteFakeSysCall(NID_THREADRETURN, threadReturnHackAddr, pos);
__KernelWriteFakeSysCall(NID_CALLBACKRETURN, cbReturnHackAddr, pos);
__KernelWriteFakeSysCall(NID_INTERRUPTRETURN, intReturnHackAddr, pos);
__KernelWriteFakeSysCall(NID_EXTENDRETURN, extendReturnHackAddr, pos);
for (size_t i = 0; i < ARRAY_SIZE(threadHacks); ++i) {
__KernelWriteFakeSysCall(threadHacks[i].nid, threadHacks[i].addr, pos);
}
eventScheduledWakeup = CoreTiming::RegisterEvent("ScheduledWakeup", &hleScheduledWakeup);
eventThreadEndTimeout = CoreTiming::RegisterEvent("ThreadEndTimeout", &hleThreadEndTimeout);
@ -892,8 +934,8 @@ void __KernelThreadingInit()
// Create the two idle threads, as well. With the absolute minimal possible priority.
// 4096 stack size - don't know what the right value is. Hm, if callbacks are ever to run on these threads...
__KernelResetThread(__KernelCreateThread(threadIdleID[0], 0, "idle0", idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL));
__KernelResetThread(__KernelCreateThread(threadIdleID[1], 0, "idle1", idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL));
__KernelResetThread(__KernelCreateThread(threadIdleID[0], 0, "idle0", idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL), 0);
__KernelResetThread(__KernelCreateThread(threadIdleID[1], 0, "idle1", idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL), 0);
// These idle threads are later started in LoadExec, which calls __KernelStartIdleThreads below.
__KernelListenThreadEnd(__KernelCancelWakeup);
@ -910,6 +952,7 @@ void __KernelThreadingDoState(PointerWrap &p)
p.Do(cbReturnHackAddr);
p.Do(intReturnHackAddr);
p.Do(extendReturnHackAddr);
p.Do(moduleReturnHackAddr);
p.Do(currentThread);
SceUID dv = 0;
@ -1256,9 +1299,8 @@ u32 sceKernelReferThreadRunStatus(u32 threadID, u32 statusPtr)
return 0;
}
void sceKernelGetThreadExitStatus()
int sceKernelGetThreadExitStatus(SceUID threadID)
{
SceUID threadID = PARAM(0);
if (threadID == 0)
threadID = __KernelGetCurThread();
@ -1269,17 +1311,17 @@ void sceKernelGetThreadExitStatus()
if (t->nt.status == THREADSTATUS_DORMANT) // TODO: can be dormant before starting, too, need to avoid that
{
DEBUG_LOG(HLE,"sceKernelGetThreadExitStatus(%i)", threadID);
RETURN(t->nt.exitStatus);
return t->nt.exitStatus;
}
else
{
RETURN(SCE_KERNEL_ERROR_NOT_DORMANT);
return SCE_KERNEL_ERROR_NOT_DORMANT;
}
}
else
{
ERROR_LOG(HLE,"sceKernelGetThreadExitStatus Error %08x", error);
RETURN(SCE_KERNEL_ERROR_UNKNOWN_THID);
return SCE_KERNEL_ERROR_UNKNOWN_THID;
}
}
@ -1468,7 +1510,7 @@ void __KernelWaitCurThread(WaitType type, SceUID waitID, u32 waitValue, u32 time
Thread *thread = __GetCurrentThread();
thread->nt.waitID = waitID;
thread->nt.waitType = type;
__KernelChangeThreadState(thread, THREADSTATUS_WAIT);
__KernelChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->nt.status & THREADSTATUS_SUSPEND)));
thread->nt.numReleases++;
thread->waitInfo.waitValue = waitValue;
thread->waitInfo.timeoutPtr = timeoutPtr;
@ -1495,7 +1537,7 @@ void __KernelWaitCallbacksCurThread(WaitType type, SceUID waitID, u32 waitValue,
Thread *thread = __GetCurrentThread();
thread->nt.waitID = waitID;
thread->nt.waitType = type;
__KernelChangeThreadState(thread, THREADSTATUS_WAIT);
__KernelChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->nt.status & THREADSTATUS_SUSPEND)));
// TODO: Probably not...?
thread->nt.numReleases++;
thread->waitInfo.waitValue = waitValue;
@ -1705,14 +1747,17 @@ void ThreadContext::reset()
lo = 0;
}
void __KernelResetThread(Thread *t)
void __KernelResetThread(Thread *t, int lowestPriority)
{
t->context.reset();
t->context.hi = 0;
t->context.lo = 0;
t->context.pc = t->nt.entrypoint;
// TODO: Reset the priority?
// If the thread would be better than lowestPriority, reset to its initial. Yes, kinda odd...
if (t->nt.currentPriority < lowestPriority)
t->nt.currentPriority = t->nt.initialPriority;
t->nt.waitType = WAITTYPE_NONE;
t->nt.waitID = 0;
memset(&t->waitInfo, 0, sizeof(t->waitInfo));
@ -1770,14 +1815,14 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32
return t;
}
void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int prio, int stacksize, int attr)
SceUID __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int prio, int stacksize, int attr)
{
//grab mips regs
SceUID id;
Thread *thread = __KernelCreateThread(id, moduleID, "root", currentMIPS->pc, prio, stacksize, attr);
if (thread->currentStack.start == 0)
ERROR_LOG_REPORT(HLE, "Unable to allocate stack for root thread.");
__KernelResetThread(thread);
__KernelResetThread(thread, 0);
Thread *prevThread = __GetCurrentThread();
if (prevThread && prevThread->isRunning())
@ -1795,6 +1840,8 @@ void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int pr
mipsr4k.r[MIPS_REG_A1] = location;
for (int i = 0; i < args; i++)
Memory::Write_U8(argp[i], location + i);
return id;
}
int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr)
@ -1881,7 +1928,8 @@ int sceKernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr)
INFO_LOG(HLE, "sceKernelStartThread(thread=%i, argSize=%i, argPtr=%08x)", threadToStartID, argSize, argBlockPtr);
__KernelResetThread(startThread);
Thread *cur = __GetCurrentThread();
__KernelResetThread(startThread, cur ? cur->nt.currentPriority : 0);
u32 &sp = startThread->context.r[MIPS_REG_SP];
if (argBlockPtr && argSize > 0)
@ -1908,7 +1956,6 @@ int sceKernelStartThread(SceUID threadToStartID, int argSize, u32 argBlockPtr)
// This could be stack overflow safety, or just stack eaten by the kernel entry func.
sp -= 64;
Thread *cur = __GetCurrentThread();
// Smaller is better for priority. Only switch if the new thread is better.
if (cur && cur->nt.currentPriority > startThread->nt.currentPriority)
{
@ -1966,10 +2013,6 @@ void __KernelReturnFromThread()
_dbg_assert_msg_(HLE, thread != NULL, "Returned from a NULL thread.");
INFO_LOG(HLE,"__KernelReturnFromThread: %d", exitStatus);
// TEMPORARY HACK: kill the stack of the root thread early:
if (!strcmp(thread->GetName(), "root")) {
thread->FreeStack();
}
thread->nt.exitStatus = exitStatus;
__KernelChangeReadyState(thread, currentThread, false);
@ -2080,7 +2123,7 @@ int sceKernelRotateThreadReadyQueue(int priority)
if (cur->nt.currentPriority == priority)
{
threadReadyQueue.push_back(priority, currentThread);
cur->nt.status = THREADSTATUS_READY;
cur->nt.status = (cur->nt.status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
}
// Yield the next thread of this priority to all other threads of same priority.
else
@ -2117,57 +2160,65 @@ int sceKernelDeleteThread(int threadHandle)
}
}
int sceKernelTerminateDeleteThread(int threadno)
int sceKernelTerminateDeleteThread(int threadID)
{
if (threadno != currentThread)
if (threadID == 0 || threadID == currentThread)
{
INFO_LOG(HLE, "sceKernelTerminateDeleteThread(%i)", threadno);
ERROR_LOG(HLE, "sceKernelTerminateDeleteThread(%i): cannot terminate current thread", threadID);
return SCE_KERNEL_ERROR_ILLEGAL_THID;
}
u32 error;
Thread *t = kernelObjects.Get<Thread>(threadno, error);
if (t)
{
//TODO: should we really reschedule here?
error = __KernelDeleteThread(threadno, SCE_KERNEL_ERROR_THREAD_TERMINATED, "thread terminated with delete", false);
hleReSchedule("thread terminated with delete");
u32 error;
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
{
INFO_LOG(HLE, "sceKernelTerminateDeleteThread(%i)", threadID);
//TODO: should we really reschedule here?
error = __KernelDeleteThread(threadID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "thread terminated with delete", false);
hleReSchedule("thread terminated with delete");
return error;
}
// TODO: Error when doesn't exist?
return 0;
return error;
}
else
{
ERROR_LOG_REPORT(HLE, "Thread \"%s\" trying to delete itself! :(", __GetCurrentThread() ? __GetCurrentThread()->GetName() : "NULL");
return -1;
ERROR_LOG(HLE, "sceKernelTerminateDeleteThread(%i): thread doesn't exist", threadID);
return error;
}
}
int sceKernelTerminateThread(SceUID threadID)
{
if (threadID != currentThread)
if (threadID == 0 || threadID == currentThread)
{
ERROR_LOG(HLE, "sceKernelTerminateThread(%i): cannot terminate current thread", threadID);
return SCE_KERNEL_ERROR_ILLEGAL_THID;
}
u32 error;
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
{
if (t->isStopped())
{
ERROR_LOG(HLE, "sceKernelTerminateThread(%i): already stopped", threadID);
return SCE_KERNEL_ERROR_DORMANT;
}
INFO_LOG(HLE, "sceKernelTerminateThread(%i)", threadID);
u32 error;
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
{
t->nt.exitStatus = SCE_KERNEL_ERROR_THREAD_TERMINATED;
__KernelChangeReadyState(t, threadID, false);
t->nt.status = THREADSTATUS_DORMANT;
__KernelFireThreadEnd(threadID);
// TODO: Should this really reschedule?
__KernelTriggerWait(WAITTYPE_THREADEND, threadID, t->nt.exitStatus, "thread terminated", true);
}
// TODO: Return an error if it doesn't exist?
t->nt.exitStatus = SCE_KERNEL_ERROR_THREAD_TERMINATED;
__KernelChangeReadyState(t, threadID, false);
t->nt.status = THREADSTATUS_DORMANT;
__KernelFireThreadEnd(threadID);
// TODO: Should this really reschedule?
__KernelTriggerWait(WAITTYPE_THREADEND, threadID, t->nt.exitStatus, "thread terminated", true);
return 0;
}
else
{
ERROR_LOG_REPORT(HLE, "Thread \"%s\" trying to delete itself! :(", __GetCurrentThread() ? __GetCurrentThread()->GetName() : "NULL");
return -1;
ERROR_LOG(HLE, "sceKernelTerminateThread(%i): thread doesn't exist", threadID);
return error;
}
}
@ -2205,45 +2256,73 @@ void sceKernelGetThreadCurrentPriority()
RETURN(retVal);
}
void sceKernelChangeCurrentThreadAttr()
int sceKernelChangeCurrentThreadAttr(u32 clearAttr, u32 setAttr)
{
int clearAttr = PARAM(0);
int setAttr = PARAM(1);
DEBUG_LOG(HLE,"0 = sceKernelChangeCurrentThreadAttr(clear = %08x, set = %08x", clearAttr, setAttr);
// Seems like this is the only allowed attribute?
if ((clearAttr & ~PSP_THREAD_ATTR_VFPU) != 0 || (setAttr & ~PSP_THREAD_ATTR_VFPU) != 0)
{
ERROR_LOG_REPORT(HLE, "0 = sceKernelChangeCurrentThreadAttr(clear = %08x, set = %08x): invalid attr", clearAttr, setAttr);
return SCE_KERNEL_ERROR_ILLEGAL_ATTR;
}
DEBUG_LOG(HLE, "0 = sceKernelChangeCurrentThreadAttr(clear = %08x, set = %08x)", clearAttr, setAttr);
Thread *t = __GetCurrentThread();
if (t)
t->nt.attr = (t->nt.attr & ~clearAttr) | setAttr;
else
ERROR_LOG(HLE, "%s(): No current thread?", __FUNCTION__);
RETURN(0);
ERROR_LOG_REPORT(HLE, "%s(): No current thread?", __FUNCTION__);
return 0;
}
void sceKernelChangeThreadPriority()
int sceKernelChangeThreadPriority(SceUID threadID, int priority)
{
int id = PARAM(0);
if (id == 0) id = currentThread; //special
if (threadID == 0)
threadID = currentThread;
// 0 means the current (running) thread's priority, not target's.
if (priority == 0)
{
Thread *cur = __GetCurrentThread();
if (!cur)
ERROR_LOG_REPORT(HLE, "sceKernelChangeThreadPriority(%i, %i): no current thread?", threadID, priority)
else
priority = cur->nt.currentPriority;
}
u32 error;
Thread *thread = kernelObjects.Get<Thread>(id, error);
Thread *thread = kernelObjects.Get<Thread>(threadID, error);
if (thread)
{
DEBUG_LOG(HLE,"sceKernelChangeThreadPriority(%i, %i)", id, PARAM(1));
if (thread->isStopped())
{
ERROR_LOG_REPORT(HLE, "sceKernelChangeThreadPriority(%i, %i): thread is dormant", threadID, priority);
return SCE_KERNEL_ERROR_DORMANT;
}
int prio = thread->nt.currentPriority;
threadReadyQueue.remove(prio, id);
if (priority < 0x08 || priority > 0x77)
{
ERROR_LOG_REPORT(HLE, "sceKernelChangeThreadPriority(%i, %i): bogus priority", threadID, priority);
return SCE_KERNEL_ERROR_ILLEGAL_PRIORITY;
}
thread->nt.currentPriority = PARAM(1);
DEBUG_LOG(HLE, "sceKernelChangeThreadPriority(%i, %i)", threadID, priority);
int old = thread->nt.currentPriority;
threadReadyQueue.remove(old, threadID);
thread->nt.currentPriority = priority;
threadReadyQueue.prepare(thread->nt.currentPriority);
if (thread->isRunning())
thread->nt.status = (thread->nt.status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
if (thread->isReady())
threadReadyQueue.push_back(thread->nt.currentPriority, id);
threadReadyQueue.push_back(thread->nt.currentPriority, threadID);
RETURN(0);
hleReSchedule("change thread priority");
return 0;
}
else
{
ERROR_LOG(HLE,"%08x=sceKernelChangeThreadPriority(%i, %i) failed - no such thread", error, id, PARAM(1));
RETURN(error);
ERROR_LOG(HLE, "%08x=sceKernelChangeThreadPriority(%i, %i) failed - no such thread", error, threadID, priority);
return error;
}
}
@ -2334,9 +2413,8 @@ bool __KernelThreadSortPriority(SceUID thread1, SceUID thread2)
//////////////////////////////////////////////////////////////////////////
// WAIT/SLEEP ETC
//////////////////////////////////////////////////////////////////////////
void sceKernelWakeupThread()
int sceKernelWakeupThread(SceUID uid)
{
SceUID uid = PARAM(0);
u32 error;
Thread *t = kernelObjects.Get<Thread>(uid, error);
if (t)
@ -2344,22 +2422,21 @@ void sceKernelWakeupThread()
if (!t->isWaitingFor(WAITTYPE_SLEEP, 1)) {
t->nt.wakeupCount++;
DEBUG_LOG(HLE,"sceKernelWakeupThread(%i) - wakeupCount incremented to %i", uid, t->nt.wakeupCount);
RETURN(0);
} else {
VERBOSE_LOG(HLE,"sceKernelWakeupThread(%i) - woke thread at %i", uid, t->nt.wakeupCount);
__KernelResumeThreadFromWait(uid);
hleReSchedule("thread woken up");
}
return 0;
}
else {
ERROR_LOG(HLE,"sceKernelWakeupThread(%i) - bad thread id", uid);
RETURN(error);
return error;
}
}
void sceKernelCancelWakeupThread()
int sceKernelCancelWakeupThread(SceUID uid)
{
SceUID uid = PARAM(0);
u32 error;
if (uid == 0) uid = __KernelGetCurThread();
Thread *t = kernelObjects.Get<Thread>(uid, error);
@ -2368,44 +2445,42 @@ void sceKernelCancelWakeupThread()
int wCount = t->nt.wakeupCount;
t->nt.wakeupCount = 0;
DEBUG_LOG(HLE,"sceKernelCancelWakeupThread(%i) - wakeupCount reset from %i", uid, wCount);
RETURN(wCount);
return wCount;
}
else {
ERROR_LOG(HLE,"sceKernelCancelWakeupThread(%i) - bad thread id", uid);
RETURN(error);
return error;
}
}
static void __KernelSleepThread(bool doCallbacks) {
static int __KernelSleepThread(bool doCallbacks) {
Thread *thread = __GetCurrentThread();
if (!thread)
{
if (!thread) {
ERROR_LOG(HLE, "sceKernelSleepThread*(): bad current thread");
return;
return -1;
}
if (thread->nt.wakeupCount > 0) {
thread->nt.wakeupCount--;
DEBUG_LOG(HLE, "sceKernelSleepThread() - wakeupCount decremented to %i", thread->nt.wakeupCount);
RETURN(0);
} else {
VERBOSE_LOG(HLE, "sceKernelSleepThread()");
RETURN(0);
__KernelWaitCurThread(WAITTYPE_SLEEP, 1, 0, 0, doCallbacks, "thread slept");
}
return 0;
}
void sceKernelSleepThread()
int sceKernelSleepThread()
{
__KernelSleepThread(false);
return __KernelSleepThread(false);
}
//the homebrew PollCallbacks
void sceKernelSleepThreadCB()
int sceKernelSleepThreadCB()
{
VERBOSE_LOG(HLE, "sceKernelSleepThreadCB()");
__KernelSleepThread(true);
__KernelCheckCallbacks();
hleCheckCurrentCallbacks();
return __KernelSleepThread(true);
}
int sceKernelWaitThreadEnd(SceUID threadID, u32 timeoutPtr)
@ -2476,6 +2551,11 @@ int sceKernelReleaseWaitThread(SceUID threadID)
{
if (!t->isWaiting())
return SCE_KERNEL_ERROR_NOT_WAIT;
if (t->nt.waitType == WAITTYPE_HLEDELAY)
{
WARN_LOG_REPORT(HLE, "sceKernelReleaseWaitThread(): Refusing to wake HLE-delayed thread, right thing to do?");
return SCE_KERNEL_ERROR_NOT_WAIT;
}
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_RELEASE_WAIT);
hleReSchedule("thread released from wait");
@ -2488,16 +2568,74 @@ int sceKernelReleaseWaitThread(SceUID threadID)
}
}
void sceKernelSuspendThread()
int sceKernelSuspendThread(SceUID threadID)
{
WARN_LOG_REPORT(HLE,"UNIMPL sceKernelSuspendThread");
RETURN(0);
// TODO: What about interrupts/callbacks?
if (threadID == 0 || threadID == currentThread)
{
ERROR_LOG(HLE, "sceKernelSuspendThread(%d): cannot suspend current thread", threadID);
return SCE_KERNEL_ERROR_ILLEGAL_THID;
}
u32 error;
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
{
if (t->isStopped())
{
ERROR_LOG(HLE, "sceKernelSuspendThread(%d): thread not running", threadID);
return SCE_KERNEL_ERROR_DORMANT;
}
if (t->isSuspended())
{
ERROR_LOG(HLE, "sceKernelSuspendThread(%d): thread already suspended", threadID);
return SCE_KERNEL_ERROR_SUSPEND;
}
WARN_LOG(HLE, "sceKernelSuspendThread(%d)", threadID);
if (t->isReady())
__KernelChangeReadyState(t, threadID, false);
t->nt.status = (t->nt.status & ~THREADSTATUS_READY) | THREADSTATUS_SUSPEND;
return 0;
}
else
{
ERROR_LOG(HLE, "sceKernelSuspendThread(%d): bad thread", threadID);
return error;
}
}
void sceKernelResumeThread()
int sceKernelResumeThread(SceUID threadID)
{
WARN_LOG_REPORT(HLE,"UNIMPL sceKernelResumeThread");
RETURN(0);
// TODO: What about interrupts/callbacks?
if (threadID == 0 || threadID == currentThread)
{
ERROR_LOG(HLE, "sceKernelSuspendThread(%d): cannot suspend current thread", threadID);
return SCE_KERNEL_ERROR_ILLEGAL_THID;
}
u32 error;
Thread *t = kernelObjects.Get<Thread>(threadID, error);
if (t)
{
if (!t->isSuspended())
{
ERROR_LOG(HLE, "sceKernelSuspendThread(%d): thread not suspended", threadID);
return SCE_KERNEL_ERROR_NOT_SUSPEND;
}
WARN_LOG(HLE, "sceKernelResumeThread(%d)", threadID);
t->nt.status &= ~THREADSTATUS_SUSPEND;
// If it was dormant, waiting, etc. before we don't flip it's ready state.
if (t->nt.status == 0)
__KernelChangeReadyState(t, threadID, true);
return 0;
}
else
{
ERROR_LOG(HLE, "sceKernelResumeThread(%d): bad thread", threadID);
return error;
}
}
@ -2689,15 +2827,14 @@ void ActionAfterMipsCall::run(MipsCall &call) {
ActionAfterMipsCall *Thread::getRunningCallbackAction()
{
if (this->GetUID() == currentThread && g_inCbCount > 0)
{
if (this->GetUID() == currentThread && g_inCbCount > 0) {
MipsCall *call = mipsCalls.get(this->currentMipscallId);
ActionAfterMipsCall *action = 0;
if (call)
action = dynamic_cast<ActionAfterMipsCall *>(call->doAfter);
action = static_cast<ActionAfterMipsCall *>(call->doAfter);
if (!call || !action)
{
// We don't have rtti, so check manually.
if (!call || !action || action->actionTypeID != actionAfterMipsCall) {
ERROR_LOG(HLE, "Failed to access deferred info for thread: %s", this->nt.name);
return NULL;
}
@ -2863,11 +3000,14 @@ void __KernelSwitchContext(Thread *target, const char *reason)
oldUID, oldPC, currentThread, currentMIPS->pc);
}
// No longer waiting.
target->nt.waitType = WAITTYPE_NONE;
target->nt.waitID = 0;
if (target)
{
// No longer waiting.
target->nt.waitType = WAITTYPE_NONE;
target->nt.waitID = 0;
__KernelExecutePendingMipsCalls(target, true);
__KernelExecutePendingMipsCalls(target, true);
}
}
void __KernelChangeThreadState(Thread *thread, ThreadStatus newStatus) {

View File

@ -24,7 +24,7 @@
#include "sceKernelModule.h"
#include "HLE.h"
void sceKernelChangeThreadPriority();
int sceKernelChangeThreadPriority(SceUID threadID, int priority);
int __KernelCreateThread(const char *threadName, SceUID moduleID, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr);
int sceKernelCreateThread(const char *threadName, u32 entry, u32 prio, int stacksize, u32 attr, u32 optionAddr);
int sceKernelDelayThread(u32 usec);
@ -44,17 +44,19 @@ int sceKernelWaitThreadEnd(SceUID threadID, u32 timeoutPtr);
u32 sceKernelReferThreadStatus(u32 uid, u32 statusPtr);
u32 sceKernelReferThreadRunStatus(u32 uid, u32 statusPtr);
int sceKernelReleaseWaitThread(SceUID threadID);
void sceKernelChangeCurrentThreadAttr();
int sceKernelChangeCurrentThreadAttr(u32 clearAttr, u32 setAttr);
int sceKernelRotateThreadReadyQueue(int priority);
int sceKernelCheckThreadStack();
void sceKernelSuspendThread();
void sceKernelResumeThread();
void sceKernelWakeupThread();
void sceKernelCancelWakeupThread();
int sceKernelSuspendThread(SceUID threadID);
int sceKernelResumeThread(SceUID threadID);
int sceKernelWakeupThread(SceUID threadID);
int sceKernelCancelWakeupThread(SceUID threadID);
int sceKernelSleepThread();
int sceKernelSleepThreadCB();
int sceKernelTerminateDeleteThread(int threadno);
int sceKernelTerminateThread(SceUID threadID);
int sceKernelWaitThreadEndCB(SceUID threadID, u32 timeoutPtr);
void sceKernelGetThreadExitStatus();
int sceKernelGetThreadExitStatus(SceUID threadID);
u32 sceKernelGetThreadmanIdType(u32);
u32 sceKernelGetThreadmanIdList(u32 type, u32 readBufPtr, u32 readBufSize, u32 idCountPtr);
u32 sceKernelExtendThreadStack(u32 size, u32 entryAddr, u32 entryParameter);
@ -87,6 +89,8 @@ enum WaitType
WAITTYPE_IO = 16,
WAITTYPE_GEDRAWSYNC = 17,
WAITTYPE_GELISTSYNC = 18,
WAITTYPE_MODULE = 19,
WAITTYPE_HLEDELAY = 20,
NUM_WAITTYPES
};
@ -184,7 +188,7 @@ u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int not
SceUID __KernelGetCurThread();
SceUID __KernelGetCurThreadModuleId();
void __KernelSetupRootThread(SceUID moduleId, int args, const char *argp, int prio, int stacksize, int attr); //represents the real PSP elf loader, run before execution
SceUID __KernelSetupRootThread(SceUID moduleId, int args, const char *argp, int prio, int stacksize, int attr); //represents the real PSP elf loader, run before execution
void __KernelStartIdleThreads(SceUID moduleId);
void __KernelReturnFromThread(); // Called as HLE function
u32 __KernelGetThreadPrio(SceUID id);
@ -230,6 +234,10 @@ void __KernelNotifyCallback(RegisteredCallbackType type, SceUID cbId, int notify
bool __KernelSwitchOffThread(const char *reason);
bool __KernelSwitchToThread(SceUID threadID, const char *reason);
// Set a thread's return address to a specific FakeSyscall nid.
// Discards old RA. Only useful for special threads that do special things on exit.
u32 __KernelSetThreadRA(SceUID threadID, u32 nid);
// A call into game code. These can be pending on a thread.
// Similar to Callback-s (NOT CallbackInfos) in JPCSP.
typedef Action *(*ActionCreator)();

View File

@ -191,13 +191,13 @@ u32 sceMp3ReserveMp3Handle(u32 mp3Addr) {
}
int sceMp3InitResource() {
WARN_LOG(HLE, "UNIML: sceMp3InitResource");
WARN_LOG(HLE, "UNIMPL: sceMp3InitResource");
// Do nothing here
return 0;
}
int sceMp3TermResource() {
WARN_LOG(HLE, "UNIML: sceMp3TermResource");
WARN_LOG(HLE, "UNIMPL: sceMp3TermResource");
// Do nothing here
return 0;
}

View File

@ -23,9 +23,10 @@
#include "sceKernelThread.h"
#include "HLE.h"
#include "../HW/MediaEngine.h"
#include "../../Core/Config.h"
static bool useMediaEngine;
#include "Core/Config.h"
#include "Core/Reporting.h"
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
// MPEG AVC elementary stream.
static const int MPEG_AVC_ES_SIZE = 2048; // MPEG packet size.
@ -67,7 +68,7 @@ static const int avcDecodeDelayMs = 5400; // Varies between 4700 and 600
static const int avcEmptyDelayMs = 320;
static const int mpegDecodeErrorDelayMs = 100;
static const int mpegTimestampPerSecond = 90000; // How many MPEG Timestamp units in a second.
//static const int videoTimestampStep = 3003; // Value based on pmfplayer (mpegTimestampPerSecond / 29.970 (fps)).
static const int videoTimestampStep = 3003; // Value based on pmfplayer (mpegTimestampPerSecond / 29.970 (fps)).
static const int audioTimestampStep = 4180; // For audio play at 44100 Hz (2048 samples / 44100 * mpegTimestampPerSecond == 4180)
//static const int audioFirstTimestamp = 89249; // The first MPEG audio AU has always this timestamp
static const int audioFirstTimestamp = 90000; // The first MPEG audio AU has always this timestamp
@ -157,8 +158,8 @@ struct MpegContext {
u32 mpegRawVersion;
u32 mpegOffset;
u32 mpegStreamSize;
u32 mpegFirstTimestamp;
u32 mpegLastTimestamp;
s64 mpegFirstTimestamp;
s64 mpegLastTimestamp;
u32 mpegFirstDate;
u32 mpegLastDate;
u32 mpegRingbufferAddr;
@ -181,9 +182,9 @@ struct MpegContext {
MediaEngine *mediaengine;
};
static bool isMpegInit;
static u32 streamIdGen;
static bool isCurrentMpegAnalyzed;
static bool fakeMode;
static int actionPostPut;
static std::map<u32, MpegContext *> mpegMap;
static u32 lastMpegHandle = 0;
@ -194,7 +195,7 @@ MpegContext *getMpegCtx(u32 mpegAddr) {
// TODO: Remove.
if (mpegMap.find(mpeg) == mpegMap.end())
{
ERROR_LOG(HLE, "Bad mpeg handle %08x - using last one (%08x) instead", mpeg, lastMpegHandle);
ERROR_LOG_REPORT(HLE, "Bad mpeg handle %08x - using last one (%08x) instead", mpeg, lastMpegHandle);
mpeg = lastMpegHandle;
}
@ -211,7 +212,7 @@ static void InitRingbuffer(SceMpegRingBuffer *buf, int packets, int data, int si
buf->packets = packets;
buf->packetsRead = 0;
buf->packetsWritten = 0;
buf->packetsFree = 0; // set later
buf->packetsFree = 0;
buf->packetSize = 2048;
buf->data = data;
buf->callback_addr = callback_addr;
@ -248,8 +249,8 @@ void AnalyzeMpeg(u32 buffer_addr, MpegContext *ctx) {
}
ctx->mpegOffset = bswap32(Memory::Read_U32(buffer_addr + PSMF_STREAM_OFFSET_OFFSET));
ctx->mpegStreamSize = bswap32(Memory::Read_U32(buffer_addr + PSMF_STREAM_SIZE_OFFSET));
ctx->mpegFirstTimestamp = bswap32(Memory::Read_U32(buffer_addr + PSMF_FIRST_TIMESTAMP_OFFSET));
ctx->mpegLastTimestamp = bswap32(Memory::Read_U32(buffer_addr + PSMF_LAST_TIMESTAMP_OFFSET));
ctx->mpegFirstTimestamp = getMpegTimeStamp(Memory::GetPointer(buffer_addr + PSMF_FIRST_TIMESTAMP_OFFSET));
ctx->mpegLastTimestamp = getMpegTimeStamp(Memory::GetPointer(buffer_addr + PSMF_LAST_TIMESTAMP_OFFSET));
ctx->mpegFirstDate = convertTimestampToDate(ctx->mpegFirstTimestamp);
ctx->mpegLastDate = convertTimestampToDate(ctx->mpegLastTimestamp);
ctx->avc.avcDetailFrameWidth = (Memory::Read_U8(buffer_addr + 142) * 0x10);
@ -257,25 +258,21 @@ void AnalyzeMpeg(u32 buffer_addr, MpegContext *ctx) {
ctx->avc.avcDecodeResult = MPEG_AVC_DECODE_SUCCESS;
ctx->avc.avcFrameStatus = 0;
//if (!isCurrentMpegAnalyzed) {
//SceMpegRingBuffer ringbuffer;
//InitRingbuffer(&ringbuffer, 0, 0, 0, 0, 0);
// ????
//Memory::WriteStruct(ctx->mpegRingbufferAddr, &ringbuffer);
//}
ctx->videoFrameCount = 0;
ctx->audioFrameCount = 0;
ctx->endOfAudioReached = false;
ctx->endOfVideoReached = false;
if ((ctx->mpegStreamSize > 0) && !ctx->isAnalyzed) {
ctx->mediaengine->setFakeMode(fakeMode);
ctx->mediaengine->init(buffer_addr, ctx->mpegStreamSize, ctx->mpegOffset);
ctx->mediaengine->setVideoDim(ctx->avc.avcDetailFrameWidth, ctx->avc.avcDetailFrameHeight);
// mysterious?
//meChannel = new PacketChannel();
//meChannel.write(buffer_addr, mpegOffset);
if (ctx->mpegMagic != PSMF_MAGIC || ctx->mpegVersion < 0 ||
(ctx->mpegOffset & 2047) != 0 || ctx->mpegOffset == 0) {
// mpeg header is invalid!
return;
}
if (ctx->mediaengine && (ctx->mpegStreamSize > 0) && !ctx->isAnalyzed) {
// init mediaEngine
ctx->mediaengine->loadStream(Memory::GetPointer(buffer_addr), ctx->mpegOffset, ctx->mpegOffset + ctx->mpegStreamSize);
ctx->mediaengine->setVideoDim();
}
// When used with scePsmf, some applications attempt to use sceMpegQueryStreamOffset
// and sceMpegQueryStreamSize, which forces a packet overwrite in the Media Engine and in
@ -284,7 +281,7 @@ void AnalyzeMpeg(u32 buffer_addr, MpegContext *ctx) {
ctx->isAnalyzed = true;
INFO_LOG(ME, "Stream offset: %d, Stream size: 0x%X", ctx->mpegOffset, ctx->mpegStreamSize);
INFO_LOG(ME, "First timestamp: %d, Last timestamp: %d", ctx->mpegFirstTimestamp, ctx->mpegLastTimestamp);
INFO_LOG(ME, "First timestamp: %lld, Last timestamp: %lld", ctx->mpegFirstTimestamp, ctx->mpegLastTimestamp);
}
class PostPutAction : public Action {
@ -298,19 +295,19 @@ private:
u32 ringAddr_;
};
void __MpegInit(bool useMediaEngine_) {
void __MpegInit() {
lastMpegHandle = 0;
streamIdGen = 1;
fakeMode = !useMediaEngine_;
isCurrentMpegAnalyzed = false;
isMpegInit = false;
actionPostPut = __KernelRegisterActionType(PostPutAction::Create);
}
void __MpegDoState(PointerWrap &p) {
p.Do(lastMpegHandle);
p.Do(streamIdGen);
p.Do(fakeMode);
p.Do(isCurrentMpegAnalyzed);
p.Do(isMpegInit);
p.Do(actionPostPut);
__KernelRestoreActionType(actionPostPut, PostPutAction::Create);
@ -327,15 +324,15 @@ void __MpegShutdown() {
mpegMap.clear();
}
u32 sceMpegInit()
{
if (!g_Config.bUseMediaEngine){
WARN_LOG(HLE, "Media Engine disabled");
return -1;
u32 sceMpegInit() {
if (isMpegInit) {
WARN_LOG(HLE, "sceMpegInit(): already initialized");
return ERROR_MPEG_ALREADY_INIT;
}
WARN_LOG(HLE, "sceMpegInit()");
return 0;
INFO_LOG(HLE, "sceMpegInit()");
isMpegInit = true;
return hleDelayResult(0, "mpeg init", 750);
}
u32 sceMpegRingbufferQueryMemSize(int packets)
@ -356,11 +353,6 @@ u32 sceMpegRingbufferConstruct(u32 ringbufferAddr, u32 numPackets, u32 data, u32
u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr, u32 frameWidth, u32 mode, u32 ddrTop)
{
if (!g_Config.bUseMediaEngine){
WARN_LOG(HLE, "Media Engine disabled");
return -1;
}
if (size < MPEG_MEMSIZE) {
WARN_LOG(HLE, "ERROR_MPEG_NO_MEMORY=sceMpegCreate(%08x, %08x, %i, %08x, %i, %i, %i)",
mpegAddr, dataPtr, size, ringbufferAddr, frameWidth, mode, ddrTop);
@ -369,14 +361,14 @@ u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr, u32 f
SceMpegRingBuffer ringbuffer;
if(ringbufferAddr != 0){
Memory::ReadStruct(ringbufferAddr, &ringbuffer);
if (ringbuffer.packetSize == 0) {
ringbuffer.packetsFree = 0;
} else {
ringbuffer.packetsFree = (ringbuffer.dataUpperBound - ringbuffer.data) / ringbuffer.packetSize;
}
ringbuffer.mpeg = mpegAddr;
Memory::WriteStruct(ringbufferAddr, &ringbuffer);
Memory::ReadStruct(ringbufferAddr, &ringbuffer);
if (ringbuffer.packetSize == 0) {
ringbuffer.packetsFree = 0;
} else {
ringbuffer.packetsFree = (ringbuffer.dataUpperBound - ringbuffer.data) / ringbuffer.packetSize;
}
ringbuffer.mpeg = mpegAddr;
Memory::WriteStruct(ringbufferAddr, &ringbuffer);
}
// Generate, and write mpeg handle into mpeg data, for some reason
@ -415,7 +407,7 @@ u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr, u32 f
INFO_LOG(HLE, "%08x=sceMpegCreate(%08x, %08x, %i, %08x, %i, %i, %i)",
mpegHandle, mpegAddr, dataPtr, size, ringbufferAddr, frameWidth, mode, ddrTop);
return 0;
return hleDelayResult(0, "mpeg create", 29000);
}
int sceMpegDelete(u32 mpeg)
@ -496,7 +488,7 @@ u32 sceMpegQueryStreamSize(u32 bufferAddr, u32 sizeAddr)
DEBUG_LOG(HLE, "sceMpegQueryStreamSize(%08x, %08x)", bufferAddr, sizeAddr);
MpegContext ctx;
ctx.mediaengine = new MediaEngine();
ctx.mediaengine = 0;
AnalyzeMpeg(bufferAddr, &ctx);
@ -523,15 +515,17 @@ int sceMpegRegistStream(u32 mpeg, u32 streamType, u32 streamNum)
return -1;
}
DEBUG_LOG(HLE, "sceMpegRegistStream(%08x, %i, %i)", mpeg, streamType, streamNum);
INFO_LOG(HLE, "sceMpegRegistStream(%08x, %i, %i)", mpeg, streamType, streamNum);
switch (streamType) {
case MPEG_AVC_STREAM:
ctx->avcRegistered = true;
ctx->mediaengine->setVideoStream(streamNum);
break;
case MPEG_AUDIO_STREAM:
case MPEG_ATRAC_STREAM:
ctx->atracRegistered = true;
ctx->mediaengine->setAudioStream(streamNum);
break;
case MPEG_PCM_STREAM:
ctx->pcmRegistered = true;
@ -596,11 +590,6 @@ int sceMpegFreeAvcEsBuf(u32 mpeg, int esBuf)
u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr, u32 initAddr)
{
if (!g_Config.bUseMediaEngine){
WARN_LOG(HLE, "Media Engine disabled");
return -1;
}
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegAvcDecode(%08x, %08x, %d, %08x, %08x): bad mpeg handle", mpeg, auAddr, frameWidth, bufferAddr, initAddr);
@ -623,11 +612,16 @@ u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr, u32 i
SceMpegAu avcAu;
avcAu.read(auAddr);
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ctx->mpegRingbufferAddr, &ringbuffer);
SceMpegRingBuffer ringbuffer = {0};
if (Memory::IsValidAddress(ctx->mpegRingbufferAddr)) {
Memory::ReadStruct(ctx->mpegRingbufferAddr, &ringbuffer);
} else {
ERROR_LOG(HLE, "Bogus mpegringbufferaddr");
return -1;
}
if (ringbuffer.packetsRead == 0) {
// empty!
if (ringbuffer.packetsRead == 0 || ctx->mediaengine->IsVideoEnd()) {
WARN_LOG(HLE, "sceMpegAvcDecode(%08x, %08x, %d, %08x, %08x): mpeg buffer empty", mpeg, auAddr, frameWidth, bufferAddr, initAddr);
return hleDelayResult(MPEG_AVC_DECODE_ERROR_FATAL, "mpeg buffer empty", avcEmptyDelayMs);
}
@ -635,57 +629,17 @@ u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr, u32 i
u32 init = Memory::Read_U32(initAddr);
DEBUG_LOG(HLE, "*buffer = %08x, *init = %08x", buffer, init);
const int width = std::min((int)frameWidth, 480);
const int height = ctx->avc.avcDetailFrameHeight;
int packetsInRingBuffer = ringbuffer.packets - ringbuffer.packetsFree;
int processedPackets = ringbuffer.packetsRead - packetsInRingBuffer;
int processedSize = processedPackets * ringbuffer.packetSize;
int packetsConsumed = 3;
if (ctx->mpegStreamSize > 0 && ctx->mpegLastTimestamp > 0) {
// Try a better approximation of the packets consumed based on the timestamp
int processedSizeBasedOnTimestamp = (int) ((((float) avcAu.pts) / ctx->mpegLastTimestamp) * ctx->mpegStreamSize);
if (processedSizeBasedOnTimestamp < processedSize) {
packetsConsumed = 0;
} else {
packetsConsumed = (processedSizeBasedOnTimestamp - processedSize) / ringbuffer.packetSize;
if (packetsConsumed > 10) {
packetsConsumed = 10;
}
}
DEBUG_LOG(HLE, "sceMpegAvcDecode consumed %d %d/%d %d", processedSizeBasedOnTimestamp, processedSize, ctx->mpegStreamSize, packetsConsumed);
}
if (ctx->mediaengine->stepVideo()) {
ctx->mediaengine->writeVideoImage(buffer, frameWidth, ctx->videoPixelMode);
packetsConsumed += ctx->mediaengine->readLength() / ringbuffer.packetSize;
// The MediaEngine is already consuming all the remaining
// packets when approaching the end of the video. The PSP
// is only consuming the last packet when reaching the end,
// not before.
// Consuming all the remaining packets?
if (ringbuffer.packetsFree + packetsConsumed >= ringbuffer.packets) {
// Having not yet reached the last timestamp?
if (ctx->mpegLastTimestamp > 0 && avcAu.pts < ctx->mpegLastTimestamp) {
// Do not yet consume all the remaining packets, leave 2 packets
packetsConsumed = ringbuffer.packets - ringbuffer.packetsFree - 2;
}
}
ctx->mediaengine->setReadLength(ctx->mediaengine->readLength() - packetsConsumed * ringbuffer.packetSize);
if (ctx->mediaengine->stepVideo(ctx->videoPixelMode)) {
int bufferSize = ctx->mediaengine->writeVideoImage(Memory::GetPointer(buffer), frameWidth, ctx->videoPixelMode);
gpu->InvalidateCache(buffer, bufferSize, GPU_INVALIDATE_SAFE);
ctx->avc.avcFrameStatus = 1;
ctx->videoFrameCount++;
} else {
// Consume all remaining packets
packetsConsumed = ringbuffer.packets - ringbuffer.packetsFree;
ctx->avc.avcFrameStatus = 0;
}
ctx->avc.avcFrameStatus = 1;
ctx->videoFrameCount++;
ringbuffer.packetsFree = std::max(0, ringbuffer.packets - ctx->mediaengine->getBufferedSize() / 2048);
// Update the ringbuffer with the consumed packets
if (ringbuffer.packetsFree < ringbuffer.packets && packetsConsumed > 0) {
ringbuffer.packetsFree = std::min(ringbuffer.packets, ringbuffer.packetsFree + packetsConsumed);
DEBUG_LOG(HLE, "sceMpegAvcDecode consumed %d packets, remaining %d packets", packetsConsumed, ringbuffer.packets - ringbuffer.packetsFree);
}
avcAu.pts = ctx->mediaengine->getVideoTimeStamp() + ctx->mpegFirstTimestamp;
ctx->avc.avcDecodeResult = MPEG_AVC_DECODE_SUCCESS;
@ -701,6 +655,8 @@ u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr, u32 i
return hleDelayResult(0, "mpeg decode", avcFirstDelayMs);
else
return hleDelayResult(0, "mpeg decode", avcDecodeDelayMs);
//hleEatMicro(3300);
//return hleDelayResult(0, "mpeg decode", 200);
}
u32 sceMpegAvcDecodeStop(u32 mpeg, u32 frameWidth, u32 bufferAddr, u32 statusAddr)
@ -788,11 +744,6 @@ u32 sceMpegAvcDecodeStopYCbCr(u32 mpeg, u32 bufferAddr, u32 statusAddr)
int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initAddr)
{
if (!g_Config.bUseMediaEngine){
WARN_LOG(HLE, "Media Engine disabled");
return -1;
}
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
WARN_LOG(HLE, "sceMpegAvcDecodeYCbCr(%08x, %08x, %08x, %08x): bad mpeg handle", mpeg, auAddr, bufferAddr, initAddr);
@ -807,11 +758,16 @@ int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initAddr)
SceMpegAu avcAu;
avcAu.read(auAddr);
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ctx->mpegRingbufferAddr, &ringbuffer);
SceMpegRingBuffer ringbuffer = {0};
if (Memory::IsValidAddress(ctx->mpegRingbufferAddr)) {
Memory::ReadStruct(ctx->mpegRingbufferAddr, &ringbuffer);
} else {
ERROR_LOG(HLE, "Bogus mpegringbufferaddr");
return -1;
}
if (ringbuffer.packetsRead == 0) {
// empty!
if (ringbuffer.packetsRead == 0 || ctx->mediaengine->IsVideoEnd()) {
WARN_LOG(HLE, "sceMpegAvcDecodeYCbCr(%08x, %08x, %08x, %08x): mpeg buffer empty", mpeg, auAddr, bufferAddr, initAddr);
return hleDelayResult(MPEG_AVC_DECODE_ERROR_FATAL, "mpeg buffer empty", avcEmptyDelayMs);
}
@ -819,50 +775,16 @@ int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initAddr)
u32 init = Memory::Read_U32(initAddr);
DEBUG_LOG(HLE, "*buffer = %08x, *init = %08x", buffer, init);
int packetsInRingBuffer = ringbuffer.packets - ringbuffer.packetsFree;
int processedPackets = ringbuffer.packetsRead - packetsInRingBuffer;
int processedSize = processedPackets * ringbuffer.packetSize;
int packetsConsumed = 3;
if (ctx->mpegStreamSize > 0 && ctx->mpegLastTimestamp > 0) {
// Try a better approximation of the packets consumed based on the timestamp
int processedSizeBasedOnTimestamp = (int) ((((float) avcAu.pts) / ctx->mpegLastTimestamp) * ctx->mpegStreamSize);
if (processedSizeBasedOnTimestamp < processedSize) {
packetsConsumed = 0;
} else {
packetsConsumed = (processedSizeBasedOnTimestamp - processedSize) / ringbuffer.packetSize;
if (packetsConsumed > 10) {
packetsConsumed = 10;
}
}
DEBUG_LOG(HLE, "sceMpegAvcDecodeYCbCr consumed %d %d/%d %d", processedSizeBasedOnTimestamp, processedSize, ctx->mpegStreamSize, packetsConsumed);
if (ctx->mediaengine->stepVideo(ctx->videoPixelMode)) {
// Don't draw here, we'll draw in the Csc func.
ctx->avc.avcFrameStatus = 1;
ctx->videoFrameCount++;
}else {
ctx->avc.avcFrameStatus = 0;
}
ringbuffer.packetsFree = std::max(0, ringbuffer.packets - ctx->mediaengine->getBufferedSize() / 2048);
if (ctx->mediaengine->stepVideo()) {
// TODO: Write it somewhere or buffer it or something?
packetsConsumed += ctx->mediaengine->readLength() / ringbuffer.packetSize;
// Consuming all the remaining packets?
if (ringbuffer.packetsFree + packetsConsumed >= ringbuffer.packets) {
// Having not yet reached the last timestamp?
if (ctx->mpegLastTimestamp > 0 && avcAu.pts < ctx->mpegLastTimestamp) {
// Do not yet consume all the remaining packets, leave 2 packets
packetsConsumed = ringbuffer.packets - ringbuffer.packetsFree - 2;
}
}
ctx->mediaengine->setReadLength(ctx->mediaengine->readLength() - packetsConsumed * ringbuffer.packetSize);
} else {
// Consume all remaining packets
packetsConsumed = ringbuffer.packets - ringbuffer.packetsFree;
}
ctx->avc.avcFrameStatus = 1;
ctx->videoFrameCount++;
// Update the ringbuffer with the consumed packets
if (ringbuffer.packetsFree < ringbuffer.packets && packetsConsumed > 0) {
ringbuffer.packetsFree = std::min(ringbuffer.packets, ringbuffer.packetsFree + packetsConsumed);
DEBUG_LOG(HLE, "sceMpegAvcDecodeYCbCr consumed %d packets, remaining %d packets", packetsConsumed, ringbuffer.packets - ringbuffer.packetsFree);
}
avcAu.pts = ctx->mediaengine->getVideoTimeStamp() + ctx->mpegFirstTimestamp;
ctx->avc.avcDecodeResult = MPEG_AVC_DECODE_SUCCESS;
@ -872,12 +794,14 @@ int sceMpegAvcDecodeYCbCr(u32 mpeg, u32 auAddr, u32 bufferAddr, u32 initAddr)
Memory::Write_U32(ctx->avc.avcFrameStatus, initAddr); // 1 = showing, 0 = not showing
DEBUG_LOG(HLE, "UNIMPL sceMpegAvcDecodeYCbCr(%08x, %08x, %08x, %08x)", mpeg, auAddr, bufferAddr, initAddr);
DEBUG_LOG(HLE, "sceMpegAvcDecodeYCbCr(%08x, %08x, %08x, %08x)", mpeg, auAddr, bufferAddr, initAddr);
if (ctx->videoFrameCount <= 1)
return hleDelayResult(0, "mpeg decode", avcFirstDelayMs);
else
return hleDelayResult(0, "mpeg decode", avcDecodeDelayMs);
//hleEatMicro(3300);
//return hleDelayResult(0, "mpeg decode", 200);
}
u32 sceMpegAvcDecodeFlush(u32 mpeg)
@ -946,6 +870,8 @@ int sceMpegRingbufferAvailableSize(u32 ringbufferAddr)
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ringbufferAddr, &ringbuffer);
DEBUG_LOG(HLE, "%i=sceMpegRingbufferAvailableSize(%08x)", ringbuffer.packetsFree, ringbufferAddr);
MpegContext *ctx = getMpegCtx(ringbuffer.mpeg);
int result = std::min(ringbuffer.packetsFree, ctx->mediaengine->getRemainSize() / 2048);
return ringbuffer.packetsFree;
}
@ -957,16 +883,19 @@ void PostPutAction::run(MipsCall &call) {
int packetsAdded = currentMIPS->r[2];
if (packetsAdded > 0) {
if (ctx)
ctx->mediaengine->feedPacketData(ringbuffer.data, packetsAdded * ringbuffer.packetSize);
if (packetsAdded > ringbuffer.packetsFree) {
WARN_LOG(HLE, "sceMpegRingbufferPut clamping packetsAdded old=%i new=%i", packetsAdded, ringbuffer.packetsFree);
packetsAdded = ringbuffer.packetsFree;
}
int actuallyAdded = ctx->mediaengine->addStreamData(Memory::GetPointer(ringbuffer.data), packetsAdded * 2048) / 2048;
if (actuallyAdded != packetsAdded) {
WARN_LOG_REPORT(HLE, "sceMpegRingbufferPut(): unable to enqueue all added packets, going to overwrite some frames.");
}
ringbuffer.packetsRead += packetsAdded;
ringbuffer.packetsWritten += packetsAdded;
ringbuffer.packetsFree -= packetsAdded;
}
DEBUG_LOG(HLE, "packetAdded: %i packetsRead: %i packetsTotal: %i", packetsAdded, ringbuffer.packetsRead, ringbuffer.packets);
Memory::WriteStruct(ringAddr_, &ringbuffer);
call.setReturnValue(packetsAdded);
@ -978,10 +907,8 @@ u32 sceMpegRingbufferPut(u32 ringbufferAddr, u32 numPackets, u32 available)
{
DEBUG_LOG(HLE, "sceMpegRingbufferPut(%08x, %i, %i)", ringbufferAddr, numPackets, available);
numPackets = std::min(numPackets, available);
if (numPackets <= 0) {
ERROR_LOG(HLE, "sub-zero number of packets put");
if (numPackets <= 0)
return 0;
}
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ringbufferAddr, &ringbuffer);
@ -992,19 +919,15 @@ u32 sceMpegRingbufferPut(u32 ringbufferAddr, u32 numPackets, u32 available)
return 0;
}
// Clamp to length of mpeg stream - this seems like a hack as we don't have access to the context here really
int mpegStreamPackets = (ctx->mpegStreamSize + ringbuffer.packetSize - 1) / ringbuffer.packetSize;
int remainingPackets = mpegStreamPackets - ringbuffer.packetsRead;
if (remainingPackets < 0) {
remainingPackets = 0;
}
numPackets = std::min(numPackets, (u32)remainingPackets);
// Execute callback function as a direct MipsCall, no blocking here so no messing around with wait states etc
if (ringbuffer.callback_addr) {
PostPutAction *action = (PostPutAction *) __KernelCreateAction(actionPostPut);
PostPutAction *action = (PostPutAction *)__KernelCreateAction(actionPostPut);
action->setRingAddr(ringbufferAddr);
u32 args[3] = {(u32)ringbuffer.data, numPackets, (u32)ringbuffer.callback_args};
// TODO: Should call this multiple times until we get numPackets.
// Normally this would be if it did not read enough, but also if available > packets.
// Should ultimately return the TOTAL number of returned packets.
u32 packetsThisRound = std::min(numPackets, (u32)ringbuffer.packets);
u32 args[3] = {(u32)ringbuffer.data, packetsThisRound, (u32)ringbuffer.callback_args};
__KernelDirectMipsCall(ringbuffer.callback_addr, action, args, 3, false);
} else {
ERROR_LOG(HLE, "sceMpegRingbufferPut: callback_addr zero");
@ -1028,6 +951,9 @@ int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
if (mpegRingbuffer.packetsRead == 0 || mpegRingbuffer.packetsFree == mpegRingbuffer.packets) {
DEBUG_LOG(HLE, "PSP_ERROR_MPEG_NO_DATA=sceMpegGetAvcAu(%08x, %08x, %08x, %08x)", mpeg, streamId, auAddr, attrAddr);
sceAu.pts = -1;
sceAu.dts = -1;
sceAu.write(auAddr);
// TODO: Does this really reschedule?
return hleDelayResult(PSP_ERROR_MPEG_NO_DATA, "mpeg get avc", mpegDecodeErrorDelayMs);
}
@ -1045,33 +971,24 @@ int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
streamInfo->second.needsReset = false;
}
// Wait for audio if too much ahead
if (ctx->atracRegistered && (sceAu.pts > sceAu.pts + getMaxAheadTimestamp(mpegRingbuffer)))
/*// Wait for audio if too much ahead
if (ctx->atracRegistered && (ctx->mediaengine->getVideoTimeStamp() > ctx->mediaengine->getAudioTimeStamp() + getMaxAheadTimestamp(mpegRingbuffer)))
{
ERROR_LOG(HLE, "sceMpegGetAvcAu - video too much ahead");
// TODO: Does this really reschedule?
return hleDelayResult(PSP_ERROR_MPEG_NO_DATA, "mpeg get avc", mpegDecodeErrorDelayMs);
}
}*/
int result = 0;
// read the au struct from ram
// TODO: For now, always checking, since readVideoAu() is stubbed.
if (!ctx->mediaengine->readVideoAu(&sceAu) || true) {
// Only return this after the video already ended.
if (ctx->endOfVideoReached) {
if (mpegRingbuffer.packetsFree < mpegRingbuffer.packets) {
mpegRingbuffer.packetsFree = mpegRingbuffer.packets;
Memory::WriteStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
}
result = PSP_ERROR_MPEG_NO_DATA;
}
if (ctx->mpegLastTimestamp <= 0 || sceAu.pts >= ctx->mpegLastTimestamp) {
NOTICE_LOG(HLE, "End of video reached");
ctx->endOfVideoReached = true;
} else {
ctx->endOfAudioReached = false;
}
sceAu.pts = ctx->mediaengine->getVideoTimeStamp() + ctx->mpegFirstTimestamp;
sceAu.dts = sceAu.pts - videoTimestampStep;
if (ctx->mediaengine->IsVideoEnd()) {
INFO_LOG(HLE, "video end reach. pts: %i dts: %i", (int)sceAu.pts, (int)ctx->mediaengine->getLastTimeStamp());
mpegRingbuffer.packetsFree = mpegRingbuffer.packets;
Memory::WriteStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
result = PSP_ERROR_MPEG_NO_DATA;
}
// The avcau struct may have been modified by mediaengine, write it back.
@ -1089,9 +1006,16 @@ int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
u32 sceMpegFinish()
{
ERROR_LOG(HLE, "sceMpegFinish(...)");
if (!isMpegInit)
{
WARN_LOG(HLE, "sceMpegFinish(...): not initialized");
return ERROR_MPEG_NOT_YET_INIT;
}
INFO_LOG(HLE, "sceMpegFinish(...)");
isMpegInit = false;
//__MpegFinish();
return 0;
return hleDelayResult(0, "mpeg finish", 250);
}
u32 sceMpegQueryMemSize()
@ -1121,27 +1045,25 @@ int sceMpegGetAtracAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
streamInfo->second.needsReset = false;
}
int result = 0;
if (mpegRingbuffer.packetsFree == mpegRingbuffer.packets) {
// The audio can end earlier than the video does.
if (mpegRingbuffer.packetsFree == mpegRingbuffer.packets || (ctx->mediaengine->IsAudioEnd() && !ctx->mediaengine->IsVideoEnd())) {
DEBUG_LOG(HLE, "PSP_ERROR_MPEG_NO_DATA=sceMpegGetAtracAu(%08x, %08x, %08x, %08x)", mpeg, streamId, auAddr, attrAddr);
// TODO: Does this really delay?
return hleDelayResult(PSP_ERROR_MPEG_NO_DATA, "mpeg get atrac", mpegDecodeErrorDelayMs);
}
//...
// TODO: Just faking it.
sceAu.pts += videoTimestampStep;
sceAu.write(auAddr);
int result = 0;
sceAu.pts = ctx->mediaengine->getAudioTimeStamp() + ctx->mpegFirstTimestamp;
if (ctx->mediaengine->IsVideoEnd()) {
INFO_LOG(HLE, "video end reach. pts: %i dts: %i", (int)sceAu.pts, (int)ctx->mediaengine->getLastTimeStamp());
mpegRingbuffer.packetsFree = mpegRingbuffer.packets;
Memory::WriteStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
// TODO: And also audio end?
if (ctx->endOfVideoReached) {
if (mpegRingbuffer.packetsFree < mpegRingbuffer.packets) {
mpegRingbuffer.packetsFree = mpegRingbuffer.packets;
Memory::WriteStruct(ctx->mpegRingbufferAddr, &mpegRingbuffer);
}
result = PSP_ERROR_MPEG_NO_DATA;
}
sceAu.write(auAddr);
if (Memory::IsValidAddress(attrAddr)) {
Memory::Write_U32(0, attrAddr);
@ -1269,15 +1191,49 @@ u32 sceMpegAvcCopyYCbCr(u32 mpeg, u32 sourceAddr, u32 YCbCrAddr)
u32 sceMpegAtracDecode(u32 mpeg, u32 auAddr, u32 bufferAddr, int init)
{
DEBUG_LOG(HLE, "UNIMPL sceMpegAtracDecode(%08x, %08x, %08x, %i)", mpeg, auAddr, bufferAddr, init);
if (Memory::IsValidAddress(bufferAddr))
Memory::Memset(bufferAddr, 0, MPEG_ATRAC_ES_OUTPUT_SIZE);
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx) {
return 0;
}
if (!Memory::IsValidAddress(auAddr) || !Memory::IsValidAddress(bufferAddr)) {
ERROR_LOG(HLE, "sceMpegAtracDecode: bad addresses");
return 0;
}
SceMpegAu avcAu;
avcAu.read(auAddr);
Memory::Memset(bufferAddr, 0, MPEG_ATRAC_ES_OUTPUT_SIZE);
ctx->mediaengine->getAudioSamples(Memory::GetPointer(bufferAddr));
avcAu.pts = ctx->mediaengine->getAudioTimeStamp() + ctx->mpegFirstTimestamp;
avcAu.write(auAddr);
return hleDelayResult(0, "mpeg atrac decode", atracDecodeDelayMs);
//hleEatMicro(4000);
//return hleDelayResult(0, "mpeg atrac decode", 200);
}
// YCbCr -> RGB color space conversion
u32 sceMpegAvcCsc(u32 mpeg, u32 sourceAddr, u32 rangeAddr, int frameWidth, u32 destAddr)
{
ERROR_LOG(HLE, "UNIMPL sceMpegAvcCsc(%08x, %08x, %08x, %i, %08x)", mpeg, sourceAddr, rangeAddr, frameWidth, destAddr);
DEBUG_LOG(HLE, "sceMpegAvcCsc(%08x, %08x, %08x, %i, %08x)", mpeg, sourceAddr, rangeAddr, frameWidth, destAddr);
MpegContext *ctx = getMpegCtx(mpeg);
if (!ctx)
return -1;
if ((!Memory::IsValidAddress(rangeAddr)) || (!Memory::IsValidAddress(destAddr)))
return -1;
int x = Memory::Read_U32(rangeAddr);
int y = Memory::Read_U32(rangeAddr + 4);
int width = Memory::Read_U32(rangeAddr + 8);
int height = Memory::Read_U32(rangeAddr + 12);
int destSize = ctx->mediaengine->writeVideoImageWithRange(Memory::GetPointer(destAddr), frameWidth, ctx->videoPixelMode,
x, y, width, height);
gpu->InvalidateCache(destAddr, destSize, GPU_INVALIDATE_SAFE);
return 0;
}
@ -1367,10 +1323,14 @@ int sceMpegAvcConvertToYuv420(u32 mpeg, u32 bufferOutput, u32 unknown1, int unkn
int sceMpegGetUserdataAu(u32 mpeg, u32 streamUid, u32 auAddr, u32 resultAddr)
{
ERROR_LOG(HLE, "UNIMPL sceMpegGetUserdataAu(%08x, %08x, %08x, %08x)", mpeg, streamUid, auAddr, resultAddr);
// TODO: Are these at all right? Seen in Phantasy Star Portable 2.
Memory::Write_U32(0, resultAddr);
Memory::Write_U32(0, resultAddr + 4);
return 0;
// We currently can't demux userdata so this seems like the best thing to return in the meantime..
// Then we probably shouldn't do the above writes? but it works...
return ERROR_MPEG_NO_DATA;
}
const HLEFunction sceMpeg[] =

View File

@ -40,6 +40,8 @@ enum {
ERROR_PSMFPLAYER_NO_MORE_DATA = 0x8061600c,
ERROR_MPEG_NO_DATA = 0x80618001,
ERROR_MPEG_ALREADY_INIT = 0x80618005,
ERROR_MPEG_NOT_YET_INIT = 0x80618009,
};
// MPEG statics.
@ -51,8 +53,8 @@ static const int PSMF_VERSION_0015 = 0x35313030;
static const int PSMF_STREAM_VERSION_OFFSET = 0x4;
static const int PSMF_STREAM_OFFSET_OFFSET = 0x8;
static const int PSMF_STREAM_SIZE_OFFSET = 0xC;
static const int PSMF_FIRST_TIMESTAMP_OFFSET = 0x56;
static const int PSMF_LAST_TIMESTAMP_OFFSET = 0x5C;
static const int PSMF_FIRST_TIMESTAMP_OFFSET = 0x54;
static const int PSMF_LAST_TIMESTAMP_OFFSET = 0x5A;
struct SceMpegAu {
s64 pts; // presentation time stamp
@ -62,19 +64,17 @@ struct SceMpegAu {
void read(u32 addr) {
Memory::ReadStruct(addr, this);
pts = (pts & 0xFFFFFFFFULL) << 32 | (pts >> 32);
dts = (dts & 0xFFFFFFFFULL) << 32 | (dts >> 32);
pts = (pts & 0xFFFFFFFFULL) << 32 | (((u64)pts) >> 32);
dts = (dts & 0xFFFFFFFFULL) << 32 | (((u64)dts) >> 32);
}
void write(u32 addr) {
pts = (pts & 0xFFFFFFFFULL) << 32 | (pts >> 32);
dts = (dts & 0xFFFFFFFFULL) << 32 | (dts >> 32);
pts = (pts & 0xFFFFFFFFULL) << 32 | (((u64)pts) >> 32);
dts = (dts & 0xFFFFFFFFULL) << 32 | (((u64)dts) >> 32);
Memory::WriteStruct(addr, this);
}
};
const int videoTimestampStep = 3003;
// As native in PSP ram
struct SceMpegRingBuffer {
// PSP info
@ -91,7 +91,7 @@ struct SceMpegRingBuffer {
u32 mpeg; // pointer to mpeg struct, fixed up in sceMpegCreate
};
void __MpegInit(bool useMediaEngine_);
void __MpegInit();
void __MpegDoState(PointerWrap &p);
void __MpegShutdown();

View File

@ -52,6 +52,21 @@ enum {
ERROR_NET_ADHOCCTL_TOO_MANY_HANDLERS = 0x80410b12,
};
//these might come in handy in the future, if PPSSPP ever supports wifi/ad-hoc
struct SceNetAdhocctlParams
{
int channel; //which ad-hoc channel to connect to
char name[8]; //connection name
u8 bssid[6]; //BSSID of the connection?
char nickname[128]; //PSP's nickname?
};
struct ProductStruct
{
int unknown; //unknown, set to 0
char product[9]; //Product name?
};
void __NetInit() {
netInited = false;
netAdhocInited = false;
@ -90,7 +105,7 @@ u32 sceNetAdhocInit() {
}
u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) {
ERROR_LOG(HLE,"UNIMPL sceNetAdhocInit(%i, %i, %08x)", stackSize, prio, productAddr);
ERROR_LOG(HLE,"UNIMPL sceNetAdhocctlInit(%i, %i, %08x)", stackSize, prio, productAddr);
return 0;
}
@ -114,11 +129,96 @@ u32 sceWlanGetSwitchState() {
return 0;
}
//handler probably shouldn't be an int...
u32 sceNetAdhocctlAddHandler(int handler, void *unknown) {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocctlAddHandler(%x, %x)", handler, unknown);
return 0;
}
u32 sceNetAdhocctlDisconnect() {
DEBUG_LOG(HLE, "UNIMPL sceNetAdhocctlDisconnect()");
return 0;
}
u32 sceNetAdhocctlDelHandler(int handler) {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocctlDelHandler(%x)", handler);
return 0;
}
int sceNetAdhocMatchingTerm() {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocMatchingTerm()");
return 0;
}
int sceNetAdhocctlTerm() {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocctlTerm()");
return 0;
}
int sceNetAdhocTerm() {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocTerm()");
return 0;
}
//homebrew SDK claims it's a void function, but tests seem to indicate otherwise
int sceNetEtherNtostr(const char *mac, u32 bufferPtr) {
DEBUG_LOG(HLE, "UNTESTED sceNetEtherNtostr(%s, %x)", mac, bufferPtr);
int len = strlen(mac);
for (int i = 0; i < len; i++)
Memory::Write_U8(mac[i], bufferPtr + i);
return 0;
}
//write SCE_KERNEL_ERROR_ERRNO_NOT_CONNECTED to attempt to stop games from spamming wifi syscalls...
int sceNetAdhocctlGetState(u32 ptrToStatus) {
DEBUG_LOG(HLE, "UNTESTED sceNetAdhocctlGetState(%x)", ptrToStatus);
Memory::Write_U32(SCE_KERNEL_ERROR_ERRNO_NOT_CONNECTED, ptrToStatus);
return 0;
}
//always return -1 since we don't have any real networking...
int sceNetAdhocPdpCreate(const char *mac, u32 port, int bufferSize, u32 unknown) {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocPdpCreate(%s, %x, %x, %x)", mac, port, bufferSize, unknown);
return -1;
}
//SHOULD be int sceNetAdhocctlGetParameter(struct SceNetAdhocctlParams *params), but I don't want
//to mess up the function wrappers..
int sceNetAdhocctlGetParameter() {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocctlGetParameter()");
return -1;
}
//SHOULD be int sceNetAdhocctlGetAdhocId(struct productStruct * product), but I don't want
//to mess up the function wrappers..
int sceNetAdhocctlGetAdhocId() {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocctlGetAdhocId()");
return -1;
}
//return -1 packets since we don't have networking yet
int sceNetAdhocPdpRecv(int id, const char *mac, u32 port, void *data, void *dataLength, u32 timeout, int nonBlock) {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocPdpRecv(%d, %d, %d, %x, %x, %d, %d)", id, mac, port, data, dataLength, timeout, nonBlock);
return -1;
}
//Assuming < 0 for failure, homebrew SDK doesn't have much to say about this one
int sceNetAdhocSetSocketAlert(int id, int flag) {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocSetSocketAlert(%d, %d)", id, flag);
return -1;
}
int sceNetAdhocPdpDelete(int id, int unknown) {
ERROR_LOG(HLE, "UNIMPL sceNetAdhocPdpDelete(%d, %d)", id, unknown);
return 0;
}
const HLEFunction sceNet[] =
{
{0x39AF39A6, sceNetInit, "sceNetInit"},
{0x281928A9, WrapU_V<sceNetTerm>, "sceNetTerm"},
{0x89360950, 0, "sceNetEtherNtostr"},
{0x89360950, WrapI_CU<sceNetEtherNtostr>, "sceNetEtherNtostr"},
{0x0bf0a3ae, 0, "sceNetGetLocalEtherAddr"},
{0xd27961c9, 0, "sceNetEtherStrton"},
{0x50647530, 0, "sceNetFreeThreadinfo"},
@ -129,12 +229,12 @@ const HLEFunction sceNet[] =
const HLEFunction sceNetAdhoc[] =
{
{0xE1D621D7, WrapU_V<sceNetAdhocInit>, "sceNetAdhocInit"},
{0xA62C6F57, 0, "sceNetAdhocTerm"},
{0xA62C6F57, WrapI_V<sceNetAdhocTerm>, "sceNetAdhocTerm"},
{0x0AD043ED, 0, "sceNetAdhocctlConnect"},
{0x6f92741b, 0, "sceNetAdhocPdpCreate"},
{0x6f92741b, WrapI_CUIU<sceNetAdhocPdpCreate>, "sceNetAdhocPdpCreate"},
{0xabed3790, 0, "sceNetAdhocPdpSend"},
{0xdfe53e03, 0, "sceNetAdhocPdpRecv"},
{0x7f27bb5e, 0, "sceNetAdhocPdpDelete"},
{0xdfe53e03, WrapI_ICUVVUI<sceNetAdhocPdpRecv>, "sceNetAdhocPdpRecv"},
{0x7f27bb5e, WrapI_II<sceNetAdhocPdpDelete>, "sceNetAdhocPdpDelete"},
{0xc7c1fc57, 0, "sceNetAdhocGetPdpStat"},
{0x157e6225, 0, "sceNetAdhocPtpClose"},
{0x4da4c788, 0, "sceNetAdhocPtpSend"},
@ -151,7 +251,7 @@ const HLEFunction sceNetAdhoc[] =
{0xa0229362, 0, "sceNetAdhocGameModeDeleteMaster"},
{0x0b2228e9, 0, "sceNetAdhocGameModeDeleteReplica"},
{0x7F75C338, 0, "sceNetAdhocGameModeCreateMaster"},
{0x73bfd52d, 0, "sceNetAdhocSetSocketAlert"},
{0x73bfd52d, WrapI_II<sceNetAdhocSetSocketAlert>, "sceNetAdhocSetSocketAlert"},
{0x7a662d6b, 0, "sceNetAdhocPollSocket"},
{0x4d2ce199, 0, "sceNetAdhocGetSocketAlert"},
};
@ -164,7 +264,7 @@ int sceNetAdhocMatchingInit(u32 memsize) {
const HLEFunction sceNetAdhocMatching[] =
{
{0x2a2a1e07, WrapI_U<sceNetAdhocMatchingInit>, "sceNetAdhocMatchingInit"},
{0x7945ecda, 0, "sceNetAdhocMatchingTerm"},
{0x7945ecda, WrapI_V<sceNetAdhocMatchingTerm>, "sceNetAdhocMatchingTerm"},
{0xca5eda6f, 0, "sceNetAdhocMatchingCreate"},
{0x93ef3843, 0, "sceNetAdhocMatchingStart"},
{0x32b156b3, 0, "sceNetAdhocMatchingStop"},
@ -184,15 +284,15 @@ const HLEFunction sceNetAdhocMatching[] =
const HLEFunction sceNetAdhocctl[] =
{
{0xE26F226E, WrapU_IIU<sceNetAdhocctlInit>, "sceNetAdhocctlInit"},
{0x9D689E13, 0, "sceNetAdhocctlTerm"},
{0x20B317A0, 0, "sceNetAdhocctlAddHandler"},
{0x6402490B, 0, "sceNetAdhocctlDelHandler"},
{0x34401D65, 0, "sceNetAdhocctlDisconnect"},
{0x9D689E13, WrapI_V<sceNetAdhocctlTerm>, "sceNetAdhocctlTerm"},
{0x20B317A0, WrapU_IV<sceNetAdhocctlAddHandler>, "sceNetAdhocctlAddHandler"},
{0x6402490B, WrapU_I<sceNetAdhocctlDelHandler>, "sceNetAdhocctlDelHandler"},
{0x34401D65, WrapU_V<sceNetAdhocctlDisconnect>, "sceNetAdhocctlDisconnect"},
{0x0ad043ed, 0, "sceNetAdhocctlConnect"},
{0x08fff7a0, 0, "sceNetAdhocctlScan"},
{0x75ecd386, 0, "sceNetAdhocctlGetState"},
{0x75ecd386, WrapI_U<sceNetAdhocctlGetState>, "sceNetAdhocctlGetState"},
{0x8916c003, 0, "sceNetAdhocctlGetNameByAddr"},
{0xded9d28e, 0, "sceNetAdhocctlGetParameter"},
{0xded9d28e, WrapI_V<sceNetAdhocctlGetParameter>, "sceNetAdhocctlGetParameter"},
{0x81aee1be, 0, "sceNetAdhocctlGetScanInfo"},
{0x5e7f79c9, 0, "sceNetAdhocctlJoin"},
{0x8db83fdc, 0, "sceNetAdhocctlGetPeerInfo"},
@ -201,7 +301,7 @@ const HLEFunction sceNetAdhocctl[] =
{0x1ff89745, 0, "sceNetAdhocctlJoinEnterGameMode"},
{0xcf8e084d, 0, "sceNetAdhocctlExitGameMode"},
{0xe162cb14, 0, "sceNetAdhocctlGetPeerList"},
{0x362cbe8f, 0, "sceNetAdhocctlGetAdhocId"},
{0x362cbe8f, WrapI_V<sceNetAdhocctlGetAdhocId>, "sceNetAdhocctlGetAdhocId"},
{0x5a014ce0, 0, "sceNetAdhocctlGetGameModeInfo"},
{0x99560abe, 0, "sceNetAdhocctlGetAddrByName"},
{0xb0b80e80, 0, "sceNetAdhocctlCreateEnterGameModeMin"},

View File

@ -31,9 +31,34 @@ u32 sceP3daBridgeExit()
return 0;
}
static inline int getScaleValue(u32 channelsNum) {
int val = 0;
while (channelsNum > 1) {
channelsNum >>= 1;
val++;
}
return val;
}
u32 sceP3daBridgeCore(u32 p3daCoreAddr, u32 channelsNum, u32 samplesNum, u32 inputAddr, u32 outputAddr)
{
ERROR_LOG_REPORT(HLE, "UNIMPL sceP3daBridgeCore(%08x, %08x, %08x, %08x, %08x)", p3daCoreAddr, channelsNum, samplesNum, inputAddr, outputAddr);
INFO_LOG(HLE, "sceP3daBridgeCore(%08x, %08x, %08x, %08x, %08x)", p3daCoreAddr, channelsNum, samplesNum, inputAddr, outputAddr);
if (Memory::IsValidAddress(inputAddr) && Memory::IsValidAddress(outputAddr)) {
int scaleval = getScaleValue(channelsNum);
s16* outbuf = (s16*)Memory::GetPointer(outputAddr);
memset(outbuf, 0, samplesNum * sizeof(s16) * 2);
for (u32 k = 0; k < channelsNum; k++) {
u32 inaddr = Memory::Read_U32(inputAddr + k * 4);
s16 *inbuf = (s16*)Memory::GetPointer(inaddr);
if (!inbuf)
continue;
for (u32 i = 0; i < samplesNum; i++) {
s16 sample = inbuf[i] >> scaleval;
outbuf[i*2] += sample;
outbuf[i*2 + 1] += sample;
}
}
}
return 0;
}

View File

@ -282,6 +282,12 @@ float scePowerGetBusClockFrequencyFloat() {
return (float) busFreq;
}
int scePowerTick() {
DEBUG_LOG(HLE, "scePowerTick()");
// Don't think we need to do anything.
return 0;
}
u32 IsPSPNonFat() {
return PSP_MODEL_FAT;
@ -291,7 +297,7 @@ static const HLEFunction scePower[] = {
{0x04B7766E,&WrapI_II<scePowerRegisterCallback>,"scePowerRegisterCallback"},
{0x2B51FE2F,0,"scePower_2B51FE2F"},
{0x442BFBAC,0,"scePowerGetBacklightMaximum"},
{0xEFD3C963,0,"scePowerTick"},
{0xEFD3C963,&WrapI_V<scePowerTick>,"scePowerTick"},
{0xEDC13FE5,0,"scePowerGetIdleTimer"},
{0x7F30B3B1,0,"scePowerIdleTimerEnable"},
{0x972CE941,0,"scePowerIdleTimerDisable"},

View File

@ -21,6 +21,9 @@
#include "Core/HLE/scePsmf.h"
#include "Core/HLE/sceMpeg.h"
#include "Core/HW/MediaEngine.h"
#include "GPU/GPUInterface.h"
#include "GPU/GPUState.h"
#include <map>
@ -109,6 +112,13 @@ public:
Psmf(u32 data);
~Psmf();
void DoState(PointerWrap &p);
bool isValidCurrentStreamNumber() {
return currentStreamNum >= 0 && currentStreamNum < streamMap.size(); // urgh, checking size isn't really right here.
}
void setStreamNum(int num);
bool setStreamWithType(int type, int channel);
u32 magic;
u32 version;
@ -146,8 +156,9 @@ public:
class PsmfPlayer {
public:
// For savestates only.
PsmfPlayer() {}
PsmfPlayer() { mediaengine = new MediaEngine;}
PsmfPlayer(u32 data);
~PsmfPlayer() { if (mediaengine) delete mediaengine;}
void DoState(PointerWrap &p);
int videoCodec;
@ -166,6 +177,8 @@ public:
SceMpegAu psmfPlayerAtracAu;
SceMpegAu psmfPlayerAvcAu;
PsmfPlayerStatus status;
MediaEngine* mediaengine;
};
class PsmfStream {
@ -205,6 +218,7 @@ public:
p.Do(channel);
}
int type;
int channel;
};
@ -261,8 +275,9 @@ PsmfPlayer::PsmfPlayer(u32 data) {
audioStreamNum = Memory::Read_U32(data + 12);
playMode = Memory::Read_U32(data+ 16);
playSpeed = Memory::Read_U32(data + 20);
psmfPlayerLastTimestamp = bswap32(Memory::Read_U32(data + PSMF_LAST_TIMESTAMP_OFFSET)) ;
psmfPlayerLastTimestamp = getMpegTimeStamp(Memory::GetPointer(data + PSMF_LAST_TIMESTAMP_OFFSET)) ;
status = PSMF_PLAYER_STATUS_INIT;
mediaengine = new MediaEngine;
}
void Psmf::DoState(PointerWrap &p) {
@ -307,10 +322,50 @@ void PsmfPlayer::DoState(PointerWrap &p) {
p.Do(playbackThreadPriority);
p.Do(psmfMaxAheadTimestamp);
p.Do(psmfPlayerLastTimestamp);
p.DoClass(mediaengine);
p.DoMarker("PsmfPlayer");
}
void Psmf::setStreamNum(int num) {
currentStreamNum = num;
if (!isValidCurrentStreamNumber())
return;
PsmfStreamMap::iterator iter = streamMap.find(currentStreamNum);
if (iter == streamMap.end())
return;
int type = iter->second->type;
int channel = iter->second->channel;
switch (type) {
case PSMF_AVC_STREAM:
if (currentVideoStreamNum != num) {
// TODO: Tell video mediaengine or something about channel.
currentVideoStreamNum = num;
}
break;
case PSMF_ATRAC_STREAM:
case PSMF_PCM_STREAM:
if (currentAudioStreamNum != num) {
// TODO: Tell audio mediaengine or something about channel.
currentAudioStreamNum = num;
}
break;
}
}
bool Psmf::setStreamWithType(int type, int channel) {
for (PsmfStreamMap::iterator iter = streamMap.begin(); iter != streamMap.end(); ++iter) {
if (iter->second->type == type) {
setStreamNum(iter->first);
return true;
}
}
return false;
}
static std::map<u32, Psmf *> psmfMap;
static std::map<u32, PsmfPlayer *> psmfPlayerMap;
@ -401,16 +456,40 @@ u32 scePsmfGetNumberOfSpecificStreams(u32 psmfStruct, u32 streamType)
u32 scePsmfSpecifyStreamWithStreamType(u32 psmfStruct, u32 streamType, u32 channel)
{
Psmf *psmf = getPsmf(psmfStruct);
if (!psmf) {
ERROR_LOG(HLE, "scePsmfSpecifyStreamWithStreamType - invalid psmf");
return ERROR_PSMF_NOT_FOUND;
}
ERROR_LOG(HLE, "UNIMPL scePsmfSpecifyStreamWithStreamType(%08x, %08x, %i)", psmfStruct, streamType, channel);
if (!psmf->setStreamWithType(streamType, channel)) {
psmf->setStreamNum(-1);
}
return 0;
}
u32 scePsmfSpecifyStreamWithStreamTypeNumber(u32 psmfStruct, u32 streamType, u32 typeNum)
{
Psmf *psmf = getPsmf(psmfStruct);
if (!psmf) {
ERROR_LOG(HLE, "scePsmfSpecifyStream - invalid psmf");
return ERROR_PSMF_NOT_FOUND;
}
ERROR_LOG(HLE, "UNIMPL scePsmfSpecifyStreamWithStreamTypeNumber(%08x, %08x, %08x)", psmfStruct, streamType, typeNum);
return 0;
}
u32 scePsmfSpecifyStream(u32 psmfStruct, int streamNum) {
Psmf *psmf = getPsmf(psmfStruct);
if (!psmf) {
ERROR_LOG(HLE, "scePsmfSpecifyStream - invalid psmf");
return ERROR_PSMF_NOT_FOUND;
}
INFO_LOG(HLE, "scePsmfSpecifyStream(%08x, %i)", psmfStruct, streamNum);
psmf->setStreamNum(streamNum);
return 0;
}
u32 scePsmfGetVideoInfo(u32 psmfStruct, u32 videoInfoAddr) {
INFO_LOG(HLE, "scePsmfGetVideoInfo(%08x, %08x)", psmfStruct, videoInfoAddr);
Psmf *psmf = getPsmf(psmfStruct);
@ -476,17 +555,16 @@ u32 scePsmfQueryStreamOffset(u32 bufferAddr, u32 offsetAddr)
{
ERROR_LOG(HLE, "UNIMPL scePsmfQueryStreamOffset(%08x, %08x)", bufferAddr, offsetAddr);
if (Memory::IsValidAddress(offsetAddr)) {
Memory::Write_U32(0, offsetAddr);
Memory::Write_U32(bswap32(Memory::Read_U32(bufferAddr + PSMF_STREAM_OFFSET_OFFSET)), offsetAddr);
}
// return 0 breaks history mode in Saint Seiya Omega
return 1;
return 0;
}
u32 scePsmfQueryStreamSize(u32 bufferAddr, u32 sizeAddr)
{
ERROR_LOG(HLE, "UNIMPL scePsmfQueryStreamSize(%08x, %08x)", bufferAddr, sizeAddr);
if (Memory::IsValidAddress(sizeAddr)) {
Memory::Write_U32(1, sizeAddr);
Memory::Write_U32(bswap32(Memory::Read_U32(bufferAddr + PSMF_STREAM_SIZE_OFFSET)), sizeAddr);
}
return 0;
}
@ -518,7 +596,17 @@ u32 scePsmfGetPsmfVersion(u32 psmfStruct)
u32 scePsmfVerifyPsmf(u32 psmfAddr)
{
ERROR_LOG(HLE, "UNIMPLEMENTED scePsmfVerifyPsmf(%08x)", psmfAddr);
DEBUG_LOG(HLE, "scePsmfVerifyPsmf(%08x)", psmfAddr);
int magic = Memory::Read_U32(psmfAddr);
if (magic != PSMF_MAGIC) {
ERROR_LOG(HLE, "scePsmfVerifyPsmf - bad magic");
return ERROR_PSMF_NOT_FOUND;
}
int version = Memory::Read_U32(psmfAddr + PSMF_STREAM_VERSION_OFFSET);
if (version < 0) {
ERROR_LOG(HLE, "scePsmfVerifyPsmf - bad version");
return ERROR_PSMF_NOT_FOUND;
}
return 0;
}
@ -667,18 +755,38 @@ int scePsmfPlayerBreak(u32 psmfPlayer)
int scePsmfPlayerSetPsmf(u32 psmfPlayer, const char *filename)
{
ERROR_LOG(HLE, "UNIMPL scePsmfPlayerSetPsmf(%08x, %s)", psmfPlayer, filename);
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (psmfplayer)
{
psmfplayer->status = PSMF_PLAYER_STATUS_STANDBY;
psmfplayer->mediaengine->loadFile(filename);
psmfplayer->psmfPlayerLastTimestamp = psmfplayer->mediaengine->getLastTimeStamp();
}
else
{
ERROR_LOG(HLE, "psmfplayer null in scePsmfPlayerSetPsmf");
}
return 0;
}
int scePsmfPlayerSetPsmfCB(u32 psmfPlayer, const char *filename)
{
ERROR_LOG(HLE, "UNIMPL scePsmfPlayerSetPsmfCB(%08x, %s)", psmfPlayer, filename);
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (psmfplayer)
{
psmfplayer->status = PSMF_PLAYER_STATUS_STANDBY;
psmfplayer->mediaengine->loadFile(filename);
psmfplayer->psmfPlayerLastTimestamp = psmfplayer->mediaengine->getLastTimeStamp();
}
else
{
ERROR_LOG(HLE, "psmfplayer null in scePsmfPlayerSetPsmfCB");
}
return 0;
}
@ -698,15 +806,24 @@ int scePsmfPlayerStart(u32 psmfPlayer, u32 psmfPlayerData, int initPts)
psmfPlayerMap[psmfPlayer] = psmfplayer;
}
PsmfPlayerData data = {0};
data.videoCodec = psmfplayer->videoCodec;
data.videoStreamNum = psmfplayer->videoStreamNum;
data.audioCodec = psmfplayer->audioCodec;
data.audioStreamNum = psmfplayer->audioStreamNum;
data.playMode = psmfplayer->playMode;
data.playSpeed = psmfplayer->playSpeed;
data.psmfPlayerLastTimestamp = psmfplayer->psmfPlayerLastTimestamp;
Memory::WriteStruct(psmfPlayerData, &data);
if (Memory::IsValidAddress(psmfPlayerData)) {
PsmfPlayerData data = {0};
Memory::ReadStruct(psmfPlayerData, &data);
psmfplayer->videoCodec = data.videoCodec;
psmfplayer->videoStreamNum = data.videoStreamNum;
psmfplayer->audioCodec = data.audioCodec;
psmfplayer->audioStreamNum = data.audioStreamNum;
psmfplayer->playMode = data.playMode;
psmfplayer->playSpeed = data.playSpeed;
/*data.videoCodec = psmfplayer->videoCodec;
data.videoStreamNum = psmfplayer->videoStreamNum;
data.audioCodec = psmfplayer->audioCodec;
data.audioStreamNum = psmfplayer->audioStreamNum;
data.playMode = psmfplayer->playMode;
data.playSpeed = psmfplayer->playSpeed;
data.psmfPlayerLastTimestamp = psmfplayer->psmfPlayerLastTimestamp;
Memory::WriteStruct(psmfPlayerData, &data);*/
}
psmfplayer->psmfPlayerAtracAu.dts = initPts;
psmfplayer->psmfPlayerAtracAu.pts = initPts;
@ -714,6 +831,8 @@ int scePsmfPlayerStart(u32 psmfPlayer, u32 psmfPlayerData, int initPts)
psmfplayer->psmfPlayerAvcAu.pts = initPts;
psmfplayer->status = PSMF_PLAYER_STATUS_PLAYING;
psmfplayer->mediaengine->openContext();
return 0;
}
@ -731,7 +850,7 @@ int scePsmfPlayerDelete(u32 psmfPlayer)
int scePsmfPlayerUpdate(u32 psmfPlayer)
{
ERROR_LOG(HLE, "scePsmfPlayerUpdate(%08x)", psmfPlayer);
DEBUG_LOG(HLE, "scePsmfPlayerUpdate(%08x)", psmfPlayer);
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (!psmfplayer) {
ERROR_LOG(HLE, "scePsmfPlayerUpdate - invalid psmf");
@ -739,12 +858,11 @@ int scePsmfPlayerUpdate(u32 psmfPlayer)
}
if (psmfplayer->psmfPlayerAvcAu.pts > 0) {
if (psmfplayer->psmfPlayerAvcAu.pts > psmfplayer->psmfPlayerLastTimestamp) {
if (psmfplayer->psmfPlayerAvcAu.pts >= psmfplayer->psmfPlayerLastTimestamp) {
INFO_LOG(HLE,"video end reach");
psmfplayer->status = PSMF_PLAYER_STATUS_PLAYING_FINISHED;
}
}
// TODO: Once we start increasing pts somewhere, and actually know the last timestamp, do this better.
psmfplayer->status = PSMF_PLAYER_STATUS_PLAYING_FINISHED;
return 0;
}
@ -759,30 +877,52 @@ int scePsmfPlayerReleasePsmf(u32 psmfPlayer)
int scePsmfPlayerGetVideoData(u32 psmfPlayer, u32 videoDataAddr)
{
ERROR_LOG(HLE, "UNIMPL scePsmfPlayerGetVideoData(%08x, %08x)", psmfPlayer, videoDataAddr);
DEBUG_LOG(HLE, "scePsmfPlayerGetVideoData(%08x, %08x)", psmfPlayer, videoDataAddr);
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (!psmfplayer) {
ERROR_LOG(HLE, "scePsmfPlayerGetVideoData - invalid psmf");
return ERROR_PSMF_NOT_FOUND;
}
// TODO: Once we start increasing pts somewhere, and actually know the last timestamp, do this better.
psmfplayer->status = PSMF_PLAYER_STATUS_PLAYING_FINISHED;
return 0;
if (Memory::IsValidAddress(videoDataAddr)) {
int frameWidth = Memory::Read_U32(videoDataAddr);
u32 displaybuf = Memory::Read_U32(videoDataAddr + 4);
int displaypts = Memory::Read_U32(videoDataAddr + 8);
if (psmfplayer->mediaengine->stepVideo(videoPixelMode)) {
int displaybufSize = psmfplayer->mediaengine->writeVideoImage(Memory::GetPointer(displaybuf), frameWidth, videoPixelMode);
gpu->InvalidateCache(displaybuf, displaybufSize, GPU_INVALIDATE_SAFE);
}
psmfplayer->psmfPlayerAvcAu.pts = psmfplayer->mediaengine->getVideoTimeStamp();
Memory::Write_U32(psmfplayer->psmfPlayerAvcAu.pts, videoDataAddr + 8);
}
int ret = psmfplayer->mediaengine->IsVideoEnd() ? ERROR_PSMFPLAYER_NO_MORE_DATA : 0;
s64 deltapts = psmfplayer->mediaengine->getVideoTimeStamp() - psmfplayer->mediaengine->getAudioTimeStamp();
int delaytime = 3000;
if (deltapts > 0 && !psmfplayer->mediaengine->IsAudioEnd())
delaytime = deltapts * 1000000 / 90000;
if (!ret)
return hleDelayResult(ret, "psmfPlayer video decode", delaytime);
else
return hleDelayResult(ret, "psmfPlayer all data decoded", 3000);
}
int scePsmfPlayerGetAudioData(u32 psmfPlayer, u32 audioDataAddr)
{
ERROR_LOG(HLE, "UNIMPL scePsmfPlayerGetAudioData(%08x, %08x)", psmfPlayer, audioDataAddr);
DEBUG_LOG(HLE, "scePsmfPlayerGetAudioData(%08x, %08x)", psmfPlayer, audioDataAddr);
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (!psmfplayer) {
ERROR_LOG(HLE, "scePsmfPlayerGetAudioData - invalid psmf");
return ERROR_PSMF_NOT_FOUND;
}
if (Memory::IsValidAddress(audioDataAddr))
if (Memory::IsValidAddress(audioDataAddr)) {
Memory::Memset(audioDataAddr, 0, audioSamplesBytes);
return 0;
psmfplayer->mediaengine->getAudioSamples(Memory::GetPointer(audioDataAddr));
}
int ret = psmfplayer->mediaengine->IsAudioEnd() ? ERROR_PSMFPLAYER_NO_MORE_DATA : 0;
return hleDelayResult(ret, "psmfPlayer audio decode", 3000);
}
int scePsmfPlayerGetCurrentStatus(u32 psmfPlayer)
@ -792,13 +932,13 @@ int scePsmfPlayerGetCurrentStatus(u32 psmfPlayer)
ERROR_LOG(HLE, "scePsmfPlayerGetCurrentStatus(%08x) - invalid psmf", psmfPlayer);
return ERROR_PSMF_NOT_FOUND;
}
ERROR_LOG(HLE, "%d=scePsmfPlayerGetCurrentStatus(%08x)", psmfplayer->status, psmfPlayer);
DEBUG_LOG(HLE, "%d=scePsmfPlayerGetCurrentStatus(%08x)", psmfplayer->status, psmfPlayer);
return psmfplayer->status;
}
u32 scePsmfPlayerGetCurrentPts(u32 psmfPlayer, u32 currentPtsAddr)
{
ERROR_LOG(HLE, "scePsmfPlayerGetCurrentPts(%08x, %08x)", psmfPlayer , currentPtsAddr);
DEBUG_LOG(HLE, "scePsmfPlayerGetCurrentPts(%08x, %08x)", psmfPlayer , currentPtsAddr);
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
if (!psmfplayer) {
ERROR_LOG(HLE, "scePsmfPlayerGetCurrentPts - invalid psmf");
@ -810,7 +950,7 @@ u32 scePsmfPlayerGetCurrentPts(u32 psmfPlayer, u32 currentPtsAddr)
if (Memory::IsValidAddress(currentPtsAddr)) {
//Comment out until psmfPlayerAvcAu.pts start increasing correctly, Ultimate Ghosts N Goblins relies on it .
//Memory::Write_U64(psmfplayer->psmfPlayerAvcAu.pts, currentPtsAddr);
Memory::Write_U32(psmfplayer->psmfPlayerAvcAu.pts, currentPtsAddr);
}
return 0;
}
@ -828,7 +968,13 @@ u32 scePsmfPlayerGetPsmfInfo(u32 psmfPlayer, u32 psmfInfoAddr)
}
if (Memory::IsValidAddress(psmfInfoAddr)) {
Memory::Write_U64(psmfplayer->psmfPlayerAvcAu.pts, psmfInfoAddr);
Memory::Write_U32(psmfplayer->psmfPlayerLastTimestamp, psmfInfoAddr);
Memory::Write_U32(psmfplayer->videoStreamNum, psmfInfoAddr + 4);
Memory::Write_U32(psmfplayer->audioStreamNum, psmfInfoAddr + 8);
// pcm stream num?
Memory::Write_U32(0, psmfInfoAddr + 12);
// Player version?
Memory::Write_U32(0, psmfInfoAddr + 16);
}
return 0;
}
@ -942,6 +1088,7 @@ u32 scePsmfPlayerSelectSpecificVideo(u32 psmfPlayer, int videoCodec, int videoSt
psmfplayer->videoCodec = videoCodec;
psmfplayer->videoStreamNum = videoStreamNum;
psmfplayer->mediaengine->setVideoStream(videoStreamNum);
return 0;
}
@ -956,6 +1103,7 @@ u32 scePsmfPlayerSelectSpecificAudio(u32 psmfPlayer, int audioCodec, int audioSt
psmfplayer->audioCodec = audioCodec;
psmfplayer->audioStreamNum = audioStreamNum;
psmfplayer->mediaengine->setAudioStream(audioStreamNum);
return 0;
}
@ -969,19 +1117,17 @@ u32 scePsmfPlayerConfigPlayer(u32 psmfPlayer, int configMode, int configAttr)
if (configMode == PSMF_PLAYER_CONFIG_MODE_LOOP) {
videoLoopStatus = configAttr;
} else if (configMode == PSMF_PLAYER_CONFIG_MODE_PIXEL_TYPE) {
videoPixelMode = configAttr;
// Does -1 mean default or something?
if (configAttr != -1) {
videoPixelMode = configAttr;
}
} else {
ERROR_LOG(HLE, "scePsmfPlayerConfigPlayer(%08x, %i, %i)", psmfPlayer , configMode, configAttr);
ERROR_LOG(HLE, "scePsmfPlayerConfigPlayer(%08x, %i, %i): unknown parameter", psmfPlayer, configMode, configAttr);
}
return 0;
}
u32 scePsmfSpecifyStream(u32 psmfPlayer, int streamNum) {
ERROR_LOG(HLE, "UNIMPL scePsmfSpecifyStream(%08x, %i)", psmfPlayer, streamNum);
return 0;
}
const HLEFunction scePsmf[] = {
{0xc22c8327, WrapU_UU<scePsmfSetPsmf>, "scePsmfSetPsmf"},
{0xC7DB3A5B, WrapU_UUU<scePsmfGetCurrentStreamType>, "scePsmfGetCurrentStreamType"},

View File

@ -43,18 +43,6 @@ int sceNpDrmOpen()
return 0;
}
int sceKernelLoadModuleNpDrm()
{
ERROR_LOG(HLE, "UNIMPL sceKernelLoadModuleNpDrm");
return 0;
}
int sceKernelLoadExecNpDrm()
{
ERROR_LOG(HLE, "UNIMPL sceKernelLoadExecNpDrm");
return 0;
}
const HLEFunction sceNpDrm[] =
{
{0xA1336091, WrapI_U<sceNpDrmSetLicenseeKey>, "sceNpDrmSetLicenseeKey"},
@ -63,8 +51,6 @@ const HLEFunction sceNpDrm[] =
{0x08d98894, WrapI_U<sceNpDrmEdataSetupKey>, "sceNpDrmEdataSetupKey"},
{0x219EF5CC, WrapI_U<sceNpDrmEdataGetDataSize>, "sceNpDrmEdataGetDataSize"},
{0x2BAA4294, 0, "sceNpDrmOpen"},
{0xC618D0B1, 0, "sceKernelLoadModuleNpDrm"},
{0xAA5FC85B, 0, "sceKernelLoadExecNpDrm"},
};
void Register_sceNpDrm()

View File

@ -37,8 +37,10 @@
static timeval rtcBaseTime;
// Grabbed from JPSCP
// This is # of microseconds between January 1, 0001 and January 1, 1970.
const u64 rtcMagicOffset = 62135596800000000L;
// This is the # of microseconds between January 1, 0001 and January 1, 1970.
const u64 rtcMagicOffset = 62135596800000000ULL;
// This is the # of microseconds between January 1, 0001 and January 1, 1601 (for Win32 FILETIME.)
const u64 rtcFiletimeOffset = 50491123200000000ULL;
const int PSP_TIME_INVALID_YEAR = -1;
const int PSP_TIME_INVALID_MONTH = -2;
@ -55,7 +57,7 @@ u64 __RtcGetCurrentTick()
}
#if defined(_WIN32)
#define FILETIME_FROM_UNIX_EPOCH_US 11644473600000000ULL
#define FILETIME_FROM_UNIX_EPOCH_US (rtcMagicOffset - rtcFiletimeOffset)
void gettimeofday(timeval *tv, void *ignore)
{
@ -621,18 +623,46 @@ int sceRtcGetDosTime(u32 datePtr, u32 dosTime)
retValue = -1;
}
return retValue;
}
int sceRtcSetWin32FileTime(u32 datePtr, u32 win32TimePtr)
int sceRtcSetWin32FileTime(u32 datePtr, u64 win32Time)
{
ERROR_LOG_REPORT(HLE, "UNIMPL sceRtcSetWin32FileTime(%d,%d)", datePtr, win32TimePtr);
if (!Memory::IsValidAddress(datePtr))
{
ERROR_LOG_REPORT(HLE, "sceRtcSetWin32FileTime(%08x, %lld): invalid address", datePtr, win32Time);
return -1;
}
DEBUG_LOG(HLE, "sceRtcSetWin32FileTime(%08x, %lld)", datePtr, win32Time);
u64 ticks = (win32Time / 10) + rtcFiletimeOffset;
auto pspTime = Memory::GetStruct<ScePspDateTime>(datePtr);
__RtcTicksToPspTime(*pspTime, ticks);
return 0;
}
int sceRtcGetWin32FileTime(u32 datePtr, u32 win32TimePtr)
{
ERROR_LOG_REPORT(HLE, "UNIMPL sceRtcGetWin32FileTime(%d,%d)", datePtr, win32TimePtr);
if (!Memory::IsValidAddress(datePtr))
{
ERROR_LOG_REPORT(HLE, "sceRtcGetWin32FileTime(%08x, %08x): invalid address", datePtr, win32TimePtr);
return -1;
}
DEBUG_LOG(HLE, "sceRtcGetWin32FileTime(%08x, %08x)", datePtr, win32TimePtr);
if (!Memory::IsValidAddress(win32TimePtr))
return SCE_KERNEL_ERROR_INVALID_VALUE;
auto pspTime = Memory::GetStruct<ScePspDateTime>(datePtr);
u64 result = __RtcPspTimeToTicks(*pspTime);
if (!__RtcValidatePspTime(*pspTime) || result < rtcFiletimeOffset)
{
Memory::Write_U64(0, win32TimePtr);
return SCE_KERNEL_ERROR_INVALID_VALUE;
}
Memory::Write_U64((result - rtcFiletimeOffset) * 10, win32TimePtr);
return 0;
}
@ -836,6 +866,13 @@ int sceRtcGetLastAdjustedTime(u32 tickPtr)
return 0;
}
//Returns 0 on success, according to Project Diva 2nd jpcsptrace log
int sceRtcSetAlarmTick(u32 unknown1, u32 unknown2)
{
ERROR_LOG(HLE, "UNIMPL sceRtcSetAlarmTick(%x, %x)", unknown1, unknown2);
return 0;
}
const HLEFunction sceRtc[] =
{
{0xC41C2853, &WrapU_V<sceRtcGetTickResolution>, "sceRtcGetTickResolution"},
@ -854,7 +891,7 @@ const HLEFunction sceRtc[] =
{0x27c4594c, &WrapI_UU<sceRtcGetTime_t>, "sceRtcGetTime_t"},
{0xF006F264, &WrapI_UU<sceRtcSetDosTime>, "sceRtcSetDosTime"},
{0x36075567, &WrapI_UU<sceRtcGetDosTime>, "sceRtcGetDosTime"},
{0x7ACE4C04, &WrapI_UU<sceRtcSetWin32FileTime>, "sceRtcSetWin32FileTime"},
{0x7ACE4C04, &WrapI_UU64<sceRtcSetWin32FileTime>, "sceRtcSetWin32FileTime"},
{0xCF561893, &WrapI_UU<sceRtcGetWin32FileTime>, "sceRtcGetWin32FileTime"},
{0x7ED29E40, &WrapU_UU<sceRtcSetTick>, "sceRtcSetTick"},
{0x6FF40ACC, &WrapU_UU<sceRtcGetTick>, "sceRtcGetTick"},
@ -878,7 +915,7 @@ const HLEFunction sceRtc[] =
{0x1909c99b, &WrapI_UU64<sceRtcSetTime64_t>, "sceRtcSetTime64_t"},
{0x62685E98, &WrapI_U<sceRtcGetLastAdjustedTime>, "sceRtcGetLastAdjustedTime"},
{0x203ceb0d, 0, "sceRtcGetLastReincarnatedTime"},
{0x7d1fbed3, 0, "sceRtcSetAlarmTick"},
{0x7d1fbed3, &WrapI_UU<sceRtcSetAlarmTick>, "sceRtcSetAlarmTick"},
{0xf5fcc995, 0, "sceRtc_F5FCC995"},
};

View File

@ -23,6 +23,7 @@
// JPCSP is, as it often is, a pretty good reference although I didn't actually use it much yet:
// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java
#include <cstdlib>
#include "base/basictypes.h"
#include "Log.h"
#include "HLE.h"
@ -34,10 +35,16 @@
#include "sceKernel.h"
enum {
ERROR_SAS_INVALID_GRAIN = 0x80420001,
ERROR_SAS_INVALID_MAX_VOICES = 0x80420002,
ERROR_SAS_INVALID_OUTPUT_MODE = 0x80420003,
ERROR_SAS_INVALID_SAMPLE_RATE = 0x80420004,
ERROR_SAS_BAD_ADDRESS = 0x80420005,
ERROR_SAS_INVALID_VOICE = 0x80420010,
ERROR_SAS_INVALID_ADSR_CURVE_MODE = 0x80420013,
ERROR_SAS_INVALID_PARAMETER = 0x80420014,
ERROR_SAS_VOICE_PAUSED = 0x80420016,
ERROR_SAS_INVALID_VOLUME = 0x80420018,
ERROR_SAS_INVALID_SIZE = 0x8042001A,
ERROR_SAS_BUSY = 0x80420030,
ERROR_SAS_NOT_INIT = 0x80420100,
@ -63,15 +70,32 @@ void __SasShutdown() {
u32 sceSasInit(u32 core, u32 grainSize, u32 maxVoices, u32 outputMode, u32 sampleRate) {
INFO_LOG(HLE,"sceSasInit(%08x, %i, %i, %i, %i)", core, grainSize, maxVoices, outputMode, sampleRate);
if (!Memory::IsValidAddress(core) || (core & 0x3F) != 0) {
ERROR_LOG_REPORT(HLE, "sceSasInit(%08x, %i, %i, %i, %i): bad core address", core, grainSize, maxVoices, outputMode, sampleRate);
return ERROR_SAS_BAD_ADDRESS;
}
if (maxVoices == 0 || maxVoices > PSP_SAS_VOICES_MAX) {
ERROR_LOG_REPORT(HLE, "sceSasInit(%08x, %i, %i, %i, %i): bad max voices", core, grainSize, maxVoices, outputMode, sampleRate);
return ERROR_SAS_INVALID_MAX_VOICES;
}
if (grainSize < 0x40 || grainSize > 0x800 || (grainSize & 0x1F) != 0) {
ERROR_LOG_REPORT(HLE, "sceSasInit(%08x, %i, %i, %i, %i): bad grain size", core, grainSize, maxVoices, outputMode, sampleRate);
return ERROR_SAS_INVALID_GRAIN;
}
if (outputMode != 0 && outputMode != 1) {
ERROR_LOG_REPORT(HLE, "sceSasInit(%08x, %i, %i, %i, %i): bad output mode", core, grainSize, maxVoices, outputMode, sampleRate);
return ERROR_SAS_INVALID_OUTPUT_MODE;
}
INFO_LOG(HLE, "sceSasInit(%08x, %i, %i, %i, %i)", core, grainSize, maxVoices, outputMode, sampleRate);
sas->SetGrainSize(grainSize);
sas->maxVoices = maxVoices;
// Seems like maxVoiecs is actually ignored for all intents and purposes.
sas->maxVoices = PSP_SAS_VOICES_MAX;
sas->outputMode = outputMode;
for (int i = 0; i < sas->maxVoices; i++) {
sas->voices[i].sampleRate = sampleRate;
sas->voices[i].playing = false;
sas->voices[i].loop = true; // inverted flag
sas->voices[i].loop = false;
}
return 0;
}
@ -96,7 +120,9 @@ u32 _sceSasCore(u32 core, u32 outAddr) {
}
sas->Mix(outAddr);
return 0;
// Actual delay time seems to between 240 and 1000 us, based on grain and possibly other factors.
// Let's aim low for now.
return hleDelayResult(0, "sas core", 240);
}
// Another way of running the mixer, the inoutAddr should be both input and output
@ -108,7 +134,9 @@ u32 _sceSasCoreWithMix(u32 core, u32 inoutAddr, int leftVolume, int rightVolume)
}
sas->Mix(inoutAddr, inoutAddr, leftVolume, rightVolume);
return 0;
// Actual delay time seems to between 240 and 1000 us, based on grain and possibly other factors.
// Let's aim low for now.
return hleDelayResult(0, "sas core", 240);
}
u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) {
@ -119,23 +147,23 @@ u32 sceSasSetVoice(u32 core, int voiceNum, u32 vagAddr, int size, int loop) {
return ERROR_SAS_INVALID_VOICE;
}
if (!Memory::IsValidAddress(vagAddr)) {
ERROR_LOG(HLE, "Ignoring invalid VAG audio address %08x", vagAddr);
return 0;
}
if (size <= 0 || (size & 0xF) != 0) {
WARN_LOG(HLE, "%s: invalid size %d", __FUNCTION__, size);
return ERROR_SAS_INVALID_SIZE;
}
if (!Memory::IsValidAddress(vagAddr)) {
ERROR_LOG(HLE, "Ignoring invalid VAG audio address %08x", vagAddr);
return 0;
}
//Real VAG header is 0x30 bytes behind the vagAddr
SasVoice &v = sas->voices[voiceNum];
u32 prevVagAddr = v.vagAddr;
v.type = VOICETYPE_VAG;
v.vagAddr = vagAddr;
v.vagSize = size;
v.loop = loop ? false : true;
v.loop = loop ? true : false;
v.ChangedParams(vagAddr == prevVagAddr);
return 0;
}
@ -165,7 +193,7 @@ u32 sceSasSetVoicePCM(u32 core, int voiceNum, u32 pcmAddr, int size, int loop)
v.pcmAddr = pcmAddr;
v.pcmSize = size;
v.pcmIndex = 0;
v.loop = loop ? false : true;
v.loop = loop ? true : false;
v.playing = true;
v.ChangedParams(pcmAddr == prevPcmAddr);
return 0;
@ -182,15 +210,14 @@ u32 sceSasGetPauseFlag(u32 core) {
return pauseFlag;
}
u32 sceSasSetPause(u32 core, int voicebit, int pause) {
u32 sceSasSetPause(u32 core, u32 voicebit, int pause) {
DEBUG_LOG(HLE,"sceSasSetPause(%08x, %08x, %i)", core, voicebit, pause);
for (int i = 0; voicebit != 0; i++, voicebit >>= 1) {
for (int i = 0; voicebit != 0; i++, voicebit >>= 1) {
if (i < PSP_SAS_VOICES_MAX && i >= 0) {
if ((voicebit & 1) != 0)
sas->voices[i].paused = pause ? true : false;
} else // TODO: Correct error code? Mimana crashes otherwise.
return ERROR_SAS_INVALID_VOICE;
}
}
return 0;
@ -203,6 +230,10 @@ u32 sceSasSetVolume(u32 core, int voiceNum, int leftVol, int rightVol, int effec
WARN_LOG(HLE, "%s: invalid voicenum %d", __FUNCTION__, voiceNum);
return ERROR_SAS_INVALID_VOICE;
}
bool overVolume = abs(leftVol) > PSP_SAS_VOL_MAX || abs(rightVol) > PSP_SAS_VOL_MAX;
overVolume = overVolume || abs(effectLeftVol) > PSP_SAS_VOL_MAX || abs(effectRightVol) > PSP_SAS_VOL_MAX;
if (overVolume)
return ERROR_SAS_INVALID_VOLUME;
SasVoice &v = sas->voices[voiceNum];
v.volumeLeft = leftVol;
@ -234,7 +265,7 @@ u32 sceSasSetKeyOn(u32 core, int voiceNum) {
return ERROR_SAS_INVALID_VOICE;
}
if (sas->voices[voiceNum].paused) {
if (sas->voices[voiceNum].paused || sas->voices[voiceNum].on) {
return ERROR_SAS_VOICE_PAUSED;
}
@ -255,7 +286,7 @@ u32 sceSasSetKeyOff(u32 core, int voiceNum) {
} else {
DEBUG_LOG(HLE,"sceSasSetKeyOff(%08x, %i)", core, voiceNum);
if (sas->voices[voiceNum].paused) {
if (sas->voices[voiceNum].paused || !sas->voices[voiceNum].on) {
return ERROR_SAS_VOICE_PAUSED;
}
@ -373,8 +404,8 @@ u32 sceSasRevEVOL(u32 core, int lv, int rv) {
u32 sceSasRevVON(u32 core, int dry, int wet) {
DEBUG_LOG(HLE,"sceSasRevVON(%08x, %i, %i)", core, dry, wet);
sas->waveformEffect.isDryOn = (dry > 0);
sas->waveformEffect.isWetOn = (wet > 0);
sas->waveformEffect.isDryOn = dry & 1;
sas->waveformEffect.isWetOn = wet & 1;
return 0;
}
@ -426,18 +457,28 @@ u32 sceSasSetSteepWave(u32 sasCore, int voice, int unknown) {
return 0;
}
u32 __sceSasSetVoiceATRAC3(u32 core, int voice, int atrac3Context) {
ERROR_LOG_REPORT(HLE, "UNIMPL __sceSasSetVoiceATRAC3(%08x, %i, %i)", core, voice, atrac3Context);
u32 __sceSasSetVoiceATRAC3(u32 core, int voiceNum, u32 atrac3Context) {
INFO_LOG_REPORT(HLE, "__sceSasSetVoiceATRAC3(%08x, %i, %08x)", core, voiceNum, atrac3Context);
SasVoice &v = sas->voices[voiceNum];
v.type = VOICETYPE_ATRAC3;
v.loop = false;
v.playing = true;
v.atrac3.setContext(atrac3Context);
Memory::Write_U32(atrac3Context, core + 56 * voiceNum + 20);
return 0;
}
u32 __sceSasConcatenateATRAC3(u32 core, int voice, u32 atrac3DataAddr, int atrac3DataLength) {
ERROR_LOG_REPORT(HLE, "UNIMPL __sceSasConcatenateATRAC3(%08x, %i, %08x, %i)", core, voice, atrac3DataAddr, atrac3DataLength);
u32 __sceSasConcatenateATRAC3(u32 core, int voiceNum, u32 atrac3DataAddr, int atrac3DataLength) {
INFO_LOG_REPORT(HLE, "__sceSasConcatenateATRAC3(%08x, %i, %08x, %i)", core, voiceNum, atrac3DataAddr, atrac3DataLength);
SasVoice &v = sas->voices[voiceNum];
if (Memory::IsValidAddress(atrac3DataAddr))
v.atrac3.addStreamData(Memory::GetPointer(atrac3DataAddr), atrac3DataLength);
return 0;
}
u32 __sceSasUnsetATRAC3(u32 core, int voice) {
ERROR_LOG_REPORT(HLE, "UNIMPL __sceSasUnsetATRAC3(%08x, %i)", core, voice);
u32 __sceSasUnsetATRAC3(u32 core, int voiceNum) {
INFO_LOG_REPORT(HLE, "__sceSasUnsetATRAC3(%08x, %i)", core, voiceNum);
Memory::Write_U32(0, core + 56 * voiceNum + 20);
return 0;
}
@ -463,7 +504,7 @@ const HLEFunction sceSasCore[] =
{0x33d4ab37, WrapU_UI<sceSasRevType>, "__sceSasRevType"},
{0x267a6dd2, WrapU_UII<sceSasRevParam>, "__sceSasRevParam"},
{0x2c8e6ab3, WrapU_U<sceSasGetPauseFlag>, "__sceSasGetPauseFlag"},
{0x787d04d5, WrapU_UII<sceSasSetPause>, "__sceSasSetPause"},
{0x787d04d5, WrapU_UUI<sceSasSetPause>, "__sceSasSetPause"},
{0xa232cbe6, WrapU_UII<sceSasSetTriangularWave>, "__sceSasSetTrianglarWave"}, // Typo.
{0xd5ebbbcd, WrapU_UII<sceSasSetSteepWave>, "__sceSasSetSteepWave"},
{0xBD11B7C2, WrapU_U<sceSasGetGrain>, "__sceSasGetGrain"},
@ -472,7 +513,7 @@ const HLEFunction sceSasCore[] =
{0xe855bf76, WrapU_UU<sceSasSetOutputMode>, "__sceSasSetOutputmode"},
{0x07f58c24, WrapU_UU<sceSasGetAllEnvelopeHeights>, "__sceSasGetAllEnvelopeHeights"},
{0xE1CD9561, WrapU_UIUII<sceSasSetVoicePCM>, "__sceSasSetVoicePCM"},
{0x4AA9EAD6, WrapU_UII<__sceSasSetVoiceATRAC3>,"__sceSasSetVoiceATRAC3"},
{0x4AA9EAD6, WrapU_UIU<__sceSasSetVoiceATRAC3>,"__sceSasSetVoiceATRAC3"},
{0x7497EA85, WrapU_UIUI<__sceSasConcatenateATRAC3>,"__sceSasConcatenateATRAC3"},
{0xF6107F00, WrapU_UI<__sceSasUnsetATRAC3>,"__sceSasUnsetATRAC3"},
};

View File

@ -52,6 +52,7 @@ static PSPSaveDialog saveDialog;
static PSPMsgDialog msgDialog;
static PSPOskDialog oskDialog;
static PSPPlaceholderDialog netDialog;
static PSPPlaceholderDialog screenshotDialog;
static std::set<int> currentlyLoadedModules;
@ -77,10 +78,10 @@ void __UtilityDoState(PointerWrap &p)
void __UtilityShutdown()
{
saveDialog.Shutdown();
msgDialog.Shutdown();
oskDialog.Shutdown();
netDialog.Shutdown();
saveDialog.Shutdown(true);
msgDialog.Shutdown(true);
oskDialog.Shutdown(true);
netDialog.Shutdown(true);
}
int __UtilityGetStatus()
@ -377,10 +378,31 @@ int sceUtilityNetconfGetStatus()
return netDialog.GetStatus();
}
//TODO: Implement all sceUtilityScreenshot* for real, it doesn't seem to be complex
//but it requires more investigation
u32 sceUtilityScreenshotInitStart(u32 unknown1, u32 unknown2, u32 unknown3, u32 unknown4, u32 unknown5, u32 unknown6)
{
u32 retval = screenshotDialog.Init();
WARN_LOG(HLE, "UNIMPL %i=sceUtilityScreenshotInitStart(%x, %x, %x, %x, %x, %x)", retval, unknown1, unknown2, unknown3, unknown4, unknown5, unknown6);
return retval;
}
u32 sceUtilityScreenshotShutdownStart()
{
WARN_LOG(HLE, "UNTESTED sceUtilityScreenshotShutdownStart()");
return screenshotDialog.Shutdown();
}
u32 sceUtilityScreenshotUpdate(u32 unknown)
{
ERROR_LOG(HLE, "UNIMPL sceUtilityScreenshotUpdate(%d)", unknown);
return screenshotDialog.Update();
}
int sceUtilityScreenshotGetStatus()
{
u32 retval = __UtilityGetStatus();
ERROR_LOG(HLE, "UNIMPL %i=sceUtilityScreenshotGetStatus()", retval);
u32 retval = screenshotDialog.GetStatus();
WARN_LOG(HLE, "UNIMPL %i=sceUtilityScreenshotGetStatus()", retval);
return retval;
}
@ -561,9 +583,9 @@ const HLEFunction sceUtility[] =
{0x2a2b3de0, &WrapU_U<sceUtilityLoadModule>, "sceUtilityLoadModule"},
{0xe49bfe92, &WrapU_U<sceUtilityUnloadModule>, "sceUtilityUnloadModule"},
{0x0251B134, 0, "sceUtilityScreenshotInitStart"},
{0xF9E0008C, 0, "sceUtilityScreenshotShutdownStart"},
{0xAB083EA9, 0, "sceUtilityScreenshotUpdate"},
{0x0251B134, &WrapU_UUUUUU<sceUtilityScreenshotInitStart>, "sceUtilityScreenshotInitStart"},
{0xF9E0008C, &WrapU_V<sceUtilityScreenshotShutdownStart>, "sceUtilityScreenshotShutdownStart"},
{0xAB083EA9, &WrapU_U<sceUtilityScreenshotUpdate>, "sceUtilityScreenshotUpdate"},
{0xD81957B7, &WrapI_V<sceUtilityScreenshotGetStatus>, "sceUtilityScreenshotGetStatus"},
{0x86A03A27, 0, "sceUtilityScreenshotContStart"},

View File

@ -17,27 +17,616 @@
#include "MediaEngine.h"
#include "../MemMap.h"
#include "GPU/GPUInterface.h"
#include "Core/HW/atrac3plus.h"
static const int modeBpp[4] = { 2, 2, 2, 4 };
#ifdef USE_FFMPEG
// Urgh! Why is this needed?
#ifdef ANDROID
#ifndef UINT64_C
#define UINT64_C(c) (c ## ULL)
#endif
#endif
void MediaEngine::writeVideoImage(u32 bufferPtr, int frameWidth, int videoPixelMode)
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
}
#endif // USE_FFMPEG
static const int TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650 = 0x00;
static const int TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR5551 = 0x01;
static const int TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR4444 = 0x02;
static const int TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888 = 0x03;
#ifdef USE_FFMPEG
static AVPixelFormat getSwsFormat(int pspFormat)
{
if (videoPixelMode >= (int)(sizeof(modeBpp) / sizeof(modeBpp[0])) || videoPixelMode < 0)
switch (pspFormat)
{
ERROR_LOG(ME, "Unexpected videoPixelMode %d, using 0 instead.", videoPixelMode);
videoPixelMode = 0;
case TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650:
return AV_PIX_FMT_BGR565LE;
case TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR5551:
return AV_PIX_FMT_BGR555LE;
case TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR4444:
return AV_PIX_FMT_BGR444LE;
case TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888:
return AV_PIX_FMT_RGBA;
default:
ERROR_LOG(ME, "Unknown pixel format");
return (AVPixelFormat)0;
}
}
#endif
static int getPixelFormatBytes(int pspFormat)
{
switch (pspFormat)
{
case TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650:
case TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR5551:
case TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR4444:
return 2;
case TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888:
return 4;
default:
ERROR_LOG(ME, "Unknown pixel format");
return 4;
}
}
MediaEngine::MediaEngine(): m_streamSize(0), m_readSize(0), m_decodedPos(0), m_pdata(0) {
m_pFormatCtx = 0;
m_pCodecCtx = 0;
m_pFrame = 0;
m_pFrameRGB = 0;
m_pIOContext = 0;
m_videoStream = -1;
m_audioStream = -1;
m_buffer = 0;
m_demux = 0;
m_audioContext = 0;
m_isVideoEnd = false;
m_isAudioEnd = false;
}
MediaEngine::~MediaEngine() {
closeMedia();
}
void MediaEngine::closeMedia() {
#ifdef USE_FFMPEG
if (m_buffer)
av_free(m_buffer);
if (m_pFrameRGB)
av_free(m_pFrameRGB);
if (m_pFrame)
av_free(m_pFrame);
if (m_pIOContext && m_pIOContext->buffer)
av_free(m_pIOContext->buffer);
if (m_pIOContext)
av_free(m_pIOContext);
if (m_pCodecCtx)
avcodec_close(m_pCodecCtx);
if (m_pFormatCtx)
avformat_close_input(&m_pFormatCtx);
#endif // USE_FFMPEG
if (m_pdata)
delete [] m_pdata;
if (m_demux)
delete m_demux;
m_buffer = 0;
m_pFrame = 0;
m_pFrameRGB = 0;
m_pIOContext = 0;
m_pCodecCtx = 0;
m_pFormatCtx = 0;
m_pdata = 0;
m_demux = 0;
Atrac3plus_Decoder::CloseContext(&m_audioContext);
m_isVideoEnd = false;
m_isAudioEnd = false;
}
int _MpegReadbuffer(void *opaque, uint8_t *buf, int buf_size)
{
MediaEngine *mpeg = (MediaEngine*)opaque;
int size = std::min(mpeg->m_bufSize, buf_size);
size = std::max(std::min((mpeg->m_readSize - mpeg->m_decodeNextPos), size), 0);
if (size > 0)
memcpy(buf, mpeg->m_pdata + mpeg->m_decodeNextPos, size);
mpeg->m_decodeNextPos += size;
return size;
}
int64_t _MpegSeekbuffer(void *opaque, int64_t offset, int whence)
{
MediaEngine *mpeg = (MediaEngine*)opaque;
switch (whence) {
case SEEK_SET:
mpeg->m_decodeNextPos = offset;
break;
case SEEK_CUR:
mpeg->m_decodeNextPos += offset;
break;
case SEEK_END:
mpeg->m_decodeNextPos = mpeg->m_streamSize - (u32)offset;
break;
}
return offset;
}
#ifdef _DEBUG
void ffmpeg_logger(void *, int, const char *format, va_list va_args) {
char tmp[1024];
vsprintf(tmp, format, va_args);
INFO_LOG(HLE, tmp);
}
#endif
bool MediaEngine::openContext() {
#ifdef USE_FFMPEG
#ifdef _DEBUG
av_log_set_level(AV_LOG_VERBOSE);
av_log_set_callback(&ffmpeg_logger);
#endif
if (m_readSize <= 0x2000 || m_pFormatCtx || !m_pdata)
return false;
u8* tempbuf = (u8*)av_malloc(m_bufSize);
m_pFormatCtx = avformat_alloc_context();
m_pIOContext = avio_alloc_context(tempbuf, m_bufSize, 0, (void*)this, _MpegReadbuffer, NULL, _MpegSeekbuffer);
m_pFormatCtx->pb = m_pIOContext;
// Open video file
if(avformat_open_input((AVFormatContext**)&m_pFormatCtx, NULL, NULL, NULL) != 0)
return false;
if(avformat_find_stream_info(m_pFormatCtx, NULL) < 0)
return false;
if (m_videoStream == -1) {
// Find the first video stream
for(int i = 0; i < (int)m_pFormatCtx->nb_streams; i++) {
if(m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
m_videoStream = i;
break;
}
}
if(m_videoStream == -1)
return false;
}
int bpp = modeBpp[videoPixelMode];
// Get a pointer to the codec context for the video stream
m_pCodecCtx = m_pFormatCtx->streams[m_videoStream]->codec;
// Find the decoder for the video stream
AVCodec *pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id);
if(pCodec == NULL)
return false;
// Open codec
AVDictionary *optionsDict = 0;
if(avcodec_open2(m_pCodecCtx, pCodec, &optionsDict)<0)
return false; // Could not open codec
// fake image. To be improved.
if (Memory::IsValidAddress(bufferPtr))
// Use Dark Grey to identify CG is running
Memory::Memset(bufferPtr, 0x69, frameWidth * videoHeight_ * bpp);
setVideoDim();
int mpegoffset = bswap32(*(int*)(m_pdata + 8));
m_demux = new MpegDemux(m_pdata, m_streamSize, mpegoffset);
m_demux->setReadSize(m_readSize);
m_demux->demux(m_audioStream);
m_audioPos = 0;
m_audioContext = Atrac3plus_Decoder::OpenContext();
m_isVideoEnd = false;
m_isAudioEnd = false;
m_decodedPos = mpegoffset;
#endif // USE_FFMPEG
return true;
}
void MediaEngine::feedPacketData(u32 addr, int size)
bool MediaEngine::loadStream(u8* buffer, int readSize, int StreamSize)
{
// This media engine is totally incompetent and will just ignore all data sent to it.
closeMedia();
// force to clear the useless FBO
gpu->Resized();
m_videopts = 0;
m_audiopts = 0;
m_bufSize = 0x2000;
m_decodeNextPos = 0;
m_readSize = readSize;
m_streamSize = StreamSize;
m_pdata = new u8[StreamSize];
if (!m_pdata)
return false;
memcpy(m_pdata, buffer, m_readSize);
return true;
}
bool MediaEngine::loadFile(const char* filename)
{
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
s64 infosize = info.size;
u8* buf = new u8[infosize];
if (!buf)
return false;
u32 h = pspFileSystem.OpenFile(filename, (FileAccess) FILEACCESS_READ);
pspFileSystem.ReadFile(h, buf, infosize);
pspFileSystem.CloseFile(h);
closeMedia();
// force to clear the useless FBO
gpu->Resized();
m_videopts = 0;
m_audiopts = 0;
m_bufSize = 0x2000;
m_decodeNextPos = 0;
m_readSize = infosize;
m_streamSize = infosize;
m_pdata = buf;
return true;
}
int MediaEngine::addStreamData(u8* buffer, int addSize) {
int size = std::min(addSize, m_streamSize - m_readSize);
if (size > 0 && m_pdata) {
memcpy(m_pdata + m_readSize, buffer, size);
m_readSize += size;
if (!m_pFormatCtx && m_readSize > 0x2000)
openContext();
if (m_demux) {
m_demux->setReadSize(m_readSize);
m_demux->demux(m_audioStream);
}
}
return size;
}
bool MediaEngine::setVideoDim(int width, int height)
{
if (!m_pCodecCtx)
return false;
#ifdef USE_FFMPEG
if (width == 0 && height == 0)
{
// use the orignal video size
m_desWidth = m_pCodecCtx->width;
m_desHeight = m_pCodecCtx->height;
}
else
{
m_desWidth = width;
m_desHeight = height;
}
// Allocate video frame
m_pFrame = avcodec_alloc_frame();
m_sws_ctx = NULL;
m_sws_fmt = -1;
updateSwsFormat(TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888);
// Allocate video frame for RGB24
m_pFrameRGB = avcodec_alloc_frame();
int numBytes = avpicture_get_size((AVPixelFormat)m_sws_fmt, m_desWidth, m_desHeight);
m_buffer = (u8*)av_malloc(numBytes * sizeof(uint8_t));
// Assign appropriate parts of buffer to image planes in pFrameRGB
avpicture_fill((AVPicture *)m_pFrameRGB, m_buffer, (AVPixelFormat)m_sws_fmt, m_desWidth, m_desHeight);
#endif // USE_FFMPEG
return true;
}
void MediaEngine::updateSwsFormat(int videoPixelMode) {
#ifdef USE_FFMPEG
AVPixelFormat swsDesired = getSwsFormat(videoPixelMode);
if (swsDesired != m_sws_fmt && m_pCodecCtx != 0) {
m_sws_fmt = swsDesired;
m_sws_ctx = sws_getCachedContext
(
m_sws_ctx,
m_pCodecCtx->width,
m_pCodecCtx->height,
m_pCodecCtx->pix_fmt,
m_desWidth,
m_desHeight,
(AVPixelFormat)m_sws_fmt,
SWS_BILINEAR,
NULL,
NULL,
NULL
);
}
#endif
}
bool MediaEngine::stepVideo(int videoPixelMode) {
if (!m_pFormatCtx)
return false;
if (!m_pCodecCtx)
return false;
// if video engine is broken, force to add timestamp
m_videopts += 3003;
#ifdef USE_FFMPEG
updateSwsFormat(videoPixelMode);
// TODO: Technically we could set this to frameWidth instead of m_desWidth for better perf.
// Update the linesize for the new format too. We started with the largest size, so it should fit.
m_pFrameRGB->linesize[0] = getPixelFormatBytes(videoPixelMode) * m_desWidth;
if ((!m_pFrame)||(!m_pFrameRGB))
return false;
AVPacket packet;
int frameFinished;
bool bGetFrame = false;
while (!bGetFrame) {
bool dataEnd = av_read_frame(m_pFormatCtx, &packet) < 0;
if (!dataEnd) {
if (packet.pos != -1) {
m_decodedPos = packet.pos;
} else {
// Packet doesn't know where it is in the file, let's try to approximate.
m_decodedPos += packet.size;
}
}
// Even if we've read all frames, some may have been re-ordered frames at the end.
// Still need to decode those, so keep calling avcodec_decode_video2().
if (dataEnd || packet.stream_index == m_videoStream) {
// avcodec_decode_video2() gives us the re-ordered frames with a NULL packet.
if (dataEnd)
av_free_packet(&packet);
int result = avcodec_decode_video2(m_pCodecCtx, m_pFrame, &frameFinished, &packet);
if (frameFinished) {
sws_scale(m_sws_ctx, m_pFrame->data, m_pFrame->linesize, 0,
m_pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize);
s64 firstTimeStamp = getMpegTimeStamp(m_pdata + PSMF_FIRST_TIMESTAMP_OFFSET);
m_videopts = m_pFrame->pkt_dts + av_frame_get_pkt_duration(m_pFrame) - firstTimeStamp;
bGetFrame = true;
}
if (result <= 0 && dataEnd) {
// Sometimes, m_readSize is less than m_streamSize at the end, but not by much.
// This is kinda a hack, but the ringbuffer would have to be prematurely empty too.
m_isVideoEnd = !bGetFrame && m_readSize >= (m_streamSize - 4096);
if (m_isVideoEnd)
m_decodedPos = m_readSize;
break;
}
}
av_free_packet(&packet);
}
return bGetFrame;
#else
return true;
#endif // USE_FFMPEG
}
int MediaEngine::writeVideoImage(u8* buffer, int frameWidth, int videoPixelMode) {
if ((!m_pFrame)||(!m_pFrameRGB))
return false;
#ifdef USE_FFMPEG
int videoImageSize = 0;
// lock the image size
int height = m_desHeight;
int width = m_desWidth;
u8 *imgbuf = buffer;
u8 *data = m_pFrameRGB->data[0];
u16 *imgbuf16 = (u16 *)buffer;
u16 *data16 = (u16 *)data;
switch (videoPixelMode) {
case TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888:
for (int y = 0; y < height; y++) {
memcpy(imgbuf, data, width * sizeof(u32));
data += width * sizeof(u32);
imgbuf += frameWidth * sizeof(u32);
}
videoImageSize = frameWidth * sizeof(u32) * height;
break;
case TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650:
for (int y = 0; y < height; y++) {
memcpy(imgbuf, data, width * sizeof(u16));
data += width * sizeof(u16);
imgbuf += frameWidth * sizeof(u16);
}
videoImageSize = frameWidth * sizeof(u16) * height;
break;
case TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR5551:
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
*imgbuf16++ = *data16++ | (1 << 15);
}
imgbuf16 += (frameWidth - width);
}
videoImageSize = frameWidth * sizeof(u16) * height;
break;
case TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR4444:
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
*imgbuf16++ = *data16++ | (0xF << 12);
}
imgbuf16 += (frameWidth - width);
}
videoImageSize = frameWidth * sizeof(u16) * height;
break;
default:
ERROR_LOG(ME, "Unsupported video pixel format %d", videoPixelMode);
break;
}
return videoImageSize;
#endif // USE_FFMPEG
return true;
}
int MediaEngine::writeVideoImageWithRange(u8* buffer, int frameWidth, int videoPixelMode,
int xpos, int ypos, int width, int height) {
if ((!m_pFrame)||(!m_pFrameRGB))
return false;
#ifdef USE_FFMPEG
int videoImageSize = 0;
// lock the image size
u8 *imgbuf = buffer;
u8 *data = m_pFrameRGB->data[0];
u16 *imgbuf16 = (u16 *)buffer;
u16 *data16 = (u16 *)data;
if (width > m_desWidth - xpos)
width = m_desWidth - xpos;
if (height > m_desHeight - ypos)
height = m_desHeight - ypos;
switch (videoPixelMode) {
case TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888:
data += (ypos * m_desWidth + xpos) * sizeof(u32);
for (int y = 0; y < height; y++) {
memcpy(imgbuf, data, width * sizeof(u32));
data += m_desWidth * sizeof(u32);
imgbuf += frameWidth * sizeof(u32);
}
videoImageSize = frameWidth * sizeof(u32) * m_desHeight;
break;
case TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650:
data += (ypos * m_desWidth + xpos) * sizeof(u16);
for (int y = 0; y < height; y++) {
memcpy(imgbuf, data, width * sizeof(u16));
data += m_desWidth * sizeof(u16);
imgbuf += frameWidth * sizeof(u16);
}
videoImageSize = frameWidth * sizeof(u16) * m_desHeight;
break;
case TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR5551:
data += (ypos * m_desWidth + xpos) * sizeof(u16);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
*imgbuf16++ = *data16++ | (1 << 15);
}
imgbuf16 += (frameWidth - width);
data16 += (m_desWidth - width);
}
videoImageSize = frameWidth * sizeof(u16) * m_desHeight;
break;
case TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR4444:
data += (ypos * m_desWidth + xpos) * sizeof(u16);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
*imgbuf16++ = *data16++ | (0xF << 12);
}
imgbuf16 += (frameWidth - width);
data16 += (m_desWidth - width);
}
videoImageSize = frameWidth * sizeof(u16) * m_desHeight;
break;
default:
ERROR_LOG(ME, "Unsupported video pixel format %d", videoPixelMode);
break;
}
return videoImageSize;
#endif // USE_FFMPEG
return true;
}
static bool isHeader(u8* audioStream, int offset)
{
const u8 header1 = (u8)0x0F;
const u8 header2 = (u8)0xD0;
return (audioStream[offset] == header1) && (audioStream[offset+1] == header2);
}
static int getNextHeaderPosition(u8* audioStream, int curpos, int limit, int frameSize)
{
int endScan = limit - 1;
// Most common case: the header can be found at each frameSize
int offset = curpos + frameSize - 8;
if (offset < endScan && isHeader(audioStream, offset))
return offset;
for (int scan = curpos; scan < endScan; scan++) {
if (isHeader(audioStream, scan))
return scan;
}
return -1;
}
int MediaEngine::getBufferedSize() {
return std::max(0, m_readSize - (int)m_decodedPos);
}
int MediaEngine::getAudioSamples(u8* buffer) {
if (!m_demux) {
return 0;
}
u8* audioStream = 0;
int audioSize = m_demux->getaudioStream(&audioStream);
if (m_audioPos >= audioSize || !isHeader(audioStream, m_audioPos))
{
m_isAudioEnd = m_demux->getFilePosition() >= m_streamSize;
return 0;
}
u8 headerCode1 = audioStream[2];
u8 headerCode2 = audioStream[3];
int frameSize = ((headerCode1 & 0x03) << 8) | (headerCode2 & 0xFF) * 8 + 0x10;
if (m_audioPos + frameSize > audioSize)
return 0;
m_audioPos += 8;
int nextHeader = getNextHeaderPosition(audioStream, m_audioPos, audioSize, frameSize);
u8* frame = audioStream + m_audioPos;
int outbytes = 0;
Atrac3plus_Decoder::Decode(m_audioContext, frame, frameSize - 8, &outbytes, buffer);
if (headerCode1 == 0x24) {
// it a mono atrac3plus, convert it to stereo
s16 *outbuf = (s16*)buffer;
s16 *inbuf = (s16*)buffer;
for (int i = 0x800 - 1; i >= 0; i--) {
s16 sample = inbuf[i];
outbuf[i * 2] = sample;
outbuf[i * 2 + 1] = sample;
}
}
if (nextHeader >= 0) {
m_audioPos = nextHeader;
} else
m_audioPos = audioSize;
m_audiopts += 4180;
m_decodedPos += frameSize;
return outbytes;
}
s64 MediaEngine::getVideoTimeStamp() {
return m_videopts;
}
s64 MediaEngine::getAudioTimeStamp() {
if (m_demux)
return std::max(m_audiopts - 4180, (s64)0);
return m_videopts;
}
s64 MediaEngine::getLastTimeStamp() {
if (!m_pdata)
return 0;
s64 firstTimeStamp = getMpegTimeStamp(m_pdata + PSMF_FIRST_TIMESTAMP_OFFSET);
s64 lastTimeStamp = getMpegTimeStamp(m_pdata + PSMF_LAST_TIMESTAMP_OFFSET);
return lastTimeStamp - firstTimeStamp;
}

View File

@ -28,70 +28,90 @@
#include "../../Globals.h"
#include "../HLE/sceMpeg.h"
#include "ChunkFile.h"
#include "Core/HW/MpegDemux.h"
struct SwsContext;
struct AVFrame;
struct AVIOContext;
struct AVFormatContext;
struct AVCodecContext;
inline s64 getMpegTimeStamp(u8* buf) {
return (s64)buf[5] | ((s64)buf[4] << 8) | ((s64)buf[3] << 16) | ((s64)buf[2] << 24)
| ((s64)buf[1] << 32) | ((s64)buf[0] << 36);
}
class MediaEngine
{
public:
MediaEngine() : fakeMode_(false), readLength_(0), fakeFrameCounter_(0) {}
MediaEngine();
~MediaEngine();
void setFakeMode(bool fake) {
fakeMode_ = fake;
}
void closeMedia();
bool loadStream(u8* buffer, int readSize, int StreamSize);
bool loadFile(const char* filename);
// open the mpeg context
bool openContext();
// Returns number of packets actually added.
int addStreamData(u8* buffer, int addSize);
void init(u32 bufferAddr, u32 mpegStreamSize, u32 mpegOffset) {
bufferAddr_ = bufferAddr;
mpegStreamSize_ = mpegStreamSize;
mpegOffset_ = mpegOffset;
}
void finish();
void setVideoStream(int streamNum) { m_videoStream = streamNum; }
void setAudioStream(int streamNum) { m_audioStream = streamNum; }
void setVideoDim(int w, int h) { videoWidth_ = w; videoHeight_ = h; }
void feedPacketData(u32 addr, int size);
int getRemainSize() { return m_streamSize - m_readSize;}
int getBufferedSize();
bool readVideoAu(SceMpegAu *au) {
if (fakeMode_) {
au->pts += videoTimestampStep;
}
return true;
}
bool readAudioAu(SceMpegAu *au) {
if (fakeMode_) {
bool stepVideo(int videoPixelMode);
int writeVideoImage(u8* buffer, int frameWidth = 512, int videoPixelMode = 3);
int writeVideoImageWithRange(u8* buffer, int frameWidth, int videoPixelMode,
int xpos, int ypos, int width, int height);
int getAudioSamples(u8* buffer);
}
return true;
}
bool setVideoDim(int width = 0, int height = 0);
s64 getVideoTimeStamp();
s64 getAudioTimeStamp();
s64 getLastTimeStamp();
bool stepVideo() {
if (fakeMode_)
return true;
return true;
}
void writeVideoImage(u32 bufferPtr, int frameWidth, int videoPixelMode);
// WTF is this?
int readLength() { return readLength_; }
void setReadLength(int len) { readLength_ = len; }
bool IsVideoEnd() { return m_isVideoEnd; }
bool IsAudioEnd() { return m_isAudioEnd; }
void DoState(PointerWrap &p) {
p.Do(fakeMode_);
p.Do(bufferAddr_);
p.Do(mpegStreamSize_);
p.Do(mpegOffset_);
p.Do(readLength_);
p.Do(videoWidth_);
p.Do(videoHeight_);
p.Do(fakeFrameCounter_);
p.Do(m_streamSize);
p.Do(m_readSize);
p.DoMarker("MediaEngine");
}
private:
bool fakeMode_;
u32 bufferAddr_;
u32 mpegStreamSize_;
u32 mpegOffset_;
int readLength_;
int videoWidth_;
int videoHeight_;
int fakeFrameCounter_;
void updateSwsFormat(int videoPixelMode);
public:
AVFormatContext *m_pFormatCtx;
AVCodecContext *m_pCodecCtx;
AVFrame *m_pFrame;
AVFrame *m_pFrameRGB;
AVIOContext *m_pIOContext;
SwsContext *m_sws_ctx;
int m_sws_fmt;
u8 *m_buffer;
int m_videoStream;
int m_audioStream;
int m_desWidth;
int m_desHeight;
int m_streamSize;
int m_readSize;
int m_decodeNextPos;
s64 m_decodedPos;
int m_bufSize;
s64 m_videopts;
u8* m_pdata;
MpegDemux *m_demux;
int m_audioPos;
void* m_audioContext;
s64 m_audiopts;
bool m_isVideoEnd;
bool m_isAudioEnd;
};

200
Core/HW/MpegDemux.cpp Normal file
View File

@ -0,0 +1,200 @@
#include "MpegDemux.h"
const int PACKET_START_CODE_MASK = 0xffffff00;
const int PACKET_START_CODE_PREFIX = 0x00000100;
// http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html
const int USER_DATA_START_CODE = 0x000001b2;
const int SEQUENCE_START_CODE = 0x000001b3;
const int EXT_START_CODE = 0x000001b5;
const int SEQUENCE_END_CODE = 0x000001b7;
const int GOP_START_CODE = 0x000001b8;
const int ISO_11172_END_CODE = 0x000001b9;
const int PACK_START_CODE = 0x000001ba;
const int SYSTEM_HEADER_START_CODE = 0x000001bb;
const int PROGRAM_STREAM_MAP = 0x000001bc;
const int PRIVATE_STREAM_1 = 0x000001bd;
const int PADDING_STREAM = 0x000001be;
const int PRIVATE_STREAM_2 = 0x000001bf;
MpegDemux::MpegDemux(u8* buffer, int size, int offset)
{
m_buf = buffer;
m_len = size;
m_index = offset;
m_audioStream = 0;
m_audiopos = 0;
m_audioChannel = -1;
m_readSize = 0;
}
MpegDemux::~MpegDemux(void)
{
if (m_audioStream)
delete [] m_audioStream;
}
void MpegDemux::setReadSize(int readSize)
{
m_readSize = readSize;
}
int MpegDemux::readPesHeader(PesHeader &pesHeader, int length, int startCode) {
int c = 0;
while (length > 0) {
c = read8();
length--;
if (c != 0xFF) {
break;
}
}
if ((c & 0xC0) == 0x40) {
read8();
c = read8();
length -= 2;
}
pesHeader.pts = 0;
pesHeader.dts = 0;
if ((c & 0xE0) == 0x20) {
pesHeader.dts = pesHeader.pts = readPts(c);
length -= 4;
if ((c & 0x10) != 0) {
pesHeader.dts = readPts();
length -= 5;
}
} else if ((c & 0xC0) == 0x80) {
int flags = read8();
int headerLength = read8();
length -= 2;
length -= headerLength;
if ((flags & 0x80) != 0) {
pesHeader.dts = pesHeader.pts = readPts();
headerLength -= 5;
if ((flags & 0x40) != 0) {
pesHeader.dts = readPts();
headerLength -= 5;
}
}
if ((flags & 0x3F) != 0 && headerLength == 0) {
flags &= 0xC0;
}
if ((flags & 0x01) != 0) {
int pesExt = read8();
headerLength--;
int skip = (pesExt >> 4) & 0x0B;
skip += skip & 0x09;
if ((pesExt & 0x40) != 0 || skip > headerLength) {
pesExt = skip = 0;
}
this->skip(skip);
headerLength -= skip;
if ((pesExt & 0x01) != 0) {
int ext2Length = read8();
headerLength--;
if ((ext2Length & 0x7F) != 0) {
int idExt = read8();
headerLength--;
if ((idExt & 0x80) == 0) {
startCode = ((startCode & 0xFF) << 8) | idExt;
}
}
}
}
skip(headerLength);
}
if (startCode == PRIVATE_STREAM_1) {
int channel = read8();
pesHeader.channel = channel;
length--;
if (channel >= 0x80 && channel <= 0xCF) {
// Skip audio header
skip(3);
length -= 3;
if (channel >= 0xB0 && channel <= 0xBF) {
skip(1);
length--;
}
} else {
// PSP audio has additional 3 bytes in header
skip(3);
length -= 3;
}
}
return length;
}
int MpegDemux::demuxStream(bool bdemux, int startCode, int channel)
{
int length = read16();
if (bdemux) {
PesHeader pesHeader(channel);
length = readPesHeader(pesHeader, length, startCode);
if (pesHeader.channel == channel || channel < 0) {
channel = pesHeader.channel;
memcpy(m_audioStream + m_audiopos, m_buf + m_index, length);
m_audiopos += length;
}
skip(length);
} else {
skip(length);
}
return channel;
}
void MpegDemux::demux(int audioChannel)
{
if (!m_audioStream)
m_audioStream = new u8[m_len - m_index];
if (audioChannel >= 0)
m_audioChannel = audioChannel;
while (m_index < m_len)
{
if (m_readSize != m_len && m_index + 2048 > m_readSize)
return;
// Search for start code
int startCode = 0xFF;
while ((startCode & PACKET_START_CODE_MASK) != PACKET_START_CODE_PREFIX && !isEOF()) {
startCode = (startCode << 8) | read8();
}
switch (startCode) {
case PACK_START_CODE:
skip(10);
break;
case SYSTEM_HEADER_START_CODE:
skip(14);
break;
case PADDING_STREAM:
case PRIVATE_STREAM_2:
{
int length = read16();
skip(length);
break;
}
case PRIVATE_STREAM_1: {
// Audio stream
m_audioChannel = demuxStream(true, startCode, m_audioChannel);
break;
}
case 0x1E0: case 0x1E1: case 0x1E2: case 0x1E3:
case 0x1E4: case 0x1E5: case 0x1E6: case 0x1E7:
case 0x1E8: case 0x1E9: case 0x1EA: case 0x1EB:
case 0x1EC: case 0x1ED: case 0x1EE: case 0x1EF:
// Video Stream
demuxStream(false, startCode, -1);
break;
case USER_DATA_START_CODE:
// User data, probably same as queried by sceMpegGetUserdataAu.
// Not sure what exactly to do or how much to read.
// TODO: implement properly.
break;
}
}
}
int MpegDemux::getaudioStream(u8** audioStream)
{
*audioStream = m_audioStream;
return m_audiopos;
}

64
Core/HW/MpegDemux.h Normal file
View File

@ -0,0 +1,64 @@
// This is a simple version MpegDemux that can get media's audio stream.
// Thanks to JPCSP project.
#pragma once
#include "../../Globals.h"
class MpegDemux
{
public:
MpegDemux(u8* buffer, int size, int offset);
~MpegDemux(void);
void setReadSize(int readSize);
void demux(int audioChannel);
// return its size
int getaudioStream(u8 **audioStream);
int getFilePosition() { return m_index; }
private:
struct PesHeader {
long pts;
long dts;
int channel;
PesHeader(int chan) {
pts = 0;
dts = 0;
channel = chan;
}
};
int read8() {
return m_buf[m_index++] & 0xFF;
}
int read16() {
return (read8() << 8) | read8();
}
long readPts() {
return readPts(read8());
}
long readPts(int c) {
return (((long) (c & 0x0E)) << 29) | ((read16() >> 1) << 15) | (read16() >> 1);
}
bool isEOF() {
return m_index >= m_len;
}
void skip(int n) {
if (n > 0) {
m_index += n;
}
}
int readPesHeader(PesHeader &pesHeader, int length, int startCode);
int demuxStream(bool bdemux, int startCode, int channel);
private:
int m_index;
int m_len;
u8* m_buf;
u8* m_audioStream;
int m_audiopos;
int m_audioChannel;
int m_readSize;
};

View File

@ -1,5 +1,4 @@
#include "OMAConvert.h"
#include <cstring>
namespace OMAConvert {

View File

@ -18,16 +18,31 @@
#include "base/basictypes.h"
#include "../Globals.h"
#include "../MemMap.h"
#include "Core/HLE/sceAtrac.h"
#include "SasAudio.h"
// #define AUDIO_TO_FILE
static const s8 f[5][2] =
{ { 0, 0 },
{ 60, 0 },
{ 115, -52 },
{ 98, -55 },
{ 122, -60 } };
static const s8 f[16][2] = {
{ 0, 0 },
{ 60, 0 },
{ 115, -52 },
{ 98, -55 },
{ 122, -60 },
// Padding to prevent overflow.
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
};
void VagDecoder::Start(u32 data, int vagSize, bool loopEnabled) {
loopEnabled_ = loopEnabled;
@ -47,11 +62,9 @@ void VagDecoder::DecodeBlock(u8 *&readp) {
int predict_nr = *readp++;
int shift_factor = predict_nr & 0xf;
predict_nr >>= 4;
if (predict_nr >= sizeof(f) / sizeof(f[0])) {
predict_nr = 0;
}
int flags = *readp++;
if (flags == 7) {
VERBOSE_LOG(SAS, "VAG ending block at %d", curBlock_);
end_ = true;
return;
}
@ -66,14 +79,9 @@ void VagDecoder::DecodeBlock(u8 *&readp) {
for (int i = 0; i < 28; i += 2) {
int d = *readp++;
int s = (short)((d & 0xf) << 12);
samples[i] = s >> shift_factor;
DecodeSample(i, s >> shift_factor, predict_nr);
s = (short)((d & 0xf0) << 8);
samples[i + 1] = s >> shift_factor;
}
for (int i = 0; i < 28; i++) {
samples[i] = (int) (samples[i] + ((s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1]) >> 6));
s_2 = s_1;
s_1 = samples[i];
DecodeSample(i + 1, s >> shift_factor, predict_nr);
}
curSample = 0;
curBlock_++;
@ -82,6 +90,12 @@ void VagDecoder::DecodeBlock(u8 *&readp) {
}
}
inline void VagDecoder::DecodeSample(int i, int sample, int predict_nr) {
samples[i] = (int) (sample + ((s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1]) >> 6));
s_2 = s_1;
s_1 = samples[i];
}
void VagDecoder::GetSamples(s16 *outSamples, int numSamples) {
if (end_) {
memset(outSamples, 0, numSamples * sizeof(s16));
@ -90,17 +104,20 @@ void VagDecoder::GetSamples(s16 *outSamples, int numSamples) {
u8 *readp = Memory::GetPointer(read_);
if (!readp)
{
WARN_LOG(HLE, "Bad VAG samples address?");
WARN_LOG(SAS, "Bad VAG samples address?");
return;
}
u8 *origp = readp;
for (int i = 0; i < numSamples; i++) {
if (curSample == 28) {
if (loopAtNextBlock_) {
read_ = data_ + 16 * loopStartBlock_;
VERBOSE_LOG(SAS, "Looping VAG from block %d/%d to %d", curBlock_, numBlocks_, loopStartBlock_);
// data_ starts at curBlock = -1.
read_ = data_ + 16 * loopStartBlock_ + 16;
readp = Memory::GetPointer(read_);
origp = readp;
curBlock_ = loopStartBlock_;
loopAtNextBlock_ = false;
}
DecodeBlock(readp);
if (end_) {
@ -136,6 +153,49 @@ void VagDecoder::DoState(PointerWrap &p)
p.Do(end_);
}
int SasAtrac3::setContext(u32 context) {
contextAddr = context;
atracID = _AtracGetIDByContext(context);
if (!sampleQueue)
sampleQueue = new Atrac3plus_Decoder::BufferQueue;
sampleQueue->clear();
return 0;
}
int SasAtrac3::getNextSamples(s16* outbuf, int wantedSamples) {
if (atracID < 0)
return -1;
u32 finish = 0;
int wantedbytes = wantedSamples * sizeof(s16);
while (!finish && sampleQueue->getQueueSize() < wantedbytes) {
u32 numSamples = 0;
int remains = 0;
static s16 buf[0x800];
_AtracDecodeData(atracID, (u8*)buf, &numSamples, &finish, &remains);
if (numSamples > 0)
sampleQueue->push((u8*)buf, numSamples * sizeof(s16));
else
finish = 1;
}
sampleQueue->pop_front((u8*)outbuf, wantedbytes);
return finish;
}
int SasAtrac3::addStreamData(u8* buf, u32 addbytes) {
if (atracID > 0) {
_AtracAddStreamData(atracID, buf, addbytes);
}
return 0;
}
void SasAtrac3::DoState(PointerWrap &p) {
p.Do(contextAddr);
p.Do(atracID);
if (p.mode == p.MODE_READ && atracID >= 0 && !sampleQueue) {
sampleQueue = new Atrac3plus_Decoder::BufferQueue;
}
}
// http://code.google.com/p/jpcsp/source/browse/trunk/src/jpcsp/HLE/modules150/sceSasCore.java
int simpleRate(int n) {
@ -219,6 +279,9 @@ SasInstance::SasInstance()
#ifdef AUDIO_TO_FILE
audioDump = fopen("D:\\audio.raw", "wb");
#endif
memset(&waveformEffect, 0, sizeof(waveformEffect));
waveformEffect.type = -1;
waveformEffect.isDryOn = 1;
}
SasInstance::~SasInstance() {
@ -324,6 +387,16 @@ void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) {
}
}
break;
case VOICETYPE_ATRAC3:
{
int ret = voice.atrac3.getNextSamples(resampleBuffer + 2, numSamples);
if (ret) {
// Hit atrac3 voice end
voice.playing = false;
voice.on = false; // ??
}
}
break;
default:
{
memset(resampleBuffer + 2, 0, numSamples * sizeof(s16));
@ -344,12 +417,14 @@ void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) {
int sample = resampleBuffer[sampleFrac / PSP_SAS_PITCH_BASE + 2];
sampleFrac += voice.pitch;
// Reduce envelope to 15 bits, rounding down.
// The maximum envelope height (PSP_SAS_ENVELOPE_HEIGHT_MAX) is (1 << 30) - 1.
// Reduce it to 14 bits, by shifting off 15. Round up by adding (1 << 14) first.
int envelopeValue = voice.envelope.GetHeight();
envelopeValue = ((envelopeValue >> 15) + 1) >> 1;
envelopeValue = (envelopeValue + (1 << 14)) >> 15;
// We just scale by the envelope before we scale by volumes.
sample = sample * (envelopeValue + 0x4000) >> 15;
// Again, we round up by adding (1 << 14) first (*after* multiplying.)
sample = ((sample * envelopeValue) + (1 << 14)) >> 15;
// We mix into this 32-bit temp buffer and clip in a second loop
// Ideally, the shift right should be there too but for now I'm concerned about
@ -491,6 +566,7 @@ void SasVoice::DoState(PointerWrap &p)
p.Do(vagSize);
p.Do(pcmAddr);
p.Do(pcmSize);
p.Do(pcmIndex);
p.Do(sampleRate);
p.Do(sampleFrac);
@ -511,6 +587,7 @@ void SasVoice::DoState(PointerWrap &p)
envelope.DoState(p);
vag.DoState(p);
atrac3.DoState(p);
}
// This is horribly stolen from JPCSP.
@ -571,9 +648,15 @@ static int durationFromRate(int rate)
const short expCurveReference = 0x7000;
// This needs a rewrite / rethink. Doing all this per sample is insane.
static int getExpCurveAt(int index, int duration) {
const short curveLength = sizeof(expCurve) / sizeof(short);
if (duration == 0) {
// Avoid division by zero, and thus undefined behaviour in conversion to int.
return 0;
}
float curveIndex = (index * curveLength) / (float) duration;
int curveIndex1 = (int) curveIndex;
int curveIndex2 = curveIndex1 + 1;
@ -595,14 +678,13 @@ ADSREnvelope::ADSREnvelope()
decayRate(0),
decayType(PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE),
sustainRate(0),
sustainType(PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE),
sustainType(PSP_SAS_ADSR_CURVE_MODE_LINEAR_INCREASE),
releaseRate(0),
releaseType(PSP_SAS_ADSR_CURVE_MODE_LINEAR_DECREASE),
sustainLevel(0),
sustainLevel(0x100),
state_(STATE_OFF),
steps_(0),
height_(0) {
memset(this, 0, sizeof(*this));
}
void ADSREnvelope::WalkCurve(int rate, int type) {
@ -664,6 +746,10 @@ void ADSREnvelope::Step() {
break;
case STATE_SUSTAIN:
WalkCurve(sustainRate, sustainType);
if (height_ <= 0) {
height_ = 0;
SetState(STATE_RELEASE);
}
break;
case STATE_RELEASE:
WalkCurve(releaseRate, releaseType);

View File

@ -25,6 +25,8 @@
#include "../Globals.h"
#include "ChunkFile.h"
#include "Core/HW/atrac3plus.h"
enum {
PSP_SAS_VOICES_MAX = 32,
@ -75,7 +77,7 @@ enum VoiceType {
// It compresses 28 16-bit samples into a block of 16 bytes.
class VagDecoder {
public:
VagDecoder() : data_(0), read_(0) {}
VagDecoder() : data_(0), read_(0), end_(true) {}
void Start(u32 dataPtr, int vagSize, bool loopEnabled);
void GetSamples(s16 *outSamples, int numSamples);
@ -86,6 +88,7 @@ public:
void DoState(PointerWrap &p);
private:
void DecodeSample(int i, int sample, int predict_nr);
int samples[28];
int curSample;
@ -104,6 +107,20 @@ private:
bool end_;
};
class SasAtrac3 {
public:
SasAtrac3() : contextAddr(0), atracID(-1), sampleQueue(0){}
~SasAtrac3() { if (sampleQueue) delete sampleQueue; }
int setContext(u32 context);
int getNextSamples(s16* outbuf, int wantedSamples);
int addStreamData(u8* buf, u32 addbytes);
void DoState(PointerWrap &p);
private:
u32 contextAddr;
int atracID;
Atrac3plus_Decoder::BufferQueue *sampleQueue;
};
// Max height: 0x40000000 I think
class ADSREnvelope
{
@ -168,14 +185,14 @@ struct SasVoice
sampleRate(44100),
sampleFrac(0),
pitch(PSP_SAS_PITCH_BASE),
loop(true), // true = ignore VAG loop , false = process VAG loop
loop(false),
noiseFreq(0),
volumeLeft(PSP_SAS_VOL_MAX),
volumeRight(PSP_SAS_VOL_MAX),
volumeLeftSend(0),
volumeRightSend(0),
effectLeft(0),
effectRight(0) {
effectLeft(PSP_SAS_VOL_MAX),
effectRight(PSP_SAS_VOL_MAX) {
memset(resampleHist, 0, sizeof(resampleHist));
}
@ -216,6 +233,7 @@ struct SasVoice
ADSREnvelope envelope;
VagDecoder vag;
SasAtrac3 atrac3;
};
class SasInstance

200
Core/HW/atrac3plus.cpp Normal file
View File

@ -0,0 +1,200 @@
#ifdef _WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#include <errno.h>
#ifdef ANDROID
#include <sys/stat.h>
#endif
#endif // _WIN32
#include <string.h>
#include <string>
#include "base/logging.h"
#include "Core/Config.h"
#include "Common/FileUtil.h"
#include "Core/HW/atrac3plus.h"
extern std::string externalDirectory;
namespace Atrac3plus_Decoder {
#ifdef _WIN32
HMODULE hlib = 0;
#else
static void *so;
#endif // _WIN32
typedef int (* ATRAC3PLUS_DECODEFRAME)(void* context, void* inbuf, int inbytes, int* channels, void** outbuf);
typedef void* (* ATRAC3PLUS_OPENCONTEXT)();
typedef int (* ATRAC3PLUS_CLOSECONTEXT)(void* context);
ATRAC3PLUS_DECODEFRAME frame_decoder = 0;
ATRAC3PLUS_OPENCONTEXT open_context = 0;
ATRAC3PLUS_CLOSECONTEXT close_context = 0;
std::string GetInstalledFilename() {
#if defined(ANDROID) && defined(ARM)
return g_Config.internalDataDirectory + "libat3plusdecoder.so";
#elif defined(_WIN32)
#ifdef _M_X64
return "at3plusdecoder64.dll";
#else
return "at3plusdecoder.dll";
#endif
#else
return "libat3plusdecoder.so";
#endif
}
std::string GetAutoInstallFilename() {
#if ARMEABI_V7A
return g_Config.memCardDirectory + "PSP/libs/armeabi-v7a/libat3plusdecoder.so";
#else
return g_Config.memCardDirectory + "PSP/libs/armeabi/libat3plusdecoder.so";
#endif
}
// Android-only: From SD card. .so files must be in internal memory to load on many devices.
bool CanAutoInstall() {
#if defined(ANDROID) && defined(ARM)
// Android will auto install from SD card
if (File::Exists(GetAutoInstallFilename()))
return true;
#endif
// Other platforms can't.
return false;
}
bool IsInstalled() {
return File::Exists(GetInstalledFilename());
}
bool DoAutoInstall() {
#if defined(ANDROID) && defined(ARM)
std::string internalFilename = g_Config.internalDataDirectory + "libat3plusdecoder.so";
#if ARMEABI_V7A
std::string sdFilename = g_Config.memCardDirectory + "PSP/libs/armeabi-v7a/libat3plusdecoder.so";
#else
std::string sdFilename = g_Config.memCardDirectory + "PSP/libs/armeabi/libat3plusdecoder.so";
#endif
// SD cards are often mounted no-exec.
if (!File::Exists(internalFilename)) {
if (!File::Copy(sdFilename, internalFilename)) {
ELOG("Failed to copy %s to %s", sdFilename.c_str(), internalFilename.c_str());
return false;
}
if (chmod(internalFilename.c_str(), 0777) < 0) {
ELOG("Failed to chmod %s, continuing anyway", internalFilename.c_str());
}
}
#else
ELOG("Autoinstall is for android only");
#endif
return true;
}
int Init() {
if (!g_Config.bEnableAtrac3plus)
return -1;
if (!IsInstalled()) {
// Okay, we're screwed. Let's bail.
return -1;
}
#ifdef _WIN32
#ifdef _M_X64
hlib = LoadLibraryA(GetInstalledFilename().c_str());
#else
hlib = LoadLibraryA(GetInstalledFilename().c_str());
#endif
if (hlib) {
frame_decoder = (ATRAC3PLUS_DECODEFRAME)GetProcAddress(hlib, "Atrac3plusDecoder_decodeFrame");
open_context = (ATRAC3PLUS_OPENCONTEXT)GetProcAddress(hlib, "Atrac3plusDecoder_openContext");
close_context = (ATRAC3PLUS_CLOSECONTEXT)GetProcAddress(hlib, "Atrac3plusDecoder_closeContext");
} else {
return -1;
}
#else
std::string filename = GetInstalledFilename();
ILOG("Attempting to load atrac3plus decoder from %s", filename.c_str());
so = dlopen(filename.c_str(), RTLD_LAZY);
if (so) {
frame_decoder = (ATRAC3PLUS_DECODEFRAME)dlsym(so, "Atrac3plusDecoder_decodeFrame");
open_context = (ATRAC3PLUS_OPENCONTEXT)dlsym(so, "Atrac3plusDecoder_openContext");
close_context = (ATRAC3PLUS_CLOSECONTEXT)dlsym(so, "Atrac3plusDecoder_closeContext");
ILOG("Successfully loaded atrac3plus decoder from %s", filename.c_str());
if (!frame_decoder || !open_context || !close_context) {
ILOG("Found atrac3plus decoder at %s but failed to load functions", filename.c_str());
return -1;
}
} else {
if (errno == ENOEXEC) {
ELOG("Failed to load atrac3plus decoder from %s. errno=%i, dlerror=%s", filename.c_str(), (int)(errno), dlerror());
} else {
ELOG("Failed to load atrac3plus decoder from %s. errno=%i", filename.c_str(), (int)(errno));
}
return -1;
}
#endif
return 0;
}
int Shutdown() {
#ifdef _WIN32
if (hlib) {
FreeLibrary(hlib);
hlib = 0;
}
#else
if (so) {
dlclose(so);
so = 0;
}
#endif // _WIN32
frame_decoder = 0;
open_context = 0;
close_context = 0;
return 0;
}
void* OpenContext() {
if (!open_context)
return 0;
return open_context();
}
int CloseContext(Context *context) {
if (!close_context || !context)
return 0;
close_context(*context);
*context = 0;
return 0;
}
bool Decode(Context context, void* inbuf, int inbytes, int *outbytes, void* outbuf) {
if (!frame_decoder) {
*outbytes = 0;
return false;
}
int channels = 0;
void* buf;
int ret = frame_decoder(context, inbuf, inbytes, &channels, &buf);
if (ret != 0) {
*outbytes = 0;
return false;
}
*outbytes = channels * 2 * 0x800;
memcpy(outbuf, buf, *outbytes);
return true;
}
} // Atrac3plus_Decoder

91
Core/HW/atrac3plus.h Normal file
View File

@ -0,0 +1,91 @@
#ifndef _ATRAC3PLUS_DECODER_
#define _ATRAC3PLUS_DECODER_
namespace Atrac3plus_Decoder {
bool IsInstalled();
bool CanAutoInstall();
bool DoAutoInstall();
std::string GetInstalledFilename();
int Init();
int Shutdown();
typedef void* Context;
Context OpenContext();
int CloseContext(Context *context);
bool Decode(Context context, void* inbuf, int inbytes, int *outbytes, void* outbuf);
struct BufferQueue {
BufferQueue(int size = 0x20000) {
bufQueue = 0;
alloc(size);
}
~BufferQueue() {
if (bufQueue)
delete [] bufQueue;
}
bool alloc(int size) {
if (size < 0)
return false;
if (bufQueue)
delete [] bufQueue;
bufQueue = new unsigned char[size];
start = 0;
end = 0;
bufQueueSize = size;
return true;
}
void clear() {
start = 0;
end = 0;
}
inline int getQueueSize() {
return (end + bufQueueSize - start) % bufQueueSize;
}
bool push(unsigned char *buf, int addsize) {
int queuesz = getQueueSize();
int space = bufQueueSize - queuesz;
if (space < addsize || addsize < 0)
return false;
if (end + addsize <= bufQueueSize) {
memcpy(bufQueue + end, buf, addsize);
} else {
int size = bufQueueSize - end;
memcpy(bufQueue + end, buf, size);
memcpy(bufQueue, buf + size, addsize - size);
}
end = (end + addsize) % bufQueueSize;
return true;
}
int pop_front(unsigned char *buf, int wantedsize) {
if (wantedsize <= 0)
return 0;
int bytesgot = getQueueSize();
if (wantedsize < bytesgot)
bytesgot = wantedsize;
if (start + bytesgot <= bufQueueSize) {
memcpy(buf, bufQueue + start, bytesgot);
} else {
int size = bufQueueSize - start;
memcpy(buf, bufQueue + start, size);
memcpy(buf + size, bufQueue, bytesgot - size);
}
start = (start + bytesgot) % bufQueueSize;
return bytesgot;
}
unsigned char* bufQueue;
int start, end;
int bufQueueSize;
};
}
#endif // _ATRAC3PLUS_DECODER_

View File

@ -1,594 +0,0 @@
#ifdef _USE_DSHOW_
#include "audioPlayer.h"
#include "Core/Config.h"
#include <dshow.h>
#include "qeditsimple.h"
#pragma comment(lib, "Strmiids.lib")
#pragma comment(lib, "Quartz.lib")
#include "OMAConvert.h"
#include <map>
#include "StdMutex.h"
#include "../Core/System.h"
#define JIF(x) if (FAILED(hr=(x))) \
{return false;}
#define KIF(x) if (FAILED(hr=(x))) \
{return hr;}
#define LIF(x) if (FAILED(hr=(x))) \
{}
// Add filter from a dll/ax library directly
#include <comdef.h>
typedef HRESULT (STDAPICALLTYPE* FN_DLLGETCLASSOBJECT)(REFCLSID clsid, REFIID iid, void** ppv);
static const int importlibnum = 1;
static HMODULE lib[importlibnum] = {0};
static FN_DLLGETCLASSOBJECT fn[importlibnum] = {0};
HRESULT LoadFilterLibrary(int index, const char *pPath)
{
if (index < 0 || index >= importlibnum)
return E_FAIL;
// load the target DLL directly
lib[index] = LoadLibrary(pPath);
if (!lib[index])
{
return HRESULT_FROM_WIN32(GetLastError());
}
// the entry point is an exported function
fn[index] = (FN_DLLGETCLASSOBJECT)GetProcAddress(lib[index], "DllGetClassObject");
if (fn[index] == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
HRESULT FreeFilterLibrary(int index)
{
if (index < 0 || index >= importlibnum)
return E_FAIL;
if (lib[index])
FreeLibrary(lib[index]);
lib[index] = 0;
return S_OK;
}
HRESULT CreateFilterFromLibrary(int index, REFCLSID clsid, REFIID iid, void** ppUnk)
{
if (index < 0 || index >= importlibnum)
return E_FAIL;
if (!fn[index])
return E_FAIL;
// create a class factory
IUnknownPtr pUnk;
HRESULT hr = fn[index](clsid, IID_IUnknown, (void**)&pUnk);
if (SUCCEEDED(hr))
{
IClassFactoryPtr pCF = pUnk;
if (pCF == NULL)
{
hr = E_NOINTERFACE;
}
else
{
// ask the class factory to create the object
hr = pCF->CreateInstance(NULL, iid, (void**)ppUnk);
}
}
return hr;
}
// {2AE44C10-B451-4B01-9BBE-A5FBEF68C9D4}
static const GUID CLSID_AsyncStreamSource =
{ 0x2ae44c10, 0xb451, 0x4b01, { 0x9b, 0xbe, 0xa5, 0xfb, 0xef, 0x68, 0xc9, 0xd4 } };
// {268424D1-B6E9-4B28-8751-B7774F5ECF77}
static const GUID IID_IStreamSourceFilter =
{ 0x268424d1, 0xb6e9, 0x4b28, { 0x87, 0x51, 0xb7, 0x77, 0x4f, 0x5e, 0xcf, 0x77 } };
// We define the interface the app can use to program us
MIDL_INTERFACE("268424D1-B6E9-4B28-8751-B7774F5ECF77")
IStreamSourceFilter : public IFileSourceFilter
{
public:
virtual STDMETHODIMP LoadStream(void *stream, LONGLONG readSize, LONGLONG streamSize, AM_MEDIA_TYPE *pmt ) = 0;
virtual STDMETHODIMP AddStreamData(LONGLONG offset, void *stream, LONGLONG addSize) = 0;
virtual STDMETHODIMP GetStreamInfo(LONGLONG *readSize, LONGLONG *streamSize) = 0;
virtual STDMETHODIMP SetReadSize(LONGLONG readSize) = 0;
virtual BOOL STDMETHODCALLTYPE IsReadPassEnd() = 0;
};
HRESULT GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)
{
IEnumPins *pEnum;
IPin *pPin;
pFilter->EnumPins(&pEnum);
while(pEnum->Next(1, &pPin, 0) == S_OK)
{
PIN_DIRECTION PinDirThis;
pPin->QueryDirection(&PinDirThis);
if (PinDir == PinDirThis)
{
pEnum->Release();
*ppPin = pPin;
return S_OK;
}
pPin->Release();
}
pEnum->Release();
return E_FAIL;
}
HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pFirst, IBaseFilter *pSecond)
{
IPin *pOut = NULL, *pIn = NULL;
HRESULT hr = GetPin(pFirst, PINDIR_OUTPUT, &pOut);
if (FAILED(hr)) return hr;
hr = GetPin(pSecond, PINDIR_INPUT, &pIn);
if (FAILED(hr))
{
pOut->Release();
return E_FAIL;
}
hr = pGraph->Connect(pOut, pIn);
pIn->Release();
pOut->Release();
return hr;
}
class CSampleGrabberCallback : public ISampleGrabberCB
{
public:
CSampleGrabberCallback(audioPlayer *player)
{
m_player = player;
// 1MB round buffer size
m_bufsize = 0x100000;
m_buf = new u8[m_bufsize];
isNeedReset = true;
m_readPos = m_writePos = 0;
}
~CSampleGrabberCallback(){ if (m_buf) delete [] m_buf; }
STDMETHODIMP_(ULONG) AddRef() { return 2; }
STDMETHODIMP_(ULONG) Release() { return 1; }
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
{
return S_OK;
}
STDMETHODIMP SampleCB(double Time, IMediaSample *pSample)
{
return S_OK;
}
STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long buflen)
{
m_mutex.lock();
if (isNeedReset) {
isNeedReset = false;
m_readPos = 0;
m_writePos = 0;
}
if (m_writePos + buflen <= m_bufsize) {
memcpy(m_buf + m_writePos, pBuffer, buflen);
} else {
int size = m_bufsize - m_writePos;
memcpy(m_buf + m_writePos, pBuffer, size);
memcpy(m_buf, pBuffer + size, buflen - size);
}
m_writePos = (m_writePos + buflen) % m_bufsize;
// check how much space left to write
int space = (m_readPos - m_writePos + m_bufsize) % m_bufsize;
m_mutex.unlock();
if (space < buflen * 3) {
m_player->pause();
}
return S_OK;
}
int getNextSamples(u8* buf, int wantedbufsize) {
int timecount = 0;
while (isNeedReset) {
Sleep(1);
timecount++;
if (timecount >= 10)
return 0;
}
m_mutex.lock();
// check how much space left to read
int space = (m_writePos - m_readPos + m_bufsize) % m_bufsize;
if (m_readPos + wantedbufsize <= m_bufsize) {
memcpy(buf, m_buf + m_readPos, wantedbufsize);
} else {
int size = m_bufsize - m_readPos;
memcpy(buf, m_buf + m_readPos, size);
memcpy(buf + size, m_buf, wantedbufsize - size);
}
int bytesgot = min(wantedbufsize, space);
m_readPos = (m_readPos + bytesgot) % m_bufsize;
// check how much space left to read
space = (m_writePos - m_readPos + m_bufsize) % m_bufsize;
m_mutex.unlock();
if (space < wantedbufsize * 3) {
m_player->play();
}
return bytesgot;
}
void setResetflag(bool reset) { isNeedReset = reset; }
private:
audioPlayer *m_player;
u8* m_buf;
int m_bufsize;
int m_readPos;
int m_writePos;
bool isNeedReset;
std::recursive_mutex m_mutex;
};
bool addSampleGrabber(IGraphBuilder *pGB, IBaseFilter *pSrc,
ISampleGrabberCB *callback, void **outgrabber)
{
HRESULT hr;
ISampleGrabber *pGrabber = 0;
IBaseFilter *pGrabberF = 0;
JIF(CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (void **)&pGrabberF));
JIF(pGB->AddFilter(pGrabberF, L"Sample Grabber"));
JIF(pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber));
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_PCM;
JIF(pGrabber->SetMediaType(&mt));
JIF(ConnectFilters(pGB, pSrc, pGrabberF));
pGrabberF->Release();
IBaseFilter *pNull = NULL;
JIF(CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (void**)&pNull));
JIF(pGB->AddFilter(pNull, L"NullRenderer"));
JIF(ConnectFilters(pGB, pGrabberF, pNull));
// Set one-shot mode and buffering.
JIF(pGrabber->SetOneShot(FALSE));
JIF(pGrabber->SetBufferSamples(TRUE));
JIF(pGrabber->SetCallback(callback, 1));
// close the clock to run as fast as possible
IMediaFilter *pMediaFilter = 0;
pGB->QueryInterface(IID_IMediaFilter, (void**)&pMediaFilter);
pMediaFilter->SetSyncSource(0);
pMediaFilter->Release();
*outgrabber = (void*)pGrabber;
return true;
}
static volatile int g_volume = 60;
audioPlayer::audioPlayer(void)
{
m_playmode = -1;
m_volume = g_volume;
m_pMC = 0;
m_pGB = 0;
m_pMS = 0;
m_pGrabber = 0;
m_pGrabberCB = 0;
m_pStreamReader = 0;
}
audioPlayer::~audioPlayer(void)
{
closeMedia();
}
bool audioPlayer::load(const char* filename, u8* stream, int readSize, int streamSize, bool samplebuffermode, bool isWave)
{
if (m_playmode == 1)
return false;
WCHAR wstrfilename[MAX_PATH + 1];
MultiByteToWideChar( CP_ACP, 0, filename ? filename : "stream", -1,
wstrfilename, MAX_PATH );
wstrfilename[MAX_PATH] = 0;
HRESULT hr;
JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&m_pGB));
IGraphBuilder *pGB=(IGraphBuilder*)m_pGB;
JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC));
JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&m_pMS));
IBaseFilter *pSrc = 0;
JIF(CreateFilterFromLibrary(0, CLSID_AsyncStreamSource, IID_IBaseFilter, (void**)&pSrc));
JIF(pGB->AddFilter(pSrc,wstrfilename));
JIF(pSrc->QueryInterface(IID_IStreamSourceFilter,(void**)&m_pStreamReader));
IStreamSourceFilter* pStreamReader = (IStreamSourceFilter*)m_pStreamReader;
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Stream;
mt.subtype = isWave ? MEDIASUBTYPE_WAVE : MEDIASUBTYPE_NULL;
if (filename) {
JIF(pStreamReader->Load(wstrfilename, &mt));
} else {
JIF(pStreamReader->LoadStream(stream, readSize, streamSize, &mt));
}
if (samplebuffermode) {
m_pGrabberCB = new CSampleGrabberCallback(this);
addSampleGrabber(pGB, pSrc, (ISampleGrabberCB*)m_pGrabberCB, &m_pGrabber);
pSrc->Release();
m_playmode = 0;
play();
} else {
IPin *pOut;
JIF(GetPin(pSrc, PINDIR_OUTPUT, &pOut));
pSrc->Release();
JIF(pGB->Render(pOut));
pOut->Release();
setVolume(m_volume);
m_playmode = 0;
}
IMediaSeeking *pMS=(IMediaSeeking*)m_pMS;
m_startpos = 0;
JIF(pMS->GetStopPosition(&m_endpos));
return true;
}
bool audioPlayer::play()
{
if ((!m_pMC) || (m_playmode == -1))
return false;
IMediaControl *pMC = (IMediaControl*)m_pMC;
HRESULT hr;
JIF(pMC->Run());
m_playmode = 1;
return true;
}
bool audioPlayer::pause()
{
if ((!m_pMC) || (m_playmode == -1))
return false;
IMediaControl *pMC = (IMediaControl*)m_pMC;
HRESULT hr;
JIF(pMC->Pause());
m_playmode = 2;
return true;
}
bool audioPlayer::stop()
{
if ((!m_pMC) || (m_playmode <= 0))
return true;
IMediaControl *pMC = (IMediaControl*)m_pMC;
HRESULT hr;
JIF(pMC->Stop());
m_playmode = 0;
return true;
}
bool audioPlayer::closeMedia()
{
if (m_pGrabber) {
ISampleGrabber *pGrabber = (ISampleGrabber *)m_pGrabber;
pGrabber->SetCallback(0, 1);
pGrabber->Release();
}
if (m_pGrabberCB)
delete ((CSampleGrabberCallback*)m_pGrabberCB);
m_pGrabber = 0;
m_pGrabberCB = 0;
stop();
if (m_pMS)
((IMediaSeeking*)m_pMS)->Release();
if (m_pMC)
((IMediaControl*)m_pMC)->Release();
if (m_pStreamReader)
((IStreamSourceFilter*)m_pStreamReader)->Release();
if (m_pGB)
((IGraphBuilder*)m_pGB)->Release();
m_pMS = 0;
m_pMC = 0;
m_pStreamReader = 0;
m_pGB = 0;
m_playmode = -1;
return true;
}
bool audioPlayer::setVolume(int volume)
{
if ((volume < 0) || (volume > 100))
return false;
m_volume = volume;
if (!m_pGB)
return true;
IBasicAudio *pBA = NULL;
HRESULT hr;
int now = -(int)(exp(log((double)10001)/100*(100-volume))-1+0.5);
JIF(((IGraphBuilder*)m_pGB)->QueryInterface(IID_IBasicAudio, (void**)&pBA));
pBA->put_Volume(now);
pBA->Release();
return true;
}
bool audioPlayer::isEnd(long *mstimetoend)
{
if (!m_pMS)
return false;
IMediaSeeking *pMS=(IMediaSeeking*)m_pMS;
LONGLONG curpos;
HRESULT hr;
JIF(pMS->GetCurrentPosition(&curpos));
if (curpos >= m_endpos)
return true;
if (mstimetoend)
*mstimetoend = (m_endpos - curpos) / 10000;
return false;
}
bool audioPlayer::setPlayPos(long ms)
{
if (!m_pGB)
return false;
HRESULT hr;
IMediaSeeking *pMS = (IMediaSeeking*)m_pMS;
LONGLONG pos = ((LONGLONG)ms)*10000;
if (!m_pGrabberCB) {
JIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning,
NULL, AM_SEEKING_NoPositioning));
} else {
pause();
JIF(pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning,
NULL, AM_SEEKING_NoPositioning));
((CSampleGrabberCallback*)m_pGrabberCB)->setResetflag(true);
play();
}
return true;
}
bool audioPlayer::getPlayPos(long *ms)
{
if (!m_pGB)
return false;
HRESULT hr;
IMediaSeeking *pMS = (IMediaSeeking*)m_pMS;
LONGLONG curpos;
JIF(pMS->GetCurrentPosition(&curpos));
if (ms)
*ms = curpos / 10000;
return true;
}
int audioPlayer::getNextSamples(u8* buf, int wantedbufsize)
{
if (!m_pGrabberCB || !m_pMC) {
memset(buf, 0, wantedbufsize);
return wantedbufsize;
}
return ((CSampleGrabberCallback*)m_pGrabberCB)->getNextSamples(buf, wantedbufsize);
}
////////////////////////////////////////////////////////////////////////
// audioEngine
bool audioEngine::loadRIFFStream(u8* stream, int streamsize, int atracID)
{
u8 *oma = 0;
m_ID = atracID;
m_channel = OMAConvert::getRIFFChannels(stream, streamsize);
bool bResult = false;
if (m_channel != 1) {
int readsize = 0;
int omasize = OMAConvert::convertRIFFtoOMA(stream, streamsize, &oma, &readsize);
if (omasize > 0){
bResult = load(0, oma, readsize, omasize, true);
OMAConvert::releaseStream(&oma);
}
}
return bResult;
}
bool audioEngine::closeStream()
{
bool bResult = closeMedia();
m_ID = -1;
return bResult;
}
bool audioEngine::setPlaySample(int sample)
{
return setPlayPos(((s64)sample) * 1000 / 44100);
}
void audioEngine::addStreamData(int offset, u8* buf, int size, int cursample)
{
if ((!m_pGB) || (m_channel == 1))
return;
IStreamSourceFilter* pStreamReader = (IStreamSourceFilter*)m_pStreamReader;
pStreamReader->AddStreamData(offset, buf, size);
bool bsetpos = pStreamReader->IsReadPassEnd();
if (bsetpos)
setPlaySample(cursample);
}
//////////////////////////////////////////////////////////////////////////////
//
std::map<int, audioEngine*> audioMap;
std::recursive_mutex atracsection;
void addAtrac3Audio(u8* stream, int streamsize, int atracID)
{
if (audioMap.find(atracID) != audioMap.end())
return;
audioEngine *temp = new audioEngine();
bool bResult = temp->loadRIFFStream(stream, streamsize, atracID);
atracsection.lock();
audioMap[atracID] = temp;
atracsection.unlock();
if (!bResult)
temp->closeMedia();
}
audioEngine* getaudioEngineByID(int atracID)
{
if (audioMap.find(atracID) == audioMap.end()) {
return NULL;
}
return audioMap[atracID];
}
void deleteAtrac3Audio(int atracID)
{
atracsection.lock();
if (audioMap.find(atracID) != audioMap.end()) {
delete audioMap[atracID];
audioMap.erase(atracID);
}
atracsection.unlock();
}
void initaudioEngine()
{
if (g_Config.bAutoLoadDShow) {
if (LoadFilterLibrary(0, "filter\\lib\\AsyncStreamflt.ax") != S_OK)
{
WARN_LOG(HLE, "Can't load AsyncStreamflt.ax");
}
}
CoInitialize(0);
}
void shutdownEngine()
{
atracsection.lock();
for (auto it = audioMap.begin(); it != audioMap.end(); ++it) {
delete it->second;
}
audioMap.clear();
atracsection.unlock();
CoUninitialize();
FreeFilterLibrary(0);
}
#endif // _USE_DSHOW_

View File

@ -1,60 +0,0 @@
#pragma once
#ifdef _USE_DSHOW_
#include "../../Globals.h"
class audioPlayer
{
public:
audioPlayer(void);
~audioPlayer(void);
// if samplebuffermode is true, it would provide sample buffers instead play sounds
// if filename is set, then load a file, otherwise load from stream
bool load(const char* filename, u8* stream = 0, int readSize = 0, int streamSize = 0,
bool samplebuffermode = false, bool isWave = false);
bool play();
bool pause();
bool stop();
bool closeMedia();
bool setVolume(int volume);
bool isEnd(long *mstimetoend = 0);
bool setPlayPos(long ms);
bool getPlayPos(long *ms);
int getNextSamples(u8* buf, int wantedbufsize);
protected:
void *m_pGB;
void *m_pMC;
void *m_pMS;
void *m_pStreamReader;
void *m_pGrabber;
void *m_pGrabberCB;
// 0 for stop, 1 for playing, 2 for pause, -1 for not loaded files
int m_playmode;
int m_volume;
protected:
s64 m_startpos;
s64 m_endpos;
};
class audioEngine: public audioPlayer{
public:
audioEngine(void):audioPlayer(), m_ID(-1){}
~audioEngine(void){ closeStream();}
bool loadRIFFStream(u8* stream, int streamsize, int atracID);
bool closeStream();
bool setPlaySample(int sample);
void addStreamData(int offset, u8* buf, int size, int cursample);
private:
int m_ID;
int m_channel;
};
void addAtrac3Audio(u8* stream, int streamsize, int atracID);
audioEngine* getaudioEngineByID(int atracID);
void deleteAtrac3Audio(int atracID);
void initaudioEngine();
void shutdownEngine();
#endif // _USE_DSHOW_

View File

@ -1,381 +0,0 @@
// it's a simple mirror for directshow qedit.h
#ifndef __QEDIT_SIMPLE_H__
#define __QEDIT_SIMPLE_H__
#ifndef __ISampleGrabberCB_FWD_DEFINED__
#define __ISampleGrabberCB_FWD_DEFINED__
typedef interface ISampleGrabberCB ISampleGrabberCB;
#endif /* __ISampleGrabberCB_FWD_DEFINED__ */
#ifndef __ISampleGrabber_FWD_DEFINED__
#define __ISampleGrabber_FWD_DEFINED__
typedef interface ISampleGrabber ISampleGrabber;
#endif /* __ISampleGrabber_FWD_DEFINED__ */
EXTERN_C const CLSID CLSID_SampleGrabber;
#ifdef __cplusplus
class DECLSPEC_UUID("C1F400A0-3F08-11d3-9F0B-006008039E37")
SampleGrabber;
#endif
EXTERN_C const CLSID CLSID_NullRenderer;
#ifdef __cplusplus
class DECLSPEC_UUID("C1F400A4-3F08-11d3-9F0B-006008039E37")
NullRenderer;
#endif
#ifndef __ISampleGrabberCB_INTERFACE_DEFINED__
#define __ISampleGrabberCB_INTERFACE_DEFINED__
/* interface ISampleGrabberCB */
/* [unique][helpstring][local][uuid][object] */
EXTERN_C const IID IID_ISampleGrabberCB;
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("0579154A-2B53-4994-B0D0-E773148EFF85")
ISampleGrabberCB : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE SampleCB(
double SampleTime,
IMediaSample *pSample) = 0;
virtual HRESULT STDMETHODCALLTYPE BufferCB(
double SampleTime,
BYTE *pBuffer,
long BufferLen) = 0;
};
#else /* C style interface */
typedef struct ISampleGrabberCBVtbl
{
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
ISampleGrabberCB * This,
/* [in] */ REFIID riid,
/* [iid_is][out] */ void **ppvObject);
ULONG ( STDMETHODCALLTYPE *AddRef )(
ISampleGrabberCB * This);
ULONG ( STDMETHODCALLTYPE *Release )(
ISampleGrabberCB * This);
HRESULT ( STDMETHODCALLTYPE *SampleCB )(
ISampleGrabberCB * This,
double SampleTime,
IMediaSample *pSample);
HRESULT ( STDMETHODCALLTYPE *BufferCB )(
ISampleGrabberCB * This,
double SampleTime,
BYTE *pBuffer,
long BufferLen);
END_INTERFACE
} ISampleGrabberCBVtbl;
interface ISampleGrabberCB
{
CONST_VTBL struct ISampleGrabberCBVtbl *lpVtbl;
};
#ifdef COBJMACROS
#define ISampleGrabberCB_QueryInterface(This,riid,ppvObject) \
(This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define ISampleGrabberCB_AddRef(This) \
(This)->lpVtbl -> AddRef(This)
#define ISampleGrabberCB_Release(This) \
(This)->lpVtbl -> Release(This)
#define ISampleGrabberCB_SampleCB(This,SampleTime,pSample) \
(This)->lpVtbl -> SampleCB(This,SampleTime,pSample)
#define ISampleGrabberCB_BufferCB(This,SampleTime,pBuffer,BufferLen) \
(This)->lpVtbl -> BufferCB(This,SampleTime,pBuffer,BufferLen)
#endif /* COBJMACROS */
#endif /* C style interface */
HRESULT STDMETHODCALLTYPE ISampleGrabberCB_SampleCB_Proxy(
ISampleGrabberCB * This,
double SampleTime,
IMediaSample *pSample);
void __RPC_STUB ISampleGrabberCB_SampleCB_Stub(
IRpcStubBuffer *This,
IRpcChannelBuffer *_pRpcChannelBuffer,
PRPC_MESSAGE _pRpcMessage,
DWORD *_pdwStubPhase);
HRESULT STDMETHODCALLTYPE ISampleGrabberCB_BufferCB_Proxy(
ISampleGrabberCB * This,
double SampleTime,
BYTE *pBuffer,
long BufferLen);
void __RPC_STUB ISampleGrabberCB_BufferCB_Stub(
IRpcStubBuffer *This,
IRpcChannelBuffer *_pRpcChannelBuffer,
PRPC_MESSAGE _pRpcMessage,
DWORD *_pdwStubPhase);
#endif /* __ISampleGrabberCB_INTERFACE_DEFINED__ */
#ifndef __ISampleGrabber_INTERFACE_DEFINED__
#define __ISampleGrabber_INTERFACE_DEFINED__
/* interface ISampleGrabber */
/* [unique][helpstring][local][uuid][object] */
EXTERN_C const IID IID_ISampleGrabber;
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("6B652FFF-11FE-4fce-92AD-0266B5D7C78F")
ISampleGrabber : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE SetOneShot(
BOOL OneShot) = 0;
virtual HRESULT STDMETHODCALLTYPE SetMediaType(
const AM_MEDIA_TYPE *pType) = 0;
virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType(
AM_MEDIA_TYPE *pType) = 0;
virtual HRESULT STDMETHODCALLTYPE SetBufferSamples(
BOOL BufferThem) = 0;
virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer(
/* [out][in] */ long *pBufferSize,
/* [out] */ long *pBuffer) = 0;
virtual HRESULT STDMETHODCALLTYPE GetCurrentSample(
/* [retval][out] */ IMediaSample **ppSample) = 0;
virtual HRESULT STDMETHODCALLTYPE SetCallback(
ISampleGrabberCB *pCallback,
long WhichMethodToCallback) = 0;
};
#else /* C style interface */
typedef struct ISampleGrabberVtbl
{
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
ISampleGrabber * This,
/* [in] */ REFIID riid,
/* [iid_is][out] */ void **ppvObject);
ULONG ( STDMETHODCALLTYPE *AddRef )(
ISampleGrabber * This);
ULONG ( STDMETHODCALLTYPE *Release )(
ISampleGrabber * This);
HRESULT ( STDMETHODCALLTYPE *SetOneShot )(
ISampleGrabber * This,
BOOL OneShot);
HRESULT ( STDMETHODCALLTYPE *SetMediaType )(
ISampleGrabber * This,
const AM_MEDIA_TYPE *pType);
HRESULT ( STDMETHODCALLTYPE *GetConnectedMediaType )(
ISampleGrabber * This,
AM_MEDIA_TYPE *pType);
HRESULT ( STDMETHODCALLTYPE *SetBufferSamples )(
ISampleGrabber * This,
BOOL BufferThem);
HRESULT ( STDMETHODCALLTYPE *GetCurrentBuffer )(
ISampleGrabber * This,
/* [out][in] */ long *pBufferSize,
/* [out] */ long *pBuffer);
HRESULT ( STDMETHODCALLTYPE *GetCurrentSample )(
ISampleGrabber * This,
/* [retval][out] */ IMediaSample **ppSample);
HRESULT ( STDMETHODCALLTYPE *SetCallback )(
ISampleGrabber * This,
ISampleGrabberCB *pCallback,
long WhichMethodToCallback);
END_INTERFACE
} ISampleGrabberVtbl;
interface ISampleGrabber
{
CONST_VTBL struct ISampleGrabberVtbl *lpVtbl;
};
#ifdef COBJMACROS
#define ISampleGrabber_QueryInterface(This,riid,ppvObject) \
(This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
#define ISampleGrabber_AddRef(This) \
(This)->lpVtbl -> AddRef(This)
#define ISampleGrabber_Release(This) \
(This)->lpVtbl -> Release(This)
#define ISampleGrabber_SetOneShot(This,OneShot) \
(This)->lpVtbl -> SetOneShot(This,OneShot)
#define ISampleGrabber_SetMediaType(This,pType) \
(This)->lpVtbl -> SetMediaType(This,pType)
#define ISampleGrabber_GetConnectedMediaType(This,pType) \
(This)->lpVtbl -> GetConnectedMediaType(This,pType)
#define ISampleGrabber_SetBufferSamples(This,BufferThem) \
(This)->lpVtbl -> SetBufferSamples(This,BufferThem)
#define ISampleGrabber_GetCurrentBuffer(This,pBufferSize,pBuffer) \
(This)->lpVtbl -> GetCurrentBuffer(This,pBufferSize,pBuffer)
#define ISampleGrabber_GetCurrentSample(This,ppSample) \
(This)->lpVtbl -> GetCurrentSample(This,ppSample)
#define ISampleGrabber_SetCallback(This,pCallback,WhichMethodToCallback) \
(This)->lpVtbl -> SetCallback(This,pCallback,WhichMethodToCallback)
#endif /* COBJMACROS */
#endif /* C style interface */
HRESULT STDMETHODCALLTYPE ISampleGrabber_SetOneShot_Proxy(
ISampleGrabber * This,
BOOL OneShot);
void __RPC_STUB ISampleGrabber_SetOneShot_Stub(
IRpcStubBuffer *This,
IRpcChannelBuffer *_pRpcChannelBuffer,
PRPC_MESSAGE _pRpcMessage,
DWORD *_pdwStubPhase);
HRESULT STDMETHODCALLTYPE ISampleGrabber_SetMediaType_Proxy(
ISampleGrabber * This,
const AM_MEDIA_TYPE *pType);
void __RPC_STUB ISampleGrabber_SetMediaType_Stub(
IRpcStubBuffer *This,
IRpcChannelBuffer *_pRpcChannelBuffer,
PRPC_MESSAGE _pRpcMessage,
DWORD *_pdwStubPhase);
HRESULT STDMETHODCALLTYPE ISampleGrabber_GetConnectedMediaType_Proxy(
ISampleGrabber * This,
AM_MEDIA_TYPE *pType);
void __RPC_STUB ISampleGrabber_GetConnectedMediaType_Stub(
IRpcStubBuffer *This,
IRpcChannelBuffer *_pRpcChannelBuffer,
PRPC_MESSAGE _pRpcMessage,
DWORD *_pdwStubPhase);
HRESULT STDMETHODCALLTYPE ISampleGrabber_SetBufferSamples_Proxy(
ISampleGrabber * This,
BOOL BufferThem);
void __RPC_STUB ISampleGrabber_SetBufferSamples_Stub(
IRpcStubBuffer *This,
IRpcChannelBuffer *_pRpcChannelBuffer,
PRPC_MESSAGE _pRpcMessage,
DWORD *_pdwStubPhase);
HRESULT STDMETHODCALLTYPE ISampleGrabber_GetCurrentBuffer_Proxy(
ISampleGrabber * This,
/* [out][in] */ long *pBufferSize,
/* [out] */ long *pBuffer);
void __RPC_STUB ISampleGrabber_GetCurrentBuffer_Stub(
IRpcStubBuffer *This,
IRpcChannelBuffer *_pRpcChannelBuffer,
PRPC_MESSAGE _pRpcMessage,
DWORD *_pdwStubPhase);
HRESULT STDMETHODCALLTYPE ISampleGrabber_GetCurrentSample_Proxy(
ISampleGrabber * This,
/* [retval][out] */ IMediaSample **ppSample);
void __RPC_STUB ISampleGrabber_GetCurrentSample_Stub(
IRpcStubBuffer *This,
IRpcChannelBuffer *_pRpcChannelBuffer,
PRPC_MESSAGE _pRpcMessage,
DWORD *_pdwStubPhase);
HRESULT STDMETHODCALLTYPE ISampleGrabber_SetCallback_Proxy(
ISampleGrabber * This,
ISampleGrabberCB *pCallback,
long WhichMethodToCallback);
void __RPC_STUB ISampleGrabber_SetCallback_Stub(
IRpcStubBuffer *This,
IRpcChannelBuffer *_pRpcChannelBuffer,
PRPC_MESSAGE _pRpcMessage,
DWORD *_pdwStubPhase);
#endif /* __ISampleGrabber_INTERFACE_DEFINED__ */
#endif // __QEDIT_SIMPLE_H__

View File

@ -17,8 +17,6 @@
#pragma once
#include <cstring>
#include <string>
#include "../Globals.h"
struct InputState;

View File

@ -27,39 +27,68 @@
#include "System.h"
// TODO : improve, look in the file more
EmuFileType Identify_File(const char *filename)
EmuFileType Identify_File(std::string &filename)
{
//then: easy bulletproof IDs.
FILE *f = fopen(filename, "rb");
if (!f)
{
//File does not exists
if (filename.size() < 5) {
ERROR_LOG(LOADER, "invalid filename %s", filename.c_str());
return FILETYPE_ERROR;
}
u32 id;
u32 psar_offset, psar_id;
size_t readSize = fread(&id,4,1,f);
if(readSize != 1)
std::string extension = filename.substr(filename.size() - 4);
if (!strcasecmp(extension.c_str(),".iso"))
{
return FILETYPE_PSP_ISO;
}
else if (!strcasecmp(extension.c_str(),".cso"))
{
return FILETYPE_PSP_ISO;
}
// First, check if it's a directory with an EBOOT.PBP in it.
FileInfo info;
if (!getFileInfo(filename.c_str(), &info)) {
return FILETYPE_ERROR;
}
psar_id = 0;
fseek(f, 0x24, SEEK_SET);
fread(&psar_offset, 4, 1, f);
fseek(f, psar_offset, SEEK_SET);
fread(&psar_id, 4, 1, f);
if (info.isDirectory) {
FileInfo ebootInfo;
// Check for existence of EBOOT.PBP, as required for "Directory games".
if (getFileInfo((filename + "/EBOOT.PBP").c_str(), &ebootInfo)) {
if (ebootInfo.exists) {
return FILETYPE_PSP_PBP_DIRECTORY;
}
}
}
FILE *f = fopen(filename.c_str(), "rb");
if (!f) {
// File does not exists
return FILETYPE_ERROR;
}
u32 id;
size_t readSize = fread(&id, 4, 1, f);
if (readSize != 1) {
fclose(f);
return FILETYPE_ERROR;
}
u32 psar_offset = 0, psar_id = 0;
if (id == 'PBP\x00') {
fseek(f, 0x24, SEEK_SET);
fread(&psar_offset, 4, 1, f);
fseek(f, psar_offset, SEEK_SET);
fread(&psar_id, 4, 1, f);
}
fclose(f);
if (strlen(filename) < 5) {
ERROR_LOG(LOADER, "invalid filename %s", filename);
}
const char *extension = filename + strlen(filename) - 4;
if (id == 'FLE\x7F')
{
if (!strcasecmp(extension, ".plf") || strstr(filename,"BOOT.BIN") ||
!strcasecmp(extension, ".elf") || !strcasecmp(extension,".prx") )
if (!strcasecmp(extension.c_str(), ".plf") || strstr(filename.c_str(),"BOOT.BIN") ||
!strcasecmp(extension.c_str(), ".elf") || !strcasecmp(extension.c_str(), ".prx") )
{
return FILETYPE_PSP_ELF;
}
@ -68,26 +97,28 @@ EmuFileType Identify_File(const char *filename)
}
else if (id == 'PBP\x00')
{
if(psar_id == 'MUPN')
if (psar_id == 'MUPN') {
return FILETYPE_PSP_ISO_NP;
else
return FILETYPE_PSP_PBP;
} else {
// Let's check if we got pointed to a PBP within such a directory.
// If so we just move up and return the directory itself as the game.
std::string path = getDir(filename);
// If loading from memstick...
size_t pos = path.find("/PSP/GAME/");
if (pos != std::string::npos) {
filename = path;
return FILETYPE_PSP_PBP_DIRECTORY;
}
}
}
else
{
if (!strcasecmp(extension,".pbp"))
if (!strcasecmp(extension.c_str(),".pbp"))
{
ERROR_LOG(LOADER, "A PBP with the wrong magic number?");
return FILETYPE_PSP_PBP;
}
else if (!strcasecmp(extension,".iso"))
{
return FILETYPE_PSP_ISO;
}
else if (!strcasecmp(extension,".cso"))
{
return FILETYPE_PSP_ISO;
}
else if (!strcasecmp(extension,".bin"))
else if (!strcasecmp(extension.c_str(),".bin"))
{
return FILETYPE_UNKNOWN_BIN;
}
@ -95,30 +126,46 @@ EmuFileType Identify_File(const char *filename)
return FILETYPE_UNKNOWN;
}
bool LoadFile(const char *filename, std::string *error_string)
{
bool LoadFile(std::string &filename, std::string *error_string) {
INFO_LOG(LOADER,"Identifying file...");
switch (Identify_File(filename))
{
// Note that this can modify filename!
switch (Identify_File(filename)) {
case FILETYPE_PSP_PBP_DIRECTORY:
{
std::string ebootFilename = filename + "/EBOOT.PBP";
FileInfo fileInfo;
getFileInfo((filename + "/EBOOT.PBP").c_str(), &fileInfo);
if (fileInfo.exists) {
INFO_LOG(LOADER, "File is a PBP in a directory!");
std::string path = filename;
size_t pos = path.find("/PSP/GAME/");
if (pos != std::string::npos)
pspFileSystem.SetStartingDirectory("ms0:" + path.substr(pos));
return Load_PSP_ELF_PBP(ebootFilename.c_str(), error_string);
} else {
*error_string = "No EBOOT.PBP, misidentified game";
return false;
}
}
case FILETYPE_PSP_PBP:
case FILETYPE_PSP_ELF:
{
INFO_LOG(LOADER,"File is an ELF!");
std::string path = getDir(filename);
// If loading from memstick...
size_t pos = path.find("/PSP/GAME/");
if (pos != std::string::npos)
pspFileSystem.SetStartingDirectory("ms0:" + path.substr(pos));
return Load_PSP_ELF_PBP(filename, error_string);
INFO_LOG(LOADER,"File is an ELF or loose PBP!");
return Load_PSP_ELF_PBP(filename.c_str(), error_string);
}
case FILETYPE_PSP_ISO:
case FILETYPE_PSP_ISO_NP:
pspFileSystem.SetStartingDirectory("disc0:/PSP_GAME/USRDIR");
return Load_PSP_ISO(filename, error_string);
return Load_PSP_ISO(filename.c_str(), error_string);
case FILETYPE_ERROR:
ERROR_LOG(LOADER, "Could not read file");
*error_string = "Error reading file";
break;
case FILETYPE_UNKNOWN_BIN:
case FILETYPE_UNKNOWN_ELF:
case FILETYPE_UNKNOWN:

View File

@ -21,6 +21,8 @@ enum EmuFileType
{
FILETYPE_ERROR,
FILETYPE_PSP_PBP_DIRECTORY,
FILETYPE_PSP_PBP,
FILETYPE_PSP_ELF,
FILETYPE_PSP_ISO,
@ -32,6 +34,9 @@ enum EmuFileType
FILETYPE_UNKNOWN
};
EmuFileType Identify_File(const char *filename);
// This can modify the string, for example for stripping off the "/EBOOT.PBP"
// for a FILETYPE_PSP_PBP_DIRECTORY.
EmuFileType Identify_File(std::string &str);
bool LoadFile(const char *filename, std::string *error_string);
// Can modify the string filename, as it calls IdentifyFile above.
bool LoadFile(std::string &filename, std::string *error_string);

View File

@ -505,6 +505,39 @@ namespace MIPSComp
}
}
void Jit::Comp_Allegrex2(u32 op)
{
CONDITIONAL_DISABLE;
int rt = _RT;
int rd = _RD;
// Don't change $zr.
if (rd == 0)
return;
switch (op & 0x3ff)
{
case 0xA0: //wsbh
if (cpu_info.bArmV7) {
gpr.MapDirtyIn(rd, rt);
REV16(gpr.R(rd), gpr.R(rt));
} else {
Comp_Generic(op);
}
break;
case 0xE0: //wsbw
if (cpu_info.bArmV7) {
gpr.MapDirtyIn(rd, rt);
REV(gpr.R(rd), gpr.R(rt));
} else {
Comp_Generic(op);
}
break;
default:
Comp_Generic(op);
break;
}
}
void Jit::Comp_MulDivType(u32 op)
{
CONDITIONAL_DISABLE;
@ -553,16 +586,6 @@ namespace MIPSComp
SUB(gpr.R(MIPSREG_HI), gpr.R(rs), Operand2(R0));
} else {
DISABLE;
gpr.MapDirtyDirtyInIn(MIPSREG_LO, MIPSREG_HI, rs, rt);
VMOV(S0, gpr.R(rs));
VMOV(S1, gpr.R(rt));
VCVT(D0, S0, TO_FLOAT | IS_SIGNED);
VCVT(D1, S1, TO_FLOAT | IS_SIGNED);
VDIV(D0, D0, D1);
VCVT(S0, D0, TO_INT);
VMOV(gpr.R(MIPSREG_LO), S0);
MUL(R0, gpr.R(rt), gpr.R(MIPSREG_LO));
SUB(gpr.R(MIPSREG_HI), gpr.R(rs), Operand2(R0));
}
break;
@ -575,16 +598,6 @@ namespace MIPSComp
SUB(gpr.R(MIPSREG_HI), gpr.R(rs), Operand2(R0));
} else {
DISABLE;
gpr.MapDirtyDirtyInIn(MIPSREG_LO, MIPSREG_HI, rs, rt);
VMOV(S0, gpr.R(rs));
VMOV(S1, gpr.R(rt));
VCVT(D0, S0, TO_FLOAT);
VCVT(D1, S1, TO_FLOAT);
VDIV(D0, D0, D1);
VCVT(S0, D0, TO_INT);
VMOV(gpr.R(MIPSREG_LO), S0);
MUL(R0, gpr.R(rt), gpr.R(MIPSREG_LO));
SUB(gpr.R(MIPSREG_HI), gpr.R(rs), Operand2(R0));
}
break;

View File

@ -15,6 +15,8 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include "Core/Reporting.h"
#include "Core/HLE/HLE.h"
#include "Core/MIPS/MIPS.h"
@ -51,7 +53,7 @@ namespace MIPSComp
void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely)
{
if (js.inDelaySlot) {
ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC);
ERROR_LOG_REPORT(JIT, "Branch in RSRTComp delay slot at %08x", js.compilerPC);
return;
}
int offset = (signed short)(op&0xFFFF)<<2;
@ -111,7 +113,7 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely)
void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool andLink, bool likely)
{
if (js.inDelaySlot) {
ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC);
ERROR_LOG_REPORT(JIT, "Branch in RSZeroComp delay slot at %08x", js.compilerPC);
return;
}
int offset = (signed short)(op&0xFFFF)<<2;
@ -206,7 +208,7 @@ void Jit::Comp_RelBranchRI(u32 op)
void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely)
{
if (js.inDelaySlot) {
ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC);
ERROR_LOG_REPORT(JIT, "Branch in FPFlag delay slot at %08x", js.compilerPC);
return;
}
int offset = (signed short)(op & 0xFFFF) << 2;
@ -263,6 +265,10 @@ void Jit::Comp_FPUBranch(u32 op)
// If likely is set, discard the branch slot if NOT taken.
void Jit::BranchVFPUFlag(u32 op, ArmGen::CCFlags cc, bool likely)
{
if (js.inDelaySlot) {
ERROR_LOG_REPORT(JIT, "Branch in VFPU delay slot at %08x", js.compilerPC);
return;
}
int offset = (signed short)(op & 0xFFFF) << 2;
u32 targetAddr = js.compilerPC + offset + 4;
@ -320,7 +326,7 @@ void Jit::Comp_VBranch(u32 op)
void Jit::Comp_Jump(u32 op)
{
if (js.inDelaySlot) {
ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC);
ERROR_LOG_REPORT(JIT, "Branch in Jump delay slot at %08x", js.compilerPC);
return;
}
u32 off = ((op & 0x03FFFFFF) << 2);
@ -352,7 +358,7 @@ void Jit::Comp_Jump(u32 op)
void Jit::Comp_JumpReg(u32 op)
{
if (js.inDelaySlot) {
ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC);
ERROR_LOG_REPORT(JIT, "Branch in JumpReg delay slot at %08x", js.compilerPC);
return;
}
int rs = _RS;

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