diff --git a/.appveyor.yml b/.appveyor.yml index 4e8f3457a..3067d854c 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,7 +9,7 @@ cache: install: - git -C C:\Tools\vcpkg pull --quiet - C:\Tools\vcpkg\bootstrap-vcpkg -- vcpkg --triplet x64-windows install ffmpeg libepoxy libpng libzip sdl2 sqlite3 +- vcpkg --triplet x64-windows --recurse install ffmpeg libepoxy libpng libzip sdl2 sqlite3 - vcpkg --no-dry-run upgrade - rd /Q /S C:\Tools\vcpkg\buildtrees before_build: diff --git a/CHANGES b/CHANGES index d12ece948..620cc609b 100644 --- a/CHANGES +++ b/CHANGES @@ -44,9 +44,13 @@ Emulation fixes: - ARM: Fix ALU reading PC after shifting - ARM: Fix STR storing PC after address calculation - GB: Partially fix timing for skipped BIOS + - GB Audio: Fix initial sweep state + - GB Audio: Fix serializing sweep time + - GB Audio: Fix deserializing audio channels 2 and 3 - GB MBC: Fix MBC1 mode changing behavior - GB Video: Fix state after skipping BIOS (fixes mgba.io/i/1715 and mgba.io/i/1716) - GBA: Fix timing advancing too quickly in rare cases + - GBA Audio: Fix deserializing SOUNDCNT_L - GBA BIOS: Implement dummy sound driver calls - GBA BIOS: Improve HLE BIOS timing - GBA BIOS: Fix reloading video registers after reset (fixes mgba.io/i/1808) @@ -56,6 +60,7 @@ Emulation fixes: - GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190) - GBA SIO: Fix copying Normal mode transfer values - GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800) + - GBA SIO: Fix deseralizing SIO registers - GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319) - GBA Video: Fix Hblank timing - GBA Video: Invalidate map cache when modifying BGCNT (fixes mgba.io/i/1846) @@ -65,9 +70,11 @@ Other fixes: - 3DS: Fix crash with libctru 2.0 when exiting - All: Improve export headers (fixes mgba.io/i/1738) - Core: Ensure ELF regions can be written before trying + - Core: Fix reported ROM size when a fixed buffer size is used - Debugger: Don't skip undefined instructions when debugger attached - FFmpeg: Fix some small memory leaks - FFmpeg: Fix encoding of time base + - GBA: Disable more checks when loading GS save with checks disabled (fixes mgba.io/i/1851) - Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642) - Qt: Fix static compilation in MinGW (fixes mgba.io/i/1769) - Qt: Fix a race condition in the frame inspector @@ -82,6 +89,7 @@ Misc: - GBA: Allow pausing event loop while CPU is blocked - Debugger: Keep track of global cycle count - FFmpeg: Add looping option for GIF/APNG + - mGUI: Show battery percentage - Qt: Renderer can be changed while a game is running - Qt: Add hex index to palette view - Qt: Add transformation matrix info to sprite view diff --git a/include/mgba-util/gui.h b/include/mgba-util/gui.h index a13251bc6..29c0638f8 100644 --- a/include/mgba-util/gui.h +++ b/include/mgba-util/gui.h @@ -42,13 +42,15 @@ enum GUICursorState { enum { BATTERY_EMPTY = 0, - BATTERY_LOW = 1, - BATTERY_HALF = 2, - BATTERY_HIGH = 3, - BATTERY_FULL = 4, + BATTERY_LOW = 25, + BATTERY_HALF = 50, + BATTERY_HIGH = 75, + BATTERY_FULL = 100, + BATTERY_VALUE = 0x7F, + BATTERY_PERCENTAGE_VALID = 0x80, - BATTERY_CHARGING = 8, - BATTERY_NOT_PRESENT = 16 + BATTERY_CHARGING = 0x100, + BATTERY_NOT_PRESENT = 0x200, }; struct GUIBackground { diff --git a/include/mgba/core/timing.h b/include/mgba/core/timing.h index 10a64171e..3db79493c 100644 --- a/include/mgba/core/timing.h +++ b/include/mgba/core/timing.h @@ -35,6 +35,7 @@ void mTimingInit(struct mTiming* timing, int32_t* relativeCycles, int32_t* nextE void mTimingDeinit(struct mTiming* timing); void mTimingClear(struct mTiming* timing); void mTimingSchedule(struct mTiming* timing, struct mTimingEvent*, int32_t when); +void mTimingScheduleAbsolute(struct mTiming* timing, struct mTimingEvent*, int32_t when); void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent*); bool mTimingIsScheduled(const struct mTiming* timing, const struct mTimingEvent*); int32_t mTimingTick(struct mTiming* timing, int32_t cycles); diff --git a/include/mgba/internal/arm/decoder.h b/include/mgba/internal/arm/decoder.h index 8e245f8c7..6d21f5ae7 100644 --- a/include/mgba/internal/arm/decoder.h +++ b/include/mgba/internal/arm/decoder.h @@ -250,6 +250,7 @@ void ARMDecodeThumb(uint16_t opcode, struct ARMInstructionInfo* info); bool ARMDecodeThumbCombine(struct ARMInstructionInfo* info1, struct ARMInstructionInfo* info2, struct ARMInstructionInfo* out); int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, int blen); +uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc); CXX_GUARD_END diff --git a/include/mgba/internal/arm/isa-inlines.h b/include/mgba/internal/arm/isa-inlines.h index 22dde1c9c..93659b6d7 100644 --- a/include/mgba/internal/arm/isa-inlines.h +++ b/include/mgba/internal/arm/isa-inlines.h @@ -142,4 +142,26 @@ static inline bool ARMTestCondition(struct ARMCore* cpu, unsigned condition) { } } +static inline enum RegisterBank ARMSelectBank(enum PrivilegeMode mode) { + switch (mode) { + case MODE_USER: + case MODE_SYSTEM: + // No banked registers + return BANK_NONE; + case MODE_FIQ: + return BANK_FIQ; + case MODE_IRQ: + return BANK_IRQ; + case MODE_SUPERVISOR: + return BANK_SUPERVISOR; + case MODE_ABORT: + return BANK_ABORT; + case MODE_UNDEFINED: + return BANK_UNDEFINED; + default: + // This should be unreached + return BANK_NONE; + } +} + #endif diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 7d3a73be5..6df5d2859 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -59,7 +59,9 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * | bits 21 - 31: Reserved * | 0x0004C - 0x0004F: Next frame * | 0x00050 - 0x00053: Next channel 3 fade - * | 0x00054 - 0x00057: Reserved + * | 0x00054 - 0x00057: Sweep state + * | bits 0 - 2: Timesteps + * | bits 3 - 31: Reserved * | 0x00058 - 0x0005B: Next event * 0x0005C - 0x0006B: Audio channel 2 state * | 0x0005C - 0x0005F: Envelepe timing @@ -87,22 +89,23 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * | bits 0 - 3: Current volume * | bits 4 - 5: Is dead? * | bit 6: Is high? +* | bit 7: Reserved * | 0x000A5: Channel 2 envelope state * | bits 0 - 3: Current volume * | bits 4 - 5: Is dead? * | bit 6: Is high? -* | bits 7: Reserved +* | bit 7: Reserved * | 0x000A6: Channel 4 envelope state * | bits 0 - 3: Current volume * | bits 4 - 5: Is dead? - * | bit 6: Is high? -* | bits 7: Reserved + * | bits 6 - 7: Current frame (continued) * | 0x000A7: Miscellaneous audio flags - * | bits 0 - 3: Current frame - * | bit 4: Is channel 1 sweep enabled? - * | bit 5: Has channel 1 sweep occurred? - * | bit 6: Is channel 3's memory readable? - * | bit 7: Reserved + * | bit 0: Current frame (continuation) + * | bit 1: Is channel 1 sweep enabled? + * | bit 2: Has channel 1 sweep occurred? + * | bit 3: Is channel 3's memory readable? + * | bit 4: Skip frame + * | bits 5 - 7: Reserved * | 0x000A8 - 0x000AB: Left capacitor charge * | 0x000AC - 0x000AF: Right capacitor charge * | 0x000B0 - 0x000B3: Next sample @@ -203,12 +206,16 @@ DECL_BITS(GBSerializedAudioEnvelope, Length, 0, 7); DECL_BITS(GBSerializedAudioEnvelope, NextStep, 7, 3); DECL_BITS(GBSerializedAudioEnvelope, Frequency, 10, 11); + +DECL_BITFIELD(GBSerializedAudioSweep, uint32_t); +DECL_BITS(GBSerializedAudioSweep, Time, 0, 3); + struct GBSerializedPSGState { struct { GBSerializedAudioEnvelope envelope; int32_t nextFrame; int32_t nextCh3Fade; - int32_t reserved; + GBSerializedAudioSweep sweep; uint32_t nextEvent; } ch1; struct { diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index e9ef92cee..24cc1b85c 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -41,7 +41,10 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bits 10 - 20: Shadow frequency register * | bits 21 - 31: Reserved * | 0x00134 - 0x00137: Next frame - * | 0x00138 - 0x0013F: Reserved + * | 0x00138 - 0x0013B: Next channel 3 fade + * | 0x0013C - 0x0013F: Sweep state + * | bits 0 - 2: Timesteps + * | bits 3 - 7: Reserved * | 0x00140 - 0x00143: Next event * 0x00144 - 0x00153: Audio channel 2 state * | 0x00144 - 0x00147: Envelepe timing @@ -75,21 +78,23 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | bits 0 - 3: Current volume * | bits 4 - 5: Is dead? * | bit 6: Is high? +* | bit 7: Reserved * | 0x001DD - 0x001DD: Channel 2 envelope state * | bits 0 - 3: Current volume * | bits 4 - 5: Is dead? * | bit 6: Is high? -* | bits 7: Reserved +* | bit 7: Reserved * | 0x001DE - 0x001DE: Channel 4 envelope state * | bits 0 - 3: Current volume * | bits 4 - 5: Is dead? - * | bit 6: Is high? -* | bits 7: Reserved + * | bits 6 - 7: Current frame (continued) * | 0x001DF - 0x001DF: Miscellaneous audio flags - * | bits 0 - 3: Current frame - * | bit 4: Is channel 1 sweep enabled? - * | bit 5: Has channel 1 sweep occurred? - * | bits 6 - 7: Reserved + * | bit 0: Current frame (continuation) + * | bit 1: Is channel 1 sweep enabled? + * | bit 2: Has channel 1 sweep occurred? + * | bit 3: Is channel 3's memory readable? + * | bit 4: Skip frame + * | bits 5 - 7: Reserved * 0x001E0 - 0x001FF: Video miscellaneous state * | 0x001E0 - 0x001E3: Next event * | 0x001E4 - 0x001F7: Reserved diff --git a/src/arm/arm.c b/src/arm/arm.c index c72b04b01..9b64f9807 100644 --- a/src/arm/arm.c +++ b/src/arm/arm.c @@ -9,16 +9,14 @@ #include #include -static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode); - void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) { if (mode == cpu->privilegeMode) { // Not switching modes after all return; } - enum RegisterBank newBank = _ARMSelectBank(mode); - enum RegisterBank oldBank = _ARMSelectBank(cpu->privilegeMode); + enum RegisterBank newBank = ARMSelectBank(mode); + enum RegisterBank oldBank = ARMSelectBank(cpu->privilegeMode); if (newBank != oldBank) { // Switch banked registers if (mode == MODE_FIQ || cpu->privilegeMode == MODE_FIQ) { @@ -46,28 +44,6 @@ void ARMSetPrivilegeMode(struct ARMCore* cpu, enum PrivilegeMode mode) { cpu->privilegeMode = mode; } -static inline enum RegisterBank _ARMSelectBank(enum PrivilegeMode mode) { - switch (mode) { - case MODE_USER: - case MODE_SYSTEM: - // No banked registers - return BANK_NONE; - case MODE_FIQ: - return BANK_FIQ; - case MODE_IRQ: - return BANK_IRQ; - case MODE_SUPERVISOR: - return BANK_SUPERVISOR; - case MODE_ABORT: - return BANK_ABORT; - case MODE_UNDEFINED: - return BANK_UNDEFINED; - default: - // This should be unreached - return BANK_NONE; - } -} - void ARMInit(struct ARMCore* cpu) { memset(&cpu->cp15, 0, sizeof(cpu->cp15)); cpu->master->init(cpu, cpu->master); diff --git a/src/arm/debugger/debugger.c b/src/arm/debugger/debugger.c index dd569ff00..4e49096ff 100644 --- a/src/arm/debugger/debugger.c +++ b/src/arm/debugger/debugger.c @@ -15,6 +15,8 @@ #include #include +#define FRAME_PRIV(FRAME) ((struct ARMRegisterFile*) FRAME->regs)->cpsr.priv + DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint); static bool ARMDecodeCombined(struct ARMCore* cpu, struct ARMInstructionInfo* info) { @@ -36,7 +38,8 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin struct mStackTrace* stack = &d->p->stackTrace; struct mStackFrame* frame = mStackTraceGetFrame(stack, 0); - if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP]) { + enum RegisterBank currentStack = ARMSelectBank(cpu->cpsr.priv); + if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame))) { // The stack frame has been popped off the stack. This means the function // has been returned from, or that the stack pointer has been otherwise // manipulated. Either way, the function is done executing. @@ -45,7 +48,7 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin shouldBreak = shouldBreak || frame->breakWhenFinished; mStackTracePop(stack); frame = mStackTraceGetFrame(stack, 0); - } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP]); + } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame))); if (shouldBreak) { struct mDebuggerEntryInfo debuggerInfo = { .address = pc, @@ -83,10 +86,10 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin return false; } - bool isCall = interrupt || (info.branchType & ARM_BRANCH_LINKED); + bool isCall = info.branchType & ARM_BRANCH_LINKED; uint32_t destAddress; - if (interrupt && info.branchType == ARM_BRANCH_NONE) { + if (interrupt && !isCall) { // The stack frame was already pushed up above, so there's no // action necessary here, but we still want to check for a // breakpoint down below. @@ -114,6 +117,10 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin bool isBranch = ARMInstructionIsBranch(info.mnemonic); int reg = (isBranch ? info.op1.reg : info.op2.reg); destAddress = cpu->gprs[reg]; + if (!isBranch && (info.branchType & ARM_BRANCH_INDIRECT) && info.op1.reg == ARM_PC && info.operandFormat & ARM_OPERAND_MEMORY_2) { + uint32_t ptrAddress = ARMResolveMemoryAccess(&info, &cpu->regs, pc); + destAddress = cpu->memory.load32(cpu, ptrAddress, NULL); + } if (isBranch || (info.op1.reg == ARM_PC && !isMovPcLr)) { // ARMv4 doesn't have the BLX opcode, so it uses an assignment to LR before a BX for that purpose. struct ARMInstructionInfo prevInfo; @@ -144,25 +151,23 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin return false; } - if (info.branchType & ARM_BRANCH_INDIRECT) { - destAddress = cpu->memory.load32(cpu, destAddress, NULL); - } - if (isCall) { int instructionLength = isWideInstruction ? WORD_SIZE_ARM : WORD_SIZE_THUMB; frame = mStackTracePush(stack, pc, destAddress + instructionLength, cpu->gprs[ARM_SP], &cpu->regs); if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) { return false; } - } else { - mStackTracePop(stack); + } else if (!interrupt) { + if (frame && currentStack == ARMSelectBank(FRAME_PRIV(frame))) { + mStackTracePop(stack); + } if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) { return false; } } struct mDebuggerEntryInfo debuggerInfo = { .address = pc, - .type.st.traceType = isCall ? STACK_TRACE_BREAK_ON_CALL : STACK_TRACE_BREAK_ON_RETURN, + .type.st.traceType = (interrupt || isCall) ? STACK_TRACE_BREAK_ON_CALL : STACK_TRACE_BREAK_ON_RETURN, .pointId = 0 }; mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo); diff --git a/src/arm/decoder.c b/src/arm/decoder.c index 2e9c77afa..30da5f76c 100644 --- a/src/arm/decoder.c +++ b/src/arm/decoder.c @@ -534,3 +534,46 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i buffer[blen - 1] = '\0'; return total; } + +uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc) { + uint32_t address = 0; + int32_t offset = 0; + if (info->memory.format & ARM_MEMORY_REGISTER_BASE) { + if (info->memory.baseReg == ARM_PC && info->memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) { + address = pc; + } else { + address = regs->gprs[info->memory.baseReg]; + } + } + if (info->memory.format & ARM_MEMORY_POST_INCREMENT) { + return address; + } + if (info->memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) { + offset = info->memory.offset.immediate; + } else if (info->memory.format & ARM_MEMORY_REGISTER_OFFSET) { + offset = info->memory.offset.reg == ARM_PC ? pc : regs->gprs[info->memory.offset.reg]; + } + if (info->memory.format & ARM_MEMORY_SHIFTED_OFFSET) { + uint8_t shiftSize = info->memory.offset.shifterImm; + switch (info->memory.offset.shifterOp) { + case ARM_SHIFT_LSL: + offset <<= shiftSize; + break; + case ARM_SHIFT_LSR: + offset = ((uint32_t) offset) >> shiftSize; + break; + case ARM_SHIFT_ASR: + offset >>= shiftSize; + break; + case ARM_SHIFT_ROR: + offset = ROR(offset, shiftSize); + break; + case ARM_SHIFT_RRX: + offset = (regs->cpsr.c << 31) | ((uint32_t) offset >> 1); + break; + default: + break; + }; + } + return address + (info->memory.format & ARM_MEMORY_OFFSET_SUBTRACT ? -offset : offset); +} diff --git a/src/core/core.c b/src/core/core.c index 89201658e..1d2e0523e 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -152,7 +152,10 @@ bool mCorePreloadVFCB(struct mCore* core, struct VFile* vf, void (cb)(size_t, si #ifdef FIXED_ROM_BUFFER extern uint32_t* romBuffer; extern size_t romBufferSize; - vfm = VFileFromMemory(romBuffer, romBufferSize); + if (size > romBufferSize) { + size = romBufferSize; + } + vfm = VFileFromMemory(romBuffer, size); #else vfm = VFileMemChunk(NULL, size); #endif diff --git a/src/core/timing.c b/src/core/timing.c index f9854c6ac..ddee6d332 100644 --- a/src/core/timing.c +++ b/src/core/timing.c @@ -50,6 +50,10 @@ void mTimingSchedule(struct mTiming* timing, struct mTimingEvent* event, int32_t *previous = event; } +void mTimingScheduleAbsolute(struct mTiming* timing, struct mTimingEvent* event, int32_t when) { + mTimingSchedule(timing, event, when - mTimingCurrentTime(timing)); +} + void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent* event) { if (timing->reroot) { timing->root = timing->reroot; diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 691474f3a..c2748d913 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -1035,7 +1035,13 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r case DEBUGGER_ENTER_STACK: if (info) { if (info->type.st.traceType == STACK_TRACE_BREAK_ON_CALL) { - cliDebugger->backend->printf(cliDebugger->backend, "Hit function call at at 0x%08X\n", info->address); + struct mStackTrace* stack = &cliDebugger->d.stackTrace; + struct mStackFrame* frame = mStackTraceGetFrame(stack, 0); + if (frame->interrupt) { + cliDebugger->backend->printf(cliDebugger->backend, "Hit interrupt at at 0x%08X\n", info->address); + } else { + cliDebugger->backend->printf(cliDebugger->backend, "Hit function call at at 0x%08X\n", info->address); + } } else { cliDebugger->backend->printf(cliDebugger->backend, "Hit function return at at 0x%08X\n", info->address); } diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index 014552de1..5d45f69ad 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -654,7 +654,7 @@ void mGUIRunloop(struct mGUIRunner* runner) { mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config)); } } - while (runner->running(runner)) { + while (!runner->running || runner->running(runner)) { char path[PATH_MAX]; const char* preselect = mCoreConfigGetValue(&runner->config, "lastGame"); if (preselect) { diff --git a/src/gb/audio.c b/src/gb/audio.c index 4bc5fa351..be3b8a6b3 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -117,7 +117,7 @@ void GBAudioReset(struct GBAudio* audio) { if (audio->style == GB_AUDIO_GBA) { mTimingSchedule(audio->timing, &audio->frameEvent, 0); } - audio->ch1 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; + audio->ch1 = (struct GBAudioSquareChannel) { .sweep = { .time = 8 }, .envelope = { .dead = 2 } }; audio->ch2 = (struct GBAudioSquareChannel) { .envelope = { .dead = 2 } }; audio->ch3 = (struct GBAudioWaveChannel) { .bank = 0 }; audio->ch4 = (struct GBAudioNoiseChannel) { .nSamples = 0 }; @@ -964,6 +964,7 @@ static void _updateChannel4(struct mTiming* timing, void* user, uint32_t cyclesL void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGState* state, uint32_t* flagsOut) { uint32_t flags = 0; + uint32_t sweep = 0; uint32_t ch1Flags = 0; uint32_t ch2Flags = 0; uint32_t ch4Flags = 0; @@ -980,7 +981,9 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat ch1Flags = GBSerializedAudioEnvelopeSetLength(ch1Flags, audio->ch1.control.length); ch1Flags = GBSerializedAudioEnvelopeSetNextStep(ch1Flags, audio->ch1.envelope.nextStep); ch1Flags = GBSerializedAudioEnvelopeSetFrequency(ch1Flags, audio->ch1.sweep.realFrequency); + sweep = GBSerializedAudioSweepSetTime(sweep, audio->ch1.sweep.time & 7); STORE_32LE(ch1Flags, 0, &state->ch1.envelope); + STORE_32LE(sweep, 0, &state->ch1.sweep); STORE_32LE(audio->ch1Event.when - mTimingCurrentTime(audio->timing), 0, &state->ch1.nextEvent); flags = GBSerializedAudioFlagsSetCh2Volume(flags, audio->ch2.envelope.currentVolume); @@ -1011,6 +1014,7 @@ void GBAudioPSGSerialize(const struct GBAudio* audio, struct GBSerializedPSGStat void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGState* state, const uint32_t* flagsIn) { uint32_t flags; + uint32_t sweep; uint32_t ch1Flags = 0; uint32_t ch2Flags = 0; uint32_t ch4Flags = 0; @@ -1032,11 +1036,16 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt audio->skipFrame = GBSerializedAudioFlagsGetSkipFrame(flags); LOAD_32LE(ch1Flags, 0, &state->ch1.envelope); + LOAD_32LE(sweep, 0, &state->ch1.sweep); audio->ch1.envelope.currentVolume = GBSerializedAudioFlagsGetCh1Volume(flags); audio->ch1.envelope.dead = GBSerializedAudioFlagsGetCh1Dead(flags); audio->ch1.control.hi = GBSerializedAudioFlagsGetCh1Hi(flags); audio->ch1.sweep.enable = GBSerializedAudioFlagsGetCh1SweepEnabled(flags); audio->ch1.sweep.occurred = GBSerializedAudioFlagsGetCh1SweepOccurred(flags); + audio->ch1.sweep.time = GBSerializedAudioSweepGetTime(sweep); + if (!audio->ch1.sweep.time) { + audio->ch1.sweep.time = 8; + } audio->ch1.control.length = GBSerializedAudioEnvelopeGetLength(ch1Flags); audio->ch1.envelope.nextStep = GBSerializedAudioEnvelopeGetNextStep(ch1Flags); audio->ch1.sweep.realFrequency = GBSerializedAudioEnvelopeGetFrequency(ch1Flags); diff --git a/src/gb/io.c b/src/gb/io.c index 45b358146..c6baa5a30 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -699,7 +699,8 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) { memcpy(gb->memory.io, state->io, GB_SIZE_IO); gb->memory.ie = state->ie; - if (GBAudioEnableGetEnable(*gb->audio.nr52)) { + gb->audio.enable = GBAudioEnableGetEnable(*gb->audio.nr52); + if (GBAudioEnableGetEnable(gb->audio.enable)) { GBIOWrite(gb, REG_NR10, gb->memory.io[REG_NR10]); GBIOWrite(gb, REG_NR11, gb->memory.io[REG_NR11]); GBIOWrite(gb, REG_NR12, gb->memory.io[REG_NR12]); @@ -709,14 +710,14 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) { gb->audio.ch1.control.stop = GBAudioRegisterControlGetStop(gb->memory.io[REG_NR14] << 8); GBIOWrite(gb, REG_NR21, gb->memory.io[REG_NR21]); GBIOWrite(gb, REG_NR22, gb->memory.io[REG_NR22]); - GBIOWrite(gb, REG_NR22, gb->memory.io[REG_NR23]); + GBIOWrite(gb, REG_NR23, gb->memory.io[REG_NR23]); gb->audio.ch2.control.frequency &= 0xFF; gb->audio.ch2.control.frequency |= GBAudioRegisterControlGetFrequency(gb->memory.io[REG_NR24] << 8); gb->audio.ch2.control.stop = GBAudioRegisterControlGetStop(gb->memory.io[REG_NR24] << 8); GBIOWrite(gb, REG_NR30, gb->memory.io[REG_NR30]); GBIOWrite(gb, REG_NR31, gb->memory.io[REG_NR31]); GBIOWrite(gb, REG_NR32, gb->memory.io[REG_NR32]); - GBIOWrite(gb, REG_NR32, gb->memory.io[REG_NR33]); + GBIOWrite(gb, REG_NR33, gb->memory.io[REG_NR33]); gb->audio.ch3.rate &= 0xFF; gb->audio.ch3.rate |= GBAudioRegisterControlGetRate(gb->memory.io[REG_NR34] << 8); gb->audio.ch3.stop = GBAudioRegisterControlGetStop(gb->memory.io[REG_NR34] << 8); diff --git a/src/gb/mbc.c b/src/gb/mbc.c index b87885964..407cd8e79 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -440,6 +440,7 @@ static void _GBMBC1Update(struct GB* gb) { GBMBCSwitchSramBank(gb, 0); } if (!(state->bankLo & 0x1F)) { + ++state->bankLo; ++bank; } GBMBCSwitchBank(gb, bank); diff --git a/src/gb/memory.c b/src/gb/memory.c index a856b6635..a6abaf166 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -211,6 +211,7 @@ void GBMemoryReset(struct GB* gb) { switch (gb->memory.mbcType) { case GB_MBC1: gb->memory.mbcState.mbc1.mode = 0; + gb->memory.mbcState.mbc1.bankLo = 1; break; case GB_MBC6: GBMBCSwitchHalfBank(gb, 0, 2); @@ -784,10 +785,14 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { LOAD_32LE(when, 0, &state->memory.dmaNext); if (memory->dmaRemaining) { mTimingSchedule(&gb->timing, &memory->dmaEvent, when); + } else { + memory->dmaEvent.when = when + mTimingCurrentTime(&gb->timing); } LOAD_32LE(when, 0, &state->memory.hdmaNext); if (memory->hdmaRemaining) { mTimingSchedule(&gb->timing, &memory->hdmaEvent, when); + } else { + memory->hdmaEvent.when = when + mTimingCurrentTime(&gb->timing); } GBSerializedMemoryFlags flags; diff --git a/src/gb/serialize.c b/src/gb/serialize.c index 3ae5cbf10..e6b053b9b 100644 --- a/src/gb/serialize.c +++ b/src/gb/serialize.c @@ -188,6 +188,8 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) { LOAD_32LE(when, 0, &state->cpu.eiPending); if (GBSerializedCpuFlagsIsEiPending(flags)) { mTimingSchedule(&gb->timing, &gb->eiPending, when); + } else { + gb->eiPending.when = when + mTimingCurrentTime(&gb->timing); } gb->model = state->model; diff --git a/src/gb/timer.c b/src/gb/timer.c index da08d49bf..8125ebcaa 100644 --- a/src/gb/timer.c +++ b/src/gb/timer.c @@ -140,8 +140,10 @@ void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* s GBSerializedTimerFlags flags = state->timer.flags; + LOAD_32LE(when, 0, &state->timer.nextIRQ); if (GBSerializedTimerFlagsIsIrqPending(flags)) { - LOAD_32LE(when, 0, &state->timer.nextIRQ); mTimingSchedule(&timer->p->timing, &timer->irq, when); + } else { + timer->irq.when = when + mTimingCurrentTime(&timer->p->timing); } } diff --git a/src/gb/video.c b/src/gb/video.c index 09588fc14..6c207297d 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -904,13 +904,17 @@ void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* s } uint32_t when; + LOAD_32LE(when, 0, &state->video.nextMode); if (!GBSerializedVideoFlagsIsNotModeEventScheduled(flags)) { - LOAD_32LE(when, 0, &state->video.nextMode); mTimingSchedule(&video->p->timing, &video->modeEvent, when); + } else { + video->modeEvent.when = when + mTimingCurrentTime(&video->p->timing); } + LOAD_32LE(when, 0, &state->video.nextFrame); if (!GBSerializedVideoFlagsIsNotFrameEventScheduled(flags)) { - LOAD_32LE(when, 0, &state->video.nextFrame); mTimingSchedule(&video->p->timing, &video->frameEvent, when); + } else { + video->frameEvent.when = when + mTimingCurrentTime(&video->p->timing); } video->renderer->deinit(video->renderer); diff --git a/src/gba/bios.c b/src/gba/bios.c index 57e57ea0f..e62afe70f 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -423,7 +423,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { switch (immediate) { case 0xF0: // Used for internal stall counting - cpu->gprs[4] = gba->biosStall; + cpu->gprs[12] = gba->biosStall; return; case 0xFA: GBAPrintFlush(gba); diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index 4da22d005..05223b937 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -9,17 +9,17 @@ const uint8_t hleBios[SIZE_BIOS] = { 0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0x4c, 0x01, 0x9f, 0x15, 0x10, 0xff, 0x2f, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x29, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, - 0x20, 0xd0, 0x4d, 0x02, 0x10, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, + 0x20, 0xd0, 0x4d, 0x02, 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0xd4, 0xc0, 0xa0, 0xe3, 0x0b, 0xb1, 0x9c, 0xe7, 0xd2, 0xcf, 0xa0, 0xe3, - 0x0b, 0x00, 0x5c, 0xe1, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xc0, 0x4f, 0xe1, - 0x00, 0x10, 0x2d, 0xe9, 0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3, - 0x0c, 0xf0, 0x21, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x00, 0x5b, 0xe3, + 0x0b, 0x00, 0x5c, 0xe1, 0x00, 0xc0, 0x4f, 0xe1, 0x00, 0x10, 0x2d, 0xe9, + 0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3, 0x0c, 0xf0, 0x21, 0xe1, + 0x00, 0x00, 0xf0, 0x0f, 0x04, 0x40, 0x2d, 0xe9, 0x00, 0x00, 0x5b, 0xe3, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x0f, 0xe0, 0xa0, 0xe1, 0x1b, 0xff, 0x2f, 0x11, 0x00, 0x00, 0xa0, 0xe1, - 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x40, 0xbd, 0xe8, + 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x04, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, 0x00, 0x10, 0xbd, 0xe8, 0x0c, 0xf0, 0x69, 0xe1, - 0x10, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xcc, 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, @@ -73,5 +73,5 @@ const uint8_t hleBios[SIZE_BIOS] = { 0x01, 0xa0, 0xa0, 0xe1, 0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8, 0xfa, 0x07, 0xa0, 0xe8, 0x00, 0x10, 0xa0, 0xe3, 0xf0, 0x07, 0xbd, 0xe8, 0x1e, 0xff, 0x2f, 0xe1, 0xb0, 0x01, 0x00, 0x00, - 0x04, 0x40, 0x54, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1 + 0x04, 0xc0, 0x5c, 0xe2, 0xfd, 0xff, 0xff, 0x8a, 0x1e, 0xff, 0x2f, 0xe1 }; diff --git a/src/gba/hle-bios.s b/src/gba/hle-bios.s index c27e220b5..8234b9e45 100644 --- a/src/gba/hle-bios.s +++ b/src/gba/hle-bios.s @@ -31,19 +31,19 @@ swiBase: cmp sp, #0 moveq sp, #0x04000000 subeq sp, #0x20 -stmfd sp!, {r4, r11-r12, lr} +stmfd sp!, {r11-r12, lr} ldrb r11, [lr, #-2] mov r12, #swiTable ldr r11, [r12, r11, lsl #2] mov r12, #StallCall cmp r12, r11 -swieq 0xF00000 @ Special mGBA-internal call to load the stall count into r4 mrs r12, spsr stmfd sp!, {r12} and r12, #0x80 orr r12, #0x1F msr cpsr_c, r12 -stmfd sp!, {lr} +swieq 0xF00000 @ Special mGBA-internal call to load the stall count into r12 +stmfd sp!, {r2, lr} cmp r11, #0 nop nop @@ -56,11 +56,11 @@ bxne r11 nop nop nop -ldmfd sp!, {lr} +ldmfd sp!, {r2, lr} msr cpsr, #0x93 ldmfd sp!, {r12} msr spsr, r12 -ldmfd sp!, {r4, r11-r12, lr} +ldmfd sp!, {r11-r12, lr} movs pc, lr .word 0 .word 0xE3A02004 @@ -310,6 +310,6 @@ ArcTan: ArcTan2: StallCall: -subs r4, #4 +subs r12, #4 bhi StallCall bx lr diff --git a/src/gba/io.c b/src/gba/io.c index 6845e6a46..7953e0d62 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -293,7 +293,7 @@ static const int _isWSpecialRegister[REG_MAX >> 1] = { // Audio 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, - 1, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // DMA @@ -980,6 +980,8 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) { LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].when); } GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]); + gba->sio.siocnt = gba->memory.io[REG_SIOCNT >> 1]; + GBASIOWriteRCNT(&gba->sio, gba->memory.io[REG_RCNT >> 1]); LOAD_32(gba->memory.dmaTransferRegister, 0, &state->dmaTransferRegister); LOAD_32(gba->dmaPC, 0, &state->dmaBlockPC); diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 97345ad05..8732d2ec1 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -69,7 +69,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { } miscFlags = GBASerializedMiscFlagsSetBlocked(miscFlags, gba->cpuBlocked); STORE_32(miscFlags, 0, &state->miscFlags); - STORE_32(gba->biosStall, 0, &state->nextIrq); + STORE_32(gba->biosStall, 0, &state->biosStall); GBAMemorySerialize(&gba->memory, state); GBAIOSerialize(gba, state); @@ -188,7 +188,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { mTimingSchedule(&gba->timing, &gba->irqEvent, when); } gba->cpuBlocked = GBASerializedMiscFlagsGetBlocked(miscFlags); - LOAD_32(gba->biosStall, 0, &state->nextIrq); + LOAD_32(gba->biosStall, 0, &state->biosStall); GBAVideoDeserialize(&gba->video, state); GBAMemoryDeserialize(&gba->memory, state); diff --git a/src/gba/sharkport.c b/src/gba/sharkport.c index ee717bdee..f3d033c8d 100644 --- a/src/gba/sharkport.c +++ b/src/gba/sharkport.c @@ -69,7 +69,7 @@ bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf, bool testChec return false; } LOAD_32(size, 0, &buffer.i); - if (size < 0x1C || size > SIZE_CART_FLASH1M + 0x1C) { + if (size < 0x1C || size >= SIZE_CART_FLASH1M + 0x1C) { return false; } char* payload = malloc(size); @@ -91,7 +91,7 @@ bool GBASavedataImportSharkPort(struct GBA* gba, struct VFile* vf, bool testChec buffer.c[0x19] = 0; buffer.c[0x1A] = 0; buffer.c[0x1B] = 0; - if (memcmp(buffer.c, payload, 0x1C) != 0) { + if (memcmp(buffer.c, payload, testChecksum ? 0x1C : 0xF) != 0) { goto cleanup; } diff --git a/src/gba/timer.c b/src/gba/timer.c index dba29db18..41708d53a 100644 --- a/src/gba/timer.c +++ b/src/gba/timer.c @@ -134,9 +134,8 @@ void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timi tickIncrement = (0x10000 - tickIncrement) << prescaleBits; currentTime += tickIncrement; currentTime &= ~tickMask; - currentTime -= mTimingCurrentTime(timing); mTimingDeschedule(timing, &timer->event); - mTimingSchedule(timing, &timer->event, currentTime); + mTimingScheduleAbsolute(timing, &timer->event, currentTime); } void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) { diff --git a/src/platform/3ds/cia.rsf.in b/src/platform/3ds/cia.rsf.in index 507da46e0..adc36ea0a 100644 --- a/src/platform/3ds/cia.rsf.in +++ b/src/platform/3ds/cia.rsf.in @@ -154,14 +154,13 @@ AccessControlInfo: - cam:u - cecd:u - cfg:u - - dlp:FKCL - - dlp:SRVR - dsp::DSP - frd:u - fs:USER - gsp::Gpu - hid:USER - http:C + - mcu::HWC - mic:u - ndm:u - news:u diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index 7eb57c3d4..0adf73b26 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -187,6 +187,7 @@ static void _cleanup(void) { camExit(); ndspExit(); ptmuExit(); + mcuHwcExit(); } static void _map3DSKey(struct mInputMap* map, int ctrKey, enum GBAKey key) { @@ -237,15 +238,20 @@ static void _drawEnd(void) { static int _batteryState(void) { u8 charge; u8 adapter; - PTMU_GetBatteryLevel(&charge); PTMU_GetBatteryChargeState(&adapter); + int state = 0; + if (R_SUCCEEDED(MCUHWC_GetBatteryLevel(&charge))) { + charge |= BATTERY_PERCENTAGE_VALID; + } else { + PTMU_GetBatteryLevel(&charge); + if (charge > 0) { + --charge; + } + } if (adapter) { state |= BATTERY_CHARGING; } - if (charge > 0) { - --charge; - } return state | charge; } @@ -815,6 +821,7 @@ int main() { camera.cam = SELECT_IN1; ptmuInit(); + mcuHwcInit(); camInit(); hasSound = NO_SOUND; diff --git a/src/platform/psp2/main.c b/src/platform/psp2/main.c index 6a96a7456..74199d299 100644 --- a/src/platform/psp2/main.c +++ b/src/platform/psp2/main.c @@ -72,13 +72,12 @@ static enum GUICursorState _pollCursor(unsigned* x, unsigned* y) { } static int _batteryState(void) { - int charge = scePowerGetBatteryLifePercent(); + int charge = scePowerGetBatteryLifePercent() | BATTERY_PERCENTAGE_VALID; int adapter = scePowerIsPowerOnline(); int state = 0; if (adapter) { state |= BATTERY_CHARGING; } - charge /= 25; return state | charge; } diff --git a/src/platform/qt/FrameView.cpp b/src/platform/qt/FrameView.cpp index 0b97fbd6c..c32e1f9c8 100644 --- a/src/platform/qt/FrameView.cpp +++ b/src/platform/qt/FrameView.cpp @@ -142,7 +142,7 @@ void FrameView::disableLayer(const QPointF& coord) { } #ifdef M_CORE_GBA -void FrameView::updateTilesGBA(bool force) { +void FrameView::updateTilesGBA(bool) { if (m_ui.freeze->checkState() == Qt::Checked) { return; } @@ -285,7 +285,7 @@ void FrameView::injectGBA() { #endif #ifdef M_CORE_GB -void FrameView::updateTilesGB(bool force) { +void FrameView::updateTilesGB(bool) { if (m_ui.freeze->checkState() == Qt::Checked) { return; } @@ -314,10 +314,12 @@ void FrameView::invalidateQueue(const QSize& dims) { #ifdef M_CORE_GBA case PLATFORM_GBA: injectGBA(); + break; #endif #ifdef M_CORE_GB case PLATFORM_GB: injectGB(); + break; #endif } m_vl->runFrame(m_vl); @@ -376,6 +378,8 @@ bool FrameView::eventFilter(QObject* obj, QEvent* event) { pos /= m_ui.magnification->value(); disableLayer(pos); return true; + default: + break; } return false; } @@ -462,4 +466,4 @@ QString FrameView::LayerId::readable() const { return typeStr; } return tr("%1 %2").arg(typeStr).arg(index); -} \ No newline at end of file +} diff --git a/src/platform/qt/MapView.cpp b/src/platform/qt/MapView.cpp index 46b6ab814..5771b83ac 100644 --- a/src/platform/qt/MapView.cpp +++ b/src/platform/qt/MapView.cpp @@ -159,7 +159,7 @@ bool MapView::eventFilter(QObject* obj, QEvent* event) { return true; } -void MapView::updateTilesGBA(bool force) { +void MapView::updateTilesGBA(bool) { { CoreController::Interrupter interrupter(m_controller); int bitmap = -1; diff --git a/src/platform/qt/Swatch.cpp b/src/platform/qt/Swatch.cpp index 366b981bb..3eb85d780 100644 --- a/src/platform/qt/Swatch.cpp +++ b/src/platform/qt/Swatch.cpp @@ -50,7 +50,7 @@ void Swatch::setColor(int index, uint32_t color) { updateFill(index); } -void Swatch::paintEvent(QPaintEvent* event) { +void Swatch::paintEvent(QPaintEvent*) { QPainter painter(this); painter.drawPixmap(QPoint(), m_backing); } diff --git a/src/platform/qt/TilePainter.cpp b/src/platform/qt/TilePainter.cpp index 127b31058..4e0490f36 100644 --- a/src/platform/qt/TilePainter.cpp +++ b/src/platform/qt/TilePainter.cpp @@ -19,12 +19,12 @@ TilePainter::TilePainter(QWidget* parent) setTileCount(3072); } -void TilePainter::paintEvent(QPaintEvent* event) { +void TilePainter::paintEvent(QPaintEvent*) { QPainter painter(this); painter.drawPixmap(QPoint(), m_backing); } -void TilePainter::resizeEvent(QResizeEvent* event) { +void TilePainter::resizeEvent(QResizeEvent*) { int w = width() / m_size; if (!w) { w = 1; diff --git a/src/platform/qt/ts/mgba-zh_CN.ts b/src/platform/qt/ts/mgba-zh_CN.ts index 8a8a1a623..11119dd9f 100644 --- a/src/platform/qt/ts/mgba-zh_CN.ts +++ b/src/platform/qt/ts/mgba-zh_CN.ts @@ -79,7 +79,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Tile # - 瓷贴 # + 图块 # @@ -228,7 +228,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Add New Set - 添加新集 + 添加新代码集 @@ -251,7 +251,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Enter command (try `help` for more info) - 输入命令(尝试输入 `help` 获取更多信息) + 输入命令 (尝试输入 `help` 获取更多信息) @@ -310,34 +310,44 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 录制 GIF/APNG - - APNG - APNG + + Loop + 循环 - + Start 开始 - + Stop 停止 - + Select File 选择文件 - - Frameskip - 跳帧 + + APNG + APNG + + + + GIF + GIF - GIF - GIF + WebP + WebP + + + + Frameskip + 跳帧 @@ -1057,7 +1067,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Tile - 瓷贴 + 图块 @@ -1065,7 +1075,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Game Overrides - 游戏覆盖 + 游戏覆写 @@ -1093,7 +1103,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Tilt - 瓷贴 + 图块 @@ -1247,17 +1257,27 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 HuC-3 - + + Wisdom Tree (Unlicensed) + Wisdom Tree (未授权) + + + + Pokémon Jade/Diamond (Unlicensed) + Pokémon Jade/Diamond (未授权) + + + Background Colors 背景颜色 - + Sprite Colors 1 精灵图颜色 1 - + Sprite Colors 2 精灵图颜色 2 @@ -1462,7 +1482,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 BattleChip data is missing. BattleChip Gates will still work, but some graphics will be missing. Would you like to download the data now? - BattleChip 数据已丢失。BattleChip Gate 仍然能够工作,但部分图形会丢失。您想立即下载数据吗? + BattleChip 数据已丢失。BattleChip Gate 仍然能够工作,但会丢失部分图形。您想立即下载数据吗? @@ -1484,12 +1504,12 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 QGBA::CheatsModel - + (untitled) (无标题) - + Failed to open cheats file: %1 打开作弊码文件失败: %1 @@ -1555,50 +1575,55 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 打开游戏文件失败: %1 - + Could not load game. Are you sure it's in the correct format? - 无法载入游戏。是否确认游戏格式无误? + 无法载入游戏。请确认游戏格式是否正确? + + + + Failed to open save file. Is the save directory writable? + 无法打开存档文件。存档目录是否可写入? QGBA::FrameView - + Export frame 导出框架 - + Portable Network Graphics (*.png) 便携式网络图形 (*.png) - + None - + Background 背景 - + Window 窗口 - + Sprite 精灵图 - + Backdrop 背幕 - + %1 %2 %1 %2 @@ -1680,19 +1705,19 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 QGBA::GIFView - - Failed to open output GIF file: %1 - 打开输出 GIF 文件失败: %1 + + Failed to open output file: %1 + 打开输出文件失败: %1 - + Select output file 选择输出文件 - - Graphics Interchange Format (*.gif);;Animated Portable Network Graphics (*.png *.apng)" - 图形交换格式 (*.gif);;动画便携式网络图形 (*.png *.apng)" + + Graphics Interchange Format (*.gif);;Animated Portable Network Graphics (*.png *.webp *.apng)" + 图形交换格式 (*.gif);;动画便携式网络图形 (*.png *.webp *.apng)" @@ -1705,27 +1730,27 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Mode 0: 4 tile layers - 模式0: 4 个瓷贴层 + 模式 0: 4 个图块层 Mode 1: 2 tile layers + 1 rotated/scaled tile layer - 模式 1: 2 个瓷贴层 + 1 个已旋转/缩放瓷贴层 + 模式 1: 2 个图块层 + 1 个旋转/缩放图块层 Mode 2: 2 rotated/scaled tile layers - 模式 2: 2 个已旋转或缩放瓷贴层 + 模式 2: 2 个旋转/缩放图块层 Mode 3: Full 15-bit bitmap - 模式 3: 完整 15 位位图 + 模式 3: 15 位完整位图 Mode 4: Full 8-bit bitmap - 模式 4: 完整 8 位位图 + 模式 4: 8 位完整位图 @@ -1745,12 +1770,12 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Unlocked HBlank - 已解锁 HBlank + 已解锁的 HBlank Linear OBJ tile mapping - 线性 OBJ 瓷贴映射 + 线性 OBJ 图块映射 @@ -1851,7 +1876,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Tile data base (* 16kB) - 瓷贴数据基 (* 16kB) + 图块数据基 (* 16kB) @@ -1875,7 +1900,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Tile map base (* 2kB) - 瓷贴映射基 (* 2kB) + 图块映射基 (* 2kB) @@ -3224,7 +3249,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Tile base - 瓷贴基 + 图块基 @@ -3313,42 +3338,42 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 载入 - + All 全部 - + Load TBL 载入 TBL - + Save selected memory 保存所选内存 - + Failed to open output file: %1 打开输出文件失败: %1 - + Load memory 载入内存 - + Failed to open input file: %1 打开输入文件失败: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3554,23 +3579,23 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 控制器 - + Shortcuts 快捷键 - - + + Shaders 着色器 - + Select BIOS 选择 BIOS - + (%1×%2) (%1×%2) @@ -3626,17 +3651,17 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 QGBA::VideoView - + Failed to open output video file: %1 打开输出视频文件失败: %1 - + Native (%0x%1) 原生 (%0x%1) - + Select output file 选择输出文件 @@ -3774,578 +3799,578 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 无法启动游戏。 - + Unimplemented BIOS call 未实现的 BIOS 调用 - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - 此游戏使用了尚未实现的 BIOS 调用。请使用官方 BIOS 以获得最佳体验。 + 此游戏使用尚未实现的 BIOS 调用。请使用官方 BIOS 以获得最佳体验。 - + Really make portable? 确定进行程序便携化? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? 进行此操作后,模拟器将从其可执行文件所在目录中载入模拟器配置。您想继续吗? - + Restart needed 需要重新启动 - + Some changes will not take effect until the emulator is restarted. - 更改将在模拟器下次启动时生效。 + 更改将在模拟器下次重新启动时生效。 - + - Player %1 of %2 - 玩家 %1 共 %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File 文件(&F) - + Load &ROM... 载入 ROM(&R)... - + Load ROM in archive... 从压缩文件中载入 ROM... - + Add folder to library... 将文件夹添加到库中... - + Load alternate save... 载入其他存档... - + Load temporary save... 载入临时存档... - + Load &patch... 载入补丁(&P)... - + Boot BIOS 引导 BIOS - + Replace ROM... 替换 ROM... - + Scan e-Reader dotcodes... 扫描 e-Reader 点码... - + ROM &info... ROM 信息(&I)... - + Recent 最近打开 - + Make portable 程序便携化 - + &Load state 载入即时存档(&L) - + Load state file... 载入即时存档文件... - + &Save state 保存即时存档(&S) - + Save state file... 保存即时存档文件... - + Quick load 快速读档 - + Quick save 快速存档 - + Load recent 载入最近存档 - + Save recent 保存最近存档 - + Undo load state 撤消读档 - + Undo save state 撤消存档 - - + + State &%1 即时存档 (&%1) - + Load camera image... 载入相机图片... - - Import GameShark Save - 导入 GameShark 存档 + + Import GameShark Save... + 导入 GameShark 存档... - - Export GameShark Save - 导出 GameShark 存档 + + Export GameShark Save... + 导出 GameShark 存档... - + New multiplayer window 新建多人游戏窗口 - + About... 关于... - + E&xit 退出(&X) - + &Emulation 模拟(&E) - + &Reset 重置(&R) - + Sh&utdown 关机(&U) - + Yank game pak 快速抽出游戏卡带 - + &Pause 暂停(&P) - + &Next frame 下一帧(&N) - + Fast forward (held) 快进 (长按) - + &Fast forward 快进(&F) - + Fast forward speed 快进速度 - + Unbounded 不限制 - + %0x %0x - + Rewind (held) 倒带 (长按) - + Re&wind 倒带(&W) - + Step backwards 步退 - + Sync to &video 视频同步(&V) - + Sync to &audio 音频同步(&A) - + Solar sensor 太阳光传感器 - + Increase solar level 增加太阳光等级 - + Decrease solar level 降低太阳光等级 - + Brightest solar level 太阳光等级为最亮 - + Darkest solar level 太阳光等级为最暗 - + Brightness %1 亮度 %1 - + Game Boy Printer... Game Boy 打印机... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video 音频/视频(&V) - + Frame size - 帧率 + 画面大小 - + %1× %1× - + Toggle fullscreen 切换全屏 - + Lock aspect ratio 锁定纵横比 - + Force integer scaling 强制整数缩放 - + Interframe blending 帧间混合 - + Bilinear filtering 双线性过滤 - + Frame&skip 跳帧(&S) - + Mute 静音 - + FPS target 目标 FPS - + Native (59.7275) 原生 (59.7275) - + Take &screenshot 截图(&S) - + F12 F12 - + Record A/V... 录制音频/视频... - - Record GIF... - 录制 GIF... + + Record GIF/WebP/APNG... + 录制 GIF/WebP/APNG... - + Video layers 视频图层 - + Audio channels 音频通道 - + Adjust layer placement... 调整图层布局... - + &Tools 工具(&T) - + View &logs... 查看日志(&L)... - + Game &overrides... - 覆盖游戏(&O)... + 覆写游戏(&O)... - + Game Pak sensors... 游戏卡带传感器... - + &Cheats... 作弊码(&C)... - + Settings... 设置... - + Open debugger console... 打开调试器控制台... - + Start &GDB server... 打开 GDB 服务器(&G)... - + View &palette... 查看调色板(&P)... - + View &sprites... 查看精灵图(&S)... - + View &tiles... - 查看瓷贴(&T)... + 查看图块(&T)... - + View &map... 查看映射(&M)... - + &Frame inspector... - 框架检查(&F)... + 框架检查器(&F)... - + View memory... 查看内存... - + Search memory... 搜索内存... - + View &I/O registers... 查看 I/O 寄存器(&I)... - + Record debug video log... 记录调试视频日志... - + Stop debug video log 停止记录调试视频日志 - + Exit fullscreen 退出全屏 - + GameShark Button (held) GameShark 键 (长按) - + Autofire 连发 - + Autofire A 连发 A - + Autofire B 连发 B - + Autofire L 连发 L - + Autofire R 连发 R - + Autofire Start 连发 Start - + Autofire Select 连发 Select - + Autofire Up 连发 上 - + Autofire Right 连发 右 - + Autofire Down 连发 下 - + Autofire Left 连发 左 - + Clear 清除 @@ -4755,7 +4780,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Allow opposing input directions - 允许逆向输入 + 允许相对方向输入 @@ -4780,7 +4805,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Enable Discord Rich Presence - 启用 Enable Discord Rich Presence + 启用 Discord Rich Presence @@ -4808,305 +4833,310 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 显示 OSD 信息 - + + Show filename instead of ROM name in title bar + 标题栏显示文件名而不显示 ROM 名称 + + + Fast forward speed: 快进速度: - - - + + + × × - - + + Unbounded 不限制 - + Fast forward (held) speed: 快进 (按住) 速度: - + Autofire interval: 连发间隔: - + Enable rewind 启用倒带 - + Rewind history: 倒带历史: - + Idle loops: 空循环: - + Run all 全部运行 - + Remove known 移除已知 - + Detect and remove 检测并移除 - + Preload entire ROM into memory 将整个 ROM 预载到内存中 - + Savestate extra data: 即时存档额外数据: - - + + Screenshot 截图 - - + + Save data 保存数据 - - + + Cheat codes 作弊码 - + Load extra data: 载入额外数据: - + Video renderer: 视频渲染器: - + Software 软件 - + OpenGL OpenGL - + OpenGL enhancements OpenGL 增强 - + High-resolution scale: 高分辨率比例: - + (240×160) (240×160) - + XQ GBA audio (experimental) - XQ GBA 音频 (实验) + XQ GBA 音频 (实验性) - + GB BIOS file: GB BIOS 文件: - - - - - - - - - + + + + + + + + + Browse 浏览 - + Use BIOS file if found 当可用时使用 BIOS 文件 - + Skip BIOS intro 跳过 BIOS 启动画面 - + GBA BIOS file: GBA BIOS 文件: - + GBC BIOS file: GBC BIOS 文件: - + SGB BIOS file: SGB BIOS 文件: - + Save games 游戏存档 - - - - - + + + + + Same directory as the ROM 与 ROM 所在目录相同 - + Save states 即时存档 - + Screenshots 截图 - + Patches 补丁 - + Cheats 作弊码 - + Log to file 记录日志到文件 - + Log to console 记录日志到控制台 - + Select Log File 选择日志文件 - + Game Boy model: Game Boy 型号: - - - - - Autodetect - 自动检测 - - - - - - Game Boy (DMG) - Game Boy (DMG) - - Super Game Boy (SGB) - Super Game Boy (SGB) + Autodetect + 自动检测 - Game Boy Color (CGB) - Game Boy Color (CGB) + Game Boy (DMG) + Game Boy (DMG) + Super Game Boy (SGB) + Super Game Boy (SGB) + + + + + + Game Boy Color (CGB) + Game Boy Color (CGB) + + + + + Game Boy Advance (AGB) Game Boy Advance (AGB) - + Super Game Boy model: Super Game Boy 型号: - + Game Boy Color model: Game Boy Color 型号: - + Default BG colors: 默认背景颜色: - + Super Game Boy borders Super Game Boy 边框 - + Camera driver: 相机驱动程序: - + Default sprite colors 1: 默认精灵图颜色 1: - + Default sprite colors 2: 默认精灵图颜色 2: - + Use GBC colors in GB games 在 GB 游戏中使用 GBC 颜色 - + Camera: 相机: @@ -5177,7 +5207,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Tiles - 瓷贴 + 图块 @@ -5207,7 +5237,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Tiles per row - 每行瓷贴 + 每行图块 diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index e27450fd1..13c5ebf2f 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -607,7 +607,7 @@ static int _batteryState(void) { u32 charge; int state = 0; if (R_SUCCEEDED(psmGetBatteryChargePercentage(&charge))) { - state = (charge + 12) / 25; + state = charge | BATTERY_PERCENTAGE_VALID; } else { return BATTERY_NOT_PRESENT; } diff --git a/src/platform/test/cinema-main.c b/src/platform/test/cinema-main.c index e565cd8f9..dc74992fa 100644 --- a/src/platform/test/cinema-main.c +++ b/src/platform/test/cinema-main.c @@ -226,6 +226,7 @@ void CIflush(struct StringBuilder* builder, FILE* out) { builder->repeat = 0; fputs(string, out); free(string); + fflush(out); } static bool parseCInemaArgs(int argc, char* const* argv) { @@ -736,7 +737,7 @@ static void _writeDiff(const char* testName, const struct CInemaImage* image, si return; } char name[32]; - snprintf(name, sizeof(name), "%s_%04" PRIz "u.png", type, frame); + snprintf(name, sizeof(name), "%s_%05" PRIz "u.png", type, frame); struct VFile* vf = dir->openFile(dir, name, O_CREAT | O_TRUNC | O_WRONLY); if (!vf) { CIerr(0, "Could not open output file %s\n", name); @@ -792,12 +793,21 @@ static bool _compareImages(struct CInemaTest* restrict test, const struct CInema for (x = 0; x < image->width; ++x) { size_t pix = expected->stride * y + x; size_t tpix = image->stride * y + x; +#ifndef __BIG_ENDIAN__ int testR = testPixels[tpix * 4 + 0]; int testG = testPixels[tpix * 4 + 1]; int testB = testPixels[tpix * 4 + 2]; int expectR = expectPixels[pix * 4 + 0]; int expectG = expectPixels[pix * 4 + 1]; int expectB = expectPixels[pix * 4 + 2]; +#else + int testB = testPixels[tpix * 4 + 1]; + int testG = testPixels[tpix * 4 + 2]; + int testR = testPixels[tpix * 4 + 3]; + int expectB = expectPixels[pix * 4 + 1]; + int expectG = expectPixels[pix * 4 + 2]; + int expectR = expectPixels[pix * 4 + 3]; +#endif int r = expectR - testR; int g = expectG - testG; int b = expectB - testB; @@ -828,9 +838,15 @@ static bool _compareImages(struct CInemaTest* restrict test, const struct CInema if (b > *max) { *max = b; } +#ifndef __BIG_ENDIAN__ diff[pix * 4 + 0] = r; diff[pix * 4 + 1] = g; diff[pix * 4 + 2] = b; +#else + diff[pix * 4 + 1] = b; + diff[pix * 4 + 2] = g; + diff[pix * 4 + 3] = r; +#endif } if (test) { @@ -864,13 +880,21 @@ void _writeDiffSet(struct CInemaImage* expected, const char* name, uint8_t* diff for (y = 0; y < outdiff.height; ++y) { for (x = 0; x < outdiff.width; ++x) { size_t pix = outdiff.stride * y + x; +#ifndef __BIG_ENDIAN__ diff[pix * 4 + 0] = diff[pix * 4 + 0] * 255 / max; diff[pix * 4 + 1] = diff[pix * 4 + 1] * 255 / max; diff[pix * 4 + 2] = diff[pix * 4 + 2] * 255 / max; +#else + diff[pix * 4 + 1] = diff[pix * 4 + 1] * 255 / max; + diff[pix * 4 + 2] = diff[pix * 4 + 2] * 255 / max; + diff[pix * 4 + 3] = diff[pix * 4 + 3] * 255 / max; +#endif } } if (xfail) { _writeDiff(name, &outdiff, frame, "xnormalized"); + } else { + _writeDiff(name, &outdiff, frame, "normalized"); } } @@ -975,7 +999,7 @@ void CInemaTestRun(struct CInemaTest* test) { core->setVideoBuffer(core, image.data, image.stride); mCoreConfigInit(&core->config, "cinema"); - unsigned limit = 9999; + unsigned limit = 3600; unsigned skip = 0; unsigned fail = 0; unsigned video = 0; @@ -1086,6 +1110,14 @@ void CInemaTestRun(struct CInemaTest* test) { if (!FFmpegDecoderRead(&decoder)) { CIlog(1, "Failed to read more frames. EOF?\n"); test->status = CI_FAIL; + if (rebaseline && !FFmpegEncoderIsOpen(&encoder)) { + _replayBaseline(test, &encoder, &image, frame); + if (test->status == CI_ERROR) { + break; + } + encoder.d.postVideoFrame(&encoder.d, image.data, image.stride); + core->setAVStream(core, &encoder.d); + } break; } } diff --git a/src/util/gui/font-metrics.c b/src/util/gui/font-metrics.c index 0e965a3e6..99bc2daf6 100644 --- a/src/util/gui/font-metrics.c +++ b/src/util/gui/font-metrics.c @@ -137,11 +137,11 @@ const struct GUIFontGlyphMetric defaultFontMetrics[128] = { }; const struct GUIIconMetric defaultIconMetrics[] = { - [GUI_ICON_BATTERY_FULL] = { 0, 0, 32, 16 }, - [GUI_ICON_BATTERY_HIGH] = { 32, 0, 32, 16 }, - [GUI_ICON_BATTERY_HALF] = { 64, 0, 32, 16 }, - [GUI_ICON_BATTERY_LOW] = { 96, 0, 32, 16 }, - [GUI_ICON_BATTERY_EMPTY] = { 128, 0, 32, 16 }, + [GUI_ICON_BATTERY_FULL] = { 0, 2, 32, 12 }, + [GUI_ICON_BATTERY_HIGH] = { 32, 2, 32, 12 }, + [GUI_ICON_BATTERY_HALF] = { 64, 2, 32, 12 }, + [GUI_ICON_BATTERY_LOW] = { 96, 2, 32, 12 }, + [GUI_ICON_BATTERY_EMPTY] = { 128, 2, 32, 12 }, [GUI_ICON_SCROLLBAR_BUTTON] = { 6, 16, 4, 5 }, [GUI_ICON_SCROLLBAR_TRACK] = { 23, 16, 2, 16 }, [GUI_ICON_SCROLLBAR_THUMB] = { 38, 16, 4, 16 }, diff --git a/src/util/gui/font.c b/src/util/gui/font.c index 05ceffa92..04a22153c 100644 --- a/src/util/gui/font.c +++ b/src/util/gui/font.c @@ -43,7 +43,7 @@ void GUIFontPrint(const struct GUIFont* font, int x, int y, enum GUIAlignment al if (c == '\1') { c = utf8Char(&text, &len); if (c < GUI_ICON_MAX) { - GUIFontDrawIcon(font, x, y, GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, c); + GUIFontDrawIcon(font, x, y, (align & GUI_ALIGN_HCENTER) | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, c); unsigned w; GUIFontIconMetrics(font, c, &w, 0); x += w; diff --git a/src/util/gui/menu.c b/src/util/gui/menu.c index 68ba7781f..63aa4747d 100644 --- a/src/util/gui/menu.c +++ b/src/util/gui/menu.c @@ -264,20 +264,20 @@ void GUIDrawBattery(struct GUIParams* params) { return; } uint32_t color = 0xFF000000; - if (state == (BATTERY_CHARGING | BATTERY_FULL)) { + if ((state & (BATTERY_CHARGING | BATTERY_FULL)) == (BATTERY_CHARGING | BATTERY_FULL)) { color |= 0xFFC060; } else if (state & BATTERY_CHARGING) { color |= 0x60FF60; - } else if (state >= BATTERY_HALF) { + } else if ((state & BATTERY_VALUE) >= BATTERY_HALF) { color |= 0xFFFFFF; - } else if (state == BATTERY_LOW) { + } else if ((state & BATTERY_VALUE) >= BATTERY_LOW) { color |= 0x30FFFF; } else { color |= 0x3030FF; } enum GUIIcon batteryIcon; - switch (state & ~BATTERY_CHARGING) { + switch ((state & BATTERY_VALUE) - (state & BATTERY_VALUE) % 25) { case BATTERY_EMPTY: batteryIcon = GUI_ICON_BATTERY_EMPTY; break; @@ -298,7 +298,12 @@ void GUIDrawBattery(struct GUIParams* params) { break; } - GUIFontDrawIcon(params->font, params->width, 0, GUI_ALIGN_RIGHT, GUI_ORIENT_0, color, batteryIcon); + GUIFontDrawIcon(params->font, params->width, GUIFontHeight(params->font) + 2, GUI_ALIGN_RIGHT | GUI_ALIGN_BOTTOM, GUI_ORIENT_0, color, batteryIcon); + if (state & BATTERY_PERCENTAGE_VALID) { + unsigned width; + GUIFontIconMetrics(params->font, batteryIcon, &width, NULL); + GUIFontPrintf(params->font, params->width - width, GUIFontHeight(params->font), GUI_ALIGN_RIGHT, color, "%u%%", state & BATTERY_VALUE); + } } void GUIDrawClock(struct GUIParams* params) { diff --git a/src/util/vfs/vfs-fd.c b/src/util/vfs/vfs-fd.c index dde239717..8460a0253 100644 --- a/src/util/vfs/vfs-fd.c +++ b/src/util/vfs/vfs-fd.c @@ -137,6 +137,7 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags) { static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) { UNUSED(vf); + msync(memory, size, MS_SYNC); munmap(memory, size); } #else @@ -167,6 +168,7 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags) { static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) { UNUSED(size); struct VFileFD* vfd = (struct VFileFD*) vf; + FlushViewOfFile(memory, size); size_t i; for (i = 0; i < HandleMappingListSize(&vfd->handles); ++i) { if (HandleMappingListGetPointer(&vfd->handles, i)->mapping == memory) { @@ -214,6 +216,9 @@ static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) { GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); SetFileTime(h, NULL, &ft, &ft); + if (buffer && size) { + FlushViewOfFile(buffer, size); + } return FlushFileBuffers(h); #endif }