all repos — mgba @ 9567a8632f4a3804051866ec4174feb3460d0cdf

mGBA Game Boy Advance Emulator

Merge branch 'master' into medusa
Vicki Pfau vi@endrift.com
Tue, 04 Apr 2017 02:13:31 -0700
commit

9567a8632f4a3804051866ec4174feb3460d0cdf

parent

e60013ec9301efc65a7b71f9f785cd71e70b331a

M CHANGESCHANGES

@@ -34,6 +34,10 @@ - Util: Fix highest-fd socket not being returned by SocketAccept

- Qt: Fix linking after some windows have been closed - GBA Video: Fix wrong palette on 256-color sprites in OBJWIN - Windows: Fix VDir.rewind + - SDL: Fix game crash check + - SDL: Fix race condition with audio thread when starting + - GB: Fix flickering when screen is strobed quickly + - FFmpeg: Fix overflow and general issues with audio encoding Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers

@@ -80,6 +84,8 @@ - GBA: Ignore invalid opcodes used by the Wii U VC emulator

- Qt: Remove audio thread - Qt: Remove audio buffer sizing in AudioProcessorQt - Qt: Re-enable QtMultimedia on Windows + - FFmpeg: Return false if a file fails to open + - FFmpeg: Force MP4 files to YUV420P 0.5.2: (2016-12-31) Bugfixes:
M include/mgba/internal/gb/video.hinclude/mgba/internal/gb/video.h

@@ -119,6 +119,7 @@ bool bcpIncrement;

int ocpIndex; bool ocpIncrement; + uint16_t dmgPalette[4]; uint16_t palette[64]; int32_t frameCounter;

@@ -137,6 +138,8 @@ void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value);

void GBVideoWriteLYC(struct GBVideo* video, uint8_t value); void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value); void GBVideoSwitchBank(struct GBVideo* video, uint8_t value); + +void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color); struct GBSerializedState; void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state);
M src/debugger/gdb-stub.csrc/debugger/gdb-stub.c

@@ -321,11 +321,29 @@ struct ARMCore* cpu = stub->d.core->cpu;

UNUSED(message); int r; int i = 0; + + // General purpose registers for (r = 0; r < ARM_PC; ++r) { _int2hex32(cpu->gprs[r], &stub->outgoing[i]); i += 8; } + + // Program counter _int2hex32(cpu->gprs[ARM_PC] - (cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM), &stub->outgoing[i]); + i += 8; + + // Floating point registers, unused on the GBA (8 of them, 24 bits each) + for (r = 0; r < 8 * 3; ++r) { + _int2hex32(0, &stub->outgoing[i]); + i += 8; + } + + // Floating point status, unused on the GBA (32 bits) + _int2hex32(0, &stub->outgoing[i]); + i += 8; + + // CPU status + _int2hex32(cpu->cpsr.packed, &stub->outgoing[i]); i += 8; stub->outgoing[i] = 0;
M src/feature/ffmpeg/ffmpeg-encoder.csrc/feature/ffmpeg/ffmpeg-encoder.c

