Merge branch 'master' into port/wii

This commit is contained in:
Jeffrey Pfau 2015-08-15 20:33:05 -07:00
commit ddab7a7e44
64 changed files with 1390 additions and 143 deletions

View File

@ -30,6 +30,8 @@ Features:
- Libretro now supports BIOS, rumble and solar sensor
- Implement BIOS call Stop, for sleep mode
- Automatically load patches, if found
- Improved video synchronization
- Configurable audio output sample rate
Bugfixes:
- ARM7: Fix SWI and IRQ timings
- GBA Audio: Force audio FIFOs to 32-bit
@ -67,6 +69,10 @@ Bugfixes:
- Qt: Fix analog buttons not getting unmapped
- GBA Video: Prevent tiles < 512 from being used in modes 3 - 5
- Qt: Fix passing command line options
- Qt: Fix crashes on Windows by using using QMetaObject to do cross-thread calls
- GBA Video: Fix timing on first scanline
- GBA: Ensure cycles never go negative
- Util: Fix formatting of floats
Misc:
- Qt: Handle saving input settings better
- Debugger: Free watchpoints in addition to breakpoints
@ -115,6 +121,9 @@ Misc:
- Qt: Increase usability of key mapper
- Qt: Show checkmark for window sizes
- Qt: Set window path to loaded ROM
- GBA Memory: Run multiple DMAs in a tight loop if they all occur before present
- GBA Audio: Process multiple audio events at once, if necessary
- GBA: Process multiple timer events at once, if necessary
0.2.1: (2015-05-13)
Bugfixes:

View File

@ -45,13 +45,17 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type (e.g. Release or Debug)" FORCE)
endif()
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBDIR}")
include(GNUInstallDirs)
if (NOT DEFINED LIBDIR)
set(LIBDIR "lib")
endif()
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBDIR}")
include(GNUInstallDirs)
if (NOT DEFINED MANDIR)
set(MANDIR ${CMAKE_INSTALL_MANDIR})
endif()
# Function definitions
include(FindPkgConfig)

View File

@ -30,13 +30,12 @@ The ports are vaguely usable, but by no means should be considered stable.
### PS Vita (port/psp2)
* Add menu
* Add audio
* Fix audio
* Make it faster
* Threaded renderer shim
* Hardware acceleration
### Wii (port/wii)
* Add menu
* Add audio
* Thread support
* Clean up video detection

118
doc/mgba-qt.6 Normal file
View File

