mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into medusa
This commit is contained in:
commit
9567a8632f
6
CHANGES
6
CHANGES
|
@ -34,6 +34,10 @@ Bugfixes:
|
|||
- 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 @@ Misc:
|
|||
- 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:
|
||||
|
|
|
@ -119,6 +119,7 @@ struct GBVideo {
|
|||
int ocpIndex;
|
||||
bool ocpIncrement;
|
||||
|
||||
uint16_t dmgPalette[4];
|
||||
uint16_t palette[64];
|
||||
|
||||
int32_t frameCounter;
|
||||
|
@ -138,6 +139,8 @@ 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);
|
||||
void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state);
|
||||
|
|
|
@ -321,13 +321,31 @@ static void _readGPRs(struct GDBStub* stub, const char* message) {
|
|||
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;
|
||||
_sendMessage(stub);
|
||||
}
|
||||
|
|
|
@ -304,6 +304,14 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
|||
}
|
||||
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 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
|||
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 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right
|
|||
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);
|
||||
|
|
|
@ -114,6 +114,21 @@ static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* conf
|
|||
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");
|
||||
|
||||
|
|
|
@ -24,26 +24,15 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
|
|||
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*
|
|||
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:
|
||||
|
@ -197,6 +183,9 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer)
|
|||
mappedMemoryFree(softwareRenderer->temporaryBuffer, GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4);
|
||||
softwareRenderer->temporaryBuffer = 0;
|
||||
}
|
||||
if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) {
|
||||
_clearScreen(softwareRenderer);
|
||||
}
|
||||
softwareRenderer->currentWy = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,11 @@ void GBVideoInit(struct GBVideo* 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 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
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 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
|||
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 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
|
|||
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 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
|
|||
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 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
|||
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 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
|||
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 GBVideoWriteLYC(struct GBVideo* video, uint8_t value) {
|
|||
}
|
||||
|
||||
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]);
|
||||
|
@ -432,6 +439,13 @@ void GBVideoSwitchBank(struct GBVideo* video, uint8_t value) {
|
|||
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) {
|
||||
UNUSED(renderer);
|
||||
UNUSED(model);
|
||||
|
|
|
@ -1548,6 +1548,9 @@ void _pristineCow(struct GBA* gba) {
|
|||
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);
|
||||
|
|
|
@ -22,7 +22,7 @@ if(BUILD_SDL)
|
|||
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()
|
||||
|
|
|
@ -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 @@ void PainterGL::draw() {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QStr
|
|||
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 @@ QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QStr
|
|||
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,23 +185,11 @@ QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title) {
|
|||
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() {
|
||||
#ifdef DATADIR
|
||||
QString path = QString::fromUtf8(DATADIR);
|
||||
|
@ -241,24 +229,6 @@ bool GBAApp::reloadGameDB() {
|
|||
}
|
||||
#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)
|
||||
: QObject(parent)
|
||||
|
|
|
@ -55,9 +55,6 @@ public:
|
|||
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);
|
||||
|
|
|
@ -345,6 +345,8 @@ void GameController::setConfig(const mCoreConfig* 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& bas
|
|||
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 @@ void GameController::setSync(bool enable) {
|
|||
}
|
||||
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;
|
||||
|
|
|
@ -149,6 +149,8 @@ public slots:
|
|||
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();
|
||||
|
|
|
@ -244,12 +244,8 @@ void ObjView::updateTilesGB(bool force) {
|
|||
|
||||
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);
|
||||
|
|
|
@ -134,21 +134,16 @@ void PaletteView::exportPalette(int start, int length) {
|
|||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -63,7 +63,9 @@ VideoView::VideoView(QWidget* parent)
|
|||
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 @@ void VideoView::uncheckIncompatible() {
|
|||
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];
|
||||
}
|
||||
|
|
|
@ -266,12 +266,17 @@
|
|||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>VP8</string>
|
||||
<string>h.264 (NVENC)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Xvid</string>
|
||||
<string>HEVC</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>VP8</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
|
|
|
@ -1192,14 +1192,14 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
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 @@ void Window::updateMRU() {
|
|||
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);
|
||||
|
|
|
@ -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 @@ Q_IMPORT_PLUGIN(QWindowsAudioPlugin);
|
|||
#endif
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
#ifdef BUILD_SDL
|
||||
SDL_SetMainReady();
|
||||
#endif
|
||||
QGBA::GBAApp application(argc, argv);
|
||||
|
||||
QLocale locale = QLocale::system();
|
||||
|
|
|
@ -185,30 +185,31 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) {
|
|||
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;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue