From 501f705ba3c279cb583aae1703fe9bf204926092 Mon Sep 17 00:00:00 2001 From: Florent Castelli Date: Sun, 20 Jan 2013 14:30:16 +0100 Subject: [PATCH 01/80] Merge mp3 support from Orphis' old FFMPEG code. Conflicts: CMakeLists.txt Core/HLE/sceAudio.cpp Core/HLE/sceMp3.cpp Core/HLE/sceMpeg.cpp --- CMakeLists.txt | 36 ++++ Core/HLE/sceAudio.cpp | 13 +- Core/HLE/sceMp3.cpp | 370 ++++++++++++++++++++++++++++++------------ Core/HLE/sceMpeg.cpp | 5 + 4 files changed, 315 insertions(+), 109 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c982e1d46e..f8497ea1d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ option(IOS "Set to ON if targeting an iOS device" ${IOS}) option(USING_GLES2 "Set to ON if target device uses OpenGL ES 2.0" ${USING_GLES2}) option(USING_QT_UI "Set to ON if you wish to use the Qt frontend wrapper" ${USING_QT_UI}) option(HEADLESS "Set to OFF to not generate the PPSSPPHeadless target" ${HEADLESS}) +option(USE_FFMPEG "Build with FFMPEG support (beta)" ${USE_FFMPEG}) if(ANDROID) if(NOT ANDROID_ABI) @@ -1036,6 +1037,41 @@ set(NativeAssets assets/ppge_atlas.zim) set(LinkCommon ${CoreLibName} ${CMAKE_THREAD_LIBS_INIT} ${nativeExtraLibs}) +if(USE_FFMPEG AND NOT DEFINED FFMPEG_BUILDDIR) + message(WARNING "FFMPEG_BUILDDIR variable is required to enable FFmpeg. Disabling it.") + unset(USE_FFMPEG) +endif(USE_FFMPEG AND NOT DEFINED FFMPEG_BUILDDIR) + +if(USE_FFMPEG) + include_directories(ffmpeg ${FFMPEG_BUILDDIR}) + + add_library(libavformat STATIC IMPORTED) + set_target_properties(libavformat PROPERTIES IMPORTED_LOCATION ${FFMPEG_BUILDDIR}/libavformat/libavformat.a) + add_library(libavcodec STATIC IMPORTED) + set_target_properties(libavcodec PROPERTIES IMPORTED_LOCATION ${FFMPEG_BUILDDIR}/libavcodec/libavcodec.a) + add_library(libavutil STATIC IMPORTED) + set_target_properties(libavutil PROPERTIES IMPORTED_LOCATION ${FFMPEG_BUILDDIR}/libavutil/libavutil.a) + add_library(libswresample STATIC IMPORTED) + set_target_properties(libswresample PROPERTIES IMPORTED_LOCATION ${FFMPEG_BUILDDIR}/libswresample/libswresample.a) + add_library(libswscale STATIC IMPORTED) + set_target_properties(libswscale PROPERTIES IMPORTED_LOCATION ${FFMPEG_BUILDDIR}/libswscale/libswscale.a) + + SET (FFMPEG_LIBRARIES + libavcodec + libavformat + libavutil + libswresample + libswscale + ) + + if(APPLE) + set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} bz2 "-framework VideoDecodeAcceleration" "-framework CoreVideo") + endif(APPLE) + + set(LinkCommon ${LinkCommon} ${FFMPEG_LIBRARIES}) + add_definitions(-DUSE_FFMPEG) +endif(USE_FFMPEG) + if (TargetBin) if (IOS) add_executable(${TargetBin} MACOSX_BUNDLE ${NativeAppSource}) diff --git a/Core/HLE/sceAudio.cpp b/Core/HLE/sceAudio.cpp index ab2e9819e3..84296be4e0 100644 --- a/Core/HLE/sceAudio.cpp +++ b/Core/HLE/sceAudio.cpp @@ -346,15 +346,16 @@ u32 sceAudioSRCChRelease() { return 0; } -u32 sceAudioSRCOutputBlocking(u32 vol, u32 buf) { - if (vol > 0xFFFF) { - ERROR_LOG(HLE,"sceAudioSRCOutputBlocking(%08x, %08x) - invalid volume", vol, buf); +u32 sceAudioSRCOutputBlocking(u32 volume, u32 buf) { + if (volume > 0xFFFF) { + ERROR_LOG(HLE,"sceAudioSRCOutputBlocking(%08x, %08x) - invalid volume", volume, buf); return SCE_ERROR_AUDIO_INVALID_VOLUME; } - DEBUG_LOG(HLE, "sceAudioSRCOutputBlocking(%08x, %08x)", vol, buf); - chans[src].leftVolume = vol; - chans[src].rightVolume = vol; + chans[src].leftVolume = volume; + chans[src].rightVolume = volume; chans[src].sampleAddress = buf; + // __AudioEnqueue(chans[chanSRC], chanSRC, true); + // return chans[chanSRC].sampleCount; return __AudioEnqueue(chans[src], src, true); } diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 6808a08e2e..049b167746 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -1,46 +1,36 @@ -// 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 +#include "sceMp3.h" #include "HLE.h" #include "../HW/MediaEngine.h" +#ifdef USE_FFMPEG +#ifndef PRId64 +#define PRId64 "%llu" +#endif +// Urgh! Why is this needed? +#ifdef ANDROID +#ifndef UINT64_C +#define UINT64_C(c) (c ## ULL) +#endif +#endif +extern "C" { +#include +#include +#include +#include +#include +} +#endif + static const int MP3_BITRATES[] = {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}; -struct Mp3Context { - Mp3Context() : mediaengine(NULL) {} - ~Mp3Context() { - if (mediaengine != NULL) { - delete mediaengine; - } - } - +struct Mp3Context { void DoState(PointerWrap &p) { p.Do(mp3StreamStart); p.Do(mp3StreamEnd); p.Do(mp3Buf); p.Do(mp3BufSize); p.Do(mp3PcmBuf); - p.Do(mp3BufPendingSize); p.Do(mp3PcmBufSize); - p.Do(mp3InputFileReadPos); - p.Do(mp3InputBufWritePos); - p.Do(mp3InputBufSize); - p.Do(mp3InputFileSize); p.Do(mp3DecodedBytes); p.Do(mp3LoopNum); p.Do(mp3MaxSamples); @@ -52,32 +42,36 @@ struct Mp3Context { p.DoMarker("Mp3Context"); } - u64 mp3StreamStart; - u64 mp3StreamEnd; - u64 mp3StreamPosition; + int mp3StreamStart; + int mp3StreamEnd; u32 mp3Buf; int mp3BufSize; - int mp3BufPendingSize; u32 mp3PcmBuf; int mp3PcmBufSize; - int mp3InputFileReadPos; - int mp3InputBufWritePos; - int mp3InputBufSize; - int mp3InputFileSize; + int readPosition; + + int bufferRead; + int bufferWrite; + int bufferAvailable; + int mp3DecodedBytes; int mp3LoopNum; int mp3MaxSamples; + MediaEngine *mediaengine; int mp3Channels; int mp3Bitrate; int mp3SamplingRate; int mp3Version; - MediaEngine *mediaengine; + AVFormatContext *avformat_context; + AVIOContext *avio_context; + AVCodecContext *decoder_context; + SwrContext *resampler_context; + int audio_stream_index; }; - static std::map mp3Map; static u32 lastMp3Handle = 0; @@ -86,11 +80,13 @@ Mp3Context *getMp3Ctx(u32 mp3) { ERROR_LOG(HLE, "Bad mp3 handle %08x - using last one (%08x) instead", mp3, lastMp3Handle); mp3 = lastMp3Handle; } + if (mp3Map.find(mp3) == mp3Map.end()) return NULL; return mp3Map[mp3]; } +/* MP3 */ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { DEBUG_LOG(HLE, "sceMp3Decode(%08x,%08x)", mp3, outPcmPtr); @@ -101,43 +97,87 @@ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { } // Nothing to decode - if(ctx->mp3BufPendingSize == 0 || ctx->mp3StreamPosition >= ctx->mp3StreamEnd) { - if (ctx->mp3LoopNum == 0) { - return 0; - } else if (ctx->mp3LoopNum > 0) { - --ctx->mp3LoopNum; - } + if (ctx->bufferAvailable == 0 || ctx->readPosition >= ctx->mp3StreamEnd) { + return 0; } + int bytesdecoded = 0; +#ifndef USE_FFMPEG Memory::Memset(ctx->mp3PcmBuf, 0, ctx->mp3PcmBufSize); Memory::Write_U32(ctx->mp3PcmBuf, outPcmPtr); +#else - // TODO: Actually decode the data -#ifdef _DEBUG + AVFrame frame; + AVPacket packet; + int got_frame, ret; + static int audio_frame_count = 0; + + while (bytesdecoded < ctx->mp3PcmBufSize) { + if ((ret = av_read_frame(ctx->avformat_context, &packet)) < 0) + break; + + if (packet.stream_index == ctx->audio_stream_index) { + avcodec_get_frame_defaults(&frame); + got_frame = 0; + ret = avcodec_decode_audio4(ctx->decoder_context, &frame, &got_frame, &packet); + if (ret < 0) { + ERROR_LOG(HLE, "avcodec_decode_audio4: Error decoding audio %d", ret); + continue; + } + if (got_frame) { + char buf[256]; + av_ts_make_time_string(buf, frame.pts, &ctx->decoder_context->time_base); + INFO_LOG(HLE, "audio_frame n:%d nb_samples:%d pts:%s", + audio_frame_count++, frame.nb_samples, buf); + + u8 *audio_dst_data; + int audio_dst_linesize; + + ret = av_samples_alloc(&audio_dst_data, &audio_dst_linesize, frame.channels, frame.nb_samples, (AVSampleFormat)frame.format, 1); + if (ret < 0) { + ERROR_LOG(HLE, "av_samples_alloc: Could not allocate audio buffer %d", ret); + return -1; + } + + int decoded = av_samples_get_buffer_size(NULL, frame.channels, frame.nb_samples, (AVSampleFormat)frame.format, 1); + + u8* out = Memory::GetPointer(ctx->mp3PcmBuf + bytesdecoded); + ret = swr_convert(ctx->resampler_context, &out, frame.nb_samples, (const u8**)frame.extended_data, frame.nb_samples); + if (ret < 0) { + ERROR_LOG(HLE, "swr_convert: Error while converting %d", ret); + return -1; + } + + //av_samples_copy(&audio_dst_data, frame.data, 0, 0, frame.nb_samples, frame.channels, (AVSampleFormat)frame.format); + + //memcpy(Memory::GetPointer(ctx->mp3PcmBuf + bytesdecoded), audio_dst_data, decoded); + bytesdecoded += decoded; + //av_freep(&audio_dst_data[0]); + } + } + av_free_packet(&packet); + } + Memory::Write_U32(ctx->mp3PcmBuf, outPcmPtr); +#endif + + #if 0 && defined(_DEBUG) char fileName[256]; - sprintf(fileName, "%lli.mp3", ctx->mp3StreamPosition); + sprintf(fileName, "out.wav", mp3); - FILE * file = fopen(fileName, "wb"); - if(file) { - if(!Memory::IsValidAddress(ctx->mp3Buf)) { + FILE * file = fopen(fileName, "a+b"); + if (file) { + if (!Memory::IsValidAddress(ctx->mp3Buf)) { ERROR_LOG(HLE, "sceMp3Decode mp3Buf %08X is not a valid address!", ctx->mp3Buf); } - u8 * ptr = Memory::GetPointer(ctx->mp3Buf); - fwrite(ptr, 1, ctx->mp3BufPendingSize, file); - + //u8 * ptr = Memory::GetPointer(ctx->mp3Buf); + fwrite(Memory::GetPointer(ctx->mp3PcmBuf), 1, bytesdecoded, file); + fclose(file); } -#endif + #endif - ctx->mp3StreamPosition += ctx->mp3BufPendingSize; - if(ctx->mp3StreamPosition > ctx->mp3StreamEnd) - ctx->mp3StreamPosition = ctx->mp3StreamEnd; - - // Reset the pending buffer size so the program will know that we need to buffer more data - ctx->mp3BufPendingSize = (ctx->mp3StreamPosition < ctx->mp3StreamEnd)?-1:0; - - return ctx->mp3PcmBufSize; + return bytesdecoded; } int sceMp3ResetPlayPosition(u32 mp3) { @@ -148,8 +188,8 @@ int sceMp3ResetPlayPosition(u32 mp3) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } - ctx->mp3StreamPosition = 0; - ctx->mp3BufPendingSize = -1; + + ctx->readPosition = ctx->mp3StreamStart; return 0; } @@ -161,7 +201,51 @@ int sceMp3CheckStreamDataNeeded(u32 mp3) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } - return (ctx->mp3BufPendingSize < 0) && (ctx->mp3StreamPosition < ctx->mp3StreamEnd); + + return ctx->bufferAvailable != ctx->mp3BufSize && ctx->readPosition < ctx->mp3StreamEnd; +} + +int readFunc(void *opaque, uint8_t *buf, int buf_size) { + Mp3Context *ctx = static_cast(opaque); + + int res = 0; + while (ctx->bufferAvailable && buf_size) { + // Maximum bytes we can read + int to_read = std::min(ctx->bufferAvailable, buf_size); + + // Don't read past the end if the buffer loops + to_read = std::min(ctx->mp3BufSize - ctx->bufferRead, to_read); + memcpy(buf + res, Memory::GetCharPointer(ctx->mp3Buf + ctx->bufferRead), to_read); + + ctx->bufferRead += to_read; + if (ctx->bufferRead == ctx->mp3BufSize) + ctx->bufferRead = 0; + ctx->bufferAvailable -= to_read; + res += to_read; + } + + if (ctx->bufferAvailable == 0) { + ctx->bufferRead = 0; + ctx->bufferWrite = 0; + } + +#if 0 && defined(_DEBUG) + char fileName[256]; + sprintf(fileName, "out.mp3"); + + FILE * file = fopen(fileName, "a+b"); + if (file) { + if (!Memory::IsValidAddress(ctx->mp3Buf)) { + ERROR_LOG(HLE, "sceMp3Decode mp3Buf %08X is not a valid address!", ctx->mp3Buf); + } + + fwrite(buf, 1, res, file); + + fclose(file); + } +#endif + + return res; } u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { @@ -177,27 +261,29 @@ u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { ctx->mp3PcmBuf = Memory::Read_U32(mp3Addr+24); ctx->mp3PcmBufSize = Memory::Read_U32(mp3Addr+28); - ctx->mp3StreamPosition = ctx->mp3StreamStart; - ctx->mp3BufPendingSize = -1; + ctx->readPosition = ctx->mp3StreamStart; ctx->mp3MaxSamples = ctx->mp3PcmBufSize / 4 ; /*ctx->mp3Channels = 2; ctx->mp3Bitrate = 128; ctx->mp3SamplingRate = 44100;*/ + ctx->avformat_context = NULL; + ctx->avio_context = NULL; + mp3Map[mp3Addr] = ctx; return mp3Addr; } int sceMp3InitResource() { WARN_LOG(HLE, "UNIML: sceMp3InitResource"); - // Do nothing here + // Do nothing here return 0; } int sceMp3TermResource() { WARN_LOG(HLE, "UNIML: sceMp3TermResource"); - // Do nothing here + // Do nothing here return 0; } @@ -213,36 +299,90 @@ int sceMp3Init(u32 mp3) { // Read in the header and swap the endian int header = Memory::Read_U32(ctx->mp3Buf); header = (header >> 24) | - ((header<<8) & 0x00FF0000) | - ((header>>8) & 0x0000FF00) | - (header << 24); + ((header<<8) & 0x00FF0000) | + ((header>>8) & 0x0000FF00) | + (header << 24); int channels = ((header >> 6) & 0x3); - if(channels == 0 || channels == 1 || channels == 2) + if (channels == 0 || channels == 1 || channels == 2) ctx->mp3Channels = 2; - else if(channels == 3) + else if (channels == 3) ctx->mp3Channels = 1; - else + else ctx->mp3Channels = 0; // 0 == VBR int bitrate = ((header >> 10) & 0x3); - if(bitrate < (int)ARRAY_SIZE(MP3_BITRATES)) + if (bitrate < sizeof(MP3_BITRATES) / sizeof(MP3_BITRATES[0])) ctx->mp3Bitrate = MP3_BITRATES[bitrate]; else ctx->mp3Bitrate = -1; int samplerate = ((header >> 12) & 0x3); - if (samplerate == 0) + if (samplerate == 0) { ctx->mp3SamplingRate = 44100; - else if (samplerate == 1) + } else if (samplerate == 1) { ctx->mp3SamplingRate = 48000; - else if (samplerate == 2) + } else if (samplerate == 2) { ctx->mp3SamplingRate = 32000; - else + } else { ctx->mp3SamplingRate = 0; - + } + ctx->mp3Version = ((header >> 19) & 0x3); + + u8* avio_buffer = static_cast(av_malloc(ctx->mp3BufSize)); + ctx->avio_context = avio_alloc_context(avio_buffer, ctx->mp3BufSize, 0, ctx, readFunc, NULL, NULL); + ctx->avformat_context = avformat_alloc_context(); + ctx->avformat_context->pb = ctx->avio_context; + + int ret; + if ((ret = avformat_open_input(&ctx->avformat_context, NULL, av_find_input_format("mp3"), NULL)) < 0) { + ERROR_LOG(HLE, "avformat_open_input: Cannot open input %d", ret); + return -1; + } + + if ((ret = avformat_find_stream_info(ctx->avformat_context, NULL)) < 0) { + ERROR_LOG(HLE, "avformat_find_stream_info: Cannot find stream information %d", ret); + return -1; + } + + AVCodec *dec; + + /* select the audio stream */ + ret = av_find_best_stream(ctx->avformat_context, AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0); + if (ret < 0) { + ERROR_LOG(HLE, "av_find_best_stream: Cannot find a audio stream in the input file %d", ret); + return -1; + } + ctx->audio_stream_index = ret; + ctx->decoder_context = ctx->avformat_context->streams[ctx->audio_stream_index]->codec; + + /* init the audio decoder */ + if ((ret = avcodec_open2(ctx->decoder_context, dec, NULL)) < 0) { + ERROR_LOG(HLE, "avcodec_open2: Cannot open audio decoder %d", ret); + return -1; + } + + ctx->resampler_context = swr_alloc_set_opts(NULL, + ctx->decoder_context->channel_layout, + AV_SAMPLE_FMT_S16, + ctx->decoder_context->sample_rate, + ctx->decoder_context->channel_layout, + ctx->decoder_context->sample_fmt, + ctx->decoder_context->sample_rate, + 0, NULL); + if (!ctx->resampler_context) { + ERROR_LOG(HLE, "Could not allocate resampler context %d", ret); + return -1; + } + + if ((ret = swr_init(ctx->resampler_context)) < 0) { + ERROR_LOG(HLE, "Failed to initialize the resampling context %d", ret); + return -1; + } + + av_dump_format(ctx->avformat_context, 0, "mp3", 0); return 0; } @@ -253,16 +393,19 @@ int sceMp3GetLoopNum(u32 mp3) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } + return ctx->mp3LoopNum; } -int sceMp3GetMaxOutputSample(u32 mp3) { +int sceMp3GetMaxOutputSample(u32 mp3) +{ DEBUG_LOG(HLE, "sceMp3GetMaxOutputSample(%08x)", mp3); Mp3Context *ctx = getMp3Ctx(mp3); if (!ctx) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } + return ctx->mp3MaxSamples; } @@ -274,7 +417,13 @@ int sceMp3NotifyAddStreamData(u32 mp3, int size) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } - ctx->mp3BufPendingSize = size; + + ctx->readPosition += size; + ctx->bufferAvailable += size; + ctx->bufferWrite += size; + if (ctx->bufferWrite == ctx->mp3BufSize) + ctx->bufferWrite = 0; + return 0; } @@ -291,10 +440,11 @@ int sceMp3SetLoopNum(u32 mp3, int loop) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } + ctx->mp3LoopNum = loop; + return 0; } - int sceMp3GetMp3ChannelNum(u32 mp3) { DEBUG_LOG(HLE, "sceMp3GetMp3ChannelNum(%08X)", mp3); @@ -303,9 +453,9 @@ int sceMp3GetMp3ChannelNum(u32 mp3) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } + return ctx->mp3Channels; } - int sceMp3GetBitRate(u32 mp3) { DEBUG_LOG(HLE, "sceMp3GetBitRate(%08X)", mp3); @@ -314,9 +464,9 @@ int sceMp3GetBitRate(u32 mp3) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } + return ctx->mp3Bitrate; } - int sceMp3GetSamplingRate(u32 mp3) { DEBUG_LOG(HLE, "sceMp3GetSamplingRate(%08X)", mp3); @@ -325,26 +475,36 @@ int sceMp3GetSamplingRate(u32 mp3) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } + return ctx->mp3SamplingRate; } - int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcposPtr) { DEBUG_LOG(HLE, "HACK: sceMp3GetInfoToAddStreamData(%08X, %08X, %08X, %08X)", mp3, dstPtr, towritePtr, srcposPtr); - + Mp3Context *ctx = getMp3Ctx(mp3); if (!ctx) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } - if(Memory::IsValidAddress(dstPtr)) - Memory::Write_U32(ctx->mp3Buf, dstPtr); - if(Memory::IsValidAddress(towritePtr)) - Memory::Write_U32(ctx->mp3BufSize, towritePtr); - if(Memory::IsValidAddress(srcposPtr)) - Memory::Write_U32((u32)ctx->mp3StreamPosition, srcposPtr); + + u32 buf, max_write; + if (ctx->readPosition < ctx->mp3StreamEnd) { + buf = ctx->mp3Buf; + max_write = std::min(ctx->mp3BufSize - ctx->bufferWrite, ctx->mp3BufSize - ctx->bufferAvailable); + } else { + buf = 0; + max_write = 0; + } + + if (Memory::IsValidAddress(dstPtr)) + Memory::Write_U32(buf, dstPtr); + if (Memory::IsValidAddress(towritePtr)) + Memory::Write_U32(max_write, towritePtr); + if (Memory::IsValidAddress(srcposPtr)) + Memory::Write_U32(ctx->readPosition, srcposPtr); + return 0; } - int sceMp3ReleaseMp3Handle(u32 mp3) { DEBUG_LOG(HLE, "sceMp3ReleaseMp3Handle(%08X)", mp3); @@ -353,8 +513,13 @@ int sceMp3ReleaseMp3Handle(u32 mp3) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } + + av_free(ctx->avio_context->buffer); + av_free(ctx->avio_context); mp3Map.erase(mp3Map.find(mp3)); + delete ctx; + return 0; } @@ -380,11 +545,11 @@ u32 sceMp3GetVersion(u32 mp3) { ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); return -1; } + return ctx->mp3Version; } -const HLEFunction sceMp3[] = -{ +const HLEFunction sceMp3[] = { {0x07EC321A,WrapU_U,"sceMp3ReserveMp3Handle"}, {0x0DB149F4,WrapI_UI,"sceMp3NotifyAddStreamData"}, {0x2A368661,WrapI_U,"sceMp3ResetPlayPosition"}, @@ -408,7 +573,6 @@ const HLEFunction sceMp3[] = {0x3548AEC8,WrapU_U,"sceMp3GetFrameNum"}, }; -void Register_sceMp3() -{ +void Register_sceMp3() { RegisterModule("sceMp3", ARRAY_SIZE(sceMp3), sceMp3); -} +} \ No newline at end of file diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 8fb8f9bf93..e544c8bf82 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -304,6 +304,11 @@ void __MpegInit(bool useMediaEngine_) { fakeMode = !useMediaEngine_; isCurrentMpegAnalyzed = false; actionPostPut = __KernelRegisterActionType(PostPutAction::Create); + +#ifdef USING_FFMPEG + avcodec_register_all(); + av_register_all(); +#endif } void __MpegDoState(PointerWrap &p) { From e65390184ecda0b0a4bdbdc702cbec3c56d7ae16 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 12 May 2013 19:48:12 +0200 Subject: [PATCH 02/80] Uncomment some unused stack-corrupting code from sceMp3Decode. Update ffmpeg. --- Core/HLE/sceMp3.cpp | 13 +++++++------ ffmpeg | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 049b167746..68729aa551 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -107,8 +107,8 @@ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { Memory::Write_U32(ctx->mp3PcmBuf, outPcmPtr); #else - AVFrame frame; - AVPacket packet; + AVFrame frame = {0}; + AVPacket packet = {0}; int got_frame, ret; static int audio_frame_count = 0; @@ -125,11 +125,11 @@ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { continue; } if (got_frame) { - char buf[256]; + char buf[1024] = ""; av_ts_make_time_string(buf, frame.pts, &ctx->decoder_context->time_base); - INFO_LOG(HLE, "audio_frame n:%d nb_samples:%d pts:%s", - audio_frame_count++, frame.nb_samples, buf); + INFO_LOG(HLE, "audio_frame n:%d nb_samples:%d pts:%s", audio_frame_count++, frame.nb_samples, buf); + /* u8 *audio_dst_data; int audio_dst_linesize; @@ -138,6 +138,7 @@ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { ERROR_LOG(HLE, "av_samples_alloc: Could not allocate audio buffer %d", ret); return -1; } + */ int decoded = av_samples_get_buffer_size(NULL, frame.channels, frame.nb_samples, (AVSampleFormat)frame.format, 1); @@ -152,7 +153,7 @@ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { //memcpy(Memory::GetPointer(ctx->mp3PcmBuf + bytesdecoded), audio_dst_data, decoded); bytesdecoded += decoded; - //av_freep(&audio_dst_data[0]); + // av_freep(&audio_dst_data[0]); } } av_free_packet(&packet); diff --git a/ffmpeg b/ffmpeg index f667ccddff..fe9c6b483d 160000 --- a/ffmpeg +++ b/ffmpeg @@ -1 +1 @@ -Subproject commit f667ccddffbfa09f946bcc9783d6fbfcb665e631 +Subproject commit fe9c6b483d42c2e481deea97fb6a382c050c10f2 From 0e1b6d8488c67de2b0ae6de658d8ca3d39921920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 12 May 2013 20:13:07 +0200 Subject: [PATCH 03/80] Fix sceMp3 build without USE_FFMPEG --- Core/HLE/sceMp3.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 68729aa551..6d19ec6e7d 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -64,12 +64,13 @@ struct Mp3Context { int mp3Bitrate; int mp3SamplingRate; int mp3Version; - +#ifdef USE_FFMPEG AVFormatContext *avformat_context; AVIOContext *avio_context; AVCodecContext *decoder_context; SwrContext *resampler_context; int audio_stream_index; +#endif }; static std::map mp3Map; @@ -269,8 +270,10 @@ u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { ctx->mp3Bitrate = 128; ctx->mp3SamplingRate = 44100;*/ +#ifdef USE_FFMPEG ctx->avformat_context = NULL; ctx->avio_context = NULL; +#endif mp3Map[mp3Addr] = ctx; return mp3Addr; @@ -332,6 +335,7 @@ int sceMp3Init(u32 mp3) { ctx->mp3Version = ((header >> 19) & 0x3); +#ifdef USE_FFMPEG u8* avio_buffer = static_cast(av_malloc(ctx->mp3BufSize)); ctx->avio_context = avio_alloc_context(avio_buffer, ctx->mp3BufSize, 0, ctx, readFunc, NULL, NULL); ctx->avformat_context = avformat_alloc_context(); @@ -384,6 +388,8 @@ int sceMp3Init(u32 mp3) { } av_dump_format(ctx->avformat_context, 0, "mp3", 0); +#endif + return 0; } @@ -515,8 +521,10 @@ int sceMp3ReleaseMp3Handle(u32 mp3) { return -1; } +#ifdef USE_FFMPEG av_free(ctx->avio_context->buffer); av_free(ctx->avio_context); +#endif mp3Map.erase(mp3Map.find(mp3)); delete ctx; @@ -576,4 +584,4 @@ const HLEFunction sceMp3[] = { void Register_sceMp3() { RegisterModule("sceMp3", ARRAY_SIZE(sceMp3), sceMp3); -} \ No newline at end of file +} From 780e252059f340bdd3bfa140fbd14ffb70615405 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 12 May 2013 11:41:31 -0700 Subject: [PATCH 04/80] Fix crash in mp3 reading. --- Core/HLE/sceMp3.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 6d19ec6e7d..280e918b86 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -223,6 +223,7 @@ int readFunc(void *opaque, uint8_t *buf, int buf_size) { if (ctx->bufferRead == ctx->mp3BufSize) ctx->bufferRead = 0; ctx->bufferAvailable -= to_read; + buf_size -= to_read; res += to_read; } From f864e81e72aa4057b5f206250056c8466c7c98aa Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 12 May 2013 22:29:43 +0200 Subject: [PATCH 05/80] Get mp3 sample rate and channels from FFMPEG instead of trying to detect ourselves. --- Core/HLE/sceMp3.cpp | 76 ++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 46 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 280e918b86..e7a34a9261 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -267,9 +267,9 @@ u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { ctx->readPosition = ctx->mp3StreamStart; ctx->mp3MaxSamples = ctx->mp3PcmBufSize / 4 ; - /*ctx->mp3Channels = 2; + ctx->mp3Channels = 2; ctx->mp3Bitrate = 128; - ctx->mp3SamplingRate = 44100;*/ + ctx->mp3SamplingRate = 44100; #ifdef USE_FFMPEG ctx->avformat_context = NULL; @@ -308,32 +308,6 @@ int sceMp3Init(u32 mp3) { ((header>>8) & 0x0000FF00) | (header << 24); - int channels = ((header >> 6) & 0x3); - if (channels == 0 || channels == 1 || channels == 2) - ctx->mp3Channels = 2; - else if (channels == 3) - ctx->mp3Channels = 1; - else - ctx->mp3Channels = 0; - - // 0 == VBR - int bitrate = ((header >> 10) & 0x3); - if (bitrate < sizeof(MP3_BITRATES) / sizeof(MP3_BITRATES[0])) - ctx->mp3Bitrate = MP3_BITRATES[bitrate]; - else - ctx->mp3Bitrate = -1; - - int samplerate = ((header >> 12) & 0x3); - if (samplerate == 0) { - ctx->mp3SamplingRate = 44100; - } else if (samplerate == 1) { - ctx->mp3SamplingRate = 48000; - } else if (samplerate == 2) { - ctx->mp3SamplingRate = 32000; - } else { - ctx->mp3SamplingRate = 0; - } - ctx->mp3Version = ((header >> 19) & 0x3); #ifdef USE_FFMPEG @@ -378,6 +352,13 @@ int sceMp3Init(u32 mp3) { ctx->decoder_context->sample_fmt, ctx->decoder_context->sample_rate, 0, NULL); + + // Let's just grab this info from FFMPEG, it seems more reliable than the code above. + + ctx->mp3SamplingRate = ctx->decoder_context->sample_rate; + ctx->mp3Channels = ctx->decoder_context->channels; + ctx->mp3Bitrate = ctx->decoder_context->bit_rate; + if (!ctx->resampler_context) { ERROR_LOG(HLE, "Could not allocate resampler context %d", ret); return -1; @@ -417,24 +398,6 @@ int sceMp3GetMaxOutputSample(u32 mp3) return ctx->mp3MaxSamples; } -int sceMp3NotifyAddStreamData(u32 mp3, int size) { - DEBUG_LOG(HLE, "sceMp3NotifyAddStreamData(%08X, %i)", mp3, size); - - Mp3Context *ctx = getMp3Ctx(mp3); - if (!ctx) { - ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; - } - - ctx->readPosition += size; - ctx->bufferAvailable += size; - ctx->bufferWrite += size; - if (ctx->bufferWrite == ctx->mp3BufSize) - ctx->bufferWrite = 0; - - return 0; -} - int sceMp3GetSumDecodedSample(u32 mp3) { DEBUG_LOG(HLE, "UNIMPL: sceMp3GetSumDecodedSample(%08X)", mp3); return 0; @@ -486,6 +449,7 @@ int sceMp3GetSamplingRate(u32 mp3) { return ctx->mp3SamplingRate; } + int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcposPtr) { DEBUG_LOG(HLE, "HACK: sceMp3GetInfoToAddStreamData(%08X, %08X, %08X, %08X)", mp3, dstPtr, towritePtr, srcposPtr); @@ -513,6 +477,26 @@ int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcpos return 0; } + +int sceMp3NotifyAddStreamData(u32 mp3, int size) { + DEBUG_LOG(HLE, "sceMp3NotifyAddStreamData(%08X, %i)", mp3, size); + + Mp3Context *ctx = getMp3Ctx(mp3); + if (!ctx) { + ERROR_LOG(HLE, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); + return -1; + } + + ctx->readPosition += size; + ctx->bufferAvailable += size; + ctx->bufferWrite += size; + + if (ctx->bufferWrite == ctx->mp3BufSize) + ctx->bufferWrite = 0; + + return 0; +} + int sceMp3ReleaseMp3Handle(u32 mp3) { DEBUG_LOG(HLE, "sceMp3ReleaseMp3Handle(%08X)", mp3); From b2d57ff3653b6f86af187ac4a481d7bbdfb19cb8 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Mon, 13 May 2013 19:25:33 +0200 Subject: [PATCH 06/80] VERBOSE a log statement --- Core/FileSystems/MetaFileSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index 4393f406c1..f8958487a6 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -200,7 +200,7 @@ bool MetaFileSystem::MapFilePath(const std::string &_inpath, std::string &outpat outpath = realpath.substr(prefLen); *system = &(fileSystems[i]); - DEBUG_LOG(HLE, "MapFilePath: mapped \"%s\" to prefix: \"%s\", path: \"%s\"", inpath.c_str(), fileSystems[i].prefix.c_str(), outpath.c_str()); + VERBOSE_LOG(HLE, "MapFilePath: mapped \"%s\" to prefix: \"%s\", path: \"%s\"", inpath.c_str(), fileSystems[i].prefix.c_str(), outpath.c_str()); return true; } From de4d970ab60d5ab062aad3cffabff8a46633d8cd Mon Sep 17 00:00:00 2001 From: raven02 Date: Wed, 19 Jun 2013 13:08:29 +0800 Subject: [PATCH 07/80] Toggle to adjust VPS/FPS/Both , new timeFormat/dateFormat/buttonPerference --- Core/Config.cpp | 12 ++-- Core/Config.h | 7 +- Core/Dialog/PSPSaveDialog.cpp | 55 +++++++++++----- Core/HLE/sceDisplay.cpp | 2 +- Core/HLE/sceUtility.cpp | 4 +- Core/Reporting.cpp | 2 +- UI/EmuScreen.cpp | 11 +++- UI/MenuScreens.cpp | 119 +++++++++++++++++++++++++++++----- Windows/WndMainWindow.cpp | 4 +- headless/Headless.cpp | 4 +- 10 files changed, 168 insertions(+), 52 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index dc273895b3..93461ec9a8 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -87,7 +87,7 @@ void Config::Load(const char *iniFileName) cpu->Get("FastMemory", &bFastMemory, false); IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics"); - graphics->Get("ShowFPSCounter", &bShowFPSCounter, false); + graphics->Get("ShowFPSCounter", &iShowFPSCounter, false); graphics->Get("DisplayFramebuffer", &bDisplayFramebuffer, false); #ifdef _WIN32 graphics->Get("ResolutionScale", &iWindowZoom, 2); @@ -143,11 +143,11 @@ void Config::Load(const char *iniFileName) IniFile::Section *pspConfig = iniFile.GetOrCreateSection("SystemParam"); pspConfig->Get("NickName", &sNickName, "shadow"); pspConfig->Get("Language", &ilanguage, PSP_SYSTEMPARAM_LANGUAGE_ENGLISH); - pspConfig->Get("TimeFormat", &itimeformat, PSP_SYSTEMPARAM_TIME_FORMAT_24HR); + pspConfig->Get("TimeFormat", &iTimeFormat, PSP_SYSTEMPARAM_TIME_FORMAT_24HR); pspConfig->Get("DateFormat", &iDateFormat, PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD); pspConfig->Get("TimeZone", &iTimeZone, 0); pspConfig->Get("DayLightSavings", &bDayLightSavings, PSP_SYSTEMPARAM_DAYLIGHTSAVINGS_STD); - pspConfig->Get("ButtonPreference", &bButtonPreference, PSP_SYSTEMPARAM_BUTTON_CROSS); + pspConfig->Get("ButtonPreference", &iButtonPreference, PSP_SYSTEMPARAM_BUTTON_CROSS); pspConfig->Get("LockParentalLevel", &iLockParentalLevel, 0); pspConfig->Get("WlanAdhocChannel", &iWlanAdhocChannel, PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC); pspConfig->Get("WlanPowerSave", &bWlanPowerSave, PSP_SYSTEMPARAM_WLAN_POWERSAVE_OFF); @@ -196,7 +196,7 @@ void Config::Save() cpu->Set("FastMemory", bFastMemory); IniFile::Section *graphics = iniFile.GetOrCreateSection("Graphics"); - graphics->Set("ShowFPSCounter", bShowFPSCounter); + graphics->Set("ShowFPSCounter", iShowFPSCounter); graphics->Set("DisplayFramebuffer", bDisplayFramebuffer); graphics->Set("ResolutionScale", iWindowZoom); graphics->Set("BufferedRendering", bBufferedRendering); @@ -238,11 +238,11 @@ void Config::Save() IniFile::Section *pspConfig = iniFile.GetOrCreateSection("SystemParam"); pspConfig->Set("NickName", sNickName.c_str()); pspConfig->Set("Language", ilanguage); - pspConfig->Set("TimeFormat", itimeformat); + pspConfig->Set("TimeFormat", iTimeFormat); pspConfig->Set("DateFormat", iDateFormat); pspConfig->Set("TimeZone", iTimeZone); pspConfig->Set("DayLightSavings", bDayLightSavings); - pspConfig->Set("ButtonPreference", bButtonPreference); + pspConfig->Set("ButtonPreference", iButtonPreference); pspConfig->Set("LockParentalLevel", iLockParentalLevel); pspConfig->Set("WlanAdhocChannel", iWlanAdhocChannel); pspConfig->Set("WlanPowerSave", bWlanPowerSave); diff --git a/Core/Config.h b/Core/Config.h index 7fbd525e65..a63e571c46 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -95,7 +95,7 @@ public: bool bShowTouchControls; bool bShowDebuggerOnLoad; bool bShowAnalogStick; - bool bShowFPSCounter; + int iShowFPSCounter; bool bShowDebugStats; bool bLargeControls; bool bAccelerometerToAnalogHoriz; @@ -115,18 +115,19 @@ public: // SystemParam std::string sNickName; int ilanguage; - int itimeformat; + int iTimeFormat; int iDateFormat; int iTimeZone; bool bDayLightSavings; bool bButtonPreference; + int iButtonPreference; int iLockParentalLevel; bool bEncryptSave; int iWlanAdhocChannel; bool bWlanPowerSave; std::string currentDirectory; - std::string externalDirectory; + std::string externalDirectory; std::string memCardDirectory; std::string flashDirectory; std::string internalDataDirectory; diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index 1c1a1b8023..23d32df181 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -356,14 +356,24 @@ void PSPSaveDialog::DisplaySaveDataInfo1() 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 == PSP_SYSTEMPARAM_TIME_FORMAT_12HR) { + switch (g_Config.iTimeFormat) { + case 1: if (hour > 12) { strcpy(am_pm, "PM"); hour -= 12; } snprintf(hour_time,10,"%02d:%02d %s", hour, min, am_pm); - } else - snprintf(hour_time,10,"%02d:%02d", hour, min); + break; + case 2: + snprintf(hour_time,10,"%02d:%02d", hour, min); + break; + default: + if (hour > 12) { + strcpy(am_pm, "PM"); + hour -= 12; + } + snprintf(hour_time,10,"%02d:%02d %s", hour, min, am_pm); + } snprintf(title, 512, "%s", param.GetFileInfo(currentSelectedSave).title); int day = param.GetFileInfo(currentSelectedSave).modif_time.tm_mday; @@ -371,14 +381,15 @@ void PSPSaveDialog::DisplaySaveDataInfo1() 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); + case 1: + snprintf(time, 512, "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK); break; - case PSP_SYSTEMPARAM_DATE_FORMAT_MMDDYYYY: + case 2: snprintf(time, 512, "%02d/%02d/%d %s %lld KB", month, day, year, hour_time, sizeK); break; - case PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD: - // fall through + case 3: + snprintf(time, 512, "%02d/%02d/%d %s %lld KB", day, month, year, hour_time, sizeK); + break; default: snprintf(time, 512, "%d/%02d/%02d %s %lld KB", year, month, day, hour_time, sizeK); } @@ -408,14 +419,24 @@ void PSPSaveDialog::DisplaySaveDataInfo2() 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 == PSP_SYSTEMPARAM_TIME_FORMAT_12HR) { + switch (g_Config.iTimeFormat) { + case 1: if (hour > 12) { strcpy(am_pm, "PM"); hour -= 12; } snprintf(hour_time,10,"%02d:%02d %s", hour, min, am_pm); - } else - snprintf(hour_time,10,"%02d:%02d", hour, min); + break; + case 2: + snprintf(hour_time,10,"%02d:%02d", hour, min); + break; + default: + if (hour > 12) { + strcpy(am_pm, "PM"); + hour -= 12; + } + snprintf(hour_time,10,"%02d:%02d %s", hour, min, am_pm); + } const char *saveTitle = param.GetFileInfo(currentSelectedSave).saveTitle; int day = param.GetFileInfo(currentSelectedSave).modif_time.tm_mday; @@ -423,14 +444,14 @@ void PSPSaveDialog::DisplaySaveDataInfo2() 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: + case 1: + snprintf(date, 256, "%d/%02d/%02d", year, month, day); + case 2: snprintf(date, 256, "%02d/%02d/%d", month, day, year); break; - case PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD: - // fall through + case 3: + snprintf(date, 256, "%02d/%02d/%d", day, month, year); + break; default: snprintf(date, 256, "%d/%02d/%02d", year, month, day); } diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 7f7688deff..6a0fbe358e 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -426,7 +426,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { gpuStats.numFrames++; - if (g_Config.bShowFPSCounter) { + if (g_Config.iShowFPSCounter) { CalculateFPS(); } diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index 8253876bff..e254cf973b 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -461,7 +461,7 @@ u32 sceUtilityGetSystemParamInt(u32 id, u32 destaddr) param = g_Config.iDateFormat; break; case PSP_SYSTEMPARAM_ID_INT_TIME_FORMAT: - param = g_Config.itimeformat?PSP_SYSTEMPARAM_TIME_FORMAT_12HR:PSP_SYSTEMPARAM_TIME_FORMAT_24HR; + param = g_Config.iTimeFormat?PSP_SYSTEMPARAM_TIME_FORMAT_12HR:PSP_SYSTEMPARAM_TIME_FORMAT_24HR; break; case PSP_SYSTEMPARAM_ID_INT_TIMEZONE: param = g_Config.iTimeZone; @@ -473,7 +473,7 @@ u32 sceUtilityGetSystemParamInt(u32 id, u32 destaddr) param = g_Config.ilanguage; break; case PSP_SYSTEMPARAM_ID_INT_BUTTON_PREFERENCE: - param = g_Config.bButtonPreference?PSP_SYSTEMPARAM_BUTTON_CROSS:PSP_SYSTEMPARAM_BUTTON_CIRCLE; + param = g_Config.iButtonPreference?PSP_SYSTEMPARAM_BUTTON_CROSS:PSP_SYSTEMPARAM_BUTTON_CIRCLE; break; case PSP_SYSTEMPARAM_ID_INT_LOCK_PARENTAL_LEVEL: param = g_Config.iLockParentalLevel; diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index 8cbabbed25..32f22b8d17 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -211,7 +211,7 @@ namespace Reporting postdata.Add("pixel_height", PSP_CoreParameter().pixelHeight); postdata.Add("ticks", (const uint64_t)CoreTiming::GetTicks()); - if (g_Config.bShowFPSCounter) + if (g_Config.iShowFPSCounter) { float vps, fps; __DisplayGetAveragedFPS(&vps, &fps); diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 5ce338c37d..e461ab91ed 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -337,11 +337,18 @@ void EmuScreen::render() { ui_draw2d.SetFontScale(1.0f, 1.0f); } - if (g_Config.bShowFPSCounter) { + if (g_Config.iShowFPSCounter) { float vps, fps; __DisplayGetFPS(&vps, &fps); char fpsbuf[256]; - sprintf(fpsbuf, "VPS: %0.1f\nFPS: %0.1f", vps, fps); + switch (g_Config.iShowFPSCounter) { + case 2: + sprintf(fpsbuf, "VPS: %0.1f", vps); break; + case 3: + sprintf(fpsbuf, "FPS: %0.1f", fps); break; + case 4: + sprintf(fpsbuf, "VPS: %0.1f\nFPS: %0.1f", vps, fps); break; + } ui_draw2d.DrawText(UBUNTU24, fpsbuf, dp_xres - 8, 12, 0xc0000000, ALIGN_TOPRIGHT); ui_draw2d.DrawText(UBUNTU24, fpsbuf, dp_xres - 10, 10, 0xFF3fFF3f, ALIGN_TOPRIGHT); } diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index dbcc1b3b84..d4028b2757 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -465,8 +465,6 @@ void PauseScreen::render() { if (gpu) gpu->Resized(); } - UICheckBox(GEN_ID, x, y += stride, ss->T("Show FPS"), ALIGN_TOPLEFT, &g_Config.bShowFPSCounter); - bool enableFrameSkip = g_Config.iFrameSkip != 0; UICheckBox(GEN_ID, x, y += stride , gs->T("Frame Skipping"), ALIGN_TOPLEFT, &enableFrameSkip); if (enableFrameSkip) { @@ -686,24 +684,24 @@ void DeveloperScreen::render() { screenManager()->finishDialog(this, DR_OK); } - if (UIButton(GEN_ID, vlinear, w, 0, d->T("Load language ini"), ALIGN_LEFT)) { + if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH + 80, 0, d->T("Load language ini"), ALIGN_LEFT)) { i18nrepo.LoadIni(g_Config.languageIni); // After this, g and s are no longer valid. Need to reload them. g = GetI18NCategory("General"); d = GetI18NCategory("Developer"); } - if (UIButton(GEN_ID, vlinear, w, 0, d->T("Save language ini"), ALIGN_LEFT)) { + if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH + 80, 0, d->T("Save language ini"), ALIGN_LEFT)) { i18nrepo.SaveIni(g_Config.languageIni); } - if (UIButton(GEN_ID, vlinear, w, 0, d->T("Run CPU tests"), ALIGN_LEFT)) { + if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH + 80, 0, d->T("Run CPU Tests"), ALIGN_LEFT)) { // TODO: Run tests RunTests(); // screenManager()->push(new EmuScreen()) } - if (UIButton(GEN_ID, vlinear, w, 0, d->T("Dump frame to log"), ALIGN_LEFT)) { + if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH + 80, 0, d->T("Dump next frame"), ALIGN_LEFT)) { gpu->DumpNextFrame(); } @@ -928,7 +926,35 @@ void GraphicsScreenP3::render() { int y = 35; int stride = 40; int columnw = 400; + + bool ShowCounter = g_Config.iShowFPSCounter > 0; + UICheckBox(GEN_ID, x, y += stride, gs->T("Show VPS/FPS"), ALIGN_TOPLEFT, &ShowCounter); + if (ShowCounter) { + if (g_Config.iShowFPSCounter <= 0) + g_Config.iShowFPSCounter = 1; + char counter[256]; + std::string type; + + switch (g_Config.iShowFPSCounter) { + case 1: type = "VPS";break; + case 2: type = "FPS";break; + case 3: type = "Both";break; + } + sprintf(counter, "%s %s", gs->T("Format :"), type.c_str()); + ui_draw2d.DrawText(UBUNTU24, counter, x + 60, y += stride , 0xFFFFFFFF, ALIGN_LEFT); + HLinear hlinear1(x + 250, y, 20); + if (UIButton(GEN_ID, hlinear1, 80, 0, gs->T("VPS"), ALIGN_LEFT)) + g_Config.iShowFPSCounter = 1; + if (UIButton(GEN_ID, hlinear1, 80, 0, gs->T("FPS"), ALIGN_LEFT)) + g_Config.iShowFPSCounter = 2; + if (UIButton(GEN_ID, hlinear1, 90, 0, gs->T("Both"), ALIGN_LEFT)) + g_Config.iShowFPSCounter = 3; + + y += 20; + } else + g_Config.iShowFPSCounter = 0; + bool FpsLimit = g_Config.iFpsLimit != 0; UICheckBox(GEN_ID, x, y += stride, gs->T("FPS Limit"), ALIGN_TOPLEFT, &FpsLimit); if (FpsLimit) { @@ -1122,13 +1148,74 @@ void SystemScreen::render() { #endif if (g_Config.bJit) UICheckBox(GEN_ID, x, y += stride, s->T("Fast Memory", "Fast Memory (unstable)"), ALIGN_TOPLEFT, &g_Config.bFastMemory); - UICheckBox(GEN_ID, x, y += stride, s->T("Show FPS"), ALIGN_TOPLEFT, &g_Config.bShowFPSCounter); - UICheckBox(GEN_ID, x, y += stride, s->T("Encrypt Save"), ALIGN_TOPLEFT, &g_Config.bEncryptSave); - UICheckBox(GEN_ID, x, y += stride, s->T("Use Button X to Confirm"), ALIGN_TOPLEFT, &g_Config.bButtonPreference); - bool tf = g_Config.itimeformat == 1; - if (UICheckBox(GEN_ID, x, y += stride, s->T("12HR Time Format"), ALIGN_TOPLEFT, &tf)) { - g_Config.itimeformat = tf ? 1 : 0; + + UICheckBox(GEN_ID, x, y += stride, s->T("Daylight Savings"), ALIGN_TOPLEFT, &g_Config.bDayLightSavings); + UICheckBox(GEN_ID, x, y += stride, s->T("Button Perference"), ALIGN_TOPLEFT, &g_Config.bButtonPreference); + if (g_Config.bButtonPreference) { + char button[256]; + std::string type; + switch (g_Config.iButtonPreference) { + case 0: type = "O to Enter";break; + case 1: type = "X to Enter";break; + } + sprintf(button, "%s %s", s->T("Type :"), type.c_str()); + ui_draw2d.DrawText(UBUNTU24, button, x + 60, y += stride , 0xFFFFFFFF, ALIGN_LEFT); + HLinear hlinear1(x + 280, y, 20); + if (UIButton(GEN_ID, hlinear1, 90, 0, s->T("Use O"), ALIGN_LEFT)) + g_Config.iButtonPreference = 0; + if (UIButton(GEN_ID, hlinear1, 90, 0, s->T("Use X"), ALIGN_LEFT)) + g_Config.iButtonPreference = 1; + y += 10; } + + bool time = g_Config.iTimeFormat > 0 ; + UICheckBox(GEN_ID, x, y += stride, s->T("Time Format"), ALIGN_TOPLEFT, &time); + if (time) { + if (g_Config.iTimeFormat <= 0) + g_Config.iTimeFormat = 1; + + char button[256]; + std::string type; + switch (g_Config.iTimeFormat) { + case 1: type = "12HR";break; + case 2: type = "24HR";break; + } + sprintf(button, "%s %s", s->T("Format :"), type.c_str()); + ui_draw2d.DrawText(UBUNTU24, button, x + 60, y += stride , 0xFFFFFFFF, ALIGN_LEFT); + HLinear hlinear1(x + 280, y, 20); + if (UIButton(GEN_ID, hlinear1, 80, 0, s->T("12HR"), ALIGN_LEFT)) + g_Config.iTimeFormat = 1; + if (UIButton(GEN_ID, hlinear1, 80, 0, s->T("24HR"), ALIGN_LEFT)) + g_Config.iTimeFormat = 2; + y += 10; + } else + g_Config.iTimeFormat = 0 ; + + bool date = g_Config.iDateFormat > 0; + UICheckBox(GEN_ID, x, y += stride, s->T("Date Format"), ALIGN_TOPLEFT, &date); + if (date) { + if (g_Config.iDateFormat <= 0) + g_Config.iDateFormat = 1; + char button[256]; + std::string type; + switch (g_Config.iDateFormat) { + case 1: type = "YYYYMMDD";break; + case 2: type = "MMDDYYYY";break; + case 3: type = "DDMMYYYY";break; + } + sprintf(button, "%s %s", s->T("Format :"), type.c_str()); + ui_draw2d.DrawText(UBUNTU24, button, x + 60, y += stride , 0xFFFFFFFF, ALIGN_LEFT); + HLinear hlinear1(x + 350, y, 10); + if (UIButton(GEN_ID, hlinear1, 70, 0, s->T("YMD"), ALIGN_LEFT)) + g_Config.iDateFormat = 1; + if (UIButton(GEN_ID, hlinear1, 70, 0, s->T("MDY"), ALIGN_LEFT)) + g_Config.iDateFormat = 2; + if (UIButton(GEN_ID, hlinear1, 70, 0, s->T("DMY"), ALIGN_LEFT)) + g_Config.iDateFormat = 3; + y += 10; + } else + g_Config.iDateFormat = 0; + UICheckBox(GEN_ID, x, y += stride, s->T("Enable Cheats"), ALIGN_TOPLEFT, &g_Config.bEnableCheats); if (g_Config.bEnableCheats) { HLinear hlinear1(x + 60, y += stride + 10, 20); @@ -1139,7 +1226,7 @@ void SystemScreen::render() { HLinear hlinear2(x, y += stride + 10, 20); if (UIButton(GEN_ID, hlinear2, LARGE_BUTTON_WIDTH, 0, s->T("Language"), ALIGN_TOPLEFT)) { screenManager()->push(new LanguageScreen()); - } + } UIEnd(); } @@ -1166,6 +1253,8 @@ void ControlsScreen::render() { int columnw = 440; UICheckBox(GEN_ID, x, y += stride, c->T("OnScreen", "On-Screen Touch Controls"), ALIGN_TOPLEFT, &g_Config.bShowTouchControls); + UICheckBox(GEN_ID, x, y += stride, c->T("Show Analog Stick"), ALIGN_TOPLEFT, &g_Config.bShowAnalogStick); + UICheckBox(GEN_ID, x, y += stride, c->T("Tilt", "Tilt to Analog (horizontal)"), ALIGN_TOPLEFT, &g_Config.bAccelerometerToAnalogHoriz); if (g_Config.bShowTouchControls) { UICheckBox(GEN_ID, x, y += stride, c->T("Buttons Scaling"), ALIGN_TOPLEFT, &g_Config.bLargeControls); if (g_Config.bLargeControls) { @@ -1183,11 +1272,10 @@ void ControlsScreen::render() { g_Config.fButtonScale += 0.1; y += 20; } - UICheckBox(GEN_ID, x, y += stride, c->T("Show Analog Stick"), ALIGN_TOPLEFT, &g_Config.bShowAnalogStick); // This will be a slider in the new UI later bool bTransparent = g_Config.iTouchButtonOpacity < 65; bool prev = bTransparent; - UICheckBox(GEN_ID, x, y += stride, c->T("Transparent Buttons"), ALIGN_TOPLEFT, &bTransparent); + UICheckBox(GEN_ID, x, y += stride, c->T("Buttons Opacity"), ALIGN_TOPLEFT, &bTransparent); if (bTransparent) { char opacity[256]; sprintf(opacity, "%s %d", c->T("Opacity :"), g_Config.iTouchButtonOpacity); @@ -1206,7 +1294,6 @@ void ControlsScreen::render() { if (bTransparent != prev) g_Config.iTouchButtonOpacity = bTransparent ? 15 : 65; } - UICheckBox(GEN_ID, x, y += stride, c->T("Tilt", "Tilt to Analog (horizontal)"), ALIGN_TOPLEFT, &g_Config.bAccelerometerToAnalogHoriz); UIEnd(); } diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index 34a0176c77..ee02b7bc89 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -726,7 +726,7 @@ namespace MainWindow g_Config.bVertexCache = !g_Config.bVertexCache; break; case ID_OPTIONS_SHOWFPS: - g_Config.bShowFPSCounter = !g_Config.bShowFPSCounter; + g_Config.iShowFPSCounter = !g_Config.iShowFPSCounter; break; case ID_OPTIONS_DISPLAYRAWFRAMEBUFFER: g_Config.bDisplayFramebuffer = !g_Config.bDisplayFramebuffer; @@ -898,7 +898,7 @@ namespace MainWindow CHECKITEM(ID_EMULATION_RUNONLOAD, g_Config.bAutoRun); CHECKITEM(ID_OPTIONS_USEVBO, g_Config.bUseVBO); CHECKITEM(ID_OPTIONS_VERTEXCACHE, g_Config.bVertexCache); - CHECKITEM(ID_OPTIONS_SHOWFPS, g_Config.bShowFPSCounter); + CHECKITEM(ID_OPTIONS_SHOWFPS, g_Config.iShowFPSCounter); CHECKITEM(ID_OPTIONS_FRAMESKIP, g_Config.iFrameSkip != 0); CHECKITEM(ID_OPTIONS_MIPMAP, g_Config.bMipMap); CHECKITEM(ID_OPTIONS_VSYNC, g_Config.iVSyncInterval != 0); diff --git a/headless/Headless.cpp b/headless/Headless.cpp index af2375fe67..5bba70a795 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -200,12 +200,12 @@ int main(int argc, const char* argv[]) g_Config.bVertexCache = true; g_Config.bTrueColor = true; g_Config.ilanguage = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH; - g_Config.itimeformat = PSP_SYSTEMPARAM_TIME_FORMAT_24HR; + g_Config.iTimeFormat = PSP_SYSTEMPARAM_TIME_FORMAT_24HR; g_Config.bEncryptSave = true; g_Config.sNickName = "shadow"; g_Config.iTimeZone = 60; g_Config.iDateFormat = PSP_SYSTEMPARAM_DATE_FORMAT_DDMMYYYY; - g_Config.bButtonPreference = true; + g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CROSS; g_Config.iLockParentalLevel = 9; #if defined(ANDROID) From d24ad28ceb00e468277aa145b5fd7743d4f54595 Mon Sep 17 00:00:00 2001 From: raven02 Date: Wed, 19 Jun 2013 15:24:35 +0800 Subject: [PATCH 08/80] Follow as 1/2/3 option --- UI/EmuScreen.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index e461ab91ed..3636cc027f 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -342,11 +342,11 @@ void EmuScreen::render() { __DisplayGetFPS(&vps, &fps); char fpsbuf[256]; switch (g_Config.iShowFPSCounter) { - case 2: + case 1: sprintf(fpsbuf, "VPS: %0.1f", vps); break; - case 3: + case 2: sprintf(fpsbuf, "FPS: %0.1f", fps); break; - case 4: + case 3: sprintf(fpsbuf, "VPS: %0.1f\nFPS: %0.1f", vps, fps); break; } ui_draw2d.DrawText(UBUNTU24, fpsbuf, dp_xres - 8, 12, 0xc0000000, ALIGN_TOPRIGHT); From 2dad8cb90d82d893b605964261f5752a93c5ff56 Mon Sep 17 00:00:00 2001 From: raven02 Date: Wed, 19 Jun 2013 21:49:51 +0800 Subject: [PATCH 09/80] Quick fix spacing issue on Pause menu --- UI/MenuScreens.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index d4028b2757..7acfcb95fc 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -487,8 +487,9 @@ void PauseScreen::render() { } else g_Config.iFrameSkip = 0; - ui_draw2d.DrawText(UBUNTU24, gs->T("Save State :"), 30, 360, 0xFFFFFFFF, ALIGN_LEFT); - HLinear hlinear4(x + 180 , y += 50, 10); + UICheckBox(GEN_ID, x, y += stride, gs->T("Linear Filtering"), ALIGN_TOPLEFT, &g_Config.bLinearFiltering); + ui_draw2d.DrawText(UBUNTU24, gs->T("Save State :"), 30, y += 40, 0xFFFFFFFF, ALIGN_LEFT); + HLinear hlinear4(x + 180 , y , 10); if (UIButton(GEN_ID, hlinear4, 60, 0, "1", ALIGN_LEFT)) { SaveState::SaveSlot(0, 0, 0); screenManager()->finishDialog(this, DR_CANCEL); @@ -510,9 +511,8 @@ void PauseScreen::render() { screenManager()->finishDialog(this, DR_CANCEL); } - - ui_draw2d.DrawText(UBUNTU24, gs->T("Load State :"), 30, 420, 0xFFFFFFFF, ALIGN_LEFT); - HLinear hlinear3(x + 180 , y += 60, 10); + ui_draw2d.DrawText(UBUNTU24, gs->T("Load State :"), 30, y += 60, 0xFFFFFFFF, ALIGN_LEFT); + HLinear hlinear3(x + 180 , y + 10 , 10); if (UIButton(GEN_ID, hlinear3, 60, 0, "1", ALIGN_LEFT)) { SaveState::LoadSlot(0, 0, 0); screenManager()->finishDialog(this, DR_CANCEL); From f2b7096bdf53e103b8b28909ec3a88f57a629183 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 19 Jun 2013 23:54:45 +0200 Subject: [PATCH 10/80] Attempted workaround for issue #2308. Needs plenty of testing! Fixes black screens in GTA when pausing for example. --- GPU/GLES/Framebuffer.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 2f83bb05fa..6f7f6703c7 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -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 + #include "gfx_es2/glsl_program.h" #include "gfx_es2/gl_state.h" #include "gfx_es2/fbo.h" @@ -354,12 +356,28 @@ void FramebufferManager::SetRenderFrameBuffer() { VirtualFramebuffer *vfb = 0; for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { VirtualFramebuffer *v = *iter; - if (MaskedEqual(v->fb_address, fb_address) && v->width == drawing_width && v->height == drawing_height && v->format == fmt) { - // Let's not be so picky for now. Let's say this is the one. - vfb = v; - // Update fb stride in case it changed - vfb->fb_stride = fb_stride; - break; + if (MaskedEqual(v->fb_address, fb_address) && v->format == fmt) { + // Okay, let's check the sizes. If the new one is bigger than the old one, recreate. + // If the opposite, just use it and hope that the game sets scissors accordingly. + if (v->width >= drawing_width && v->height >= drawing_height) { + // Let's not be so picky for now. Let's say this is the one. + vfb = v; + // Update fb stride in case it changed + vfb->fb_stride = fb_stride; + // Just hack the width/height and we should be fine. also hack renderwidth/renderheight? + v->width = drawing_width; + v->height = drawing_height; + break; + } else { + INFO_LOG(HLE, "Embiggening framebuffer (%i, %i) -> (%i, %i)", (int)v->width, (int)v->height, drawing_width, drawing_height); + // drawing_width or drawing_height is bigger. Let's recreate with the max. + // To do this right we should copy the data over too, but meh. + drawing_width = std::max((int)v->width, drawing_width); + drawing_height = std::max((int)v->height, drawing_height); + delete v; + vfbs_.erase(iter); + break; + } } } From 283f382aff958b5da538884d6cc9d6017a6739ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 20 Jun 2013 01:26:18 +0200 Subject: [PATCH 11/80] Revert "Attempted workaround for issue #2308. Needs plenty of testing! Fixes black screens in GTA when pausing for example." This reverts commit f2b7096bdf53e103b8b28909ec3a88f57a629183. --- GPU/GLES/Framebuffer.cpp | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 6f7f6703c7..2f83bb05fa 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -15,8 +15,6 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include - #include "gfx_es2/glsl_program.h" #include "gfx_es2/gl_state.h" #include "gfx_es2/fbo.h" @@ -356,28 +354,12 @@ void FramebufferManager::SetRenderFrameBuffer() { VirtualFramebuffer *vfb = 0; for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { VirtualFramebuffer *v = *iter; - if (MaskedEqual(v->fb_address, fb_address) && v->format == fmt) { - // Okay, let's check the sizes. If the new one is bigger than the old one, recreate. - // If the opposite, just use it and hope that the game sets scissors accordingly. - if (v->width >= drawing_width && v->height >= drawing_height) { - // Let's not be so picky for now. Let's say this is the one. - vfb = v; - // Update fb stride in case it changed - vfb->fb_stride = fb_stride; - // Just hack the width/height and we should be fine. also hack renderwidth/renderheight? - v->width = drawing_width; - v->height = drawing_height; - break; - } else { - INFO_LOG(HLE, "Embiggening framebuffer (%i, %i) -> (%i, %i)", (int)v->width, (int)v->height, drawing_width, drawing_height); - // drawing_width or drawing_height is bigger. Let's recreate with the max. - // To do this right we should copy the data over too, but meh. - drawing_width = std::max((int)v->width, drawing_width); - drawing_height = std::max((int)v->height, drawing_height); - delete v; - vfbs_.erase(iter); - break; - } + if (MaskedEqual(v->fb_address, fb_address) && v->width == drawing_width && v->height == drawing_height && v->format == fmt) { + // Let's not be so picky for now. Let's say this is the one. + vfb = v; + // Update fb stride in case it changed + vfb->fb_stride = fb_stride; + break; } } From c5dfccd55dd6f84be4716bab3dd2b85a597f47a0 Mon Sep 17 00:00:00 2001 From: The Dax Date: Thu, 20 Jun 2013 03:15:07 -0400 Subject: [PATCH 12/80] Implement more sceNet and sceHttp stubs. List includes: sceNetApctlAddHandler sceNetApctlDelHandler sceNetApctlInit sceNetApctlTerm sceNetInetInit sceNetInetTerm sceHttpInit sceHttpEnd --- Core/HLE/sceHttp.cpp | 14 ++++- Core/HLE/sceNet.cpp | 136 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 137 insertions(+), 13 deletions(-) diff --git a/Core/HLE/sceHttp.cpp b/Core/HLE/sceHttp.cpp index 0ba77a7ec7..d755e2f5d2 100644 --- a/Core/HLE/sceHttp.cpp +++ b/Core/HLE/sceHttp.cpp @@ -25,14 +25,24 @@ int sceHttpSetResolveRetry(int connectionID, int retryCount) return 0; } +int sceHttpInit() { + ERROR_LOG(HLE, "UNIMPL sceHttpInit()"); + return 0; +} + +int sceHttpEnd() { + ERROR_LOG(HLE, "UNIMPL sceHttpInit()"); + return 0; +} + /* * 0x62411801 sceSircsInit 0x19155a2f sceSircsEnd 0x71eef62d sceSircsSend */ const HLEFunction sceHttp[] = { - {0xab1abe07,0,"sceHttpInit"}, - {0xd1c8945e,0,"sceHttpEnd"}, + {0xab1abe07,WrapI_V,"sceHttpInit"}, + {0xd1c8945e,WrapI_V,"sceHttpEnd"}, {0xa6800c34,0,"sceHttpInitCache"}, {0x78b54c09,0,"sceHttpEndCache"}, {0x59e6d16f,0,"sceHttpEnableCache"}, diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 2ecba10ec0..d0c7821c59 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -24,12 +24,16 @@ #include "sceUtility.h" static bool netInited; +static bool netInetInited; static bool netAdhocInited; +static bool netApctlInited; static u32 adhocctlHandlerCount; +static u32 apctlHandlerCount; // TODO: Determine how many handlers we can actually have const u32 MAX_ADHOCCTL_HANDLERS = 32; +const u32 MAX_APCTL_HANDLERS = 32; enum { ERROR_NET_BUFFER_TOO_SMALL = 0x80400706, @@ -88,9 +92,19 @@ struct AdhocctlHandler { static std::map adhocctlHandlers; +struct ApctlHandler { + u32 entryPoint; + u32 argument; +}; + +static std::map apctlHandlers; + + void __NetInit() { netInited = false; netAdhocInited = false; + netApctlInited = false; + netInetInited = false; memset(&netMallocStat, 0, sizeof(netMallocStat)); } @@ -100,22 +114,40 @@ void __NetShutdown() { void __UpdateAdhocctlHandlers(int flag, int error) { u32 args[3] = { 0, 0, 0 }; + args[0] = flag; + args[1] = error; for(std::map::iterator it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); ++it) { - args[0] = flag; - args[1] = error; args[2] = it->second.argument; - __KernelDirectMipsCall(it->second.entryPoint, NULL, args, 3, true); + __KernelDirectMipsCall(it->second.entryPoint, NULL, args, 5, true); + } +} + +void __UpdateApctlHandlers(int oldState, int newState, int flag, int error) { + u32 args[5] = { 0, 0, 0, 0, 0 }; + args[0] = oldState; + args[1] = newState; + args[2] = flag; + args[3] = error; + + for(std::map::iterator it = apctlHandlers.begin(); it != apctlHandlers.end(); ++it) { + args[4] = it->second.argument; + + __KernelDirectMipsCall(it->second.entryPoint, NULL, args, 5, true); } } // This feels like a dubious proposition, mostly... void __NetDoState(PointerWrap &p) { p.Do(netInited); + p.Do(netInetInited); p.Do(netAdhocInited); + p.Do(netApctlInited); p.Do(adhocctlHandlers); p.Do(adhocctlHandlerCount); + p.Do(apctlHandlers); + p.Do(apctlHandlerCount); p.Do(netMallocStat); p.DoMarker("net"); } @@ -274,7 +306,7 @@ int sceNetAdhocPdpCreate(const char *mac, u32 port, int bufferSize, u32 unknown) // TODO: Should we really write the struct if we're disconnected? int sceNetAdhocctlGetParameter(u32 paramAddr) { - ERROR_LOG(HLE, "UNTESTED %x=sceNetAdhocctlGetParameter(%x)", 0, paramAddr); + WARN_LOG(HLE, "UNTESTED sceNetAdhocctlGetParameter(%x)", paramAddr); struct SceNetAdhocctlParams params; params.channel = 0; for(int i = 0; i < 6; i++) @@ -331,7 +363,7 @@ int sceNetAdhocctlConnect(u32 ptrToGroupName) { // Write static data since we don't actually manage any memory for sceNet* yet. int sceNetGetMallocStat(u32 statPtr) { - ERROR_LOG(HLE, "UNTESTED sceNetGetMallocStat(%x)", statPtr); + WARN_LOG(HLE, "UNTESTED sceNetGetMallocStat(%x)", statPtr); if(Memory::IsValidAddress(statPtr)) Memory::WriteStruct(statPtr, &netMallocStat); else @@ -345,6 +377,88 @@ int sceNetAdhocMatchingInit(u32 memsize) { return 0; } +int sceNetInetInit() { + ERROR_LOG(HLE, "UNIMPL sceNetInetInit()"); + if (netInetInited) + return ERROR_NET_ADHOC_ALREADY_INITIALIZED; // TODO: What's the proper error for netInet already being inited? + netInetInited = true; + + return 0; +} + +int sceNetInetTerm() { + ERROR_LOG(HLE, "UNIMPL sceNetInetTerm()"); + netInetInited = false; + + return 0; +} + +int sceNetApctlInit() { + ERROR_LOG(HLE, "UNIMPL sceNetApctlInit()"); + if (netAdhocInited) + return ERROR_NET_ADHOC_ALREADY_INITIALIZED; // TODO: What's the proper error for apctl already being inited? + netApctlInited = true; + + return 0; +} + +int sceNetApctlTerm() { + ERROR_LOG(HLE, "UNIMPL sceNeApctlTerm()"); + netInetInited = false; + + return 0; +} + +// TODO: How many handlers can the PSP actually have for Apctl? +// TODO: Should we allow the same handler to be added more than once? +u32 sceNetApctlAddHandler(u32 handlerPtr, u32 handlerArg) { + bool foundHandler = false; + u32 retval = apctlHandlerCount; + struct ApctlHandler handler; + memset(&handler, 0, sizeof(handler)); + + handler.entryPoint = handlerPtr; + handler.argument = handlerArg; + + for(std::map::iterator it = apctlHandlers.begin(); it != apctlHandlers.end(); it++) { + if(it->second.entryPoint == handlerPtr) { + foundHandler = true; + break; + } + } + + if(!foundHandler && Memory::IsValidAddress(handlerPtr)) { + if(apctlHandlerCount >= MAX_APCTL_HANDLERS) { + ERROR_LOG(HLE, "UNTESTED sceNetApctlAddHandler(%x, %x): Too many handlers", handlerPtr, handlerArg); + retval = ERROR_NET_ADHOCCTL_TOO_MANY_HANDLERS; // TODO: What's the proper error code for Apctl's TOO_MANY_HANDLERS? + return retval; + } + apctlHandlers[apctlHandlerCount++] = handler; + WARN_LOG(HLE, "UNTESTED sceNetApctlAddHandler(%x, %x): added handler %d", handlerPtr, handlerArg, apctlHandlerCount); + } + else + ERROR_LOG(HLE, "UNTESTED sceNetApctlAddHandler(%x, %x): Same handler already exists", handlerPtr, handlerArg); + + + // The id to return is the number of handlers currently registered + return retval; +} + +int sceNetApctlDelHandler(u32 handlerID) { + + if(apctlHandlers.find(handlerID) != apctlHandlers.end()) { + apctlHandlers.erase(handlerID); + apctlHandlerCount = apctlHandlerCount > 0? --apctlHandlerCount : 0; + WARN_LOG(HLE, "UNTESTED sceNetapctlDelHandler(%d): deleted handler %d", handlerID, handlerID); + } + else + ERROR_LOG(HLE, "UNTESTED sceNetapctlDelHandler(%d): asked to delete invalid handler %d", handlerID, handlerID); + + return 0; +} + + + const HLEFunction sceNet[] = { {0x39AF39A6, sceNetInit, "sceNetInit"}, {0x281928A9, WrapU_V, "sceNetTerm"}, @@ -444,7 +558,7 @@ const HLEFunction sceNetResolver[] = { }; const HLEFunction sceNetInet[] = { - {0x17943399, 0, "sceNetInetInit"}, + {0x17943399, WrapI_V, "sceNetInetInit"}, {0x2fe71fe7, 0, "sceNetInetSetsockopt"}, {0x410b34aa, 0, "sceNetInetConnect"}, {0x5be8d595, 0, "sceNetInetSelect"}, @@ -463,7 +577,7 @@ const HLEFunction sceNetInet[] = { {0xd10a1a7a, 0, "sceNetInetListen"}, {0xdb094e1b, 0, "sceNetInetAccept"}, {0x8ca3a97e, 0, "sceNetInetGetPspError"}, - {0xa9ed66b9, 0, "sceNetInetTerm"}, + {0xa9ed66b9, WrapI_V, "sceNetInetTerm"}, {0xE30B8C19, 0, "sceNetInetInetPton"}, {0xE247B6D6, 0, "sceNetInetGetpeername"}, {0x162e6fd5, 0, "sceNetInetGetsockname"}, @@ -481,10 +595,10 @@ const HLEFunction sceNetApctl[] = { {0xCFB957C6, 0, "sceNetApctlConnect"}, {0x24fe91a1, 0, "sceNetApctlDisconnect"}, {0x5deac81b, 0, "sceNetApctlGetState"}, - {0x8abadd51, 0, "sceNetApctlAddHandler"}, - {0xe2f91f9b, 0, "sceNetApctlInit"}, - {0x5963991b, 0, "sceNetApctlDelHandler"}, - {0xb3edd0ec, 0, "sceNetApctlTerm"}, + {0x8abadd51, WrapU_UU, "sceNetApctlAddHandler"}, + {0xe2f91f9b, WrapI_V, "sceNetApctlInit"}, + {0x5963991b, WrapI_U, "sceNetApctlDelHandler"}, + {0xb3edd0ec, WrapI_V, "sceNetApctlTerm"}, {0x2BEFDF23, 0, "sceNetApctlGetInfo"}, {0xa3e77e13, 0, "sceNetApctlScanSSID2"}, {0xf25a5006, 0, "sceNetApctlGetBSSDescIDList2"}, From b989292c7cfa10d7cbb71b4e03f64dd406ff455a Mon Sep 17 00:00:00 2001 From: The Dax Date: Thu, 20 Jun 2013 03:20:10 -0400 Subject: [PATCH 13/80] Fix typo in __UpdateAdhocctlHandlers and remove stray carriage return. --- Core/HLE/sceNet.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index d0c7821c59..2e31cd71fe 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -99,7 +99,6 @@ struct ApctlHandler { static std::map apctlHandlers; - void __NetInit() { netInited = false; netAdhocInited = false; @@ -120,7 +119,7 @@ void __UpdateAdhocctlHandlers(int flag, int error) { for(std::map::iterator it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); ++it) { args[2] = it->second.argument; - __KernelDirectMipsCall(it->second.entryPoint, NULL, args, 5, true); + __KernelDirectMipsCall(it->second.entryPoint, NULL, args, 3, true); } } From 250260cccdee1f76bca462cb1e4dbde48ac7e07c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 20 Jun 2013 01:00:53 -0700 Subject: [PATCH 14/80] Fix sceCtrl analog range for all input devices. It should map (-1.0... 0.0... 1.0) to (0... 128... 255.) However, it was instead being mapped to (1... 128... 255.) This was causing games to not respect analog movement if they checked for 100%. Fixes #2363. --- Core/HLE/sceCtrl.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index d38bd48151..7d032010f4 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -161,11 +161,11 @@ void __CtrlSetAnalog(float x, float y, int stick) { std::lock_guard guard(ctrlMutex); if (stick == 0) { - ctrlCurrent.analog[0] = (u8)(x * 127.f + 128.f); - ctrlCurrent.analog[1] = (u8)(-y * 127.f + 128.f); + ctrlCurrent.analog[0] = (u8)(x * 127.5f + 128.f); + ctrlCurrent.analog[1] = (u8)(-y * 127.5f + 128.f); } else { - ctrlCurrent.analogRight[0] = (u8)(x * 127.f + 128.f); - ctrlCurrent.analogRight[1] = (u8)(-y * 127.f + 128.f); + ctrlCurrent.analogRight[0] = (u8)(x * 127.5f + 128.f); + ctrlCurrent.analogRight[1] = (u8)(-y * 127.5f + 128.f); } } From 362c5be657c49f3f649ab0c0c9802da356197fb3 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 20 Jun 2013 01:11:45 -0700 Subject: [PATCH 15/80] Tweak the ctrl analog rounding to be safer. --- Core/HLE/sceCtrl.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceCtrl.cpp b/Core/HLE/sceCtrl.cpp index 7d032010f4..7f828cb904 100644 --- a/Core/HLE/sceCtrl.cpp +++ b/Core/HLE/sceCtrl.cpp @@ -161,11 +161,11 @@ void __CtrlSetAnalog(float x, float y, int stick) { std::lock_guard guard(ctrlMutex); if (stick == 0) { - ctrlCurrent.analog[0] = (u8)(x * 127.5f + 128.f); - ctrlCurrent.analog[1] = (u8)(-y * 127.5f + 128.f); + ctrlCurrent.analog[0] = (u8)ceilf(x * 127.5f + 127.5f); + ctrlCurrent.analog[1] = (u8)ceilf(-y * 127.5f + 127.5f); } else { - ctrlCurrent.analogRight[0] = (u8)(x * 127.5f + 128.f); - ctrlCurrent.analogRight[1] = (u8)(-y * 127.5f + 128.f); + ctrlCurrent.analogRight[0] = (u8)ceilf(x * 127.5f + 127.5f); + ctrlCurrent.analogRight[1] = (u8)ceilf(-y * 127.5f + 127.5f); } } From 2863e74e521cfee3510c610001253d1bf449dc33 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 20 Jun 2013 01:53:07 -0700 Subject: [PATCH 16/80] For now, ignore duplicate sceMpegInit()/Finish()'s. Need to properly handle module unload for this to work right. Fixes #2364. --- Core/HLE/sceMpeg.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 1ccf65b1e9..305b535df2 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -327,10 +327,11 @@ void __MpegShutdown() { u32 sceMpegInit() { if (isMpegInit) { WARN_LOG(HLE, "sceMpegInit(): already initialized"); - return ERROR_MPEG_ALREADY_INIT; + // TODO: Need to properly hook module load/unload for this to work right. + //return ERROR_MPEG_ALREADY_INIT; + } else { + INFO_LOG(HLE, "sceMpegInit()"); } - - INFO_LOG(HLE, "sceMpegInit()"); isMpegInit = true; return hleDelayResult(0, "mpeg init", 750); } @@ -1009,10 +1010,11 @@ u32 sceMpegFinish() if (!isMpegInit) { WARN_LOG(HLE, "sceMpegFinish(...): not initialized"); - return ERROR_MPEG_NOT_YET_INIT; + // TODO: Need to properly hook module load/unload for this to work right. + //return ERROR_MPEG_NOT_YET_INIT; + } else { + INFO_LOG(HLE, "sceMpegFinish(...)"); } - - INFO_LOG(HLE, "sceMpegFinish(...)"); isMpegInit = false; //__MpegFinish(); return hleDelayResult(0, "mpeg finish", 250); From 32bedefe200cb5a7b7583c866ce707f3fbc2801a Mon Sep 17 00:00:00 2001 From: The Dax Date: Thu, 20 Jun 2013 12:50:10 -0400 Subject: [PATCH 17/80] Windows: Make DirectSound buffer global to allow sound to be played, even if other applications like Steam are brought to the foreground. --- Windows/DSoundStream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Windows/DSoundStream.cpp b/Windows/DSoundStream.cpp index 45047172bf..f2e83c8b76 100644 --- a/Windows/DSoundStream.cpp +++ b/Windows/DSoundStream.cpp @@ -49,7 +49,7 @@ namespace DSound pcmwf.wBitsPerSample = 16; dsbdesc.dwSize = sizeof(DSBUFFERDESC); - dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STICKYFOCUS; // //DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; + dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; // //DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; dsbdesc.dwBufferBytes = bufferSize = BUFSIZE; //FIX32(pcmwf.wf.nAvgBytesPerSec); //change to set buffer size dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&pcmwf; From e224180ba62cf581df1133e038f798ee36dd3f92 Mon Sep 17 00:00:00 2001 From: raven02 Date: Fri, 21 Jun 2013 01:37:52 +0800 Subject: [PATCH 18/80] Add "Cleanup Recents" button --- UI/MenuScreens.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index 7acfcb95fc..7d613267b8 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -705,6 +705,10 @@ void DeveloperScreen::render() { gpu->DumpNextFrame(); } + if (UIButton(GEN_ID, vlinear, LARGE_BUTTON_WIDTH + 80, 0, d->T("Cleanup Recents"), ALIGN_LEFT)) { + g_Config.recentIsos.clear(); + } + UIEnd(); } From a4bdd4bfc4a96e08ce3f987efa0efe84abac4a68 Mon Sep 17 00:00:00 2001 From: Sacha Date: Fri, 21 Jun 2013 04:23:34 +1000 Subject: [PATCH 19/80] Clean up RE code in sceChnnlsv.{cpp,h} --- Core/HLE/sceChnnlsv.cpp | 825 +++++++++++++--------------------------- Core/HLE/sceChnnlsv.h | 4 +- 2 files changed, 272 insertions(+), 557 deletions(-) diff --git a/Core/HLE/sceChnnlsv.cpp b/Core/HLE/sceChnnlsv.cpp index 27c583819e..25d96d613a 100644 --- a/Core/HLE/sceChnnlsv.cpp +++ b/Core/HLE/sceChnnlsv.cpp @@ -27,321 +27,217 @@ extern "C" u8 dataBuf[2048+20]; u8* dataBuf2 = dataBuf + 20; -static const u8 hash198C[16] = {0xFA, 0xAA, 0x50, 0xEC, 0x2F, 0xDE, 0x54, 0x93, 0xAD, 0x14, 0xB2, 0xCE, 0xA5, 0x30, 0x05, 0xDF }; -static const u8 hash19BC[16] = {0xCB, 0x15, 0xF4, 0x07, 0xF9, 0x6A, 0x52, 0x3C, 0x04, 0xB9, 0xB2, 0xEE, 0x5C, 0x53, 0xFA, 0x86 }; +static const u8 hash198C[16] = {0xFA, 0xAA, 0x50, 0xEC, 0x2F, 0xDE, 0x54, 0x93, 0xAD, 0x14, 0xB2, 0xCE, 0xA5, 0x30, 0x05, 0xDF}; +static const u8 hash19BC[16] = {0xCB, 0x15, 0xF4, 0x07, 0xF9, 0x6A, 0x52, 0x3C, 0x04, 0xB9, 0xB2, 0xEE, 0x5C, 0x53, 0xFA, 0x86}; -static const u8 key000019CC[16] = {0x70, 0x44, 0xA3, 0xAE, 0xEF, 0x5D, 0xA5, 0xF2, 0x85, 0x7F, 0xF2, 0xD6, 0x94, 0xF5, 0x36, 0x3B}; -static const u8 key000019DC[16] = {0xEC, 0x6D, 0x29, 0x59, 0x26, 0x35, 0xA5, 0x7F, 0x97, 0x2A, 0x0D, 0xBC, 0xA3, 0x26, 0x33, 0x00}; -static const u8 key0000199C[16] = {0x36, 0xA5, 0x3E, 0xAC, 0xC5, 0x26, 0x9E, 0xA3, 0x83, 0xD9, 0xEC, 0x25, 0x6C, 0x48, 0x48, 0x72}; -static const u8 key000019AC[16] = {0xD8, 0xC0, 0xB0, 0xF3, 0x3E, 0x6B, 0x76, 0x85, 0xFD, 0xFB, 0x4D, 0x7D, 0x45, 0x1E, 0x92, 0x03}; +static const u8 key19CC[16] = {0x70, 0x44, 0xA3, 0xAE, 0xEF, 0x5D, 0xA5, 0xF2, 0x85, 0x7F, 0xF2, 0xD6, 0x94, 0xF5, 0x36, 0x3B}; +static const u8 key19DC[16] = {0xEC, 0x6D, 0x29, 0x59, 0x26, 0x35, 0xA5, 0x7F, 0x97, 0x2A, 0x0D, 0xBC, 0xA3, 0x26, 0x33, 0x00}; +static const u8 key199C[16] = {0x36, 0xA5, 0x3E, 0xAC, 0xC5, 0x26, 0x9E, 0xA3, 0x83, 0xD9, 0xEC, 0x25, 0x6C, 0x48, 0x48, 0x72}; +static const u8 key19AC[16] = {0xD8, 0xC0, 0xB0, 0xF3, 0x3E, 0x6B, 0x76, 0x85, 0xFD, 0xFB, 0x4D, 0x7D, 0x45, 0x1E, 0x92, 0x03}; -int sub_000014BC(u8* data, int length) +void *memxor(void * dest, const void * src, size_t n) { - *(s32*)(data + 0) = 5; - *(s32*)(data + 12) = 256; - *(s32*)(data + 4) = 0; - *(s32*)(data + 8) = 0; - *(s32*)(data + 16) = length; - int res = sceUtilsBufferCopyWithRange(data, length + 20, data, length + 20, 8); - if (res == 0) - return 0; - return -258; + char const *s = (char const*)src; + char *d = (char*)dest; + + for (; n > 0; n--) + *d++ ^= *s++; + + return dest; } -int sub_00001418(u8* data, int length, int val2) +// The reason for the values from *FromMode calculations are not known. +int numFromMode(int mode) { - *(s32*)(data + 12) = val2; - *(s32*)(data + 0) = 5; - *(s32*)(data + 4) = 0; - *(s32*)(data + 8) = 0; - *(s32*)(data + 16) = length; - int res = sceUtilsBufferCopyWithRange(data, length + 20, data, length + 20, 7); - if (res == 0) - return 0; - return -257; -} - - -int sub_000015B0(u8* data, int alignedLen, u8* buf, int val) -{ - u8 sp0[16]; - - for(int i = 0; i < 16; i++) - { - sp0[i] = data[alignedLen+i+4]; - } - int res = sub_00001418(data, alignedLen, val); - if (res != 0) - { - return res; - } - - for(int i = 0; i < 16; i++) - { - data[i] = data[i] ^ buf[i]; - } - for(int i = 0; i < 16; i++) - { - buf[i] = sp0[i]; - } - return 0; -} - -int sub_00000000(u8* data_out, u8* data, int alignedLen, u8* data2, int& data3, int mode) -{ - for(int i = 0; i < 16; i++) - { - data_out[20+i] = data2[i]; - } - int valS4 = 100; - int res; - if (mode == 6) - { - valS4 = 100; - for(int i = 0; i < 16; i++) - { - data_out[20+i] = data_out[20+i] ^ key000019DC[i]; - } - res = sub_000014BC(data_out, 16); - for(int i = 0; i < 16; i++) - { - data_out[i] = data_out[i] ^ key000019CC[i]; - } - } - else if (mode == 4) - { - valS4 = 87; - for(int i = 0; i < 16; i++) - { - data_out[20+i] = data_out[20+i] ^ key000019AC[i]; - } - res = sub_000014BC(data_out, 16); - for(int i = 0; i < 16; i++) - { - data_out[i] = data_out[i] ^ key0000199C[i]; - } - } - else if (mode == 2) - { - res = sub_000014BC(data_out, 16); - valS4 = 83; - } - else if (mode == 1) - { - res = sub_00001418(data_out, 16, 4); - valS4 = 83; - } - else if (mode == 3) - { - valS4 = 87; - for(int i = 0; i < 16; i++) - { - data_out[i+20] = data_out[i+20] ^ key000019AC[i]; - } - res = sub_00001418(data_out, 16, 14); - for(int i = 0; i < 16; i++) - { - data_out[i] = data_out[i] ^ key0000199C[i]; - } - } - else - { - valS4 = 100; - for(int i = 0; i < 16; i++) - { - data_out[i + 20] = data_out[i + 20] ^ key000019DC[i]; - } - res = sub_00001418(data_out, 16, 18); - for(int i = 0; i < 16; i++) - { - data_out[i] = data_out[i] ^ key000019CC[i]; - } - } - - u8 sp16[16]; - if (res != 0) - { - return res; - } - for(int i = 0; i < 16; i++) - { - sp16[i] = data_out[i]; - } - u8 sp0[16]; - if (data3 == 1) - { - for(int i = 0; i < 16; i++) - { - sp0[i] = 0; - } - } - else - { - for(int i = 0; i < 12; i++) - { - sp0[i] = sp16[i]; - } - sp0[12] = (data3-1) & 0xFF ; - sp0[13] = ((data3-1) >> 8) & 0xFF; - sp0[14] = ((data3-1) >> 16) & 0xFF; - sp0[15] = ((data3-1) >> 24) & 0xFF; - } - - if ((u32)20 < (u32)alignedLen + 20) - { - for(int i = 20; i < alignedLen + 20; i += 16) - { - for(int j = 0; j < 12; j++) - { - data_out[i+j] = sp16[j]; - } - data_out[12+i] = data3; - data_out[13+i] = (data3 >> 8) & 0xFF; - data_out[14+i] = (data3 >> 16) & 0xFF; - data_out[15+i] = (data3 >> 24) & 0xFF; - data3++; - } - } - - res = sub_000015B0(data_out, alignedLen, sp0, valS4); - if (res != 0) - { - return res; - } - if (res >= alignedLen) - { - return 0; - } - for(int i = 0; i < alignedLen; i++) - { - data[i] = data[i] ^ data_out[i]; - } - - return 0; -} - -int sub_000013C8(u8* data, int size, int num) -{ - *(int*)(data+0) = 4; - *(int*)(data+4) = 0; - *(int*)(data+8) = 0; - *(int*)(data+12) = num; - *(int*)(data+16) = size; - size = size + 20; - - int res = sceUtilsBufferCopyWithRange(data,size,data,size,4); - if(res != 0) - { - return -257; - } - return 0; -} - -int sub_00001468(u8* data, int size) -{ - *(int*)(data+0) = 4; - *(int*)(data+12) = 256; - *(int*)(data+4) = 0; - *(int*)(data+8) = 0; - *(int*)(data+16) = size; - size = size + 20; - - int res = sceUtilsBufferCopyWithRange(data,size,data,size,5); - if(res != 0) - { - return -258; - } - return 0; -} - -int sub_00001510(u8* data, int size, u8* result , int num) -{ - for(int i = 0; i < 16; i++) - { - int v1 = data[i+20]; - v1 = v1 ^ result[i]; - data[i+20] = v1; - } - - int res = sub_000013C8(data, size, num); - if(res != 0) - { - return res; - } - - for(int i = 0; i < 16; i++) - { - result[i] = data[size + i + 4]; - } - return 0; -} - -int sub_000017A8(u8* data) - { - int res = sceUtilsBufferCopyWithRange(data, 20, 0, 0, 14); - if (res == 0) - return 0; - return -261; -} - -int sceSdGetLastIndex(u32 addressCtx,u32 addressHash, u32 addressKey) -{ - pspChnnlsvContext1 ctx; - Memory::ReadStruct(addressCtx,&ctx); - u8* in_hash; - u8* in_key; - in_hash = Memory::GetPointer(addressHash); - in_key = Memory::GetPointer(addressKey); - - int res = sceSdGetLastIndex_(ctx, in_hash, in_key); - - Memory::WriteStruct(addressCtx,&ctx); - return res; -} -int sceSdGetLastIndex_(pspChnnlsvContext1& ctx, u8* in_hash, u8* in_key) -{ - if(ctx.keyLength >= 17) - { - return -1026; - } int num = 0; - switch(ctx.mode) + switch(mode) { - case 6: - num = 17; - break; - case 4: - num = 13; + case 1: + num = 3; break; case 2: num = 5; break; - case 1: - num = 3; - break; case 3: num = 12; break; + case 4: + num = 13; + break; + case 6: + num = 17; + break; default: num = 16; break; } + return num; +} +int numFromMode2(int mode) +{ + int num = 18; + if (mode == 1) + num = 4; + else if (mode == 3) + num = 14; + return num; +} - memset(dataBuf2,0,16); +int typeFromMode(int mode) +{ + return (mode == 1 || mode == 2) ? 83 : + ((mode == 3 || mode == 4) ? 87 : 100); +} - int res = sub_000013C8(dataBuf,16,num); - if(res != 0) - { +int kirkSendCmd(u8* data, int length, int num, bool encrypt) +{ + *(int*)(data+0) = encrypt ? KIRK_MODE_ENCRYPT_CBC : KIRK_MODE_DECRYPT_CBC; + *(int*)(data+4) = 0; + *(int*)(data+8) = 0; + *(int*)(data+12) = num; + *(int*)(data+16) = length; + + if (sceUtilsBufferCopyWithRange(data, length + 20, data, length + 20, encrypt ? KIRK_CMD_ENCRYPT_IV_0 : KIRK_CMD_DECRYPT_IV_0)) + return -257; + + return 0; +} + +int kirkSendFuseCmd(u8* data, int length, bool encrypt) +{ + *(int*)(data+0) = encrypt ? KIRK_MODE_ENCRYPT_CBC : KIRK_MODE_DECRYPT_CBC; + *(int*)(data+4) = 0; + *(int*)(data+8) = 0; + *(int*)(data+12) = 256; + *(int*)(data+16) = length; + + // Note: CMD 5 and 8 are not available, will always return -1 + if (sceUtilsBufferCopyWithRange(data, length + 20, data, length + 20, encrypt ? KIRK_CMD_ENCRYPT_IV_FUSE : KIRK_CMD_DECRYPT_IV_FUSE)) + return -258; + + return 0; +} + +int sub_15B0(u8* data, int alignedLen, u8* buf, int val) +{ + u8 sp0[16]; + memcpy(sp0, data+alignedLen+4, 16); + + int res = kirkSendCmd(data, alignedLen, val, false); + if (res) return res; + + memxor(data, buf, 16); + memcpy(buf, sp0, 16); + return 0; +} + +int sub_0000(u8* data_out, u8* data, int alignedLen, u8* data2, int& data3, int mode) +{ + memcpy(data_out+20, data2, 16); + // Mode 1:2 is 83, 3:4 is 87, 5:6 is 100 + int type = typeFromMode(mode); + int res; + + if (type == 87) + memxor(data_out+20, key19AC, 16); + else if (type == 100) + memxor(data_out+20, key19DC, 16); + + // Odd is Cmd, Even is FuseCmd + switch(mode) + { + case 2: case 4: case 6: res = kirkSendFuseCmd(data_out, 16, false); + break; + case 1: case 3: default:res = kirkSendCmd(data_out, 16, numFromMode2(mode), false); + break; } - u8 data1[16]; - u8 data2[16]; + if (type == 87) + memxor(data_out, key199C, 16); + else if (type == 100) + memxor(data_out, key19CC, 16); - memcpy(data1,dataBuf2,16); - int tmp1 = 0; - if((s8)data1[0] < 0) - tmp1 = 135; + if (res) + return res; + + u8 sp0[16], sp16[16]; + memcpy(sp16, data_out, 16); + if (data3 == 1) + { + memset(sp0, 0, 16); + } + else + { + memcpy(sp0, sp16, 12); + *(u32*)(sp0+12) = data3-1; + } + + if (alignedLen > 0) + { + for(int i = 20; i < alignedLen + 20; i += 16) + { + memcpy(data_out+i, sp16, 12); + *(u32*)(data_out+12+i) = data3; + data3++; + } + } + + res = sub_15B0(data_out, alignedLen, sp0, type); + if (res) + return res; + + if (alignedLen > 0) + memxor(data, data_out, alignedLen); + + return 0; +} + +int sub_1510(u8* data, int size, u8* result , int num) +{ + memxor(data+20, result, 16); + + int res = kirkSendCmd(data, size, num, true); + if(res) + return res; + + memcpy(result, data+size+4, 16); + return 0; +} + +int sub_17A8(u8* data) +{ + if (sceUtilsBufferCopyWithRange(data, 20, 0, 0, 14) == 0) + return 0; + return -261; +} + +int sceSdGetLastIndex(u32 addressCtx, u32 addressHash, u32 addressKey) +{ + pspChnnlsvContext1 ctx; + Memory::ReadStruct(addressCtx, &ctx); + int res = sceSdGetLastIndex_(ctx, Memory::GetPointer(addressHash), Memory::GetPointer(addressKey)); + Memory::WriteStruct(addressCtx, &ctx); + return res; +} + +int sceSdGetLastIndex_(pspChnnlsvContext1& ctx, u8* in_hash, u8* in_key) +{ + if(ctx.keyLength >= 17) + return -1026; + + int num = numFromMode(ctx.mode); + + memset(dataBuf2, 0, 16); + + int res = kirkSendCmd(dataBuf, 16, num, true); + if(res) + return res; + + u8 data1[16], data2[16]; + + memcpy(data1, dataBuf2, 16); + int tmp1 = (data1[0] & 0x80) ? 135 : 0; for(int i = 0; i < 15; i++) { @@ -373,72 +269,37 @@ int sceSdGetLastIndex_(pspChnnlsvContext1& ctx, u8* in_hash, u8* in_key) int oldKeyLength = ctx.keyLength; *(s8*)(ctx.key + ctx.keyLength) = -128; - if(oldKeyLength + 1 < 16) - { - for(int i = oldKeyLength + 1; i < 16; i++) - { - *(s8*)(ctx.key + i) = 0; - } - } + int i = oldKeyLength + 1; + if(i < 16) + memset(ctx.key + i, 0, 16 - i); } - for(int i = 0; i < 16; i++) - { - ctx.key[i] = ctx.key[i] ^ data1[i]; - } + memxor(ctx.key, data1, 16); + memcpy(dataBuf2, ctx.key, 16); + memcpy(data2, ctx.result, 16); - for(int i = 0; i < 16; i++) - { - dataBuf2[i] = ctx.key[i]; - } - - for(int i = 0; i < 16; i++) - { - data2[i] = ctx.result[i]; - } - int ret = sub_00001510(dataBuf,16,data2,num); - if(ret != 0) - { + int ret = sub_1510(dataBuf, 16, data2, num); + if(ret) return ret; - } - if((u32)(ctx.mode-3) < 2) - { - for(int i = 0; i < 16; i++) - { - data2[i] = data2[i] ^ hash198C[i]; - } - } - else if((u32)(ctx.mode-5) < 2) - { - for(int i = 0; i < 16; i++) - { - data2[i] = data2[i] ^ hash19BC[i]; - } - } + if(ctx.mode == 3 || ctx.mode == 4) + memxor(data2, hash198C, 16); + else if(ctx.mode == 5 || ctx.mode == 6) + memxor(data2, hash19BC, 16); int cond = ((ctx.mode ^ 0x2) < 1 || (ctx.mode ^ 0x4) < 1 || ctx.mode == 6); if(cond != 0) { - for(int i = 0; i < 16; i++) - { - dataBuf2[i] = data2[i]; - } - int ret = sub_00001468(dataBuf,16); - if(ret != 0) - { + memcpy(dataBuf2, data2, 16); + int ret = kirkSendFuseCmd(dataBuf, 16, true); + if(ret) return ret; - } - int res = sub_000013C8(dataBuf,16,num); - if(res != 0) - { - return res; - } - for(int i = 0; i < 16; i++) - { - data2[i] = dataBuf2[i]; - } + int res = kirkSendCmd(dataBuf, 16, num, true); + if(res) + return res; + + memcpy(data2, dataBuf2, 16); } if(in_key != 0) @@ -448,37 +309,16 @@ int sceSdGetLastIndex_(pspChnnlsvContext1& ctx, u8* in_hash, u8* in_key) data2[i] = in_key[i] ^ data2[i]; } - for(int i = 0; i < 16; i++) - { - dataBuf2[i] = data2[i]; - } + memcpy(dataBuf2, data2, 16); - int res = sub_000013C8(dataBuf,16,num); - if(res != 0) - { + int res = kirkSendCmd(dataBuf, 16, num, true); + if(res) return res; - } - for(int i = 0; i < 16; i++) - { - data2[i] = dataBuf2[i]; - } - } - for(int i = 0; i < 16; i++) - { - in_hash[i] = data2[i]; - } - for(int i = 0; i < 16; i++) - { - ctx.result[i] = 0; + memcpy(data2, dataBuf2, 16); } - - for(int i = 0; i < 16; i++) - { - ctx.key[i] = 0; - } - ctx.keyLength = 0; - ctx.mode = 0; + memcpy(in_hash, data2, 16); + sceSdSetIndex_(ctx, 0); return 0; } @@ -495,8 +335,8 @@ int sceSdSetIndex(u32 addressCtx, int value) int sceSdSetIndex_(pspChnnlsvContext1& ctx, int value) { ctx.mode = value; - memset(ctx.result,0,16); - memset(ctx.key,0,16); + memset(ctx.result, 0, 16); + memset(ctx.key, 0, 16); ctx.keyLength = 0; return 0; } @@ -505,104 +345,52 @@ int sceSdSetIndex_(pspChnnlsvContext1& ctx, int value) int sceSdRemoveValue(u32 addressCtx, u32 addressData, int length) { pspChnnlsvContext1 ctx; - Memory::ReadStruct(addressCtx,&ctx); - u8* data; - data = Memory::GetPointer(addressData); + Memory::ReadStruct(addressCtx, &ctx); + int res = sceSdRemoveValue_(ctx, Memory::GetPointer(addressData), length); + Memory::WriteStruct(addressCtx, &ctx); - int res = sceSdRemoveValue_(ctx, data, length); - - Memory::WriteStruct(addressCtx,&ctx); return res; } int sceSdRemoveValue_(pspChnnlsvContext1& ctx, u8* data, int length) { if(ctx.keyLength >= 17) - { return -1026; - } + if(ctx.keyLength + length < 17) { - if(length == 0) - { - return 0; - } - for(int i = 0; i < length; i++) - { - ctx.key[ctx.keyLength+i] = data[i]; - } + memcpy(ctx.key+ctx.keyLength, data, length); ctx.keyLength = ctx.keyLength + length; return 0; } - int mode = ctx.mode; - int num = 0; - switch(mode) - { - case 6: - num = 17; - break; - case 4: - num = 13; - break; - case 2: - num = 5; - break; - case 1: - num = 3; - break; - case 3: - num = 12; - break; - default: - num = 16; - break; - } + int num = numFromMode(ctx.mode); - memset(dataBuf2,0,2048); + memset(dataBuf2, 0, 2048); + memcpy(dataBuf2, ctx.key, ctx.keyLength); - if(ctx.keyLength > 0) - { - memcpy(dataBuf2,ctx.key,ctx.keyLength); - } int len = (ctx.keyLength + length) & 0xF; if(len == 0) len = 16; - int oldLength = ctx.keyLength; + int newSize = ctx.keyLength; ctx.keyLength = len; int diff = length - len; - if(len != 0) + memcpy(ctx.key, data+diff, len); + for(int i = 0; i < diff; i++) { - memcpy(ctx.key,data+diff,len); - } - - int newSize = oldLength; - if(diff != 0) - { - for(int i = 0; i < diff; i++) + if(newSize == 2048) { - if(newSize == 2048) - { - int res = sub_00001510(dataBuf,2048,ctx.result,num); - if(res != 0) - { - return res; - } - newSize = 0; - } - dataBuf2[newSize] = data[i]; - newSize++; + int res = sub_1510(dataBuf, 2048, ctx.result, num); + if(res) + return res; + newSize = 0; } + dataBuf2[newSize] = data[i]; + newSize++; } - if(newSize == 0) - { - return 0; - } - int res = sub_00001510(dataBuf,newSize,ctx.result, num); - if(res == 0) - { - return res; - } + if(newSize) + sub_1510(dataBuf, newSize, ctx.result, num); + // The RE code showed this always returning 0. I suspect it would want to return res instead. return 0; } @@ -626,116 +414,49 @@ int sceSdCreateList_(pspChnnlsvContext2& ctx2, int mode, int uknw, u8* data, u8* ctx2.unkn = 1; if (uknw == 2) { - for(int i = 0; i < 16; i++) - { - ctx2.unknown[i] = data[i]; - } - if (cryptkey == 0) - { - return 0; - } + memcpy(ctx2.cryptedData, data, 16); + if (cryptkey) + memxor(ctx2.cryptedData, cryptkey, 16); - for(int i = 0; i < 16; i++) - { - ctx2.unknown[i] = ctx2.unknown[i] ^ cryptkey[i]; - } return 0; } else if (uknw == 1) { u8 kirkHeader[37]; u8* kirkData = kirkHeader+20; - int res = sub_000017A8(kirkHeader); - if (res != 0) - { + int res = sub_17A8(kirkHeader); + if (res) return res; - } - for(int i = 15; i >= 0 ; i--) + + memcpy(kirkHeader+20, kirkHeader, 16); + memset(kirkHeader+32, 0, 4); + + int type = typeFromMode(mode); + if (type == 87) + memxor(kirkData, key199C, 16); + else if (type == 100) + memxor(kirkData, key19CC, 16); + + switch (mode) { - kirkHeader[i+20] = kirkHeader[i]; - } - for(int i = 0; i < 4; i++) - { - kirkHeader[i+32] = 0; + case 2: case 4: case 6: res = kirkSendFuseCmd(kirkHeader, 16, true); + break; + case 1: case 3: default:res = kirkSendCmd(kirkHeader, 16, numFromMode2(mode), true); + break; } - if (mode == 6) - { - for(int i = 0; i < 16; i++) - { - kirkData[i] = kirkData[i] ^ key000019CC[i]; - } - res = sub_00001468(kirkHeader, 16); - for(int i = 0; i < 16; i++) - { - kirkData[i] = kirkData[i] ^ key000019DC[i]; - } - } - else if (mode == 4) - { - for(int i = 0; i < 16; i++) - { - kirkData[i] = kirkData[i] ^ key0000199C[i]; - } - res = sub_00001468(kirkHeader, 16); - for(int i = 0; i < 16; i++) - { - kirkData[i] = kirkData[i] ^ key000019AC[i]; - } - } - else if (mode == 2) - { - res = sub_00001468(kirkHeader, 16); - } - else if (mode == 1) - { - res = sub_000013C8(kirkHeader, 16, 4); - } - else if (mode == 3) - { - for(int i = 0; i < 16; i++) - { - kirkData[i] = kirkData[i] ^ key0000199C[i]; - } - res = sub_000013C8(kirkHeader, 16, 14); - for(int i = 0; i < 16; i++) - { - kirkData[i] = kirkData[i] ^ key000019AC[i]; - } - } - else - { - for(int i = 0; i < 16; i++) - { - kirkData[i] = kirkData[i] ^ key000019CC[i]; - } - res = sub_000013C8(kirkHeader, 16, 18); - for(int i = 0; i < 16; i++) - { - kirkData[i] = kirkData[i] ^ key000019DC[i]; - } - } + if (type == 87) + memxor(kirkData, key19AC, 16); + else if (type == 100) + memxor(kirkData, key19DC, 16); - if (res != 0) - { + if (res) return res; - } - for(int i = 0; i < 16; i++) - { - ctx2.unknown[i] = kirkData[i]; - } - for(int i = 0; i < 16; i++) - { - data[i] = kirkData[i]; - } - if (cryptkey != 0) - { - for(int i = 0; i < 16; i++) - { - ctx2.unknown[i] = ctx2.unknown[i] ^ cryptkey[i]; - } - } + memcpy(ctx2.cryptedData, kirkData, 16); + memcpy(data, kirkData, 16); + if (cryptkey) + memxor(ctx2.cryptedData, cryptkey, 16); } return 0; @@ -770,12 +491,10 @@ int sceSdSetMember_(pspChnnlsvContext2& ctx, u8* data, int alignedLen) { for(i = 0; alignedLen >= 2048; i += 2048) { - int res = sub_00000000(kirkData, data + i, 2048, ctx.unknown, ctx.unkn, ctx.mode); - alignedLen = alignedLen - 2048; - if (res != 0) - { + int res = sub_0000(kirkData, data + i, 2048, ctx.cryptedData, ctx.unkn, ctx.mode); + alignedLen -= 2048; + if (res) return res; - } } } if (alignedLen == 0) @@ -783,8 +502,7 @@ int sceSdSetMember_(pspChnnlsvContext2& ctx, u8* data, int alignedLen) return 0; } - int res = sub_00000000(kirkData, data + i, alignedLen, ctx.unknown, ctx.unkn, ctx.mode); - return res; + return sub_0000(kirkData, data + i, alignedLen, ctx.cryptedData, ctx.unkn, ctx.mode); } int sceChnnlsv_21BE78B4(u32 ctxAddr) @@ -800,10 +518,7 @@ int sceChnnlsv_21BE78B4(u32 ctxAddr) int sceChnnlsv_21BE78B4_(pspChnnlsvContext2& ctx) { - for(int i = 0; i < 16; i++) - { - ctx.unknown[i] = 0; - } + memset(ctx.cryptedData, 0, 16); ctx.unkn = 0; ctx.mode = 0; @@ -822,6 +537,6 @@ const HLEFunction sceChnnlsv[] = void Register_sceChnnlsv() { - RegisterModule("sceChnnlsv",ARRAY_SIZE(sceChnnlsv),sceChnnlsv); + RegisterModule("sceChnnlsv", ARRAY_SIZE(sceChnnlsv), sceChnnlsv); kirk_init(); } diff --git a/Core/HLE/sceChnnlsv.h b/Core/HLE/sceChnnlsv.h index d6ec1480f3..df7604735f 100644 --- a/Core/HLE/sceChnnlsv.h +++ b/Core/HLE/sceChnnlsv.h @@ -23,7 +23,7 @@ typedef struct _pspChnnlsvContext1 { /** Context data */ u8 result[0x10]; - u8 key[0x10]; + u8 key[0x10]; int keyLength; } pspChnnlsvContext1; @@ -31,7 +31,7 @@ typedef struct _pspChnnlsvContext2 { /** Context data */ int mode; int unkn; - u8 unknown[0x92]; + u8 cryptedData[0x92]; } pspChnnlsvContext2; int sceSdSetIndex_(pspChnnlsvContext1& ctx, int value); From a6c9c7e7c6a2ababc9108771e9e620be68bbafa3 Mon Sep 17 00:00:00 2001 From: raven02 Date: Fri, 21 Jun 2013 02:54:10 +0800 Subject: [PATCH 20/80] Comment out date/time format buttons --- UI/MenuScreens.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index 7d613267b8..c4f7428b14 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -1172,6 +1172,7 @@ void SystemScreen::render() { y += 10; } + /* bool time = g_Config.iTimeFormat > 0 ; UICheckBox(GEN_ID, x, y += stride, s->T("Time Format"), ALIGN_TOPLEFT, &time); if (time) { @@ -1219,7 +1220,8 @@ void SystemScreen::render() { y += 10; } else g_Config.iDateFormat = 0; - + */ + UICheckBox(GEN_ID, x, y += stride, s->T("Enable Cheats"), ALIGN_TOPLEFT, &g_Config.bEnableCheats); if (g_Config.bEnableCheats) { HLinear hlinear1(x + 60, y += stride + 10, 20); From eaede89761c97292b0c929abbcc0d2765235a46e Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 20 Jun 2013 21:34:35 +0200 Subject: [PATCH 21/80] Support tex level factor 000001 (fixed at 0, that is, mipmapping off) --- GPU/GLES/DisplayListInterpreter.cpp | 5 ++--- GPU/GLES/TextureCache.cpp | 5 ++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index 118575425e..af91a48b3b 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -956,9 +956,8 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { break; case GE_CMD_TEXLEVEL: - if (data == 1) - WARN_LOG_REPORT_ONCE(texLevel1, G3D, "Unsupported texture level bias settings: %06x", data) - else if (data != 0) + // We support 000001, it means fixed at mip level 0. Don't report 0 as it's normal. + if (data != 1 && data != 0) WARN_LOG_REPORT_ONCE(texLevel2, G3D, "Unsupported texture level bias settings: %06x", data); break; #endif diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 39984d410c..82772eb823 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -492,6 +492,9 @@ void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) { bool sClamp = gstate.texwrap & 1; bool tClamp = (gstate.texwrap>>8) & 1; + + bool noMip = gstate.texlevel == 0x000001; // Fix texlevel at 0 + if (entry.maxLevel == 0) { // Enforce no mip filtering, for safety. minFilt &= 1; // no mipmaps yet @@ -511,7 +514,7 @@ void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) { minFilt |= 1; } - if (!g_Config.bMipMap) { + if (!g_Config.bMipMap || noMip) { magFilt &= 1; minFilt &= 1; } From 27f8f6a2f5c0b8c82593b6688330ca42de1b4fe9 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 20 Jun 2013 21:35:04 +0200 Subject: [PATCH 22/80] Logging reduction --- Core/HLE/sceAtrac.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 3f3ea5175f..d34d52b565 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -528,7 +528,7 @@ u32 _AtracAddStreamData(int atracID, u8 *buf, u32 bytesToAdd) { // because that function would tell games how to add the left stream data. u32 sceAtracAddStreamData(int atracID, u32 bytesToAdd) { - INFO_LOG(HLE, "sceAtracAddStreamData(%i, %08x)", atracID, bytesToAdd); + DEBUG_LOG(HLE, "sceAtracAddStreamData(%i, %08x)", atracID, bytesToAdd); Atrac *atrac = getAtrac(atracID); if (!atrac) { return 0; From 2953d01d15d97fd3922fc18f2633b9241d3e04c5 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 20 Jun 2013 21:51:31 +0200 Subject: [PATCH 23/80] UI tweaks --- UI/MenuScreens.cpp | 2 +- UI/PluginScreen.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index e682b32bae..ced7085e53 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -1511,7 +1511,7 @@ static const char * credits[] = { "PPSSPP is intended for educational purposes only.", "", "Please make sure that you own the rights to any games", - "you play by owning the UMD or buying the digital", + "you play by owning the UMD or by buying the digital", "download from the PSN store on your real PSP.", "", "", diff --git a/UI/PluginScreen.cpp b/UI/PluginScreen.cpp index bbc37c8b2c..8fb535b6ae 100644 --- a/UI/PluginScreen.cpp +++ b/UI/PluginScreen.cpp @@ -55,7 +55,7 @@ void PluginScreen::CreateViews() { Margins textMargins(20,17); Margins buttonMargins(10,10); - root_->Add(new TextView(UBUNTU24, "Atrac3+ Audio Decoding Support", ALIGN_HCENTER, 1.0f, new LinearLayoutParams(textMargins))); + root_->Add(new TextView(UBUNTU24, "Atrac3+ Audio Support", ALIGN_HCENTER, 1.5f, new LinearLayoutParams(textMargins))); ViewGroup *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0)); LinearLayout *scrollContents = new LinearLayout(ORIENT_VERTICAL); @@ -66,11 +66,11 @@ void PluginScreen::CreateViews() { const char *legalityNotice = p->T("Origins are dubious", "* Mai's Atrac3+ decoder is currently required\n" - "for audio in many games.\n" + "for background audio and voice in many games.\n" "Please note that the origins of this code are dubious.\n" "Choose More Information for more information."); - scrollContents->Add(new TextView(0, legalityNotice, ALIGN_LEFT, 1.0f, new LinearLayoutParams(textMargins) )); + scrollContents->Add(new TextView(0, legalityNotice, ALIGN_LEFT, 0.65f, new LinearLayoutParams(textMargins) )); progress_ = root_->Add(new ProgressBar()); progress_->SetVisibility(V_GONE); From 2694343243e68e6d3e940f14258234486bb31cfc Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 20 Jun 2013 22:18:44 +0200 Subject: [PATCH 24/80] Another attempt at reusing bigger framebuffers. Doesn't solve everything but hopefully won't break much like the last attempt. --- GPU/GLES/Framebuffer.cpp | 35 ++++++++++++++++++++++++++++------- GPU/GLES/Framebuffer.h | 6 ++++++ GPU/GLES/StateMapping.cpp | 4 ++-- native | 2 +- 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 2f83bb05fa..8ea2711612 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -350,16 +350,35 @@ void FramebufferManager::SetRenderFrameBuffer() { int drawing_width, drawing_height; GuessDrawingSize(drawing_width, drawing_height); - // Find a matching framebuffer + int buffer_width = drawing_width; + int buffer_height = drawing_height; + + // Find a matching framebuffer, same size or bigger VirtualFramebuffer *vfb = 0; for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { VirtualFramebuffer *v = *iter; - if (MaskedEqual(v->fb_address, fb_address) && v->width == drawing_width && v->height == drawing_height && v->format == fmt) { - // Let's not be so picky for now. Let's say this is the one. - vfb = v; - // Update fb stride in case it changed - vfb->fb_stride = fb_stride; - break; + if (MaskedEqual(v->fb_address, fb_address) && v->format == fmt) { + // Okay, let's check the sizes. If the new one is bigger than the old one, recreate. + // If the opposite, just use it and hope that the game sets scissors accordingly. + if (v->bufferWidth >= drawing_width && v->bufferHeight >= drawing_height) { + // Let's not be so picky for now. Let's say this is the one. + vfb = v; + // Update fb stride in case it changed + vfb->fb_stride = fb_stride; + // Just hack the width/height and we should be fine. also hack renderwidth/renderheight? + v->width = drawing_width; + v->height = drawing_height; + break; + } else { + INFO_LOG(HLE, "Embiggening framebuffer (%i, %i) -> (%i, %i)", (int)v->width, (int)v->height, drawing_width, drawing_height); + // drawing_width or drawing_height is bigger. Let's recreate with the max. + // To do this right we should copy the data over too, but meh. + buffer_width = std::max((int)v->width, drawing_width); + buffer_height = std::max((int)v->height, drawing_height); + delete v; + vfbs_.erase(iter); + break; + } } } @@ -379,6 +398,8 @@ void FramebufferManager::SetRenderFrameBuffer() { vfb->height = drawing_height; vfb->renderWidth = (u16)(drawing_width * renderWidthFactor); vfb->renderHeight = (u16)(drawing_height * renderHeightFactor); + vfb->bufferWidth = buffer_width; + vfb->bufferHeight = buffer_height; vfb->format = fmt; vfb->usageFlags = FB_USAGE_RENDERTARGET; vfb->dirtyAfterDisplay = true; diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 9285cab997..78a6dfa3eb 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -54,10 +54,16 @@ struct VirtualFramebuffer { int z_stride; // There's also a top left of the drawing region, but meh... + + // width/height: The detected size of the current framebuffer. u16 width; u16 height; + // renderWidth/renderHeight: The actual size we render at. May be scaled to render at higher resolutions. u16 renderWidth; u16 renderHeight; + // bufferWidth/bufferHeight: The actual (but non scaled) size of the buffer we render to. May only be bigger than width/height. + u16 bufferWidth; + u16 bufferHeight; u16 usageFlags; diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 813be0caa8..4582fa18ca 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -265,8 +265,8 @@ void TransformDrawEngine::ApplyDrawState(int prim) { if (g_Config.bBufferedRendering) { renderX = 0; renderY = 0; - renderWidth = framebufferManager_->GetRenderWidth(); - renderHeight = framebufferManager_->GetRenderHeight(); + renderWidth = framebufferManager_->GetRenderWidth(); + renderHeight = framebufferManager_->GetRenderHeight(); renderWidthFactor = (float)renderWidth / framebufferManager_->GetTargetWidth(); renderHeightFactor = (float)renderHeight / framebufferManager_->GetTargetHeight(); } else { diff --git a/native b/native index deb4ba8323..0a26fae856 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit deb4ba83235a86491d073db73a1e5bab2a5ce523 +Subproject commit 0a26fae856aecdaa426d92fa5dde101b19487796 From 7ebc5b6daaaca67507a1c2775c9df21ddd6bc130 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 20 Jun 2013 22:37:19 +0200 Subject: [PATCH 25/80] Fix typo, update lang (latest Swedish translation and more) --- UI/MenuScreens.cpp | 2 +- lang | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index ced7085e53..4a47e67991 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -1161,7 +1161,7 @@ void SystemScreen::render() { UICheckBox(GEN_ID, x, y += stride, s->T("Fast Memory", "Fast Memory (unstable)"), ALIGN_TOPLEFT, &g_Config.bFastMemory); UICheckBox(GEN_ID, x, y += stride, s->T("Daylight Savings"), ALIGN_TOPLEFT, &g_Config.bDayLightSavings); - UICheckBox(GEN_ID, x, y += stride, s->T("Button Perference"), ALIGN_TOPLEFT, &g_Config.bButtonPreference); + UICheckBox(GEN_ID, x, y += stride, s->T("Button Preference"), ALIGN_TOPLEFT, &g_Config.bButtonPreference); if (g_Config.bButtonPreference) { char button[256]; std::string type; diff --git a/lang b/lang index cf710f2017..81dc0b9141 160000 --- a/lang +++ b/lang @@ -1 +1 @@ -Subproject commit cf710f2017db93c4019ce53a03764edf3136fa84 +Subproject commit 81dc0b9141e7d1445efc7b152b88ab16ff75c737 From 34e6b72599fd35d2954bfa47b60242651e05626e Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 20 Jun 2013 23:06:05 +0200 Subject: [PATCH 26/80] Fix nomip check bug --- GPU/GLES/TextureCache.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 82772eb823..e6fee64e7a 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -492,8 +492,7 @@ void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) { bool sClamp = gstate.texwrap & 1; bool tClamp = (gstate.texwrap>>8) & 1; - - bool noMip = gstate.texlevel == 0x000001; // Fix texlevel at 0 + bool noMip = (gstate.texlevel & 0xFFFFFF) == 0x000001; // Fix texlevel at 0 if (entry.maxLevel == 0) { // Enforce no mip filtering, for safety. From 916184d2f4bcab877f7830f5b91432580fd070c5 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 20 Jun 2013 23:30:21 +0200 Subject: [PATCH 27/80] Optimize the most common light type, directional, by doing the normalization beforehand. May shorten some shaders below Mali's limit with luck. --- GPU/GLES/ShaderManager.cpp | 17 ++++++++++++++++- GPU/GLES/VertexShaderGenerator.cpp | 7 ++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index c4ab5939c0..b727481bc9 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -390,7 +390,22 @@ void LinkedShader::updateUniforms() { for (int i = 0; i < 4; i++) { if (dirtyUniforms & (DIRTY_LIGHT0 << i)) { - if (u_lightpos[i] != -1) glUniform3fv(u_lightpos[i], 1, gstate_c.lightpos[i]); + GELightType type = (GELightType)((gstate.ltype[i] >> 8) & 3); + if (type == GE_LIGHTTYPE_DIRECTIONAL) { + // Prenormalize + float x = gstate_c.lightpos[i][0]; + float y = gstate_c.lightpos[i][1]; + float z = gstate_c.lightpos[i][2]; + float len = sqrtf(x*x+y*y+z*z); + if (len == 0.0f) + len = 1.0f; + else + len = 1.0f / len; + float vec[3] = { x / len, y / len, z / len }; + if (u_lightpos[i] != -1) glUniform3fv(u_lightpos[i], 1, vec); + } else { + if (u_lightpos[i] != -1) glUniform3fv(u_lightpos[i], 1, gstate_c.lightpos[i]); + } if (u_lightdir[i] != -1) glUniform3fv(u_lightdir[i], 1, gstate_c.lightdir[i]); if (u_lightatt[i] != -1) glUniform3fv(u_lightatt[i], 1, gstate_c.lightatt[i]); if (u_lightangle[i] != -1) glUniform1f(u_lightangle[i], gstate_c.lightangle[i]); diff --git a/GPU/GLES/VertexShaderGenerator.cpp b/GPU/GLES/VertexShaderGenerator.cpp index 785e6db6b5..9ae40d540d 100644 --- a/GPU/GLES/VertexShaderGenerator.cpp +++ b/GPU/GLES/VertexShaderGenerator.cpp @@ -429,9 +429,10 @@ void GenerateVertexShader(int prim, char *buffer, bool useHWTransform) { GELightComputation comp = (GELightComputation)(gstate.ltype[i] & 3); GELightType type = (GELightType)((gstate.ltype[i] >> 8) & 3); - if (type == GE_LIGHTTYPE_DIRECTIONAL) - WRITE(p, " toLight = normalize(u_lightpos%i);\n", i); - else { + if (type == GE_LIGHTTYPE_DIRECTIONAL) { + // We prenormalize light positions for directional lights. + WRITE(p, " toLight = u_lightpos%i;\n", i); + } else { WRITE(p, " toLight = u_lightpos%i - worldpos;\n", i); WRITE(p, " distance = length(toLight);\n"); WRITE(p, " toLight /= distance;\n"); From dc17cb1e2b902bb45346c423c86b2c8191c415e3 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 21 Jun 2013 00:28:05 +0200 Subject: [PATCH 28/80] New icons --- android/res/drawable-hdpi/ic_launcher.png | Bin 6056 -> 5994 bytes android/res/drawable-ldpi/ic_launcher.png | Bin 2360 -> 2482 bytes android/res/drawable-mdpi/ic_launcher.png | Bin 3504 -> 3640 bytes android/res/drawable-xhdpi/ic_launcher.png | Bin 8702 -> 8422 bytes android/res/drawable-xxhdpi/ic_launcher.png | Bin 14445 -> 13358 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/android/res/drawable-hdpi/ic_launcher.png b/android/res/drawable-hdpi/ic_launcher.png index 99e96d00e280c96e323a76b2e6d9c922c47f76b8..5f52b44ea6d672f194297991da3e3075a19c4fe2 100644 GIT binary patch delta 5938 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 TDE(y#00000NkvXXu0mjf72$X| diff --git a/android/res/drawable-mdpi/ic_launcher.png b/android/res/drawable-mdpi/ic_launcher.png index 75f936ed1d8a3008b405603da0df4afa935f7af4..854af501a5fc5d41edfc879ed1c5fb3309d77153 100644 GIT binary patch delta 3564 zcmVhZhy5&L_t(&fyG*TbR5-r|J~i4*`3+&I&{E%#9%d%_( z28waw5}IOYT22WC3QmHtQwYyMNShY~8VZG!CY+oGPB^i13OUpP2mC^QNEUu0A=`>% zTatIBUG2{9&hy^>u@6a>WVE)@erM;*-8(b){(j$g@ArN8&VLZb82+3>2q6Fn0KynE zrn`O1bT^m*Y|0$wx*In5d~<4>c|j;6guKWY)2Fk)D<(h)$$~WjFfYIJp*haNqE(z# zY~%%Tv5B`CK!?Y~lp_H5RKd zj}XEG=|S#vi3s>T2>3k+20U;&?H~Y#B5|DW3&Se9Rsi?_!2dk~0K_q~e5Jdv?3Vyw zv6$iay5P@%4fs9a`H7qJdK|Er&EN!a8Pe99Hpzzo>wi7jy4O`$#!8EWWJS|LmF#j zh5=1C;D2&C0Ybo8#m39%cT&llKyt8eCjeXsN5=qE_4GMi)<=~MsD=SX0GHDaP7v#H zne$+xp_f-6V~jCENatN0w90NqV2m%n_ zoVm^T0Pm#l$^jl7(M?Of`j0bx#igq_(cZ}0#B0pFFh_|Ey$#@Q0NRxhpqiZAZ?(JD z3=Iv#<#vwK^h87`1$uk?0RWs$EC*o9f({0Q{K~ICQZIV^D>bLsd_qiiq*=k^aU$S%BM|T)==VSrZP>Y|9q0Rus}|gNtBVk#WQS4S1Q=tq zvaW8wzkHK2&=@bFNoKS#(hZ(WWZ+KnYs4H4Z-3$wJTsI)(>8M z{5JsBU%&C0J3K|@{~{;k;HeX5;C~6Y;V%ioWFpWs9T(1B006|p{oil<-J8A5fB)@9 z(G$Ff09aB#7wIm>3S%SlXLQ-&5~1nJ0u&Zn04QS!j9U8T7AGt2+C0zU4X&~XVk2i0 z>n*&sGII~sKl$(Xhr5q$-0||$zh1EVE8EMLt^SGN_WpG=93}Cn1aGk)iGQdBYEWo$ z^3e9Te)?~LK)@Q9Is0WoV5zJuK``J3rEDBf#wI4=@i;Iv6m>M+d}lo&WG`ckS~9>b zrHzfw*$ZxHvO3*OyiHun*@T6}Z1y69*yVB{5OBjT3izzE8-RUj@!W>JH*b0VmE+rg z`RtJ&Y`y#1e|X~8p3=&f0Dn}3dk3=G)CapyeiH)&`r?Ow__&$pYdOx0nt4@Fbz@R( zkC0%wdF66BL74%Y%eMkR3jk^XU|#pwzrG?kJ$C_w1Jb#E!0*m@1~>dZH+Y`QnYg-Y z1`f4%;7`5%yu)Ac(2}oyYklRiGn+s9`E%Q6T-~r_!MfYOD|-F+gMSc9GIHVPyWf1V zwc+;rt`>d4EtFBromT}A6I5NF5a>8UnQL~Z2+AnTR$DHgvH*agX;FZ1v#QH5tEvPZ zw-cAjcx_f5%a_eZMR_p}96F8!qm_ZmtA2aSBQI|2ZQr}){hw@pr15JHzvZ1-@#vYI z|MMgOQ=q!$XADg6dVgIgE-4rRKLOCmNSHYe9B+YcW;n0x|bbQ`Jc&F=bv5s>_;y@{VIUl0MP5c@$BDng3wGE1!EMk zm;{?(n-nNbk3GOXqq^^%eje$_Xhc_4oHu(9KX$G7x#9&J`qQk$l0 zIQdy8_U&)QP;@xgj;b3_^vqdHr{$_{K-EUh#6mzBg~=u?2Vly&QA3rbHnWwli9}=Y zdEKDdiNzS`sEI~Lo0_ql$B4xxoapETr3~?bb59)m!++bMRo{5}JEG73RNXy~-WTca zdhp?#gK@{A;se` zjP&N7#U|DPAcQdnLWp_Q#wWk-E-ZU(NnH(QRh5qij*Eb2h-Ay3v>e3XU<6t+vGc