@@ -304,6 +304,14 @@ av_opt_set(encoder->video->priv_data, "preset", "faster", 0);

} av_opt_set(encoder->video->priv_data, "tune", "zerolatency", 0); } + + if (encoder->video->codec->id == AV_CODEC_ID_H264 && + (strcasecmp(encoder->containerFormat, "mp4") || + strcasecmp(encoder->containerFormat, "m4v") || + strcasecmp(encoder->containerFormat, "mov"))) { + // QuickTime and a few other things require YUV420 + encoder->video->pix_fmt = AV_PIX_FMT_YUV420P; + } avcodec_open2(encoder->video, vcodec, 0); #if LIBAVCODEC_VERSION_MAJOR >= 55 encoder->videoFrame = av_frame_alloc();

@@ -320,7 +328,9 @@ #ifdef FFMPEG_USE_CODECPAR

avcodec_parameters_from_context(encoder->videoStream->codecpar, encoder->video); #endif - avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE); + if (avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE) < 0) { + return false; + } return avformat_write_header(encoder->context, 0) >= 0; }

@@ -389,27 +399,27 @@

encoder->audioBuffer[encoder->currentAudioSample * 2] = left; encoder->audioBuffer[encoder->currentAudioSample * 2 + 1] = right; - ++encoder->currentAudioFrame; ++encoder->currentAudioSample; - if ((encoder->currentAudioSample * 4) < encoder->audioBufferSize) { + if (encoder->currentAudioSample * 4 < encoder->audioBufferSize) { return; } int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt); - avresample_convert(encoder->resampleContext, - 0, 0, 0, - (uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4); + avresample_convert(encoder->resampleContext, 0, 0, 0, + (uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4); + + encoder->currentAudioSample = 0; if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) { return; } #if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_make_writable(encoder->audioFrame); #endif - avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize); + int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize); - encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame - encoder->currentAudioSample, encoder->audio->time_base, encoder->audioStream->time_base); - encoder->currentAudioSample = 0; + encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame, encoder->audio->time_base, encoder->audioStream->time_base); + encoder->currentAudioFrame += samples; AVPacket packet; av_init_packet(&packet);
M src/gb/core.csrc/gb/core.c

@@ -114,6 +114,21 @@ } else {

gb->audio.masterVolume = core->opts.volume; } gb->video.frameskip = core->opts.frameskip; + + int color; + if (mCoreConfigGetIntValue(&core->config, "gb.pal[0]", &color)) { + GBVideoSetPalette(&gb->video, 0, color); + } + if (mCoreConfigGetIntValue(&core->config, "gb.pal[1]", &color)) { + GBVideoSetPalette(&gb->video, 1, color); + } + if (mCoreConfigGetIntValue(&core->config, "gb.pal[2]", &color)) { + GBVideoSetPalette(&gb->video, 2, color); + } + if (mCoreConfigGetIntValue(&core->config, "gb.pal[3]", &color)) { + GBVideoSetPalette(&gb->video, 3, color); + } + mCoreConfigCopyValue(&core->config, config, "gb.bios"); mCoreConfigCopyValue(&core->config, config, "gbc.bios");
M src/gb/renderers/software.csrc/gb/renderers/software.c

@@ -24,26 +24,15 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int sx, int sy);

static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y); static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) { - // TODO: Dynamic from dmgPalette -#ifdef COLOR_16_BIT -#ifdef COLOR_5_6_5 - color_t palette0 = 0xFFDF; -#else - color_t palette0 = 0x7FFF; -#endif -#else - color_t palette0 = 0xFFFFFF; -#endif - int y; for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) { color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y]; int x; for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) { - row[x + 0] = palette0; - row[x + 1] = palette0; - row[x + 2] = palette0; - row[x + 3] = palette0; + row[x + 0] = renderer->palette[0]; + row[x + 1] = renderer->palette[0]; + row[x + 2] = renderer->palette[0]; + row[x + 3] = renderer->palette[0]; } } }

@@ -85,9 +74,6 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {

struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; switch (address) { case REG_LCDC: - if (GBRegisterLCDCIsEnable(softwareRenderer->lcdc) && !GBRegisterLCDCIsEnable(value)) { - _clearScreen(softwareRenderer); - } softwareRenderer->lcdc = value; break; case REG_SCY:

@@ -196,6 +182,9 @@

if (softwareRenderer->temporaryBuffer) { mappedMemoryFree(softwareRenderer->temporaryBuffer, GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4); softwareRenderer->temporaryBuffer = 0; + } + if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) { + _clearScreen(softwareRenderer); } softwareRenderer->currentWy = 0; }
M src/gb/video.csrc/gb/video.c

@@ -61,6 +61,11 @@ video->frameEvent.context = video;

video->frameEvent.name = "GB Video Frame"; video->frameEvent.callback = _updateFrameCount; video->frameEvent.priority = 9; + + video->dmgPalette[0] = 0x7FFF; + video->dmgPalette[1] = 0x56B5; + video->dmgPalette[2] = 0x294A; + video->dmgPalette[3] = 0x0000; } void GBVideoReset(struct GBVideo* video) {

@@ -128,14 +133,6 @@ if (GBRegisterSTATIsVblankIRQ(video->stat) || GBRegisterSTATIsOAMIRQ(video->stat)) {

video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); } video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK); - - size_t c; - for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) { - struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c); - if (callbacks->videoFrameEnded) { - callbacks->videoFrameEnded(callbacks->context); - } - } } if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);

