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
|
- Qt: Fix linking after some windows have been closed
|
||||||
- GBA Video: Fix wrong palette on 256-color sprites in OBJWIN
|
- GBA Video: Fix wrong palette on 256-color sprites in OBJWIN
|
||||||
- Windows: Fix VDir.rewind
|
- 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:
|
Misc:
|
||||||
- SDL: Remove scancode key input
|
- SDL: Remove scancode key input
|
||||||
- GBA Video: Clean up unused timers
|
- GBA Video: Clean up unused timers
|
||||||
|
@ -80,6 +84,8 @@ Misc:
|
||||||
- Qt: Remove audio thread
|
- Qt: Remove audio thread
|
||||||
- Qt: Remove audio buffer sizing in AudioProcessorQt
|
- Qt: Remove audio buffer sizing in AudioProcessorQt
|
||||||
- Qt: Re-enable QtMultimedia on Windows
|
- 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)
|
0.5.2: (2016-12-31)
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|
|
@ -119,6 +119,7 @@ struct GBVideo {
|
||||||
int ocpIndex;
|
int ocpIndex;
|
||||||
bool ocpIncrement;
|
bool ocpIncrement;
|
||||||
|
|
||||||
|
uint16_t dmgPalette[4];
|
||||||
uint16_t palette[64];
|
uint16_t palette[64];
|
||||||
|
|
||||||
int32_t frameCounter;
|
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 GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value);
|
||||||
void GBVideoSwitchBank(struct GBVideo* video, uint8_t value);
|
void GBVideoSwitchBank(struct GBVideo* video, uint8_t value);
|
||||||
|
|
||||||
|
void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color);
|
||||||
|
|
||||||
struct GBSerializedState;
|
struct GBSerializedState;
|
||||||
void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state);
|
void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state);
|
||||||
void GBVideoDeserialize(struct GBVideo* video, const 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);
|
UNUSED(message);
|
||||||
int r;
|
int r;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
// General purpose registers
|
||||||
for (r = 0; r < ARM_PC; ++r) {
|
for (r = 0; r < ARM_PC; ++r) {
|
||||||
_int2hex32(cpu->gprs[r], &stub->outgoing[i]);
|
_int2hex32(cpu->gprs[r], &stub->outgoing[i]);
|
||||||
i += 8;
|
i += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Program counter
|
||||||
_int2hex32(cpu->gprs[ARM_PC] - (cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM), &stub->outgoing[i]);
|
_int2hex32(cpu->gprs[ARM_PC] - (cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM), &stub->outgoing[i]);
|
||||||
i += 8;
|
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;
|
stub->outgoing[i] = 0;
|
||||||
_sendMessage(stub);
|
_sendMessage(stub);
|
||||||
}
|
}
|
||||||
|
|
|
@ -304,6 +304,14 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) {
|
||||||
}
|
}
|
||||||
av_opt_set(encoder->video->priv_data, "tune", "zerolatency", 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);
|
avcodec_open2(encoder->video, vcodec, 0);
|
||||||
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
||||||
encoder->videoFrame = av_frame_alloc();
|
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);
|
avcodec_parameters_from_context(encoder->videoStream->codecpar, encoder->video);
|
||||||
#endif
|
#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;
|
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] = left;
|
||||||
encoder->audioBuffer[encoder->currentAudioSample * 2 + 1] = right;
|
encoder->audioBuffer[encoder->currentAudioSample * 2 + 1] = right;
|
||||||
|
|
||||||
++encoder->currentAudioFrame;
|
|
||||||
++encoder->currentAudioSample;
|
++encoder->currentAudioSample;
|
||||||
|
|
||||||
if ((encoder->currentAudioSample * 4) < encoder->audioBufferSize) {
|
if (encoder->currentAudioSample * 4 < encoder->audioBufferSize) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt);
|
int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt);
|
||||||
avresample_convert(encoder->resampleContext,
|
avresample_convert(encoder->resampleContext, 0, 0, 0,
|
||||||
0, 0, 0,
|
(uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4);
|
||||||
(uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4);
|
|
||||||
|
encoder->currentAudioSample = 0;
|
||||||
if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) {
|
if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
#if LIBAVCODEC_VERSION_MAJOR >= 55
|
||||||
av_frame_make_writable(encoder->audioFrame);
|
av_frame_make_writable(encoder->audioFrame);
|
||||||
#endif
|
#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->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame, encoder->audio->time_base, encoder->audioStream->time_base);
|
||||||
encoder->currentAudioSample = 0;
|
encoder->currentAudioFrame += samples;
|
||||||
|
|
||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
av_init_packet(&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->audio.masterVolume = core->opts.volume;
|
||||||
}
|
}
|
||||||
gb->video.frameskip = core->opts.frameskip;
|
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, "gb.bios");
|
||||||
mCoreConfigCopyValue(&core->config, config, "gbc.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 GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y);
|
||||||
|
|
||||||
static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) {
|
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;
|
int y;
|
||||||
for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) {
|
for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) {
|
||||||
color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y];
|
color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y];
|
||||||
int x;
|
int x;
|
||||||
for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) {
|
for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) {
|
||||||
row[x + 0] = palette0;
|
row[x + 0] = renderer->palette[0];
|
||||||
row[x + 1] = palette0;
|
row[x + 1] = renderer->palette[0];
|
||||||
row[x + 2] = palette0;
|
row[x + 2] = renderer->palette[0];
|
||||||
row[x + 3] = palette0;
|
row[x + 3] = renderer->palette[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,9 +74,6 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer*
|
||||||
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
|
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case REG_LCDC:
|
case REG_LCDC:
|
||||||
if (GBRegisterLCDCIsEnable(softwareRenderer->lcdc) && !GBRegisterLCDCIsEnable(value)) {
|
|
||||||
_clearScreen(softwareRenderer);
|
|
||||||
}
|
|
||||||
softwareRenderer->lcdc = value;
|
softwareRenderer->lcdc = value;
|
||||||
break;
|
break;
|
||||||
case REG_SCY:
|
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);
|
mappedMemoryFree(softwareRenderer->temporaryBuffer, GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4);
|
||||||
softwareRenderer->temporaryBuffer = 0;
|
softwareRenderer->temporaryBuffer = 0;
|
||||||
}
|
}
|
||||||
|
if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) {
|
||||||
|
_clearScreen(softwareRenderer);
|
||||||
|
}
|
||||||
softwareRenderer->currentWy = 0;
|
softwareRenderer->currentWy = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,11 @@ void GBVideoInit(struct GBVideo* video) {
|
||||||
video->frameEvent.name = "GB Video Frame";
|
video->frameEvent.name = "GB Video Frame";
|
||||||
video->frameEvent.callback = _updateFrameCount;
|
video->frameEvent.callback = _updateFrameCount;
|
||||||
video->frameEvent.priority = 9;
|
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) {
|
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_LCDSTAT);
|
||||||
}
|
}
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK);
|
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) {
|
if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) {
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
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);
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
GBUpdateIRQs(video->p);
|
GBUpdateIRQs(video->p);
|
||||||
}
|
}
|
||||||
video->renderer->finishFrame(video->renderer);
|
|
||||||
if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) {
|
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);
|
video->p->memory.rotation->sample(video->p->memory.rotation);
|
||||||
}
|
}
|
||||||
|
@ -231,9 +227,18 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat
|
||||||
return;
|
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);
|
GBFrameEnded(video->p);
|
||||||
--video->frameskipCounter;
|
--video->frameskipCounter;
|
||||||
if (video->frameskipCounter < 0) {
|
if (video->frameskipCounter < 0) {
|
||||||
|
video->renderer->finishFrame(video->renderer);
|
||||||
mCoreSyncPostFrame(video->p->sync);
|
mCoreSyncPostFrame(video->p->sync);
|
||||||
video->frameskipCounter = video->frameskip;
|
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);
|
video->p->stream->postVideoFrame(video->p->stream, pixels, stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t c;
|
|
||||||
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
|
for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) {
|
||||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
|
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c);
|
||||||
if (callbacks->videoFrameStarted) {
|
if (callbacks->videoFrameStarted) {
|
||||||
|
@ -319,6 +323,8 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
||||||
GBUpdateIRQs(video->p);
|
GBUpdateIRQs(video->p);
|
||||||
}
|
}
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
|
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
|
||||||
|
|
||||||
mTimingDeschedule(&video->p->timing, &video->frameEvent);
|
mTimingDeschedule(&video->p->timing, &video->frameEvent);
|
||||||
}
|
}
|
||||||
if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) {
|
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->p->memory.io[REG_STAT] = video->stat;
|
||||||
video->ly = 0;
|
video->ly = 0;
|
||||||
video->p->memory.io[REG_LY] = 0;
|
video->p->memory.io[REG_LY] = 0;
|
||||||
|
video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]);
|
||||||
|
|
||||||
mTimingDeschedule(&video->p->timing, &video->modeEvent);
|
mTimingDeschedule(&video->p->timing, &video->modeEvent);
|
||||||
mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH);
|
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) {
|
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) {
|
if (video->p->model < GB_MODEL_CGB) {
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case REG_BGP:
|
case REG_BGP:
|
||||||
video->palette[0] = dmgPalette[value & 3];
|
video->palette[0] = video->dmgPalette[value & 3];
|
||||||
video->palette[1] = dmgPalette[(value >> 2) & 3];
|
video->palette[1] = video->dmgPalette[(value >> 2) & 3];
|
||||||
video->palette[2] = dmgPalette[(value >> 4) & 3];
|
video->palette[2] = video->dmgPalette[(value >> 4) & 3];
|
||||||
video->palette[3] = dmgPalette[(value >> 6) & 3];
|
video->palette[3] = video->dmgPalette[(value >> 6) & 3];
|
||||||
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
|
video->renderer->writePalette(video->renderer, 0, video->palette[0]);
|
||||||
video->renderer->writePalette(video->renderer, 1, video->palette[1]);
|
video->renderer->writePalette(video->renderer, 1, video->palette[1]);
|
||||||
video->renderer->writePalette(video->renderer, 2, video->palette[2]);
|
video->renderer->writePalette(video->renderer, 2, video->palette[2]);
|
||||||
video->renderer->writePalette(video->renderer, 3, video->palette[3]);
|
video->renderer->writePalette(video->renderer, 3, video->palette[3]);
|
||||||
break;
|
break;
|
||||||
case REG_OBP0:
|
case REG_OBP0:
|
||||||
video->palette[8 * 4 + 0] = dmgPalette[value & 3];
|
video->palette[8 * 4 + 0] = video->dmgPalette[value & 3];
|
||||||
video->palette[8 * 4 + 1] = dmgPalette[(value >> 2) & 3];
|
video->palette[8 * 4 + 1] = video->dmgPalette[(value >> 2) & 3];
|
||||||
video->palette[8 * 4 + 2] = dmgPalette[(value >> 4) & 3];
|
video->palette[8 * 4 + 2] = video->dmgPalette[(value >> 4) & 3];
|
||||||
video->palette[8 * 4 + 3] = dmgPalette[(value >> 6) & 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 + 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 + 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 + 2, video->palette[8 * 4 + 2]);
|
||||||
video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]);
|
video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]);
|
||||||
break;
|
break;
|
||||||
case REG_OBP1:
|
case REG_OBP1:
|
||||||
video->palette[9 * 4 + 0] = dmgPalette[value & 3];
|
video->palette[9 * 4 + 0] = video->dmgPalette[value & 3];
|
||||||
video->palette[9 * 4 + 1] = dmgPalette[(value >> 2) & 3];
|
video->palette[9 * 4 + 1] = video->dmgPalette[(value >> 2) & 3];
|
||||||
video->palette[9 * 4 + 2] = dmgPalette[(value >> 4) & 3];
|
video->palette[9 * 4 + 2] = video->dmgPalette[(value >> 4) & 3];
|
||||||
video->palette[9 * 4 + 3] = dmgPalette[(value >> 6) & 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 + 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 + 1, video->palette[9 * 4 + 1]);
|
||||||
video->renderer->writePalette(video->renderer, 9 * 4 + 2, video->palette[9 * 4 + 2]);
|
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;
|
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) {
|
static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) {
|
||||||
UNUSED(renderer);
|
UNUSED(renderer);
|
||||||
UNUSED(model);
|
UNUSED(model);
|
||||||
|
|
|
@ -1548,6 +1548,9 @@ void _pristineCow(struct GBA* gba) {
|
||||||
void* newRom = anonymousMemoryMap(SIZE_CART0);
|
void* newRom = anonymousMemoryMap(SIZE_CART0);
|
||||||
memcpy(newRom, gba->memory.rom, gba->memory.romSize);
|
memcpy(newRom, gba->memory.rom, gba->memory.romSize);
|
||||||
memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, SIZE_CART0 - 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) {
|
if (gba->romVf) {
|
||||||
#ifndef _3DS
|
#ifndef _3DS
|
||||||
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize);
|
gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize);
|
||||||
|
|
|
@ -22,7 +22,7 @@ if(BUILD_SDL)
|
||||||
if(SDL2_FOUND)
|
if(SDL2_FOUND)
|
||||||
link_directories(${SDL2_LIBDIR})
|
link_directories(${SDL2_LIBDIR})
|
||||||
endif()
|
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)
|
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)
|
include_directories(${SDL_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/src/platform/sdl)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -322,15 +322,6 @@ void PainterGL::draw() {
|
||||||
if (m_queue.isEmpty() || !mCoreThreadIsActive(m_context)) {
|
if (m_queue.isEmpty() || !mCoreThreadIsActive(m_context)) {
|
||||||
return;
|
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()) {
|
if (mCoreSyncWaitFrameStart(&m_context->sync) || !m_queue.isEmpty()) {
|
||||||
dequeue();
|
dequeue();
|
||||||
|
@ -339,6 +330,14 @@ void PainterGL::draw() {
|
||||||
performDraw();
|
performDraw();
|
||||||
m_painter.end();
|
m_painter.end();
|
||||||
m_backend->swap(m_backend);
|
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 {
|
} else {
|
||||||
mCoreSyncWaitFrameEnd(&m_context->sync);
|
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);
|
QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getOption("lastDirectory"), filter);
|
||||||
continueAll(paused);
|
continueAll(paused);
|
||||||
if (!filename.isEmpty()) {
|
if (!filename.isEmpty()) {
|
||||||
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path());
|
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath());
|
||||||
}
|
}
|
||||||
return filename;
|
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);
|
QString filename = QFileDialog::getSaveFileName(owner, title, m_configController.getOption("lastDirectory"), filter);
|
||||||
continueAll(paused);
|
continueAll(paused);
|
||||||
if (!filename.isEmpty()) {
|
if (!filename.isEmpty()) {
|
||||||
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path());
|
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath());
|
||||||
}
|
}
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
@ -185,23 +185,11 @@ QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title) {
|
||||||
QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getOption("lastDirectory"));
|
QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getOption("lastDirectory"));
|
||||||
continueAll(paused);
|
continueAll(paused);
|
||||||
if (!filename.isEmpty()) {
|
if (!filename.isEmpty()) {
|
||||||
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path());
|
m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath());
|
||||||
}
|
}
|
||||||
return filename;
|
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() {
|
QString GBAApp::dataDir() {
|
||||||
#ifdef DATADIR
|
#ifdef DATADIR
|
||||||
QString path = QString::fromUtf8(DATADIR);
|
QString path = QString::fromUtf8(DATADIR);
|
||||||
|
@ -241,24 +229,6 @@ bool GBAApp::reloadGameDB() {
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
#ifdef USE_SQLITE3
|
||||||
GameDBParser::GameDBParser(NoIntroDB* db, QObject* parent)
|
GameDBParser::GameDBParser(NoIntroDB* db, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
|
|
@ -55,9 +55,6 @@ public:
|
||||||
QString getSaveFileName(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);
|
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; }
|
const NoIntroDB* gameDB() const { return m_db; }
|
||||||
bool reloadGameDB();
|
bool reloadGameDB();
|
||||||
|
|
||||||
|
@ -65,16 +62,6 @@ protected:
|
||||||
bool event(QEvent*);
|
bool event(QEvent*);
|
||||||
|
|
||||||
private:
|
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();
|
Window* newWindowInternal();
|
||||||
|
|
||||||
void pauseAll(QList<Window*>* paused);
|
void pauseAll(QList<Window*>* paused);
|
||||||
|
|
|
@ -345,6 +345,8 @@ void GameController::setConfig(const mCoreConfig* config) {
|
||||||
if (isLoaded()) {
|
if (isLoaded()) {
|
||||||
Interrupter interrupter(this);
|
Interrupter interrupter(this);
|
||||||
mCoreLoadForeignConfig(m_threadContext.core, config);
|
mCoreLoadForeignConfig(m_threadContext.core, config);
|
||||||
|
m_audioSync = m_threadContext.sync.audioWait;
|
||||||
|
m_videoSync = m_threadContext.sync.videoFrameWait;
|
||||||
m_audioProcessor->setInput(&m_threadContext);
|
m_audioProcessor->setInput(&m_threadContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -407,10 +409,10 @@ void GameController::loadGame(VFile* vf, const QString& path, const QString& bas
|
||||||
closeGame();
|
closeGame();
|
||||||
QFileInfo info(base);
|
QFileInfo info(base);
|
||||||
if (info.isDir()) {
|
if (info.isDir()) {
|
||||||
m_fname = base + QDir::separator() + path;
|
m_fname = QFileInfo(base + '/' + path).canonicalFilePath();
|
||||||
m_fsub = QString();
|
m_fsub = QString();
|
||||||
} else {
|
} else {
|
||||||
m_fname = base;
|
m_fname = info.canonicalFilePath();
|
||||||
m_fsub = path;
|
m_fsub = path;
|
||||||
}
|
}
|
||||||
m_vf = vf;
|
m_vf = vf;
|
||||||
|
@ -1112,6 +1114,17 @@ void GameController::setSync(bool enable) {
|
||||||
}
|
}
|
||||||
m_sync = 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) {
|
void GameController::setAVStream(mAVStream* stream) {
|
||||||
Interrupter interrupter(this);
|
Interrupter interrupter(this);
|
||||||
m_stream = stream;
|
m_stream = stream;
|
||||||
|
|
|
@ -149,6 +149,8 @@ public slots:
|
||||||
void setTurbo(bool, bool forced = true);
|
void setTurbo(bool, bool forced = true);
|
||||||
void setTurboSpeed(float ratio);
|
void setTurboSpeed(float ratio);
|
||||||
void setSync(bool);
|
void setSync(bool);
|
||||||
|
void setAudioSync(bool);
|
||||||
|
void setVideoSync(bool);
|
||||||
void setAVStream(mAVStream*);
|
void setAVStream(mAVStream*);
|
||||||
void clearAVStream();
|
void clearAVStream();
|
||||||
void reloadAudioDriver();
|
void reloadAudioDriver();
|
||||||
|
|
|
@ -244,12 +244,8 @@ void ObjView::updateTilesGB(bool force) {
|
||||||
|
|
||||||
void ObjView::exportObj() {
|
void ObjView::exportObj() {
|
||||||
GameController::Interrupter interrupter(m_controller);
|
GameController::Interrupter interrupter(m_controller);
|
||||||
QFileDialog* dialog = GBAApp::app()->getSaveFileDialog(this, tr("Export sprite"),
|
QString filename = GBAApp::app()->getSaveFileName(this, tr("Export sprite"),
|
||||||
tr("Portable Network Graphics (*.png)"));
|
tr("Portable Network Graphics (*.png)"));
|
||||||
if (!dialog->exec()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QString filename = dialog->selectedFiles()[0];
|
|
||||||
VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||||
if (!vf) {
|
if (!vf) {
|
||||||
LOG(QT, ERROR) << tr("Failed to open output PNG file: %1").arg(filename);
|
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);
|
GameController::Interrupter interrupter(m_controller);
|
||||||
QFileDialog* dialog = GBAApp::app()->getSaveFileDialog(this, tr("Export palette"),
|
QString filename = GBAApp::app()->getSaveFileName(this, tr("Export palette"),
|
||||||
tr("Windows PAL (*.pal);;Adobe Color Table (*.act)"));
|
tr("Windows PAL (*.pal);;Adobe Color Table (*.act)"));
|
||||||
if (!dialog->exec()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QString filename = dialog->selectedFiles()[0];
|
|
||||||
VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC);
|
||||||
if (!vf) {
|
if (!vf) {
|
||||||
LOG(QT, ERROR) << tr("Failed to open output palette file: %1").arg(filename);
|
LOG(QT, ERROR) << tr("Failed to open output palette file: %1").arg(filename);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString filter = dialog->selectedNameFilter();
|
if (filename.endsWith(".pal", Qt::CaseInsensitive)) {
|
||||||
if (filter.contains("*.pal")) {
|
|
||||||
exportPaletteRIFF(vf, length, &static_cast<GBA*>(m_controller->thread()->core->board)->video.palette[start]);
|
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]);
|
exportPaletteACT(vf, length, &static_cast<GBA*>(m_controller->thread()->core->board)->video.palette[start]);
|
||||||
}
|
}
|
||||||
vf->close(vf);
|
vf->close(vf);
|
||||||
|
|
|
@ -63,7 +63,9 @@ VideoView::VideoView(QWidget* parent)
|
||||||
if (s_vcodecMap.empty()) {
|
if (s_vcodecMap.empty()) {
|
||||||
s_vcodecMap["dirac"] = "libschroedinger";
|
s_vcodecMap["dirac"] = "libschroedinger";
|
||||||
s_vcodecMap["h264"] = "libx264";
|
s_vcodecMap["h264"] = "libx264";
|
||||||
|
s_vcodecMap["h264 nvenc"] = "h264_nvenc";
|
||||||
s_vcodecMap["hevc"] = "libx265";
|
s_vcodecMap["hevc"] = "libx265";
|
||||||
|
s_vcodecMap["hevc nvenc"] = "hevc_nvenc";
|
||||||
s_vcodecMap["theora"] = "libtheora";
|
s_vcodecMap["theora"] = "libtheora";
|
||||||
s_vcodecMap["vp8"] = "libvpx";
|
s_vcodecMap["vp8"] = "libvpx";
|
||||||
s_vcodecMap["vp9"] = "libvpx-vp9";
|
s_vcodecMap["vp9"] = "libvpx-vp9";
|
||||||
|
@ -458,6 +460,8 @@ void VideoView::uncheckIncompatible() {
|
||||||
QString VideoView::sanitizeCodec(const QString& codec, const QMap<QString, QString>& mapping) {
|
QString VideoView::sanitizeCodec(const QString& codec, const QMap<QString, QString>& mapping) {
|
||||||
QString sanitized = codec.toLower();
|
QString sanitized = codec.toLower();
|
||||||
sanitized = sanitized.remove(QChar('.'));
|
sanitized = sanitized.remove(QChar('.'));
|
||||||
|
sanitized = sanitized.remove(QChar('('));
|
||||||
|
sanitized = sanitized.remove(QChar(')'));
|
||||||
if (mapping.contains(sanitized)) {
|
if (mapping.contains(sanitized)) {
|
||||||
sanitized = mapping[sanitized];
|
sanitized = mapping[sanitized];
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,12 +266,17 @@
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>VP8</string>
|
<string>h.264 (NVENC)</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Xvid</string>
|
<string>HEVC</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>VP8</string>
|
||||||
</property>
|
</property>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -1192,14 +1192,14 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
ConfigOption* videoSync = m_config->addOption("videoSync");
|
ConfigOption* videoSync = m_config->addOption("videoSync");
|
||||||
videoSync->addBoolean(tr("Sync to &video"), emulationMenu);
|
videoSync->addBoolean(tr("Sync to &video"), emulationMenu);
|
||||||
videoSync->connect([this](const QVariant& value) {
|
videoSync->connect([this](const QVariant& value) {
|
||||||
reloadConfig();
|
m_controller->setVideoSync(value.toBool());
|
||||||
}, this);
|
}, this);
|
||||||
m_config->updateOption("videoSync");
|
m_config->updateOption("videoSync");
|
||||||
|
|
||||||
ConfigOption* audioSync = m_config->addOption("audioSync");
|
ConfigOption* audioSync = m_config->addOption("audioSync");
|
||||||
audioSync->addBoolean(tr("Sync to &audio"), emulationMenu);
|
audioSync->addBoolean(tr("Sync to &audio"), emulationMenu);
|
||||||
audioSync->connect([this](const QVariant& value) {
|
audioSync->connect([this](const QVariant& value) {
|
||||||
reloadConfig();
|
m_controller->setAudioSync(value.toBool());
|
||||||
}, this);
|
}, this);
|
||||||
m_config->updateOption("audioSync");
|
m_config->updateOption("audioSync");
|
||||||
|
|
||||||
|
@ -1552,7 +1552,7 @@ void Window::updateMRU() {
|
||||||
m_mruMenu->clear();
|
m_mruMenu->clear();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const QString& file : m_mruFiles) {
|
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));
|
item->setShortcut(QString("Ctrl+%1").arg(i));
|
||||||
connect(item, &QAction::triggered, [this, file]() { m_controller->loadGame(file); });
|
connect(item, &QAction::triggered, [this, file]() { m_controller->loadGame(file); });
|
||||||
m_mruMenu->addAction(item);
|
m_mruMenu->addAction(item);
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* 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
|
* 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/. */
|
* 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 "GBAApp.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
|
||||||
|
@ -22,6 +26,9 @@ Q_IMPORT_PLUGIN(QWindowsAudioPlugin);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
#ifdef BUILD_SDL
|
||||||
|
SDL_SetMainReady();
|
||||||
|
#endif
|
||||||
QGBA::GBAApp application(argc, argv);
|
QGBA::GBAApp application(argc, argv);
|
||||||
|
|
||||||
QLocale locale = QLocale::system();
|
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.samples = renderer->core->opts.audioBuffers;
|
||||||
renderer->audio.sampleRate = 44100;
|
renderer->audio.sampleRate = 44100;
|
||||||
|
|
||||||
bool didFail = !mSDLInitAudio(&renderer->audio, &thread);
|
bool didFail = !mCoreThreadStart(&thread);
|
||||||
if (!didFail) {
|
if (!didFail) {
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver);
|
mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver);
|
||||||
mSDLSuspendScreensaver(&renderer->events);
|
mSDLSuspendScreensaver(&renderer->events);
|
||||||
#endif
|
#endif
|
||||||
if (mCoreThreadStart(&thread)) {
|
if (mSDLInitAudio(&renderer->audio, &thread)) {
|
||||||
renderer->runloop(renderer, &thread);
|
renderer->runloop(renderer, &thread);
|
||||||
mSDLPauseAudio(&renderer->audio);
|
mSDLPauseAudio(&renderer->audio);
|
||||||
mCoreThreadJoin(&thread);
|
if (mCoreThreadHasCrashed(&thread)) {
|
||||||
|
didFail = true;
|
||||||
|
printf("The game crashed!\n");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
didFail = true;
|
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)
|
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||||
mSDLResumeScreensaver(&renderer->events);
|
mSDLResumeScreensaver(&renderer->events);
|
||||||
mSDLSetScreensaverSuspendable(&renderer->events, false);
|
mSDLSetScreensaverSuspendable(&renderer->events, false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (mCoreThreadHasCrashed(&thread)) {
|
mCoreThreadJoin(&thread);
|
||||||
didFail = true;
|
} else {
|
||||||
printf("The game crashed!\n");
|
printf("Could not run game. Are you sure the file exists and is a compatible game?\n");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
renderer->core->unloadROM(renderer->core);
|
renderer->core->unloadROM(renderer->core);
|
||||||
return didFail;
|
return didFail;
|
||||||
|
|
|
@ -10,11 +10,11 @@ set(SUMMARY "${PROJECT_NAME} Game Boy Advance Emulator")
|
||||||
|
|
||||||
find_program(GIT git)
|
find_program(GIT git)
|
||||||
if(GIT AND NOT SKIP_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 --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 OUTPUT_VARIABLE GIT_COMMIT_SHORT 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 OUTPUT_VARIABLE GIT_BRANCH 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 OUTPUT_VARIABLE GIT_REV 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 OUTPUT_VARIABLE GIT_TAG 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()
|
endif()
|
||||||
|
|
||||||
if(NOT GIT_REV)
|
if(NOT GIT_REV)
|
||||||
|
|
Loading…
Reference in New Issue