$VMe`TDV0F0G1At8sP&%^$ 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 FV1lFUr3wH5 diff --git a/android/res/drawable-xhdpi/ic_launcher.png b/android/res/drawable-xhdpi/ic_launcher.png index d59290c61b617602746d0990af7da349ae6ab31b..9cd5e36b885ea920997ce9c7c77f61295b781202 100644 GIT binary patch delta 8377 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%0AL(WvR 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 literal 14445 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 From 4bb9e32f03e4e6f9ee7916865880151577e89f08 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 21 Jun 2013 01:01:28 +0200 Subject: [PATCH 29/80] Atrac3: Bail on bad data --- Core/HLE/sceAtrac.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index d34d52b565..6dcd66e036 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -580,7 +580,11 @@ u32 _AtracDecodeData(int atracID, u8* outbuf, u32 *SamplesNum, u32* finish, int if (avret < 0) { ERROR_LOG(HLE, "avcodec_decode_audio4: Error decoding audio %d", avret); av_free_packet(&packet); - break; + // Avoid getting stuck in a loop (Virtua Tennis) + *SamplesNum = 0; + *finish = 1; + *remains = 0; + return ATRAC_ERROR_ALL_DATA_DECODED; } if (got_frame) { From 0c1dea77d7c165f65609273d0fab603bbef28dab Mon Sep 17 00:00:00 2001 From: raven02 Date: Fri, 21 Jun 2013 11:01:00 +0800 Subject: [PATCH 30/80] Remove Wireframe option in windows --- Windows/ppsspp.rc | Bin 39580 -> 19327 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 8520b2b3e522bb9b8a93f16b2c9aa0cdf646d18d..d33018f4b497875be2dc33af7e948574fe1b1879 100644 GIT binary patch literal 19327 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 P&rb`irmH{Y6WjjUwziOmpXS@ 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 eWPL1M0mv9d9;Gn&sW!9@T~K;QD{#d6-@R From 77983b690aeff811624fc8303f9e187f6b73744a Mon Sep 17 00:00:00 2001 From: raven02 Date: Fri, 21 Jun 2013 11:13:08 +0800 Subject: [PATCH 31/80] Minor UI space fixing --- UI/MenuScreens.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index 4a47e67991..fa5a53e54b 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -483,7 +483,7 @@ void PauseScreen::render() { if (UIButton(GEN_ID, hlinear2, 40, 0, gs->T("+1"), ALIGN_LEFT)) if (g_Config.iFrameSkip < 9) g_Config.iFrameSkip += 1; - + y+=20; } else g_Config.iFrameSkip = 0; From 5f01acd03b14c61baa995369f0c0c686a831408b Mon Sep 17 00:00:00 2001 From: Sacha Date: Fri, 21 Jun 2013 15:33:29 +1000 Subject: [PATCH 32/80] Clean up CMakeLists with regards to FFMPEG. --- CMakeLists.txt | 56 +++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3da63391c4..91ad486465 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,16 +5,22 @@ project(PPSSPP) if(ANDROID) if(ARMEABI OR ARMEABI_V7A) set(ARM ON) + set(FFMPEG ON) endif() endif() +if(BLACKBERRY) + set(CMAKE_SYSTEM_NAME "QNX") +endif() + if (IOS OR BLACKBERRY) set(ARM ON) + set(FFMPEG ON) endif() if (MAEMO) set(ARM ON) - #Maemo's gcc-4.7.2 is strickt + #Maemo's gcc-4.7.2 is strict set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") endif() @@ -28,8 +34,16 @@ else() # Assume x86 set(X86 ON) endif() +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(LINUX ON) + if (NOT USING_GLES2) + set(FFMPEG ON) + endif() +endif() + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(MACOSX ON) + set(FFMPEG ON) endif() if(ANDROID OR BLACKBERRY OR IOS OR PANDORA OR MAEMO OR MACOSX) @@ -49,6 +63,7 @@ option(MAEMO "Set to ON if targeting an Maemo (N900) device" ${MAEMO}) option(IOS "Set to ON if targeting an iOS device" ${IOS}) option(USING_GLES2 "Set to ON if target device uses OpenGL ES 2.0" ${USING_GLES2}) option(USING_QT_UI "Set to ON if you wish to use the Qt frontend wrapper" ${USING_QT_UI}) +option(FFMPEG "Set to OFF to disable FFMPEG usage" ${FFMPEG}) option(HEADLESS "Set to OFF to not generate the PPSSPPHeadless target" ${HEADLESS}) if(ANDROID) @@ -92,7 +107,6 @@ endif() include_directories("${CMAKE_SOURCE_DIR}") if(BLACKBERRY) - set(CMAKE_SYSTEM_NAME "QNX") add_definitions(-DBLACKBERRY) if(BLACKBERRY VERSION_GREATER 10) add_definitions(-DBLACKBERRY10) @@ -454,11 +468,6 @@ elseif(IOS) 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 @@ -471,18 +480,8 @@ elseif(USING_QT_UI) elseif(BLACKBERRY) 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 libswscale.a) set(TargetBin PPSSPPBlackberry) elseif(SDL_FOUND) - if(MACOSX) - include_directories(ffmpeg/macosx/x86_64/include) - link_directories(ffmpeg/macosx/x86_64/lib) - set(nativeExtraLibs ${nativeExtraLibs} libavformat.a libavcodec.a libswresample.a libavutil.a libswscale.a) - endif() set(TargetBin PPSSPPSDL) # Require SDL include_directories(${SDL_INCLUDE_DIR}) @@ -494,14 +493,29 @@ 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.") endif() +if(FFMPEG) + add_definitions(-DUSE_FFMPEG) + include_directories(ffmpeg) + if (BLACKBERRY) + link_directories(ffmpeg/blackberry/armv7/lib) + set(nativeExtraLibs ${nativeExtraLibs} iconv) + elseif(IOS) + link_directories(ffmpeg/ios/universal/lib) + set(nativeExtraLibs ${nativeExtraLibs} iconv bz2) + elseif(MACOSX) + link_directories(ffmpeg/macosx/x86_64/lib) + elseif(LINUX) + # Note: Only 64-bit is supported + link_directories(ffmpeg/linux/x86_64/lib) + endif() + set(nativeExtraLibs ${nativeExtraLibs} libavformat.a libavcodec.a libswresample.a libavutil.a libswscale.a) +endif() + if(X86) set(nativeExtra ${nativeExtra} native/base/backtrace.cpp @@ -722,7 +736,7 @@ 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") +if(LINUX) set(CoreExtraLibs ${CoreExtraLibs} ${CMAKE_DL_LIBS}) endif() From 25702683939cb39d897088870572a26510522f87 Mon Sep 17 00:00:00 2001 From: Sacha Date: Fri, 21 Jun 2013 15:40:52 +1000 Subject: [PATCH 33/80] Remove 'BLACKBERRY10' define as it is now the minimum OS requirement. --- CMakeLists.txt | 3 --- Core/Config.cpp | 6 +++--- Core/Config.h | 2 +- GPU/GLES/Framebuffer.cpp | 2 +- Qt/Settings.pri | 2 +- UI/MenuScreens.cpp | 2 +- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 91ad486465..d46c7a9981 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,9 +108,6 @@ include_directories("${CMAKE_SOURCE_DIR}") if(BLACKBERRY) add_definitions(-DBLACKBERRY) - if(BLACKBERRY VERSION_GREATER 10) - add_definitions(-DBLACKBERRY10) - endif() endif() if(ANDROID) diff --git a/Core/Config.cpp b/Core/Config.cpp index 93461ec9a8..75ff92f6c8 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -108,7 +108,7 @@ void Config::Load(const char *iniFileName) #endif graphics->Get("VertexCache", &bVertexCache, true); graphics->Get("FullScreen", &bFullScreen, false); -#ifdef BLACKBERRY10 +#ifdef BLACKBERRY graphics->Get("PartialStretch", &bPartialStretch, pixel_xres == pixel_yres); #endif graphics->Get("StretchToDisplay", &bStretchToDisplay, false); @@ -125,7 +125,7 @@ void Config::Load(const char *iniFileName) IniFile::Section *control = iniFile.GetOrCreateSection("Control"); control->Get("ShowStick", &bShowAnalogStick, false); -#ifdef BLACKBERRY10 +#ifdef BLACKBERRY control->Get("ShowTouchControls", &bShowTouchControls, pixel_xres != pixel_yres); #elif defined(USING_GLES2) control->Get("ShowTouchControls", &bShowTouchControls, true); @@ -209,7 +209,7 @@ void Config::Save() graphics->Set("AnisotropyLevel", iAnisotropyLevel); graphics->Set("VertexCache", bVertexCache); graphics->Set("FullScreen", bFullScreen); -#ifdef BLACKBERRY10 +#ifdef BLACKBERRY graphics->Set("PartialStretch", bPartialStretch); #endif graphics->Set("StretchToDisplay", bStretchToDisplay); diff --git a/Core/Config.h b/Core/Config.h index a63e571c46..7b37b1a2ec 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -63,7 +63,7 @@ public: bool bBufferedRendering; bool bLinearFiltering; bool bUseVBO; -#ifdef BLACKBERRY10 +#ifdef BLACKBERRY bool bPartialStretch; #endif bool bStretchToDisplay; diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 8ea2711612..c511789314 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -88,7 +88,7 @@ void CenterRect(float *x, float *y, float *w, float *h, *x = 0.0f; *w = frameW; *h = frameW / origRatio; -#ifdef BLACKBERRY10 +#ifdef BLACKBERRY // Stretch a little bit if (g_Config.bPartialStretch) *h = (frameH + *h) / 2.0f; // (408 + 720) / 2 = 564 diff --git a/Qt/Settings.pri b/Qt/Settings.pri index d527c478b4..b18172f157 100644 --- a/Qt/Settings.pri +++ b/Qt/Settings.pri @@ -37,7 +37,7 @@ contains(MEEGO_EDITION,harmattan)|!count(gleslib,0) { contains(MEEGO_EDITION,harmattan): DEFINES += MEEGO_EDITION_HARMATTAN "_SYS_UCONTEXT_H=1" qnx { # Use mkspec: unsupported/qws/qnx-armv7-g++ - DEFINES += BLACKBERRY BLACKBERRY10 "_QNX_SOURCE=1" "_C99=1" + DEFINES += BLACKBERRY "_QNX_SOURCE=1" "_C99=1" } symbian { # Does not seem to be a way to change to armv6 compile so just override in variants.xml (see README) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index 4a47e67991..8372e8706d 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -454,7 +454,7 @@ void PauseScreen::render() { UICheckBox(GEN_ID, x, y += stride, a->T("Enable Sound"), ALIGN_TOPLEFT, &g_Config.bEnableSound); // TODO: Maybe shouldn't show this if the screen ratios are very close... -#ifdef BLACKBERRY10 +#ifdef BLACKBERRY if (pixel_xres == pixel_yres) UICheckBox(GEN_ID, x, y += stride, gs->T("Partial Vertical Stretch"), ALIGN_TOPLEFT, &g_Config.bPartialStretch); #endif From f6743316dbdf4bb37ee8db825240951115f5d90c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 20 Jun 2013 23:13:00 -0700 Subject: [PATCH 34/80] Make sure Windows shuts down its audio properly. ~EmuScreen can call PSP_Shutdown(), which calls host->ShutdownSound(). Because of this, we really need host to be WindowsHost during NativeShutdownGraphics(). --- Windows/EmuThread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index cb0be4f817..eb78ec2a6b 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -150,8 +150,8 @@ unsigned int WINAPI TheThread(void *) shutdown: _InterlockedExchange(&emuThreadReady, THREAD_SHUTDOWN); - host = nativeHost; NativeShutdownGraphics(); + host = nativeHost; NativeShutdown(); host = oldHost; host->ShutdownGL(); From d41ed97008bff19ea6d06193e60f886c4540b7e1 Mon Sep 17 00:00:00 2001 From: raven02 Date: Fri, 21 Jun 2013 15:54:26 +0800 Subject: [PATCH 35/80] use lowercase for cheats to be consistent with lang/memstick/assets etc --- Core/CwCheat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/CwCheat.cpp b/Core/CwCheat.cpp index 2c0d93934f..ebc302f720 100644 --- a/Core/CwCheat.cpp +++ b/Core/CwCheat.cpp @@ -7,7 +7,7 @@ #include "MIPS/MIPS.h" #include "Core/Config.h" -const static std::string CHEATS_DIR = "Cheats"; +const static std::string CHEATS_DIR = "cheats"; static std::string activeCheatFile; static int CheatEvent = -1; From cc705c72eb5a479d08bfd51981a139f3a6e060ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 21 Jun 2013 09:59:29 +0200 Subject: [PATCH 36/80] Revert "Support tex level factor 000001 (fixed at 0, that is, mipmapping off)" This reverts commit eaede89761c97292b0c929abbcc0d2765235a46e. Conflicts: GPU/GLES/TextureCache.cpp --- GPU/GLES/DisplayListInterpreter.cpp | 5 +++-- GPU/GLES/TextureCache.cpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index af91a48b3b..118575425e 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -956,8 +956,9 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) { break; case GE_CMD_TEXLEVEL: - // We support 000001, it means fixed at mip level 0. Don't report 0 as it's normal. - if (data != 1 && data != 0) + if (data == 1) + WARN_LOG_REPORT_ONCE(texLevel1, G3D, "Unsupported texture level bias settings: %06x", data) + else if (data != 0) WARN_LOG_REPORT_ONCE(texLevel2, G3D, "Unsupported texture level bias settings: %06x", data); break; #endif diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index e6fee64e7a..108a0f8dc8 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -492,7 +492,7 @@ void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) { bool sClamp = gstate.texwrap & 1; bool tClamp = (gstate.texwrap>>8) & 1; - bool noMip = (gstate.texlevel & 0xFFFFFF) == 0x000001; // Fix texlevel at 0 + // bool noMip = (gstate.texlevel & 0xFFFFFF) == 0x000001; // Fix texlevel at 0 if (entry.maxLevel == 0) { // Enforce no mip filtering, for safety. @@ -513,7 +513,7 @@ void TextureCache::UpdateSamplingParams(TexCacheEntry &entry, bool force) { minFilt |= 1; } - if (!g_Config.bMipMap || noMip) { + if (!g_Config.bMipMap) { magFilt &= 1; minFilt &= 1; } From 266ce888f2f71359ebded1c8b778a11ba77a2eb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 21 Jun 2013 09:59:35 +0200 Subject: [PATCH 37/80] Fix to buttonpreference --- Core/HLE/sceUtility.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index e254cf973b..afe4fb2537 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -473,7 +473,7 @@ u32 sceUtilityGetSystemParamInt(u32 id, u32 destaddr) param = g_Config.ilanguage; break; case PSP_SYSTEMPARAM_ID_INT_BUTTON_PREFERENCE: - param = g_Config.iButtonPreference?PSP_SYSTEMPARAM_BUTTON_CROSS:PSP_SYSTEMPARAM_BUTTON_CIRCLE; + param = g_Config.iButtonPreference; break; case PSP_SYSTEMPARAM_ID_INT_LOCK_PARENTAL_LEVEL: param = g_Config.iLockParentalLevel; From 2fe8663ad059ff56e1fa7c49c91cc172855d0a75 Mon Sep 17 00:00:00 2001 From: raven02 Date: Fri, 21 Jun 2013 16:21:13 +0800 Subject: [PATCH 38/80] Rename Linear filtering as --- Windows/ppsspp.rc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index d33018f4b4..bd316f4fa5 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -384,7 +384,7 @@ BEGIN MENUITEM "&Vertex Cache", ID_OPTIONS_VERTEXCACHE MENUITEM "&Use VBO (experimental)", ID_OPTIONS_USEVBO MENUITEM "&Frame Skipping", ID_OPTIONS_FRAMESKIP - MENUITEM "&Linear Filtering", ID_OPTIONS_LINEARFILTERING + MENUITEM "&Force Linear Filtering", ID_OPTIONS_LINEARFILTERING MENUITEM "Simple &2x SSAA", ID_OPTIONS_SIMPLE2XSSAA MENUITEM "&MipMapping", ID_OPTIONS_MIPMAP MENUITEM "VS&ync", ID_OPTIONS_VSYNC @@ -400,7 +400,6 @@ BEGIN MENUITEM "&Hybrid", ID_TEXTURESCALING_HYBRID MENUITEM "&Bicubic", ID_TEXTURESCALING_BICUBIC MENUITEM "&Hybrid Bicubic", ID_TEXTURESCALING_HYBRID_BICUBIC - MENUITEM "&Nearest Neighbor", ID_TEXTURESCALING_NEAREST_NEIGHBOR MENUITEM SEPARATOR MENUITEM "&Deposterize", ID_TEXTURESCALING_DEPOSTERIZE END From b90504a0dc4d835750ebe7e9272e3f0135a602b0 Mon Sep 17 00:00:00 2001 From: Sacha Date: Sat, 22 Jun 2013 01:13:19 +1000 Subject: [PATCH 39/80] CMake ffmpeg: Use correct include dir (which does vary for each platform). --- CMakeLists.txt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d46c7a9981..75487bc557 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,8 @@ endif() if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(LINUX ON) if (NOT USING_GLES2) - set(FFMPEG ON) + # Not working yet. + #set(FFMPEG ON) endif() endif() @@ -497,19 +498,21 @@ endif() if(FFMPEG) add_definitions(-DUSE_FFMPEG) - include_directories(ffmpeg) + set(PLATFORM_ARCH "") if (BLACKBERRY) - link_directories(ffmpeg/blackberry/armv7/lib) + set(PLATFORM_ARCH "blackberry/armv7") set(nativeExtraLibs ${nativeExtraLibs} iconv) elseif(IOS) - link_directories(ffmpeg/ios/universal/lib) + set(PLATFORM_ARCH "ios/universal") set(nativeExtraLibs ${nativeExtraLibs} iconv bz2) elseif(MACOSX) - link_directories(ffmpeg/macosx/x86_64/lib) + set(PLATFORM_ARCH "macosx/x86_64") elseif(LINUX) # Note: Only 64-bit is supported - link_directories(ffmpeg/linux/x86_64/lib) + set(PLATFORM_ARCH "linux/x86_64") endif() + include_directories(ffmpeg/${PLATFORM_ARCH}/include) + link_directories(ffmpeg/${PLATFORM_ARCH}/lib) set(nativeExtraLibs ${nativeExtraLibs} libavformat.a libavcodec.a libswresample.a libavutil.a libswscale.a) endif() From 286caf3deb0052c43f9aa885d285afa44f1d3552 Mon Sep 17 00:00:00 2001 From: Sacha Date: Sat, 22 Jun 2013 01:33:36 +1000 Subject: [PATCH 40/80] Linux Qt buildfix. --- Qt/mainwindow.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Qt/mainwindow.cpp b/Qt/mainwindow.cpp index 9e33b29969..97d350cc98 100644 --- a/Qt/mainwindow.cpp +++ b/Qt/mainwindow.cpp @@ -156,7 +156,7 @@ void MainWindow::UpdateMenus() ui->action_Sound->setChecked(g_Config.bEnableSound); ui->action_OptionsShowDebugStatistics->setChecked(g_Config.bShowDebugStats); - ui->action_Show_FPS_counter->setChecked(g_Config.bShowFPSCounter); + ui->action_Show_FPS_counter->setChecked(g_Config.iShowFPSCounter); ui->actionLogDefDebug->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::COMMON) == LogTypes::LDEBUG); ui->actionLogDefInfo->setChecked(LogManager::GetInstance()->GetLogLevel(LogTypes::COMMON) == LogTypes::LINFO); @@ -629,7 +629,7 @@ void MainWindow::on_action_OptionsShowDebugStatistics_triggered() void MainWindow::on_action_Show_FPS_counter_triggered() { - g_Config.bShowFPSCounter = !g_Config.bShowFPSCounter; + g_Config.iShowFPSCounter = !g_Config.iShowFPSCounter; UpdateMenus(); } From 2fa96378184cbbae029fc69115163e82c81bc7a7 Mon Sep 17 00:00:00 2001 From: oioitff Date: Fri, 21 Jun 2013 23:42:09 +0800 Subject: [PATCH 41/80] Use a larger index buffer. Fix crash for 7th Dragon 2020 at some certain places. --- GPU/GLES/TransformPipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GLES/TransformPipeline.cpp b/GPU/GLES/TransformPipeline.cpp index 9c20120ef2..8d8c1d33c9 100644 --- a/GPU/GLES/TransformPipeline.cpp +++ b/GPU/GLES/TransformPipeline.cpp @@ -51,7 +51,7 @@ const GLuint glprim[8] = { enum { DECODED_VERTEX_BUFFER_SIZE = 65536 * 48, - DECODED_INDEX_BUFFER_SIZE = 65536 * 2, + DECODED_INDEX_BUFFER_SIZE = 65536 * 20, TRANSFORMED_VERTEX_BUFFER_SIZE = 65536 * sizeof(TransformedVertex) }; From 0d808188a093947ae8768e2d9a0a7d9fd2578937 Mon Sep 17 00:00:00 2001 From: Sacha Date: Sat, 22 Jun 2013 03:00:35 +1000 Subject: [PATCH 42/80] Add FFMPEG support for Symbian. --- Qt/PPSSPP.pro | 2 +- Qt/Settings.pri | 2 +- ffmpeg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Qt/PPSSPP.pro b/Qt/PPSSPP.pro index 384b1f5896..831bd20e53 100755 --- a/Qt/PPSSPP.pro +++ b/Qt/PPSSPP.pro @@ -11,7 +11,7 @@ mobile_platform: MOBILITY += sensors symbian: MOBILITY += systeminfo # Libs -symbian: LIBS += -lCore.lib -lCommon.lib -lNative.lib +symbian: LIBS += -lCore.lib -lCommon.lib -lNative.lib -L ffmpeg/symbian/armv6/lib -lavformat.a -lavcodec.a -lswresample.a -lswscale.a qnx: LIBS += -L. -lCore -lCommon -lNative -lscreen -lz win32 { CONFIG(release, debug|release) { diff --git a/Qt/Settings.pri b/Qt/Settings.pri index b18172f157..42dccc57d1 100644 --- a/Qt/Settings.pri +++ b/Qt/Settings.pri @@ -43,7 +43,7 @@ symbian { # Does not seem to be a way to change to armv6 compile so just override in variants.xml (see README) MMP_RULES -= "ARMFPU softvfp+vfpv2" MMP_RULES += "ARMFPU vfpv2" - DEFINES += "BOOST_COMPILER_CONFIG=" + DEFINES += USE_FFMPEG "BOOST_COMPILER_CONFIG=" QMAKE_CXXFLAGS += -marm -Wno-parentheses -Wno-comment INCLUDEPATH += $$EPOCROOT/epoc32/include/stdapis/glib-2.0 } diff --git a/ffmpeg b/ffmpeg index e8ef410aac..84dac8cd4a 160000 --- a/ffmpeg +++ b/ffmpeg @@ -1 +1 @@ -Subproject commit e8ef410aac458a35064e67582fb80103c29e6474 +Subproject commit 84dac8cd4af62868ddc18ee813931e5a4788c28d From cee1bcf7e9709bf2901266c018c3c9007d4a40e3 Mon Sep 17 00:00:00 2001 From: Sacha Date: Sat, 22 Jun 2013 04:56:39 +1000 Subject: [PATCH 43/80] Symbian buildfix. --- Qt/PPSSPP.pro | 6 +++++- Qt/Settings.pri | 3 ++- README.md | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Qt/PPSSPP.pro b/Qt/PPSSPP.pro index 831bd20e53..094d17aa8f 100755 --- a/Qt/PPSSPP.pro +++ b/Qt/PPSSPP.pro @@ -11,7 +11,11 @@ mobile_platform: MOBILITY += sensors symbian: MOBILITY += systeminfo # Libs -symbian: LIBS += -lCore.lib -lCommon.lib -lNative.lib -L ffmpeg/symbian/armv6/lib -lavformat.a -lavcodec.a -lswresample.a -lswscale.a +symbian { + LIBS += -lCore.lib -lCommon.lib -lNative.lib + # For now you have to copy these to the Symbian lib dir. Better solution coming later. + LIBS += -lavutil.lib -lavformat.lib -lavcodec.lib -lswresample.lib -lswscale.lib +} qnx: LIBS += -L. -lCore -lCommon -lNative -lscreen -lz win32 { CONFIG(release, debug|release) { diff --git a/Qt/Settings.pri b/Qt/Settings.pri index 42dccc57d1..c95e2633fe 100644 --- a/Qt/Settings.pri +++ b/Qt/Settings.pri @@ -43,7 +43,8 @@ symbian { # Does not seem to be a way to change to armv6 compile so just override in variants.xml (see README) MMP_RULES -= "ARMFPU softvfp+vfpv2" MMP_RULES += "ARMFPU vfpv2" - DEFINES += USE_FFMPEG "BOOST_COMPILER_CONFIG=" + DEFINES += USE_FFMPEG __STDC_CONSTANT_MACROS "BOOST_COMPILER_CONFIG=" QMAKE_CXXFLAGS += -marm -Wno-parentheses -Wno-comment INCLUDEPATH += $$EPOCROOT/epoc32/include/stdapis/glib-2.0 + INCLUDEPATH += ../ffmpeg/symbian/armv6/include } diff --git a/README.md b/README.md index 6351b74f86..4dc3d0b3b3 100644 --- a/README.md +++ b/README.md @@ -200,7 +200,7 @@ You will need to add the GCCE 4.6.3 variant to Symbian\tools\sbs\lib\config\vari You will also need to increase the data section of the executable in linking stage by modifying Symbian\tools\sbs\lib\config\gcce.xml as follows: ``` - + ``` Then simply compile the PPSSPPQt.pro with `qmake` from the SDK or the included QtCreator. From 2438259c8ac67e33befb62aacfbdddf1233f59b8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 21 Jun 2013 17:40:41 -0700 Subject: [PATCH 44/80] Improve button preference UI (there's always one.) --- UI/MenuScreens.cpp | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index c36bac4ad4..37b5c8d5f7 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -1161,24 +1161,33 @@ void SystemScreen::render() { UICheckBox(GEN_ID, x, y += stride, s->T("Fast Memory", "Fast Memory (unstable)"), ALIGN_TOPLEFT, &g_Config.bFastMemory); UICheckBox(GEN_ID, x, y += stride, s->T("Daylight Savings"), ALIGN_TOPLEFT, &g_Config.bDayLightSavings); - UICheckBox(GEN_ID, x, y += stride, s->T("Button Preference"), ALIGN_TOPLEFT, &g_Config.bButtonPreference); - if (g_Config.bButtonPreference) { - char button[256]; - std::string type; - switch (g_Config.iButtonPreference) { - case 0: type = "O to Enter";break; - case 1: type = "X to Enter";break; - } - sprintf(button, "%s %s", s->T("Type :"), type.c_str()); - ui_draw2d.DrawText(UBUNTU24, button, x + 60, y += stride , 0xFFFFFFFF, ALIGN_LEFT); - HLinear hlinear1(x + 280, y, 20); - if (UIButton(GEN_ID, hlinear1, 90, 0, s->T("Use O"), ALIGN_LEFT)) - g_Config.iButtonPreference = 0; - if (UIButton(GEN_ID, hlinear1, 90, 0, s->T("Use X"), ALIGN_LEFT)) - g_Config.iButtonPreference = 1; - y += 10; + + const char *buttonPreferenceTitle; + switch (g_Config.iButtonPreference) { + case PSP_SYSTEMPARAM_BUTTON_CIRCLE: + buttonPreferenceTitle = s->T("Button Preference - O to Confirm"); + break; + case PSP_SYSTEMPARAM_BUTTON_CROSS: + default: + buttonPreferenceTitle = s->T("Button Preference - X to Confirm"); + break; } +#ifdef _WIN32 + const int checkboxH = 32; +#else + const int checkboxH = 48; +#endif + ui_draw2d.DrawTextShadow(UBUNTU24, buttonPreferenceTitle, x + UI_SPACE + 29, (y += stride) + checkboxH / 2, 0xFFFFFFFF, ALIGN_LEFT | ALIGN_VCENTER); + y += stride; + // 29 is the width of the checkbox, new UI will replace. + HLinear hlinearButtonPref(x + UI_SPACE + 29, y, 20); + if (UIButton(GEN_ID, hlinearButtonPref, 90, 0, s->T("Use O"), ALIGN_LEFT)) + g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CIRCLE; + if (UIButton(GEN_ID, hlinearButtonPref, 90, 0, s->T("Use X"), ALIGN_LEFT)) + g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CROSS; + y += 20 + 6; + /* bool time = g_Config.iTimeFormat > 0 ; UICheckBox(GEN_ID, x, y += stride, s->T("Time Format"), ALIGN_TOPLEFT, &time); From 8fe962035040e71f984c9d0a2174c7f01b82eaf4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 21 Jun 2013 17:45:39 -0700 Subject: [PATCH 45/80] Always use iButtonPreference, fixes #2390. --- Core/Config.h | 1 - Core/Dialog/PSPOskDialog.cpp | 7 ++++--- Core/HLE/sceImpose.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/Config.h b/Core/Config.h index 7b37b1a2ec..1ff70602bd 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -119,7 +119,6 @@ public: int iDateFormat; int iTimeZone; bool bDayLightSavings; - bool bButtonPreference; int iButtonPreference; int iLockParentalLevel; bool bEncryptSave; diff --git a/Core/Dialog/PSPOskDialog.cpp b/Core/Dialog/PSPOskDialog.cpp index 823683b835..ea1c178ec7 100755 --- a/Core/Dialog/PSPOskDialog.cpp +++ b/Core/Dialog/PSPOskDialog.cpp @@ -21,6 +21,7 @@ #include "Core/Dialog/PSPOskDialog.h" #include "Core/Util/PPGeDraw.h" #include "Core/HLE/sceCtrl.h" +#include "Core/HLE/sceUtility.h" #include "Core/Config.h" #include "Core/Reporting.h" #include "Common/ChunkFile.h" @@ -750,7 +751,7 @@ int PSPOskDialog::Update() StartDraw(); PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0x63636363)); RenderKeyboard(); - if (g_Config.bButtonPreference) + if (g_Config.iButtonPreference != PSP_SYSTEMPARAM_BUTTON_CIRCLE) { PPGeDrawImage(I_CROSS, 30, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF)); PPGeDrawImage(I_CIRCLE, 150, 220, 20, 20, 0, CalcFadedColor(0xFFFFFFFF)); @@ -795,7 +796,7 @@ int PSPOskDialog::Update() selectedChar = (selectedChar + (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard])) % (numKeyCols[currentKeyboard] * numKeyRows[currentKeyboard]); - if ((IsButtonPressed(CTRL_CROSS) && g_Config.bButtonPreference) || (IsButtonPressed(CTRL_CIRCLE) && !g_Config.bButtonPreference)) + if (IsButtonPressed(g_Config.iButtonPreference != PSP_SYSTEMPARAM_BUTTON_CIRCLE ? CTRL_CROSS : CTRL_CIRCLE)) { inputChars = CombinationString(true); } @@ -816,7 +817,7 @@ int PSPOskDialog::Update() selectedChar = selectedRow * numKeyCols[currentKeyboard] + selectedExtra; } - else if ((IsButtonPressed(CTRL_CIRCLE) && g_Config.bButtonPreference) || (IsButtonPressed(CTRL_CROSS) && !g_Config.bButtonPreference)) + else if (IsButtonPressed(g_Config.iButtonPreference != PSP_SYSTEMPARAM_BUTTON_CIRCLE ? CTRL_CROSS : CTRL_CIRCLE)) { if (inputChars.size() > 0) { diff --git a/Core/HLE/sceImpose.cpp b/Core/HLE/sceImpose.cpp index ca073eca13..5d12ea8bcf 100644 --- a/Core/HLE/sceImpose.cpp +++ b/Core/HLE/sceImpose.cpp @@ -37,7 +37,7 @@ static u32 backlightOffTime; void __ImposeInit() { language = g_Config.ilanguage; - buttonValue = g_Config.bButtonPreference?PSP_SYSTEMPARAM_BUTTON_CROSS:PSP_SYSTEMPARAM_BUTTON_CIRCLE; + buttonValue = g_Config.iButtonPreference; umdPopup = PSP_UMD_POPUP_DISABLE; backlightOffTime = 0; } From 47b0c5962a1e81dae85b5c6ab2bd6aecec55cece Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 21 Jun 2013 17:59:12 -0700 Subject: [PATCH 46/80] Add a right stick config for Valkyria Chronicles. --- Core/Config.h | 1 + Windows/XinputDevice.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/Core/Config.h b/Core/Config.h index 1ff70602bd..06f1df64b3 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -104,6 +104,7 @@ public: // 1 = arrow buttons // 2 = face buttons // 3 = L/R + // 4 = L/R + triangle/cross int iRightStickBind; // Control diff --git a/Windows/XinputDevice.cpp b/Windows/XinputDevice.cpp index 90cfc0505b..c1a25a3c6d 100644 --- a/Windows/XinputDevice.cpp +++ b/Windows/XinputDevice.cpp @@ -193,6 +193,12 @@ void XinputDevice::ApplyDiff(XINPUT_STATE &state, InputState &input_state) { if (state.Gamepad.sThumbRX > rthreshold) input_state.pad_buttons |= PAD_BUTTON_RBUMPER; else if (state.Gamepad.sThumbRX < -rthreshold) input_state.pad_buttons |= PAD_BUTTON_LBUMPER; break; + case 4: + if (state.Gamepad.sThumbRX > rthreshold) input_state.pad_buttons |= PAD_BUTTON_RBUMPER; + else if (state.Gamepad.sThumbRX < -rthreshold) input_state.pad_buttons |= PAD_BUTTON_LBUMPER; + if (state.Gamepad.sThumbRY > rthreshold) input_state.pad_buttons |= PAD_BUTTON_Y; + else if (state.Gamepad.sThumbRY < -rthreshold) input_state.pad_buttons |= PAD_BUTTON_A; + break; } } From a789b9edc26dc84567ef8283ae3833df8219219f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 21 Jun 2013 18:07:50 -0700 Subject: [PATCH 47/80] Add right stick options to DirectInput. At least, my controller uses Z axis/rotation. --- Windows/DinputDevice.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Windows/DinputDevice.cpp b/Windows/DinputDevice.cpp index f39e0d3bb1..cc0222578e 100644 --- a/Windows/DinputDevice.cpp +++ b/Windows/DinputDevice.cpp @@ -210,6 +210,35 @@ int DinputDevice::UpdateState(InputState &input_state) } } + const LONG rthreshold = 8000; + + switch (g_Config.iRightStickBind) { + case 0: + break; + case 1: + if (js.lRz > rthreshold) input_state.pad_buttons |= PAD_BUTTON_RIGHT; + else if (js.lRz < -rthreshold) input_state.pad_buttons |= PAD_BUTTON_LEFT; + if (js.lZ > rthreshold) input_state.pad_buttons |= PAD_BUTTON_UP; + else if (js.lZ < -rthreshold) input_state.pad_buttons |= PAD_BUTTON_DOWN; + break; + case 2: + if (js.lRz > rthreshold) input_state.pad_buttons |= PAD_BUTTON_B; + else if (js.lRz < -rthreshold) input_state.pad_buttons |= PAD_BUTTON_X; + if (js.lZ > rthreshold) input_state.pad_buttons |= PAD_BUTTON_Y; + else if (js.lZ < -rthreshold) input_state.pad_buttons |= PAD_BUTTON_A; + break; + case 3: + if (js.lRz > rthreshold) input_state.pad_buttons |= PAD_BUTTON_RBUMPER; + else if (js.lRz < -rthreshold) input_state.pad_buttons |= PAD_BUTTON_LBUMPER; + break; + case 4: + if (js.lRz > rthreshold) input_state.pad_buttons |= PAD_BUTTON_RBUMPER; + else if (js.lRz < -rthreshold) input_state.pad_buttons |= PAD_BUTTON_LBUMPER; + if (js.lZ > rthreshold) input_state.pad_buttons |= PAD_BUTTON_Y; + else if (js.lZ < -rthreshold) input_state.pad_buttons |= PAD_BUTTON_A; + break; + } + return UPDATESTATE_SKIP_PAD; } From 2432101216a14c4a61ad6f56c9cb3a2ccb7c7bef Mon Sep 17 00:00:00 2001 From: oioitff Date: Sat, 22 Jun 2013 21:25:08 +0800 Subject: [PATCH 48/80] Start looping if there is not enough samples. Fix noise in some looping bgm. --- Core/HLE/sceAtrac.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 3f3ea5175f..eda71722d2 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -566,6 +566,7 @@ u32 _AtracDecodeData(int atracID, u8* outbuf, u32 *SamplesNum, u32* finish, int } else { // TODO: This isn't at all right, but at least it makes the music "last" some time. u32 numSamples = 0; + u32 atracSamplesPerFrame = (atrac->codeType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES); #ifdef USE_FFMPEG if (atrac->codeType == PSP_MODE_AT_3 && atrac->pCodecCtx) { int forceseekSample = atrac->currentSample * 2 > atrac->endSample ? 0 : atrac->endSample; @@ -635,7 +636,6 @@ u32 _AtracDecodeData(int atracID, u8* outbuf, u32 *SamplesNum, u32* finish, int } else { numSamples = atrac->endSample - atrac->currentSample; - u32 atracSamplesPerFrame = (atrac->codeType == PSP_MODE_AT_3_PLUS ? ATRAC3PLUS_MAX_SAMPLES : ATRAC3_MAX_SAMPLES); if (atrac->currentSample >= atrac->endSample) { numSamples = 0; } else if (numSamples > atracSamplesPerFrame) { @@ -654,7 +654,7 @@ u32 _AtracDecodeData(int atracID, u8* outbuf, u32 *SamplesNum, u32* finish, int atrac->decodePos = atrac->getDecodePosBySample(atrac->currentSample); int finishFlag = 0; - if (atrac->loopNum != 0 && (atrac->currentSample >= atrac->loopEndSample || + if (atrac->loopNum != 0 && (atrac->currentSample + atracSamplesPerFrame > atrac->loopEndSample || (numSamples == 0 && atrac->first.size >= atrac->first.filesize))) { atrac->currentSample = atrac->loopStartSample; if (atrac->loopNum > 0) From ca78dd4e6d507c1866157b11abef075fd3eb1f5b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 08:36:31 -0700 Subject: [PATCH 49/80] Eat cycles in sceMpegRingbufferAvailableSize(). Some games call this in a loop, so it affects perf. And, this eats quite a few cycles, implying it does a lot more work than I thought it did. --- Core/HLE/sceMpeg.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 305b535df2..1464082f95 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -873,6 +873,7 @@ int sceMpegRingbufferAvailableSize(u32 ringbufferAddr) DEBUG_LOG(HLE, "%i=sceMpegRingbufferAvailableSize(%08x)", ringbuffer.packetsFree, ringbufferAddr); MpegContext *ctx = getMpegCtx(ringbuffer.mpeg); int result = std::min(ringbuffer.packetsFree, ctx->mediaengine->getRemainSize() / 2048); + hleEatCycles(2020); return ringbuffer.packetsFree; } From 67c185f8eaf7f4c2805384b08b7529625eaddb96 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 22 Jun 2013 17:34:46 +0200 Subject: [PATCH 50/80] Update native to not wait in the opensl callback. Some minor audio optimizations. Also it seems ffmpeg has changed so update it. --- Core/HW/SasAudio.cpp | 31 ++++++++++++++++++++----------- android/jni/Android.mk | 2 +- ffmpeg | 2 +- native | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/Core/HW/SasAudio.cpp b/Core/HW/SasAudio.cpp index 60c5f3e626..f5a468bbaf 100644 --- a/Core/HW/SasAudio.cpp +++ b/Core/HW/SasAudio.cpp @@ -456,18 +456,27 @@ void SasInstance::Mix(u32 outAddr, u32 inAddr, int leftVol, int rightVol) { // Alright, all voices mixed. Let's convert and clip, and at the same time, wipe mixBuffer for next time. Could also dither. s16 *outp = (s16 *)Memory::GetPointer(outAddr); - s16 *inp = inAddr ? (s16*)Memory::GetPointer(inAddr) : 0; - for (int i = 0; i < grainSize * 2; i += 2) { - int sampleL = mixBuffer[i] + sendBuffer[i]; - if (inp) - sampleL += (*inp++) * leftVol >> 12; - *outp++ = clamp_s16(sampleL); - if (outputMode == 0) { - // stereo - int sampleR = mixBuffer[i + 1] + sendBuffer[i + 1]; + const s16 *inp = inAddr ? (s16*)Memory::GetPointer(inAddr) : 0; + if (outputMode == 0) { + if (inp) { + for (int i = 0; i < grainSize * 2; i += 2) { + int sampleL = mixBuffer[i] + sendBuffer[i] + ((*inp++) * leftVol >> 12); + int sampleR = mixBuffer[i + 1] + sendBuffer[i + 1] + ((*inp++) * rightVol >> 12); + *outp++ = clamp_s16(sampleL); + *outp++ = clamp_s16(sampleR); + } + } else { + for (int i = 0; i < grainSize * 2; i += 2) { + *outp++ = clamp_s16(mixBuffer[i] + sendBuffer[i]); + *outp++ = clamp_s16(mixBuffer[i + 1] + sendBuffer[i + 1]); + } + } + } else { + for (int i = 0; i < grainSize * 2; i += 2) { + int sampleL = mixBuffer[i] + sendBuffer[i]; if (inp) - sampleR += (*inp++) * rightVol >> 12; - *outp++ = clamp_s16(sampleR); + sampleL += (*inp++) * leftVol >> 12; + *outp++ = clamp_s16(sampleL); } } memset(mixBuffer, 0, grainSize * sizeof(int) * 2); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index f85dd14969..ab1e76df51 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -5,7 +5,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := native_audio -LOCAL_CFLAGS := -O2 -fsigned-char -ffast-math -Wall -Wno-multichar -Wno-psabi +LOCAL_CFLAGS := -O3 -fsigned-char -ffast-math -Wall -Wno-multichar -Wno-psabi # yes, it's really CPPFLAGS for C++ LOCAL_CPPFLAGS := -std=gnu++11 -fno-rtti NATIVE := ../../native diff --git a/ffmpeg b/ffmpeg index 84dac8cd4a..278f774252 160000 --- a/ffmpeg +++ b/ffmpeg @@ -1 +1 @@ -Subproject commit 84dac8cd4af62868ddc18ee813931e5a4788c28d +Subproject commit 278f7742523d26b6469a63b2bb902c51d23fbfca diff --git a/native b/native index 0a26fae856..ca9301fedf 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit 0a26fae856aecdaa426d92fa5dde101b19487796 +Subproject commit ca9301fedf879f28d6f11fda59709a66c5368bec From 0b4e93a67190c8f4da5bb7c952eb101679336fa6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 08:39:25 -0700 Subject: [PATCH 51/80] Clean up sceMpegRingbufferAvailableSize(). Avoid a memory copy, some crash checks. --- Core/HLE/sceMpeg.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 1464082f95..6d2dd246a0 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -863,18 +863,23 @@ int sceMpegQueryAtracEsSize(u32 mpeg, u32 esSizeAddr, u32 outSizeAddr) int sceMpegRingbufferAvailableSize(u32 ringbufferAddr) { - if (!Memory::IsValidAddress(ringbufferAddr)) { + PSPPointer ringbuffer; + ringbuffer = ringbufferAddr; + + if (!ringbuffer.Valid()) { ERROR_LOG(HLE, "sceMpegRingbufferAvailableSize(%08x) - bad address", ringbufferAddr); return -1; } - 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); + MpegContext *ctx = getMpegCtx(ringbuffer->mpeg); + if (!ctx) { + ERROR_LOG(HLE, "sceMpegRingbufferAvailableSize(%08x) - bad mpeg", ringbufferAddr); + return -1; + } + hleEatCycles(2020); - return ringbuffer.packetsFree; + DEBUG_LOG(HLE, "%i=sceMpegRingbufferAvailableSize(%08x)", ringbuffer->packetsFree, ringbufferAddr); + return ringbuffer->packetsFree; } void PostPutAction::run(MipsCall &call) { From 3b9e365b1e9d12fc5d2ca55c18d73fdb1a2fd34a Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 22 Jun 2013 18:50:40 +0200 Subject: [PATCH 52/80] Allow ELFs to be named PBP, there are a bunch of such homebrew floating around it seems --- Core/Loaders.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index aa2bf444b3..f9a9e1714a 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -87,8 +87,10 @@ EmuFileType Identify_File(std::string &filename) if (id == 'FLE\x7F') { + // There are a few elfs misnamed as pbp (like Trig Wars), accept that. if (!strcasecmp(extension.c_str(), ".plf") || strstr(filename.c_str(),"BOOT.BIN") || - !strcasecmp(extension.c_str(), ".elf") || !strcasecmp(extension.c_str(), ".prx") ) + !strcasecmp(extension.c_str(), ".elf") || !strcasecmp(extension.c_str(), ".prx") || + !strcasecmp(extension.c_str(), ".pbp")) { return FILETYPE_PSP_ELF; } From 88fbe6c1b57c235133d31653372eeb0b37994a68 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 10:15:30 -0700 Subject: [PATCH 53/80] Clear lastBoundTexture when binding a null one. --- GPU/GLES/TextureCache.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 108a0f8dc8..c62b7c4e32 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -75,8 +75,8 @@ TextureCache::~TextureCache() { void TextureCache::Clear(bool delete_them) { glBindTexture(GL_TEXTURE_2D, 0); + lastBoundTexture = -1; if (delete_them) { - lastBoundTexture = -1; for (TexCache::iterator iter = cache.begin(); iter != cache.end(); ++iter) { DEBUG_LOG(G3D, "Deleting texture %i", iter->second.texture); glDeleteTextures(1, &iter->second.texture); @@ -96,6 +96,7 @@ void TextureCache::Clear(bool delete_them) { // Removes old textures. void TextureCache::Decimate() { glBindTexture(GL_TEXTURE_2D, 0); + lastBoundTexture = -1; int killAge = lowMemoryMode_ ? TEXTURE_KILL_AGE_LOWMEM : TEXTURE_KILL_AGE; for (TexCache::iterator iter = cache.begin(); iter != cache.end(); ) { if (iter->second.lastFrame + TEXTURE_KILL_AGE < gpuStats.numFrames) { @@ -868,6 +869,7 @@ void TextureCache::SetTexture() { if (!Memory::IsValidAddress(texaddr)) { // Bind a null texture and return. glBindTexture(GL_TEXTURE_2D, 0); + lastBoundTexture = -1; return; } @@ -918,12 +920,14 @@ void TextureCache::SetTexture() { if (entry->framebuffer->fbo) entry->framebuffer->fbo = 0; glBindTexture(GL_TEXTURE_2D, 0); + lastBoundTexture = -1; entry->lastFrame = gpuStats.numFrames; } else { if (entry->framebuffer->fbo) { fbo_bind_color_as_texture(entry->framebuffer->fbo, 0); } else { glBindTexture(GL_TEXTURE_2D, 0); + lastBoundTexture = -1; gstate_c.skipDrawReason |= SKIPDRAW_BAD_FB_TEXTURE; } UpdateSamplingParams(*entry, false); From 390f0d79fc17c2ea233257d17dad4e618db5d1e4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 10:16:38 -0700 Subject: [PATCH 54/80] Add a copy path to ConvertColors RGBA8888. It's not actually ever called this way, but I'd rather not let someone get confused if they try to. --- GPU/GLES/TextureCache.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index c62b7c4e32..87cdf9cb54 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -700,6 +700,8 @@ static void ConvertColors(void *dstBuf, const void *srcBuf, GLuint dstFmt, int n default: { // No need to convert RGBA8888, right order already + if (dst != src) + memcpy(dst, src, numPixels * sizeof(u32)); } break; } From cd213e655b7614b04a59aaead57f23f41bf3904c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 10:35:34 -0700 Subject: [PATCH 55/80] Add some debug code for finding textures. --- GPU/GLES/FragmentShaderGenerator.cpp | 1 + GPU/GLES/TextureCache.cpp | 50 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index 6ce57a7b89..3fb06b1df8 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -302,6 +302,7 @@ void GenerateFragmentShader(char *buffer) { #ifdef DEBUG_SHADER if (doTexture) { WRITE(p, " gl_FragColor = texture2D(tex, v_texcoord.xy);\n"); + WRITE(p, " gl_FragColor += vec4(0.3,0,0.3,0.3);\n"); } else { WRITE(p, " gl_FragColor = vec4(1,0,1,1);\n"); } diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 87cdf9cb54..c613c6f269 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -866,7 +866,57 @@ inline u32 TextureCache::GetCurrentClutHash() { return clutHash_; } +// #define DEBUG_TEXTURES + +#ifdef DEBUG_TEXTURES +bool SetDebugTexture() { + static const int highlightFrames = 30; + + static int numTextures = 0; + static int lastFrames = 0; + static int mostTextures = 1; + + if (lastFrames != gpuStats.numFrames) { + mostTextures = std::max(mostTextures, numTextures); + numTextures = 0; + lastFrames = gpuStats.numFrames; + } + + static GLuint solidTexture = 0; + + bool changed = false; + if (((gpuStats.numFrames / highlightFrames) % mostTextures) == numTextures) { + if (gpuStats.numFrames % highlightFrames == 0) { + NOTICE_LOG(HLE, "Highlighting texture # %d / %d", numTextures, mostTextures); + } + static const u32 solidTextureData[] = {0x99AA99FF}; + + if (solidTexture == 0) { + glGenTextures(1, &solidTexture); + glBindTexture(GL_TEXTURE_2D, solidTexture); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, solidTextureData); + } else { + glBindTexture(GL_TEXTURE_2D, solidTexture); + } + changed = true; + } + + ++numTextures; + return changed; +} +#endif + void TextureCache::SetTexture() { +#ifdef DEBUG_TEXTURES + if (SetDebugTexture()) { + // A different texture was bound, let's rebind next time. + lastBoundTexture = -1; + return; + } +#endif + u32 texaddr = (gstate.texaddr[0] & 0xFFFFF0) | ((gstate.texbufwidth[0]<<8) & 0x0F000000); if (!Memory::IsValidAddress(texaddr)) { // Bind a null texture and return. From 1f0d87afd9d1b9b38e2bb4d8b6ec5f1712040cea Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 11:59:35 -0700 Subject: [PATCH 56/80] Allow multiple LO16s after a HI16 in var relocs. The God Eater demo uses these sometimes, and it should be safe if the addresses match or at least overflow to the HI16 the same. --- Core/HLE/sceKernelModule.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index d18392ae43..2a1078413d 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -292,9 +292,13 @@ void __KernelModuleShutdown() void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type) { + // We have to post-process the HI16 part, since it might be +1 or not depending on the LO16 value. static u32 lastHI16RelocAddress = 0; static u32 lastHI16ExportAddress = 0; - static bool lastHI16Processed = false; + // Sometimes, there are multiple LO16s per HI16. This is okay if they match, but we need the old + // HI16 value (before our fixup) for each one. + static u32 lastHI16RelocData = 0; + static bool lastHI16Processed = true; static u32 lastHILO16Address = 0; u32 relocData = Memory::Read_Instruction(relocAddress); @@ -327,6 +331,7 @@ void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type) // The R_MIPS_LO16 and R_MIPS_HI16 will often be *different* relocAddress values. lastHI16RelocAddress = relocAddress; lastHI16ExportAddress = exportAddress; + lastHI16RelocData = Memory::Read_Instruction(relocAddress); lastHI16Processed = false; break; @@ -338,20 +343,19 @@ 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; + u32 full = (lastHI16RelocData << 16) + (s16)(u16)(relocData & 0xFFFF) + exportAddress; 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); + Memory::Write_U32((lastHI16RelocData & ~0xFFFF) | high, lastHI16RelocAddress); lastHI16Processed = true; } else if (lastHILO16Address != full) - WARN_LOG_REPORT(LOADER, "Potentially unsafe unpaired LO16 variable relocation @ %08x / %08x", lastHI16RelocAddress, relocAddress); + WARN_LOG_REPORT(LOADER, "Potentially unsafe unpaired LO16 variable relocation: hi=%08x lo=%08x / last=%08x this=%08x", lastHI16RelocAddress, relocAddress, lastHILO16Address, full); // And then this is the low relocation, hurray. relocData = (relocData & ~0xFFFF) | (full & 0xFFFF); From d37e224f5ca110cb014ad3d1040a9a8bcb9e01ba Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 12:44:25 -0700 Subject: [PATCH 57/80] Allow multiple HI16's in a row. The God Eater demo does in fact use these. Ugh. --- Core/HLE/sceKernelModule.cpp | 67 ++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 2a1078413d..c5eb655449 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -292,14 +292,19 @@ void __KernelModuleShutdown() void WriteVarSymbol(u32 exportAddress, u32 relocAddress, u8 type) { + // Sometimes there are multiple LO16's or HI16's per pair, even though the ABI says nothing of this. + // For multiple LO16's, we need the original (unrelocated) instruction data of the HI16. + // For multiple HI16's, we just need to set each one. + struct HI16RelocInfo + { + u32 addr; + u32 data; + }; + // We have to post-process the HI16 part, since it might be +1 or not depending on the LO16 value. - static u32 lastHI16RelocAddress = 0; static u32 lastHI16ExportAddress = 0; - // Sometimes, there are multiple LO16s per HI16. This is okay if they match, but we need the old - // HI16 value (before our fixup) for each one. - static u32 lastHI16RelocData = 0; + static std::vector lastHI16Relocs; static bool lastHI16Processed = true; - static u32 lastHILO16Address = 0; u32 relocData = Memory::Read_Instruction(relocAddress); @@ -324,42 +329,52 @@ 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); + if (lastHI16ExportAddress != exportAddress) + { + if (!lastHI16Processed && lastHI16Relocs.size() >= 1) + WARN_LOG_REPORT(LOADER, "Unsafe unpaired HI16 variable relocation @ %08x / %08x", lastHI16Relocs[lastHI16Relocs.size() - 1].addr, relocAddress); + + lastHI16ExportAddress = exportAddress; + lastHI16Relocs.clear(); + } // 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; - lastHI16RelocData = Memory::Read_Instruction(relocAddress); + HI16RelocInfo reloc; + reloc.addr = relocAddress; + reloc.data = Memory::Read_Instruction(relocAddress); + lastHI16Relocs.push_back(reloc); lastHI16Processed = false; break; case R_MIPS_LO16: { - // The ABI requires that these come in pairs. - if (lastHI16ExportAddress != exportAddress) - { - ERROR_LOG_REPORT(LOADER, "HI16 and LO16 imports do not match for %08x => %08x/%08x (hi16 export: %08x)", exportAddress, lastHI16RelocAddress, relocAddress, lastHI16ExportAddress); - break; - } // Sign extend the existing low value (e.g. from addiu.) - u32 full = (lastHI16RelocData << 16) + (s16)(u16)(relocData & 0xFFFF) + exportAddress; + const u32 exportOffsetLo = exportAddress + (s16)(u16)(relocData & 0xFFFF); + u32 full = exportOffsetLo; - if (!lastHI16Processed) + // The ABI requires that these come in pairs, at least. + if (lastHI16Relocs.empty()) + ERROR_LOG_REPORT(LOADER, "LO16 without any HI16 variable import at %08x for %08x", relocAddress, exportAddress) + // Try to process at least the low relocation... + else if (lastHI16ExportAddress != exportAddress) + ERROR_LOG_REPORT(LOADER, "HI16 and LO16 imports do not match at %08x for %08x (should be %08x)", relocAddress, lastHI16ExportAddress, exportAddress) + else { - // 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((lastHI16RelocData & ~0xFFFF) | high, lastHI16RelocAddress); + // Process each of the HI16. Usually there's only one. + for (auto it = lastHI16Relocs.begin(), end = lastHI16Relocs.end(); it != end; ++it) + { + full = (it->data << 16) + exportOffsetLo; + // 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((it->data & ~0xFFFF) | high, it->addr); + } lastHI16Processed = true; } - else if (lastHILO16Address != full) - WARN_LOG_REPORT(LOADER, "Potentially unsafe unpaired LO16 variable relocation: hi=%08x lo=%08x / last=%08x this=%08x", lastHI16RelocAddress, relocAddress, lastHILO16Address, full); - // And then this is the low relocation, hurray. + // With full set above (hopefully), now we just need to correct the low instruction. relocData = (relocData & ~0xFFFF) | (full & 0xFFFF); - lastHILO16Address = full; } break; From 23ee9b8cd55ad4254ee25246dea418674a9ecf90 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 22 Jun 2013 22:27:59 +0200 Subject: [PATCH 58/80] Add "Take Screenshot" to Windows version. Writes to "screenshots" subdirectory. No smart file naming yet. --- .gitignore | 2 +- GPU/GLES/DisplayListInterpreter.h | 2 +- GPU/GLES/Framebuffer.h | 1 + UI/NativeApp.cpp | 47 ++++++++++++++++++++++++++++++ Windows/WndMainWindow.cpp | 5 +++- Windows/ppsspp.rc | 2 ++ Windows/resource.h | Bin 22882 -> 22976 bytes native | 2 +- 8 files changed, 57 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 51767a038d..7e50d2692b 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,7 @@ __testerror.txt __testfinish.txt __testfailure.bmp GameLogNotes.txt - +screenshots android/ui_atlas.zim android/assets/lang android/assets/flash0 diff --git a/GPU/GLES/DisplayListInterpreter.h b/GPU/GLES/DisplayListInterpreter.h index f1da2ea9c0..15225bf3c9 100644 --- a/GPU/GLES/DisplayListInterpreter.h +++ b/GPU/GLES/DisplayListInterpreter.h @@ -52,7 +52,7 @@ public: virtual void DumpNextFrame(); virtual void Flush(); virtual void DoState(PointerWrap &p); - + // Called by the window system if the window size changed. This will be reflected in PSPCoreParam.pixel*. virtual void Resized(); virtual bool DecodeTexture(u8* dest, GPUgstate state) diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 78a6dfa3eb..8abe456b7d 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -104,6 +104,7 @@ public: void CopyDisplayToOutput(); void SetRenderFrameBuffer(); // Uses parameters computed from gstate void UpdateFromMemory(u32 addr, int size); + // TODO: Break out into some form of FBO manager VirtualFramebuffer *GetDisplayFBO(); void SetDisplayFramebuffer(u32 framebuf, u32 stride, int format); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 7b4adda767..26d3bb0760 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -34,6 +34,7 @@ #include "base/NativeApp.h" #include "file/vfs.h" #include "file/zip_read.h" +#include "ext/jpge/jpge.h" #include "gfx_es2/gl_state.h" #include "gfx/gl_lost_manager.h" #include "gfx/texture.h" @@ -64,6 +65,7 @@ #include "UIShader.h" #include "UI/PluginScreen.h" +#include "UI/OnScreenDisplay.h" // The new UI framework, for initialization @@ -87,6 +89,8 @@ std::string game_title; bool isJailed; #endif +// Really need to clean this mess of globals up... but instead I add more :P +bool g_TakeScreenshot; recursive_mutex pendingMutex; static bool isMessagePending; @@ -445,6 +449,45 @@ void NativeInitGraphics() { glstate.viewport.set(0, 0, pixel_xres, pixel_yres); } +void TakeScreenshot() { +#ifdef _WIN32 + g_TakeScreenshot = false; + mkDir("screenshots"); + + // First, find a free filename. + int i = 0; + + char temp[256]; + while (i < 10000) { + sprintf(temp, "screenshots/screen%05d.jpg", i); + FileInfo info; + if (!getFileInfo(temp, &info)) + break; + i++; + } + + // Okay, allocate a buffer. + u8 *buffer = new u8[4 * pixel_xres * pixel_yres]; + // Silly openGL reads upside down, we flip to another buffer for simplicity. + u8 *flipbuffer = new u8[4 * pixel_xres * pixel_yres]; + + glReadPixels(0, 0, pixel_xres, pixel_yres, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + + for (int y = 0; y < pixel_yres; y++) { + memcpy(flipbuffer + y * pixel_xres * 4, buffer + (pixel_yres - y - 1) * pixel_xres * 4, pixel_xres * 4); + } + + jpge::params params; + params.m_quality = 90; + compress_image_to_jpeg_file(temp, pixel_xres, pixel_yres, 4, flipbuffer, params); + + delete [] buffer; + delete [] flipbuffer; + + osm.Show(temp); +#endif +} + void NativeRender() { EnableFZ(); @@ -464,6 +507,10 @@ void NativeRender() { glUniformMatrix4fv(UIShader_Get()->u_worldviewproj, 1, GL_FALSE, ortho.getReadPtr()); screenManager->render(); + + if (g_TakeScreenshot) { + TakeScreenshot(); + } } void NativeUpdate(InputState &input) { diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index ee02b7bc89..90ff964f78 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -47,7 +47,7 @@ BOOL g_bFullScreen = FALSE; static RECT g_normalRC = {0}; - +extern bool g_TakeScreenshot; extern InputState input_state; extern const char * getVirtualKeyName(unsigned char key); extern const char * getXinputButtonName(unsigned int button); @@ -771,6 +771,9 @@ namespace MainWindow DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); DialogManager::EnableAll(TRUE); break; + case ID_DEBUG_TAKESCREENSHOT: + g_TakeScreenshot = true; + break; default: MessageBox(hwndMain,"Unimplemented","Sorry",0); diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index bd316f4fa5..c2c0f1f9c4 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -73,6 +73,7 @@ BEGIN "4", ID_OPTIONS_SCREEN4X, VIRTKEY, CONTROL, NOINVERT VK_F5, ID_OPTIONS_BUFFEREDRENDERING, VIRTKEY, NOINVERT VK_F6, ID_OPTIONS_HARDWARETRANSFORM, VIRTKEY, NOINVERT + VK_F9, ID_DEBUG_TAKESCREENSHOT, VIRTKEY, NOINVERT VK_F11, ID_OPTIONS_FULLSCREEN, VIRTKEY, NOINVERT END @@ -360,6 +361,7 @@ BEGIN MENUITEM "&Reset Symbol Table", ID_DEBUG_RESETSYMBOLTABLE MENUITEM SEPARATOR MENUITEM "D&ump Next Frame to Log", ID_DEBUG_DUMPNEXTFRAME + MENUITEM "&Take Screenshot\tF9", ID_DEBUG_TAKESCREENSHOT MENUITEM SEPARATOR MENUITEM "&Disassembly\tCtrl+D", ID_DEBUG_DISASSEMBLY MENUITEM "&Log Console\tCtrl+L", ID_DEBUG_LOG diff --git a/Windows/resource.h b/Windows/resource.h index e508cf6a72926ea3153d79169fb2fafc52355ca2..ccf442fae0cfababba4fa11939017c1d7f0dfd0e 100644 GIT binary patch delta 72 zcmaE~iSfW@#tpl|CU0QplMG>SWbkHiWe8?)W(We(t_*%awg-d%WJhVy$qJzyliMQ1 bCO-%f*?cN&594Gu0kO$;5fYQPM63e<7iSh- delta 33 rcmV++0N($=vjO6=0kFDOk{$|^?olM-331)mO< diff --git a/native b/native index ca9301fedf..bd78a06015 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit ca9301fedf879f28d6f11fda59709a66c5368bec +Subproject commit bd78a060158aa7ee12713727164541be29f8355a From 9161c1e2895318b68c9940cf10fff2a1e0fcda43 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 22 Jun 2013 23:25:22 +0200 Subject: [PATCH 59/80] Use unknown.png for missing pbp/elf icons --- UI/GameInfoCache.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index bfa8e8be6d..2ce862fa2a 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -21,6 +21,7 @@ #include "base/timeutil.h" #include "base/stringutil.h" #include "file/file_util.h" +#include "file/zip_read.h" #include "image/png_load.h" #include "thread/prioritizedworkqueue.h" #include "GameInfoCache.h" @@ -181,15 +182,21 @@ public: if (pbp.GetSubFileSize(PBP_ICON0_PNG) > 0) { pbp.GetSubFileAsString(PBP_ICON0_PNG, &info_->iconTextureData); } else { - // We should load a default image here. + // Read standard icon + size_t sz; + uint8_t *contents = ReadLocalFile("assets/unknown.png", &sz); + { + lock_guard lock(info_->lock); + info_->iconTextureData = std::string((const char *)contents, sz); + } + delete [] contents; } } if (info_->wantBG) { - { + if (pbp.GetSubFileSize(PBP_PIC1_PNG) > 0) { lock_guard lock(info_->lock); - if (pbp.GetSubFileSize(PBP_PIC1_PNG) > 0) - pbp.GetSubFileAsString(PBP_PIC1_PNG, &info_->pic1TextureData); + pbp.GetSubFileAsString(PBP_PIC1_PNG, &info_->pic1TextureData); } } } @@ -199,6 +206,18 @@ public: info_->id = "ELF000000"; info_->id_version = "ELF000000_1.00"; info_->paramSFOLoaded = true; + + { + // Read standard icon + size_t sz; + uint8_t *contents = ReadLocalFile("assets/unknown.png", &sz); + { + lock_guard lock(info_->lock); + info_->iconTextureData = std::string((const char *)contents, sz); + } + delete [] contents; + } + return; case FILETYPE_PSP_ISO: From a6e80d318b9f6fe7a00b4f4a8818aa7e9d2401e1 Mon Sep 17 00:00:00 2001 From: raven02 Date: Sun, 23 Jun 2013 05:30:54 +0800 Subject: [PATCH 60/80] "Show analog stick"/"Tilt" should only valid when on-screen control is on --- UI/MenuScreens.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index 37b5c8d5f7..d31b345f40 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -1038,10 +1038,10 @@ void LanguageScreen::render() { screenManager()->finishDialog(this, DR_OK); } - int buttonW = LARGE_BUTTON_WIDTH - 50; + int buttonW = LARGE_BUTTON_WIDTH - 30; if (small) { - buttonW = LARGE_BUTTON_WIDTH - 70; + buttonW = LARGE_BUTTON_WIDTH - 50; } VGrid vlang(20, small ? 20 : 100, dp_yres - 50, 10, 10); @@ -1160,7 +1160,7 @@ void SystemScreen::render() { if (g_Config.bJit) UICheckBox(GEN_ID, x, y += stride, s->T("Fast Memory", "Fast Memory (unstable)"), ALIGN_TOPLEFT, &g_Config.bFastMemory); - UICheckBox(GEN_ID, x, y += stride, s->T("Daylight Savings"), ALIGN_TOPLEFT, &g_Config.bDayLightSavings); + //UICheckBox(GEN_ID, x, y += stride, s->T("Daylight Savings"), ALIGN_TOPLEFT, &g_Config.bDayLightSavings); const char *buttonPreferenceTitle; switch (g_Config.iButtonPreference) { @@ -1275,9 +1275,9 @@ void ControlsScreen::render() { int columnw = 440; UICheckBox(GEN_ID, x, y += stride, c->T("OnScreen", "On-Screen Touch Controls"), ALIGN_TOPLEFT, &g_Config.bShowTouchControls); - UICheckBox(GEN_ID, x, y += stride, c->T("Show Analog Stick"), ALIGN_TOPLEFT, &g_Config.bShowAnalogStick); - UICheckBox(GEN_ID, x, y += stride, c->T("Tilt", "Tilt to Analog (horizontal)"), ALIGN_TOPLEFT, &g_Config.bAccelerometerToAnalogHoriz); if (g_Config.bShowTouchControls) { + UICheckBox(GEN_ID, x, y += stride, c->T("Show Analog Stick"), ALIGN_TOPLEFT, &g_Config.bShowAnalogStick); + UICheckBox(GEN_ID, x, y += stride, c->T("Tilt", "Tilt to Analog (horizontal)"), ALIGN_TOPLEFT, &g_Config.bAccelerometerToAnalogHoriz); UICheckBox(GEN_ID, x, y += stride, c->T("Buttons Scaling"), ALIGN_TOPLEFT, &g_Config.bLargeControls); if (g_Config.bLargeControls) { char scale[256]; From 643b3b0c7158e89ee6a123d0a165ee5f24aa4b3d Mon Sep 17 00:00:00 2001 From: raven02 Date: Sun, 23 Jun 2013 05:46:54 +0800 Subject: [PATCH 61/80] Keep Tilt to Analog even without on-screen control --- UI/MenuScreens.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index d31b345f40..dc7ad0f91f 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -1275,9 +1275,9 @@ void ControlsScreen::render() { int columnw = 440; UICheckBox(GEN_ID, x, y += stride, c->T("OnScreen", "On-Screen Touch Controls"), ALIGN_TOPLEFT, &g_Config.bShowTouchControls); + UICheckBox(GEN_ID, x, y += stride, c->T("Tilt", "Tilt to Analog (horizontal)"), ALIGN_TOPLEFT, &g_Config.bAccelerometerToAnalogHoriz); if (g_Config.bShowTouchControls) { UICheckBox(GEN_ID, x, y += stride, c->T("Show Analog Stick"), ALIGN_TOPLEFT, &g_Config.bShowAnalogStick); - UICheckBox(GEN_ID, x, y += stride, c->T("Tilt", "Tilt to Analog (horizontal)"), ALIGN_TOPLEFT, &g_Config.bAccelerometerToAnalogHoriz); UICheckBox(GEN_ID, x, y += stride, c->T("Buttons Scaling"), ALIGN_TOPLEFT, &g_Config.bLargeControls); if (g_Config.bLargeControls) { char scale[256]; From eb1f39f0ebd4b45306393fa90a03c58d73c279ce Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 14:48:09 -0700 Subject: [PATCH 62/80] Use the saveName by default in MAKE/WRITE/READDATA. Suspected this, but wasn't sure. Tests confirm it works like AUTOLOAD. --- Core/Dialog/PSPSaveDialog.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index 23d32df181..175e2b9151 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -223,7 +223,6 @@ const std::string PSPSaveDialog::GetSelectedSaveDirName() return param.GetSaveDirName(param.GetPspParam()); // Intentional fallthrough when saveName not valid. - // 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: @@ -233,7 +232,7 @@ const std::string PSPSaveDialog::GetSelectedSaveDirName() case SCE_UTILITY_SAVEDATA_TYPE_ERASESECURE: case SCE_UTILITY_SAVEDATA_TYPE_ERASE: case SCE_UTILITY_SAVEDATA_TYPE_DELETEDATA: - if (param.GetSaveDirName(param.GetPspParam(), currentSelectedSave) == "<>") + if (param.GetSaveName(param.GetPspParam()).length() != 0) return param.GetSaveDirName(param.GetPspParam()); // Intentional fallthrough when saveName not valid. From f7a717746fad73fc9cf5a875e9b6f3a417fe098f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 22:40:48 -0700 Subject: [PATCH 63/80] Sometimes newData.buf is NULL, don't log an error. --- Core/Dialog/SavedataParam.cpp | 7 +++---- Core/Dialog/SavedataParam.h | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 02693a1e33..9be6d41556 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -1210,15 +1210,14 @@ void SavedataParam::ClearFileInfo(SaveFileInfo &saveInfo, std::string saveName) saveInfo.idx = 0; saveInfo.textureData = 0; - if (Memory::IsValidAddress(GetPspParam()->newData)) + if (GetPspParam()->newData.Valid() && Memory::IsValidAddress(GetPspParam()->newData->buf)) { // We have a png to show if (!noSaveIcon) { noSaveIcon = new SaveFileInfo(); - PspUtilitySavedataFileData newData; - Memory::ReadStruct(GetPspParam()->newData, &newData); - CreatePNGIcon(Memory::GetPointer(newData.buf), (int)newData.size, *noSaveIcon); + PspUtilitySavedataFileData *newData = GetPspParam()->newData; + CreatePNGIcon(Memory::GetPointer(newData->buf), (int)newData->size, *noSaveIcon); } saveInfo.textureData = noSaveIcon->textureData; saveInfo.textureWidth = noSaveIcon->textureWidth; diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index 979c43f533..119e229ab0 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -161,7 +161,7 @@ struct SceUtilitySavedataParam PspUtilitySavedataFileData pic1FileData; PspUtilitySavedataFileData snd0FileData; - u32 newData; + PSPPointer newData; int focus; int abortStatus; From 12232dcad45fe5c7a7edc24cb7afe9faf3bd68fe Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 22:44:00 -0700 Subject: [PATCH 64/80] Simplify some Memory::GetPointer stuff. --- Core/Dialog/SavedataParam.cpp | 24 ++++++++++-------------- Core/Dialog/SavedataParam.h | 2 +- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 9be6d41556..bf7ea74ae9 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -404,33 +404,29 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save // SAVE ICON0 - if (param->icon0FileData.buf) + if (param->icon0FileData.buf.Valid()) { - u8* data_ = (u8*)Memory::GetPointer(param->icon0FileData.buf); std::string icon0path = dirPath + "/" + ICON0_FILENAME; - WritePSPFile(icon0path, data_, param->icon0FileData.bufSize); + WritePSPFile(icon0path, param->icon0FileData.buf, param->icon0FileData.bufSize); } // SAVE ICON1 - if (param->icon1FileData.buf) + if (param->icon1FileData.buf.Valid()) { - u8* data_ = (u8*)Memory::GetPointer(param->icon1FileData.buf); std::string icon1path = dirPath + "/" + ICON1_FILENAME; - WritePSPFile(icon1path, data_, param->icon1FileData.bufSize); + WritePSPFile(icon1path, param->icon1FileData.buf, param->icon1FileData.bufSize); } // SAVE PIC1 - if (param->pic1FileData.buf) + if (param->pic1FileData.buf.Valid()) { - u8* data_ = (u8*)Memory::GetPointer(param->pic1FileData.buf); std::string pic1path = dirPath + "/" + PIC1_FILENAME; - WritePSPFile(pic1path, data_, param->pic1FileData.bufSize); + WritePSPFile(pic1path, param->pic1FileData.buf, param->pic1FileData.bufSize); } // Save SND - if (param->snd0FileData.buf) + if (param->snd0FileData.buf.Valid()) { - u8* data_ = (u8*)Memory::GetPointer(param->snd0FileData.buf); std::string snd0path = dirPath + "/" + SND0_FILENAME; - WritePSPFile(snd0path, data_, param->snd0FileData.bufSize); + WritePSPFile(snd0path, param->snd0FileData.buf, param->snd0FileData.bufSize); } return true; @@ -1210,14 +1206,14 @@ void SavedataParam::ClearFileInfo(SaveFileInfo &saveInfo, std::string saveName) saveInfo.idx = 0; saveInfo.textureData = 0; - if (GetPspParam()->newData.Valid() && Memory::IsValidAddress(GetPspParam()->newData->buf)) + if (GetPspParam()->newData.Valid() && GetPspParam()->newData->buf.Valid()) { // We have a png to show if (!noSaveIcon) { noSaveIcon = new SaveFileInfo(); PspUtilitySavedataFileData *newData = GetPspParam()->newData; - CreatePNGIcon(Memory::GetPointer(newData->buf), (int)newData->size, *noSaveIcon); + CreatePNGIcon(newData->buf, (int)newData->size, *noSaveIcon); } saveInfo.textureData = noSaveIcon->textureData; saveInfo.textureWidth = noSaveIcon->textureWidth; diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index 119e229ab0..e8a83d7699 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -77,7 +77,7 @@ struct PspUtilitySavedataSFOParam }; struct PspUtilitySavedataFileData { - int buf; + PSPPointer buf; SceSize bufSize; // Size of the buffer pointed to by buf SceSize size; // Actual file size to write / was read int unknown; From dae4156654725004cc8977dfe4c5219c3f5b6b5d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 22 Jun 2013 22:49:39 -0700 Subject: [PATCH 65/80] Make more savedata struct parts PSPPointers. --- Core/Dialog/SavedataParam.cpp | 15 +++++++-------- Core/Dialog/SavedataParam.h | 8 +++++--- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index bf7ea74ae9..aee1205b1a 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -264,7 +264,7 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save cryptedSize = param->dataSize; if(cryptedSize == 0 || (SceSize)cryptedSize > param->dataBufSize) cryptedSize = param->dataBufSize; // fallback, should never use this - u8* data_ = (u8*)Memory::GetPointer(param->dataBuf); + u8 *data_ = param->dataBuf; int aligned_len = align16(cryptedSize); cryptedData = new u8[aligned_len + 0x10]; @@ -369,7 +369,7 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save if(param->dataBuf != 0) // Can launch save without save data in mode 13 { std::string filePath = dirPath+"/"+GetFileName(param); - u8* data_ = 0; + u8 *data_ = 0; SceSize saveSize = 0; if(cryptedData == 0) // Save decrypted data { @@ -377,7 +377,7 @@ bool SavedataParam::Save(SceUtilitySavedataParam* param, const std::string &save if(saveSize == 0 || saveSize > param->dataBufSize) saveSize = param->dataBufSize; // fallback, should never use this - data_ = (u8*)Memory::GetPointer(param->dataBuf); + data_ = param->dataBuf; } else { @@ -438,7 +438,7 @@ bool SavedataParam::Load(SceUtilitySavedataParam *param, const std::string &save return false; } - u8 *data_ = (u8*)Memory::GetPointer(param->dataBuf); + u8 *data_ = param->dataBuf; std::string dirPath = GetSaveFilePath(param, GetSaveDir(param, saveDirName)); if (saveId >= 0 && saveNameListDataCount > 0) // if user selection, use it @@ -1036,14 +1036,13 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param) listEmptyFile = false; } - typedef char (*SaveNameListData_t)[20]; - SaveNameListData_t saveNameListData; + SceUtilitySavedataSaveName *saveNameListData; bool hasMultipleFileName = false; - if (param->saveNameList != 0) + if (param->saveNameList.Valid()) { Clear(); - saveNameListData = (SaveNameListData_t)Memory::GetPointer(param->saveNameList); + saveNameListData = param->saveNameList; // Get number of fileName in array saveDataListCount = 0; diff --git a/Core/Dialog/SavedataParam.h b/Core/Dialog/SavedataParam.h index e8a83d7699..6fa1ddc69c 100644 --- a/Core/Dialog/SavedataParam.h +++ b/Core/Dialog/SavedataParam.h @@ -128,6 +128,8 @@ struct SceUtilitySavedataFileListInfo PSPPointer systemEntries; }; +typedef char SceUtilitySavedataSaveName[20]; + // Structure to hold the parameters for the sceUtilitySavedataInitStart function. struct SceUtilitySavedataParam { @@ -142,14 +144,14 @@ struct SceUtilitySavedataParam char gameName[13]; char unused[3]; /** saveName: name of the particular save, normally a number */ - char saveName[20]; - u32 saveNameList; + SceUtilitySavedataSaveName saveName; + PSPPointer saveNameList; /** fileName: name of the data file of the game for example DATA.BIN */ char fileName[13]; char unused2[3]; /** pointer to a buffer that will contain data file unencrypted data */ - u32 dataBuf; // Initially void*, but void* in 64bit system take 8 bytes. + PSPPointer dataBuf; /** size of allocated space to dataBuf */ SceSize dataBufSize; SceSize dataSize; // Size of the actual save data From 3fc397c5907a629296b8d540900e38ee1f868afa Mon Sep 17 00:00:00 2001 From: 173210 Date: Sun, 23 Jun 2013 16:52:41 +0900 Subject: [PATCH 66/80] Added scePowerGetBatteryTemp support It returns 0 celsius degrees --- Core/HLE/scePower.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Core/HLE/scePower.cpp b/Core/HLE/scePower.cpp index 13f31e17da..0903e3d07a 100644 --- a/Core/HLE/scePower.cpp +++ b/Core/HLE/scePower.cpp @@ -71,6 +71,12 @@ int scePowerGetBatteryLifeTime() { return 0; } +int scePowerGetBatteryTemp() { + DEBUG_LOG(HLE, "0=scePowerGetBatteryTemp()"); + // 0 means celsius temperature of the battery + return 0; +} + int scePowerIsPowerOnline() { DEBUG_LOG(HLE, "1=scePowerIsPowerOnline"); return 1; @@ -314,7 +320,7 @@ static const HLEFunction scePower[] = { {0xFD18A0FF,0,"scePowerGetBatteryFullCapacity"}, {0x2085D15D,&WrapI_V,"scePowerGetBatteryLifePercent"}, {0x8EFB3FA2,&WrapI_V,"scePowerGetBatteryLifeTime"}, - {0x28E12023,0,"scePowerGetBatteryTemp"}, + {0x28E12023,&WrapI_V,"scePowerGetBatteryTemp"}, {0x862AE1A6,0,"scePowerGetBatteryElec"}, {0x483CE86B,0,"scePowerGetBatteryVolt"}, {0x23436A4A,0,"scePowerGetInnerTemp"}, From 747d3d2d9c9f9c1ced6fb7a18953e2d2233e1074 Mon Sep 17 00:00:00 2001 From: raven02 Date: Sun, 23 Jun 2013 21:33:50 +0800 Subject: [PATCH 67/80] Add 4X/5X to texure scaling level for windows --- UI/MenuScreens.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index dc7ad0f91f..0e07cf9aee 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -894,7 +894,12 @@ void GraphicsScreenP2::render() { g_Config.iTexScalingLevel = 2; if (UIButton(GEN_ID, hlinear2, 45, 0, gs->T("3x"), ALIGN_LEFT)) g_Config.iTexScalingLevel = 3; - +#ifdef _WIN32 + if (UIButton(GEN_ID, hlinear2, 45, 0, gs->T("4x"), ALIGN_LEFT)) + g_Config.iTexScalingLevel = 4; + if (UIButton(GEN_ID, hlinear2, 45, 0, gs->T("5x"), ALIGN_LEFT)) + g_Config.iTexScalingLevel = 5; +#endif UICheckBox(GEN_ID, x + 60, y += stride + 20, gs->T("Deposterize"), ALIGN_LEFT, &g_Config.bTexDeposterize); } else g_Config.iTexScalingLevel = 1; From ffc3db14290e532809cf39d6db613d749391ae91 Mon Sep 17 00:00:00 2001 From: oioitff Date: Sat, 22 Jun 2013 20:17:31 +0800 Subject: [PATCH 68/80] Bug fix for sceMp3, make sceMp3 work fine. (dunno why this didn't merge before) Conflicts: Core/HLE/sceMp3.cpp --- Core/HLE/sceMp3.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 449362a665..83abca6e11 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -20,6 +20,7 @@ #include "Core/HLE/sceMp3.h" #include "Core/HW/MediaEngine.h" #include "Core/Reporting.h" +#include "../HW/MediaEngine.h" #ifdef USE_FFMPEG #ifndef PRId64 @@ -129,10 +130,10 @@ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { AVFrame frame = {0}; AVPacket packet = {0}; - int got_frame, ret; + int got_frame = 0, ret; static int audio_frame_count = 0; - while (bytesdecoded < ctx->mp3PcmBufSize) { + while (!got_frame) { if ((ret = av_read_frame(ctx->avformat_context, &packet)) < 0) break; @@ -147,7 +148,7 @@ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { if (got_frame) { char buf[1024] = ""; av_ts_make_time_string(buf, frame.pts, &ctx->decoder_context->time_base); - INFO_LOG(HLE, "audio_frame n:%d nb_samples:%d pts:%s", audio_frame_count++, frame.nb_samples, buf); + DEBUG_LOG(HLE, "audio_frame n:%d nb_samples:%d pts:%s", audio_frame_count++, frame.nb_samples, buf); /* u8 *audio_dst_data; @@ -423,7 +424,7 @@ int sceMp3GetSumDecodedSample(u32 mp3) { } int sceMp3SetLoopNum(u32 mp3, int loop) { - DEBUG_LOG(HLE, "sceMp3SetLoopNum(%08X, %i)", mp3, loop); + INFO_LOG(HLE, "sceMp3SetLoopNum(%08X, %i)", mp3, loop); Mp3Context *ctx = getMp3Ctx(mp3); if (!ctx) { @@ -470,7 +471,7 @@ int sceMp3GetSamplingRate(u32 mp3) { } int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcposPtr) { - DEBUG_LOG(HLE, "HACK: sceMp3GetInfoToAddStreamData(%08X, %08X, %08X, %08X)", mp3, dstPtr, towritePtr, srcposPtr); + INFO_LOG(HLE, "sceMp3GetInfoToAddStreamData(%08X, %08X, %08X, %08X)", mp3, dstPtr, towritePtr, srcposPtr); Mp3Context *ctx = getMp3Ctx(mp3); if (!ctx) { @@ -480,7 +481,7 @@ int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcpos u32 buf, max_write; if (ctx->readPosition < ctx->mp3StreamEnd) { - buf = ctx->mp3Buf; + buf = ctx->mp3Buf + ctx->bufferWrite; max_write = std::min(ctx->mp3BufSize - ctx->bufferWrite, ctx->mp3BufSize - ctx->bufferAvailable); } else { buf = 0; @@ -498,7 +499,7 @@ int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcpos } int sceMp3NotifyAddStreamData(u32 mp3, int size) { - DEBUG_LOG(HLE, "sceMp3NotifyAddStreamData(%08X, %i)", mp3, size); + INFO_LOG(HLE, "sceMp3NotifyAddStreamData(%08X, %i)", mp3, size); Mp3Context *ctx = getMp3Ctx(mp3); if (!ctx) { @@ -513,6 +514,11 @@ int sceMp3NotifyAddStreamData(u32 mp3, int size) { if (ctx->bufferWrite == ctx->mp3BufSize) ctx->bufferWrite = 0; + if (ctx->readPosition >= ctx->mp3StreamEnd && ctx->mp3LoopNum != 0) { + ctx->readPosition = ctx->mp3StreamStart; + if (ctx->mp3LoopNum > 0) + ctx->mp3LoopNum--; + } return 0; } From 0e61385c2edceda8228179d8b60d78f1c124f727 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 23 Jun 2013 16:10:03 +0200 Subject: [PATCH 69/80] Mark 4x and 5x texture scaling as "overkill" and "crazy" in the windows menu. Because they really are. --- Windows/ppsspp.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index c2c0f1f9c4..277d7a53c2 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -395,8 +395,8 @@ BEGIN MENUITEM "&Off", ID_TEXTURESCALING_OFF MENUITEM "&2x", ID_TEXTURESCALING_2X MENUITEM "&3x", ID_TEXTURESCALING_3X - MENUITEM "&4x", ID_TEXTURESCALING_4X - MENUITEM "&5x", ID_TEXTURESCALING_5X + MENUITEM "&4x (overkill)", ID_TEXTURESCALING_4X + MENUITEM "&5x (insane)", ID_TEXTURESCALING_5X MENUITEM SEPARATOR MENUITEM "&xBRZ", ID_TEXTURESCALING_XBRZ MENUITEM "&Hybrid", ID_TEXTURESCALING_HYBRID From 6f24514ad54b89a186ce346cb4ca3c253dfc3b68 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 23 Jun 2013 16:24:45 +0200 Subject: [PATCH 70/80] Fix unknown.png issues. Hopefully fixes #2411. --- UI/GameInfoCache.cpp | 60 +++++++++++++++++++++++--------------------- UI/MenuScreens.cpp | 2 +- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 2ce862fa2a..2046e6e2ad 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -184,8 +184,8 @@ public: } else { // Read standard icon size_t sz; - uint8_t *contents = ReadLocalFile("assets/unknown.png", &sz); - { + uint8_t *contents = VFSReadFile("unknown.png", &sz); + if (contents) { lock_guard lock(info_->lock); info_->iconTextureData = std::string((const char *)contents, sz); } @@ -210,7 +210,7 @@ public: { // Read standard icon size_t sz; - uint8_t *contents = ReadLocalFile("assets/unknown.png", &sz); + uint8_t *contents = VFSReadFile("assets/unknown.png", &sz); { lock_guard lock(info_->lock); info_->iconTextureData = std::string((const char *)contents, sz); @@ -222,34 +222,38 @@ public: case FILETYPE_PSP_ISO: case FILETYPE_PSP_ISO_NP: - info_->fileType = FILETYPE_PSP_ISO; - SequentialHandleAllocator handles; - // Let's assume it's an ISO. - // TODO: This will currently read in the whole directory tree. Not really necessary for just a - // few files. - BlockDevice *bd = constructBlockDevice(gamePath_.c_str()); - if (!bd) - return; // nothing to do here.. - ISOFileSystem umd(&handles, bd, "/PSP_GAME"); + { + info_->fileType = FILETYPE_PSP_ISO; + SequentialHandleAllocator handles; + // Let's assume it's an ISO. + // TODO: This will currently read in the whole directory tree. Not really necessary for just a + // few files. + BlockDevice *bd = constructBlockDevice(gamePath_.c_str()); + if (!bd) + return; // nothing to do here.. + ISOFileSystem umd(&handles, bd, "/PSP_GAME"); - // Alright, let's fetch the PARAM.SFO. - std::string paramSFOcontents; - if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", ¶mSFOcontents, 0)) { - lock_guard lock(info_->lock); - info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); - info_->title = info_->paramSFO.GetValueString("TITLE"); - info_->id = info_->paramSFO.GetValueString("DISC_ID"); - info_->id_version = info_->paramSFO.GetValueString("DISC_ID") + "_" + info_->paramSFO.GetValueString("DISC_VERSION"); + // Alright, let's fetch the PARAM.SFO. + std::string paramSFOcontents; + if (ReadFileToString(&umd, "/PSP_GAME/PARAM.SFO", ¶mSFOcontents, 0)) { + lock_guard lock(info_->lock); + info_->paramSFO.ReadSFO((const u8 *)paramSFOcontents.data(), paramSFOcontents.size()); + info_->title = info_->paramSFO.GetValueString("TITLE"); + info_->id = info_->paramSFO.GetValueString("DISC_ID"); + info_->id_version = info_->paramSFO.GetValueString("DISC_ID") + "_" + info_->paramSFO.GetValueString("DISC_VERSION"); - info_->paramSFOLoaded = true; + info_->paramSFOLoaded = true; + } + + ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->iconTextureData, &info_->lock); + if (info_->wantBG) { + ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0TextureData, &info_->lock); + } + ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1TextureData, &info_->lock); + break; } - - ReadFileToString(&umd, "/PSP_GAME/ICON0.PNG", &info_->iconTextureData, &info_->lock); - if (info_->wantBG) { - ReadFileToString(&umd, "/PSP_GAME/PIC0.PNG", &info_->pic0TextureData, &info_->lock); - } - ReadFileToString(&umd, "/PSP_GAME/PIC1.PNG", &info_->pic1TextureData, &info_->lock); - + default: + ; } // probably only want these when we ask for the background image... // should maybe flip the flag to "onlyIcon" diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index 0e07cf9aee..d18b1b0e80 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -306,7 +306,7 @@ void MenuScreen::render() { // This might create a texture so we must flush first. UIFlush(); GameInfo *ginfo = g_gameInfoCache.GetInfo(g_Config.recentIsos[i], false); - if (ginfo && ginfo->fileType != FILETYPE_PSP_ELF) { + if (ginfo) { u32 color; if (ginfo->iconTexture == 0) { color = 0; From 67ef7fea3ebaa85e976d48d67ed2273cfadf217a Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 23 Jun 2013 17:05:59 +0200 Subject: [PATCH 71/80] Various FBO-related fixes, hopefully reducing or removing the related crashes... --- GPU/GLES/Framebuffer.cpp | 37 ++++++++++++++++++++++++++++++++----- GPU/GLES/Framebuffer.h | 4 +++- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index c511789314..1082b0517d 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -324,6 +324,18 @@ void GuessDrawingSize(int &drawing_width, int &drawing_height) { drawing_height = std::min(drawing_height, 512); } +void FramebufferManager::NotifyDestroyed(VirtualFramebuffer *v) { + // Wipe some pointers + if (currentRenderVfb_ == v) + currentRenderVfb_ = 0; + if (displayFramebuf_ == v) + displayFramebuf_ = 0; + if (prevDisplayFramebuf_ == v) + prevDisplayFramebuf_ = 0; + if (prevPrevDisplayFramebuf_ == v) + prevPrevDisplayFramebuf_ = 0; +} + void FramebufferManager::SetRenderFrameBuffer() { if (!gstate_c.framebufChanged && currentRenderVfb_) { currentRenderVfb_->last_frame_used = gpuStats.numFrames; @@ -375,8 +387,16 @@ void FramebufferManager::SetRenderFrameBuffer() { // To do this right we should copy the data over too, but meh. buffer_width = std::max((int)v->width, drawing_width); buffer_height = std::max((int)v->height, drawing_height); + + textureCache_->NotifyFramebufferDestroyed(v->fb_address, v); + if (v->fbo) { + fbo_destroy(v->fbo); + v->fbo = 0; + } + NotifyDestroyed(v); + delete v; - vfbs_.erase(iter); + vfbs_.erase(iter++); break; } } @@ -432,7 +452,11 @@ void FramebufferManager::SetRenderFrameBuffer() { if (useBufferedRendering_) { vfb->fbo = fbo_create(vfb->renderWidth, vfb->renderHeight, 1, true, vfb->colorDepth); - fbo_bind_as_render_target(vfb->fbo); + if (vfb->fbo) { + fbo_bind_as_render_target(vfb->fbo); + } else { + ERROR_LOG(HLE, "Error creating FBO! %i x %i", vfb->renderWidth, vfb->renderHeight); + } } else { fbo_unbind(); // Let's ignore rendering to targets that have not (yet) been displayed. @@ -649,14 +673,14 @@ std::vector FramebufferManager::GetFramebufferList() { void FramebufferManager::DecimateFBOs() { fbo_unbind(); currentRenderVfb_ = 0; - + retry: for (auto iter = vfbs_.begin(); iter != vfbs_.end();) { VirtualFramebuffer *vfb = *iter; if (vfb == displayFramebuf_ || vfb == prevDisplayFramebuf_ || vfb == prevPrevDisplayFramebuf_) { ++iter; continue; } - int age = frameLastFramebufUsed - (*iter)->last_frame_used; + int age = frameLastFramebufUsed - (*iter)->last_frame_used; if (age > FBO_OLD_AGE) { textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb); INFO_LOG(HLE, "Decimating FBO for %08x (%i x %i x %i), age %i", vfb->fb_address, vfb->width, vfb->height, vfb->format, age) @@ -664,8 +688,10 @@ void FramebufferManager::DecimateFBOs() { fbo_destroy(vfb->fbo); vfb->fbo = 0; } + NotifyDestroyed(vfb); delete vfb; - vfbs_.erase(iter++); + vfbs_.erase(iter); + goto retry; } else ++iter; @@ -687,6 +713,7 @@ void FramebufferManager::DestroyAllFBOs() { fbo_destroy(vfb->fbo); vfb->fbo = 0; } + NotifyDestroyed(vfb); delete vfb; } vfbs_.clear(); diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 8abe456b7d..ba4a279530 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -124,6 +124,8 @@ public: return displayFramebuf_ ? (0x04000000 | displayFramebuf_->fb_address) : 0; } + void NotifyDestroyed(VirtualFramebuffer *vfb); + private: u32 ramDisplayFramebufPtr_; // workaround for MotoGP insanity u32 displayFramebufPtr_; @@ -135,7 +137,7 @@ private: VirtualFramebuffer *prevPrevDisplayFramebuf_; int frameLastFramebufUsed; - std::list vfbs_; + std::vector vfbs_; VirtualFramebuffer *currentRenderVfb_; From fcd9168ff9c0fcf59e465efe022845019fa67833 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 23 Jun 2013 17:10:37 +0200 Subject: [PATCH 72/80] Change vfbs_ back to a list for now. --- GPU/GLES/Framebuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index ba4a279530..6a7b21b3ec 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -137,7 +137,7 @@ private: VirtualFramebuffer *prevPrevDisplayFramebuf_; int frameLastFramebufUsed; - std::vector vfbs_; + std::list vfbs_; VirtualFramebuffer *currentRenderVfb_; From 5d1fff328fb33ce10dd64bdfa0e20923fa44b1d2 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 23 Jun 2013 17:17:07 +0200 Subject: [PATCH 73/80] Update native with a possible fix for task switching on android --- native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native b/native index bd78a06015..80c85b1c60 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit bd78a060158aa7ee12713727164541be29f8355a +Subproject commit 80c85b1c604ccead734d595dd7f67656f3fd9d85 From 03cab6edf76a0f8555423c57722d2f567c2fecf5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 23 Jun 2013 08:16:22 -0700 Subject: [PATCH 74/80] Centralize destruction of FBOs, use std::vector. --- GPU/GLES/Framebuffer.cpp | 64 +++++++++++++--------------------------- GPU/GLES/Framebuffer.h | 4 +-- 2 files changed, 22 insertions(+), 46 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 1082b0517d..9a8eb1e8a5 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -324,7 +324,13 @@ void GuessDrawingSize(int &drawing_width, int &drawing_height) { drawing_height = std::min(drawing_height, 512); } -void FramebufferManager::NotifyDestroyed(VirtualFramebuffer *v) { +void FramebufferManager::DestroyFramebuf(VirtualFramebuffer *v) { + textureCache_->NotifyFramebufferDestroyed(v->fb_address, v); + if (v->fbo) { + fbo_destroy(v->fbo); + v->fbo = 0; + } + // Wipe some pointers if (currentRenderVfb_ == v) currentRenderVfb_ = 0; @@ -334,6 +340,8 @@ void FramebufferManager::NotifyDestroyed(VirtualFramebuffer *v) { prevDisplayFramebuf_ = 0; if (prevPrevDisplayFramebuf_ == v) prevPrevDisplayFramebuf_ = 0; + + delete v; } void FramebufferManager::SetRenderFrameBuffer() { @@ -388,15 +396,8 @@ void FramebufferManager::SetRenderFrameBuffer() { buffer_width = std::max((int)v->width, drawing_width); buffer_height = std::max((int)v->height, drawing_height); - textureCache_->NotifyFramebufferDestroyed(v->fb_address, v); - if (v->fbo) { - fbo_destroy(v->fbo); - v->fbo = 0; - } - NotifyDestroyed(v); - - delete v; - vfbs_.erase(iter++); + DestroyFramebuf(v); + vfbs_.erase(iter--); break; } } @@ -673,28 +674,17 @@ std::vector FramebufferManager::GetFramebufferList() { void FramebufferManager::DecimateFBOs() { fbo_unbind(); currentRenderVfb_ = 0; - retry: - for (auto iter = vfbs_.begin(); iter != vfbs_.end();) { + for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { VirtualFramebuffer *vfb = *iter; if (vfb == displayFramebuf_ || vfb == prevDisplayFramebuf_ || vfb == prevPrevDisplayFramebuf_) { - ++iter; continue; } - int age = frameLastFramebufUsed - (*iter)->last_frame_used; + int age = frameLastFramebufUsed - (*iter)->last_frame_used; if (age > FBO_OLD_AGE) { - textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb); INFO_LOG(HLE, "Decimating FBO for %08x (%i x %i x %i), age %i", vfb->fb_address, vfb->width, vfb->height, vfb->format, age) - if (vfb->fbo) { - fbo_destroy(vfb->fbo); - vfb->fbo = 0; - } - NotifyDestroyed(vfb); - delete vfb; - vfbs_.erase(iter); - goto retry; + DestroyFramebuf(vfb); + vfbs_.erase(iter--); } - else - ++iter; } } @@ -707,14 +697,8 @@ void FramebufferManager::DestroyAllFBOs() { for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { VirtualFramebuffer *vfb = *iter; - textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb); - if (vfb->fbo) { - INFO_LOG(HLE, "Destroying FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format); - fbo_destroy(vfb->fbo); - vfb->fbo = 0; - } - NotifyDestroyed(vfb); - delete vfb; + INFO_LOG(HLE, "Destroying FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format); + DestroyFramebuf(vfb); } vfbs_.clear(); } @@ -733,7 +717,7 @@ void FramebufferManager::UpdateFromMemory(u32 addr, int size) { currentRenderVfb_ = 0; bool needUnbind = false; - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ) { + for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { VirtualFramebuffer *vfb = *iter; if (MaskedEqual(vfb->fb_address, addr)) { // TODO: This without the fbo_unbind() above would be better than destroying the FBO. @@ -742,20 +726,12 @@ void FramebufferManager::UpdateFromMemory(u32 addr, int size) { fbo_bind_as_render_target(vfb->fbo); needUnbind = true; DrawPixels(Memory::GetPointer(addr), vfb->format, vfb->fb_stride); - ++iter; } else { - textureCache_->NotifyFramebufferDestroyed(vfb->fb_address, vfb); INFO_LOG(HLE, "Invalidating FBO for %08x (%i x %i x %i)", vfb->fb_address, vfb->width, vfb->height, vfb->format) - if (vfb->fbo) { - fbo_destroy(vfb->fbo); - vfb->fbo = 0; - } - delete vfb; - vfbs_.erase(iter++); + DestroyFramebuf(vfb); + vfbs_.erase(iter--); } } - else - ++iter; } if (needUnbind) diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 6a7b21b3ec..8142e1949d 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -124,7 +124,7 @@ public: return displayFramebuf_ ? (0x04000000 | displayFramebuf_->fb_address) : 0; } - void NotifyDestroyed(VirtualFramebuffer *vfb); + void DestroyFramebuf(VirtualFramebuffer *vfb); private: u32 ramDisplayFramebufPtr_; // workaround for MotoGP insanity @@ -137,7 +137,7 @@ private: VirtualFramebuffer *prevPrevDisplayFramebuf_; int frameLastFramebufUsed; - std::list vfbs_; + std::vector vfbs_; VirtualFramebuffer *currentRenderVfb_; From 8c7dfdb0f7dcaf04507336bb3007902b3712b3cb Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 23 Jun 2013 08:51:35 -0700 Subject: [PATCH 75/80] Iterate over vfbs_ using size_t not iter. This way we know for sure that our erase()s are safe. --- GPU/GLES/Framebuffer.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 9a8eb1e8a5..9cabd91c87 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -269,8 +269,8 @@ void FramebufferManager::DrawActiveTexture(float x, float y, float w, float h, b VirtualFramebuffer *FramebufferManager::GetDisplayFBO() { VirtualFramebuffer *match = NULL; - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { - VirtualFramebuffer *v = *iter; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *v = vfbs_[i]; if (MaskedEqual(v->fb_address, displayFramebufPtr_) && v->format == displayFormat_ && v->width >= 480) { // Could check w too but whatever if (match == NULL || match->last_frame_used < v->last_frame_used) { @@ -285,9 +285,9 @@ VirtualFramebuffer *FramebufferManager::GetDisplayFBO() { DEBUG_LOG(HLE, "Finding no FBO matching address %08x", displayFramebufPtr_); #if 0 // defined(_DEBUG) std::string debug = "FBOs: "; - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { + for (size_t i = 0; i < vfbs_.size(); ++i) { char temp[256]; - sprintf(temp, "%08x %i %i", (*iter)->fb_address, (*iter)->width, (*iter)->height); + sprintf(temp, "%08x %i %i", vfbs_[i]->fb_address, vfbs_[i]->width, vfbs_[i]->height); debug += std::string(temp); } ERROR_LOG(HLE, "FBOs: %s", debug.c_str()); @@ -375,8 +375,8 @@ void FramebufferManager::SetRenderFrameBuffer() { // Find a matching framebuffer, same size or bigger VirtualFramebuffer *vfb = 0; - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { - VirtualFramebuffer *v = *iter; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *v = vfbs_[i]; if (MaskedEqual(v->fb_address, fb_address) && v->format == fmt) { // Okay, let's check the sizes. If the new one is bigger than the old one, recreate. // If the opposite, just use it and hope that the game sets scissors accordingly. @@ -397,7 +397,7 @@ void FramebufferManager::SetRenderFrameBuffer() { buffer_height = std::max((int)v->height, drawing_height); DestroyFramebuf(v); - vfbs_.erase(iter--); + vfbs_.erase(vfbs_.begin() + i--); break; } } @@ -655,8 +655,8 @@ void FramebufferManager::SetDisplayFramebuffer(u32 framebuf, u32 stride, int for std::vector FramebufferManager::GetFramebufferList() { std::vector list; - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { - VirtualFramebuffer *vfb = *iter; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; FramebufferInfo info; info.fb_address = vfb->fb_address; @@ -674,16 +674,16 @@ std::vector FramebufferManager::GetFramebufferList() { void FramebufferManager::DecimateFBOs() { fbo_unbind(); currentRenderVfb_ = 0; - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { - VirtualFramebuffer *vfb = *iter; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; if (vfb == displayFramebuf_ || vfb == prevDisplayFramebuf_ || vfb == prevPrevDisplayFramebuf_) { continue; } - int age = frameLastFramebufUsed - (*iter)->last_frame_used; + int age = frameLastFramebufUsed - vfb->last_frame_used; if (age > FBO_OLD_AGE) { INFO_LOG(HLE, "Decimating FBO for %08x (%i x %i x %i), age %i", vfb->fb_address, vfb->width, vfb->height, vfb->format, age) DestroyFramebuf(vfb); - vfbs_.erase(iter--); + vfbs_.erase(vfbs_.begin() + i--); } } } @@ -695,8 +695,8 @@ void FramebufferManager::DestroyAllFBOs() { prevDisplayFramebuf_ = 0; prevPrevDisplayFramebuf_ = 0; - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { - VirtualFramebuffer *vfb = *iter; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; INFO_LOG(HLE, "Destroying FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format); DestroyFramebuf(vfb); } @@ -717,8 +717,8 @@ void FramebufferManager::UpdateFromMemory(u32 addr, int size) { currentRenderVfb_ = 0; bool needUnbind = false; - for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) { - VirtualFramebuffer *vfb = *iter; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; if (MaskedEqual(vfb->fb_address, addr)) { // TODO: This without the fbo_unbind() above would be better than destroying the FBO. // However, it doesn't seem to work for Star Ocean, at least @@ -729,7 +729,7 @@ void FramebufferManager::UpdateFromMemory(u32 addr, int size) { } else { INFO_LOG(HLE, "Invalidating FBO for %08x (%i x %i x %i)", vfb->fb_address, vfb->width, vfb->height, vfb->format) DestroyFramebuf(vfb); - vfbs_.erase(iter--); + vfbs_.erase(vfbs_.begin() + i--); } } } From 3216410ac8ae608f536e8759342e172e11500c68 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 23 Jun 2013 17:57:24 +0200 Subject: [PATCH 76/80] Fix unknown.png issues properly this time :P --- UI/GameInfoCache.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index 2046e6e2ad..562dd99b4a 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -184,6 +184,7 @@ public: } else { // Read standard icon size_t sz; + INFO_LOG(HLE, "Loading unknown.png because a PBP was missing an icon"); uint8_t *contents = VFSReadFile("unknown.png", &sz); if (contents) { lock_guard lock(info_->lock); @@ -210,8 +211,9 @@ public: { // Read standard icon size_t sz; - uint8_t *contents = VFSReadFile("assets/unknown.png", &sz); - { + uint8_t *contents = VFSReadFile("unknown.png", &sz); + INFO_LOG(HLE, "Loading unknown.png because there was an ELF"); + if (contents) { lock_guard lock(info_->lock); info_->iconTextureData = std::string((const char *)contents, sz); } From 30ed5110e59b9a9df946aff845b9ccfccb8c96b1 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 23 Jun 2013 18:01:11 +0200 Subject: [PATCH 77/80] Possible iOS build fix, see #2418 --- Core/HLE/sceMp3.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 83abca6e11..60377e0ea2 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -24,8 +24,9 @@ #ifdef USE_FFMPEG #ifndef PRId64 -#define PRId64 "%llu" +#define PRId64 "%llu" #endif + // Urgh! Why is this needed? #ifdef ANDROID #ifndef UINT64_C @@ -35,7 +36,7 @@ extern "C" { #include #include -#include +//#include // iOS build is not happy with this one. #include #include } @@ -128,8 +129,10 @@ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { Memory::Write_U32(ctx->mp3PcmBuf, outPcmPtr); #else - AVFrame frame = {0}; - AVPacket packet = {0}; + AVFrame frame; + memset(&frame, 0, sizeof(frame)); + AVPacket packet; + memset(&packet, 0, sizeof(packet)); int got_frame = 0, ret; static int audio_frame_count = 0; @@ -146,9 +149,9 @@ int sceMp3Decode(u32 mp3, u32 outPcmPtr) { continue; } if (got_frame) { - char buf[1024] = ""; - av_ts_make_time_string(buf, frame.pts, &ctx->decoder_context->time_base); - DEBUG_LOG(HLE, "audio_frame n:%d nb_samples:%d pts:%s", audio_frame_count++, frame.nb_samples, buf); + //char buf[1024] = ""; + //av_ts_make_time_string(buf, frame.pts, &ctx->decoder_context->time_base); + //DEBUG_LOG(HLE, "audio_frame n:%d nb_samples:%d pts:%s", audio_frame_count++, frame.nb_samples, buf); /* u8 *audio_dst_data; From 1a46843996d6e931487916ac37303b09bed6d9d6 Mon Sep 17 00:00:00 2001 From: raven02 Date: Mon, 24 Jun 2013 01:01:55 +0900 Subject: [PATCH 78/80] Add text shadow to message dialog that match real PSP --- Core/Dialog/PSPMsgDialog.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/Dialog/PSPMsgDialog.cpp b/Core/Dialog/PSPMsgDialog.cpp index 8191389202..70ea0362cc 100755 --- a/Core/Dialog/PSPMsgDialog.cpp +++ b/Core/Dialog/PSPMsgDialog.cpp @@ -23,7 +23,7 @@ #include "ChunkFile.h" #include "i18n/i18n.h" -const float FONT_SCALE = 0.50f; +const float FONT_SCALE = 0.55f; PSPMsgDialog::PSPMsgDialog() : PSPDialog() @@ -173,6 +173,7 @@ void PSPMsgDialog::DisplayMessage(std::string text, bool hasYesNo) } } PPGeDrawTextWrapped(text.c_str(), 240.0f, y, WRAP_WIDTH, PPGE_ALIGN_CENTER, FONT_SCALE, CalcFadedColor(0xFFFFFFFF)); + PPGeDrawTextWrapped(text.c_str(), 241.0f, y+3, WRAP_WIDTH, PPGE_ALIGN_CENTER, FONT_SCALE, 0x20000000); float sy = 122.0f - h2, ey = 150.0f + h2; PPGeDrawRect(15.0f, sy, 465.0f, sy + 1.0f, CalcFadedColor(0xFFFFFFFF)); PPGeDrawRect(15.0f, ey, 465.0f, ey + 1.0f, CalcFadedColor(0xFFFFFFFF)); From 9a934aba9ea8b4ba517692bf37831cf07e3c4fc5 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 23 Jun 2013 19:48:26 +0200 Subject: [PATCH 79/80] Update lang --- lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang b/lang index 81dc0b9141..b05f3f8cac 160000 --- a/lang +++ b/lang @@ -1 +1 @@ -Subproject commit 81dc0b9141e7d1445efc7b152b88ab16ff75c737 +Subproject commit b05f3f8cac2f7097cac0d5b2d2526614bfda7a81 From 8310d27d7f9294e1838847022743788d74f6e176 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sun, 23 Jun 2013 20:10:00 +0200 Subject: [PATCH 80/80] Bump version numbers --- Windows/ppsspp.rc | 6 +++--- android/AndroidManifest.xml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 277d7a53c2..6b5528c9de 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -103,13 +103,13 @@ BEGIN VALUE "Comments", "PPSSPP PSP emulator" VALUE "CompanyName", "Henrik Rydgård Inc." VALUE "FileDescription", "PPSSPP" - VALUE "FileVersion", "1, 0, 5, 0" + VALUE "FileVersion", "1, 0, 8, 0" VALUE "InternalName", "s" VALUE "LegalCopyright", "Copyright (C) 2006-2013 by Henrik Rydgård" VALUE "LegalTrademarks", "All product names are trademarks of their respective owners." VALUE "OriginalFilename", "PPSSPP.exe" VALUE "ProductName", "PPSSPP" - VALUE "ProductVersion", "1, 0, 5, 0" + VALUE "ProductVersion", "1, 0, 8, 0" END END BLOCK "VarFileInfo" @@ -167,7 +167,7 @@ FONT 8, "MS Shell Dlg", 0, 0, 0x0 BEGIN DEFPUSHBUTTON "OK",IDOK,243,140,50,14 ICON IDI_PPSSPP,IDC_STATIC,10,9,21,20 - LTEXT "PPSSPP v0.7.0",IDC_VERSION,40,8,127,9 + LTEXT "PPSSPP v0.8",IDC_VERSION,40,8,127,9 LTEXT "Copyright (c) by Henrik Rydgard && the PPSSPP project 2012-",IDC_STATIC,40,33,253,8 LTEXT "All trademarks are property of their respective owners.\nThe emulator is for educational and development purposes only and it may not be used to play games you do not legally own.",IDC_STATIC,40,102,253,24 LTEXT "PSP emulator and debugger",IDC_STATIC,40,19,253,8 diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 6637a86515..bf49832017 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,8 +1,8 @@