mirror of
https://github.com/libretro/ppsspp.git
synced 2024-11-25 09:09:49 +00:00
Merge branch 'master' of github.com:hrydgard/ppsspp
This commit is contained in:
commit
cd69ec3acb
7
.gitignore
vendored
7
.gitignore
vendored
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -28,7 +28,6 @@
|
||||
#ifdef __APPLE__
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <string>
|
||||
|
||||
#include "FileSearch.h"
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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_
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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();
|
||||
|
@ -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" />
|
||||
|
@ -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" />
|
||||
|
@ -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;
|
||||
|
@ -33,8 +33,6 @@
|
||||
|
||||
#include "../Globals.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
//const int CPU_HZ = 222000000;
|
||||
|
494
Core/CwCheat.cpp
Normal file
494
Core/CwCheat.cpp
Normal 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
42
Core/CwCheat.h
Normal 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;
|
||||
};
|
@ -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);
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 ¶m.GetPspParam()->common;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -18,10 +18,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "../Globals.h"
|
||||
|
||||
class ParamSFOData
|
||||
{
|
||||
|
@ -1,5 +1,3 @@
|
||||
#include <cstring>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "ext/libkirk/kirk_engine.h"
|
||||
|
@ -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'
|
||||
|
@ -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"
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
// TODO: Remove the Windows-specific code, FILE is fine there too.
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "../Core/FileSystems/FileSystem.h"
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
#include "../../Globals.h"
|
||||
#include "ChunkFile.h"
|
||||
#include <string>
|
||||
|
||||
enum FileAccess
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -18,9 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "FileSystem.h"
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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?)");
|
||||
|
@ -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++)
|
||||
{
|
||||
|
@ -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();
|
@ -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;
|
||||
}
|
||||
|
@ -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
@ -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);
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
45
Core/HLE/sceAudiocodec.cpp
Normal file
45
Core/HLE/sceAudiocodec.cpp
Normal 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
59
Core/HLE/sceAudiocodec.h
Normal 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();
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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"},
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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"},
|
||||
};
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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)();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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[] =
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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"},
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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"},
|
||||
|
@ -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"},
|
||||
|
@ -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()
|
||||
|
@ -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"},
|
||||
};
|
||||
|
||||
|
@ -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"},
|
||||
};
|
||||
|
@ -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"},
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
200
Core/HW/MpegDemux.cpp
Normal 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
64
Core/HW/MpegDemux.h
Normal 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;
|
||||
};
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "OMAConvert.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace OMAConvert {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
200
Core/HW/atrac3plus.cpp
Normal 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
91
Core/HW/atrac3plus.h
Normal 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_
|
@ -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_
|
@ -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_
|
@ -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__
|
@ -17,8 +17,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "../Globals.h"
|
||||
|
||||
struct InputState;
|
||||
|
141
Core/Loaders.cpp
141
Core/Loaders.cpp
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user