@@ -165,7 +162,6 @@ if (GBRegisterSTATIsOAMIRQ(video->stat)) {

video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } - video->renderer->finishFrame(video->renderer); if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) { video->p->memory.rotation->sample(video->p->memory.rotation); }

@@ -231,9 +227,18 @@ mTimingSchedule(timing, &video->frameEvent, 4 - ((video->p->cpu->executionState + 1) & 3));

return; } + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c); + if (callbacks->videoFrameEnded) { + callbacks->videoFrameEnded(callbacks->context); + } + } + GBFrameEnded(video->p); --video->frameskipCounter; if (video->frameskipCounter < 0) { + video->renderer->finishFrame(video->renderer); mCoreSyncPostFrame(video->p->sync); video->frameskipCounter = video->frameskip; }

@@ -247,7 +252,6 @@ video->renderer->getPixels(video->renderer, &stride, (const void**) &pixels);

video->p->stream->postVideoFrame(video->p->stream, pixels, stride); } - size_t c; for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) { struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c); if (callbacks->videoFrameStarted) {

@@ -319,6 +323,8 @@ video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);

GBUpdateIRQs(video->p); } video->p->memory.io[REG_STAT] = video->stat; + video->renderer->writePalette(video->renderer, 0, video->palette[0]); + mTimingDeschedule(&video->p->timing, &video->frameEvent); } if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {

@@ -327,6 +333,8 @@ video->stat = GBRegisterSTATSetMode(video->stat, 0);

video->p->memory.io[REG_STAT] = video->stat; video->ly = 0; video->p->memory.io[REG_LY] = 0; + video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]); + mTimingDeschedule(&video->p->timing, &video->modeEvent); mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH); }

@@ -352,34 +360,33 @@ }

} void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value) { - static const uint16_t dmgPalette[4] = { 0x7FFF, 0x56B5, 0x294A, 0x0000}; if (video->p->model < GB_MODEL_CGB) { switch (address) { case REG_BGP: - video->palette[0] = dmgPalette[value & 3]; - video->palette[1] = dmgPalette[(value >> 2) & 3]; - video->palette[2] = dmgPalette[(value >> 4) & 3]; - video->palette[3] = dmgPalette[(value >> 6) & 3]; + video->palette[0] = video->dmgPalette[value & 3]; + video->palette[1] = video->dmgPalette[(value >> 2) & 3]; + video->palette[2] = video->dmgPalette[(value >> 4) & 3]; + video->palette[3] = video->dmgPalette[(value >> 6) & 3]; video->renderer->writePalette(video->renderer, 0, video->palette[0]); video->renderer->writePalette(video->renderer, 1, video->palette[1]); video->renderer->writePalette(video->renderer, 2, video->palette[2]); video->renderer->writePalette(video->renderer, 3, video->palette[3]); break; case REG_OBP0: - video->palette[8 * 4 + 0] = dmgPalette[value & 3]; - video->palette[8 * 4 + 1] = dmgPalette[(value >> 2) & 3]; - video->palette[8 * 4 + 2] = dmgPalette[(value >> 4) & 3]; - video->palette[8 * 4 + 3] = dmgPalette[(value >> 6) & 3]; + video->palette[8 * 4 + 0] = video->dmgPalette[value & 3]; + video->palette[8 * 4 + 1] = video->dmgPalette[(value >> 2) & 3]; + video->palette[8 * 4 + 2] = video->dmgPalette[(value >> 4) & 3]; + video->palette[8 * 4 + 3] = video->dmgPalette[(value >> 6) & 3]; video->renderer->writePalette(video->renderer, 8 * 4 + 0, video->palette[8 * 4 + 0]); video->renderer->writePalette(video->renderer, 8 * 4 + 1, video->palette[8 * 4 + 1]); video->renderer->writePalette(video->renderer, 8 * 4 + 2, video->palette[8 * 4 + 2]); video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]); break; case REG_OBP1: - video->palette[9 * 4 + 0] = dmgPalette[value & 3]; - video->palette[9 * 4 + 1] = dmgPalette[(value >> 2) & 3]; - video->palette[9 * 4 + 2] = dmgPalette[(value >> 4) & 3]; - video->palette[9 * 4 + 3] = dmgPalette[(value >> 6) & 3]; + video->palette[9 * 4 + 0] = video->dmgPalette[value & 3]; + video->palette[9 * 4 + 1] = video->dmgPalette[(value >> 2) & 3]; + video->palette[9 * 4 + 2] = video->dmgPalette[(value >> 4) & 3]; + video->palette[9 * 4 + 3] = video->dmgPalette[(value >> 6) & 3]; video->renderer->writePalette(video->renderer, 9 * 4 + 0, video->palette[9 * 4 + 0]); video->renderer->writePalette(video->renderer, 9 * 4 + 1, video->palette[9 * 4 + 1]); video->renderer->writePalette(video->renderer, 9 * 4 + 2, video->palette[9 * 4 + 2]);

@@ -430,6 +437,13 @@ void GBVideoSwitchBank(struct GBVideo* video, uint8_t value) {

value &= 1; video->vramBank = &video->vram[value * GB_SIZE_VRAM_BANK0]; video->vramCurrentBank = value; +} + +void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color) { + if (index >= 4) { + return; + } + video->dmgPalette[index] = color; } static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
M src/gba/memory.csrc/gba/memory.c