@ -0,0 +1,118 @@
.\" Copyright (c) 2015 Anthony J. Bentley <anthony@anjbe.name>
.\"
.\" 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 https://mozilla.org/MPL/2.0/.
.Dd July 29, 2015
.Dt MGBA-QT 6
.Os
.Sh NAME
.Nm mgba-qt
.Nd Game Boy Advance emulator
.Sh SYNOPSIS
.Nm mgba-qt
.Op Fl b Ar biosfile
.Op Fl l Ar loglevel
.Op Fl p Ar patchfile
.Op Fl s Ar n
.Ar file
.Sh DESCRIPTION
.Nm
is a Game Boy Advance emulator.
The options are as follows:
.Bl -tag -width Ds
.It Fl b Ar biosfile , Fl -bios Ar biosfile
Specify a BIOS file to use during boot.
If this flag is omitted,
.Nm
will use the BIOS specified in the configuration file,
or a high\(hylevel emulated BIOS if none is specified.
.It Fl l Ar loglevel
Log messages during emulation.
.Ar loglevel
is a bitmask defining which types of messages to log:
.Bl -bullet -compact
.It
1 \(en fatal errors
.It
2 \(en errors
.It
4 \(en warnings
.It
8 \(en informative messages
.It
16 \(en debugging messages
.It
32 \(en stub messages for unimplemented features
.It
256 \(en in\(hygame errors
.It
512 \(en software interrupts
.It
1024 \(en emulator status messages
.It
2048 \(en serial I/O messages
.El
The default is to log warnings, errors, fatal errors, and status messages.
.It Fl p Ar patchfile , Fl -patch Ar patchfile
Specify a patch file in BPS, IPS, or UPS format.
.It Fl s Ar n , Fl -frameskip Ar n
Skip every
.Ar n
frames.
.El
.Sh CONTROLS
The default controls are as follows:
.Bl -hang -width "Frame advance" -compact
.It A
.Cm x
.It B
.Cm z
.It L
.Cm a
.It R
.Cm s
.It Start
.Aq Cm Enter
.It Select
.Aq Cm Backspace
.It Load state
.Cm F1 Ns \(en Ns Cm F9
.It Save state
.Ao Cm Shift Ac Ns \(hy Ns Cm F1 Ns \(en Ns Cm F9
.It Frame advance
.Ao Cm Ctrl Ac Ns \(hy Ns Cm n
.El
.Sh FILES
.Bl -tag -width ~/.config/mgba/config.ini -compact
.It Pa ~/.config/mgba/config.ini
Default
.Xr mgba 6
configuration file.
.It Pa ~/.config/mgba/qt.ini
Default
.Nm mgba-qt
configuration file.
.It Pa portable.ini
If this file exists in the current directory,
.Nm
will read
.Pa config.ini
and
.Pa qt.ini
from the current directory instead of
.Pa ~/.config/mgba .
.El
.Sh AUTHORS
.An Jeffrey Pfau Aq Mt jeffrey@endrift.com
.Sh HOMEPAGE
.Bl -bullet
.It
.Lk https://mgba.io/ "mGBA homepage"
.It
.Lk https://github.com/mgba-emu/mgba "Development repository"
.It
.Lk https://github.com/mgba-emu/mgba/issues "Bug tracker"
.It
.Lk https://forums.mgba.io/ "Message board"
.El

259
doc/mgba.6 Normal file
View File

@ -0,0 +1,259 @@
.\" Copyright (c) 2015 Anthony J. Bentley <anthony@anjbe.name>
.\"
.\" 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 https://mozilla.org/MPL/2.0/.
.Dd July 29, 2015
.Dt MGBA 6
.Os
.Sh NAME
.Nm mgba
.Nd Game Boy Advance emulator
.Sh SYNOPSIS
.Nm mgba
.Op Fl 123456dfg
.Op Fl b Ar biosfile
.Op Fl c Ar cheatfile
.Op Fl l Ar loglevel
.Op Fl p Ar patchfile
.Op Fl s Ar n
.Op Fl v Ar moviefile
.Ar file
.Sh DESCRIPTION
.Nm
is a Game Boy Advance emulator.
The options are as follows:
.Bl -tag -width Ds
.It Fl 1
Scale the window 1\(mu.
.It Fl 2
Scale the window 2\(mu.
.It Fl 3
Scale the window 3\(mu.
.It Fl 4
Scale the window 4\(mu.
.It Fl 5
Scale the window 5\(mu.
.It Fl 6
Scale the window 6\(mu.
.It Fl b Ar biosfile , Fl -bios Ar biosfile
Specify a BIOS file to use during boot.
If this flag is omitted,
.Nm
will use the BIOS specified in the configuration file,
or a high\(hylevel emulated BIOS if none is specified.
.It Fl c Ar cheatfile , Fl -cheats Ar cheatfile
Apply cheat codes from
.Ar cheatfile .
.It Fl d
Start emulating via the command\(hyline debugger.
.It Fl f
Start the emulator full\(hyscreen.
.It Fl g
Start a
.Xr gdb 1
session.
By default the session starts on port 2345.
.It Fl l Ar loglevel
Log messages during emulation to
.Dv stdout .
.Ar loglevel
is a bitmask defining which types of messages to log:
.Bl -bullet -compact
.It
1 \(en fatal errors
.It
2 \(en errors
.It
4 \(en warnings
.It
8 \(en informative messages
.It
16 \(en debugging messages
.It
32 \(en stub messages for unimplemented features
.It
256 \(en in\(hygame errors
.It
512 \(en software interrupts
.It
1024 \(en emulator status messages
.It
2048 \(en serial I/O messages
.El
The default is to log warnings, errors, fatal errors, and status messages.
.It Fl p Ar patchfile , Fl -patch Ar patchfile
Specify a patch file in BPS, IPS, or UPS format.
.It Fl s Ar n , Fl -frameskip Ar n
Skip every
.Ar n
frames.
.It Fl v Ar moviefile , Fl -movie Ar moviefile
Play back a movie of recording input from
.Ar moviefile .
.El
.Sh CONTROLS
The default controls are as follows:
.Bl -hang -width "Frame advance" -compact
.It A
.Cm x
.It B
.Cm z
.It L
.Cm a
.It R
.Cm s
.It Start
.Aq Cm Enter
.It Select
.Aq Cm Backspace
.It Load state
.Cm F1 Ns \(en Ns Cm F9
.It Save state
.Ao Cm Shift Ac Ns \(hy Ns Cm F1 Ns \(en Ns Cm F9
.It Frame advance
.Ao Cm Ctrl Ac Ns \(hy Ns Cm n
.El
.Sh DEBUGGER
When
.Nm
is run with the
.Fl d
option, the command\(hyline debugger is enabled.
It supports the following commands:
.Pp
.Bl -tag -compact -width 1
.It Cm b Ns Oo Cm reak Oc Ar address
.It Cm b Ns Oo Cm reak Oc Ns Cm /a Ar address
.It Cm b Ns Oo Cm reak Oc Ns Cm /t Ar address
Set a breakpoint \(en ARM
.Pq Ql /a ,
Thumb
.Pq Ql /t ,
or the current CPU mode \(en at
.Ar address .
.It Cm c Ns Op Cm ontinue
Continue execution.
.It Cm d Ns Oo Cm elete Oc Ar address
Delete a breakpoint at
.Ar address .
.It Cm dis Ns Oo Cm asm Oc Op Ar address Op Ar count
.It Cm dis Ns Oo Cm asm Oc Ns Cm /a Op Ar address Op Ar count
.It Cm dis Ns Oo Cm asm Oc Ns Cm /t Op Ar address Op Ar count
.It Cm dis Ns Oo Cm assemble Oc Op Ar address Op Ar count
.It Cm dis Ns Oo Cm assemble Oc Ns Cm /a Op Ar address Op Ar count
.It Cm dis Ns Oo Cm assemble Oc Ns Cm /t Op Ar address Op Ar count
Disassemble
.Ar count
instructions starting at
.Ar address ,
as ARM
.Pq Ql /a ,
Thumb
.Pq Ql /t ,
or the current CPU mode.
If
.Ar count
is not specified, only disassemble the instruction at
.Ar address .
If
.Ar address
is not specified, only disassemble the current address.
.It Cm h Ns Op Cm elp
Print help.
.It Cm i Ns Op Cm nfo
.It Cm status
Print the current contents of general\(hypurpose registers and the current
program state register, and disassemble the current instruction.
.It Cm n Ns Op Cm ext
Execute the next instruction.
.It Cm p Ns Oo Cm rint Oc Ar value ...
.It Cm p Ns Oo Cm rint Oc Ns Cm /t Ar value ...
.It Cm p Ns Oo Cm rint Oc Ns Cm /x Ar value ...
Print one or more
.Ar value Ns s
as binary
.Pq Ql /t ,
hexadecimal
.Pq Ql /x ,
or decimal.
.It Cm q Ns Op Cm uit
Quit the emulator.
.It Cm reset
Reset the emulation.
.It Cm r/1 Ar address
.It Cm r/2 Ar address
.It Cm r/4 Ar address
Read a byte
.Pq Ql /1 ,
halfword
.Pq Ql /2 ,
or word
.Pq Ql /4
from
.Ar address .
.It Cm w Ns Oo Cm atch Oc Ar address
Set a watchpoint at
.Ar address .
.It Cm w/1 Ar address data
.It Cm w/2 Ar address data
.It Cm w/4 Ar address data
Write
.Ar data
as a byte
.Pq Ql /1 ,
halfword
.Pq Ql /2 ,
or word
.Pq Ql /4
to
.Ar address .
.It Cm w/r Ar register data
Write
.Ar data
as a word to
.Ar register .
.It Cm x/1 Ar address Op Ar count
.It Cm x/2 Ar address Op Ar count
.It Cm x/4 Ar address Op Ar count
Examine
.Ar count
bytes
.Pq Ql /1 ,
halfwords
.Pq Ql /2 ,
or words
.Pq Ql /4
from
.Ar address .
If
.Ar count
is not specified, examine 16 bytes, 8 halfwords, or 4 words.
.El
.Sh FILES
.Bl -tag -width ~/.config/mgba/config.ini -compact
.It Pa ~/.config/mgba/config.ini
Default
.Nm
configuration file.
.It Pa portable.ini
If this file exists in the current directory,
.Nm
will read
.Pa config.ini
from the current directory instead of
.Pa ~/.config/mgba .
.El
.Sh AUTHORS
.An Jeffrey Pfau Aq Mt jeffrey@endrift.com
.Sh HOMEPAGE
.Bl -bullet
.It
.Lk https://mgba.io/ "mGBA homepage"
.It
.Lk https://github.com/mgba-emu/mgba "Development repository"
.It
.Lk https://github.com/mgba-emu/mgba/issues "Bug tracker"
.It
.Lk https://forums.mgba.io/ "Message board"
.El

View File

@ -31,7 +31,7 @@
DEFINE_THUMB_DECODER(NAME, MNEMONIC, \
info->op1.reg = opcode & 0x0007; \
info->memory.baseReg = (opcode >> 3) & 0x0007; \
info->memory.offset.immediate = ((opcode >> 6) & 0x0007) * WIDTH; \
info->memory.offset.immediate = ((opcode >> 6) & 0x001F) * WIDTH; \
info->memory.width = (enum ARMMemoryAccessType) WIDTH; \
info->operandFormat = ARM_OPERAND_REGISTER_1 | \
ARM_OPERAND_AFFECTED_1 | \

View File

@ -18,7 +18,9 @@ static const char* ERROR_OVERFLOW = "Arguments overflow";
static struct CLIDebugger* _activeDebugger;
#ifndef NDEBUG
static void _breakInto(struct CLIDebugger*, struct CLIDebugVector*);
#endif
static void _continue(struct CLIDebugger*, struct CLIDebugVector*);
static void _disassemble(struct CLIDebugger*, struct CLIDebugVector*);
static void _disassembleArm(struct CLIDebugger*, struct CLIDebugVector*);
@ -99,7 +101,9 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
{ "x/1", _dumpByte, CLIDVParse, "Examine bytes at a specified offset" },
{ "x/2", _dumpHalfword, CLIDVParse, "Examine halfwords at a specified offset" },
{ "x/4", _dumpWord, CLIDVParse, "Examine words at a specified offset" },
#ifndef NDEBUG
{ "!", _breakInto, 0, "Break into attached debugger (for developers)" },
#endif
{ 0, 0, 0, 0 }
};
@ -114,6 +118,7 @@ static inline void _printPSR(union PSR psr) {
psr.t ? 'T' : '-');
}
#ifndef NDEBUG
static void _handleDeath(int sig) {
UNUSED(sig);
printf("No debugger attached!\n");
@ -135,6 +140,7 @@ static void _breakInto(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
#endif
sigaction(SIGTRAP, &osa, 0);
}
#endif
static void _continue(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
UNUSED(dv);

View File

@ -167,7 +167,7 @@ void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) {
audio->nextEvent -= cycles;
audio->eventDiff += cycles;
if (audio->nextEvent <= 0) {
while (audio->nextEvent <= 0) {
audio->nextEvent = INT_MAX;
if (audio->enable) {
if (audio->playingCh1 && !audio->ch1.envelope.dead) {

View File

@ -184,6 +184,11 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
int32_t cycles = cpu->nextEvent;
int32_t nextEvent = INT_MAX;
int32_t testEvent;
#ifndef NDEBUG
if (cycles < 0) {
GBALog(gba, GBA_LOG_FATAL, "Negative cycles passed: %i", cycles);
}
#endif
gba->bus = cpu->prefetch[1];
if (cpu->executionMode == MODE_THUMB) {
@ -239,7 +244,7 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
if (timer->enable) {
timer->nextEvent -= cycles;
timer->lastEvent -= cycles;
if (timer->nextEvent <= 0) {
while (timer->nextEvent <= 0) {
timer->lastEvent = timer->nextEvent;
timer->nextEvent += timer->overflowInterval;
gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload;

View File

@ -612,6 +612,7 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer
hw->readWrite = state->hw.readWrite;
hw->pinState = state->hw.pinState;
hw->direction = state->hw.pinDirection;
hw->devices = state->hw.devices;
hw->rtc = state->hw.rtc;
hw->gyroSample = state->hw.gyroSample;
hw->gyroEdge = state->hw.gyroEdge;
@ -624,4 +625,7 @@ void GBAHardwareDeserialize(struct GBACartridgeHardware* hw, const struct GBASer
hw->gbpInputsPosted = state->hw.gbpInputsPosted;
hw->gbpTxPosition = state->hw.gbpTxPosition;
hw->gbpNextEvent = state->hw.gbpNextEvent;
if (hw->devices & HW_GB_PLAYER) {
GBASIOSetDriver(&hw->p->sio, &hw->gbpDriver.d, SIO_NORMAL_32);
}
}

View File

@ -446,8 +446,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
} else {
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address);
LOAD_BAD;
uint32_t v2 = value;
LOAD_16(value, address & 2, &v2);
value = (value >> ((address & 2) * 8)) & 0xFFFF;
}
break;
case REGION_WORKING_RAM:
@ -506,8 +505,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
default:
GBALog(gba, GBA_LOG_GAME_ERROR, "Bad memory Load16: 0x%08X", address);
LOAD_BAD;
uint32_t v2 = value;
LOAD_16(value, address & 2, &v2);
value = (value >> ((address & 2) * 8)) & 0xFFFF;
break;
}
@ -1405,7 +1403,7 @@ int32_t GBAMemoryRunDMAs(struct GBA* gba, int32_t cycles) {
}
memory->nextDMA -= cycles;
memory->eventDiff += cycles;
if (memory->nextDMA <= 0) {
while (memory->nextDMA <= 0) {
struct GBADMA* dma = &memory->dma[memory->activeDMA];
GBAMemoryServiceDMA(gba, memory->activeDMA, dma);
GBAMemoryUpdateDMAs(gba, memory->eventDiff);

View File

@ -87,6 +87,10 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: CPU cycles are negative");
error = true;
}
if (state->cpu.nextEvent < 0) {
GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: Next event is negative");
error = true;
}
if (state->video.eventDiff < 0) {
GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: video eventDiff is negative");
error = true;
@ -99,6 +103,10 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: overflowInterval is negative");
error = true;
}
if (state->timers[0].nextEvent < 0 || state->timers[1].nextEvent < 0 || state->timers[2].nextEvent < 0 || state->timers[3].nextEvent < 0) {
GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: timer nextEvent is negative");
error = true;
}
if (state->audio.eventDiff < 0) {
GBALog(gba, GBA_LOG_WARN, "Savestate is corrupted: audio eventDiff is negative");
error = true;

View File

@ -247,6 +247,7 @@ void GBAConfigMap(const struct GBAConfig* config, struct GBAOptions* opts) {
if (_lookupUIntValue(config, "audioBuffers", &audioBuffers)) {
opts->audioBuffers = audioBuffers;
}
_lookupUIntValue(config, "sampleRate", &opts->sampleRate);
int fakeBool;
if (_lookupIntValue(config, "useBios", &fakeBool)) {
@ -305,6 +306,7 @@ void GBAConfigLoadDefaults(struct GBAConfig* config, const struct GBAOptions* op
ConfigurationSetIntValue(&config->defaultsTable, 0, "rewindBufferInterval", opts->rewindBufferInterval);
ConfigurationSetFloatValue(&config->defaultsTable, 0, "fpsTarget", opts->fpsTarget);
ConfigurationSetUIntValue(&config->defaultsTable, 0, "audioBuffers", opts->audioBuffers);
ConfigurationSetUIntValue(&config->defaultsTable, 0, "sampleRate", opts->sampleRate);
ConfigurationSetIntValue(&config->defaultsTable, 0, "audioSync", opts->audioSync);
ConfigurationSetIntValue(&config->defaultsTable, 0, "videoSync", opts->videoSync);
ConfigurationSetIntValue(&config->defaultsTable, 0, "fullscreen", opts->fullscreen);

View File

@ -29,6 +29,7 @@ struct GBAOptions {
int rewindBufferInterval;
float fpsTarget;
size_t audioBuffers;
unsigned sampleRate;
int fullscreen;
int width;

View File

@ -67,7 +67,7 @@ void GBAVideoReset(struct GBAVideo* video) {
video->lastHblank = 0;
video->nextHblank = VIDEO_HDRAW_LENGTH;
video->nextEvent = video->nextHblank;
video->eventDiff = video->nextEvent;
video->eventDiff = 0;
video->nextHblankIRQ = 0;
video->nextVblankIRQ = 0;

View File

@ -145,7 +145,7 @@
<item row="4" column="1">
<widget class="QLabel" name="extraLinks">
<property name="text">
<string>&lt;a href=&quot;http://mgba.io/&quot;&gt;Website&lt;/a&gt; • &lt;a href=&quot;https://forumsmgba.io/&quot;&gt;Forums / Support&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/tree/{gitBranch}&quot;&gt;Source&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/blob/{gitBranch}/LICENSE&quot;&gt;License&lt;/a&gt;</string>
<string>&lt;a href=&quot;http://mgba.io/&quot;&gt;Website&lt;/a&gt; • &lt;a href=&quot;https://forums.mgba.io/&quot;&gt;Forums / Support&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/tree/{gitBranch}&quot;&gt;Source&lt;/a&gt; • &lt;a href=&quot;https://github.com/mgba-emu/mgba/blob/{gitBranch}/LICENSE&quot;&gt;License&lt;/a&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>

View File

@ -31,6 +31,7 @@ public:
virtual void setInput(GBAThread* input);
int getBufferSamples() const { return m_samples; }
virtual unsigned sampleRate() const = 0;
public slots:
virtual void start() = 0;
@ -39,7 +40,7 @@ public slots:
virtual void setBufferSamples(int samples) = 0;
virtual void inputParametersChanged() = 0;
virtual unsigned sampleRate() const = 0;
virtual void requestSampleRate(unsigned) = 0;
protected:
GBAThread* input() { return m_context; }

View File

@ -20,6 +20,7 @@ AudioProcessorQt::AudioProcessorQt(QObject* parent)
: AudioProcessor(parent)
, m_audioOutput(nullptr)
, m_device(nullptr)
, m_sampleRate(44100)
{
}
@ -45,7 +46,7 @@ void AudioProcessorQt::start() {
if (!m_audioOutput) {
QAudioFormat format;
format.setSampleRate(44100);
format.setSampleRate(m_sampleRate);
format.setChannelCount(2);
format.setSampleSize(16);
format.setCodec("audio/pcm");
@ -84,6 +85,15 @@ void AudioProcessorQt::inputParametersChanged() {
}
}
void AudioProcessorQt::requestSampleRate(unsigned rate) {
m_sampleRate = rate;
if (m_device) {
QAudioFormat format(m_audioOutput->format());
format.setSampleRate(rate);
m_device->setFormat(format);
}
}
unsigned AudioProcessorQt::sampleRate() const {
if (!m_audioOutput) {
return 0;

View File

@ -20,6 +20,7 @@ public:
AudioProcessorQt(QObject* parent = nullptr);
virtual void setInput(GBAThread* input);
virtual unsigned sampleRate() const override;
public slots:
virtual void start();
@ -28,11 +29,12 @@ public slots:
virtual void setBufferSamples(int samples);
virtual void inputParametersChanged();
virtual unsigned sampleRate() const override;
virtual void requestSampleRate(unsigned) override;
private:
QAudioOutput* m_audioOutput;
AudioDevice* m_device;
unsigned m_sampleRate;
};
}

View File

@ -15,7 +15,7 @@ using namespace QGBA;
AudioProcessorSDL::AudioProcessorSDL(QObject* parent)
: AudioProcessor(parent)
, m_audio()
, m_audio{ 2048, 44100 }
{
}
@ -55,6 +55,18 @@ void AudioProcessorSDL::setBufferSamples(int samples) {
void AudioProcessorSDL::inputParametersChanged() {
}
unsigned AudioProcessorSDL::sampleRate() const {
return m_audio.obtainedSpec.freq;
void AudioProcessorSDL::requestSampleRate(unsigned rate) {
m_audio.sampleRate = rate;
if (m_audio.thread) {
GBASDLDeinitAudio(&m_audio);
GBASDLInitAudio(&m_audio, input());
}
}
unsigned AudioProcessorSDL::sampleRate() const {
if (m_audio.thread) {
return m_audio.obtainedSpec.freq;
} else {
return 0;
}
}

View File

@ -22,6 +22,8 @@ public:
AudioProcessorSDL(QObject* parent = nullptr);
~AudioProcessorSDL();
virtual unsigned sampleRate() const override;
public slots:
virtual void start();
virtual void pause();
@ -29,7 +31,7 @@ public slots:
virtual void setBufferSamples(int samples);
virtual void inputParametersChanged();
virtual unsigned sampleRate() const override;
virtual void requestSampleRate(unsigned) override;
private:
GBASDLAudio m_audio;

View File

@ -170,6 +170,9 @@ if(UNIX AND NOT APPLE)
install(CODE "execute_process(COMMAND ${DESKTOP_FILE_INSTALL} \"${CMAKE_SOURCE_DIR}/res/mgba-qt.desktop\" --dir \"$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/share/applications/\")")
endif()
endif()
if(UNIX)
install(FILES ${CMAKE_SOURCE_DIR}/doc/mgba-qt.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-qt)
endif()
if(APPLE OR WIN32)
set_target_properties(${BINARY_NAME}-qt PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
endif()

View File

@ -99,19 +99,29 @@ void CheatsView::removeSet() {
}
void CheatsView::enterCheat(std::function<bool(GBACheatSet*, const char*)> callback) {
GBACheatSet* set;
GBACheatSet* set = nullptr;
QModelIndexList selection = m_ui.cheatList->selectionModel()->selectedIndexes();
if (selection.count() != 1) {
return;
QModelIndex index;
if (selection.count() == 0) {
set = new GBACheatSet;
GBACheatSetInit(set, nullptr);
} else if (selection.count() == 1) {
index = selection[0];
set = m_model.itemAt(index);
}
set = m_model.itemAt(selection[0]);
if (!set) {
return;
}
m_controller->threadInterrupt();
if (selection.count() == 0) {
m_model.addSet(set);
index = m_model.index(m_model.rowCount() - 1, 0, QModelIndex());
m_ui.cheatList->selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
}
QStringList cheats = m_ui.codeEntry->toPlainText().split('\n', QString::SkipEmptyParts);
for (const QString& string : cheats) {
m_model.beginAppendRow(selection[0]);
m_model.beginAppendRow(index);
callback(set, string.toUtf8().constData());
m_model.endAppendRow();
}

View File

@ -107,7 +107,8 @@ ConfigController::ConfigController(QObject* parent)
m_opts.audioSync = GameController::AUDIO_SYNC;
m_opts.videoSync = GameController::VIDEO_SYNC;
m_opts.fpsTarget = 60;
m_opts.audioBuffers = 2048;
m_opts.audioBuffers = 1536;
m_opts.sampleRate = 44100;
m_opts.volume = GBA_AUDIO_VOLUME_MAX;
m_opts.logLevel = GBA_LOG_WARN | GBA_LOG_ERROR | GBA_LOG_FATAL | GBA_LOG_STATUS;
m_opts.rewindEnable = false;

View File

@ -22,7 +22,7 @@ Display::Driver Display::s_driver = Display::Driver::QT;
Display* Display::create(QWidget* parent) {
#ifdef BUILD_GL
QGLFormat format(QGLFormat(QGL::Rgba | QGL::DoubleBuffer));
QGLFormat format(QGLFormat(QGL::Rgba | QGL::SingleBuffer));
format.setSwapInterval(1);
#endif

View File

@ -118,7 +118,8 @@ void DisplayGL::filter(bool filter) {
void DisplayGL::framePosted(const uint32_t* buffer) {
if (m_drawThread && buffer) {
QMetaObject::invokeMethod(m_painter, "setBacking", Q_ARG(const uint32_t*, buffer));
m_painter->enqueue(buffer);
QMetaObject::invokeMethod(m_painter, "draw");
}
}
@ -152,6 +153,19 @@ PainterGL::PainterGL(QGLWidget* parent)
m_backend.d.user = this;
m_backend.d.filter = false;
m_backend.d.lockAspectRatio = false;
for (int i = 0; i < 2; ++i) {
m_free.append(new uint32_t[256 * 256]);
}
}
PainterGL::~PainterGL() {
while (!m_queue.isEmpty()) {
delete[] m_queue.dequeue();
}
for (auto item : m_free) {
delete[] item;
}
}
void PainterGL::setContext(GBAThread* context) {
@ -162,15 +176,6 @@ void PainterGL::setMessagePainter(MessagePainter* messagePainter) {
m_messagePainter = messagePainter;
}
void PainterGL::setBacking(const uint32_t* backing) {
m_gl->makeCurrent();
m_backend.d.postFrame(&m_backend.d, backing);
if (m_active) {
draw();
}
m_gl->doneCurrent();
}
void PainterGL::resize(const QSize& size) {
m_size = size;
if (m_active) {
@ -200,7 +205,11 @@ void PainterGL::start() {
}
void PainterGL::draw() {
if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip)) {
if (m_queue.isEmpty()) {
return;
}
if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip) || !m_queue.isEmpty()) {
dequeue();
m_painter.begin(m_gl->context()->device());
performDraw();
m_painter.end();
@ -209,6 +218,9 @@ void PainterGL::draw() {
} else {
GBASyncWaitFrameEnd(&m_context->sync);
}
if (!m_queue.isEmpty()) {
QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
}
}
void PainterGL::forceDraw() {
@ -221,6 +233,7 @@ void PainterGL::forceDraw() {
void PainterGL::stop() {
m_active = false;
m_gl->makeCurrent();
dequeueAll();
m_backend.d.clear(&m_backend.d);
m_backend.d.swap(&m_backend.d);
m_backend.d.deinit(&m_backend.d);
@ -231,9 +244,6 @@ void PainterGL::stop() {
void PainterGL::pause() {
m_active = false;
// Make sure both buffers are filled
forceDraw();
forceDraw();
}
void PainterGL::unpause() {
@ -250,3 +260,41 @@ void PainterGL::performDraw() {
m_messagePainter->paint(&m_painter);
}
}
void PainterGL::enqueue(const uint32_t* backing) {
m_mutex.lock();
uint32_t* buffer;
if (m_free.isEmpty()) {
buffer = m_queue.dequeue();
} else {
buffer = m_free.takeLast();
}
memcpy(buffer, backing, 256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
m_queue.enqueue(buffer);
m_mutex.unlock();
}
void PainterGL::dequeue() {
m_mutex.lock();
if (m_queue.isEmpty()) {
m_mutex.unlock();
return;
}
uint32_t* buffer = m_queue.dequeue();
m_backend.d.postFrame(&m_backend.d, buffer);
m_free.append(buffer);
m_mutex.unlock();
}
void PainterGL::dequeueAll() {
uint32_t* buffer = 0;
m_mutex.lock();
while (!m_queue.isEmpty()) {
buffer = m_queue.dequeue();
m_free.append(buffer);
}
if (buffer) {
m_backend.d.postFrame(&m_backend.d, buffer);
}
m_mutex.unlock();
}

View File

@ -9,7 +9,9 @@
#include "Display.h"
#include <QGLWidget>
#include <QList>
#include <QMouseEvent>
#include <QQueue>
#include <QThread>
#include <QTimer>
@ -74,12 +76,13 @@ Q_OBJECT
public:
PainterGL(QGLWidget* parent);
~PainterGL();
void setContext(GBAThread*);
void setMessagePainter(MessagePainter*);
void enqueue(const uint32_t* backing);
public slots:
void setBacking(const uint32_t*);
void forceDraw();
void draw();
void start();
@ -92,8 +95,13 @@ public slots:
private:
void performDraw();
void dequeue();
void dequeueAll();
QList<uint32_t*> m_free;
QQueue<uint32_t*> m_queue;
QPainter m_painter;
QMutex m_mutex;
QGLWidget* m_gl;
bool m_active;
GBAThread* m_context;

View File

@ -15,6 +15,7 @@
#include <QIcon>
extern "C" {
#include "gba/supervisor/thread.h"
#include "platform/commandline.h"
#include "util/socket.h"
}
@ -33,10 +34,13 @@ GBAApp::GBAApp(int& argc, char* argv[])
SDL_Init(SDL_INIT_NOPARACHUTE);
#endif
#ifndef Q_OS_MAC
setWindowIcon(QIcon(":/res/mgba-1024.png"));
#endif
SocketSubsystemInit();
qRegisterMetaType<const uint32_t*>("const uint32_t*");
qRegisterMetaType<GBAThread*>("GBAThread*");
QApplication::setApplicationName(projectName);
QApplication::setApplicationVersion(projectVersion);
@ -53,6 +57,7 @@ GBAApp::GBAApp(int& argc, char* argv[])
return;
}
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt()));
Window* w = new Window(&m_configController);
connect(w, &Window::destroyed, [this]() {
m_windows[0] = nullptr;
@ -67,9 +72,6 @@ GBAApp::GBAApp(int& argc, char* argv[])
freeArguments(&args);
w->show();
AudioProcessor::setDriver(static_cast<AudioProcessor::Driver>(m_configController.getQtOption("audioDriver").toInt()));
w->controller()->reloadAudioDriver();
w->controller()->setMultiplayerController(&m_multiplayer);
}

View File

@ -18,7 +18,7 @@
using namespace QGBA;
GDBWindow::GDBWindow(GDBController* controller, QWidget* parent)
: QWidget(parent)
: QDialog(parent)
, m_gdbController(controller)
{
setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint);

View File

@ -6,7 +6,7 @@
#ifndef QGBA_GDB_WINDOW
#define QGBA_GDB_WINDOW
#include <QWidget>
#include <QDialog>
class QLineEdit;
class QPushButton;
@ -15,7 +15,7 @@ namespace QGBA {
class GDBController;
class GDBWindow : public QWidget {
class GDBWindow : public QDialog {
Q_OBJECT
public:

View File

@ -31,7 +31,8 @@ using namespace std;
GameController::GameController(QObject* parent)
: QObject(parent)
, m_drawContext(new uint32_t[256 * 256])
, m_drawContext(new uint32_t[256 * VIDEO_HORIZONTAL_PIXELS])
, m_frontBuffer(new uint32_t[256 * 256])
, m_threadContext()
, m_activeKeys(0)
, m_inactiveKeys(0)
@ -86,7 +87,9 @@ GameController::GameController(QObject* parent)
m_threadContext.startCallback = [](GBAThread* context) {
GameController* controller = static_cast<GameController*>(context->userData);
controller->m_audioProcessor->setInput(context);
if (controller->m_audioProcessor) {
controller->m_audioProcessor->setInput(context);
}
context->gba->luminanceSource = &controller->m_lux;
GBARTCGenericSourceInit(&controller->m_rtc, context->gba);
context->gba->rtcSource = &controller->m_rtc.d;
@ -111,24 +114,25 @@ GameController::GameController(QObject* parent)
vf->truncate(vf, 0);
}
}
controller->gameStarted(context);
QMetaObject::invokeMethod(controller, "gameStarted", Q_ARG(GBAThread*, context));
};
m_threadContext.cleanCallback = [](GBAThread* context) {
GameController* controller = static_cast<GameController*>(context->userData);
controller->gameStopped(context);
QMetaObject::invokeMethod(controller, "gameStopped", Q_ARG(GBAThread*, context));
};
m_threadContext.frameCallback = [](GBAThread* context) {
GameController* controller = static_cast<GameController*>(context->userData);
if (GBASyncDrawingFrame(&controller->m_threadContext.sync)) {
controller->frameAvailable(controller->m_drawContext);
memcpy(controller->m_frontBuffer, controller->m_drawContext, 256 * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL);
QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, controller->m_frontBuffer));
} else {
controller->frameAvailable(nullptr);
QMetaObject::invokeMethod(controller, "frameAvailable", Q_ARG(const uint32_t*, nullptr));
}
if (controller->m_pauseAfterFrame.testAndSetAcquire(true, false)) {
GBAThreadPauseFromThread(context);
controller->gamePaused(&controller->m_threadContext);
QMetaObject::invokeMethod(controller, "gamePaused", Q_ARG(GBAThread*, context));
}
};
@ -157,7 +161,7 @@ GameController::GameController(QObject* parent)
va_copy(argc, args);
int immediate = va_arg(argc, int);
va_end(argc);
controller->unimplementedBiosCall(immediate);
QMetaObject::invokeMethod(controller, "unimplementedBiosCall", Q_ARG(int, immediate));
} else if (level == GBA_LOG_STATUS) {
// Slot 0 is reserved for suspend points
if (strncmp(savestateMessage, format, strlen(savestateMessage)) == 0) {
@ -185,9 +189,9 @@ GameController::GameController(QObject* parent)
}
QString message(QString().vsprintf(format, args));
if (level == GBA_LOG_STATUS) {
controller->statusPosted(message);
QMetaObject::invokeMethod(controller, "statusPosted", Q_ARG(const QString&, message));
}
controller->postLog(level, message);
QMetaObject::invokeMethod(controller, "postLog", Q_ARG(int, level), Q_ARG(const QString&, message));
};
connect(&m_rewindTimer, &QTimer::timeout, [this]() {
@ -216,6 +220,7 @@ GameController::~GameController() {
GBACheatDeviceDestroy(&m_cheatDevice);
delete m_renderer;
delete[] m_drawContext;
delete[] m_frontBuffer;
delete m_backupLoadState;
}
@ -339,7 +344,7 @@ void GameController::openGame(bool biosOnly) {
}
m_inputController->recalibrateAxes();
memset(m_drawContext, 0xF8, 1024 * 256);
memset(m_drawContext, 0xF8, 1024 * VIDEO_HORIZONTAL_PIXELS);
if (!GBAThreadStart(&m_threadContext)) {
m_gameOpen = false;
@ -530,9 +535,9 @@ void GameController::startRewinding() {
return;
}
m_wasPaused = isPaused();
bool signalsBlocked = blockSignals(true);
setPaused(true);
blockSignals(signalsBlocked);
if (!GBAThreadIsPaused(&m_threadContext)) {
GBAThreadPause(&m_threadContext);
}
m_rewindTimer.start();
}
@ -585,10 +590,24 @@ void GameController::clearKeys() {
}
void GameController::setAudioBufferSamples(int samples) {
threadInterrupt();
redoSamples(samples);
threadContinue();
QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
if (m_audioProcessor) {
threadInterrupt();
redoSamples(samples);
threadContinue();
QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Q_ARG(int, samples));
}
}
void GameController::setAudioSampleRate(unsigned rate) {
if (!rate) {
return;
}
if (m_audioProcessor) {
threadInterrupt();
redoSamples(m_audioProcessor->getBufferSamples());
threadContinue();
QMetaObject::invokeMethod(m_audioProcessor, "requestSampleRate", Q_ARG(unsigned, rate));
}
}
void GameController::setAudioChannelEnabled(int channel, bool enable) {
@ -641,7 +660,9 @@ void GameController::setFPSTarget(float fps) {
if (m_turbo && m_turboSpeed > 0) {
m_threadContext.fpsTarget *= m_turboSpeed;
}
redoSamples(m_audioProcessor->getBufferSamples());
if (m_audioProcessor) {
redoSamples(m_audioProcessor->getBufferSamples());
}
threadContinue();
}
@ -652,9 +673,14 @@ void GameController::setSkipBIOS(bool set) {
}
void GameController::setUseBIOS(bool use) {
threadInterrupt();
if (use == m_useBios) {
return;
}
m_useBios = use;
threadContinue();
if (m_gameOpen) {
closeGame();
openGame();
}
}
void GameController::loadState(int slot) {
@ -798,7 +824,9 @@ void GameController::enableTurbo() {
m_threadContext.sync.audioWait = true;
m_threadContext.sync.videoFrameWait = false;
}
redoSamples(m_audioProcessor->getBufferSamples());
if (m_audioProcessor) {
redoSamples(m_audioProcessor->getBufferSamples());
}
threadContinue();
}
@ -827,11 +855,21 @@ void GameController::screenshot() {
#endif
void GameController::reloadAudioDriver() {
QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
int samples = m_audioProcessor->getBufferSamples();
delete m_audioProcessor;
int samples = 0;
unsigned sampleRate = 0;
if (m_audioProcessor) {
QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
samples = m_audioProcessor->getBufferSamples();
sampleRate = m_audioProcessor->sampleRate();
delete m_audioProcessor;
}
m_audioProcessor = AudioProcessor::create();
m_audioProcessor->setBufferSamples(samples);
if (samples) {
m_audioProcessor->setBufferSamples(samples);
}
if (sampleRate) {
m_audioProcessor->requestSampleRate(sampleRate);
}
m_audioProcessor->moveToThread(m_audioThread);
connect(this, SIGNAL(gameStarted(GBAThread*)), m_audioProcessor, SLOT(start()));
connect(this, SIGNAL(gameStopped(GBAThread*)), m_audioProcessor, SLOT(pause()));

View File

@ -119,6 +119,7 @@ public slots:
void keyReleased(int key);
void clearKeys();
void setAudioBufferSamples(int samples);
void setAudioSampleRate(unsigned rate);
void setAudioChannelEnabled(int channel, bool enable = true);
void setVideoLayerEnabled(int layer, bool enable = true);
void setFPSTarget(float fps);
@ -167,6 +168,7 @@ private:
void enableTurbo();
uint32_t* m_drawContext;
uint32_t* m_frontBuffer;
GBAThread m_threadContext;
GBAVideoSoftwareRenderer* m_renderer;
GBACheatDevice m_cheatDevice;

View File

@ -26,6 +26,7 @@ LoadSaveState::LoadSaveState(GameController* controller, QWidget* parent)
, m_currentFocus(controller->stateSlot() - 1)
, m_mode(LoadSave::LOAD)
{
setAttribute(Qt::WA_TranslucentBackground);
m_ui.setupUi(this);
m_slots[0] = m_ui.state1;
@ -208,6 +209,5 @@ void LoadSaveState::showEvent(QShowEvent* event) {
void LoadSaveState::paintEvent(QPaintEvent*) {
QPainter painter(this);
QRect full(QPoint(), size());
painter.drawPixmap(full, m_currentImage);
painter.fillRect(full, QColor(0, 0, 0, 128));
}

View File

@ -15,7 +15,7 @@ extern "C" {
using namespace QGBA;
OverrideView::OverrideView(GameController* controller, ConfigController* config, QWidget* parent)
: QWidget(parent)
: QDialog(parent)
, m_controller(controller)
, m_config(config)
{

View File

@ -6,7 +6,7 @@
#ifndef QGBA_OVERRIDE_VIEW
#define QGBA_OVERRIDE_VIEW
#include <QWidget>
#include <QDialog>
#include "ui_OverrideView.h"
@ -21,7 +21,7 @@ namespace QGBA {
class ConfigController;
class GameController;
class OverrideView : public QWidget {
class OverrideView : public QDialog {
Q_OBJECT
public:

View File

@ -13,7 +13,7 @@ using namespace QGBA;
SavestateButton::SavestateButton(QWidget* parent)
: QAbstractButton(parent)
{
// Nothing to do
setAttribute(Qt::WA_TranslucentBackground);
}
void SavestateButton::paintEvent(QPaintEvent*) {

View File

@ -12,7 +12,7 @@
using namespace QGBA;
SensorView::SensorView(GameController* controller, InputController* input, QWidget* parent)
: QWidget(parent)
: QDialog(parent)
, m_controller(controller)
, m_input(input)
, m_rotation(input->rotationSource())

View File

@ -7,7 +7,7 @@
#define QGBA_SENSOR_VIEW
#include <QTimer>
#include <QWidget>
#include <QDialog>
#include <functional>
@ -22,7 +22,7 @@ class GameController;
class GamepadAxisEvent;
class InputController;
class SensorView : public QWidget {
class SensorView : public QDialog {
Q_OBJECT
public:

View File

@ -13,8 +13,8 @@
using namespace QGBA;
SettingsView::SettingsView(ConfigController* controller, QWidget* parent)
: QWidget(parent)
, m_controller(controller)
: QDialog(parent)
, m_controller(controller)
{
m_ui.setupUi(this);
@ -22,6 +22,7 @@ SettingsView::SettingsView(ConfigController* controller, QWidget* parent)
loadSetting("useBios", m_ui.useBios);
loadSetting("skipBios", m_ui.skipBios);
loadSetting("audioBuffers", m_ui.audioBufferSize);
loadSetting("sampleRate", m_ui.sampleRate);
loadSetting("videoSync", m_ui.videoSync);
loadSetting("audioSync", m_ui.audioSync);
loadSetting("frameskip", m_ui.frameskip);
@ -102,6 +103,7 @@ void SettingsView::updateConfig() {
saveSetting("useBios", m_ui.useBios);
saveSetting("skipBios", m_ui.skipBios);
saveSetting("audioBuffers", m_ui.audioBufferSize);
saveSetting("sampleRate", m_ui.sampleRate);
saveSetting("videoSync", m_ui.videoSync);
saveSetting("audioSync", m_ui.audioSync);
saveSetting("frameskip", m_ui.frameskip);

View File

@ -6,7 +6,7 @@
#ifndef QGBA_SETTINGS_VIEW
#define QGBA_SETTINGS_VIEW
#include <QWidget>
#include <QDialog>
#include "ui_SettingsView.h"
@ -14,7 +14,7 @@ namespace QGBA {
class ConfigController;
class SettingsView : public QWidget {
class SettingsView : public QDialog {
Q_OBJECT
public:

View File

@ -62,26 +62,41 @@
<bool>true</bool>
</property>
<property name="currentText">
<string>2048</string>
<string>1536</string>
</property>
<property name="currentIndex">
<number>2</number>
<number>3</number>
</property>
<item>
<property name="text">
<string>512</string>
</property>
</item>
<item>
<property name="text">
<string>768</string>
</property>
</item>
<item>
<property name="text">
<string>1024</string>
</property>
</item>
<item>
<property name="text">
<string>1536</string>
</property>
</item>
<item>
<property name="text">
<string>2048</string>
</property>
</item>
<item>
<property name="text">
<string>3072</string>
</property>
</item>
<item>
<property name="text">
<string>4096</string>
@ -99,13 +114,20 @@
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Sample rate:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Volume:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QSlider" name="volume">
@ -132,28 +154,38 @@
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<item row="4" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Display driver:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<item row="5" column="1">
<widget class="QComboBox" name="displayDriver">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Frameskip:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_16">
<item>
<widget class="QLabel" name="label_12">
@ -174,14 +206,14 @@
</item>
</layout>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>FPS target:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QDoubleSpinBox" name="fpsTarget">
@ -205,21 +237,21 @@
</item>
</layout>
</item>
<item row="7" column="0" colspan="2">
<item row="8" column="0" colspan="2">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="8" column="0">
<item row="9" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Sync:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<item row="9" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QCheckBox" name="videoSync">
@ -237,29 +269,63 @@
</item>
</layout>
</item>
<item row="9" column="1">
<item row="10" column="1">
<widget class="QCheckBox" name="lockAspectRatio">
<property name="text">
<string>Lock aspect ratio</string>
</property>
</widget>
</item>
<item row="10" column="1">
<item row="11" column="1">
<widget class="QCheckBox" name="resampleVideo">
<property name="text">
<string>Resample video</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="displayDriver">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_14">
<item>
<widget class="QComboBox" name="sampleRate">
<property name="editable">
<bool>true</bool>
</property>
<property name="currentText">
<string>44100</string>
</property>
<property name="currentIndex">
<number>2</number>
</property>
<item>
<property name="text">
<string>22050</string>
</property>
</item>
<item>
<property name="text">
<string>32000</string>
</property>
</item>
<item>
<property name="text">
<string>44100</string>
</property>
</item>
<item>
<property name="text">
<string>48000</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_20">
<property name="text">
<string>Hz</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>

View File

@ -100,6 +100,14 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
connect(m_controller, SIGNAL(gameStopped(GBAThread*)), &m_inputController, SLOT(resumeScreensaver()));
connect(m_controller, SIGNAL(stateLoaded(GBAThread*)), m_display, SLOT(forceDraw()));
connect(m_controller, SIGNAL(rewound(GBAThread*)), m_display, SLOT(forceDraw()));
connect(m_controller, &GameController::gamePaused, [this]() {
QImage currentImage(reinterpret_cast<const uchar*>(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS,
VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGBX8888);
QPixmap pixmap;
pixmap.convertFromImage(currentImage);
m_screenWidget->setPixmap(pixmap);
m_screenWidget->setLockAspectRatio(3, 2);
});
connect(m_controller, SIGNAL(gamePaused(GBAThread*)), m_display, SLOT(pauseDrawing()));
#ifndef Q_OS_MAC
connect(m_controller, SIGNAL(gamePaused(GBAThread*)), menuBar(), SLOT(show()));
@ -127,6 +135,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
connect(this, SIGNAL(shutdown()), m_controller, SLOT(closeGame()));
connect(this, SIGNAL(shutdown()), m_logView, SLOT(hide()));
connect(this, SIGNAL(audioBufferSamplesChanged(int)), m_controller, SLOT(setAudioBufferSamples(int)));
connect(this, SIGNAL(sampleRateChanged(unsigned)), m_controller, SLOT(setAudioSampleRate(unsigned)));
connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float)));
connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS()));
connect(m_display, &Display::hideCursor, [this]() {
@ -195,6 +204,7 @@ void Window::loadConfig() {
m_controller->loadBIOS(opts->bios);
}
// TODO: Move these to ConfigController
if (opts->fpsTarget) {
emit fpsTargetChanged(opts->fpsTarget);
}
@ -203,6 +213,10 @@ void Window::loadConfig() {
emit audioBufferSamplesChanged(opts->audioBuffers);
}
if (opts->sampleRate) {
emit sampleRateChanged(opts->sampleRate);
}
if (opts->width && opts->height) {
resizeFrame(opts->width, opts->height);
}
@ -393,9 +407,7 @@ void Window::gdbOpen() {
m_gdbController = new GDBController(m_controller, this);
}
GDBWindow* window = new GDBWindow(m_gdbController);
connect(this, SIGNAL(shutdown()), window, SLOT(close()));
window->setAttribute(Qt::WA_DeleteOnClose);
window->show();
openView(window);
}
#endif
@ -673,7 +685,7 @@ void Window::openStateWindow(LoadSave ls) {
connect(this, SIGNAL(shutdown()), m_stateWindow, SLOT(close()));
connect(m_controller, SIGNAL(gameStopped(GBAThread*)), m_stateWindow, SLOT(close()));
connect(m_stateWindow, &LoadSaveState::closed, [this]() {
m_screenWidget->layout()->removeWidget(m_stateWindow);
detachWidget(m_stateWindow);
m_stateWindow = nullptr;
QMetaObject::invokeMethod(this, "setFocus", Qt::QueuedConnection);
});
@ -823,13 +835,6 @@ void Window::setupMenu(QMenuBar* menubar) {
connect(pause, SIGNAL(triggered(bool)), m_controller, SLOT(setPaused(bool)));
connect(m_controller, &GameController::gamePaused, [this, pause]() {
pause->setChecked(true);
QImage currentImage(reinterpret_cast<const uchar*>(m_controller->drawContext()), VIDEO_HORIZONTAL_PIXELS,
VIDEO_VERTICAL_PIXELS, 1024, QImage::Format_RGB32);
QPixmap pixmap;
pixmap.convertFromImage(currentImage.rgbSwapped());
m_screenWidget->setPixmap(pixmap);
m_screenWidget->setLockAspectRatio(3, 2);
});
connect(m_controller, &GameController::gameUnpaused, [pause]() { pause->setChecked(false); });
m_gameActions.append(pause);
@ -939,9 +944,12 @@ void Window::setupMenu(QMenuBar* menubar) {
for (int i = 1; i <= 6; ++i) {
QAction* setSize = new QAction(tr("%1x").arg(QString::number(i)), avMenu);
setSize->setCheckable(true);
connect(setSize, &QAction::triggered, [this, i]() {
connect(setSize, &QAction::triggered, [this, i, setSize]() {
showNormal();
resizeFrame(VIDEO_HORIZONTAL_PIXELS * i, VIDEO_VERTICAL_PIXELS * i);
bool enableSignals = setSize->blockSignals(true);
setSize->setChecked(true);
setSize->blockSignals(enableSignals);
});
m_frameSizes[i] = setSize;
addControlledAction(frameMenu, setSize, QString("frame%1x").arg(QString::number(i)));
@ -980,20 +988,6 @@ void Window::setupMenu(QMenuBar* menubar) {
avMenu->addSeparator();
QMenu* buffersMenu = avMenu->addMenu(tr("Audio buffer &size"));
ConfigOption* buffers = m_config->addOption("audioBuffers");
buffers->connect([this](const QVariant& value) {
emit audioBufferSamplesChanged(value.toInt());
}, this);
buffers->addValue(tr("512"), 512, buffersMenu);
buffers->addValue(tr("768"), 768, buffersMenu);
buffers->addValue(tr("1024"), 1024, buffersMenu);
buffers->addValue(tr("2048"), 2048, buffersMenu);
buffers->addValue(tr("4096"), 4096, buffersMenu);
m_config->updateOption("audioBuffers");
avMenu->addSeparator();
QMenu* target = avMenu->addMenu(tr("FPS target"));
ConfigOption* fpsTargetOption = m_config->addOption("fpsTarget");
fpsTargetOption->connect([this](const QVariant& value) {
@ -1129,6 +1123,21 @@ void Window::setupMenu(QMenuBar* menubar) {
m_controller->setSkipBIOS(value.toBool());
}, this);
ConfigOption* useBios = m_config->addOption("useBios");
useBios->connect([this](const QVariant& value) {
m_controller->setUseBIOS(value.toBool());
}, this);
ConfigOption* buffers = m_config->addOption("audioBuffers");
buffers->connect([this](const QVariant& value) {
emit audioBufferSamplesChanged(value.toInt());
}, this);
ConfigOption* sampleRate = m_config->addOption("sampleRate");
sampleRate->connect([this](const QVariant& value) {
emit sampleRateChanged(value.toUInt());
}, this);
ConfigOption* volume = m_config->addOption("volume");
volume->connect([this](const QVariant& value) {
m_controller->setVolume(value.toInt());
@ -1171,7 +1180,7 @@ void Window::setupMenu(QMenuBar* menubar) {
void Window::attachWidget(QWidget* widget) {
m_screenWidget->layout()->addWidget(widget);
unsetCursor();
m_screenWidget->unsetCursor();
static_cast<QStackedLayout*>(m_screenWidget->layout())->setCurrentWidget(widget);
}

View File

@ -54,6 +54,7 @@ signals:
void startDrawing(GBAThread*);
void shutdown();
void audioBufferSamplesChanged(int samples);
void sampleRateChanged(unsigned samples);
void fpsTargetChanged(float target);
public slots:

View File

@ -84,3 +84,6 @@ set_target_properties(${BINARY_NAME}-sdl PROPERTIES COMPILE_DEFINITIONS "${FEATU
target_link_libraries(${BINARY_NAME}-sdl ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
set_target_properties(${BINARY_NAME}-sdl PROPERTIES OUTPUT_NAME ${BINARY_NAME})
install(TARGETS ${BINARY_NAME}-sdl DESTINATION bin COMPONENT ${BINARY_NAME}-sdl)
if(UNIX)
install(FILES ${CMAKE_SOURCE_DIR}/doc/mgba.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-sdl)
endif()

View File

@ -112,6 +112,10 @@ int main(int argc, char** argv) {
bool didFail = false;
renderer.audio.samples = context.audioBuffers;
renderer.audio.sampleRate = 44100;
if (opts.sampleRate) {
renderer.audio.sampleRate = opts.sampleRate;
}
if (!GBASDLInitAudio(&renderer.audio, &context)) {
didFail = true;
}

View File

@ -22,7 +22,7 @@ bool GBASDLInitAudio(struct GBASDLAudio* context, struct GBAThread* threadContex
return false;
}
context->desiredSpec.freq = 44100;
context->desiredSpec.freq = context->sampleRate;
context->desiredSpec.format = AUDIO_S16SYS;
context->desiredSpec.channels = 2;
context->desiredSpec.samples = context->samples;

View File

@ -15,6 +15,7 @@
struct GBASDLAudio {
// Input
size_t samples;
unsigned sampleRate;
// State
SDL_AudioSpec desiredSpec;

View File

@ -9,20 +9,20 @@
int ftostr_l(char* restrict str, size_t size, float f, locale_t locale) {
#ifdef HAVE_SNPRINTF_L
return snprintf_l(str, size, locale, "%*.g", FLT_DIG, f);
return snprintf_l(str, size, locale, "%.*g", FLT_DIG, f);
#elif defined(HAVE_LOCALE)
locale_t old = uselocale(locale);
int res = snprintf(str, size, "%*.g", FLT_DIG, f);
int res = snprintf(str, size, "%.*g", FLT_DIG, f);
uselocale(old);
return res;
#elif defined(HAVE_SETLOCALE)
char* old = setlocale(LC_NUMERIC, locale);
int res = snprintf(str, size, "%*.g", FLT_DIG, f);
int res = snprintf(str, size, "%.*g", FLT_DIG, f);
setlocale(LC_NUMERIC, old);
return res;
#else
UNUSED(locale);
return snprintf(str, size, "%*.g", FLT_DIG, f);
return snprintf(str, size, "%.*g", FLT_DIG, f);
#endif
}

5
tools/debian/changelog Normal file
View File

@ -0,0 +1,5 @@
mgba (0.3.0-1) UNRELEASED; urgency=low
* Initial release (closes: Bug#787470).
-- Sérgio Benjamim <sergio_br2@yahoo.com.br> Mon, 17 Aug 2015 18:40:00 -0300

2
tools/debian/clean Normal file
View File

@ -0,0 +1,2 @@
debian/libmgba.install
debian/libretro-mgba.install

1
tools/debian/compat Normal file
View File

@ -0,0 +1 @@
9

78
tools/debian/control Normal file
View File

@ -0,0 +1,78 @@
Source: mgba
Section: otherosfs
Priority: extra
Maintainer: Sérgio Benjamim <sergio_br2@yahoo.com.br>
Build-Depends: cmake (>= 2.8.11),
debhelper (>= 9),
libavcodec-dev,
libavformat-dev,
libavresample-dev,
libavutil-dev,
libedit-dev,
libmagickwand-dev,
libpng-dev,
libqt5opengl5-dev,
libsdl2-dev,
libswscale-dev,
libzip-dev,
pkg-config,
qtbase5-dev,
qtmultimedia5-dev,
zlib1g-dev
Standards-Version: 3.9.6
Homepage: http://mgba.io/
Package: libmgba
Architecture: any
Multi-Arch: same
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: Game Boy Advance emulator (common library for mGBA)
mGBA is a new emulator for running Game Boy Advance games. It aims to be faster
and more accurate than many existing Game Boy Advance emulators, as well as
adding features that other emulators lack.
.
This package provides the common library for mGBA.
.
Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is
not affiliated with or endorsed by any of the companies mentioned.
Package: mgba-qt
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: Game Boy Advance emulator (Qt frontend for mGBA)
mGBA is a new emulator for running Game Boy Advance games. It aims to be faster
and more accurate than many existing Game Boy Advance emulators, as well as
adding features that other emulators lack.
.
This package provides the Qt GUI frontend for mGBA.
.
Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is
not affiliated with or endorsed by any of the companies mentioned.
Package: mgba-sdl
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: Game Boy Advance emulator (SDL frontend for mGBA)
mGBA is a new emulator for running Game Boy Advance games. It aims to be faster
and more accurate than many existing Game Boy Advance emulators, as well as
adding features that other emulators lack.
.
This package provides the SDL UI console for mGBA.
.
Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is
not affiliated with or endorsed by any of the companies mentioned.
Package: libretro-mgba
Architecture: any
Multi-Arch: same
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: Libretro wrapper for mGBA
This wrapper makes mGBA API compatible with libretro, thus allowing its use
with libretro frontends, such as RetroArch.
.
mGBA is a new emulator for running Game Boy Advance games. It aims to be faster
and more accurate than many existing Game Boy Advance emulators, as well as
adding features that other emulators lack.
.
Game Boy Advance is a registered trademark of Nintendo of America Inc. mGBA is
not affiliated with or endorsed by any of the companies mentioned.

472
tools/debian/copyright Normal file
View File

@ -0,0 +1,472 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: mGBA
Upstream-Contact: Jeffrey Pfau (aka endrift) <jeffrey@endrift.com>
Source: https://github.com/mgba-emu/mgba
Comment: This package was debianized by
Sergio Benjamim (sergio-br2) <sergio_br2@yahoo.com.br> on Mon, 01 Jun 2015 18:40:00 -0300
Files: *
Copyright: 2013-2015 Jeffrey Pfau
License: MPL-2.0
Files: src/third-party/blip_buf/*
Copyright: 2003-2009 Shay Green
License: LGPL-2.1+
Files: src/third-party/inih/*
Copyright: 2009 Brush Technology. All rights reserved.
License: BSD-3-clause-Brush-Technology
Files: src/third-party/lzma/*
Copyright: 2008-2015 Igor Pavlov
License: public-domain
These files have been put in the public domain by their author
Files: src/platform/libretro/libretro.h
Copyright: 2010-2015 The RetroArch Team
License: Expat
Files: debian/*
Copyright: 2015 Sergio Benjamim (sergio-br2) <sergio_br2@yahoo.com.br>
License: MPL-2.0
License: MPL-2.0
Mozilla Public License Version 2.0
==================================
.
1. Definitions
--------------
.
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
.
1.3. "Contribution"
means Covered Software of a particular Contributor.
.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
.
1.5. "Incompatible With Secondary Licenses"
means
.
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
.
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
.
1.8. "License"
means this document.
.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
.
1.10. "Modifications"
means any of the following:
.
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
.
(b) any new file in Source Code Form that contains any Covered
Software.
.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
.
2. License Grants and Conditions
--------------------------------
.
2.1. Grants
.
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
.
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
.
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
.
2.2. Effective Date
.
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
.
2.3. Limitations on Grant Scope
.
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
.
(a) for any code that a Contributor has removed from Covered Software;
or
.
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
.
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
.
2.4. Subsequent Licenses
.
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
.
2.5. Representation
.
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
.
2.6. Fair Use
.
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
.
2.7. Conditions
.
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
.
3. Responsibilities
-------------------
.
3.1. Distribution of Source Form
.
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
.
3.2. Distribution of Executable Form
.
If You distribute Covered Software in Executable Form then:
.
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
.
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
.
3.3. Distribution of a Larger Work
.
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
.
3.4. Notices
.
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
.
3.5. Application of Additional Terms
.
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
.
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
.
5. Termination
--------------
.
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
.
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
.
8. Litigation
-------------
.
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
.
9. Miscellaneous
----------------
.
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
.
10. Versions of the License
---------------------------
.
10.1. New Versions
.
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
.
10.2. Effect of New Versions
.
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
.
10.3. Modified Versions
.
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
.
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
.
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
.
Exhibit A - Source Code Form License Notice
-------------------------------------------
.
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/.
.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
.
You may add additional accurate notices of copyright ownership.
.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
.
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.
License: BSD-3-clause-Brush-Technology
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Brush Technology nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
.
THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
License: Expat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
License: LGPL-2.1+
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
.
On Debian systems, the full text of the GNU Lesser General Public
License version 2.1 can be found in the file
`/usr/share/common-licenses/LGPL-2.1'.

2
tools/debian/docs Normal file
View File

@ -0,0 +1,2 @@
README.md
CHANGES

View File

@ -0,0 +1 @@
obj/libmgba.so* usr/lib/@DEB_HOST_MULTIARCH@/

View File

@ -0,0 +1 @@
obj/mgba_libretro.so usr/lib/@DEB_HOST_MULTIARCH@/libretro

View File

@ -0,0 +1,3 @@
usr/bin/mgba-qt
res/mgba-qt.desktop usr/share/applications
usr/share/icons

View File

@ -0,0 +1 @@
doc/mgba-qt.6

View File

@ -0,0 +1 @@
usr/bin/mgba

View File

@ -0,0 +1 @@
doc/mgba.6

27
tools/debian/rules Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/make -f
# Copyright (C) 2015 Sergio Benjamim
# 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/. */
DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH)
ARCH=$(shell dpkg-architecture -qDEB_HOST_ARCH)
ifeq ($(ARCH),armhf)
ARM=-DBUILD_GL=OFF -DBUILD_GLES2=ON
endif
%:
dh $@ --buildsystem=cmake --builddirectory=obj --parallel
override_dh_auto_configure:
dh_auto_configure -- -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_SKIP_RPATH=ON -DBUILD_LIBRETRO=ON $(ARM)
sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' \
debian/libretro-mgba.install.in > debian/libretro-mgba.install
sed 's/@DEB_HOST_MULTIARCH@/$(DEB_HOST_MULTIARCH)/g' \
debian/libmgba.install.in > debian/libmgba.install
override_dh_installchangelogs:
dh_installchangelogs -k CHANGES

View File

@ -0,0 +1 @@
3.0 (quilt)

3
tools/debian/watch Normal file
View File

@ -0,0 +1,3 @@
version=3
opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/mgba-$1\.tar\.gz/ \
https://github.com/mgba-emu/mgba/tags .*/v?(\d\S*)\.tar\.gz

View File

@ -32,6 +32,16 @@ else()
endif()
endif()
if(NOT GIT_COMMIT)
set(GIT_COMMIT "(unknown)")
endif()
if(NOT GIT_COMMIT_SHORT)
set(GIT_COMMIT_SHORT "(unknown)")
endif()
if(NOT GIT_BRANCH)
set(GIT_BRANCH "(unknown)")
endif()
if(CONFIG_FILE AND OUT_FILE)
configure_file("${CONFIG_FILE}" "${OUT_FILE}")
endif()