diff --git a/Core/AVIDump.cpp b/Core/AVIDump.cpp new file mode 100644 index 000000000..7996fd5b8 --- /dev/null +++ b/Core/AVIDump.cpp @@ -0,0 +1,352 @@ +// Copyright 2009 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#if defined(__FreeBSD__) +#define __STDC_CONSTANT_MACROS 1 +#endif + +#include +#include + +extern "C" { +#include +#include +#include +#include +} + +#include "Common/FileUtil.h" +#include "Common/MsgHandler.h" +#include "Common/ColorConv.h" + +#include "Core/Config.h" +#include "Core/AVIDump.h" +#include "Core/System.h" +#include "Core/Screenshot.h" + +#include "GPU/Common/GPUDebugInterface.h" +#ifdef _WIN32 +#include "GPU/Directx9/GPU_DX9.h" +#endif +#include "GPU/GLES/GPU_GLES.h" + +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 28, 1) +#define av_frame_alloc avcodec_alloc_frame +#define av_frame_free avcodec_free_frame +#endif + +static AVFormatContext* s_format_context = nullptr; +static AVStream* s_stream = nullptr; +static AVFrame* s_src_frame = nullptr; +static AVFrame* s_scaled_frame = nullptr; +static int s_bytes_per_pixel; +static SwsContext* s_sws_context = nullptr; +static int s_width; +static int s_height; +static bool s_start_dumping = false; +static int s_current_width; +static int s_current_height; +static int s_file_index = 0; + +static void InitAVCodec() +{ + static bool first_run = true; + if (first_run) + { + av_register_all(); + first_run = false; + } +} + +bool AVIDump::Start(int w, int h) +{ + s_width = w; + s_height = h; + s_current_width = w; + s_current_height = h; + + InitAVCodec(); + bool success = CreateAVI(); + if (!success) + CloseFile(); + return success; +} + +bool AVIDump::CreateAVI() +{ + AVCodec* codec = nullptr; + + s_format_context = avformat_alloc_context(); + std::stringstream s_file_index_str; + s_file_index_str << s_file_index; + snprintf(s_format_context->filename, sizeof(s_format_context->filename), "%s", (GetSysDirectory(DIRECTORY_VIDEO_DUMP) + "framedump" + s_file_index_str.str() + ".avi").c_str()); + // Make sure that the path exists + if (!File::Exists(GetSysDirectory(DIRECTORY_VIDEO_DUMP))) + File::CreateDir(GetSysDirectory(DIRECTORY_VIDEO_DUMP)); + + if (File::Exists(s_format_context->filename)) + File::Delete(s_format_context->filename); + + if (!(s_format_context->oformat = av_guess_format("avi", nullptr, nullptr)) || !(s_stream = avformat_new_stream(s_format_context, codec))) + { + return false; + } + + s_stream->codec->codec_id = g_Config.bUseFFV1 ? AV_CODEC_ID_FFV1 : s_format_context->oformat->video_codec; + if (!g_Config.bUseFFV1) + s_stream->codec->codec_tag = MKTAG('X', 'V', 'I', 'D'); // Force XVID FourCC for better compatibility + s_stream->codec->codec_type = AVMEDIA_TYPE_VIDEO; + s_stream->codec->bit_rate = 400000; + s_stream->codec->width = s_width; + s_stream->codec->height = s_height; + s_stream->codec->time_base.num = 1001; + s_stream->codec->time_base.den = 60000; + s_stream->codec->gop_size = 12; + s_stream->codec->pix_fmt = g_Config.bUseFFV1 ? AV_PIX_FMT_BGRA : AV_PIX_FMT_YUV420P; + + if (!(codec = avcodec_find_encoder(s_stream->codec->codec_id)) || (avcodec_open2(s_stream->codec, codec, nullptr) < 0)) + { + return false; + } + + s_src_frame = av_frame_alloc(); + s_scaled_frame = av_frame_alloc(); + + s_scaled_frame->format = s_stream->codec->pix_fmt; + s_scaled_frame->width = s_width; + s_scaled_frame->height = s_height; + +#if LIBAVCODEC_VERSION_MAJOR >= 55 + if (av_frame_get_buffer(s_scaled_frame, 1)) + return false; +#else + if (avcodec_default_get_buffer(s_stream->codec, s_scaled_frame)) + return false; +#endif + + NOTICE_LOG(G3D, "Opening file %s for dumping", s_format_context->filename); + if (avio_open(&s_format_context->pb, s_format_context->filename, AVIO_FLAG_WRITE) < 0 || avformat_write_header(s_format_context, nullptr)) + { + WARN_LOG(G3D, "Could not open %s", s_format_context->filename); + return false; + } + + return true; +} + +static void PreparePacket(AVPacket* pkt) +{ + av_init_packet(pkt); + pkt->data = nullptr; + pkt->size = 0; +} + +static const u8 *ConvertBufferTo888RGB(const GPUDebugBuffer &buf, u8 *&temp, u32 &w, u32 &h) { + // The temp buffer will be freed by the caller if set, and can be the return value. + temp = nullptr; + + w = std::min(w, buf.GetStride()); + h = std::min(h, buf.GetHeight()); + + const u8 *buffer = buf.GetData(); + if (buf.GetFlipped() && buf.GetFormat() == GPU_DBG_FORMAT_888_RGB) { + // Silly OpenGL reads upside down, we flip to another buffer for simplicity. + temp = new u8[3 * w * h]; + for (u32 y = 0; y < h; y++) { + memcpy(temp + y * w * 3, buffer + (buf.GetHeight() - y - 1) * buf.GetStride() * 3, w * 3); + } + buffer = temp; + } + else if (buf.GetFormat() != GPU_DBG_FORMAT_888_RGB) { + // Let's boil it down to how we need to interpret the bits. + int baseFmt = buf.GetFormat() & ~(GPU_DBG_FORMAT_REVERSE_FLAG | GPU_DBG_FORMAT_BRSWAP_FLAG); + bool rev = (buf.GetFormat() & GPU_DBG_FORMAT_REVERSE_FLAG) != 0; + bool brswap = (buf.GetFormat() & GPU_DBG_FORMAT_BRSWAP_FLAG) != 0; + bool flip = buf.GetFlipped(); + + temp = new u8[3 * w * h]; + + // This is pretty inefficient. + const u16 *buf16 = (const u16 *)buffer; + const u32 *buf32 = (const u32 *)buffer; + for (u32 y = 0; y < h; y++) { + for (u32 x = 0; x < w; x++) { + u8 *dst; + if (flip) { + dst = &temp[(h - y - 1) * w * 3 + x * 3]; + } + else { + dst = &temp[y * w * 3 + x * 3]; + } + + u8 &r = brswap ? dst[2] : dst[0]; + u8 &g = dst[1]; + u8 &b = brswap ? dst[0] : dst[2]; + + u32 src; + switch (baseFmt) { + case GPU_DBG_FORMAT_565: + src = buf16[y * buf.GetStride() + x]; + if (rev) { + src = bswap16(src); + } + r = Convert5To8((src >> 0) & 0x1F); + g = Convert6To8((src >> 5) & 0x3F); + b = Convert5To8((src >> 11) & 0x1F); + break; + case GPU_DBG_FORMAT_5551: + src = buf16[y * buf.GetStride() + x]; + if (rev) { + src = bswap16(src); + } + r = Convert5To8((src >> 0) & 0x1F); + g = Convert5To8((src >> 5) & 0x1F); + b = Convert5To8((src >> 10) & 0x1F); + break; + case GPU_DBG_FORMAT_4444: + src = buf16[y * buf.GetStride() + x]; + if (rev) { + src = bswap16(src); + } + r = Convert4To8((src >> 0) & 0xF); + g = Convert4To8((src >> 4) & 0xF); + b = Convert4To8((src >> 8) & 0xF); + break; + case GPU_DBG_FORMAT_8888: + src = buf32[y * buf.GetStride() + x]; + if (rev) { + src = bswap32(src); + } + r = (src >> 0) & 0xFF; + g = (src >> 8) & 0xFF; + b = (src >> 16) & 0xFF; + break; + default: + ERROR_LOG(COMMON, "Unsupported framebuffer format for screenshot: %d", buf.GetFormat()); + return nullptr; + } + } + } + buffer = temp; + } + + return buffer; +} + +void AVIDump::AddFrame() +{ + GPUDebugBuffer buf; + gpuDebug->GetCurrentFramebuffer(buf); + u32 w = buf.GetStride(); + u32 h = buf.GetHeight(); + CheckResolution(w, h); + u8 *flipbuffer = nullptr; + const u8 *buffer = ConvertBufferTo888RGB(buf, flipbuffer, w, h); + s_src_frame->data[0] = const_cast(buffer); + s_src_frame->linesize[0] = w * 3; + s_src_frame->format = AV_PIX_FMT_RGB24; + s_src_frame->width = s_width; + s_src_frame->height = s_height; + + // Convert image from BGR24 to desired pixel format, and scale to initial + // width and height + if ((s_sws_context = + sws_getCachedContext(s_sws_context, w, h, AV_PIX_FMT_RGB24, s_width, s_height, + s_stream->codec->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr))) + { + sws_scale(s_sws_context, s_src_frame->data, s_src_frame->linesize, 0, h, + s_scaled_frame->data, s_scaled_frame->linesize); + } + + s_scaled_frame->format = s_stream->codec->pix_fmt; + s_scaled_frame->width = s_width; + s_scaled_frame->height = s_height; + + // Encode and write the image. + AVPacket pkt; + PreparePacket(&pkt); + int got_packet; + int error = avcodec_encode_video2(s_stream->codec, &pkt, s_scaled_frame, &got_packet); + while (!error && got_packet) + { + // Write the compressed frame in the media file. + if (pkt.pts != (s64)AV_NOPTS_VALUE) + { + pkt.pts = av_rescale_q(pkt.pts, s_stream->codec->time_base, s_stream->time_base); + } + if (pkt.dts != (s64)AV_NOPTS_VALUE) + { + pkt.dts = av_rescale_q(pkt.dts, s_stream->codec->time_base, s_stream->time_base); + } +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(56, 60, 100) + if (s_stream->codec->coded_frame->key_frame) + pkt.flags |= AV_PKT_FLAG_KEY; +#endif + pkt.stream_index = s_stream->index; + av_interleaved_write_frame(s_format_context, &pkt); + + // Handle delayed frames. + PreparePacket(&pkt); + error = avcodec_encode_video2(s_stream->codec, &pkt, nullptr, &got_packet); + } + if (error) + ERROR_LOG(G3D, "Error while encoding video: %d", error); +} + +void AVIDump::Stop() +{ + av_write_trailer(s_format_context); + CloseFile(); + s_file_index = 0; + NOTICE_LOG(G3D, "Stopping frame dump"); +} + +void AVIDump::CloseFile() +{ + if (s_stream) + { + if (s_stream->codec) + { +#if LIBAVCODEC_VERSION_MAJOR < 55 + avcodec_default_release_buffer(s_stream->codec, s_src_frame); +#endif + avcodec_close(s_stream->codec); + } + av_freep(&s_stream); + } + + av_frame_free(&s_src_frame); + av_frame_free(&s_scaled_frame); + + if (s_format_context) + { + if (s_format_context->pb) + avio_close(s_format_context->pb); + av_freep(&s_format_context); + } + + if (s_sws_context) + { + sws_freeContext(s_sws_context); + s_sws_context = nullptr; + } +} + +void AVIDump::CheckResolution(int width, int height) +{ + // We check here to see if the requested width and height have changed since the last frame which + // was dumped, then create a new file accordingly. However, is it possible for the height + // (possibly width as well, but no examples known) to have a value of zero. This can occur as the + // VI is able to be set to a zero value for height/width to disable output. If this is the case, + // simply keep the last known resolution of the video for the added frame. + if ((width != s_current_width || height != s_current_height) && (width > 0 && height > 0)) + { + int temp_file_index = s_file_index; + Stop(); + s_file_index = temp_file_index + 1; + Start(width, height); + s_current_width = width; + s_current_height = height; + } +} diff --git a/Core/AVIDump.h b/Core/AVIDump.h new file mode 100644 index 000000000..5288d25cd --- /dev/null +++ b/Core/AVIDump.h @@ -0,0 +1,20 @@ +// Copyright 2008 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" + +class AVIDump +{ +private: + static bool CreateAVI(); + static void CloseFile(); + static void CheckResolution(int width, int height); + +public: + static bool Start(int w, int h); + static void AddFrame(); + static void Stop(); +}; \ No newline at end of file diff --git a/Core/Config.cpp b/Core/Config.cpp index e38a04c2d..670c82377 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -40,7 +40,7 @@ #include "HLE/sceUtility.h" #ifndef USING_QT_UI -extern const char *PPSSPP_GIT_VERSION; +extern const char *PPSSPP_GIT_VERSION; #endif // TODO: Find a better place for this. @@ -327,6 +327,8 @@ static ConfigSetting generalSettings[] = { ConfigSetting("CwCheatRefreshRate", &g_Config.iCwCheatRefreshRate, 77, true, true), ConfigSetting("ScreenshotsAsPNG", &g_Config.bScreenshotsAsPNG, false, true, true), + ConfigSetting("UseFFV1", &g_Config.bUseFFV1, false), + ConfigSetting("DumpFrames", &g_Config.bDumpFrames, false), ConfigSetting("StateSlot", &g_Config.iCurrentStateSlot, 0, true, true), ConfigSetting("RewindFlipFrequency", &g_Config.iRewindFlipFrequency, 0, true, true), @@ -926,7 +928,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { fcombo4X /= screen_width; fcombo4Y /= screen_height; } - + const char *gitVer = PPSSPP_GIT_VERSION; Version installed(gitVer); Version upgrade(upgradeVersion); @@ -955,7 +957,7 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { bSaveSettings = true; LoadStandardControllerIni(); - + //so this is all the way down here to overwrite the controller settings //sadly it won't benefit from all the "version conversion" going on up-above //but these configs shouldn't contain older versions anyhow @@ -987,7 +989,7 @@ void Config::Save() { g_Config.iCpuCore = CPU_CORE_JIT; } if (iniFilename_.size() && g_Config.bSaveSettings) { - + saveGameConfig(gameId_); CleanRecent(); diff --git a/Core/Config.h b/Core/Config.h index 119acebea..5f3c5dec2 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -104,6 +104,8 @@ public: // General int iNumWorkerThreads; bool bScreenshotsAsPNG; + bool bUseFFV1; + bool bDumpFrames; bool bEnableLogging; bool bDumpDecryptedEboot; bool bFullscreenOnDoubleclick; @@ -235,7 +237,7 @@ public: //considers this orientation to be equal to no movement of the analog stick. float fTiltBaseX, fTiltBaseY; //whether the x axes and y axes should invert directions (left becomes right, top becomes bottom.) - bool bInvertTiltX, bInvertTiltY; + bool bInvertTiltX, bInvertTiltY; //the sensitivity of the tilt in the x direction int iTiltSensitivityX; //the sensitivity of the tilt in the Y direction @@ -330,7 +332,7 @@ public: bool bShowComboKey2; bool bShowComboKey3; bool bShowComboKey4; - + // Combo_key mapping. These are bitfields. int iCombokey0; int iCombokey1; @@ -359,9 +361,9 @@ public: // proper options when good enough. // PrescaleUV: // * Applies UV scale/offset when decoding verts. Get rid of some work in the vertex shader, - // saves a uniform upload and is a prerequisite for future optimized hybrid + // saves a uniform upload and is a prerequisite for future optimized hybrid // (SW skinning, HW transform) skinning. - // * Still has major problems so off by default - need to store tex scale/offset per DeferredDrawCall, + // * Still has major problems so off by default - need to store tex scale/offset per DeferredDrawCall, // which currently isn't done so if texscale/offset isn't static (like in Tekken 6) things go wrong. bool bPrescaleUV; bool bDisableAlphaTest; // Helps PowerVR immensely, breaks some graphics @@ -424,7 +426,7 @@ public: bool bShowFrameProfiler; std::string currentDirectory; - std::string externalDirectory; + std::string externalDirectory; std::string memStickDirectory; std::string flash0Directory; std::string internalDataDirectory; @@ -438,7 +440,7 @@ public: void Load(const char *iniFileName = nullptr, const char *controllerIniFilename = nullptr); void Save(); void RestoreDefaults(); - + //per game config managment, should maybe be in it's own class void changeGameSpecific(const std::string &gameId = ""); bool createGameConfig(const std::string &game_id); @@ -472,7 +474,7 @@ public: protected: void LoadStandardControllerIni(); - + private: std::string gameId_; std::string iniFilename_; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 60d9c7c66..c2351f035 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -181,6 +181,7 @@ + @@ -519,6 +520,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 600070d1e..a77ebd3d1 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -673,6 +673,9 @@ MIPS\IR + + Core + @@ -1236,6 +1239,9 @@ MIPS\IR + + Core + diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 887e420dd..951498ef6 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -240,6 +240,11 @@ u64 GetTicks() return (u64)globalTimer + slicelength - currentMIPS->downcount; } +u64 GetTicksPerSecond() +{ + return CPU_HZ; +} + u64 GetIdleTicks() { return (u64)idledCycles; @@ -308,7 +313,7 @@ void AddEventToQueue(Event* ne) // This must be run ONLY from within the cpu thread // cyclesIntoFuture may be VERY inaccurate if called from anything else -// than Advance +// than Advance void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata) { Event *ne = GetNewEvent(); @@ -418,7 +423,7 @@ void RegisterMHzChangeCallback(MHzChangeCallback callback) { mhzChangeCallbacks.push_back(callback); } -bool IsScheduled(int event_type) +bool IsScheduled(int event_type) { if (!first) return false; @@ -498,7 +503,7 @@ void RemoveThreadsafeEvent(int event_type) while (ptr) { if (ptr->type == event_type) - { + { prev->next = ptr->next; if (ptr == tsLast) tsLast = prev; @@ -526,7 +531,7 @@ void ProcessFifoWaitEvents() { if (first->time <= (s64)GetTicks()) { -// LOG(TIMER, "[Scheduler] %s (%lld, %lld) ", +// LOG(TIMER, "[Scheduler] %s (%lld, %lld) ", // first->name ? first->name : "?", (u64)GetTicks(), (u64)first->time); Event* evt = first; first = first->next; diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index ee7a26a71..82f28aa8f 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -82,6 +82,7 @@ namespace CoreTiming u64 GetIdleTicks(); u64 GetGlobalTimeUs(); u64 GetGlobalTimeUsScaled(); + u64 GetTicksPerSecond(); // Returns the event_type identifier. int RegisterEvent(const char *name, TimedCallback callback); diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 74a698b58..f949426e2 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -25,6 +25,7 @@ #include "Common/FileUtil.h" #include "Common/ChunkFile.h" +#include "Core/AVIDump.h" #include "Core/SaveState.h" #include "Core/Config.h" #include "Core/Core.h" diff --git a/Core/System.cpp b/Core/System.cpp index 5a7854159..7f03be126 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -182,7 +182,7 @@ void CPU_Shutdown(); void CPU_Init() { coreState = CORE_POWERUP; currentMIPS = &mipsr4k; - + g_symbolMap = new SymbolMap(); // Default memory settings @@ -386,7 +386,7 @@ bool PSP_InitStart(const CoreParameter &coreParam, std::string *error_string) { #else INFO_LOG(BOOT, "PPSSPP %s", PPSSPP_GIT_VERSION); #endif - + GraphicsContext *temp = coreParameter.graphicsContext; coreParameter = coreParam; if (coreParameter.graphicsContext == nullptr) { @@ -593,6 +593,8 @@ std::string GetSysDirectory(PSPDirectories directoryType) { return g_Config.appCacheDirectory; } return g_Config.memStickDirectory + "PSP/SYSTEM/CACHE/"; + case DIRECTORY_VIDEO_DUMP: + return g_Config.memStickDirectory + "PSP/FRAMEDUMP/"; // Just return the memory stick root if we run into some sort of problem. default: ERROR_LOG(FILESYS, "Unknown directory type."); diff --git a/Core/System.h b/Core/System.h index 7815b60ac..2aef7c21e 100644 --- a/Core/System.h +++ b/Core/System.h @@ -47,6 +47,7 @@ enum PSPDirectories { DIRECTORY_CACHE, DIRECTORY_TEXTURES, DIRECTORY_APP_CACHE, // Use the OS app cache if available + DIRECTORY_VIDEO_DUMP }; class GraphicsContext; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index 0122b9453..e1d678cbb 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -32,6 +32,7 @@ #include "Common/KeyMap.h" +#include "Core/AVIDump.h" #include "Core/Config.h" #include "Core/CoreTiming.h" #include "Core/CoreParameter.h" @@ -70,8 +71,11 @@ #include "Windows/MainWindow.h" #endif +AVIDump avi; + static bool frameStep_; static int lastNumFlips; +static bool startDumping; static void __EmuScreenVblank() { @@ -81,6 +85,21 @@ static void __EmuScreenVblank() Core_EnableStepping(true); lastNumFlips = gpuStats.numFlips; } + + if (g_Config.bDumpFrames && !startDumping) + { + avi.Start(PSP_CoreParameter().renderWidth, PSP_CoreParameter().renderHeight); + startDumping = true; + } + if (g_Config.bDumpFrames && startDumping) + { + avi.AddFrame(); + } + else if (!g_Config.bDumpFrames && startDumping) + { + avi.Stop(); + startDumping = false; + } } EmuScreen::EmuScreen(const std::string &filename) @@ -90,6 +109,7 @@ EmuScreen::EmuScreen(const std::string &filename) __DisplayListenVblank(__EmuScreenVblank); frameStep_ = false; lastNumFlips = gpuStats.numFlips; + startDumping = false; } void EmuScreen::bootGame(const std::string &filename) { @@ -227,6 +247,11 @@ EmuScreen::~EmuScreen() { // If we were invalid, it would already be shutdown. PSP_Shutdown(); } + if (g_Config.bDumpFrames && startDumping) + { + avi.Stop(); + startDumping = false; + } } void EmuScreen::dialogFinished(const Screen *dialog, DialogResult result) { diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index fea7d947e..0977717e5 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -273,7 +273,7 @@ void GameSettingsScreen::CreateViews() { static const char *quality[] = { "Low", "Medium", "High"}; PopupMultiChoice *beziersChoice = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iSplineBezierQuality, gr->T("LowCurves", "Spline/Bezier curves quality"), quality, 0, ARRAY_SIZE(quality), gr->GetName(), screenManager())); beziersChoice->SetDisabledPtr(&g_Config.bSoftwareRendering); - + // In case we're going to add few other antialiasing option like MSAA in the future. // graphicsSettings->Add(new CheckBox(&g_Config.bFXAA, gr->T("FXAA"))); graphicsSettings->Add(new ItemHeader(gr->T("Texture Scaling"))); @@ -676,6 +676,8 @@ void GameSettingsScreen::CreateViews() { #if defined(_WIN32) || (defined(USING_QT_UI) && !defined(MOBILE_DEVICE)) // Screenshot functionality is not yet available on non-Windows/non-Qt systemSettings->Add(new CheckBox(&g_Config.bScreenshotsAsPNG, sy->T("Screenshots as PNG"))); + systemSettings->Add(new CheckBox(&g_Config.bDumpFrames, sy->T("Dump Frames"))); + systemSettings->Add(new CheckBox(&g_Config.bUseFFV1, sy->T("Use FFV1 for Frame Dumps"))); #endif systemSettings->Add(new CheckBox(&g_Config.bDayLightSavings, sy->T("Day Light Saving"))); static const char *dateFormat[] = { "YYYYMMDD", "MMDDYYYY", "DDMMYYYY"}; @@ -801,7 +803,7 @@ UI::EventReturn GameSettingsScreen::OnSavePathMydoc(UI::EventParams &e) { } UI::EventReturn GameSettingsScreen::OnSavePathOther(UI::EventParams &e) { - const std::string PPSSPPpath = File::GetExeDirectory(); + const std::string PPSSPPpath = File::GetExeDirectory(); if (otherinstalled_) { I18NCategory *di = GetI18NCategory("Dialog"); std::string folder = W32Util::BrowseForFolder(MainWindow::GetHWND(), di->T("Choose PPSSPP save folder")); @@ -983,8 +985,8 @@ UI::EventReturn GameSettingsScreen::OnChangeNickname(UI::EventParams &e) { return UI::EVENT_DONE; } -UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParams &e) { -#if defined(_WIN32) || defined(USING_QT_UI) +UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParams &e) { +#if defined(_WIN32) || defined(USING_QT_UI) if (!g_Config.bFullScreen) { const size_t name_len = 256; @@ -1002,7 +1004,7 @@ UI::EventReturn GameSettingsScreen::OnChangeproAdhocServerAddress(UI::EventParam #else screenManager()->push(new ProAdhocServerScreen); #endif - + return UI::EVENT_DONE; } @@ -1246,14 +1248,14 @@ UI::EventReturn DeveloperToolsScreen::OnJitAffectingSetting(UI::EventParams &e) } void ProAdhocServerScreen::CreateViews() { - using namespace UI; + using namespace UI; I18NCategory *sy = GetI18NCategory("System"); I18NCategory *di = GetI18NCategory("Dialog"); - + tempProAdhocServer = g_Config.proAdhocServer; root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT)); LinearLayout *leftColumn = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT)); - + leftColumn->Add(new ItemHeader(sy->T("proAdhocServer Address:"))); addrView_ = new TextView(tempProAdhocServer, ALIGN_LEFT, false); leftColumn->Add(addrView_);