Don't have any games actually using this, so let's report too. Audio is falsely working when the stream isn't playable, not exactly sure the best way to detect that.
1744 lines
58 KiB
C++
1744 lines
58 KiB
C++
// 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 "Core/HLE/FunctionWrappers.h"
|
|
#include "Common/ChunkFile.h"
|
|
#include "Core/Reporting.h"
|
|
|
|
#include "Core/System.h"
|
|
#include "Core/FileSystems/MetaFileSystem.h"
|
|
#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>
|
|
#include <algorithm>
|
|
|
|
// "Go Sudoku" is a good way to test this code...
|
|
const int PSMF_VIDEO_STREAM_ID = 0xE0;
|
|
const int PSMF_AUDIO_STREAM_ID = 0xBD;
|
|
const int PSMF_AVC_STREAM = 0;
|
|
const int PSMF_ATRAC_STREAM = 1;
|
|
const int PSMF_PCM_STREAM = 2;
|
|
const int PSMF_DATA_STREAM = 3;
|
|
const int PSMF_AUDIO_STREAM = 15;
|
|
const int PSMF_PLAYER_VERSION_FULL = 0;
|
|
const int PSMF_PLAYER_VERSION_BASIC = 1;
|
|
const int PSMF_PLAYER_VERSION_NET = 2;
|
|
const int PSMF_PLAYER_CONFIG_LOOP = 0;
|
|
const int PSMF_PLAYER_CONFIG_NO_LOOP = 1;
|
|
const int PSMF_PLAYER_CONFIG_MODE_LOOP = 0;
|
|
const int PSMF_PLAYER_CONFIG_MODE_PIXEL_TYPE = 1;
|
|
|
|
int psmfMaxAheadTimestamp = 40000;
|
|
int audioSamples = 2048;
|
|
int audioSamplesBytes = audioSamples * 4;
|
|
int videoPixelMode = GE_CMODE_32BIT_ABGR8888;
|
|
int videoLoopStatus = PSMF_PLAYER_CONFIG_NO_LOOP;
|
|
|
|
enum PsmfPlayerError {
|
|
ERROR_PSMF_NOT_INITIALIZED = 0x80615001,
|
|
ERROR_PSMF_BAD_VERSION = 0x80615002,
|
|
ERROR_PSMF_NOT_FOUND = 0x80615025,
|
|
ERROR_PSMF_INVALID_ID = 0x80615100,
|
|
ERROR_PSMF_INVALID_VALUE = 0x806151fe,
|
|
ERROR_PSMF_INVALID_TIMESTAMP = 0x80615500,
|
|
ERROR_PSMF_INVALID_PSMF = 0x80615501,
|
|
|
|
ERROR_PSMFPLAYER_INVALID_STATUS = 0x80616001,
|
|
ERROR_PSMFPLAYER_INVALID_STREAM = 0x80616003,
|
|
ERROR_PSMFPLAYER_BUFFER_SIZE = 0x80616005,
|
|
ERROR_PSMFPLAYER_INVALID_CONFIG = 0x80616006,
|
|
ERROR_PSMFPLAYER_INVALID_PARAM = 0x80616008,
|
|
ERROR_PSMFPLAYER_NO_MORE_DATA = 0x8061600c,
|
|
};
|
|
|
|
enum PsmfPlayerStatus {
|
|
PSMF_PLAYER_STATUS_NONE = 0x0,
|
|
PSMF_PLAYER_STATUS_INIT = 0x1,
|
|
PSMF_PLAYER_STATUS_STANDBY = 0x2,
|
|
PSMF_PLAYER_STATUS_PLAYING = 0x4,
|
|
PSMF_PLAYER_STATUS_ERROR = 0x100,
|
|
PSMF_PLAYER_STATUS_PLAYING_FINISHED = 0x200,
|
|
};
|
|
|
|
enum PsmfPlayerMode {
|
|
PSMF_PLAYER_MODE_PLAY = 0,
|
|
PSMF_PLAYER_MODE_SLOWMOTION = 1,
|
|
PSMF_PLAYER_MODE_STEPFRAME = 2,
|
|
PSMF_PLAYER_MODE_PAUSE = 3,
|
|
PSMF_PLAYER_MODE_FORWARD = 4,
|
|
PSMF_PLAYER_MODE_REWIND = 5,
|
|
};
|
|
|
|
struct PsmfData {
|
|
u32_le version;
|
|
u32_le headerSize;
|
|
u32_le headerOffset;
|
|
u32_le streamSize;
|
|
u32_le streamOffset;
|
|
u32_le streamNum;
|
|
u32_le unk1;
|
|
u32_le unk2;
|
|
};
|
|
|
|
struct PsmfPlayerCreateData {
|
|
PSPPointer<u8> buffer;
|
|
u32 bufferSize;
|
|
int threadPriority;
|
|
};
|
|
|
|
struct PsmfPlayerData {
|
|
s32_le videoCodec;
|
|
s32_le videoStreamNum;
|
|
s32_le audioCodec;
|
|
s32_le audioStreamNum;
|
|
s32_le playMode;
|
|
s32_le playSpeed;
|
|
// TODO: Was "long", which is 32, but should this be 64? Not even used?
|
|
s32_le psmfPlayerLastTimestamp;
|
|
};
|
|
|
|
struct PsmfInfo {
|
|
u32_le lengthTS;
|
|
s32_le numVideoStreams;
|
|
s32_le numAudioStreams;
|
|
s32_le numPCMStreams;
|
|
s32_le playerVersion;
|
|
};
|
|
|
|
struct PsmfEntry {
|
|
int EPPts;
|
|
int EPOffset;
|
|
int EPIndex;
|
|
int EPPicOffset;
|
|
};
|
|
|
|
int getMaxAheadTimestamp(int packets) {return std::max(40000, packets * 700);}
|
|
|
|
class PsmfStream;
|
|
|
|
// This does NOT match the raw structure. Due to endianness etc,
|
|
// we read it manually.
|
|
// TODO: Change to work directly with the data in RAM instead of this
|
|
// JPSCP-esque class.
|
|
typedef std::map<int, PsmfStream *> PsmfStreamMap;
|
|
|
|
class Psmf {
|
|
public:
|
|
// For savestates only.
|
|
Psmf() {}
|
|
Psmf(u32 data);
|
|
~Psmf();
|
|
void DoState(PointerWrap &p);
|
|
|
|
bool isValidCurrentStreamNumber() {
|
|
return currentStreamNum >= 0 && currentStreamNum < (int)streamMap.size(); // urgh, checking size isn't really right here.
|
|
}
|
|
|
|
void setStreamNum(int num);
|
|
bool setStreamWithType(int type, int channel);
|
|
|
|
int FindEPWithTimestamp(int pts);
|
|
|
|
u32 magic;
|
|
u32 version;
|
|
u32 streamOffset;
|
|
u32 streamSize;
|
|
u32 headerSize;
|
|
u32 headerOffset;
|
|
u32 streamType;
|
|
u32 streamChannel;
|
|
// 0x50
|
|
u32 streamDataTotalSize;
|
|
u32 presentationStartTime;
|
|
u32 presentationEndTime;
|
|
u32 streamDataNextBlockSize;
|
|
u32 streamDataNextInnerBlockSize;
|
|
|
|
int numStreams;
|
|
int currentStreamNum;
|
|
int currentAudioStreamNum;
|
|
int currentVideoStreamNum;
|
|
|
|
// parameters gotten from streams
|
|
// I guess this is the seek information?
|
|
u32 EPMapOffset;
|
|
u32 EPMapEntriesNum;
|
|
int videoWidth;
|
|
int videoHeight;
|
|
int audioChannels;
|
|
int audioFrequency;
|
|
std::vector<PsmfEntry> EPMap;
|
|
|
|
PsmfStreamMap streamMap;
|
|
};
|
|
|
|
class PsmfPlayer {
|
|
public:
|
|
// For savestates only.
|
|
PsmfPlayer() { mediaengine = new MediaEngine; filehandle = 0;}
|
|
PsmfPlayer(const PsmfPlayerCreateData *data);
|
|
~PsmfPlayer() { if (mediaengine) delete mediaengine; pspFileSystem.CloseFile(filehandle);}
|
|
void DoState(PointerWrap &p);
|
|
|
|
u32 filehandle;
|
|
u32 fileoffset;
|
|
int readSize;
|
|
int streamSize;
|
|
u8 tempbuf[0x10000];
|
|
|
|
int videoCodec;
|
|
int videoStreamNum;
|
|
int audioCodec;
|
|
int audioStreamNum;
|
|
int playMode;
|
|
int playSpeed;
|
|
long psmfPlayerLastTimestamp;
|
|
|
|
int displayBuffer;
|
|
int displayBufferSize;
|
|
int playbackThreadPriority;
|
|
int psmfMaxAheadTimestamp;
|
|
int totalVideoStreams;
|
|
int totalAudioStreams;
|
|
int playerVersion;
|
|
|
|
SceMpegAu psmfPlayerAtracAu;
|
|
SceMpegAu psmfPlayerAvcAu;
|
|
PsmfPlayerStatus status;
|
|
|
|
MediaEngine* mediaengine;
|
|
};
|
|
|
|
class PsmfStream {
|
|
public:
|
|
// Used for save states.
|
|
PsmfStream() {
|
|
}
|
|
|
|
PsmfStream(int type, int channel) {
|
|
this->type = type;
|
|
this->channel = channel;
|
|
}
|
|
|
|
void readMPEGVideoStreamParams(u32 addr, u32 data, Psmf *psmf) {
|
|
int streamId = Memory::Read_U8(addr);
|
|
int privateStreamId = Memory::Read_U8(addr + 1);
|
|
// two unknowns here
|
|
psmf->EPMapOffset = bswap32(Memory::Read_U32(addr + 4));
|
|
psmf->EPMapEntriesNum = bswap32(Memory::Read_U32(addr + 8));
|
|
psmf->videoWidth = Memory::Read_U8(addr + 12) * 16;
|
|
psmf->videoHeight = Memory::Read_U8(addr + 13) * 16;
|
|
|
|
const u32 EP_MAP_STRIDE = 1 + 1 + 4 + 4;
|
|
psmf->EPMap.clear();
|
|
for (u32 i = 0; i < psmf->EPMapEntriesNum; i++) {
|
|
const u32 entryAddr = data + psmf->EPMapOffset + EP_MAP_STRIDE * i;
|
|
PsmfEntry entry;
|
|
entry.EPIndex = Memory::Read_U8(entryAddr + 0);
|
|
entry.EPPicOffset = Memory::Read_U8(entryAddr + 1);
|
|
entry.EPPts = *(u32_be*) Memory::GetPointer(entryAddr + 2);
|
|
entry.EPOffset = *(u32_be*) Memory::GetPointer(entryAddr + 6);
|
|
psmf->EPMap.push_back(entry);
|
|
}
|
|
|
|
INFO_LOG(ME, "PSMF MPEG data found: id=%02x, privid=%02x, epmoff=%08x, epmnum=%08x, width=%i, height=%i", streamId, privateStreamId, psmf->EPMapOffset, psmf->EPMapEntriesNum, psmf->videoWidth, psmf->videoHeight);
|
|
}
|
|
|
|
void readPrivateAudioStreamParams(u32 addr, Psmf *psmf) {
|
|
int streamId = Memory::Read_U8(addr);
|
|
int privateStreamId = Memory::Read_U8(addr + 1);
|
|
psmf->audioChannels = Memory::Read_U8(addr + 14);
|
|
psmf->audioFrequency = Memory::Read_U8(addr + 15);
|
|
// two unknowns here
|
|
INFO_LOG(ME, "PSMF private audio found: id=%02x, privid=%02x, channels=%i, freq=%i", streamId, privateStreamId, psmf->audioChannels, psmf->audioFrequency);
|
|
}
|
|
|
|
void DoState(PointerWrap &p) {
|
|
auto s = p.Section("PsmfStream", 1);
|
|
if (!s)
|
|
return;
|
|
|
|
p.Do(type);
|
|
p.Do(channel);
|
|
}
|
|
|
|
|
|
int type;
|
|
int channel;
|
|
};
|
|
|
|
|
|
Psmf::Psmf(u32 data) {
|
|
headerOffset = data;
|
|
magic = Memory::Read_U32(data);
|
|
version = Memory::Read_U32(data + 4);
|
|
streamOffset = bswap32(Memory::Read_U32(data + 8));
|
|
streamSize = bswap32(Memory::Read_U32(data + 12));
|
|
streamDataTotalSize = bswap32(Memory::Read_U32(data + 0x50));
|
|
presentationStartTime = getMpegTimeStamp(Memory::GetPointer(data + PSMF_FIRST_TIMESTAMP_OFFSET));
|
|
presentationEndTime = getMpegTimeStamp(Memory::GetPointer(data + PSMF_LAST_TIMESTAMP_OFFSET));
|
|
streamDataNextBlockSize = bswap32(Memory::Read_U32(data + 0x6A));
|
|
streamDataNextInnerBlockSize = bswap32(Memory::Read_U32(data + 0x7C));
|
|
numStreams = bswap16(Memory::Read_U16(data + 0x80));
|
|
// TODO: Always?
|
|
headerSize = 0x800;
|
|
|
|
currentStreamNum = -1;
|
|
currentAudioStreamNum = -1;
|
|
currentVideoStreamNum = -1;
|
|
|
|
for (int i = 0; i < numStreams; i++) {
|
|
PsmfStream *stream = 0;
|
|
u32 currentStreamAddr = data + 0x82 + i * 16;
|
|
int streamId = Memory::Read_U8(currentStreamAddr);
|
|
if ((streamId & PSMF_VIDEO_STREAM_ID) == PSMF_VIDEO_STREAM_ID) {
|
|
stream = new PsmfStream(PSMF_AVC_STREAM, ++currentVideoStreamNum);
|
|
stream->readMPEGVideoStreamParams(currentStreamAddr, data, this);
|
|
} else if ((streamId & PSMF_AUDIO_STREAM_ID) == PSMF_AUDIO_STREAM_ID) {
|
|
stream = new PsmfStream(PSMF_ATRAC_STREAM, ++currentAudioStreamNum);
|
|
stream->readPrivateAudioStreamParams(currentStreamAddr, this);
|
|
}
|
|
if (stream) {
|
|
currentStreamNum++;
|
|
streamMap[currentStreamNum] = stream;
|
|
}
|
|
}
|
|
}
|
|
|
|
Psmf::~Psmf() {
|
|
for (auto it = streamMap.begin(), end = streamMap.end(); it != end; ++it) {
|
|
delete it->second;
|
|
}
|
|
streamMap.clear();
|
|
}
|
|
|
|
PsmfPlayer::PsmfPlayer(const PsmfPlayerCreateData *data) {
|
|
videoCodec = -1;
|
|
videoStreamNum = -1;
|
|
audioCodec = -1;
|
|
audioStreamNum = -1;
|
|
playMode = 0;
|
|
playSpeed = 0;
|
|
psmfPlayerLastTimestamp = 0;
|
|
status = PSMF_PLAYER_STATUS_INIT;
|
|
mediaengine = new MediaEngine;
|
|
filehandle = 0;
|
|
fileoffset = 0;
|
|
readSize = 0;
|
|
streamSize = 0;
|
|
|
|
displayBuffer = data->buffer.ptr;
|
|
displayBufferSize = data->bufferSize;
|
|
playbackThreadPriority = data->threadPriority;
|
|
}
|
|
|
|
void Psmf::DoState(PointerWrap &p) {
|
|
auto s = p.Section("Psmf", 1, 2);
|
|
if (!s)
|
|
return;
|
|
|
|
p.Do(magic);
|
|
p.Do(version);
|
|
p.Do(streamOffset);
|
|
p.Do(streamSize);
|
|
p.Do(headerOffset);
|
|
p.Do(streamDataTotalSize);
|
|
p.Do(presentationStartTime);
|
|
p.Do(presentationEndTime);
|
|
p.Do(streamDataNextBlockSize);
|
|
p.Do(streamDataNextInnerBlockSize);
|
|
p.Do(numStreams);
|
|
|
|
p.Do(currentStreamNum);
|
|
p.Do(currentAudioStreamNum);
|
|
p.Do(currentVideoStreamNum);
|
|
|
|
p.Do(EPMapOffset);
|
|
p.Do(EPMapEntriesNum);
|
|
p.Do(videoWidth);
|
|
p.Do(videoHeight);
|
|
p.Do(audioChannels);
|
|
p.Do(audioFrequency);
|
|
|
|
if (s >= 2) {
|
|
p.Do(EPMap);
|
|
}
|
|
|
|
p.Do(streamMap);
|
|
}
|
|
|
|
void PsmfPlayer::DoState(PointerWrap &p) {
|
|
auto s = p.Section("PsmfPlayer", 1, 2);
|
|
if (!s)
|
|
return;
|
|
|
|
p.Do(videoCodec);
|
|
p.Do(videoStreamNum);
|
|
p.Do(audioCodec);
|
|
p.Do(audioStreamNum);
|
|
p.Do(playMode);
|
|
p.Do(playSpeed);
|
|
|
|
p.Do(displayBuffer);
|
|
p.Do(displayBufferSize);
|
|
p.Do(playbackThreadPriority);
|
|
p.Do(psmfMaxAheadTimestamp);
|
|
p.Do(psmfPlayerLastTimestamp);
|
|
if (s >= 2) {
|
|
p.Do(totalVideoStreams);
|
|
p.Do(totalAudioStreams);
|
|
p.Do(playerVersion);
|
|
}
|
|
p.DoClass(mediaengine);
|
|
p.Do(filehandle);
|
|
p.Do(fileoffset);
|
|
p.Do(readSize);
|
|
p.Do(streamSize);
|
|
|
|
p.Do(status);
|
|
p.Do(psmfPlayerAvcAu);
|
|
}
|
|
|
|
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 && iter->second->channel == channel) {
|
|
setStreamNum(iter->first);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int Psmf::FindEPWithTimestamp(int pts) {
|
|
int best = -1;
|
|
int bestPts = 0;
|
|
|
|
for (int i = 0; i < (int)EPMap.size(); ++i) {
|
|
const int matchPts = EPMap[i].EPPts;
|
|
if (matchPts == pts) {
|
|
// Exact match, take it.
|
|
return i;
|
|
}
|
|
// TODO: Does it actually do fuzzy matching?
|
|
if (matchPts < pts && matchPts >= bestPts) {
|
|
best = i;
|
|
bestPts = matchPts;
|
|
}
|
|
}
|
|
|
|
return best;
|
|
}
|
|
|
|
|
|
static std::map<u32, Psmf *> psmfMap;
|
|
static std::map<u32, PsmfPlayer *> psmfPlayerMap;
|
|
|
|
Psmf *getPsmf(u32 psmf)
|
|
{
|
|
auto psmfstruct = PSPPointer<PsmfData>::Create(psmf);
|
|
if (!psmfstruct.IsValid())
|
|
return 0;
|
|
auto iter = psmfMap.find(psmfstruct->headerOffset);
|
|
if (iter != psmfMap.end())
|
|
return iter->second;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
PsmfPlayer *getPsmfPlayer(u32 psmfplayer)
|
|
{
|
|
auto iter = psmfPlayerMap.find(Memory::Read_U32(psmfplayer));
|
|
if (iter != psmfPlayerMap.end())
|
|
return iter->second;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void __PsmfInit()
|
|
{
|
|
videoPixelMode = GE_CMODE_32BIT_ABGR8888;
|
|
videoLoopStatus = PSMF_PLAYER_CONFIG_NO_LOOP;
|
|
}
|
|
|
|
void __PsmfDoState(PointerWrap &p)
|
|
{
|
|
auto s = p.Section("scePsmf", 1);
|
|
if (!s)
|
|
return;
|
|
|
|
p.Do(psmfMap);
|
|
}
|
|
|
|
void __PsmfPlayerDoState(PointerWrap &p)
|
|
{
|
|
auto s = p.Section("scePsmfPlayer", 1);
|
|
if (!s)
|
|
return;
|
|
|
|
p.Do(psmfPlayerMap);
|
|
p.Do(videoPixelMode);
|
|
p.Do(videoLoopStatus);
|
|
}
|
|
|
|
void __PsmfShutdown()
|
|
{
|
|
for (auto it = psmfMap.begin(), end = psmfMap.end(); it != end; ++it)
|
|
delete it->second;
|
|
for (auto it = psmfPlayerMap.begin(), end = psmfPlayerMap.end(); it != end; ++it)
|
|
delete it->second;
|
|
psmfMap.clear();
|
|
psmfPlayerMap.clear();
|
|
}
|
|
|
|
bool isPlayingStatus(u32 status) {
|
|
if (status != PSMF_PLAYER_STATUS_PLAYING && status != PSMF_PLAYER_STATUS_PLAYING_FINISHED) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool isInitializedStatus(u32 status) {
|
|
if (status == PSMF_PLAYER_STATUS_NONE) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
u32 scePsmfSetPsmf(u32 psmfStruct, u32 psmfData)
|
|
{
|
|
INFO_LOG(ME, "scePsmfSetPsmf(%08x, %08x)", psmfStruct, psmfData);
|
|
|
|
Psmf *psmf = new Psmf(psmfData);
|
|
|
|
PsmfData data = {0};
|
|
data.version = psmf->version;
|
|
data.headerSize = 0x800;
|
|
data.streamSize = psmf->streamSize;
|
|
data.streamNum = psmf->numStreams;
|
|
data.headerOffset = psmf->headerOffset;
|
|
auto iter = psmfMap.find(data.headerOffset);
|
|
if (iter != psmfMap.end())
|
|
delete iter->second;
|
|
psmfMap[data.headerOffset] = psmf;
|
|
Memory::WriteStruct(psmfStruct, &data);
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetNumberOfStreams(u32 psmfStruct)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetNumberOfStreams(%08x): invalid psmf", psmfStruct);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
DEBUG_LOG(ME, "scePsmfGetNumberOfStreams(%08x)", psmfStruct);
|
|
return psmf->numStreams;
|
|
}
|
|
|
|
u32 scePsmfGetNumberOfSpecificStreams(u32 psmfStruct, int streamType)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetNumberOfSpecificStreams(%08x, %08x): invalid psmf", psmfStruct, streamType);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
WARN_LOG(ME, "scePsmfGetNumberOfSpecificStreams(%08x, %08x)", psmfStruct, streamType);
|
|
int streamNum = 0;
|
|
int type = (streamType == PSMF_AUDIO_STREAM ? PSMF_ATRAC_STREAM : streamType);
|
|
for (int i = (int)psmf->streamMap.size() - 1; i >= 0; i--) {
|
|
if (psmf->streamMap[i]->type == type)
|
|
streamNum++;
|
|
}
|
|
return streamNum;
|
|
}
|
|
|
|
u32 scePsmfSpecifyStreamWithStreamType(u32 psmfStruct, u32 streamType, u32 channel)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfSpecifyStreamWithStreamType(%08x, %08x, %i): invalid psmf", psmfStruct, streamType, channel);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
INFO_LOG(ME, "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(ME, "scePsmfSpecifyStreamWithStreamTypeNumber(%08x, %08x, %08x): invalid psmf", psmfStruct, streamType, typeNum);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
INFO_LOG_REPORT(ME, "scePsmfSpecifyStreamWithStreamTypeNumber(%08x, %08x, %08x)", psmfStruct, streamType, typeNum);
|
|
// right now typeNum and channel are the same...
|
|
if (!psmf->setStreamWithType(streamType, typeNum)) {
|
|
psmf->setStreamNum(-1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfSpecifyStream(u32 psmfStruct, int streamNum) {
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfSpecifyStream(%08x, %i): invalid psmf", psmfStruct, streamNum);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
INFO_LOG(ME, "scePsmfSpecifyStream(%08x, %i)", psmfStruct, streamNum);
|
|
psmf->setStreamNum(streamNum);
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetVideoInfo(u32 psmfStruct, u32 videoInfoAddr) {
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetVideoInfo(%08x, %08x): invalid psmf", psmfStruct, videoInfoAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
INFO_LOG(ME, "scePsmfGetVideoInfo(%08x, %08x)", psmfStruct, videoInfoAddr);
|
|
if (Memory::IsValidAddress(videoInfoAddr)) {
|
|
Memory::Write_U32(psmf->videoWidth, videoInfoAddr);
|
|
Memory::Write_U32(psmf->videoHeight, videoInfoAddr + 4);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetAudioInfo(u32 psmfStruct, u32 audioInfoAddr) {
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetAudioInfo(%08x, %08x): invalid psmf", psmfStruct, audioInfoAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
INFO_LOG(ME, "scePsmfGetAudioInfo(%08x, %08x)", psmfStruct, audioInfoAddr);
|
|
if (Memory::IsValidAddress(audioInfoAddr)) {
|
|
Memory::Write_U32(psmf->audioChannels, audioInfoAddr);
|
|
Memory::Write_U32(psmf->audioFrequency, audioInfoAddr + 4);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetCurrentStreamType(u32 psmfStruct, u32 typeAddr, u32 channelAddr) {
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetCurrentStreamType(%08x, %08x, %08x): invalid psmf", psmfStruct, typeAddr, channelAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
INFO_LOG(ME, "scePsmfGetCurrentStreamType(%08x, %08x, %08x)", psmfStruct, typeAddr, channelAddr);
|
|
if (Memory::IsValidAddress(typeAddr)) {
|
|
u32 type = 0, channel = 0;
|
|
if (psmf->streamMap.find(psmf->currentStreamNum) != psmf->streamMap.end())
|
|
type = psmf->streamMap[psmf->currentStreamNum]->type;
|
|
if (psmf->streamMap.find(psmf->currentStreamNum) != psmf->streamMap.end())
|
|
channel = psmf->streamMap[psmf->currentStreamNum]->channel;
|
|
Memory::Write_U32(type, typeAddr);
|
|
Memory::Write_U32(channel, channelAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetStreamSize(u32 psmfStruct, u32 sizeAddr)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetStreamSize(%08x, %08x): invalid psmf", psmfStruct, sizeAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
DEBUG_LOG(ME, "scePsmfGetStreamSize(%08x, %08x)", psmfStruct, sizeAddr);
|
|
if (Memory::IsValidAddress(sizeAddr)) {
|
|
Memory::Write_U32(psmf->streamSize, sizeAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfQueryStreamOffset(u32 bufferAddr, u32 offsetAddr)
|
|
{
|
|
WARN_LOG(ME, "scePsmfQueryStreamOffset(%08x, %08x)", bufferAddr, offsetAddr);
|
|
if (Memory::IsValidAddress(offsetAddr)) {
|
|
Memory::Write_U32(bswap32(Memory::Read_U32(bufferAddr + PSMF_STREAM_OFFSET_OFFSET)), offsetAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfQueryStreamSize(u32 bufferAddr, u32 sizeAddr)
|
|
{
|
|
WARN_LOG(ME, "scePsmfQueryStreamSize(%08x, %08x)", bufferAddr, sizeAddr);
|
|
if (Memory::IsValidAddress(sizeAddr)) {
|
|
Memory::Write_U32(bswap32(Memory::Read_U32(bufferAddr + PSMF_STREAM_SIZE_OFFSET)), sizeAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetHeaderSize(u32 psmfStruct, u32 sizeAddr)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetHeaderSize(%08x, %08x): invalid psmf", psmfStruct, sizeAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
DEBUG_LOG(ME, "scePsmfGetHeaderSize(%08x, %08x)", psmfStruct, sizeAddr);
|
|
if (Memory::IsValidAddress(sizeAddr)) {
|
|
Memory::Write_U32(psmf->headerSize, sizeAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetPsmfVersion(u32 psmfStruct)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetPsmfVersion(%08x): invalid psmf", psmfStruct);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
DEBUG_LOG(ME, "scePsmfGetPsmfVersion(%08x)", psmfStruct);
|
|
return psmf->version;
|
|
}
|
|
|
|
u32 scePsmfVerifyPsmf(u32 psmfAddr)
|
|
{
|
|
u32 magic = Memory::Read_U32(psmfAddr);
|
|
if (magic != PSMF_MAGIC) {
|
|
ERROR_LOG(ME, "scePsmfVerifyPsmf(%08x): bad magic %08x", psmfAddr, magic);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
int version = Memory::Read_U32(psmfAddr + PSMF_STREAM_VERSION_OFFSET);
|
|
if (version < 0) {
|
|
ERROR_LOG(ME, "scePsmfVerifyPsmf(%08x): bad version %08x", psmfAddr, version);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
// Kurohyou 2 (at least the demo) uses an uninitialized value that happens to be zero on the PSP.
|
|
// It appears to be written by scePsmfVerifyPsmf(), so we write some bytes into the stack here.
|
|
Memory::Memset(currentMIPS->r[MIPS_REG_SP] - 0x20, 0, 0x20);
|
|
DEBUG_LOG(ME, "scePsmfVerifyPsmf(%08x)", psmfAddr);
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetNumberOfEPentries(u32 psmfStruct)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetNumberOfEPentries(%08x): invalid psmf", psmfStruct);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
DEBUG_LOG(ME, "scePsmfGetNumberOfEPentries(%08x)", psmfStruct);
|
|
return psmf->EPMapEntriesNum;
|
|
}
|
|
|
|
u32 scePsmfGetPresentationStartTime(u32 psmfStruct, u32 startTimeAddr)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetPresentationStartTime(%08x, %08x): invalid psmf", psmfStruct, startTimeAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
DEBUG_LOG(ME, "scePsmfGetPresentationStartTime(%08x, %08x)", psmfStruct, startTimeAddr);
|
|
if (Memory::IsValidAddress(startTimeAddr)) {
|
|
Memory::Write_U32(psmf->presentationStartTime, startTimeAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetPresentationEndTime(u32 psmfStruct, u32 endTimeAddr)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetPresentationEndTime(%08x, %08x): invalid psmf", psmfStruct, endTimeAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
DEBUG_LOG(ME, "scePsmfGetPresentationEndTime(%08x, %08x)", psmfStruct, endTimeAddr);
|
|
if (Memory::IsValidAddress(endTimeAddr)) {
|
|
Memory::Write_U32(psmf->presentationEndTime, endTimeAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetCurrentStreamNumber(u32 psmfStruct)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetCurrentStreamNumber(%08x): invalid psmf", psmfStruct);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
DEBUG_LOG(ME, "scePsmfGetCurrentStreamNumber(%08x)", psmfStruct);
|
|
return psmf->currentStreamNum;
|
|
}
|
|
|
|
u32 scePsmfCheckEPMap(u32 psmfStruct)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfCheckEPMap(%08x): invalid psmf", psmfStruct);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
DEBUG_LOG(ME, "scePsmfCheckEPMap(%08x)", psmfStruct);
|
|
return psmf->EPMap.empty() ? ERROR_PSMF_NOT_FOUND : 0;
|
|
}
|
|
|
|
u32 scePsmfGetEPWithId(u32 psmfStruct, int epid, u32 entryAddr)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetEPWithId(%08x, %i, %08x): invalid psmf", psmfStruct, epid, entryAddr);
|
|
return ERROR_PSMF_NOT_INITIALIZED;
|
|
}
|
|
DEBUG_LOG(ME, "scePsmfGetEPWithId(%08x, %i, %08x)", psmfStruct, epid, entryAddr);
|
|
|
|
if (epid < 0 || epid >= (int)psmf->EPMap.size()) {
|
|
ERROR_LOG(ME, "scePsmfGetEPWithId(%08x, %i): invalid id", psmfStruct, epid);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
if (Memory::IsValidAddress(entryAddr)) {
|
|
Memory::WriteStruct(entryAddr, &psmf->EPMap[epid]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetEPWithTimestamp(u32 psmfStruct, u32 ts, u32 entryAddr)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i, %08x): invalid psmf", psmfStruct, ts, entryAddr);
|
|
return ERROR_PSMF_NOT_INITIALIZED;
|
|
}
|
|
DEBUG_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i, %08x)", psmfStruct, ts, entryAddr);
|
|
|
|
if (ts < psmf->presentationStartTime) {
|
|
ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i): invalid timestamp", psmfStruct, ts);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
int epid = psmf->FindEPWithTimestamp(ts);
|
|
if (epid < 0 || epid >= (int)psmf->EPMap.size()) {
|
|
ERROR_LOG(ME, "scePsmfGetEPWithTimestamp(%08x, %i): invalid id", psmfStruct, epid);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
if (Memory::IsValidAddress(entryAddr)) {
|
|
Memory::WriteStruct(entryAddr, &psmf->EPMap[epid]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfGetEPidWithTimestamp(u32 psmfStruct, u32 ts)
|
|
{
|
|
Psmf *psmf = getPsmf(psmfStruct);
|
|
if (!psmf) {
|
|
ERROR_LOG(ME, "scePsmfGetEPidWithTimestamp(%08x, %i): invalid psmf", psmfStruct, ts);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
DEBUG_LOG(ME, "scePsmfGetEPidWithTimestamp(%08x, %i)", psmfStruct, ts);
|
|
|
|
if (psmf->EPMap.empty()) {
|
|
ERROR_LOG(ME, "scePsmfGetEPidWithTimestamp(%08x): EPMap is empty", psmfStruct);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
if (ts < psmf->presentationStartTime) {
|
|
ERROR_LOG(ME, "scePsmfGetEPidWithTimestamp(%08x, %i): invalid timestamp", psmfStruct, ts);
|
|
return ERROR_PSMF_INVALID_TIMESTAMP;
|
|
}
|
|
|
|
int epid = psmf->FindEPWithTimestamp(ts);
|
|
if (epid < 0 || epid >= (int)psmf->EPMap.size()) {
|
|
ERROR_LOG(ME, "scePsmfGetEPidWithTimestamp(%08x, %i): invalid id", psmfStruct, epid);
|
|
return ERROR_PSMF_INVALID_ID;
|
|
}
|
|
|
|
return epid;
|
|
}
|
|
|
|
int scePsmfPlayerCreate(u32 psmfPlayer, u32 dataPtr)
|
|
{
|
|
auto player = PSPPointer<u32>::Create(psmfPlayer);
|
|
const auto data = PSPPointer<const PsmfPlayerCreateData>::Create(dataPtr);
|
|
|
|
if (!player.IsValid() || !data.IsValid()) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerCreate(%08x, %08x): bad pointers", psmfPlayer, dataPtr);
|
|
// Crashes on a PSP.
|
|
return SCE_KERNEL_ERROR_ILLEGAL_ADDRESS;
|
|
}
|
|
if (!data->buffer.IsValid()) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerCreate(%08x, %08x): invalid buffer address %08x", psmfPlayer, dataPtr, data->buffer.ptr);
|
|
// Also crashes on a PSP.
|
|
*player = 0;
|
|
return SCE_KERNEL_ERROR_ILLEGAL_ADDRESS;
|
|
}
|
|
if (data->bufferSize < 0x00300000) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerCreate(%08x, %08x): buffer too small %08x", psmfPlayer, dataPtr, data->bufferSize);
|
|
*player = 0;
|
|
return ERROR_PSMFPLAYER_BUFFER_SIZE;
|
|
}
|
|
if (data->threadPriority < 0x10 || data->threadPriority >= 0x6E) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerCreate(%08x, %08x): bad thread priority %02x", psmfPlayer, dataPtr, data->threadPriority);
|
|
*player = 0;
|
|
return ERROR_PSMFPLAYER_INVALID_PARAM;
|
|
}
|
|
if (!psmfPlayerMap.empty()) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerCreate(%08x, %08x): already have an active player", psmfPlayer, dataPtr);
|
|
// TODO: Tests show this is what we should do. Leaving it off for now to see if safe.
|
|
//*player = 0;
|
|
//return ERROR_MPEG_ALREADY_INIT;
|
|
}
|
|
|
|
INFO_LOG(ME, "scePsmfPlayerCreate(%08x, %08x)", psmfPlayer, dataPtr);
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
psmfplayer = new PsmfPlayer(data);
|
|
if (psmfPlayerMap.find(psmfPlayer) != psmfPlayerMap.end())
|
|
delete psmfPlayerMap[psmfPlayer];
|
|
psmfPlayerMap[psmfPlayer] = psmfplayer;
|
|
|
|
// Write something there to identify it with.
|
|
*player = psmfPlayer;
|
|
}
|
|
|
|
// These really shouldn't be globals. But, you can only have one psmfplayer anyway.
|
|
videoPixelMode = GE_CMODE_32BIT_ABGR8888;
|
|
videoLoopStatus = PSMF_PLAYER_CONFIG_NO_LOOP;
|
|
|
|
psmfplayer->psmfMaxAheadTimestamp = getMaxAheadTimestamp(581);
|
|
psmfplayer->status = PSMF_PLAYER_STATUS_INIT;
|
|
return hleDelayResult(0, "player create", 20000);
|
|
}
|
|
|
|
int scePsmfPlayerStop(u32 psmfPlayer)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerStop(%08x): invalid psmf player", psmfPlayer);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerStop(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
INFO_LOG(ME, "scePsmfPlayerStop(%08x)", psmfPlayer);
|
|
psmfplayer->status = PSMF_PLAYER_STATUS_STANDBY;
|
|
return 0;
|
|
}
|
|
|
|
int scePsmfPlayerBreak(u32 psmfPlayer)
|
|
{
|
|
WARN_LOG(ME, "scePsmfPlayerBreak(%08x)", psmfPlayer);
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerBreak(%08x): invalid psmf player", psmfPlayer);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerBreak(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
// Fix everybody stress buster
|
|
psmfplayer->status = PSMF_PLAYER_STATUS_INIT;
|
|
return 0;
|
|
}
|
|
|
|
int _PsmfPlayerFillRingbuffer(PsmfPlayer *psmfplayer) {
|
|
if (!psmfplayer->filehandle)
|
|
return -1;
|
|
u8* buf = psmfplayer->tempbuf;
|
|
int tempbufSize = (int)sizeof(psmfplayer->tempbuf);
|
|
int size;
|
|
// Let's not burn a bunch of time adding data all at once.
|
|
int addMax = std::max(2048 * 100, tempbufSize);
|
|
do {
|
|
size = std::min(psmfplayer->mediaengine->getRemainSize(), tempbufSize);
|
|
size = std::min(psmfplayer->mediaengine->getAudioRemainSize(), size);
|
|
size = std::min(psmfplayer->streamSize - psmfplayer->readSize, size);
|
|
if (size <= 0)
|
|
break;
|
|
size = (int)pspFileSystem.ReadFile(psmfplayer->filehandle, buf, size);
|
|
psmfplayer->readSize += size;
|
|
psmfplayer->mediaengine->addStreamData(buf, size);
|
|
addMax -= size;
|
|
if (addMax <= 0)
|
|
break;
|
|
} while (size > 0);
|
|
if (psmfplayer->readSize >= psmfplayer->streamSize && videoLoopStatus == PSMF_PLAYER_CONFIG_LOOP) {
|
|
// start looping
|
|
psmfplayer->readSize = 0;
|
|
pspFileSystem.SeekFile(psmfplayer->filehandle, psmfplayer->fileoffset, FILEMOVE_BEGIN);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int _PsmfPlayerSetPsmfOffset(u32 psmfPlayer, const char *filename, int offset, bool docallback) {
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer || psmfplayer->status != PSMF_PLAYER_STATUS_INIT) {
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
if (!filename) {
|
|
return ERROR_PSMFPLAYER_INVALID_PARAM;
|
|
}
|
|
psmfplayer->filehandle = pspFileSystem.OpenFile(filename, (FileAccess) FILEACCESS_READ);
|
|
if (!psmfplayer->filehandle) {
|
|
return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
if (psmfplayer->filehandle && psmfplayer->tempbuf) {
|
|
if (offset != 0)
|
|
pspFileSystem.SeekFile(psmfplayer->filehandle, offset, FILEMOVE_BEGIN);
|
|
u8 *buf = psmfplayer->tempbuf;
|
|
int tempbufSize = (int)sizeof(psmfplayer->tempbuf);
|
|
int size = (int)pspFileSystem.ReadFile(psmfplayer->filehandle, buf, 2048);
|
|
const u32 magic = *(u32_le *)buf;
|
|
if (magic != PSMF_MAGIC) {
|
|
// TODO: Let's keep trying as we were before.
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmf*: incorrect PSMF magic, bad data");
|
|
//return SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT;
|
|
}
|
|
|
|
// TODO: Merge better with Psmf.
|
|
u16 numStreams = *(u16_be *)(buf + 0x80);
|
|
psmfplayer->totalVideoStreams = 0;
|
|
psmfplayer->totalAudioStreams = 0;
|
|
psmfplayer->playerVersion = PSMF_PLAYER_VERSION_FULL;
|
|
for (u16 i = 0; i < numStreams; i++) {
|
|
const u8 *currentStreamAddr = buf + 0x82 + i * 16;
|
|
const int streamId = *currentStreamAddr;
|
|
if ((streamId & PSMF_VIDEO_STREAM_ID) == PSMF_VIDEO_STREAM_ID) {
|
|
++psmfplayer->totalVideoStreams;
|
|
// If we don't have EP info for /any/ video stream, revert to BASIC.
|
|
const u32 epOffset = *(const u32_be *)(currentStreamAddr + 4);
|
|
const u32 epEntries = *(const u32_be *)(currentStreamAddr + 8);
|
|
// TODO: Actually, if these don't match, it seems to be an invalid PSMF.
|
|
if (epOffset == 0 || epEntries == 0) {
|
|
psmfplayer->playerVersion = PSMF_PLAYER_VERSION_BASIC;
|
|
}
|
|
} else if ((streamId & PSMF_AUDIO_STREAM_ID) == PSMF_AUDIO_STREAM_ID) {
|
|
++psmfplayer->totalAudioStreams;
|
|
} else {
|
|
WARN_LOG_REPORT(ME, "scePsmfPlayerSetPsmf*: unexpected streamID %x", streamId);
|
|
}
|
|
}
|
|
// TODO: It seems like it's invalid if there's not at least 1 video stream.
|
|
|
|
int mpegoffset = bswap32(*(u32_le *)(buf + PSMF_STREAM_OFFSET_OFFSET));
|
|
psmfplayer->readSize = size - mpegoffset;
|
|
psmfplayer->streamSize = bswap32(*(u32_le *)(buf + PSMF_STREAM_SIZE_OFFSET));
|
|
psmfplayer->fileoffset = offset + mpegoffset;
|
|
psmfplayer->mediaengine->loadStream(buf, 2048, std::max(2048 * 500, tempbufSize));
|
|
_PsmfPlayerFillRingbuffer(psmfplayer);
|
|
psmfplayer->psmfPlayerLastTimestamp = psmfplayer->mediaengine->getLastTimeStamp();
|
|
}
|
|
|
|
psmfplayer->status = PSMF_PLAYER_STATUS_STANDBY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int scePsmfPlayerSetPsmf(u32 psmfPlayer, const char *filename)
|
|
{
|
|
int result = _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, 0, false);
|
|
if (result == ERROR_PSMFPLAYER_INVALID_STATUS) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmf(%08x, %s): invalid psmf player or status", psmfPlayer, filename);
|
|
} else if (result == ERROR_PSMFPLAYER_INVALID_PARAM) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSetPsmf(%08x, %s): invalid filename", psmfPlayer, filename);
|
|
} else if (result == SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSetPsmf(%08x, %s): invalid file data or does not exist", psmfPlayer, filename);
|
|
} else {
|
|
INFO_LOG(ME, "scePsmfPlayerSetPsmf(%08x, %s)", psmfPlayer, filename);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int scePsmfPlayerSetPsmfCB(u32 psmfPlayer, const char *filename)
|
|
{
|
|
// TODO: hleCheckCurrentCallbacks?
|
|
int result = _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, 0, true);
|
|
if (result == ERROR_PSMFPLAYER_INVALID_STATUS) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmfCB(%08x, %s): invalid psmf player or status", psmfPlayer, filename);
|
|
} else if (result == ERROR_PSMFPLAYER_INVALID_PARAM) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSetPsmfCB(%08x, %s): invalid filename", psmfPlayer, filename);
|
|
} else if (result == SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSetPsmfCB(%08x, %s): invalid file data or does not exist", psmfPlayer, filename);
|
|
} else {
|
|
INFO_LOG(ME, "scePsmfPlayerSetPsmfCB(%08x, %s)", psmfPlayer, filename);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int scePsmfPlayerSetPsmfOffset(u32 psmfPlayer, const char *filename, int offset)
|
|
{
|
|
int result = _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, offset, false);
|
|
if (result == ERROR_PSMFPLAYER_INVALID_STATUS) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmfOffset(%08x, %s): invalid psmf player or status", psmfPlayer, filename);
|
|
} else if (result == ERROR_PSMFPLAYER_INVALID_PARAM) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSetPsmfOffset(%08x, %s): invalid filename", psmfPlayer, filename);
|
|
} else if (result == SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSetPsmfOffset(%08x, %s): invalid file data or does not exist", psmfPlayer, filename);
|
|
} else {
|
|
INFO_LOG(ME, "scePsmfPlayerSetPsmfOffset(%08x, %s)", psmfPlayer, filename);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int scePsmfPlayerSetPsmfOffsetCB(u32 psmfPlayer, const char *filename, int offset)
|
|
{
|
|
// TODO: hleCheckCurrentCallbacks?
|
|
int result = _PsmfPlayerSetPsmfOffset(psmfPlayer, filename, offset, true);
|
|
if (result == ERROR_PSMFPLAYER_INVALID_STATUS) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetPsmfOffsetCB(%08x, %s): invalid psmf player or status", psmfPlayer, filename);
|
|
} else if (result == ERROR_PSMFPLAYER_INVALID_PARAM) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSetPsmfOffsetCB(%08x, %s): invalid filename", psmfPlayer, filename);
|
|
} else if (result == SCE_KERNEL_ERROR_ILLEGAL_ARGUMENT) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSetPsmfOffsetCB(%08x, %s): invalid file data or does not exist", psmfPlayer, filename);
|
|
} else {
|
|
INFO_LOG(ME, "scePsmfPlayerSetPsmfOffsetCB(%08x, %s)", psmfPlayer, filename);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int scePsmfPlayerGetAudioOutSize(u32 psmfPlayer)
|
|
{
|
|
WARN_LOG(ME, "%i = scePsmfPlayerGetAudioOutSize(%08x)", audioSamplesBytes, psmfPlayer);
|
|
return audioSamplesBytes;
|
|
}
|
|
|
|
int scePsmfPlayerStart(u32 psmfPlayer, u32 psmfPlayerData, int initPts)
|
|
{
|
|
WARN_LOG(ME, "scePsmfPlayerStart(%08x, %08x, %08x)", psmfPlayer, psmfPlayerData, initPts);
|
|
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerStart(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
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;
|
|
psmfplayer->psmfPlayerAvcAu.dts = initPts;
|
|
psmfplayer->psmfPlayerAvcAu.pts = initPts;
|
|
|
|
psmfplayer->status = PSMF_PLAYER_STATUS_PLAYING;
|
|
|
|
psmfplayer->mediaengine->openContext();
|
|
return 0;
|
|
}
|
|
|
|
int scePsmfPlayerDelete(u32 psmfPlayer)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerDelete(%08x): invalid psmf player", psmfPlayer);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerDelete(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
INFO_LOG(ME, "scePsmfPlayerDelete(%08x)", psmfPlayer);
|
|
delete psmfplayer;
|
|
psmfPlayerMap.erase(psmfPlayer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int scePsmfPlayerUpdate(u32 psmfPlayer)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerUpdate(%08x): invalid psmf player", psmfPlayer);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerUpdate(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
bool psmfplayerstatus = isPlayingStatus(psmfplayer->status);
|
|
if (!psmfplayerstatus) {
|
|
ERROR_LOG(ME, "scePsmfPlayerUpdate(%08x): psmf not playing", psmfPlayer);
|
|
return ERROR_PSMF_NOT_INITIALIZED;
|
|
}
|
|
|
|
DEBUG_LOG(ME, "scePsmfPlayerUpdate(%08x)", psmfPlayer);
|
|
if (psmfplayer->psmfPlayerAvcAu.pts > 0) {
|
|
if (psmfplayer->mediaengine->IsVideoEnd()) {
|
|
INFO_LOG(ME, "video end reached");
|
|
psmfplayer->status = PSMF_PLAYER_STATUS_PLAYING_FINISHED;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int scePsmfPlayerReleasePsmf(u32 psmfPlayer)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerReleasePsmf(%08x): invalid psmf player", psmfPlayer);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerReleasePsmf(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
WARN_LOG(ME, "scePsmfPlayerReleasePsmf(%08x)", psmfPlayer);
|
|
psmfplayer->status = PSMF_PLAYER_STATUS_INIT;
|
|
return 0;
|
|
}
|
|
|
|
int scePsmfPlayerGetVideoData(u32 psmfPlayer, u32 videoDataAddr)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetVideoData(%08x, %08x): invalid psmf player", psmfPlayer, videoDataAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetVideoData(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
bool psmfplayerstatus = isPlayingStatus(psmfplayer->status);
|
|
if (!psmfplayerstatus) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetVideoData(%08x): psmf not playing", psmfPlayer);
|
|
return ERROR_PSMF_NOT_INITIALIZED;
|
|
}
|
|
|
|
if (psmfplayer->playMode == PSMF_PLAYER_MODE_PAUSE) {
|
|
INFO_LOG(HLE, "scePsmfPlayerGetVideoData(%08x): paused mode", psmfPlayer);
|
|
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(displaybuf, frameWidth, videoPixelMode);
|
|
gpu->InvalidateCache(displaybuf, displaybufSize, GPU_INVALIDATE_SAFE);
|
|
}
|
|
psmfplayer->psmfPlayerAvcAu.pts = psmfplayer->mediaengine->getVideoTimeStamp();
|
|
Memory::Write_U32(psmfplayer->psmfPlayerAvcAu.pts, videoDataAddr + 8);
|
|
}
|
|
|
|
_PsmfPlayerFillRingbuffer(psmfplayer);
|
|
int ret = psmfplayer->mediaengine->IsVideoEnd() ? (int)ERROR_PSMFPLAYER_NO_MORE_DATA : 0;
|
|
|
|
DEBUG_LOG(ME, "%08x=scePsmfPlayerGetVideoData(%08x, %08x)", ret, psmfPlayer, videoDataAddr);
|
|
s64 deltapts = psmfplayer->mediaengine->getVideoTimeStamp() - psmfplayer->mediaengine->getAudioTimeStamp();
|
|
int delaytime = 3000;
|
|
if (deltapts > 0 && !psmfplayer->mediaengine->IsNoAudioData())
|
|
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)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetAudioData(%08x, %08x): invalid psmf player", psmfPlayer, audioDataAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetAudioData(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
bool psmfplayerstatus = isPlayingStatus(psmfplayer->status);
|
|
if (!psmfplayerstatus) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetAudioData(%08x): psmf not playing", psmfPlayer);
|
|
return ERROR_PSMF_NOT_INITIALIZED;
|
|
}
|
|
|
|
if (psmfplayer->playMode == PSMF_PLAYER_MODE_PAUSE) {
|
|
INFO_LOG(HLE, "scePsmfPlayerGetAudioData(%08x): paused mode", psmfPlayer);
|
|
// Clear the audio when paused.
|
|
if (Memory::IsValidAddress(audioDataAddr)) {
|
|
Memory::Memset(audioDataAddr, 0, audioSamplesBytes);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (Memory::IsValidAddress(audioDataAddr)) {
|
|
psmfplayer->mediaengine->getAudioSamples(audioDataAddr);
|
|
}
|
|
|
|
int ret = psmfplayer->mediaengine->IsNoAudioData() ? (int)ERROR_PSMFPLAYER_NO_MORE_DATA : 0;
|
|
DEBUG_LOG(ME, "%08x=scePsmfPlayerGetAudioData(%08x, %08x)", ret, psmfPlayer, audioDataAddr);
|
|
return hleDelayResult(ret, "psmfPlayer audio decode", 3000);
|
|
}
|
|
|
|
int scePsmfPlayerGetCurrentStatus(u32 psmfPlayer)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
// Mana Khemia and other games call this even when not necessary.
|
|
// It's annoying so the logging is verbose'd out.
|
|
VERBOSE_LOG(ME, "scePsmfPlayerGetCurrentStatus(%08x): invalid psmf player", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetCurrentStatus(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
DEBUG_LOG(ME, "%d=scePsmfPlayerGetCurrentStatus(%08x)", psmfplayer->status, psmfPlayer);
|
|
return psmfplayer->status;
|
|
}
|
|
|
|
u32 scePsmfPlayerGetCurrentPts(u32 psmfPlayer, u32 currentPtsAddr)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetCurrentPts(%08x, %08x): invalid psmf player", psmfPlayer, currentPtsAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized || psmfplayer->status < PSMF_PLAYER_STATUS_STANDBY) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetCurrentPts(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
DEBUG_LOG(ME, "scePsmfPlayerGetCurrentPts(%08x, %08x)", psmfPlayer, currentPtsAddr);
|
|
|
|
if (Memory::IsValidAddress(currentPtsAddr)) {
|
|
Memory::Write_U32(psmfplayer->psmfPlayerAvcAu.pts, currentPtsAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfPlayerGetPsmfInfo(u32 psmfPlayer, u32 psmfInfoAddr)
|
|
{
|
|
auto info = PSPPointer<PsmfInfo>::Create(psmfInfoAddr);
|
|
if (!Memory::IsValidAddress(psmfPlayer) || !info.IsValid()) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetPsmfInfo(%08x, %08x): invalid addresses", psmfPlayer, psmfInfoAddr);
|
|
// PSP would crash.
|
|
return SCE_KERNEL_ERROR_ILLEGAL_ADDRESS;
|
|
}
|
|
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetPsmfInfo(%08x, %08x): invalid psmf player", psmfPlayer, psmfInfoAddr);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
if (psmfplayer->status < PSMF_PLAYER_STATUS_STANDBY) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetPsmfInfo(%08x): psmf not set yet", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
DEBUG_LOG(ME, "scePsmfPlayerGetPsmfInfo(%08x, %08x)", psmfPlayer, psmfInfoAddr);
|
|
info->lengthTS = psmfplayer->psmfPlayerLastTimestamp - 3003;
|
|
info->numVideoStreams = psmfplayer->totalVideoStreams;
|
|
info->numAudioStreams = psmfplayer->totalAudioStreams;
|
|
// pcm stream num?
|
|
info->numPCMStreams = 0;
|
|
info->playerVersion = psmfplayer->playerVersion;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfPlayerGetCurrentPlayMode(u32 psmfPlayer, u32 playModeAddr, u32 playSpeedAddr)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetCurrentPlayMode(%08x, %08x, %08x): invalid psmf player", psmfPlayer, playModeAddr, playSpeedAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetCurrentPlayMode(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
WARN_LOG(ME, "scePsmfPlayerGetCurrentPlayMode(%08x, %08x, %08x)", psmfPlayer, playModeAddr, playSpeedAddr);
|
|
if (Memory::IsValidAddress(playModeAddr)) {
|
|
// Fixing for AKB MPEG wrong pointer
|
|
Memory::Write_U32(psmfplayer->playMode, playModeAddr);
|
|
}
|
|
if (Memory::IsValidAddress(playSpeedAddr)) {
|
|
// Fixing for AKB MPEG wrong pointer
|
|
Memory::Write_U32(psmfplayer->playSpeed, playSpeedAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfPlayerGetCurrentVideoStream(u32 psmfPlayer, u32 videoCodecAddr, u32 videoStreamNumAddr)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetCurrentVideoStream(%08x, %08x, %08x): invalid psmf player", psmfPlayer, videoCodecAddr, videoStreamNumAddr);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
if (psmfplayer->status == PSMF_PLAYER_STATUS_INIT) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetCurrentVideoStream(%08x): psmf not yet set", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
DEBUG_LOG(ME, "scePsmfPlayerGetCurrentVideoStream(%08x, %08x, %08x)", psmfPlayer, videoCodecAddr, videoStreamNumAddr);
|
|
if (Memory::IsValidAddress(videoCodecAddr)) {
|
|
Memory::Write_U32(psmfplayer->videoCodec == 0x0E ? 0 : psmfplayer->videoCodec, videoCodecAddr);
|
|
}
|
|
if (Memory::IsValidAddress(videoStreamNumAddr)) {
|
|
Memory::Write_U32(psmfplayer->videoStreamNum, videoStreamNumAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfPlayerGetCurrentAudioStream(u32 psmfPlayer, u32 audioCodecAddr, u32 audioStreamNumAddr)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetCurrentAudioStream(%08x, %08x, %08x): invalid psmf player", psmfPlayer, audioCodecAddr, audioStreamNumAddr);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
if (psmfplayer->status == PSMF_PLAYER_STATUS_INIT) {
|
|
ERROR_LOG(ME, "scePsmfPlayerGetCurrentVideoStream(%08x): psmf not yet set", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
DEBUG_LOG(ME, "scePsmfPlayerGetCurrentAudioStream(%08x, %08x, %08x)", psmfPlayer, audioCodecAddr, audioStreamNumAddr);
|
|
if (Memory::IsValidAddress(audioCodecAddr)) {
|
|
Memory::Write_U32(psmfplayer->audioCodec == 0x0F ? 1 : psmfplayer->audioCodec, audioCodecAddr);
|
|
}
|
|
if (Memory::IsValidAddress(audioStreamNumAddr)) {
|
|
Memory::Write_U32(psmfplayer->audioStreamNum, audioStreamNumAddr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int scePsmfPlayerSetTempBuf(u32 psmfPlayer, u32 tempBufAddr, u32 tempBufSize)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSetTempBuf(%08x, %08x, %08x): invalid psmf player", psmfPlayer, tempBufAddr, tempBufSize);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
if (psmfplayer->status != PSMF_PLAYER_STATUS_INIT) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetTempBuf(%08x, %08x, %08x): invalid status %x", psmfPlayer, tempBufAddr, tempBufSize, psmfplayer->status);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
if (tempBufSize < 0x00010000) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerSetTempBuf(%08x, %08x, %08x): buffer too small", psmfPlayer, tempBufAddr, tempBufSize);
|
|
return ERROR_PSMFPLAYER_INVALID_PARAM;
|
|
}
|
|
|
|
INFO_LOG(ME, "scePsmfPlayerSetTempBuf(%08x, %08x, %08x)", psmfPlayer, tempBufAddr, tempBufSize);
|
|
// fake it right now, use tempbuf from memory directly
|
|
//psmfplayer->tempbuf = tempBufAddr;
|
|
//psmfplayer->tempbufSize = tempBufSize;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfPlayerChangePlayMode(u32 psmfPlayer, int playMode, int playSpeed)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerChangePlayMode(%08x, %i, %i): invalid psmf player", psmfPlayer, playMode, playSpeed);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerChangePlayMode(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
WARN_LOG(ME, "scePsmfPlayerChangePlayMode(%08x, %i, %i)", psmfPlayer, playMode, playSpeed);
|
|
psmfplayer->playMode = playMode;
|
|
psmfplayer->playSpeed = playSpeed;
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfPlayerSelectAudio(u32 psmfPlayer)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSelectAudio(%08x): invalid psmf player", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
if (psmfplayer->status != PSMF_PLAYER_STATUS_PLAYING) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSelectAudio(%08x): not playing", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
int next = psmfplayer->audioStreamNum + 1;
|
|
if (next >= psmfplayer->totalAudioStreams)
|
|
next = 0;
|
|
|
|
if (next == psmfplayer->audioStreamNum || !psmfplayer->mediaengine->setAudioStream(next)) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerSelectAudio(%08x): no stream to switch to", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STREAM;
|
|
}
|
|
|
|
WARN_LOG_REPORT(ME, "scePsmfPlayerSelectAudio(%08x)", psmfPlayer);
|
|
psmfplayer->audioStreamNum = next;
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfPlayerSelectVideo(u32 psmfPlayer)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSelectVideo(%08x): invalid psmf player", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
if (psmfplayer->status != PSMF_PLAYER_STATUS_PLAYING) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSelectVideo(%08x): not playing", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
int next = psmfplayer->videoStreamNum + 1;
|
|
if (next >= psmfplayer->totalVideoStreams)
|
|
next = 0;
|
|
|
|
if (next == psmfplayer->videoStreamNum || !psmfplayer->mediaengine->setVideoStream(next)) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerSelectVideo(%08x): no stream to switch to", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STREAM;
|
|
}
|
|
|
|
WARN_LOG_REPORT(ME, "scePsmfPlayerSelectVideo(%08x)", psmfPlayer);
|
|
psmfplayer->videoStreamNum = next;
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfPlayerSelectSpecificVideo(u32 psmfPlayer, int videoCodec, int videoStreamNum)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSelectSpecificVideo(%08x, %i, %i): invalid psmf player", psmfPlayer, videoCodec, videoStreamNum);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSelectSpecificVideo(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
ERROR_LOG(ME, "scePsmfPlayerSelectSpecificVideo(%08x, %i, %i)", psmfPlayer, videoCodec, videoStreamNum);
|
|
psmfplayer->videoCodec = videoCodec;
|
|
psmfplayer->videoStreamNum = videoStreamNum;
|
|
psmfplayer->mediaengine->setVideoStream(videoStreamNum);
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfPlayerSelectSpecificAudio(u32 psmfPlayer, int audioCodec, int audioStreamNum)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSelectSpecificAudio(%08x, %i, %i): invalid psmf player", psmfPlayer, audioCodec, audioStreamNum);
|
|
return ERROR_PSMF_NOT_FOUND;
|
|
}
|
|
|
|
bool isInitialized = isInitializedStatus(psmfplayer->status);
|
|
if (!isInitialized) {
|
|
ERROR_LOG(ME, "scePsmfPlayerSelectSpecificAudio(%08x): not initialized", psmfPlayer);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
|
|
ERROR_LOG(ME, "scePsmfPlayerSelectSpecificAudio(%08x, %i, %i)", psmfPlayer, audioCodec, audioStreamNum);
|
|
psmfplayer->audioCodec = audioCodec;
|
|
psmfplayer->audioStreamNum = audioStreamNum;
|
|
psmfplayer->mediaengine->setAudioStream(audioStreamNum);
|
|
return 0;
|
|
}
|
|
|
|
u32 scePsmfPlayerConfigPlayer(u32 psmfPlayer, int configMode, int configAttr)
|
|
{
|
|
PsmfPlayer *psmfplayer = getPsmfPlayer(psmfPlayer);
|
|
if (!psmfplayer) {
|
|
ERROR_LOG(ME, "scePsmfPlayerConfigPlayer(%08x, %i, %i): invalid psmf player", psmfPlayer, configMode, configAttr);
|
|
return ERROR_PSMFPLAYER_INVALID_STATUS;
|
|
}
|
|
// This one works in any status as long as it's created.
|
|
|
|
switch (configMode) {
|
|
case PSMF_PLAYER_CONFIG_MODE_LOOP:
|
|
if (configAttr != 0 && configAttr != 1) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerConfigPlayer(%08x, loop, %i): invalid value", psmfPlayer, configAttr);
|
|
return ERROR_PSMFPLAYER_INVALID_PARAM;
|
|
}
|
|
INFO_LOG(ME, "scePsmfPlayerConfigPlayer(%08x, loop, %i)", psmfPlayer, configAttr);
|
|
videoLoopStatus = configAttr;
|
|
break;
|
|
case PSMF_PLAYER_CONFIG_MODE_PIXEL_TYPE:
|
|
if (configAttr < -1 || configAttr > 3) {
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerConfigPlayer(%08x, pixelType, %i): invalid value", psmfPlayer, configAttr);
|
|
return ERROR_PSMFPLAYER_INVALID_PARAM;
|
|
}
|
|
INFO_LOG(ME, "scePsmfPlayerConfigPlayer(%08x, pixelType, %i)", psmfPlayer, configAttr);
|
|
// Does -1 mean default or something?
|
|
if (configAttr != -1) {
|
|
videoPixelMode = configAttr;
|
|
} else {
|
|
// TODO: At least for one video, this was the same as 8888.
|
|
videoPixelMode = GE_CMODE_32BIT_ABGR8888;
|
|
}
|
|
break;
|
|
default:
|
|
ERROR_LOG_REPORT(ME, "scePsmfPlayerConfigPlayer(%08x, %i, %i): unknown parameter", psmfPlayer, configMode, configAttr);
|
|
return ERROR_PSMFPLAYER_INVALID_CONFIG;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const HLEFunction scePsmf[] = {
|
|
{0xc22c8327, WrapU_UU<scePsmfSetPsmf>, "scePsmfSetPsmf"},
|
|
{0xC7DB3A5B, WrapU_UUU<scePsmfGetCurrentStreamType>, "scePsmfGetCurrentStreamType"},
|
|
{0x28240568, WrapU_U<scePsmfGetCurrentStreamNumber>, "scePsmfGetCurrentStreamNumber"},
|
|
{0x1E6D9013, WrapU_UUU<scePsmfSpecifyStreamWithStreamType>, "scePsmfSpecifyStreamWithStreamType"},
|
|
{0x0C120E1D, WrapU_UUU<scePsmfSpecifyStreamWithStreamTypeNumber>, "scePsmfSpecifyStreamWithStreamTypeNumber"},
|
|
{0x4BC9BDE0, WrapU_UI<scePsmfSpecifyStream>, "scePsmfSpecifyStream"},
|
|
{0x76D3AEBA, WrapU_UU<scePsmfGetPresentationStartTime>, "scePsmfGetPresentationStartTime"},
|
|
{0xBD8AE0D8, WrapU_UU<scePsmfGetPresentationEndTime>, "scePsmfGetPresentationEndTime"},
|
|
{0xEAED89CD, WrapU_U<scePsmfGetNumberOfStreams>, "scePsmfGetNumberOfStreams"},
|
|
{0x7491C438, WrapU_U<scePsmfGetNumberOfEPentries>, "scePsmfGetNumberOfEPentries"},
|
|
{0x0BA514E5, WrapU_UU<scePsmfGetVideoInfo>, "scePsmfGetVideoInfo"},
|
|
{0xA83F7113, WrapU_UU<scePsmfGetAudioInfo>, "scePsmfGetAudioInfo"},
|
|
{0x971A3A90, WrapU_U<scePsmfCheckEPMap>, "scePsmfCheckEPmap"},
|
|
{0x68d42328, WrapU_UI<scePsmfGetNumberOfSpecificStreams>, "scePsmfGetNumberOfSpecificStreams"},
|
|
{0x5b70fcc1, WrapU_UU<scePsmfQueryStreamOffset>, "scePsmfQueryStreamOffset"},
|
|
{0x9553cc91, WrapU_UU<scePsmfQueryStreamSize>, "scePsmfQueryStreamSize"},
|
|
{0xB78EB9E9, WrapU_UU<scePsmfGetHeaderSize>, "scePsmfGetHeaderSize"},
|
|
{0xA5EBFE81, WrapU_UU<scePsmfGetStreamSize>, "scePsmfGetStreamSize"},
|
|
{0xE1283895, WrapU_U<scePsmfGetPsmfVersion>, "scePsmfGetPsmfVersion"},
|
|
{0x2673646B, WrapU_U<scePsmfVerifyPsmf>, "scePsmfVerifyPsmf"},
|
|
{0x4E624A34, WrapU_UIU<scePsmfGetEPWithId>, "scePsmfGetEPWithId"},
|
|
{0x7C0E7AC3, WrapU_UUU<scePsmfGetEPWithTimestamp>, "scePsmfGetEPWithTimestamp"},
|
|
{0x5F457515, WrapU_UU<scePsmfGetEPidWithTimestamp>, "scePsmfGetEPidWithTimestamp"},
|
|
{0x43ac7dbb, 0, "scePsmfGetPsmfMark"},
|
|
{0xde78e9fc, 0, "scePsmfGetNumberOfPsmfMarks"},
|
|
};
|
|
|
|
const HLEFunction scePsmfPlayer[] =
|
|
{
|
|
{0x235d8787, WrapI_UU<scePsmfPlayerCreate>, "scePsmfPlayerCreate"},
|
|
{0x1078c008, WrapI_U<scePsmfPlayerStop>, "scePsmfPlayerStop"},
|
|
{0x1e57a8e7, WrapU_UII<scePsmfPlayerConfigPlayer>, "scePsmfPlayerConfigPlayer"},
|
|
{0x2beb1569, WrapI_U<scePsmfPlayerBreak>, "scePsmfPlayerBreak"},
|
|
{0x3d6d25a9, WrapI_UC<scePsmfPlayerSetPsmf>,"scePsmfPlayerSetPsmf"},
|
|
{0x58B83577, WrapI_UC<scePsmfPlayerSetPsmfCB>, "scePsmfPlayerSetPsmfCB"},
|
|
{0x3ea82a4b, WrapI_U<scePsmfPlayerGetAudioOutSize>, "scePsmfPlayerGetAudioOutSize"},
|
|
{0x3ed62233, WrapU_UU<scePsmfPlayerGetCurrentPts>, "scePsmfPlayerGetCurrentPts"},
|
|
{0x46f61f8b, WrapI_UU<scePsmfPlayerGetVideoData>, "scePsmfPlayerGetVideoData"},
|
|
{0x68f07175, WrapU_UUU<scePsmfPlayerGetCurrentAudioStream>, "scePsmfPlayerGetCurrentAudioStream"},
|
|
{0x75f03fa2, WrapU_UII<scePsmfPlayerSelectSpecificVideo>, "scePsmfPlayerSelectSpecificVideo"},
|
|
{0x85461eff, WrapU_UII<scePsmfPlayerSelectSpecificAudio>, "scePsmfPlayerSelectSpecificAudio"},
|
|
{0x8a9ebdcd, WrapU_U<scePsmfPlayerSelectVideo>, "scePsmfPlayerSelectVideo"},
|
|
{0x95a84ee5, WrapI_UUI<scePsmfPlayerStart>, "scePsmfPlayerStart"},
|
|
{0x9b71a274, WrapI_U<scePsmfPlayerDelete>, "scePsmfPlayerDelete"},
|
|
{0x9ff2b2e7, WrapU_UUU<scePsmfPlayerGetCurrentVideoStream>, "scePsmfPlayerGetCurrentVideoStream"},
|
|
{0xa0b8ca55, WrapI_U<scePsmfPlayerUpdate>, "scePsmfPlayerUpdate"},
|
|
{0xa3d81169, WrapU_UII<scePsmfPlayerChangePlayMode>, "scePsmfPlayerChangePlayMode"},
|
|
{0xb8d10c56, WrapU_U<scePsmfPlayerSelectAudio>, "scePsmfPlayerSelectAudio"},
|
|
{0xb9848a74, WrapI_UU<scePsmfPlayerGetAudioData>, "scePsmfPlayerGetAudioData"},
|
|
{0xdf089680, WrapU_UU<scePsmfPlayerGetPsmfInfo>, "scePsmfPlayerGetPsmfInfo"},
|
|
{0xe792cd94, WrapI_U<scePsmfPlayerReleasePsmf>, "scePsmfPlayerReleasePsmf"},
|
|
{0xf3efaa91, WrapU_UUU<scePsmfPlayerGetCurrentPlayMode>, "scePsmfPlayerGetCurrentPlayMode"},
|
|
{0xf8ef08a6, WrapI_U<scePsmfPlayerGetCurrentStatus>, "scePsmfPlayerGetCurrentStatus"},
|
|
{0x2D0E4E0A, WrapI_UUU<scePsmfPlayerSetTempBuf>, "scePsmfPlayerSetTempBuf"},
|
|
{0x76C0F4AE, WrapI_UCI<scePsmfPlayerSetPsmfOffset>, "scePsmfPlayerSetPsmfOffset"},
|
|
{0xA72DB4F9, WrapI_UCI<scePsmfPlayerSetPsmfOffsetCB>, "scePsmfPlayerSetPsmfOffsetCB"},
|
|
{0x340c12cb, 0, "scePsmfPlayer_340C12CB"},
|
|
};
|
|
|
|
void Register_scePsmf() {
|
|
RegisterModule("scePsmf",ARRAY_SIZE(scePsmf),scePsmf);
|
|
}
|
|
|
|
void Register_scePsmfPlayer() {
|
|
RegisterModule("scePsmfPlayer",ARRAY_SIZE(scePsmfPlayer),scePsmfPlayer);
|
|
}
|