diff --git a/.travis-deps.sh b/.travis-deps.sh index 27f885203..7a93e2dfd 100755 --- a/.travis-deps.sh +++ b/.travis-deps.sh @@ -1,7 +1,7 @@ #!/bin/sh if [ $TRAVIS_OS_NAME = "osx" ]; then brew update - brew install qt5 ffmpeg sdl2 libedit libelf libpng libzip + brew install qt5 ffmpeg sdl2 libelf libpng libzip else sudo apt-get update sudo apt-get -y install libseccomp2 diff --git a/CHANGES b/CHANGES index e29fe29b5..a2d4847ea 100644 --- a/CHANGES +++ b/CHANGES @@ -19,34 +19,65 @@ Misc: - DS Video: Simplify VRAM mapping 0.9.0: (Future) +Features: + - Add APNG recording Emulation fixes: - ARM: Fix ALU reading PC after shifting - ARM: Fix STR storing PC after address calculation + - GBA BIOS: Implement dummy sound driver calls + - GBA BIOS: Improve HLE BIOS timing - GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320) - - GBA Memory: Misaligned SRAM writes are ignored - GBA Memory: Improve gamepak prefetch timing - - GBA Serialize: Fix serializing DMA transfer register - - GBA Serialize: Fix audio serialization for desynced FIFOs - - GBA Serialize: Fix audio DMA timing deserialization - - GBA Video: Fix OAM not invalidating after reset (fixes mgba.io/i/1630) - GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319) - GBA Video: Fix Hblank timing +Other fixes: + - Core: Ensure ELF regions can be written before trying + - Core: Fix ELF loading regression (fixes mgba.io/i/1669) + - Debugger: Don't skip undefined instructions when debugger attached + - Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642) +Misc: + - FFmpeg: Add looping option for GIF/APNG + - 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 + +0.8.1: (2020-02-16) +Emulation fixes: + - GB Serialize: Fix timing bug loading channel 4 timing + - GBA: Fix multiboot entry point while skipping BIOS + - GBA BIOS: Fix undefined instruction HLE behavior + - GBA DMA: Fix invalid audio DMA parameters + - GBA Memory: Misaligned SRAM writes are ignored + - GBA Serialize: Fix serializing DMA transfer register + - GBA Serialize: Fix audio DMA timing deserialization + - GBA Video: Fix OAM not invalidating after reset (fixes mgba.io/i/1630) - GBA Video: Fix backdrop blending on lines without sprites (fixes mgba.io/i/1647) - GBA Video: Fix OpenGL sprite flag priority Other fixes: - Core: Fix race condition initializing thread proxy + - Core: Fix integer overflow in ELF loading + - FFmpeg: Fix crash when -strict -2 is needed for vcodec or container + - FFmpeg: Disallow recording video with no audio nor video + - GBA: Automatically skip BIOS for multiboot ROMs - Qt: Only dynamically reset video scale if a game is running - Qt: Fix race condition with proxied video events - - Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642) - Qt: Fix color selection in asset view (fixes mgba.io/i/1648) - Qt: Fix missing OSD messages - Qt: Fix crash unloading shaders - Qt: Fix toggled actions on gamepads (fixes mgba.io/i/1650) - Qt: Fix extraneous dialog (fixes mgba.io/i/1654) + - Qt: Fix window title not updating after shutting down game + - Qt: Fix GIF view not allowing manual filename entry + - Qt: Fix non-GB build (fixes mgba.io/i/1664) + - Qt: Fix pausing Qt Multimedia audio (fixes mgba.io/i/1643) + - Qt: Fix invalid names for modifier keys (fixes mgba.io/i/525) + - SDL: Refresh stale pointers after adding a joystick (fixes mgba.io/i/1622) - Util: Fix crash reading invalid ELFs + - VFS: Fix handle leak when double-mapping (fixes mgba.io/i/1659) Misc: - - Qt: Renderer can be changed while a game is running + - FFmpeg: Add more presets - Qt: Fix non-SDL build (fixes mgba.io/i/1656) + - SDL: Use DirectSound audio driver by default on Windows - Switch: Make OpenGL scale adjustable while running 0.8.0: (2020-01-21) diff --git a/CMakeLists.txt b/CMakeLists.txt index c6db7e13c..c28ff75da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -870,6 +870,9 @@ if(NOT SKIP_LIBRARY) target_link_libraries(${BINARY_NAME} ${DEBUGGER_LIB} ${DEPENDENCY_LIB} ${OS_LIB}) install(TARGETS ${BINARY_NAME} LIBRARY DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME} NAMELINK_SKIP ARCHIVE DESTINATION ${LIBDIR} RUNTIME DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME}) + if(BUILD_SHARED) + install(TARGETS ${BINARY_NAME} LIBRARY DESTINATION ${LIBDIR} COMPONENT ${BINARY_NAME}-dev NAMELINK_ONLY) + endif() if(UNIX AND NOT APPLE AND NOT HAIKU) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/mgba-16.png DESTINATION share/icons/hicolor/16x16/apps RENAME mgba.png COMPONENT lib${BINARY_NAME}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/res/mgba-24.png DESTINATION share/icons/hicolor/24x24/apps RENAME mgba.png COMPONENT lib${BINARY_NAME}) diff --git a/include/mgba-util/socket.h b/include/mgba-util/socket.h index e0c52fb99..aecbe9959 100644 --- a/include/mgba-util/socket.h +++ b/include/mgba-util/socket.h @@ -20,15 +20,21 @@ CXX_GUARD_START #define SOCKET_FAILED(s) ((s) == INVALID_SOCKET) typedef SOCKET Socket; #else +#ifdef GEKKO +#include +#else #include -#include -#include #include #include -#include #include +#endif +#include +#include +#include +#ifndef GEKKO #define INVALID_SOCKET (-1) +#endif #define SOCKET_FAILED(s) ((s) < 0) typedef int Socket; #endif @@ -70,6 +76,8 @@ static inline void SocketSubsystemInit() { } #elif defined(__SWITCH__) socketInitializeDefault(); +#elif defined(GEKKO) + net_init(); #endif } @@ -82,6 +90,8 @@ static inline void SocketSubsystemDeinit() { SOCUBuffer = NULL; #elif defined(__SWITCH__) socketExit(); +#elif defined(GEKKO) + net_deinit(); #endif } @@ -104,6 +114,8 @@ static inline bool SocketWouldBlock() { static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size) { #ifdef _WIN32 return send(socket, (const char*) buffer, size, 0); +#elif defined(GEKKO) + return net_write(socket, buffer, size); #else return write(socket, buffer, size); #endif @@ -112,6 +124,8 @@ static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size) static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) { #if defined(_WIN32) || defined(__SWITCH__) return recv(socket, (char*) buffer, size, 0); +#elif defined(GEKKO) + return net_read(socket, buffer, size); #else return read(socket, buffer, size); #endif @@ -120,13 +134,19 @@ static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) { static inline int SocketClose(Socket socket) { #ifdef _WIN32 return closesocket(socket) == 0; +#elif defined(GEKKO) + return net_close(socket) >= 0; #else return close(socket) >= 0; #endif } static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) { +#ifdef GEKKO + Socket sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP); +#else Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +#endif if (SOCKET_FAILED(sock)) { return sock; } @@ -142,15 +162,23 @@ static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) #else bindInfo.sin_addr.s_addr = gethostid(); #endif +#ifdef GEKKO + err = net_bind(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo)); +#else err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); +#endif } else if (bindAddress->version == IPV4) { struct sockaddr_in bindInfo; memset(&bindInfo, 0, sizeof(bindInfo)); bindInfo.sin_family = AF_INET; bindInfo.sin_port = htons(port); bindInfo.sin_addr.s_addr = htonl(bindAddress->ipv4); +#ifdef GEKKO + err = net_bind(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo)); +#else err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); -#ifndef _3DS +#endif +#if !defined(_3DS) && !defined(GEKKO) } else { struct sockaddr_in6 bindInfo; memset(&bindInfo, 0, sizeof(bindInfo)); @@ -168,7 +196,11 @@ static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) } static inline Socket SocketConnectTCP(int port, const struct Address* destinationAddress) { +#ifdef GEKKO + Socket sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP); +#else Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); +#endif if (SOCKET_FAILED(sock)) { return sock; } @@ -179,15 +211,23 @@ static inline Socket SocketConnectTCP(int port, const struct Address* destinatio memset(&bindInfo, 0, sizeof(bindInfo)); bindInfo.sin_family = AF_INET; bindInfo.sin_port = htons(port); +#ifdef GEKKO + err = net_connect(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo)); +#else err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); +#endif } else if (destinationAddress->version == IPV4) { struct sockaddr_in bindInfo; memset(&bindInfo, 0, sizeof(bindInfo)); bindInfo.sin_family = AF_INET; bindInfo.sin_port = htons(port); bindInfo.sin_addr.s_addr = htonl(destinationAddress->ipv4); +#ifdef GEKKO + err = net_connect(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo)); +#else err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo)); -#ifndef _3DS +#endif +#if !defined(_3DS) && !defined(GEKKO) } else { struct sockaddr_in6 bindInfo; memset(&bindInfo, 0, sizeof(bindInfo)); @@ -206,12 +246,23 @@ static inline Socket SocketConnectTCP(int port, const struct Address* destinatio } static inline Socket SocketListen(Socket socket, int queueLength) { +#ifdef GEKKO + return net_listen(socket, queueLength); +#else return listen(socket, queueLength); +#endif } static inline Socket SocketAccept(Socket socket, struct Address* address) { if (!address) { +#ifdef GEKKO + struct sockaddr_in addrInfo; + memset(&addrInfo, 0, sizeof(addrInfo)); + socklen_t len = sizeof(addrInfo); + return net_accept(socket, (struct sockaddr*) &addrInfo, &len); +#else return accept(socket, 0, 0); +#endif } if (address->version == IPV4) { struct sockaddr_in addrInfo; @@ -219,8 +270,12 @@ static inline Socket SocketAccept(Socket socket, struct Address* address) { addrInfo.sin_family = AF_INET; addrInfo.sin_addr.s_addr = address->ipv4; socklen_t len = sizeof(addrInfo); +#ifdef GEKKO + return net_accept(socket, (struct sockaddr*) &addrInfo, &len); +#else return accept(socket, (struct sockaddr*) &addrInfo, &len); -#ifndef _3DS +#endif +#if !defined(_3DS) && !defined(GEKKO) } else { struct sockaddr_in6 addrInfo; memset(&addrInfo, 0, sizeof(addrInfo)); @@ -237,8 +292,12 @@ static inline int SocketSetBlocking(Socket socket, bool blocking) { #ifdef _WIN32 u_long unblocking = !blocking; return ioctlsocket(socket, FIONBIO, &unblocking) == NO_ERROR; +#else +#ifdef GEKKO + int flags = net_fcntl(socket, F_GETFL, 0); #else int flags = fcntl(socket, F_GETFL); +#endif if (flags == -1) { return 0; } @@ -247,12 +306,20 @@ static inline int SocketSetBlocking(Socket socket, bool blocking) { } else { flags |= O_NONBLOCK; } +#ifdef GEKKO + return net_fcntl(socket, F_SETFL, flags) >= 0; +#else return fcntl(socket, F_SETFL, flags) >= 0; #endif +#endif } static inline int SocketSetTCPPush(Socket socket, int push) { +#ifdef GEKKO + return net_setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0; +#else return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0; +#endif } static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Socket* errors, int64_t timeoutMillis) { @@ -304,7 +371,11 @@ static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Soc struct timeval tv; tv.tv_sec = timeoutMillis / 1000; tv.tv_usec = (timeoutMillis % 1000) * 1000; +#ifdef GEKKO + int result = net_select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv); +#else int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv); +#endif int r = 0; int w = 0; int e = 0; diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index 2483cfbc2..5f57988a5 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -208,6 +208,7 @@ void mCoreLoadForeignConfig(struct mCore* core, const struct mCoreConfig* config void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc); void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size); +void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask); #ifdef USE_ELF struct ELF; diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index 7614bc2ae..0897c62df 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -176,6 +176,7 @@ enum mCoreMemoryBlockFlags { mCORE_MEMORY_READ = 0x01, mCORE_MEMORY_WRITE = 0x02, mCORE_MEMORY_RW = 0x03, + mCORE_MEMORY_WORM = 0x04, mCORE_MEMORY_MAPPED = 0x10, mCORE_MEMORY_VIRTUAL = 0x20, }; diff --git a/include/mgba/internal/gb/input.h b/include/mgba/internal/gb/input.h index 4e507ddc3..2fdaa88c2 100644 --- a/include/mgba/internal/gb/input.h +++ b/include/mgba/internal/gb/input.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau +/* Copyright (c) 2013-2019 Jeffrey Pfau * * 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 @@ -24,7 +24,6 @@ enum GBKey { GB_KEY_UP = 6, GB_KEY_DOWN = 7, GB_KEY_MAX, - GB_KEY_NONE = -1 }; CXX_GUARD_END diff --git a/include/mgba/internal/gba/bios.h b/include/mgba/internal/gba/bios.h index a89edfd68..40bbf3237 100644 --- a/include/mgba/internal/gba/bios.h +++ b/include/mgba/internal/gba/bios.h @@ -14,6 +14,52 @@ CXX_GUARD_START mLOG_DECLARE_CATEGORY(GBA_BIOS); +enum GBASwi { + GBA_SWI_SOFT_RESET = 0x00, + GBA_SWI_REGISTER_RAM_RESET = 0x01, + GBA_SWI_HALT = 0x02, + GBA_SWI_STOP = 0x03, + GBA_SWI_INTR_WAIT = 0x04, + GBA_SWI_VBLANK_INTR_WAIT = 0x05, + GBA_SWI_DIV = 0x06, + GBA_SWI_DIV_ARM = 0x07, + GBA_SWI_SQRT = 0x08, + GBA_SWI_ARCTAN = 0x09, + GBA_SWI_ARCTAN2 = 0x0A, + GBA_SWI_CPU_SET = 0x0B, + GBA_SWI_CPU_FAST_SET = 0x0C, + GBA_SWI_GET_BIOS_CHECKSUM = 0x0D, + GBA_SWI_BG_AFFINE_SET = 0x0E, + GBA_SWI_OBJ_AFFINE_SET = 0x0F, + GBA_SWI_BIT_UNPACK = 0x10, + GBA_SWI_LZ77_UNCOMP_WRAM = 0x11, + GBA_SWI_LZ77_UNCOMP_VRAM = 0x12, + GBA_SWI_HUFFMAN_UNCOMP = 0x13, + GBA_SWI_RL_UNCOMP_WRAM = 0x14, + GBA_SWI_RL_UNCOMP_VRAM = 0x15, + GBA_SWI_DIFF_8BIT_UNFILTER_WRAM = 0x16, + GBA_SWI_DIFF_8BIT_UNFILTER_VRAM = 0x17, + GBA_SWI_DIFF_16BIT_UNFILTER = 0x18, + GBA_SWI_SOUND_BIAS = 0x19, + GBA_SWI_SOUND_DRIVER_INIT = 0x1A, + GBA_SWI_SOUND_DRIVER_MODE = 0x1B, + GBA_SWI_SOUND_DRIVER_MAIN = 0x1C, + GBA_SWI_SOUND_DRIVER_VSYNC = 0x1D, + GBA_SWI_SOUND_CHANNEL_CLEAR = 0x1E, + GBA_SWI_MIDI_KEY_2_FREQ = 0x1F, + GBA_SWI_MUSIC_PLAYER_OPEN = 0x20, + GBA_SWI_MUSIC_PLAYER_START = 0x21, + GBA_SWI_MUSIC_PLAYER_STOP = 0x22, + GBA_SWI_MUSIC_PLAYER_CONTINUE = 0x23, + GBA_SWI_MUSIC_PLAYER_FADE_OUT = 0x24, + GBA_SWI_MULTI_BOOT = 0x25, + GBA_SWI_HARD_RESET = 0x26, + GBA_SWI_CUSTOM_HALT = 0x27, + GBA_SWI_SOUND_DRIVER_VSYNC_OFF = 0x28, + GBA_SWI_SOUND_DRIVER_VSYNC_ON = 0x29, + GBA_SWI_SOUND_DRIVER_GET_JUMP_LIST = 0x2A, +}; + struct ARMCore; void GBASwi16(struct ARMCore* cpu, int immediate); void GBASwi32(struct ARMCore* cpu, int immediate); diff --git a/src/core/core.c b/src/core/core.c index 80b58a782..0e46421fd 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -351,6 +351,10 @@ void mCoreSetRTC(struct mCore* core, struct mRTCSource* rtc) { } void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size) { + return mCoreGetMemoryBlockMasked(core, start, size, mCORE_MEMORY_MAPPED); +} + +void* mCoreGetMemoryBlockMasked(struct mCore* core, uint32_t start, size_t* size, uint32_t mask) { const struct mCoreMemoryBlock* blocks; size_t nBlocks = core->listMemoryBlocks(core, &blocks); size_t i; @@ -358,6 +362,9 @@ void* mCoreGetMemoryBlock(struct mCore* core, uint32_t start, size_t* size) { if (!(blocks[i].flags & mCORE_MEMORY_MAPPED)) { continue; } + if (!(blocks[i].flags & mask)) { + continue; + } if (start < blocks[i].start) { continue; } @@ -381,9 +388,9 @@ bool mCoreLoadELF(struct mCore* core, struct ELF* elf) { for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) { size_t bsize, esize; Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i); - void* block = mCoreGetMemoryBlock(core, phdr->p_paddr, &bsize); + void* block = mCoreGetMemoryBlockMasked(core, phdr->p_paddr, &bsize, mCORE_MEMORY_WRITE | mCORE_MEMORY_WORM); char* bytes = ELFBytes(elf, &esize); - if (block && bsize >= phdr->p_filesz && esize >= phdr->p_filesz + phdr->p_offset) { + if (block && bsize >= phdr->p_filesz && esize > phdr->p_offset && esize >= phdr->p_filesz + phdr->p_offset) { memcpy(block, &bytes[phdr->p_offset], phdr->p_filesz); } else { return false; diff --git a/src/feature/ffmpeg/ffmpeg-encoder.c b/src/feature/ffmpeg/ffmpeg-encoder.c index abe563fe4..427cf8fb1 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.c +++ b/src/feature/ffmpeg/ffmpeg-encoder.c @@ -65,6 +65,7 @@ void FFmpegEncoderInit(struct FFmpegEncoder* encoder) { encoder->cycles = GBA_ARM7TDMI_FREQUENCY; encoder->frameskip = 1; encoder->skipResidue = 0; + encoder->loop = false; encoder->ipixFormat = #ifdef COLOR_16_BIT #ifdef COLOR_5_6_5 @@ -232,11 +233,15 @@ void FFmpegEncoderSetDimensions(struct FFmpegEncoder* encoder, int width, int he encoder->height = height > 0 ? height : GBA_VIDEO_VERTICAL_PIXELS; } +void FFmpegEncoderSetLooping(struct FFmpegEncoder* encoder, bool loop) { + encoder->loop = loop; +} + bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder* encoder) { AVOutputFormat* oformat = av_guess_format(encoder->containerFormat, 0, 0); AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec); AVCodec* vcodec = avcodec_find_encoder_by_name(encoder->videoCodec); - if ((encoder->audioCodec && !acodec) || (encoder->videoCodec && !vcodec) || !oformat) { + if ((encoder->audioCodec && !acodec) || (encoder->videoCodec && !vcodec) || !oformat || (!acodec && !vcodec)) { return false; } if (encoder->audioCodec && !avformat_query_codec(oformat, acodec->id, FF_COMPLIANCE_EXPERIMENTAL)) { @@ -449,8 +454,11 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { encoder->sinkFrame = avcodec_alloc_frame(); #endif } - - if (avcodec_open2(encoder->video, vcodec, 0) < 0) { + AVDictionary* opts = 0; + av_dict_set(&opts, "strict", "-2", 0); + int res = avcodec_open2(encoder->video, vcodec, &opts); + av_dict_free(&opts); + if (res < 0) { FFmpegEncoderClose(encoder); return false; } @@ -470,7 +478,17 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { #endif } - if (avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE) < 0 || avformat_write_header(encoder->context, 0) < 0) { + if (strcmp(encoder->containerFormat, "gif") == 0) { + av_opt_set(encoder->context->priv_data, "loop", encoder->loop ? "0" : "-1", 0); + } else if (strcmp(encoder->containerFormat, "apng") == 0) { + av_opt_set(encoder->context->priv_data, "plays", encoder->loop ? "0" : "1", 0); + } + + AVDictionary* opts = 0; + av_dict_set(&opts, "strict", "-2", 0); + bool res = avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE) < 0 || avformat_write_header(encoder->context, &opts) < 0; + av_dict_free(&opts); + if (res) { FFmpegEncoderClose(encoder); return false; } diff --git a/src/feature/ffmpeg/ffmpeg-encoder.h b/src/feature/ffmpeg/ffmpeg-encoder.h index 4b96ac42d..17d22a933 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.h +++ b/src/feature/ffmpeg/ffmpeg-encoder.h @@ -82,6 +82,7 @@ struct FFmpegEncoder { unsigned cycles; int frameskip; int skipResidue; + bool loop; int64_t currentVideoFrame; struct SwsContext* scaleContext; struct AVStream* videoStream; @@ -98,6 +99,7 @@ bool FFmpegEncoderSetAudio(struct FFmpegEncoder*, const char* acodec, unsigned a bool FFmpegEncoderSetVideo(struct FFmpegEncoder*, const char* vcodec, unsigned vbr, int frameskip); bool FFmpegEncoderSetContainer(struct FFmpegEncoder*, const char* container); void FFmpegEncoderSetDimensions(struct FFmpegEncoder*, int width, int height); +void FFmpegEncoderSetLooping(struct FFmpegEncoder*, bool loop); bool FFmpegEncoderVerifyContainer(struct FFmpegEncoder*); bool FFmpegEncoderOpen(struct FFmpegEncoder*, const char* outfile); void FFmpegEncoderClose(struct FFmpegEncoder*); diff --git a/src/gb/audio.c b/src/gb/audio.c index 63af6bc1a..4bc5fa351 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -1078,9 +1078,13 @@ void GBAudioPSGDeserialize(struct GBAudio* audio, const struct GBSerializedPSGSt LOAD_32LE(audio->ch4.lastEvent, 0, &state->ch4.lastEvent); LOAD_32LE(when, 0, &state->ch4.nextEvent); if (audio->ch4.envelope.dead < 2 && audio->playingCh4) { - if (when - audio->ch4.lastEvent > (uint32_t) audio->sampleInterval) { + if (!audio->ch4.lastEvent) { // Back-compat: fake this value - audio->ch4.lastEvent = when - audio->sampleInterval; + uint32_t currentTime = mTimingCurrentTime(audio->timing); + int32_t cycles = audio->ch4.ratio ? 2 * audio->ch4.ratio : 1; + cycles <<= audio->ch4.frequency; + cycles *= 8 * audio->timingFactor; + audio->ch4.lastEvent = currentTime + (when & (cycles - 1)) - cycles; } mTimingSchedule(audio->timing, &audio->ch4Event, when); } diff --git a/src/gb/core.c b/src/gb/core.c index fd4d42dac..c4adf099b 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -45,7 +45,7 @@ static const struct mCoreChannelInfo _GBAudioChannels[] = { static const struct mCoreMemoryBlock _GBMemoryBlocks[] = { { -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL }, - { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 }, + { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 }, { GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 }, { GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2 , GB_SIZE_WORKING_RAM_BANK0 * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, @@ -56,7 +56,7 @@ static const struct mCoreMemoryBlock _GBMemoryBlocks[] = { static const struct mCoreMemoryBlock _GBCMemoryBlocks[] = { { -1, "mem", "All", "All", 0, 0x10000, 0x10000, mCORE_MEMORY_VIRTUAL }, - { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 }, + { GB_REGION_CART_BANK0, "cart0", "ROM Bank", "Game Pak (32kiB)", GB_BASE_CART_BANK0, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 * 2, 0x800000, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED, 511, GB_BASE_CART_BANK0 + GB_SIZE_CART_BANK0 }, { GB_REGION_VRAM, "vram", "VRAM", "Video RAM (8kiB)", GB_BASE_VRAM, GB_BASE_VRAM + GB_SIZE_VRAM, GB_SIZE_VRAM * 2, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 }, { GB_REGION_EXTERNAL_RAM, "sram", "SRAM", "External RAM (8kiB)", GB_BASE_EXTERNAL_RAM, GB_BASE_EXTERNAL_RAM + GB_SIZE_EXTERNAL_RAM, GB_SIZE_EXTERNAL_RAM * 4, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 3 }, { GB_REGION_WORKING_RAM_BANK0, "wram", "WRAM", "Working RAM (8kiB)", GB_BASE_WORKING_RAM_BANK0, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 * 2, GB_SIZE_WORKING_RAM_BANK0 * 8, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 7, GB_BASE_WORKING_RAM_BANK0 + GB_SIZE_WORKING_RAM_BANK0 }, diff --git a/src/gb/debugger/cli.c b/src/gb/debugger/cli.c index 9ad6cdd7a..cba9f4ae2 100644 --- a/src/gb/debugger/cli.c +++ b/src/gb/debugger/cli.c @@ -16,13 +16,17 @@ static void _GBCLIDebuggerInit(struct CLIDebuggerSystem*); static bool _GBCLIDebuggerCustom(struct CLIDebuggerSystem*); static void _frame(struct CLIDebugger*, struct CLIDebugVector*); +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 static void _load(struct CLIDebugger*, struct CLIDebugVector*); static void _save(struct CLIDebugger*, struct CLIDebugVector*); +#endif struct CLIDebuggerCommandSummary _GBCLIDebuggerCommands[] = { { "frame", _frame, "", "Frame advance" }, +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 { "load", _load, "*", "Load a savestate" }, { "save", _save, "*", "Save a savestate" }, +#endif { 0, 0, 0, 0 } }; @@ -73,6 +77,7 @@ static void _frame(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { gbDebugger->inVblank = GBRegisterSTATGetMode(((struct GB*) gbDebugger->core->board)->memory.io[REG_STAT]) == 1; } +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { struct CLIDebuggerBackend* be = debugger->backend; if (!dv || dv->type != CLIDV_INT_TYPE) { @@ -106,3 +111,4 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { mCoreSaveState(gbDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC | SAVESTATE_METADATA); } +#endif diff --git a/src/gb/input.c b/src/gb/input.c index 6dcceb50e..572d4513f 100644 --- a/src/gb/input.c +++ b/src/gb/input.c @@ -1,4 +1,5 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau +/* Copyright (c) 2013-2019 Jeffrey Pfau +>>>>>>> master * * 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 @@ -17,7 +18,7 @@ const struct mInputPlatformInfo GBInputInfo = { "Right", "Left", "Up", - "Down" + "Down", }, .nKeys = GB_KEY_MAX, .hat = { diff --git a/src/gba/audio.c b/src/gba/audio.c index 23b5a475e..c0a275834 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -104,6 +104,8 @@ void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) { } void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info) { + info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED); + info->reg = GBADMARegisterSetWidth(info->reg, 1); switch (info->dest) { case BASE_IO | REG_FIFO_A_LO: audio->chA.dmaSource = number; @@ -129,8 +131,6 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* audio->externalMixing = false; } } - info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED); - info->reg = GBADMARegisterSetWidth(info->reg, 1); } void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) { diff --git a/src/gba/bios.c b/src/gba/bios.c index a264354eb..b3c89f388 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -424,43 +424,43 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { return; } switch (immediate) { - case 0x0: + case GBA_SWI_SOFT_RESET: _SoftReset(gba); break; - case 0x1: + case GBA_SWI_REGISTER_RAM_RESET: _RegisterRamReset(gba); break; - case 0x2: - GBAHalt(gba); - break; - case 0x3: + case GBA_SWI_HALT: + ARMRaiseSWI(cpu); + return; + case GBA_SWI_STOP: GBAStop(gba); break; - case 0x05: + case GBA_SWI_VBLANK_INTR_WAIT: // VBlankIntrWait // Fall through: - case 0x04: + case GBA_SWI_INTR_WAIT: // IntrWait ARMRaiseSWI(cpu); - break; - case 0x6: + return; + case GBA_SWI_DIV: _Div(gba, cpu->gprs[0], cpu->gprs[1]); break; - case 0x7: + case GBA_SWI_DIV_ARM: _Div(gba, cpu->gprs[1], cpu->gprs[0]); break; - case 0x8: + case GBA_SWI_SQRT: cpu->gprs[0] = _Sqrt(cpu->gprs[0], &cpu->cycles); break; - case 0x9: + case GBA_SWI_ARCTAN: cpu->gprs[0] = _ArcTan(cpu->gprs[0], &cpu->gprs[1], &cpu->gprs[3], &cpu->cycles); break; - case 0xA: + case GBA_SWI_ARCTAN2: cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1], &cpu->gprs[1], &cpu->cycles); cpu->gprs[3] = 0x170; break; - case 0xB: - case 0xC: + case GBA_SWI_CPU_SET: + case GBA_SWI_CPU_FAST_SET: if (cpu->gprs[0] >> BASE_OFFSET < REGION_WORKING_RAM) { mLOG(GBA_BIOS, GAME_ERROR, "Cannot CpuSet from BIOS"); break; @@ -472,19 +472,19 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { mLOG(GBA_BIOS, GAME_ERROR, "Misaligned CpuSet destination"); } ARMRaiseSWI(cpu); - break; - case 0xD: + return; + case GBA_SWI_GET_BIOS_CHECKSUM: cpu->gprs[0] = GBA_BIOS_CHECKSUM; cpu->gprs[1] = 1; cpu->gprs[3] = SIZE_BIOS; break; - case 0xE: + case GBA_SWI_BG_AFFINE_SET: _BgAffineSet(gba); break; - case 0xF: + case GBA_SWI_OBJ_AFFINE_SET: _ObjAffineSet(gba); break; - case 0x10: + case GBA_SWI_BIT_UNPACK: if (cpu->gprs[0] < BASE_WORKING_RAM) { mLOG(GBA_BIOS, GAME_ERROR, "Bad BitUnPack source"); break; @@ -500,8 +500,8 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { break; } break; - case 0x11: - case 0x12: + case GBA_SWI_LZ77_UNCOMP_WRAM: + case GBA_SWI_LZ77_UNCOMP_VRAM: if (cpu->gprs[0] < BASE_WORKING_RAM) { mLOG(GBA_BIOS, GAME_ERROR, "Bad LZ77 source"); break; @@ -513,11 +513,11 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { case REGION_WORKING_RAM: case REGION_WORKING_IRAM: case REGION_VRAM: - _unLz77(gba, immediate == 0x11 ? 1 : 2); + _unLz77(gba, immediate == GBA_SWI_LZ77_UNCOMP_WRAM ? 1 : 2); break; } break; - case 0x13: + case GBA_SWI_HUFFMAN_UNCOMP: if (cpu->gprs[0] < BASE_WORKING_RAM) { mLOG(GBA_BIOS, GAME_ERROR, "Bad Huffman source"); break; @@ -533,8 +533,8 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { break; } break; - case 0x14: - case 0x15: + case GBA_SWI_RL_UNCOMP_WRAM: + case GBA_SWI_RL_UNCOMP_VRAM: if (cpu->gprs[0] < BASE_WORKING_RAM) { mLOG(GBA_BIOS, GAME_ERROR, "Bad RL source"); break; @@ -546,13 +546,13 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { case REGION_WORKING_RAM: case REGION_WORKING_IRAM: case REGION_VRAM: - _unRl(gba, immediate == 0x14 ? 1 : 2); + _unRl(gba, immediate == GBA_SWI_RL_UNCOMP_WRAM ? 1 : 2); break; } break; - case 0x16: - case 0x17: - case 0x18: + case GBA_SWI_DIFF_8BIT_UNFILTER_WRAM: + case GBA_SWI_DIFF_8BIT_UNFILTER_VRAM: + case GBA_SWI_DIFF_16BIT_UNFILTER: if (cpu->gprs[0] < BASE_WORKING_RAM) { mLOG(GBA_BIOS, GAME_ERROR, "Bad UnFilter source"); break; @@ -564,17 +564,20 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { case REGION_WORKING_RAM: case REGION_WORKING_IRAM: case REGION_VRAM: - _unFilter(gba, immediate == 0x18 ? 2 : 1, immediate == 0x16 ? 1 : 2); + _unFilter(gba, immediate == GBA_SWI_DIFF_16BIT_UNFILTER ? 2 : 1, immediate == GBA_SWI_DIFF_8BIT_UNFILTER_WRAM ? 1 : 2); break; } break; - case 0x19: + case GBA_SWI_SOUND_BIAS: // SoundBias is mostly meaningless here mLOG(GBA_BIOS, STUB, "Stub software interrupt: SoundBias (19)"); break; - case 0x1F: + case GBA_SWI_MIDI_KEY_2_FREQ: _MidiKey2Freq(gba); break; + case GBA_SWI_SOUND_DRIVER_GET_JUMP_LIST: + ARMRaiseSWI(cpu); + return; default: mLOG(GBA_BIOS, STUB, "Stub software interrupt: %02X", immediate); } diff --git a/src/gba/core.c b/src/gba/core.c index a912d3a69..8daa53955 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -62,9 +62,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocks[] = { { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, }; static const struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = { @@ -76,9 +76,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = { { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, { REGION_CART_SRAM, "sram", "SRAM", "Static RAM (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_SRAM, SIZE_CART_SRAM, true }, }; @@ -91,9 +91,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash512[] = { { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, { REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, }; @@ -106,9 +106,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = { { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, { REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 }, }; @@ -121,9 +121,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = { { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, { REGION_CART_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, SIZE_CART_EEPROM, SIZE_CART_EEPROM, mCORE_MEMORY_RW }, }; @@ -647,7 +647,7 @@ static void _GBACoreReset(struct mCore* core) { #endif ARMReset(core->cpu); - if (core->opts.skipBios && (gba->romVf || gba->memory.rom)) { + if ((core->opts.skipBios && (gba->romVf || gba->memory.rom)) || (gba->romVf && GBAIsMB(gba->romVf))) { GBASkipBIOS(core->board); } } diff --git a/src/gba/debugger/cli.c b/src/gba/debugger/cli.c index 643a12b5c..5349911ab 100644 --- a/src/gba/debugger/cli.c +++ b/src/gba/debugger/cli.c @@ -16,13 +16,17 @@ static void _GBACLIDebuggerInit(struct CLIDebuggerSystem*); static bool _GBACLIDebuggerCustom(struct CLIDebuggerSystem*); static void _frame(struct CLIDebugger*, struct CLIDebugVector*); +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 static void _load(struct CLIDebugger*, struct CLIDebugVector*); static void _save(struct CLIDebugger*, struct CLIDebugVector*); +#endif struct CLIDebuggerCommandSummary _GBACLIDebuggerCommands[] = { { "frame", _frame, "", "Frame advance" }, +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 { "load", _load, "*", "Load a savestate" }, { "save", _save, "*", "Save a savestate" }, +#endif { 0, 0, 0, 0 } }; @@ -72,6 +76,7 @@ static void _frame(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { gbaDebugger->inVblank = GBARegisterDISPSTATGetInVblank(((struct GBA*) gbaDebugger->core->board)->memory.io[REG_DISPSTAT >> 1]); } +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 static void _load(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { struct CLIDebuggerBackend* be = debugger->backend; if (!dv || dv->type != CLIDV_INT_TYPE) { @@ -107,3 +112,4 @@ static void _save(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { mCoreSaveState(gbaDebugger->core, dv->intValue, SAVESTATE_SCREENSHOT | SAVESTATE_RTC | SAVESTATE_METADATA); } +#endif diff --git a/src/gba/gba.c b/src/gba/gba.c index aa078a226..95b9e4421 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -253,7 +253,7 @@ void GBASkipBIOS(struct GBA* gba) { if (gba->memory.rom) { cpu->gprs[ARM_PC] = BASE_CART0; } else { - cpu->gprs[ARM_PC] = BASE_WORKING_RAM; + cpu->gprs[ARM_PC] = BASE_WORKING_RAM + 0xC0; } gba->video.vcount = 0x7D; gba->memory.io[REG_VCOUNT >> 1] = 0x7D; @@ -741,11 +741,9 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) { .type.bp.opcode = opcode }; mDebuggerEnter(gba->debugger->d.p, DEBUGGER_ENTER_ILLEGAL_OP, &info); - } else -#endif - { - ARMRaiseUndefined(cpu); } +#endif + ARMRaiseUndefined(cpu); } void GBABreakpoint(struct ARMCore* cpu, int immediate) { diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index 776c3d550..920f7b92f 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -3,51 +3,74 @@ #include const uint8_t hleBios[SIZE_BIOS] = { - 0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x0b, 0x00, 0x00, 0xea, + 0x06, 0x00, 0x00, 0xea, 0x66, 0x00, 0x00, 0xea, 0x0b, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, - 0x2c, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0x03, 0xa0, 0xe3, - 0x03, 0x10, 0xd0, 0xe5, 0xea, 0x00, 0x51, 0xe3, 0xf8, 0x01, 0x9f, 0x15, + 0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0x03, 0xa0, 0xe3, + 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, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02, - 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0x9c, 0xc0, 0xa0, 0xe3, + 0x00, 0x58, 0x2d, 0xe9, 0x02, 0xb0, 0x5e, 0xe5, 0xd4, 0xc0, 0xa0, 0xe3, 0x0b, 0xb1, 0x9c, 0xe7, 0x00, 0x00, 0x5b, 0xe3, 0x00, 0xc0, 0x4f, 0xe1, 0x00, 0x10, 0x2d, 0xe9, 0x80, 0xc0, 0x0c, 0xe2, 0x1f, 0xc0, 0x8c, 0xe3, - 0x0c, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1, - 0x1b, 0xff, 0x2f, 0x11, 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, - 0x00, 0x10, 0xbd, 0xe8, 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8, - 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xa0, 0xe3, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, - 0xd4, 0x01, 0x00, 0x00, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, + 0x0c, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x00, 0xa0, 0xe1, + 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, 0x00, 0x00, 0xa0, 0xe1, + 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, 0x00, 0xa0, 0xe1, + 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, 0x00, 0x10, 0xbd, 0xe8, + 0x0c, 0xf0, 0x69, 0xe1, 0x00, 0x58, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x20, 0xa0, 0xe3, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb4, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xcc, 0x01, 0x00, 0x00, 0xc4, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0x14, 0x02, 0x00, 0x00, 0xa8, 0x02, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, + 0xb0, 0x01, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0xc0, 0x00, 0x00, 0x02, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, 0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x5e, 0xe5, - 0x01, 0x00, 0xa0, 0xe3, 0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, - 0x01, 0xc3, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, - 0x01, 0x20, 0xa0, 0xe3, 0x03, 0x00, 0x00, 0x0a, 0xb8, 0x30, 0x5c, 0xe1, - 0x01, 0x30, 0xc3, 0xe1, 0xb8, 0x30, 0x4c, 0xe1, 0x01, 0x03, 0xcc, 0xe5, - 0x08, 0x02, 0xcc, 0xe5, 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0x13, 0xe0, - 0x01, 0x30, 0x23, 0x10, 0xb8, 0x30, 0x4c, 0x11, 0x08, 0x22, 0xcc, 0xe5, - 0xf7, 0xff, 0xff, 0x0a, 0x0c, 0x80, 0xbd, 0xe8, 0x30, 0x40, 0x2d, 0xe9, - 0x02, 0x46, 0xa0, 0xe1, 0x00, 0xc0, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, - 0x01, 0x04, 0x12, 0xe3, 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, - 0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xbc, 0xe8, - 0x04, 0x00, 0x55, 0xe1, 0x08, 0x00, 0xa5, 0xb8, 0xfc, 0xff, 0xff, 0xba, - 0x14, 0x00, 0x00, 0xea, 0x01, 0xc0, 0xcc, 0xe3, 0x01, 0x50, 0xc5, 0xe3, - 0xa4, 0x45, 0x85, 0xe0, 0xb0, 0x30, 0xdc, 0xe1, 0x04, 0x00, 0x55, 0xe1, - 0xb2, 0x30, 0xc5, 0xb0, 0xfc, 0xff, 0xff, 0xba, 0x0c, 0x00, 0x00, 0xea, - 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, - 0x04, 0x00, 0x55, 0xe1, 0x08, 0x00, 0xbc, 0xb8, 0x08, 0x00, 0xa5, 0xb8, - 0xfb, 0xff, 0xff, 0xba, 0x04, 0x00, 0x00, 0xea, 0xa4, 0x45, 0x85, 0xe0, - 0x04, 0x00, 0x55, 0xe1, 0xb2, 0x30, 0xdc, 0xb0, 0xb2, 0x30, 0xc5, 0xb0, - 0xfb, 0xff, 0xff, 0xba, 0x17, 0x3e, 0xa0, 0xe3, 0x30, 0x80, 0xbd, 0xe8, - 0xf0, 0x47, 0x2d, 0xe9, 0x01, 0x04, 0x12, 0xe3, 0x02, 0x36, 0xa0, 0xe1, - 0x23, 0x25, 0x81, 0xe0, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x30, 0x90, 0xe5, - 0x03, 0x40, 0xa0, 0xe1, 0x03, 0x50, 0xa0, 0xe1, 0x03, 0x60, 0xa0, 0xe1, - 0x03, 0x70, 0xa0, 0xe1, 0x03, 0x80, 0xa0, 0xe1, 0x03, 0x90, 0xa0, 0xe1, - 0x03, 0xa0, 0xa0, 0xe1, 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xa1, 0xb8, - 0xfc, 0xff, 0xff, 0xba, 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x51, 0xe1, - 0xf8, 0x07, 0xb0, 0xb8, 0xf8, 0x07, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba, - 0xf0, 0x87, 0xbd, 0xe8, 0xc0, 0x00, 0x00, 0x02 + 0x04, 0xf0, 0x5e, 0xe2, 0x00, 0x00, 0x00, 0x00, 0x04, 0xe0, 0xa0, 0x03, + 0x1e, 0xff, 0x2f, 0xe1, 0x00, 0x20, 0xa0, 0xe3, 0x01, 0xc3, 0xa0, 0xe3, + 0x01, 0x23, 0xcc, 0xe5, 0x1e, 0xff, 0x2f, 0xe1, 0x01, 0x00, 0xa0, 0xe3, + 0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, 0x01, 0xc3, 0xa0, 0xe3, + 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, 0x01, 0x20, 0xa0, 0xe3, + 0x03, 0x00, 0x00, 0x0a, 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0xc3, 0xe1, + 0xb8, 0x30, 0x4c, 0xe1, 0x01, 0x03, 0xcc, 0xe5, 0x08, 0x02, 0xcc, 0xe5, + 0xb8, 0x30, 0x5c, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10, + 0xb8, 0x30, 0x4c, 0x11, 0x08, 0x22, 0xcc, 0xe5, 0xf7, 0xff, 0xff, 0x0a, + 0x0c, 0x80, 0xbd, 0xe8, 0x30, 0x40, 0x2d, 0xe9, 0x02, 0x46, 0xa0, 0xe1, + 0x00, 0xc0, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, 0x01, 0x04, 0x12, 0xe3, + 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, + 0x24, 0x45, 0x85, 0xe0, 0x08, 0x00, 0xbc, 0xe8, 0x04, 0x00, 0x55, 0xe1, + 0x08, 0x00, 0xa5, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x14, 0x00, 0x00, 0xea, + 0x01, 0xc0, 0xcc, 0xe3, 0x01, 0x50, 0xc5, 0xe3, 0xa4, 0x45, 0x85, 0xe0, + 0xb0, 0x30, 0xdc, 0xe1, 0x04, 0x00, 0x55, 0xe1, 0xb2, 0x30, 0xc5, 0xb0, + 0xfc, 0xff, 0xff, 0xba, 0x0c, 0x00, 0x00, 0xea, 0x01, 0x03, 0x12, 0xe3, + 0x05, 0x00, 0x00, 0x0a, 0x24, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1, + 0x08, 0x00, 0xbc, 0xb8, 0x08, 0x00, 0xa5, 0xb8, 0xfb, 0xff, 0xff, 0xba, + 0x04, 0x00, 0x00, 0xea, 0xa4, 0x45, 0x85, 0xe0, 0x04, 0x00, 0x55, 0xe1, + 0xb2, 0x30, 0xdc, 0xb0, 0xb2, 0x30, 0xc5, 0xb0, 0xfb, 0xff, 0xff, 0xba, + 0x17, 0x3e, 0xa0, 0xe3, 0x30, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9, + 0x01, 0x04, 0x12, 0xe3, 0x02, 0x36, 0xa0, 0xe1, 0x23, 0x25, 0x81, 0xe0, + 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x30, 0x90, 0xe5, 0x03, 0x40, 0xa0, 0xe1, + 0x03, 0x50, 0xa0, 0xe1, 0x03, 0x60, 0xa0, 0xe1, 0x03, 0x70, 0xa0, 0xe1, + 0x03, 0x80, 0xa0, 0xe1, 0x03, 0x90, 0xa0, 0xe1, 0x03, 0xa0, 0xa0, 0xe1, + 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, + 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xb0, 0xb8, + 0xf8, 0x07, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba, 0xf0, 0x87, 0xbd, 0xe8, + 0xf0, 0x07, 0x2d, 0xe9, 0x38, 0x10, 0x9f, 0xe5, 0x01, 0x30, 0xa0, 0xe1, + 0x01, 0x40, 0xa0, 0xe1, 0x01, 0x50, 0xa0, 0xe1, 0x01, 0x60, 0xa0, 0xe1, + 0x01, 0x70, 0xa0, 0xe1, 0x01, 0x80, 0xa0, 0xe1, 0x01, 0x90, 0xa0, 0xe1, + 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 }; diff --git a/src/gba/hle-bios.s b/src/gba/hle-bios.s index 96de3ad61..e8902cc69 100644 --- a/src/gba/hle-bios.s +++ b/src/gba/hle-bios.s @@ -40,8 +40,22 @@ and r12, #0x80 orr r12, #0x1F msr cpsr, r12 stmfd sp!, {lr} +nop +nop +nop +nop +nop +nop +nop +nop +nop +nop mov lr, pc bxne r11 +nop +nop +nop +nop ldmfd sp!, {lr} msr cpsr, #0x93 ldmfd sp!, {r12} @@ -52,20 +66,51 @@ movs pc, lr .word 0xE3A02004 swiTable: -.word SoftReset -.word RegisterRamReset -.word Halt -.word Stop -.word IntrWait -.word VBlankIntrWait -.word Div -.word DivArm -.word Sqrt -.word ArcTan -.word ArcTan2 -.word CpuSet -.word CpuFastSet -@ ... The rest of this table isn't needed if the rest aren't implemented +.word SoftReset @ 0x00 +.word RegisterRamReset @ 0x01 +.word Halt @ 0x02 +.word Stop @ 0x03 +.word IntrWait @ 0x04 +.word VBlankIntrWait @ 0x05 +.word Div @ 0x06 +.word DivArm @ 0x07 +.word Sqrt @ 0x08 +.word ArcTan @ 0x09 +.word ArcTan2 @ 0x0A +.word CpuSet @ 0x0B +.word CpuFastSet @ 0x0C +.word GetBiosChecksum @ 0x0D +.word BgAffineSet @ 0x0E +.word ObjAffineSet @ 0x0F +.word BitUnPack @ 0x10 +.word Lz77UnCompWram @ 0x11 +.word Lz77UnCompVram @ 0x12 +.word HuffmanUnComp @ 0x13 +.word RlUnCompWram @ 0x14 +.word RlUnCompVram @ 0x15 +.word Diff8BitUnFilterWram @ 0x16 +.word Diff8BitUnFilterVram @ 0x17 +.word Diff16BitUnFilter @ 0x18 +.word SoundBias @ 0x19 +.word SoundDriverInit @ 0x1A +.word SoundDriverMode @ 0x1B +.word SoundDriverMain @ 0x1C +.word SoundDriverVsync @ 0x1D +.word SoundChannelClear @ 0x1E +.word MidiKey2Freq @ 0x1F +.word MusicPlayerOpen @ 0x20 +.word MusicPlayerStart @ 0x21 +.word MusicPlayerStop @ 0x22 +.word MusicPlayerContinue @ 0x23 +.word MusicPlayerFadeOut @ 0x24 +.word MultiBoot @ 0x25 +.word HardReset @ 0x26 +.word CustomHalt @ 0x27 +.word SoundDriverVsyncOff @ 0x28 +.word SoundDriverVsyncOn @ 0x29 +.word SoundDriverGetJumpList @ 0x2A + +.ltorg irqBase: stmfd sp!, {r0-r3, r12, lr} @@ -77,6 +122,59 @@ subs pc, lr, #4 .word 0 .word 0xE55EC002 +undefBase: +subs pc, lr, #4 +.word 0 +.word 0x03A0E004 + +@ Unimplemented +SoftReset: +RegisterRamReset: +Stop: +Div: +DivArm: +Sqrt: +ArcTan: +ArcTan2: +GetBiosChecksum: +BgAffineSet: +ObjAffineSet: +BitUnPack: +Lz77UnCompWram: +Lz77UnCompVram: +HuffmanUnComp: +RlUnCompWram: +RlUnCompVram: +Diff8BitUnFilterWram: +Diff8BitUnFilterVram: +Diff16BitUnFilter: +SoundBias: +SoundDriverInit: +SoundDriverMode: +SoundDriverMain: +SoundDriverVsync: +SoundChannelClear: +MidiKey2Freq: +MusicPlayerOpen: +MusicPlayerStart: +MusicPlayerStop: +MusicPlayerContinue: +MusicPlayerFadeOut: +MultiBoot: +HardReset: +CustomHalt: +SoundDriverVsyncOff: +SoundDriverVsyncOn: + +NopCall: +bx lr + +Halt: +mov r2, #0 +mov r12, #0x04000000 +strb r2, [r12, #0x301] +bx lr + VBlankIntrWait: mov r0, #1 mov r1, #1 @@ -187,4 +285,23 @@ blt 0b 2: ldmfd sp!, {r4-r10, pc} +SoundDriverGetJumpList: +stmfd sp!, {r4-r10} +ldr r1, =NopCall +mov r3, r1 +mov r4, r1 +mov r5, r1 +mov r6, r1 +mov r7, r1 +mov r8, r1 +mov r9, r1 +mov r10, r1 +stmia r0!, {r1, r3-r10} +stmia r0!, {r1, r3-r10} +stmia r0!, {r1, r3-r10} +stmia r0!, {r1, r3-r10} +mov r1, #0 +ldmfd sp!, {r4-r10} +bx lr + .ltorg diff --git a/src/gba/video.c b/src/gba/video.c index f301c4fff..e35e2c8c8 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -369,6 +369,7 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState } else { video->event.callback = _startHblank; } + break; case 1: video->event.callback = _startHdraw; break; diff --git a/src/platform/qt/GIFView.cpp b/src/platform/qt/GIFView.cpp index 8a6d47fc7..a71a4b81b 100644 --- a/src/platform/qt/GIFView.cpp +++ b/src/platform/qt/GIFView.cpp @@ -25,10 +25,11 @@ GIFView::GIFView(QWidget* parent) connect(m_ui.selectFile, &QAbstractButton::clicked, this, &GIFView::selectFile); connect(m_ui.filename, &QLineEdit::textChanged, this, &GIFView::setFilename); + connect(m_ui.fmtGif, &QAbstractButton::clicked, this, &GIFView::changeExtension); + connect(m_ui.fmtApng, &QAbstractButton::clicked, this, &GIFView::changeExtension); FFmpegEncoderInit(&m_encoder); FFmpegEncoderSetAudio(&m_encoder, nullptr, 0); - FFmpegEncoderSetContainer(&m_encoder, "gif"); } GIFView::~GIFView() { @@ -44,14 +45,24 @@ void GIFView::setController(std::shared_ptr controller) { } void GIFView::startRecording() { - FFmpegEncoderSetVideo(&m_encoder, "gif", 0, m_ui.frameskip->value()); + if (m_ui.fmtApng->isChecked()) { + FFmpegEncoderSetContainer(&m_encoder, "apng"); + FFmpegEncoderSetVideo(&m_encoder, "apng", 0, m_ui.frameskip->value()); + } else { + FFmpegEncoderSetContainer(&m_encoder, "gif"); + FFmpegEncoderSetVideo(&m_encoder, "gif", 0, m_ui.frameskip->value()); + } + FFmpegEncoderSetLooping(&m_encoder, m_ui.loop->isChecked()); if (!FFmpegEncoderOpen(&m_encoder, m_filename.toUtf8().constData())) { - LOG(QT, ERROR) << tr("Failed to open output GIF file: %1").arg(m_filename); + LOG(QT, ERROR) << tr("Failed to open output GIF or APNG file: %1").arg(m_filename); return; } m_ui.start->setEnabled(false); m_ui.stop->setEnabled(true); m_ui.frameskip->setEnabled(false); + m_ui.loop->setEnabled(false); + m_ui.fmtApng->setEnabled(false); + m_ui.fmtGif->setEnabled(false); emit recordingStarted(&m_encoder.d); } @@ -59,22 +70,45 @@ void GIFView::stopRecording() { emit recordingStopped(); FFmpegEncoderClose(&m_encoder); m_ui.stop->setEnabled(false); - m_ui.start->setEnabled(true); + m_ui.start->setEnabled(!m_filename.isEmpty()); m_ui.frameskip->setEnabled(true); + m_ui.loop->setEnabled(true); + m_ui.fmtApng->setEnabled(true); + m_ui.fmtGif->setEnabled(true); } void GIFView::selectFile() { - QString filename = GBAApp::app()->getSaveFileName(this, tr("Select output file"), tr("Graphics Interchange Format (*.gif)")); - if (!filename.isEmpty()) { - m_ui.filename->setText(filename); - if (!FFmpegEncoderIsOpen(&m_encoder)) { - m_ui.start->setEnabled(true); + QString filename = GBAApp::app()->getSaveFileName(this, tr("Select output file"), tr("Graphics Interchange Format (*.gif);;Animated Portable Network Graphics (*.png *.apng)")); + m_ui.filename->setText(filename); +} + +void GIFView::setFilename(const QString& filename) { + m_filename = filename; + if (!FFmpegEncoderIsOpen(&m_encoder)) { + m_ui.start->setEnabled(!filename.isEmpty()); + if (filename.endsWith(".gif")) { + m_ui.fmtGif->setChecked(Qt::Checked); + } else if (filename.endsWith(".png") || filename.endsWith(".apng")) { + m_ui.fmtApng->setChecked(Qt::Checked); } } } -void GIFView::setFilename(const QString& fname) { - m_filename = fname; +void GIFView::changeExtension() { + if (m_filename.isEmpty()) { + return; + } + QString filename = m_filename; + int index = m_filename.lastIndexOf("."); + if (index >= 0) { + filename.truncate(index); + } + if (m_ui.fmtGif->isChecked()) { + filename += ".gif"; + } else if (m_ui.fmtApng->isChecked()) { + filename += ".png"; + } + m_ui.filename->setText(filename); } #endif diff --git a/src/platform/qt/GIFView.h b/src/platform/qt/GIFView.h index fc4ebd1a3..37f71fd93 100644 --- a/src/platform/qt/GIFView.h +++ b/src/platform/qt/GIFView.h @@ -41,6 +41,7 @@ signals: private slots: void selectFile(); void setFilename(const QString&); + void changeExtension(); private: Ui::GIFView m_ui; diff --git a/src/platform/qt/GIFView.ui b/src/platform/qt/GIFView.ui index 57dab1cd7..7c43319a2 100644 --- a/src/platform/qt/GIFView.ui +++ b/src/platform/qt/GIFView.ui @@ -7,50 +7,42 @@ 0 0 392 - 220 + 262 - Record GIF + Record GIF/APNG QLayout::SetFixedSize - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - + + - Frameskip + APNG + + format + - + + + + 0 + 0 + + + + 9 + 2 - - - - QDialogButtonBox::Close - - - @@ -123,8 +115,55 @@ + + + + Frameskip + + + + + + + QDialogButtonBox::Close + + + + + + + GIF + + + true + + + format + + + + + + + Loop + + + true + + + + + filename + start + stop + selectFile + fmtGif + fmtApng + loop + frameskip + @@ -144,4 +183,7 @@ + + + diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index 03e0503bc..bd867b856 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -9,6 +9,7 @@ #include "GamepadButtonEvent.h" #include "InputIndex.h" +#include #include #include @@ -32,7 +33,35 @@ void KeyEditor::setValue(int key) { if (key < 0) { setText(tr("---")); } else { - setText(QKeySequence(key).toString(QKeySequence::NativeText)); + QKeySequence seq(key); + switch (key) { +#ifndef Q_OS_MAC + case Qt::Key_Shift: + setText(QCoreApplication::translate("QShortcut", "Shift")); + break; + case Qt::Key_Control: + setText(QCoreApplication::translate("QShortcut", "Control")); + break; + case Qt::Key_Alt: + setText(QCoreApplication::translate("QShortcut", "Alt")); + break; + case Qt::Key_Meta: + setText(QCoreApplication::translate("QShortcut", "Meta")); + break; +#endif + case Qt::Key_Super_L: + setText(tr("Super (L)")); + break; + case Qt::Key_Super_R: + setText(tr("Super (R)")); + break; + case Qt::Key_Menu: + setText(tr("Menu")); + break; + default: + setText(QKeySequence(key).toString(QKeySequence::NativeText)); + break; + } } } emit valueChanged(key); diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index ca2f048d3..352e9d325 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -16,11 +16,13 @@ using namespace QGBA; +#ifdef M_CORE_GB MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node) : controller(coreController) , gbNode(node) { } +#endif #ifdef M_CORE_GBA MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node) diff --git a/src/platform/qt/ObjView.cpp b/src/platform/qt/ObjView.cpp index b3623a799..3044f9259 100644 --- a/src/platform/qt/ObjView.cpp +++ b/src/platform/qt/ObjView.cpp @@ -44,6 +44,10 @@ ObjView::ObjView(std::shared_ptr controller, QWidget* parent) m_ui.priority->setFont(font); m_ui.palette->setFont(font); m_ui.transform->setFont(font); + m_ui.xformPA->setFont(font); + m_ui.xformPB->setFont(font); + m_ui.xformPC->setFont(font); + m_ui.xformPD->setFont(font); m_ui.mode->setFont(font); connect(m_ui.tiles, &TilePainter::indexPressed, this, &ObjView::translateIndex); @@ -157,9 +161,23 @@ void ObjView::updateTilesGBA(bool force) { m_ui.mosaic->setChecked(GBAObjAttributesAIsMosaic(obj->a)); if (GBAObjAttributesAIsTransformed(obj->a)) { - m_ui.transform->setText(QString::number(GBAObjAttributesBGetMatIndex(obj->b))); + int mtxId = GBAObjAttributesBGetMatIndex(obj->b); + struct GBAOAMMatrix mat; + LOAD_16LE(mat.a, 0, &gba->video.oam.mat[mtxId].a); + LOAD_16LE(mat.b, 0, &gba->video.oam.mat[mtxId].b); + LOAD_16LE(mat.c, 0, &gba->video.oam.mat[mtxId].c); + LOAD_16LE(mat.d, 0, &gba->video.oam.mat[mtxId].d); + m_ui.transform->setText(QString::number(mtxId)); + m_ui.xformPA->setText(QString("%0").arg(mat.a / 256., 5, 'f', 2)); + m_ui.xformPB->setText(QString("%0").arg(mat.b / 256., 5, 'f', 2)); + m_ui.xformPC->setText(QString("%0").arg(mat.c / 256., 5, 'f', 2)); + m_ui.xformPD->setText(QString("%0").arg(mat.d / 256., 5, 'f', 2)); } else { m_ui.transform->setText(tr("Off")); + m_ui.xformPA->setText(tr("---")); + m_ui.xformPB->setText(tr("---")); + m_ui.xformPC->setText(tr("---")); + m_ui.xformPD->setText(tr("---")); } switch (GBAObjAttributesAGetMode(obj->a)) { @@ -230,6 +248,10 @@ void ObjView::updateTilesGB(bool force) { m_ui.doubleSize->setChecked(false); m_ui.mosaic->setChecked(false); m_ui.transform->setText(tr("N/A")); + m_ui.xformPA->setText(tr("---")); + m_ui.xformPB->setText(tr("---")); + m_ui.xformPC->setText(tr("---")); + m_ui.xformPD->setText(tr("---")); m_ui.mode->setText(tr("N/A")); } #endif diff --git a/src/platform/qt/ObjView.ui b/src/platform/qt/ObjView.ui index 7584deff6..bac5eac66 100644 --- a/src/platform/qt/ObjView.ui +++ b/src/platform/qt/ObjView.ui @@ -13,7 +13,81 @@ Sprites - + + + + + QFrame::StyledPanel + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 8 + 8 + + + + + + + + + + + + + + 0 + 0 + + + + 127 + + + + + + + Address + + + + + + + 0x07000000 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + @@ -21,13 +95,49 @@ + + + + + + + 0 + 0 + + + + × + + + 1 + + + 8 + + + + + + + Magnification + + + + + + + + 0 + 0 + + Geometry - - + + @@ -93,7 +203,14 @@ - + + + + Qt::Vertical + + + + @@ -159,50 +276,86 @@ - - - - - - - QFrame::StyledPanel - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 8 - 8 - - - + + + + + + +0.00 + + + + + + + +1.00 + + + + + + + Matrix + + + + + + + +1.00 + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + +0.00 + + + + - - - - Tile + + + + + 64 + 64 + + + + QListView::LeftToRight + + + QListView::Adjust + + + + 64 + 96 + + + + QListView::IconMode + + + true @@ -215,6 +368,12 @@ + + + 0 + 0 + + Attributes @@ -517,98 +676,10 @@ - - - - - - - 0 - 0 - - - - 127 - - - - - - - Address - - - - - - - 0x07000000 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - - - - - 0 - 0 - - - - × - - - 1 - - - 8 - - - - - - - Magnification - - - - - - - - - - 64 - 64 - - - - QListView::LeftToRight - - - QListView::Adjust - - - - 64 - 96 - - - - QListView::IconMode - - - true + + + + Tile diff --git a/src/platform/qt/PaletteView.cpp b/src/platform/qt/PaletteView.cpp index 394a54e7e..9a7a1206b 100644 --- a/src/platform/qt/PaletteView.cpp +++ b/src/platform/qt/PaletteView.cpp @@ -120,7 +120,7 @@ void PaletteView::selectIndex(int index) { hexcode |= (hexcode >> 5) & 0x070707; m_ui.hexcode->setText(tr("#%0").arg(hexcode, 6, 16, QChar('0'))); m_ui.value->setText(tr("0x%0").arg(color, 4, 16, QChar('0'))); - m_ui.index->setText(tr("%0").arg(index, 3, 10, QChar('0'))); + m_ui.index->setText(tr("0x%0 (%1)").arg(index, 3, 16, QChar('0')).arg(index, 3, 10, QChar('0'))); m_ui.r->setText(tr("0x%0 (%1)").arg(r, 2, 16, QChar('0')).arg(r, 2, 10, QChar('0'))); m_ui.g->setText(tr("0x%0 (%1)").arg(g, 2, 16, QChar('0')).arg(g, 2, 10, QChar('0'))); m_ui.b->setText(tr("0x%0 (%1)").arg(b, 2, 16, QChar('0')).arg(b, 2, 10, QChar('0'))); diff --git a/src/platform/qt/PaletteView.ui b/src/platform/qt/PaletteView.ui index 030c028e8..d8ae48601 100644 --- a/src/platform/qt/PaletteView.ui +++ b/src/platform/qt/PaletteView.ui @@ -6,7 +6,7 @@ 0 0 - 443 + 500 397 @@ -289,7 +289,7 @@ - 000 + 0x000 (000) Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/src/platform/qt/VideoView.cpp b/src/platform/qt/VideoView.cpp index 3a0b34ab1..b58c2b284 100644 --- a/src/platform/qt/VideoView.cpp +++ b/src/platform/qt/VideoView.cpp @@ -112,6 +112,15 @@ VideoView::VideoView(QWidget* parent) void VideoView::updatePresets() { m_presets.clear(); + addPreset(m_ui.preset4K, { + .container = QString(), + .vcodec = QString(), + .acodec = QString(), + .vbr = 0, + .abr = 0, + .dims = maintainAspect(QSize(3840, 2160)) + }); + addPreset(m_ui.preset1080, { .container = QString(), .vcodec = QString(), @@ -177,6 +186,14 @@ void VideoView::updatePresets() { .abr = 128 }); + addPreset(m_ui.presetMP4, { + .container = "MP4", + .vcodec = "h.264", + .acodec = "AAC", + .vbr = 800, + .abr = 128 + }); + if (m_nativeWidth && m_nativeHeight) { addPreset(m_ui.presetLossless, { .container = "MKV", diff --git a/src/platform/qt/VideoView.ui b/src/platform/qt/VideoView.ui index 24a3c5e75..0b1536e48 100644 --- a/src/platform/qt/VideoView.ui +++ b/src/platform/qt/VideoView.ui @@ -133,6 +133,13 @@ + + + + MP4 + + + @@ -150,6 +157,13 @@ + + + + 4K + + + @@ -289,6 +303,11 @@ FFV1 + + + None + + @@ -326,6 +345,11 @@ Uncompressed + + + None + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index bcfacbba3..27fd4437e 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -871,7 +871,6 @@ void Window::gameStopped() { action->setEnabled(false); } setWindowFilePath(QString()); - updateTitle(); detachWidget(m_display.get()); m_screenWidget->setCenteredAspectRatio(m_logo.width(), m_logo.height()); m_screenWidget->setDimensions(m_logo.width(), m_logo.height()); @@ -900,6 +899,7 @@ void Window::gameStopped() { m_display->stopDrawing(); m_controller.reset(); + updateTitle(); m_display->setVideoProxy({}); if (m_pendingClose) { @@ -1015,6 +1015,8 @@ void Window::reloadAudioDriver() { m_audioProcessor->start(); connect(m_controller.get(), &CoreController::stopping, m_audioProcessor.get(), &AudioProcessor::stop); connect(m_controller.get(), &CoreController::fastForwardChanged, m_audioProcessor.get(), &AudioProcessor::inputParametersChanged); + connect(m_controller.get(), &CoreController::paused, m_audioProcessor.get(), &AudioProcessor::pause); + connect(m_controller.get(), &CoreController::unpaused, m_audioProcessor.get(), &AudioProcessor::start); } void Window::changeRenderer() { @@ -1547,7 +1549,7 @@ void Window::setupMenu(QMenuBar* menubar) { #ifdef USE_FFMPEG addGameAction(tr("Record A/V..."), "recordOutput", this, &Window::openVideoWindow, "av"); - addGameAction(tr("Record GIF..."), "recordGIF", this, &Window::openGIFWindow, "av"); + addGameAction(tr("Record GIF/APNG..."), "recordGIF", this, &Window::openGIFWindow, "av"); #endif m_actions.addSeparator("av"); diff --git a/src/platform/qt/ts/mgba-zh_CN.ts b/src/platform/qt/ts/mgba-zh_CN.ts index c922c39e8..487b171ef 100644 --- a/src/platform/qt/ts/mgba-zh_CN.ts +++ b/src/platform/qt/ts/mgba-zh_CN.ts @@ -1441,7 +1441,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Incompatible deck - 不兼容的卡组 + 卡组不兼容 @@ -1480,6 +1480,11 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Add CodeBreaker 添加 CodeBreaker + + + Add GameShark + 添加 GameShark + Add GameGenie @@ -1495,24 +1500,24 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 QGBA::CoreController - + Failed to open save file: %1 - 无法打开存档: %1 + 打开存档失败: %1 - + Failed to open game file: %1 - 无法打开游戏文件: %1 + 打开游戏文件失败: %1 Failed to open snapshot file for reading: %1 - 无法读取快照文件: %1 + 读取快照文件失败: %1 Failed to open snapshot file for writing: %1 - 无法写入快照文件: %1 + 写入快照文件失败: %1 @@ -1520,7 +1525,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Failed to open game file: %1 - 无法打开游戏文件: %1 + 打开游戏文件失败: %1 @@ -1645,7 +1650,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Failed to open output GIF file: %1 - 无法打开输出的 GIF 文件: %1 + 打开输出 GIF 文件失败: %1 @@ -2612,7 +2617,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Increment and reload - 增量并重新加载 + 增量并重新载入 @@ -3110,47 +3115,47 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 QGBA::LogController - + [%1] %2: %3 [%1] %2: %3 - + An error occurred 发生错误 - + DEBUG DEBUG - + STUB STUB - + INFO INFO - + WARN WARN - + ERROR ERROR - + FATAL FATAL - + GAME ERROR GAME ERROR @@ -3223,17 +3228,17 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 - + N/A - + Export map 导出映射 - + Portable Network Graphics (*.png) 便携式网络图形 (*.png) @@ -3261,49 +3266,42 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 载入 - + All 全部 - + Load TBL 载入 TBL - - - N/A - - - - Save selected memory 保存所选内存 - + Failed to open output file: %1 - 无法打开输出文件: %1 + 打开输出文件失败: %1 - + Load memory 载入内存 - + Failed to open input file: %1 - 无法打开输入文件: %1 + 打开输入文件失败: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -3334,49 +3332,49 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 QGBA::ObjView - - + + 0x%0 0x%0 - + Off - + Normal 一般 - + Trans 变换 - + OBJWIN OBJWIN - + Invalid 无效 - + N/A - + Export sprite 导出精灵图 - + Portable Network Graphics (*.png) 便携式网络图形 (*.png) @@ -3517,7 +3515,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 选择 BIOS - + (%1×%2) (%1×%2) @@ -3525,12 +3523,12 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 QGBA::ShaderSelector - + No shader active 无活动的着色器 - + Load shader 载入着色器 @@ -3545,34 +3543,16 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 由 %1 - + Preprocessing - 预处理 + 正在预处理 - + Pass %1 通道 %1 - - QGBA::ShortcutController - - - Action - 动作 - - - - Keyboard - 键盘 - - - - Gamepad - 游戏手柄 - - QGBA::TileView @@ -3591,126 +3571,126 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 QGBA::VideoView - + Failed to open output video file: %1 - 无法打开输出视频文件: %1 + 打开输出视频文件失败: %1 - + Native (%0x%1) 原生 (%0x%1) - + Select output file - 选取输出文件 + 选择输出文件 QGBA::Window - + Game Boy Advance ROMs (%1) Game Boy Advance ROM (%1) - + Game Boy ROMs (%1) Game Boy ROM (%1) - + All ROMs (%1) 所有 ROM (%1) - + %1 Video Logs (*.mvl) %1 视频日志 (*.mvl) - + Archives (%1) 压缩文件 (%1) - - - + + + Select ROM 选择 ROM - + Select folder 选择文件夹 - + Game Boy Advance save files (%1) Game Boy Advance 存档文件 (%1) - - - + + + Select save 选择存档 - + mGBA savestate files (%1) mGBA 即时存档文件 (%1) - - + + Select savestate 选择即时存档 - + Select patch 选择补丁 - + Patches (*.ips *.ups *.bps) 补丁 (*.ips *.ups *.bps) - + Select image 选择图片 - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) 图像文件 (*.png *.gif *.jpg *.jpeg);;所有文件 (*) - - + + GameShark saves (*.sps *.xps) GameShark 存档 (*.sps *.xps) - + Select video log 选择视频日志 - + Video logs (*.mvl) 视频日志文件 (*.mvl) - + Crash 崩溃 - + The game has crashed with the following error: %1 @@ -3719,578 +3699,578 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 %1 - + Couldn't Load 无法载入 - + Could not load game. Are you sure it's in the correct format? 无法载入游戏。请确认游戏格式是否无误。 - + Unimplemented BIOS call 未实现的 BIOS 调用 - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. 该游戏使用了尚未实现的 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... - + ROM &info... ROM 信息(&I)... - + Recent 最近打开 - + Make portable 程序便携化 - + &Load state - 读取即时存档(&L) + 载入即时存档(&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(&1) - + Load camera image... - 读取相机图片... + 载入相机图片... - + Import 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) - + Record A/V... 录制音频/视频... - + Record GIF... 录制 GIF... - + Video layers 视频图层 - + Audio channels 音频通道 - + Adjust layer placement... 调整图层布局... - + &Tools 工具(&T) - + View &logs... 查看日志(&L)... - + Game &overrides... 覆盖游戏(&O)... - + Game Pak sensors... 游戏卡带传感器... - + &Cheats... 作弊码(&C)... - + Settings... 设置... - + Open debugger console... 打开调试器控制台... - + Start &GDB server... 打开 GDB 服务器(&G)... - + View &palette... 查看调色板(&P)... - + View &sprites... 查看精灵图(&S)... - + View &tiles... 查看瓷贴(&T)... - + View &map... 查看映射(&M)... - + &Frame inspector... 框架检查(&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 清除 @@ -4813,7 +4793,7 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标 Preload entire ROM into memory - 将整个 ROM 预加载到内存中 + 将整个 ROM 预载到内存中 diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 2df537cee..2b38bf9f4 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -46,7 +46,7 @@ if(USE_PIXMAN) endif() if(WIN32) - list(APPEND SDL_LIBRARY imm32 version winmm) + list(APPEND SDL_LIBRARY imm32 setupapi version winmm) elseif(APPLE) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AppKit -framework AudioUnit -framework Carbon -framework CoreAudio -framework AudioToolbox -framework ForceFeedback -framework IOKit") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}" PARENT_SCOPE) diff --git a/src/platform/sdl/sdl-audio.c b/src/platform/sdl/sdl-audio.c index 8236fefde..b66446872 100644 --- a/src/platform/sdl/sdl-audio.c +++ b/src/platform/sdl/sdl-audio.c @@ -19,6 +19,11 @@ mLOG_DEFINE_CATEGORY(SDL_AUDIO, "SDL Audio", "platform.sdl.audio"); static void _mSDLAudioCallback(void* context, Uint8* data, int len); bool mSDLInitAudio(struct mSDLAudio* context, struct mCoreThread* threadContext) { +#if defined(_WIN32) && SDL_VERSION_ATLEAST(2, 0, 8) + if (!getenv("SDL_AUDIODRIVER")) { + _putenv_s("SDL_AUDIODRIVER", "directsound"); + } +#endif if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { mLOG(SDL_AUDIO, ERROR, "Could not initialize SDL sound system: %s", SDL_GetError()); return false; diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 9b8b3e17d..3e9afbb05 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -348,6 +348,13 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration* if (!sdlJoystick) { continue; } + ssize_t joysticks[MAX_PLAYERS]; + size_t i; + // Pointers can get invalidated, so we'll need to refresh them + for (i = 0; i < events->playersAttached && i < MAX_PLAYERS; ++i) { + joysticks[i] = events->players[i]->joystick ? SDL_JoystickListIndex(&events->joysticks, events->players[i]->joystick) : SIZE_MAX; + events->players[i]->joystick = NULL; + } struct SDL_JoystickCombo* joystick = SDL_JoystickListAppend(&events->joysticks); joystick->joystick = sdlJoystick; joystick->id = SDL_JoystickInstanceID(joystick->joystick); @@ -355,8 +362,12 @@ void mSDLUpdateJoysticks(struct mSDLEvents* events, const struct Configuration* #if SDL_VERSION_ATLEAST(2, 0, 0) joystick->haptic = SDL_HapticOpenFromJoystick(joystick->joystick); #endif + for (i = 0; i < events->playersAttached && i < MAX_PLAYERS; ++i) { + if (joysticks[i] != SIZE_MAX) { + events->players[i]->joystick = SDL_JoystickListGetPointer(&events->joysticks, joysticks[i]); + } + } - size_t i; #if SDL_VERSION_ATLEAST(2, 0, 0) char joystickName[34] = {0}; SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick->joystick), joystickName, sizeof(joystickName)); diff --git a/src/platform/test/perf-main.c b/src/platform/test/perf-main.c index feb13f944..86262c5e2 100644 --- a/src/platform/test/perf-main.c +++ b/src/platform/test/perf-main.c @@ -22,6 +22,14 @@ #ifdef __SWITCH__ #include #endif +#ifdef GEKKO +#include +#include +#ifdef FIXED_ROM_BUFFER +uint32_t* romBuffer; +size_t romBufferSize; +#endif +#endif #include #include @@ -83,6 +91,29 @@ int main(int argc, char** argv) { #elif defined(__SWITCH__) UNUSED(_mPerfShutdown); consoleInit(NULL); +#elif defined(GEKKO) + VIDEO_Init(); + VIDEO_SetBlack(true); + VIDEO_Flush(); + VIDEO_WaitVSync(); + + GXRModeObj* vmode = VIDEO_GetPreferredMode(0); + void* xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(vmode)); + console_init(xfb, 20, 20, vmode->fbWidth, vmode->xfbHeight, vmode->fbWidth * VI_DISPLAY_PIX_SZ); + + VIDEO_Configure(vmode); + VIDEO_SetNextFramebuffer(xfb); + VIDEO_SetBlack(false); + VIDEO_Flush(); + VIDEO_WaitVSync(); + VIDEO_WaitVSync(); + fatInitDefault(); + +#ifdef FIXED_ROM_BUFFER + romBufferSize = 0x02000000; + romBuffer = SYS_GetArena2Lo(); + SYS_SetArena2Lo((void*)((intptr_t) romBuffer + romBufferSize)); +#endif #else signal(SIGINT, _mPerfShutdown); #endif @@ -125,6 +156,8 @@ int main(int argc, char** argv) { puts("game_code,frames,duration,renderer"); #ifdef __SWITCH__ consoleUpdate(NULL); +#elif defined(GEKKO) + VIDEO_WaitVSync(); #endif } if (perfOpts.server) { @@ -145,6 +178,11 @@ int main(int argc, char** argv) { acExit(); #elif defined(__SWITCH__) consoleExit(NULL); +#elif defined(GEKKO) + VIDEO_SetBlack(true); + VIDEO_Flush(); + VIDEO_WaitVSync(); + VIDEO_WaitVSync(); #endif return didFail; diff --git a/src/platform/wii/CMakeLists.txt b/src/platform/wii/CMakeLists.txt index 4caf73d10..d0a1a7821 100644 --- a/src/platform/wii/CMakeLists.txt +++ b/src/platform/wii/CMakeLists.txt @@ -11,6 +11,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/wii/wii-*.c) list(APPEND OS_LIB wiiuse bte fat ogc) set(OS_SRC ${OS_SRC} PARENT_SCOPE) +set(OS_LIB ${OS_LIB} PARENT_SCOPE) source_group("Wii-specific code" FILES ${OS_SRC}) set(CORE_VFS_SRC ${CORE_VFS_SRC} PARENT_SCOPE) set(OS_DEFINES ${OS_DEFINES} PARENT_SCOPE) @@ -39,6 +40,16 @@ add_custom_target(${BINARY_NAME}.dol ALL add_custom_target(run ${WIILOAD} ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.dol DEPENDS ${BINARY_NAME}.dol) +if(BUILD_PERF) + add_custom_target(${BINARY_NAME}-perf.dol ALL + ${ELF2DOL} ../${BINARY_NAME}-perf ${BINARY_NAME}-perf.dol + DEPENDS ${BINARY_NAME}-perf) + + install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}-perf.dol + DESTINATION . COMPONENT ${BINARY_NAME}-perf) +endif() + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/meta.xml.in ${CMAKE_CURRENT_BINARY_DIR}/meta.xml) install(TARGETS ${BINARY_NAME}.elf DESTINATION . COMPONENT ${BINARY_NAME}-dbg) diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 63d48214b..769469b43 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -262,7 +262,7 @@ int main(int argc, char* argv[]) { #ifdef FIXED_ROM_BUFFER romBufferSize = SIZE_CART0; romBuffer = SYS_GetArena2Lo(); - SYS_SetArena2Lo((void*)((intptr_t) romBuffer + SIZE_CART0)); + SYS_SetArena2Lo((void*)((intptr_t) romBuffer + romBufferSize)); #endif #if !defined(COLOR_16_BIT) && !defined(COLOR_5_6_5) diff --git a/src/util/vfs/vfs-fd.c b/src/util/vfs/vfs-fd.c index ed2222551..dde239717 100644 --- a/src/util/vfs/vfs-fd.c +++ b/src/util/vfs/vfs-fd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2020 Jeffrey Pfau * * 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 @@ -14,11 +14,23 @@ #include #endif +#include + +#ifdef _WIN32 +struct HandleMappingTuple { + HANDLE handle; + void* mapping; +}; + +DECLARE_VECTOR(HandleMappingList, struct HandleMappingTuple); +DEFINE_VECTOR(HandleMappingList, struct HandleMappingTuple); +#endif + struct VFileFD { struct VFile d; int fd; #ifdef _WIN32 - HANDLE hMap; + struct HandleMappingList handles; #endif }; @@ -74,12 +86,23 @@ struct VFile* VFileFromFD(int fd) { vfd->d.truncate = _vfdTruncate; vfd->d.size = _vfdSize; vfd->d.sync = _vfdSync; +#ifdef _WIN32 + HandleMappingListInit(&vfd->handles, 4); +#endif return &vfd->d; } bool _vfdClose(struct VFile* vf) { struct VFileFD* vfd = (struct VFileFD*) vf; +#ifdef _WIN32 + size_t i; + for (i = 0; i < HandleMappingListSize(&vfd->handles); ++i) { + UnmapViewOfFile(HandleMappingListGetPointer(&vfd->handles, i)->mapping); + CloseHandle(HandleMappingListGetPointer(&vfd->handles, i)->handle); + } + HandleMappingListDeinit(&vfd->handles); +#endif if (close(vfd->fd) < 0) { return false; } @@ -134,16 +157,25 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags) { if (size > fileSize) { size = fileSize; } - vfd->hMap = CreateFileMapping((HANDLE) _get_osfhandle(vfd->fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0); - return MapViewOfFile(vfd->hMap, mapFiles, 0, 0, size); + struct HandleMappingTuple tuple = {0}; + tuple.handle = CreateFileMapping((HANDLE) _get_osfhandle(vfd->fd), 0, createFlags, 0, size & 0xFFFFFFFF, 0); + tuple.mapping = MapViewOfFile(tuple.handle, mapFiles, 0, 0, size); + *HandleMappingListAppend(&vfd->handles) = tuple; + return tuple.mapping; } static void _vfdUnmap(struct VFile* vf, void* memory, size_t size) { UNUSED(size); struct VFileFD* vfd = (struct VFileFD*) vf; - UnmapViewOfFile(memory); - CloseHandle(vfd->hMap); - vfd->hMap = 0; + size_t i; + for (i = 0; i < HandleMappingListSize(&vfd->handles); ++i) { + if (HandleMappingListGetPointer(&vfd->handles, i)->mapping == memory) { + UnmapViewOfFile(memory); + CloseHandle(HandleMappingListGetPointer(&vfd->handles, i)->handle); + HandleMappingListShift(&vfd->handles, i, 1); + break; + } + } } #endif