@@ -1548,6 +1548,9 @@ }

void* newRom = anonymousMemoryMap(SIZE_CART0); memcpy(newRom, gba->memory.rom, gba->memory.romSize); memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize); + if (gba->cpu->memory.activeRegion == gba->memory.rom) { + gba->cpu->memory.activeRegion = newRom; + } if (gba->romVf) { #ifndef _3DS gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize);
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -22,7 +22,7 @@ endif()

if(SDL2_FOUND) link_directories(${SDL2_LIBDIR}) endif() - list(APPEND PLATFORM_LIBRARY ${SDL_LIBRARY} ${SDLMAIN_LIBRARY}) + list(APPEND PLATFORM_LIBRARY ${SDL_LIBRARY}) list(APPEND PLATFORM_SRC ${PLATFORM_SRC} ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-events.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-audio.c) include_directories(${SDL_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/src/platform/sdl) endif()
M src/platform/qt/DisplayGL.cppsrc/platform/qt/DisplayGL.cpp

@@ -322,15 +322,6 @@ void PainterGL::draw() {

if (m_queue.isEmpty() || !mCoreThreadIsActive(m_context)) { return; } - if (!m_delayTimer.isValid()) { - m_delayTimer.start(); - } else if (m_delayTimer.elapsed() < 16) { - QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection); - QThread::usleep(500); - return; - } else { - m_delayTimer.restart(); - } if (mCoreSyncWaitFrameStart(&m_context->sync) || !m_queue.isEmpty()) { dequeue();

@@ -339,6 +330,14 @@ m_painter.begin(m_gl->context()->device());

performDraw(); m_painter.end(); m_backend->swap(m_backend); + if (!m_delayTimer.isValid()) { + m_delayTimer.start(); + } else { + while (m_delayTimer.elapsed() < 15) { + QThread::usleep(100); + } + m_delayTimer.restart(); + } } else { mCoreSyncWaitFrameEnd(&m_context->sync); }
M src/platform/qt/GBAApp.cppsrc/platform/qt/GBAApp.cpp

@@ -163,7 +163,7 @@ pauseAll(&paused);

QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getOption("lastDirectory"), filter); continueAll(paused); if (!filename.isEmpty()) { - m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); + m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); } return filename; }

@@ -174,7 +174,7 @@ pauseAll(&paused);

QString filename = QFileDialog::getSaveFileName(owner, title, m_configController.getOption("lastDirectory"), filter); continueAll(paused); if (!filename.isEmpty()) { - m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); + m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); } return filename; }

@@ -185,21 +185,9 @@ pauseAll(&paused);

QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getOption("lastDirectory")); continueAll(paused); if (!filename.isEmpty()) { - m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); + m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); } return filename; -} - -QFileDialog* GBAApp::getOpenFileDialog(QWidget* owner, const QString& title, const QString& filter) { - FileDialog* dialog = new FileDialog(this, owner, title, filter); - dialog->setAcceptMode(QFileDialog::AcceptOpen); - return dialog; -} - -QFileDialog* GBAApp::getSaveFileDialog(QWidget* owner, const QString& title, const QString& filter) { - FileDialog* dialog = new FileDialog(this, owner, title, filter); - dialog->setAcceptMode(QFileDialog::AcceptSave); - return dialog; } QString GBAApp::dataDir() {

@@ -240,24 +228,6 @@ bool GBAApp::reloadGameDB() {

return false; } #endif - -GBAApp::FileDialog::FileDialog(GBAApp* app, QWidget* parent, const QString& caption, const QString& filter) - : QFileDialog(parent, caption, app->m_configController.getOption("lastDirectory"), filter) - , m_app(app) -{ -} - -int GBAApp::FileDialog::exec() { - QList<Window*> paused; - m_app->pauseAll(&paused); - bool didAccept = QFileDialog::exec() == QDialog::Accepted; - QStringList filenames = selectedFiles(); - if (!filenames.isEmpty()) { - m_app->m_configController.setOption("lastDirectory", QFileInfo(filenames[0]).dir().path()); - } - m_app->continueAll(paused); - return didAccept; -} #ifdef USE_SQLITE3 GameDBParser::GameDBParser(NoIntroDB* db, QObject* parent)
M src/platform/qt/GBAApp.hsrc/platform/qt/GBAApp.h

@@ -55,9 +55,6 @@ QString getOpenFileName(QWidget* owner, const QString& title, const QString& filter = QString());

QString getSaveFileName(QWidget* owner, const QString& title, const QString& filter = QString()); QString getOpenDirectoryName(QWidget* owner, const QString& title); - QFileDialog* getOpenFileDialog(QWidget* owner, const QString& title, const QString& filter = QString()); - QFileDialog* getSaveFileDialog(QWidget* owner, const QString& title, const QString& filter = QString()); - const NoIntroDB* gameDB() const { return m_db; } bool reloadGameDB();

@@ -65,16 +62,6 @@ protected:

bool event(QEvent*); private: - class FileDialog : public QFileDialog { - public: - FileDialog(GBAApp* app, QWidget* parent = nullptr, const QString& caption = QString(), - const QString& filter = QString()); - virtual int exec() override; - - private: - GBAApp* m_app; - }; - Window* newWindowInternal(); void pauseAll(QList<Window*>* paused);
M src/platform/qt/GameController.cppsrc/platform/qt/GameController.cpp

@@ -345,6 +345,8 @@ m_config = config;

if (isLoaded()) { Interrupter interrupter(this); mCoreLoadForeignConfig(m_threadContext.core, config); + m_audioSync = m_threadContext.sync.audioWait; + m_videoSync = m_threadContext.sync.videoFrameWait; m_audioProcessor->setInput(&m_threadContext); } }

@@ -407,10 +409,10 @@ void GameController::loadGame(VFile* vf, const QString& path, const QString& base) {

closeGame(); QFileInfo info(base); if (info.isDir()) { - m_fname = base + QDir::separator() + path; + m_fname = QFileInfo(base + '/' + path).canonicalFilePath(); m_fsub = QString(); } else { - m_fname = base; + m_fname = info.canonicalFilePath(); m_fsub = path; } m_vf = vf;

@@ -1112,6 +1114,17 @@ m_threadContext.sync.videoFrameWait = m_videoSync;

} m_sync = enable; } + +void GameController::setAudioSync(bool enable) { + m_audioSync = enable; + m_threadContext.sync.audioWait = enable; +} + +void GameController::setVideoSync(bool enable) { + m_videoSync = enable; + m_threadContext.sync.videoFrameWait = enable; +} + void GameController::setAVStream(mAVStream* stream) { Interrupter interrupter(this); m_stream = stream;
M src/platform/qt/GameController.hsrc/platform/qt/GameController.h

@@ -149,6 +149,8 @@ void saveBackupState();

void setTurbo(bool, bool forced = true); void setTurboSpeed(float ratio); void setSync(bool); + void setAudioSync(bool); + void setVideoSync(bool); void setAVStream(mAVStream*); void clearAVStream(); void reloadAudioDriver();
M src/platform/qt/ObjView.cppsrc/platform/qt/ObjView.cpp

@@ -244,12 +244,8 @@ #endif

void ObjView::exportObj() { GameController::Interrupter interrupter(m_controller); - QFileDialog* dialog = GBAApp::app()->getSaveFileDialog(this, tr("Export sprite"), - tr("Portable Network Graphics (*.png)")); - if (!dialog->exec()) { - return; - } - QString filename = dialog->selectedFiles()[0]; + QString filename = GBAApp::app()->getSaveFileName(this, tr("Export sprite"), + tr("Portable Network Graphics (*.png)")); VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC); if (!vf) { LOG(QT, ERROR) << tr("Failed to open output PNG file: %1").arg(filename);
M src/platform/qt/PaletteView.cppsrc/platform/qt/PaletteView.cpp

@@ -134,21 +134,16 @@ length = 512 - start;

} GameController::Interrupter interrupter(m_controller); - QFileDialog* dialog = GBAApp::app()->getSaveFileDialog(this, tr("Export palette"), - tr("Windows PAL (*.pal);;Adobe Color Table (*.act)")); - if (!dialog->exec()) { - return; - } - QString filename = dialog->selectedFiles()[0]; + QString filename = GBAApp::app()->getSaveFileName(this, tr("Export palette"), + tr("Windows PAL (*.pal);;Adobe Color Table (*.act)")); VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC); if (!vf) { LOG(QT, ERROR) << tr("Failed to open output palette file: %1").arg(filename); return; } - QString filter = dialog->selectedNameFilter(); - if (filter.contains("*.pal")) { + if (filename.endsWith(".pal", Qt::CaseInsensitive)) { exportPaletteRIFF(vf, length, &static_cast<GBA*>(m_controller->thread()->core->board)->video.palette[start]); - } else if (filter.contains("*.act")) { + } else if (filename.endsWith(".act", Qt::CaseInsensitive)) { exportPaletteACT(vf, length, &static_cast<GBA*>(m_controller->thread()->core->board)->video.palette[start]); } vf->close(vf);
M src/platform/qt/VideoView.cppsrc/platform/qt/VideoView.cpp

@@ -63,7 +63,9 @@ }

if (s_vcodecMap.empty()) { s_vcodecMap["dirac"] = "libschroedinger"; s_vcodecMap["h264"] = "libx264"; + s_vcodecMap["h264 nvenc"] = "h264_nvenc"; s_vcodecMap["hevc"] = "libx265"; + s_vcodecMap["hevc nvenc"] = "hevc_nvenc"; s_vcodecMap["theora"] = "libtheora"; s_vcodecMap["vp8"] = "libvpx"; s_vcodecMap["vp9"] = "libvpx-vp9";

@@ -458,6 +460,8 @@

QString VideoView::sanitizeCodec(const QString& codec, const QMap<QString, QString>& mapping) { QString sanitized = codec.toLower(); sanitized = sanitized.remove(QChar('.')); + sanitized = sanitized.remove(QChar('(')); + sanitized = sanitized.remove(QChar(')')); if (mapping.contains(sanitized)) { sanitized = mapping[sanitized]; }
M src/platform/qt/VideoView.uisrc/platform/qt/VideoView.ui

@@ -266,12 +266,17 @@ </property>

</item> <item> <property name="text"> - <string>VP8</string> + <string>h.264 (NVENC)</string> + </property> + </item> + <item> + <property name="text"> + <string>HEVC</string> </property> </item> <item> <property name="text"> - <string>Xvid</string> + <string>VP8</string> </property> </item> <item>
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -1192,14 +1192,14 @@

ConfigOption* videoSync = m_config->addOption("videoSync"); videoSync->addBoolean(tr("Sync to &video"), emulationMenu); videoSync->connect([this](const QVariant& value) { - reloadConfig(); + m_controller->setVideoSync(value.toBool()); }, this); m_config->updateOption("videoSync"); ConfigOption* audioSync = m_config->addOption("audioSync"); audioSync->addBoolean(tr("Sync to &audio"), emulationMenu); audioSync->connect([this](const QVariant& value) { - reloadConfig(); + m_controller->setAudioSync(value.toBool()); }, this); m_config->updateOption("audioSync");

@@ -1552,7 +1552,7 @@ }

m_mruMenu->clear(); int i = 0; for (const QString& file : m_mruFiles) { - QAction* item = new QAction(file, m_mruMenu); + QAction* item = new QAction(QDir::toNativeSeparators(file).replace("&", "&&"), m_mruMenu); item->setShortcut(QString("Ctrl+%1").arg(i)); connect(item, &QAction::triggered, [this, file]() { m_controller->loadGame(file); }); m_mruMenu->addAction(item);
M src/platform/qt/main.cppsrc/platform/qt/main.cpp

@@ -3,6 +3,10 @@ *

* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This must be defined before anything else is included. +#define SDL_MAIN_HANDLED + #include "GBAApp.h" #include "Window.h"

@@ -22,6 +26,9 @@ #endif

#endif int main(int argc, char* argv[]) { +#ifdef BUILD_SDL + SDL_SetMainReady(); +#endif QGBA::GBAApp application(argc, argv); QLocale locale = QLocale::system();
M src/platform/sdl/main.csrc/platform/sdl/main.c

@@ -185,30 +185,31 @@

renderer->audio.samples = renderer->core->opts.audioBuffers; renderer->audio.sampleRate = 44100; - bool didFail = !mSDLInitAudio(&renderer->audio, &thread); + bool didFail = !mCoreThreadStart(&thread); if (!didFail) { #if SDL_VERSION_ATLEAST(2, 0, 0) mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver); mSDLSuspendScreensaver(&renderer->events); #endif - if (mCoreThreadStart(&thread)) { + if (mSDLInitAudio(&renderer->audio, &thread)) { renderer->runloop(renderer, &thread); mSDLPauseAudio(&renderer->audio); - mCoreThreadJoin(&thread); + if (mCoreThreadHasCrashed(&thread)) { + didFail = true; + printf("The game crashed!\n"); + } } else { didFail = true; - printf("Could not run game. Are you sure the file exists and is a compatible game?\n"); + printf("Could not initialize audio.\n"); } - #if SDL_VERSION_ATLEAST(2, 0, 0) mSDLResumeScreensaver(&renderer->events); mSDLSetScreensaverSuspendable(&renderer->events, false); #endif - if (mCoreThreadHasCrashed(&thread)) { - didFail = true; - printf("The game crashed!\n"); - } + mCoreThreadJoin(&thread); + } else { + printf("Could not run game. Are you sure the file exists and is a compatible game?\n"); } renderer->core->unloadROM(renderer->core); return didFail;
M version.cmakeversion.cmake

@@ -10,11 +10,11 @@ set(SUMMARY "${PROJECT_NAME} Game Boy Advance Emulator")

find_program(GIT git) if(GIT AND NOT SKIP_GIT) - execute_process(COMMAND ${GIT} describe --always --abbrev=40 --dirty OUTPUT_VARIABLE GIT_COMMIT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT} describe --always --dirty OUTPUT_VARIABLE GIT_COMMIT_SHORT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT} symbolic-ref --short HEAD OUTPUT_VARIABLE GIT_BRANCH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT} rev-list HEAD --count OUTPUT_VARIABLE GIT_REV ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT} describe --tag --exact-match OUTPUT_VARIABLE GIT_TAG ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT} describe --always --abbrev=40 --dirty WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_COMMIT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT} describe --always --dirty WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_COMMIT_SHORT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT} symbolic-ref --short HEAD WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_BRANCH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT} rev-list HEAD --count WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_REV ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT} describe --tag --exact-match WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) endif() if(NOT GIT_REV)