mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' (early part) into medusa
This commit is contained in:
commit
0d93ff8844
18
CHANGES
18
CHANGES
|
@ -37,7 +37,11 @@ Misc:
|
|||
Features:
|
||||
- e-Reader card scanning
|
||||
- Add WebP and APNG recording
|
||||
- Add mute option in homebrew ports
|
||||
- Add status indicators for fast-forward and mute in homebrew ports
|
||||
- Support for unlicensed Pokemon Jade/Diamond Game Boy mapper
|
||||
- Support for unlicensed BBD Game Boy mapper
|
||||
- Support for unlicensed Hitek Game Boy mapper
|
||||
- Stack tracing tools in ARM debugger (by ahigerd)
|
||||
- Command scripts for CLI debugger (by ahigerd)
|
||||
Emulation fixes:
|
||||
|
@ -47,15 +51,21 @@ Emulation fixes:
|
|||
- GB Audio: Fix initial sweep state
|
||||
- GB Audio: Fix serializing sweep time
|
||||
- GB Audio: Fix deserializing audio channels 2 and 3
|
||||
- GB Audio: Fix deserializing while audio was disabled (fixes mgba.io/i/1305)
|
||||
- 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 Audio: Fix stereo in XQ audio
|
||||
- GBA Audio: Fix volume/mute in XQ audio (fixes mgba.io/i/1864)
|
||||
- GBA Audio: Revamp FIFO emulation (fixes mgba.io/i/356, mgba.io/i/875, mgba.io/i/1847)
|
||||
- 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)
|
||||
- GBA BIOS: Make HLE BIOS calls interruptable (fixes mgba.io/i/1711 and mgba.io/i/1823)
|
||||
- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320)
|
||||
- GBA DMA: Fix ordering and timing of overlapping DMAs
|
||||
- GBA Hardware: Fix GB Player detection on big endian platforms
|
||||
- GBA Memory: Improve gamepak prefetch timing
|
||||
- GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190)
|
||||
- GBA SIO: Fix copying Normal mode transfer values
|
||||
|
@ -64,21 +74,27 @@ Emulation fixes:
|
|||
- 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)
|
||||
- GBA Video: Don't draw sprites using unmapped VRAM in GL renderer (fixes mgba.io/i/1865)
|
||||
- SM83: Emulate HALT bug
|
||||
Other fixes:
|
||||
- 3DS: Redo video sync to be more precise
|
||||
- 3DS: Fix crash with libctru 2.0 when exiting
|
||||
- 3DS: Fix thread cleanup
|
||||
- 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
|
||||
- Core: Fix memory leak loading ELF files
|
||||
- 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)
|
||||
- GBA: Fix endianness issues in renderer proxy
|
||||
- GBA Core: Fix memory leak when loading symbols
|
||||
- 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
|
||||
- Qt: Add dummy English translation file (fixes mgba.io/i/1469)
|
||||
- Qt: Fix Battle Chip view not displaying chips on some DPI settings
|
||||
- mGUI: Fix closing down a game if an exit is signalled
|
||||
- mVL: Fix injecting accidentally draining non-injection buffer
|
||||
- SM83: Simplify register pair access on big endian
|
||||
|
@ -93,7 +109,9 @@ Misc:
|
|||
- 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
|
||||
- Qt: Memory viewer now supports editing decimal values directly (closes mgba.io/i/1705)
|
||||
- Util: Reset vector size on deinit
|
||||
- VFS: Change semantics of VFile.sync on mapped files (fixes mgba.io/i/1730)
|
||||
|
||||
0.8.3: (2020-08-03)
|
||||
Emulation fixes:
|
||||
|
|
|
@ -1137,7 +1137,7 @@ if(DISTBUILD)
|
|||
elseif(WII)
|
||||
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-dbg ${BINARY_NAME}-wii ${BINARY_NAME}-perf)
|
||||
elseif(PSP2)
|
||||
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-dbg ${BINARY_NAME}-psp2)
|
||||
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-dbg ${BINARY_NAME}-psp2 ${BINARY_NAME}-perf)
|
||||
elseif(SWITCH)
|
||||
set(CPACK_COMPONENTS_ALL ${BINARY_NAME} ${BINARY_NAME}-dbg ${BINARY_NAME}-switch)
|
||||
endif()
|
||||
|
|
12
README.md
12
README.md
|
@ -53,21 +53,23 @@ The following mappers are fully supported:
|
|||
- MBC7
|
||||
- Wisdom Tree (unlicensed)
|
||||
- Pokémon Jade/Diamond (unlicensed)
|
||||
- BBD (unlicensed MBC5-like)
|
||||
- Hitek (unlicensed MBC5-like)
|
||||
|
||||
The following mappers are partially supported:
|
||||
|
||||
- MBC6
|
||||
- MBC6 (missing flash memory support)
|
||||
- MMM01
|
||||
- Pocket Cam
|
||||
- TAMA5
|
||||
- HuC-1
|
||||
- HuC-3
|
||||
- TAMA5 (missing RTC support)
|
||||
- HuC-1 (missing IR support)
|
||||
- HuC-3 (missing RTC and IR support)
|
||||
|
||||
### Planned features
|
||||
|
||||
- Networked multiplayer link cable support.
|
||||
- Dolphin/JOY bus link cable support.
|
||||
- M4A audio mixing, for higher quality sound than hardware.
|
||||
- MP2k audio mixing, for higher quality sound than hardware.
|
||||
- Re-recording support for tool-assist runs.
|
||||
- Lua support for scripting.
|
||||
- A comprehensive debug suite.
|
||||
|
|
47
README_DE.md
47
README_DE.md
|
@ -27,7 +27,7 @@ Features
|
|||
- Unterstützung für Screenshots.
|
||||
- Unterstützung für Cheat-Codes.
|
||||
- 9 Speicherstände für Savestates/Spielzustände. Savestates können auch als Screenshots dargestellt werden.
|
||||
- Video- und GIF-Aufzeichnung.
|
||||
- Video-, GIF-, WebP- und APNG-Aufzeichnung.
|
||||
- e-Reader-Unterstützung.
|
||||
- Frei wählbare Tastenbelegungen für Tastaturen und Controller.
|
||||
- Unterstützung für ZIP- und 7z-Archive.
|
||||
|
@ -51,21 +51,24 @@ Die folgenden Mapper werden vollständig unterstützt:
|
|||
- MBC5+Rumble (MBC5+Rüttel-Modul)
|
||||
- MBC7
|
||||
- Wisdom Tree (nicht lizenziert)
|
||||
- Pokémon Jade/Diamond (nicht lizenziert)
|
||||
- BBD (nicht lizenziert, ählich MBC5)
|
||||
- Hitek (nicht lizenziert, ähnlich MBC5)
|
||||
|
||||
Die folgenden Mapper werden teilweise unterstützt:
|
||||
|
||||
- MBC6
|
||||
- MBC6 (fehlende Flash-Unterstützung)
|
||||
- MMM01
|
||||
- Pocket Cam
|
||||
- TAMA5
|
||||
- HuC-1
|
||||
- HuC-3
|
||||
- TAMA5 (fehlende RTC-Unterstützung)
|
||||
- HuC-1 (fehlende Infrarot-Unterstützung)
|
||||
- HuC-3 (fehlende RTC- und Infrarot-Unterstützung)
|
||||
|
||||
### Geplante Features
|
||||
|
||||
- Unterstützung für Link-Kabel-Multiplayer über ein Netzwerk.
|
||||
- Unterstützung für Link-Kabel über Dolphin/JOY-Bus.
|
||||
- M4A-Audio-Abmischung für höhere Audio-Qualität als echte Hardware.
|
||||
- MP2k-Audio-Abmischung für höhere Audio-Qualität als echte Hardware.
|
||||
- Unterstützung für Tool-Assisted Speedruns.
|
||||
- Lua-Unterstützung für Scripting.
|
||||
- Eine umfangreiche Debugging-Suite.
|
||||
|
@ -75,10 +78,11 @@ Unterstützte Plattformen
|
|||
------------------------
|
||||
|
||||
- Windows Vista oder neuer
|
||||
- OS X 10.7 (Lion)[<sup>[3]</sup>](#osxver) oder neuer
|
||||
- OS X 10.8 (Mountain Lion)[<sup>[3]</sup>](#osxver) oder neuer
|
||||
- Linux
|
||||
- FreeBSD
|
||||
- Nintendo 3DS
|
||||
- Nintendo Switch
|
||||
- Wii
|
||||
- PlayStation Vita
|
||||
|
||||
|
@ -112,7 +116,7 @@ Um mGBA kompilieren zu können, wird CMake 3.1 oder neuer benötigt. GCC und Cla
|
|||
|
||||
#### Kompilieren mit Docker
|
||||
|
||||
Der empfohlene Weg, um mGBA für die meisten Plattformen zu kompilieren, ist Docker. Mehrere Docker-Images sind verfügbar, welche die benötigte Compiler-Umgebung und alle benötigten Abhängigkeiten beinhaltet, um mGBA für verschiedene Plattformen zu bauen.
|
||||
Der empfohlene Weg, um mGBA für die meisten Plattformen zu kompilieren, ist die Verwendung von Docker. Mehrere Docker-Images sind verfügbar, welche die benötigte Compiler-Umgebung und alle benötigten Abhängigkeiten beinhaltet, um mGBA für verschiedene Plattformen zu bauen.
|
||||
|
||||
Um ein Docker-Image zum Bau von mGBA zu verwenden, führe einfach folgenden Befehl in dem Verzeichnis aus, in welches Du den mGBA-Quellcode ausgecheckt hast:
|
||||
|
||||
|
@ -150,12 +154,14 @@ Wenn Du macOS verwendest, sind die einzelnen Schritte etwas anders. Angenommen,
|
|||
brew install cmake ffmpeg libzip qt5 sdl2 libedit pkg-config
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_PREFIX_PATH='brew --prefix qt5' ..
|
||||
cmake -DCMAKE_PREFIX_PATH=`brew --prefix qt5` ..
|
||||
make
|
||||
|
||||
Bitte beachte, dass Du unter macOS nicht 'make install' verwenden solltest, da dies nicht korrekt funktionieren wird.
|
||||
Bitte beachte, dass Du unter macOS nicht `make install` verwenden solltest, da dies nicht korrekt funktionieren wird.
|
||||
|
||||
### Für Entwickler: Kompilieren unter Windows
|
||||
#### Für Entwickler: Kompilieren unter Windows
|
||||
|
||||
##### MSYS2
|
||||
|
||||
Um mGBA auf Windows zu kompilieren, wird MSYS2 empfohlen. Befolge die Installationsschritte auf der [MSYS2-Website](https://msys2.github.io). Stelle sicher, dass Du die 32-Bit-Version ("MSYS2 MinGW 32-bit") (oder die 64-Bit-Version "MSYS2 MinGW 64-bit", wenn Du mGBA für x86_64 kompilieren willst) verwendest und führe folgendes Kommando (einschließlich der Klammern) aus, um alle benötigten Abhängigkeiten zu installieren. Bitte beachte, dass dafür über 1100MiB an Paketen heruntergeladen werden, was eine Weile dauern kann:
|
||||
|
||||
|
@ -181,6 +187,20 @@ Abschließend wird mGBA über folgende Kommandos kompiliert:
|
|||
|
||||
Bitte beachte, dass mGBA für Windows aufgrund der Vielzahl an benötigten DLLs nicht für die weitere Verteilung geeignet ist, wenn es auf diese Weise gebaut wurde. Es ist jedoch perfekt für Entwickler geeignet. Soll mGBA dennoch weiter verteilt werden (beispielsweise zu Testzwecken auf Systemen, auf denen keine MSYS2-Umgebung installiert ist), kann mithilfe des Befehls `cpack -G ZIP` ein ZIP-Archiv mit allen benötigten DLLs erstellt werden.
|
||||
|
||||
##### Visual Studio
|
||||
|
||||
mGBA mit Visual Studio zu bauen erfordert ein ähnlich kompliziertes Setup. Zuerst musst Du [vcpkg](https://github.com/Microsoft/vcpkg) installieren. Nachdem vcpkg installiert ist, musst Du noch folgende zusätzlichen Pakete installieren:
|
||||
|
||||
vcpkg install ffmpeg[vpx,x264] libepoxy libpng libzip sdl2 sqlite3
|
||||
|
||||
Bitte beachte, dass diese Installation keine hardwarebeschleunigtes Video-Encoding auf Nvidia-Hardware unterstützen wird. Wenn Du darauf Wert legst, musst Du zuerst CUDA installieren und anschließend den vorherigen Befehl um `ffmpeg[vpx,x264,nvcodec]` ergänzen.
|
||||
|
||||
Zusätzlich wirst Du auch Qt installieren müssen. Unglücklicherweise steht für Qt kein Offline-Installationsprogramm für die jeweils aktuelle Version bereit. Daher musst Du entweder auf eine [ältere Version](https://download.qt.io/official_releases/qt/5.12/5.12.9/qt-opensource-windows-x86-5.12.9.exe) zurückgreifen (hierfür benötigst Du ein ansonsten nutzloses Benutzerkonto, aber Du kannst das umgehen, indem Du temporär einen ungültigen Netzwerk-Proxy hinterlegst oder über andere Methoden deine Netzwerkverbindung deaktivierst). Alternativ kannst Du auch den Online-Installer nutzen (für den ohnehin ein Benutzeraccount erfortderlich ist) oder Qt selbst mithilfe von vcpkg bauen (was verhältnismäßig lange dauert). Keine dieser Optionen ist besonders elegant. Bitte achte bei der Verwendung eines Installers darauf, die passende MSVC-Version zu wählen. Der Offline-Installer unterstützt aktuell noch nicht MSVC 2019. Die Installation mit vcpkg dauert ein wenig länger, besonders, wenn Du einen Computer mit vier oder weniger CPU-Cores nutzt:
|
||||
|
||||
vcpkg install qt5-base qt5-multimedia
|
||||
|
||||
Öffne anschließend Visual Studio, wähle "Clone Repository" und gib dort `https://github.com/mgba-emu/mgba.git` ein. Wenn Visual Studio das Repository geklont hat, gehe zu "Datei > CMake" und öffne die Datei CMakeLists.txt im Stammverzeichnis des ausgecheckten Repos. Anschließend kann mGBA in Visual Studio entwickelt werden, ähnlich wie andere Visual Studio CMake-Projekte.
|
||||
|
||||
#### Kompilieren mithilfe einer Toolchain
|
||||
|
||||
Wenn Du devkitARM (für 3DS), devkitPPC (für Wii), devkitA64 (für Switch) oder vitasdk (für PS Vita) installiert hast, kannst Du die folgenden Befehle zum Kompilieren verwenden:
|
||||
|
@ -207,7 +227,6 @@ mGBA hat keine "harten" Abhängigkeiten. Dennoch werden die folgenden optionalen
|
|||
- libedit: Für die Unterstützung des Kommandozeilen-Debuggers.
|
||||
- ffmpeg oder libav: Für Videoaufzeichnungen.
|
||||
- libzip oder zlib: Um ROMs aus ZIP-Dateien zu laden.
|
||||
- ImageMagick: Für GIF-Aufzeichnungen.
|
||||
- SQLite3: Für Spiele-Datenbanken.
|
||||
- libelf: Für das Laden von ELF-Dateien.
|
||||
|
||||
|
@ -222,7 +241,7 @@ Fußnoten
|
|||
|
||||
<a name="flashdetect">[2]</a> In manchen Fällen ist es nicht möglich, die Größe des Flash-Speichers automatisch zu ermitteln. Diese kann dann zur Laufzeit konfiguriert werden, es wird jedoch empfohlen, den Fehler zu melden.
|
||||
|
||||
<a name="osxver">[3]</a> 10.7 wird nur für die Qt-Portierung benötigt. Die SDL-Portierung ist dafür bekannt, mit 10.5 und möglicherweise auf älteren Versionen zu funktionieren.
|
||||
<a name="osxver">[3]</a> 10.8 wird nur für die Qt-Portierung benötigt. Es ist wahrscheinlich möglich, die Qt-Portierung unter macOS 10.7 und älter zu bauen und zu nutzen, aber das wird nicht offiziell unterstützt. Die SDL-Portierung ist dafür bekannt, mit 10.7 und möglicherweise auf älteren Versionen zu funktionieren.
|
||||
|
||||
[downloads]: http://mgba.io/downloads.html
|
||||
[source]: https://github.com/mgba-emu/mgba/
|
||||
|
@ -234,7 +253,7 @@ Copyright für mGBA © 2013 – 2020 Jeffrey Pfau. mGBA wird unter der [Mozilla
|
|||
|
||||
mGBA beinhaltet die folgenden Bibliotheken von Drittanbietern:
|
||||
|
||||
- [inih](https://github.com/benhoyt/inih), Copyright © 2009 Ben Hoyt, verwendet unter einer BSD 3-clause-Lizenz.
|
||||
- [inih](https://github.com/benhoyt/inih), Copyright © 2009 - 2020 Ben Hoyt, verwendet unter einer BSD 3-clause-Lizenz.
|
||||
- [blip-buf](https://code.google.com/archive/b/blip-buf), Copyright © 2003 - 2009 Shay Green, verwendet unter einer Lesser GNU Public License.
|
||||
- [LZMA SDK](http://www.7-zip.org/sdk.html), Public Domain.
|
||||
- [MurmurHash3](https://github.com/aappleby/smhasher), Implementierung von Austin Appleby, Public Domain.
|
||||
|
|
|
@ -129,31 +129,32 @@ typedef intptr_t ssize_t;
|
|||
#define LOAD_32BE(DEST, ADDR, ARR) DEST = *(uint32_t*) ((uintptr_t) (ARR) + (size_t) (ADDR))
|
||||
#if defined(__PPC__) || defined(__POWERPC__)
|
||||
#define LOAD_32LE(DEST, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
size_t _addr = (ADDR); \
|
||||
const void* _ptr = (ARR); \
|
||||
__asm__("lwbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \
|
||||
}
|
||||
|
||||
#define LOAD_16LE(DEST, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
size_t _addr = (ADDR); \
|
||||
const void* _ptr = (ARR); \
|
||||
__asm__("lhbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \
|
||||
}
|
||||
|
||||
#define STORE_32LE(SRC, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
size_t _addr = (ADDR); \
|
||||
void* _ptr = (ARR); \
|
||||
__asm__("stwbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr) : "memory"); \
|
||||
}
|
||||
|
||||
#define STORE_16LE(SRC, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
size_t _addr = (ADDR); \
|
||||
void* _ptr = (ARR); \
|
||||
__asm__("sthbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr) : "memory"); \
|
||||
}
|
||||
|
||||
#ifndef _ARCH_PWR7
|
||||
#define LOAD_64LE(DEST, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
size_t _addr = (ADDR); \
|
||||
union { \
|
||||
struct { \
|
||||
uint32_t hi; \
|
||||
|
@ -170,7 +171,7 @@ typedef intptr_t ssize_t;
|
|||
}
|
||||
|
||||
#define STORE_64LE(SRC, ADDR, ARR) { \
|
||||
uint32_t _addr = (ADDR); \
|
||||
size_t _addr = (ADDR); \
|
||||
union { \
|
||||
struct { \
|
||||
uint32_t hi; \
|
||||
|
@ -184,6 +185,19 @@ typedef intptr_t ssize_t;
|
|||
"stwbrx %1, %2, %4 \n" \
|
||||
: : "r"(bswap.hi), "r"(bswap.lo), "b"(_ptr), "r"(_addr), "r"(_addr + 4) : "memory"); \
|
||||
}
|
||||
#else
|
||||
#define LOAD_64LE(DEST, ADDR, ARR) { \
|
||||
size_t _addr = (ADDR); \
|
||||
const void* _ptr = (ARR); \
|
||||
__asm__("ldbrx %0, %1, %2" : "=r"(DEST) : "b"(_ptr), "r"(_addr)); \
|
||||
}
|
||||
|
||||
#define STORE_64LE(SRC, ADDR, ARR) { \
|
||||
size_t _addr = (ADDR); \
|
||||
void* _ptr = (ARR); \
|
||||
__asm__("stdbrx %0, %1, %2" : : "r"(SRC), "b"(_ptr), "r"(_addr) : "memory"); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(__llvm__) || (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
|
||||
#define LOAD_64LE(DEST, ADDR, ARR) DEST = __builtin_bswap64(((uint64_t*) ARR)[(ADDR) >> 3])
|
||||
|
|
|
@ -53,6 +53,8 @@ enum GUIIcon {
|
|||
GUI_ICON_BUTTON_TRIANGLE,
|
||||
GUI_ICON_BUTTON_SQUARE,
|
||||
GUI_ICON_BUTTON_HOME,
|
||||
GUI_ICON_STATUS_FAST_FORWARD,
|
||||
GUI_ICON_STATUS_MUTE,
|
||||
GUI_ICON_MAX,
|
||||
};
|
||||
|
||||
|
|
|
@ -69,12 +69,14 @@ static inline int ThreadCreate(Thread* thread, ThreadEntry entry, void* context)
|
|||
if (!entry || !thread) {
|
||||
return 1;
|
||||
}
|
||||
*thread = threadCreate(entry, context, 0x8000, 0x18, 2, true);
|
||||
*thread = threadCreate(entry, context, 0x8000, 0x18, 2, false);
|
||||
return !*thread;
|
||||
}
|
||||
|
||||
static inline int ThreadJoin(Thread* thread) {
|
||||
return threadJoin(*thread, U64_MAX);
|
||||
Result res = threadJoin(*thread, U64_MAX);
|
||||
threadFree(*thread);
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void ThreadSetName(const char* name) {
|
||||
|
|
|
@ -64,6 +64,10 @@ extern u32* SOCUBuffer;
|
|||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
#endif
|
||||
#ifdef PSP2
|
||||
#include <psp2/net/net.h>
|
||||
#include <psp2/sysmodule.h>
|
||||
#endif
|
||||
|
||||
static inline void SocketSubsystemInit() {
|
||||
#ifdef _WIN32
|
||||
|
@ -78,6 +82,10 @@ static inline void SocketSubsystemInit() {
|
|||
socketInitializeDefault();
|
||||
#elif defined(GEKKO)
|
||||
net_init();
|
||||
#elif defined(PSP2)
|
||||
static uint8_t netMem[1024*1024];
|
||||
sceSysmoduleLoadModule(SCE_SYSMODULE_NET);
|
||||
sceNetInit(&(SceNetInitParam) { netMem, sizeof(netMem) });
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -92,6 +100,9 @@ static inline void SocketSubsystemDeinit() {
|
|||
socketExit();
|
||||
#elif defined(GEKKO)
|
||||
net_deinit();
|
||||
#elif defined(PSP2)
|
||||
sceNetTerm();
|
||||
sceSysmoduleUnloadModule(SCE_SYSMODULE_NET);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -249,6 +260,11 @@ static inline Socket SocketListen(Socket socket, int queueLength) {
|
|||
#ifdef GEKKO
|
||||
return net_listen(socket, queueLength);
|
||||
#else
|
||||
#ifdef PSP2
|
||||
if (queueLength <= 0) {
|
||||
queueLength = 1;
|
||||
}
|
||||
#endif
|
||||
return listen(socket, queueLength);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -17,9 +17,10 @@ struct Table {
|
|||
size_t tableSize;
|
||||
size_t size;
|
||||
void (*deinitializer)(void*);
|
||||
uint32_t seed;
|
||||
};
|
||||
|
||||
void TableInit(struct Table*, size_t initialSize, void (deinitializer(void*)));
|
||||
void TableInit(struct Table*, size_t initialSize, void (*deinitializer)(void*));
|
||||
void TableDeinit(struct Table*);
|
||||
|
||||
void* TableLookup(const struct Table*, uint32_t key);
|
||||
|
@ -28,10 +29,10 @@ void TableInsert(struct Table*, uint32_t key, void* value);
|
|||
void TableRemove(struct Table*, uint32_t key);
|
||||
void TableClear(struct Table*);
|
||||
|
||||
void TableEnumerate(const struct Table*, void (handler(uint32_t key, void* value, void* user)), void* user);
|
||||
void TableEnumerate(const struct Table*, void (*handler)(uint32_t key, void* value, void* user), void* user);
|
||||
size_t TableSize(const struct Table*);
|
||||
|
||||
void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*)));
|
||||
void HashTableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*));
|
||||
void HashTableDeinit(struct Table* table);
|
||||
|
||||
void* HashTableLookup(const struct Table*, const char* key);
|
||||
|
@ -43,8 +44,9 @@ void HashTableRemove(struct Table*, const char* key);
|
|||
void HashTableRemoveBinary(struct Table*, const void* key, size_t keylen);
|
||||
void HashTableClear(struct Table*);
|
||||
|
||||
void HashTableEnumerate(const struct Table*, void (handler(const char* key, void* value, void* user)), void* user);
|
||||
const char* HashTableSearch(const struct Table* table, bool (predicate(const char* key, const void* value, const void* user)), const void* user);
|
||||
void HashTableEnumerate(const struct Table*, void (*handler)(const char* key, void* value, void* user), void* user);
|
||||
void HashTableEnumerateBinary(const struct Table*, void (*handler)(const char* key, size_t keylen, void* value, void* user), void* user);
|
||||
const char* HashTableSearch(const struct Table* table, bool (*predicate)(const char* key, const void* value, const void* user), const void* user);
|
||||
const char* HashTableSearchPointer(const struct Table* table, const void* value);
|
||||
const char* HashTableSearchData(const struct Table* table, const void* value, size_t bytes);
|
||||
const char* HashTableSearchString(const struct Table* table, const char* value);
|
||||
|
|
|
@ -47,7 +47,7 @@ struct VFile {
|
|||
void (*unmap)(struct VFile* vf, void* memory, size_t size);
|
||||
void (*truncate)(struct VFile* vf, size_t size);
|
||||
ssize_t (*size)(struct VFile* vf);
|
||||
bool (*sync)(struct VFile* vf, const void* buffer, size_t size);
|
||||
bool (*sync)(struct VFile* vf, void* buffer, size_t size);
|
||||
};
|
||||
|
||||
struct VDirEntry {
|
||||
|
|
|
@ -36,7 +36,7 @@ struct mStateExtdata {
|
|||
struct mStateExtdataItem data[EXTDATA_MAX];
|
||||
};
|
||||
|
||||
bool mStateExtdataInit(struct mStateExtdata*);
|
||||
void mStateExtdataInit(struct mStateExtdata*);
|
||||
void mStateExtdataDeinit(struct mStateExtdata*);
|
||||
void mStateExtdataPut(struct mStateExtdata*, enum mStateExtdataTag, struct mStateExtdataItem*);
|
||||
bool mStateExtdataGet(struct mStateExtdata*, enum mStateExtdataTag, struct mStateExtdataItem*);
|
||||
|
@ -49,6 +49,7 @@ struct mCore;
|
|||
bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags);
|
||||
bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags);
|
||||
void* mCoreExtractState(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata);
|
||||
bool mCoreExtractExtdata(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ enum GBMemoryBankControllerType {
|
|||
GB_MBC5_RUMBLE = 0x105,
|
||||
GB_UNL_WISDOM_TREE = 0x200,
|
||||
GB_UNL_PKJD = 0x203,
|
||||
GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior
|
||||
GB_UNL_HITEK = 0x221,
|
||||
};
|
||||
|
||||
struct GBSIODriver {
|
||||
|
|
|
@ -150,6 +150,11 @@ struct GBPKJDState {
|
|||
uint8_t reg[2];
|
||||
};
|
||||
|
||||
struct GBBBDState {
|
||||
int dataSwapMode;
|
||||
int bankSwapMode;
|
||||
};
|
||||
|
||||
union GBMBCState {
|
||||
struct GBMBC1State mbc1;
|
||||
struct GBMBC6State mbc6;
|
||||
|
@ -158,6 +163,7 @@ union GBMBCState {
|
|||
struct GBPocketCamState pocketCam;
|
||||
struct GBTAMA5State tama5;
|
||||
struct GBPKJDState pkjd;
|
||||
struct GBBBDState bbd;
|
||||
};
|
||||
|
||||
struct mRotationSource;
|
||||
|
|
|
@ -391,6 +391,10 @@ struct GBSerializedState {
|
|||
uint8_t locked;
|
||||
uint8_t bank0;
|
||||
} mmm01;
|
||||
struct {
|
||||
uint8_t dataSwapMode;
|
||||
uint8_t bankSwapMode;
|
||||
} bbd;
|
||||
struct {
|
||||
uint8_t reserved[16];
|
||||
} padding;
|
||||
|
|
|
@ -15,6 +15,8 @@ CXX_GUARD_START
|
|||
#include <mgba/internal/gb/audio.h>
|
||||
#include <mgba-util/circle-buffer.h>
|
||||
|
||||
#define GBA_AUDIO_FIFO_SIZE 8
|
||||
|
||||
#define MP2K_MAGIC 0x68736D53
|
||||
#define MP2K_MAX_SOUND_CHANNELS 12
|
||||
|
||||
|
@ -26,7 +28,11 @@ extern const unsigned GBA_AUDIO_SAMPLES;
|
|||
extern const int GBA_AUDIO_VOLUME_MAX;
|
||||
|
||||
struct GBAAudioFIFO {
|
||||
struct CircleBuffer fifo;
|
||||
uint32_t fifo[GBA_AUDIO_FIFO_SIZE];
|
||||
int fifoWrite;
|
||||
int fifoRead;
|
||||
uint32_t internalSample;
|
||||
int internalRemaining;
|
||||
int dmaSource;
|
||||
int8_t sample;
|
||||
};
|
||||
|
@ -298,7 +304,7 @@ void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value);
|
|||
void GBAAudioWriteSOUNDBIAS(struct GBAAudio* audio, uint16_t value);
|
||||
|
||||
void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value);
|
||||
void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value);
|
||||
uint32_t GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value);
|
||||
void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles);
|
||||
|
||||
struct GBASerializedState;
|
||||
|
|
|
@ -20,7 +20,7 @@ extern const uint32_t GBA_SAVESTATE_VERSION;
|
|||
mLOG_DECLARE_CATEGORY(GBA_STATE);
|
||||
|
||||
/* Savestate format:
|
||||
* 0x00000 - 0x00003: Version Magic (0x01000001)
|
||||
* 0x00000 - 0x00003: Version Magic (0x01000004)
|
||||
* 0x00004 - 0x00007: BIOS checksum (e.g. 0xBAAE187F for official BIOS)
|
||||
* 0x00008 - 0x0000B: ROM CRC32
|
||||
* 0x0000C - 0x0000F: Master cycles
|
||||
|
@ -69,10 +69,16 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* 0x0018C - 0x001AB: Audio FIFO 1
|
||||
* 0x001AC - 0x001CB: Audio FIFO 2
|
||||
* 0x001CC - 0x001DF: Audio miscellaneous state
|
||||
* | 0x001CC - 0x001CF: FIFO 1 size
|
||||
* | 0x001D0 - 0x001D3: Reserved
|
||||
* | 0x001CC - 0x001CF: Channel A internal audio samples
|
||||
* | 0x001D0 - 0x001D3: Channel B internal audio samples
|
||||
* | 0x001D4 - 0x001D7: Next sample
|
||||
* | 0x001D8 - 0x001DB: FIFO 2 size
|
||||
* | 0x001D8: Channel A current sample
|
||||
* | 0x001D9: Channel B current sample
|
||||
* | 0x001DA - 0x001DB: Flags
|
||||
* | bits 0 - 1: Channel B internal samples remaining
|
||||
* | bits 2 - 4: Channel B readable words
|
||||
* | bits 5 - 6: Channel A internal samples remaining
|
||||
* | bits 7 - 9: Channel A readable words
|
||||
* | TODO: Fix this, they're in big-endian order, but field is little-endian
|
||||
* | 0x001DC - 0x001DC: Channel 1 envelope state
|
||||
* | bits 0 - 3: Current volume
|
||||
|
@ -217,6 +223,12 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
|
|||
* Total size: 0x61000 (397,312) bytes
|
||||
*/
|
||||
|
||||
DECL_BITFIELD(GBASerializedAudioFlags, uint16_t);
|
||||
DECL_BITS(GBASerializedAudioFlags, FIFOInternalSamplesB, 0, 2);
|
||||
DECL_BITS(GBASerializedAudioFlags, FIFOSamplesB, 2, 3); // Yay legacy?
|
||||
DECL_BITS(GBASerializedAudioFlags, FIFOInternalSamplesA, 5, 2);
|
||||
DECL_BITS(GBASerializedAudioFlags, FIFOSamplesA, 7, 3);
|
||||
|
||||
DECL_BITFIELD(GBASerializedVideoFlags, uint32_t);
|
||||
DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2);
|
||||
|
||||
|
@ -267,12 +279,14 @@ struct GBASerializedState {
|
|||
|
||||
struct {
|
||||
struct GBSerializedPSGState psg;
|
||||
uint8_t fifoA[32];
|
||||
uint8_t fifoB[32];
|
||||
uint32_t fifoSizeA;
|
||||
int32_t reserved;
|
||||
uint32_t fifoA[8];
|
||||
uint32_t fifoB[8];
|
||||
uint32_t internalA;
|
||||
uint32_t internalB;
|
||||
int32_t nextSample;
|
||||
uint32_t fifoSizeB;
|
||||
int8_t sampleA;
|
||||
int8_t sampleB;
|
||||
GBASerializedAudioFlags gbaFlags;
|
||||
GBSerializedAudioFlags flags;
|
||||
} audio;
|
||||
|
||||
|
|
BIN
res/font-new.png
BIN
res/font-new.png
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
BIN
res/icons.png
BIN
res/icons.png
Binary file not shown.
Before Width: | Height: | Size: 402 B After Width: | Height: | Size: 541 B |
BIN
res/icons2x.png
BIN
res/icons2x.png
Binary file not shown.
Before Width: | Height: | Size: 716 B After Width: | Height: | Size: 1.2 KiB |
|
@ -400,9 +400,11 @@ bool mCoreLoadELF(struct mCore* core, struct ELF* elf) {
|
|||
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 {
|
||||
ELFProgramHeadersDeinit(&ph);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ELFProgramHeadersDeinit(&ph);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,9 +31,8 @@ struct mStateExtdataHeader {
|
|||
int64_t offset;
|
||||
};
|
||||
|
||||
bool mStateExtdataInit(struct mStateExtdata* extdata) {
|
||||
void mStateExtdataInit(struct mStateExtdata* extdata) {
|
||||
memset(extdata->data, 0, sizeof(extdata->data));
|
||||
return true;
|
||||
}
|
||||
|
||||
void mStateExtdataDeinit(struct mStateExtdata* extdata) {
|
||||
|
@ -43,6 +42,7 @@ void mStateExtdataDeinit(struct mStateExtdata* extdata) {
|
|||
extdata->data[i].clean(extdata->data[i].data);
|
||||
}
|
||||
}
|
||||
memset(extdata->data, 0, sizeof(extdata->data));
|
||||
}
|
||||
|
||||
void mStateExtdataPut(struct mStateExtdata* extdata, enum mStateExtdataTag tag, struct mStateExtdataItem* item) {
|
||||
|
@ -215,7 +215,7 @@ static int _loadPNGChunkHandler(png_structp png, png_unknown_chunkp chunk) {
|
|||
if (!strcmp((const char*) chunk->name, "gbAs")) {
|
||||
void* state = bundle->state;
|
||||
if (!state) {
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
uLongf len = bundle->stateSize;
|
||||
uncompress((Bytef*) state, &len, chunk->data, chunk->size);
|
||||
|
@ -296,6 +296,54 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx
|
|||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
static bool _loadPNGExtadata(struct VFile* vf, struct mStateExtdata* extdata) {
|
||||
png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES);
|
||||
png_infop info = png_create_info_struct(png);
|
||||
png_infop end = png_create_info_struct(png);
|
||||
if (!png || !info || !end) {
|
||||
PNGReadClose(png, info, end);
|
||||
return false;
|
||||
}
|
||||
struct mBundledState bundle = {
|
||||
.stateSize = 0,
|
||||
.state = NULL,
|
||||
.extdata = extdata
|
||||
};
|
||||
|
||||
PNGInstallChunkHandler(png, &bundle, _loadPNGChunkHandler, "gbAs gbAx");
|
||||
bool success = PNGReadHeader(png, info);
|
||||
if (!success) {
|
||||
PNGReadClose(png, info, end);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned width = png_get_image_width(png, info);
|
||||
unsigned height = png_get_image_height(png, info);
|
||||
uint32_t* pixels = NULL;
|
||||
pixels = malloc(width * height * 4);
|
||||
if (!pixels) {
|
||||
PNGReadClose(png, info, end);
|
||||
return false;
|
||||
}
|
||||
|
||||
success = PNGReadPixels(png, info, pixels, width, height, width);
|
||||
success = success && PNGReadFooter(png, end);
|
||||
PNGReadClose(png, info, end);
|
||||
|
||||
if (success) {
|
||||
struct mStateExtdataItem item = {
|
||||
.size = width * height * 4,
|
||||
.data = pixels,
|
||||
.clean = free
|
||||
};
|
||||
mStateExtdataPut(extdata, EXTDATA_SCREENSHOT, &item);
|
||||
} else {
|
||||
free(pixels);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool mCoreSaveStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
||||
|
@ -423,6 +471,20 @@ void* mCoreExtractState(struct mCore* core, struct VFile* vf, struct mStateExtda
|
|||
return state;
|
||||
}
|
||||
|
||||
bool mCoreExtractExtdata(struct mCore* core, struct VFile* vf, struct mStateExtdata* extdata) {
|
||||
#ifdef USE_PNG
|
||||
if (isPNG(vf)) {
|
||||
return _loadPNGExtadata(vf, extdata);
|
||||
}
|
||||
#endif
|
||||
if (!core) {
|
||||
return false;
|
||||
}
|
||||
ssize_t stateSize = core->stateSize(core);
|
||||
vf->seek(vf, stateSize, SEEK_SET);
|
||||
return mStateExtdataDeserialize(extdata, vf);
|
||||
}
|
||||
|
||||
bool mCoreLoadStateNamed(struct mCore* core, struct VFile* vf, int flags) {
|
||||
struct mStateExtdata extdata;
|
||||
mStateExtdataInit(&extdata);
|
||||
|
|
|
@ -61,6 +61,16 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
|
|||
},
|
||||
.nStates = 2
|
||||
};
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Show status OSD",
|
||||
.data = "showOSD",
|
||||
.submenu = 0,
|
||||
.state = true,
|
||||
.validStates = (const char*[]) {
|
||||
"Off", "On"
|
||||
},
|
||||
.nStates = 2
|
||||
};
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Autosave state",
|
||||
.data = "autosave",
|
||||
|
@ -81,6 +91,16 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
|
|||
},
|
||||
.nStates = 2
|
||||
};
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Mute",
|
||||
.data = "mute",
|
||||
.submenu = 0,
|
||||
.state = false,
|
||||
.validStates = (const char*[]) {
|
||||
"Off", "On"
|
||||
},
|
||||
.nStates = 2
|
||||
};
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Use BIOS if found",
|
||||
.data = "useBios",
|
||||
|
@ -91,10 +111,12 @@ void mGUIShowConfig(struct mGUIRunner* runner, struct GUIMenuItem* extra, size_t
|
|||
},
|
||||
.nStates = 2
|
||||
};
|
||||
#ifdef M_CORE_GBA
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Select GBA BIOS path",
|
||||
.data = "gba.bios",
|
||||
};
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
*GUIMenuItemListAppend(&menu.items) = (struct GUIMenuItem) {
|
||||
.title = "Select GB BIOS path",
|
||||
|
|
|
@ -56,6 +56,7 @@ static const struct mInputPlatformInfo _mGUIKeyInfo = {
|
|||
[mGUI_INPUT_SCREENSHOT] = "Take screenshot",
|
||||
[mGUI_INPUT_FAST_FORWARD_HELD] = "Fast forward (held)",
|
||||
[mGUI_INPUT_FAST_FORWARD_TOGGLE] = "Fast forward (toggle)",
|
||||
[mGUI_INPUT_MUTE_TOGGLE] = "Mute (toggle)",
|
||||
},
|
||||
.nKeys = GUI_INPUT_MAX
|
||||
};
|
||||
|
@ -201,6 +202,7 @@ void mGUIInit(struct mGUIRunner* runner, const char* port) {
|
|||
#else
|
||||
mCoreConfigSetDefaultIntValue(&runner->config, "autosave", true);
|
||||
#endif
|
||||
mCoreConfigSetDefaultIntValue(&runner->config, "showOSD", true);
|
||||
mCoreConfigLoad(&runner->config);
|
||||
mCoreConfigGetIntValue(&runner->config, "logLevel", &logger.logLevel);
|
||||
|
||||
|
@ -429,6 +431,12 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
mCoreLoadState(runner->core, 0, SAVESTATE_SCREENSHOT | SAVESTATE_RTC);
|
||||
}
|
||||
|
||||
int showOSD = true;
|
||||
mCoreConfigGetIntValue(&runner->config, "showOSD", &showOSD);
|
||||
|
||||
int drawFps = false;
|
||||
mCoreConfigGetIntValue(&runner->config, "fpsCounter", &drawFps);
|
||||
|
||||
bool running = true;
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
|
@ -490,6 +498,11 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
runner->setFrameLimiter(runner, true);
|
||||
}
|
||||
}
|
||||
if (guiKeys & (1 << mGUI_INPUT_MUTE_TOGGLE)) {
|
||||
int mute = !runner->core->opts.mute;
|
||||
mCoreConfigSetUIntValue(&runner->config, "mute", mute);
|
||||
runner->core->reloadConfigOption(runner->core, "mute", &runner->config);
|
||||
}
|
||||
uint16_t keys = runner->pollGameInput(runner);
|
||||
if (runner->prepareForFrame) {
|
||||
runner->prepareForFrame(runner);
|
||||
|
@ -497,16 +510,29 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
runner->core->setKeys(runner->core, keys);
|
||||
runner->core->runFrame(runner->core);
|
||||
if (runner->drawFrame) {
|
||||
int drawFps = false;
|
||||
mCoreConfigGetIntValue(&runner->config, "fpsCounter", &drawFps);
|
||||
|
||||
runner->params.drawStart();
|
||||
runner->drawFrame(runner, false);
|
||||
if (drawFps) {
|
||||
if (showOSD || drawFps) {
|
||||
if (runner->params.guiPrepare) {
|
||||
runner->params.guiPrepare();
|
||||
}
|
||||
GUIFontPrintf(runner->params.font, 0, GUIFontHeight(runner->params.font), GUI_ALIGN_LEFT, 0x7FFFFFFF, "%.2f fps", runner->fps);
|
||||
if (drawFps) {
|
||||
GUIFontPrintf(runner->params.font, 0, GUIFontHeight(runner->params.font), GUI_ALIGN_LEFT, 0x7FFFFFFF, "%.2f fps", runner->fps);
|
||||
}
|
||||
if (showOSD) {
|
||||
unsigned origin = runner->params.width - GUIFontHeight(runner->params.font) / 2;
|
||||
unsigned w;
|
||||
if (fastForward || (heldKeys & (1 << mGUI_INPUT_FAST_FORWARD_HELD))) {
|
||||
GUIFontDrawIcon(runner->params.font, origin, GUIFontHeight(runner->params.font) / 2, GUI_ALIGN_RIGHT, 0, 0x7FFFFFFF, GUI_ICON_STATUS_FAST_FORWARD);
|
||||
GUIFontIconMetrics(runner->params.font, GUI_ICON_STATUS_FAST_FORWARD, &w, NULL);
|
||||
origin -= w + GUIFontHeight(runner->params.font) / 2;
|
||||
}
|
||||
if (runner->core->opts.mute) {
|
||||
GUIFontDrawIcon(runner->params.font, origin, GUIFontHeight(runner->params.font) / 2, GUI_ALIGN_RIGHT, 0, 0x7FFFFFFF, GUI_ICON_STATUS_MUTE);
|
||||
GUIFontIconMetrics(runner->params.font, GUI_ICON_STATUS_MUTE, &w, NULL);
|
||||
origin -= w + GUIFontHeight(runner->params.font) / 2;
|
||||
}
|
||||
}
|
||||
if (runner->params.guiFinish) {
|
||||
runner->params.guiFinish();
|
||||
}
|
||||
|
@ -597,6 +623,8 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) {
|
|||
if (runner->unpaused) {
|
||||
runner->unpaused(runner);
|
||||
}
|
||||
mCoreConfigGetIntValue(&runner->config, "fpsCounter", &drawFps);
|
||||
mCoreConfigGetIntValue(&runner->config, "showOSD", &showOSD);
|
||||
}
|
||||
mLOG(GUI_RUNNER, DEBUG, "Shutting down...");
|
||||
if (runner->gameUnloaded) {
|
||||
|
|
|
@ -23,7 +23,8 @@ enum mGUIInput {
|
|||
mGUI_INPUT_SCREEN_MODE,
|
||||
mGUI_INPUT_SCREENSHOT,
|
||||
mGUI_INPUT_FAST_FORWARD_HELD,
|
||||
mGUI_INPUT_FAST_FORWARD_TOGGLE
|
||||
mGUI_INPUT_FAST_FORWARD_TOGGLE,
|
||||
mGUI_INPUT_MUTE_TOGGLE,
|
||||
};
|
||||
|
||||
struct mGUIBackground {
|
||||
|
|
|
@ -206,10 +206,6 @@ static THREAD_ENTRY _proxyThread(void* logger) {
|
|||
}
|
||||
}
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
|
||||
#ifdef _3DS
|
||||
svcExitThread();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -296,11 +296,16 @@ bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block) {
|
|||
mVideoLoggerRendererRunInjected(logger);
|
||||
ignorePackets = channel->ignorePackets;
|
||||
}
|
||||
struct mVideoLoggerDirtyInfo buffer = {0};
|
||||
struct mVideoLoggerDirtyInfo item = {0};
|
||||
while (logger->readData(logger, &item, sizeof(item), block)) {
|
||||
while (logger->readData(logger, &buffer, sizeof(buffer), block)) {
|
||||
LOAD_32LE(item.type, 0, &buffer.type);
|
||||
if (ignorePackets & (1 << item.type)) {
|
||||
continue;
|
||||
}
|
||||
LOAD_32LE(item.address, 0, &buffer.address);
|
||||
LOAD_32LE(item.value, 0, &buffer.value);
|
||||
LOAD_32LE(item.value2, 0, &buffer.value2);
|
||||
switch (item.type) {
|
||||
case DIRTY_SCANLINE:
|
||||
if (channel && channel->injectionPoint == LOGGER_INJECTION_FIRST_SCANLINE && !channel->injecting && item.address == 0) {
|
||||
|
|
|
@ -138,8 +138,10 @@ static void _GBCoreDeinit(struct mCore* core) {
|
|||
GBDestroy(core->board);
|
||||
mappedMemoryFree(core->cpu, sizeof(struct SM83Core));
|
||||
mappedMemoryFree(core->board, sizeof(struct GB));
|
||||
#if defined USE_DEBUGGERS && (!defined(MINIMAL_CORE) || MINIMAL_CORE < 2)
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
mDirectorySetDeinit(&core->dirs);
|
||||
#endif
|
||||
#ifdef USE_DEBUGGERS
|
||||
if (core->symbolTable) {
|
||||
mDebuggerSymbolTableDestroy(core->symbolTable);
|
||||
}
|
||||
|
|
|
@ -700,7 +700,7 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
gb->memory.ie = state->ie;
|
||||
|
||||
gb->audio.enable = GBAudioEnableGetEnable(*gb->audio.nr52);
|
||||
if (GBAudioEnableGetEnable(gb->audio.enable)) {
|
||||
if (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]);
|
||||
|
|
164
src/gb/mbc.c
164
src/gb/mbc.c
|
@ -37,6 +37,8 @@ static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value);
|
|||
static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value);
|
||||
static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value);
|
||||
|
||||
static uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address);
|
||||
|
@ -45,6 +47,8 @@ static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t valu
|
|||
|
||||
static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBBBDRead(struct GBMemory*, uint16_t address);
|
||||
static uint8_t _GBHitekRead(struct GBMemory*, uint16_t address);
|
||||
|
||||
static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address);
|
||||
static void _GBPocketCamCapture(struct GBMemory*);
|
||||
|
@ -145,6 +149,31 @@ static bool _isWisdomTree(const uint8_t* mem, size_t size) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t size) {
|
||||
const struct GBCartridge* cart = (const struct GBCartridge*) &mem[0x100];
|
||||
|
||||
switch (cart->type) {
|
||||
case 0:
|
||||
if (_isWisdomTree(mem, size)) {
|
||||
return GB_UNL_WISDOM_TREE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t secondaryLogo = doCrc32(&mem[0x184], 0x30);
|
||||
switch (secondaryLogo) {
|
||||
case 0x4fdab691:
|
||||
return GB_UNL_HITEK;
|
||||
case 0xc7d8c1df:
|
||||
case 0x6d1ea662: // Garou
|
||||
if (mem[0x7FFF] != 0x01) { // Make sure we're not using a "fixed" version
|
||||
return GB_UNL_BBD;
|
||||
}
|
||||
}
|
||||
|
||||
return GB_MBC_AUTODETECT;
|
||||
}
|
||||
|
||||
void GBMBCSwitchSramBank(struct GB* gb, int bank) {
|
||||
size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
|
||||
if (bankStart + GB_SIZE_EXTERNAL_RAM > gb->sramSize) {
|
||||
|
@ -202,15 +231,13 @@ void GBMBCInit(struct GB* gb) {
|
|||
gb->sramSize = 0x10000;
|
||||
break;
|
||||
}
|
||||
if (gb->memory.mbcType == GB_MBC_AUTODETECT) {
|
||||
gb->memory.mbcType = _detectUnlMBC(gb->memory.rom, gb->memory.romSize);
|
||||
}
|
||||
|
||||
if (gb->memory.mbcType == GB_MBC_AUTODETECT) {
|
||||
switch (cart->type) {
|
||||
case 0:
|
||||
if (_isWisdomTree(gb->memory.rom, gb->memory.romSize)) {
|
||||
gb->memory.mbcType = GB_UNL_WISDOM_TREE;
|
||||
break;
|
||||
}
|
||||
// Fall through
|
||||
case 8:
|
||||
case 9:
|
||||
gb->memory.mbcType = GB_MBC_NONE;
|
||||
|
@ -346,6 +373,16 @@ void GBMBCInit(struct GB* gb) {
|
|||
case GB_UNL_WISDOM_TREE:
|
||||
gb->memory.mbcWrite = _GBWisdomTree;
|
||||
break;
|
||||
case GB_UNL_BBD:
|
||||
gb->memory.mbcWrite = _GBBBD;
|
||||
gb->memory.mbcRead = _GBBBDRead;
|
||||
break;
|
||||
case GB_UNL_HITEK:
|
||||
gb->memory.mbcWrite = _GBHitek;
|
||||
gb->memory.mbcRead = _GBHitekRead;
|
||||
gb->memory.mbcState.bbd.dataSwapMode = 7;
|
||||
gb->memory.mbcState.bbd.bankSwapMode = 7;
|
||||
break;
|
||||
case GB_UNL_PKJD:
|
||||
gb->memory.mbcWrite = _GBPKJD;
|
||||
gb->memory.mbcRead = _GBPKJDRead;
|
||||
|
@ -1306,6 +1343,123 @@ static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) {
|
|||
}
|
||||
}
|
||||
|
||||
static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) {
|
||||
uint8_t newbyte = 0;
|
||||
int i;
|
||||
for(i = 0; i < 8; ++i) {
|
||||
int oldbit = reorder[i];
|
||||
int newbit = i;
|
||||
newbyte += ((input >> oldbit) & 1) << newbit;
|
||||
}
|
||||
|
||||
return newbyte;
|
||||
}
|
||||
|
||||
static const uint8_t _bbdDataReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 03 - NOT KNOWN YET
|
||||
{ 0, 5, 1, 3, 4, 2, 6, 7 }, // 04 - Garou
|
||||
{ 0, 4, 2, 3, 1, 5, 6, 7 }, // 05 - Harry
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
|
||||
{ 0, 1, 5, 3, 4, 2, 6, 7 }, // 07 - Digimon
|
||||
};
|
||||
|
||||
static const uint8_t _bbdBankReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET
|
||||
{ 3, 4, 2, 0, 1, 5, 6, 7 }, // 03 - 0,1 unconfirmed. Digimon/Garou
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 04 - NOT KNOWN YET
|
||||
{ 1, 2, 3, 4, 0, 5, 6, 7 }, // 05 - 0,1 unconfirmed. Harry
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // 07 - NOT KNOWN YET
|
||||
};
|
||||
|
||||
void _GBBBD(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
switch (address & 0xF0FF) {
|
||||
case 0x2000:
|
||||
value = _reorderBits(value, _bbdBankReordering[memory->mbcState.bbd.bankSwapMode]);
|
||||
break;
|
||||
case 0x2001:
|
||||
memory->mbcState.bbd.dataSwapMode = value & 0x07;
|
||||
if (!(memory->mbcState.bbd.dataSwapMode == 0x07 || memory->mbcState.bbd.dataSwapMode == 0x05 || memory->mbcState.bbd.dataSwapMode == 0x04 || memory->mbcState.bbd.dataSwapMode == 0x00)) {
|
||||
mLOG(GB_MBC, STUB, "Bitswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
|
||||
}
|
||||
break;
|
||||
case 0x2080:
|
||||
memory->mbcState.bbd.bankSwapMode = value & 0x07;
|
||||
if (!(memory->mbcState.bbd.bankSwapMode == 0x03 || memory->mbcState.bbd.bankSwapMode == 0x05 || memory->mbcState.bbd.bankSwapMode == 0x00)) {
|
||||
mLOG(GB_MBC, STUB, "Bankswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
uint8_t _GBBBDRead(struct GBMemory* memory, uint16_t address) {
|
||||
switch (address >> 14) {
|
||||
case 0:
|
||||
default:
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case 1:
|
||||
return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _bbdDataReordering[memory->mbcState.bbd.dataSwapMode]);
|
||||
}
|
||||
}
|
||||
|
||||
static const uint8_t _hitekDataReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 0, 6, 5, 3, 4, 1, 2, 7 },
|
||||
{ 0, 5, 6, 3, 4, 2, 1, 7 },
|
||||
{ 0, 6, 2, 3, 4, 5, 1, 7 },
|
||||
{ 0, 6, 1, 3, 4, 5, 2, 7 },
|
||||
{ 0, 1, 6, 3, 4, 5, 2, 7 },
|
||||
{ 0, 2, 6, 3, 4, 1, 5, 7 },
|
||||
{ 0, 6, 2, 3, 4, 1, 5, 7 },
|
||||
};
|
||||
|
||||
static const uint8_t _hitekBankReordering[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 3, 2, 1, 0, 4, 5, 6, 7 },
|
||||
{ 2, 1, 0, 3, 4, 5, 6, 7 },
|
||||
{ 1, 0, 3, 2, 4, 5, 6, 7 },
|
||||
{ 0, 3, 2, 1, 4, 5, 6, 7 },
|
||||
{ 2, 3, 0, 1, 4, 5, 6, 7 },
|
||||
{ 3, 0, 1, 2, 4, 5, 6, 7 },
|
||||
{ 2, 0, 3, 1, 4, 5, 6, 7 },
|
||||
};
|
||||
|
||||
void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) {
|
||||
struct GBMemory* memory = &gb->memory;
|
||||
switch (address & 0xF0FF) {
|
||||
case 0x2000:
|
||||
value = _reorderBits(value, _hitekBankReordering[memory->mbcState.bbd.bankSwapMode]);
|
||||
break;
|
||||
case 0x2001:
|
||||
memory->mbcState.bbd.dataSwapMode = value & 0x07;
|
||||
break;
|
||||
case 0x2080:
|
||||
memory->mbcState.bbd.bankSwapMode = value & 0x07;
|
||||
break;
|
||||
case 0x300:
|
||||
// See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return
|
||||
return;
|
||||
}
|
||||
_GBMBC5(gb, address, value);
|
||||
}
|
||||
|
||||
uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) {
|
||||
switch (address >> 14) {
|
||||
case 0:
|
||||
default:
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case 1:
|
||||
return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _hitekDataReordering[memory->mbcState.bbd.dataSwapMode]);
|
||||
}
|
||||
}
|
||||
|
||||
void GBMBCRTCRead(struct GB* gb) {
|
||||
struct GBMBCRTCSaveBuffer rtcBuffer;
|
||||
struct VFile* vf = gb->sramVf;
|
||||
|
|
|
@ -84,6 +84,10 @@ static void GBSetActiveRegion(struct SM83Core* cpu, uint16_t address) {
|
|||
case GB_REGION_CART_BANK1 + 1:
|
||||
case GB_REGION_CART_BANK1 + 2:
|
||||
case GB_REGION_CART_BANK1 + 3:
|
||||
if ((gb->memory.mbcType & GB_UNL_BBD) == GB_UNL_BBD) {
|
||||
cpu->memory.cpuLoad8 = GBLoad8;
|
||||
break;
|
||||
}
|
||||
cpu->memory.cpuLoad8 = GBFastLoad8;
|
||||
if (gb->memory.mbcType != GB_MBC6) {
|
||||
cpu->memory.activeRegion = memory->romBank;
|
||||
|
@ -277,6 +281,9 @@ uint8_t GBLoad8(struct SM83Core* cpu, uint16_t address) {
|
|||
if (address >= memory->romSize) {
|
||||
return 0xFF;
|
||||
}
|
||||
if ((memory->mbcType & GB_UNL_BBD) == GB_UNL_BBD) {
|
||||
return memory->mbcRead(memory, address);
|
||||
}
|
||||
return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)];
|
||||
case GB_REGION_VRAM:
|
||||
case GB_REGION_VRAM + 1:
|
||||
|
@ -754,6 +761,11 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) {
|
|||
state->memory.mmm01.locked = memory->mbcState.mmm01.locked;
|
||||
state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0;
|
||||
break;
|
||||
case GB_UNL_BBD:
|
||||
case GB_UNL_HITEK:
|
||||
state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode;
|
||||
state->memory.bbd.bankSwapMode = memory->mbcState.bbd.bankSwapMode;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -841,6 +853,11 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) {
|
|||
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
|
||||
}
|
||||
break;
|
||||
case GB_UNL_BBD:
|
||||
case GB_UNL_HITEK:
|
||||
memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7;
|
||||
memory->mbcState.bbd.bankSwapMode = state->memory.bbd.bankSwapMode & 0x7;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
153
src/gba/audio.c
153
src/gba/audio.c
|
@ -23,7 +23,6 @@
|
|||
mLOG_DEFINE_CATEGORY(GBA_AUDIO, "GBA Audio", "gba.audio");
|
||||
|
||||
const unsigned GBA_AUDIO_SAMPLES = 2048;
|
||||
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t);
|
||||
const int GBA_AUDIO_VOLUME_MAX = 0x100;
|
||||
|
||||
static const int CLOCKS_PER_FRAME = 0x800;
|
||||
|
@ -48,8 +47,6 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
|
|||
// Guess too large; we hang producing extra samples if we guess too low
|
||||
blip_set_rates(audio->psg.left, GBA_ARM7TDMI_FREQUENCY, 96000);
|
||||
blip_set_rates(audio->psg.right, GBA_ARM7TDMI_FREQUENCY, 96000);
|
||||
CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE);
|
||||
CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE);
|
||||
|
||||
audio->externalMixing = false;
|
||||
audio->forceDisableChA = false;
|
||||
|
@ -64,7 +61,17 @@ void GBAAudioReset(struct GBAAudio* audio) {
|
|||
mTimingSchedule(&audio->p->timing, &audio->sampleEvent, 0);
|
||||
audio->chA.dmaSource = 1;
|
||||
audio->chB.dmaSource = 2;
|
||||
audio->chA.fifoWrite = 0;
|
||||
audio->chA.fifoRead = 0;
|
||||
audio->chA.internalSample = 0;
|
||||
audio->chA.internalRemaining = 0;
|
||||
memset(audio->chA.fifo, 0, sizeof(audio->chA.fifo));
|
||||
audio->chA.sample = 0;
|
||||
audio->chB.fifoWrite = 0;
|
||||
audio->chB.fifoRead = 0;
|
||||
audio->chB.internalSample = 0;
|
||||
audio->chB.internalRemaining = 0;
|
||||
memset(audio->chB.fifo, 0, sizeof(audio->chB.fifo));
|
||||
audio->chB.sample = 0;
|
||||
audio->sampleRate = 0x8000;
|
||||
audio->soundbias = 0x200;
|
||||
|
@ -84,14 +91,10 @@ void GBAAudioReset(struct GBAAudio* audio) {
|
|||
blip_clear(audio->psg.left);
|
||||
blip_clear(audio->psg.right);
|
||||
audio->clock = 0;
|
||||
CircleBufferClear(&audio->chA.fifo);
|
||||
CircleBufferClear(&audio->chB.fifo);
|
||||
}
|
||||
|
||||
void GBAAudioDeinit(struct GBAAudio* audio) {
|
||||
GBAudioDeinit(&audio->psg);
|
||||
CircleBufferDeinit(&audio->chA.fifo);
|
||||
CircleBufferDeinit(&audio->chB.fifo);
|
||||
}
|
||||
|
||||
void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) {
|
||||
|
@ -199,10 +202,12 @@ void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) {
|
|||
audio->chBLeft = GBARegisterSOUNDCNT_HIGetChBLeft(value);
|
||||
audio->chBTimer = GBARegisterSOUNDCNT_HIGetChBTimer(value);
|
||||
if (GBARegisterSOUNDCNT_HIIsChAReset(value)) {
|
||||
CircleBufferClear(&audio->chA.fifo);
|
||||
audio->chA.fifoWrite = 0;
|
||||
audio->chA.fifoRead = 0;
|
||||
}
|
||||
if (GBARegisterSOUNDCNT_HIIsChBReset(value)) {
|
||||
CircleBufferClear(&audio->chB.fifo);
|
||||
audio->chB.fifoWrite = 0;
|
||||
audio->chB.fifoRead = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,26 +224,25 @@ void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) {
|
|||
audio->psg.ch3.wavedata32[address | (!audio->psg.ch3.bank * 4)] = value;
|
||||
}
|
||||
|
||||
void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) {
|
||||
struct CircleBuffer* fifo;
|
||||
uint32_t GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) {
|
||||
struct GBAAudioFIFO* channel;
|
||||
switch (address) {
|
||||
case REG_FIFO_A_LO:
|
||||
fifo = &audio->chA.fifo;
|
||||
channel = &audio->chA;
|
||||
break;
|
||||
case REG_FIFO_B_LO:
|
||||
fifo = &audio->chB.fifo;
|
||||
channel = &audio->chB;
|
||||
break;
|
||||
default:
|
||||
mLOG(GBA_AUDIO, ERROR, "Bad FIFO write to address 0x%03x", address);
|
||||
return;
|
||||
return value;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
while (!CircleBufferWrite8(fifo, value >> (8 * i))) {
|
||||
int8_t dummy;
|
||||
CircleBufferRead8(fifo, &dummy);
|
||||
}
|
||||
channel->fifo[channel->fifoWrite] = value;
|
||||
++channel->fifoWrite;
|
||||
if (channel->fifoWrite == GBA_AUDIO_FIFO_SIZE) {
|
||||
channel->fifoWrite = 0;
|
||||
}
|
||||
return channel->fifo[channel->fifoWrite];
|
||||
}
|
||||
|
||||
void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
|
||||
|
@ -251,17 +255,33 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
|
|||
mLOG(GBA_AUDIO, ERROR, "Bad FIFO write to address 0x%03x", fifoId);
|
||||
return;
|
||||
}
|
||||
if (CircleBufferSize(&channel->fifo) <= 4 * sizeof(int32_t) && channel->dmaSource > 0) {
|
||||
int fifoSize;
|
||||
if (channel->fifoWrite >= channel->fifoRead) {
|
||||
fifoSize = channel->fifoWrite - channel->fifoRead;
|
||||
} else {
|
||||
fifoSize = GBA_AUDIO_FIFO_SIZE - channel->fifoRead + channel->fifoWrite;
|
||||
}
|
||||
if (GBA_AUDIO_FIFO_SIZE - fifoSize > 4 && channel->dmaSource > 0) {
|
||||
struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource];
|
||||
if (GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM) {
|
||||
dma->when = mTimingCurrentTime(&audio->p->timing) - cycles;
|
||||
dma->nextCount = 4;
|
||||
GBADMASchedule(audio->p, channel->dmaSource, dma);
|
||||
} else {
|
||||
channel->dmaSource = 0;
|
||||
}
|
||||
}
|
||||
CircleBufferRead8(&channel->fifo, (int8_t*) &channel->sample);
|
||||
if (!channel->internalRemaining && fifoSize) {
|
||||
channel->internalSample = channel->fifo[channel->fifoRead];
|
||||
channel->internalRemaining = 4;
|
||||
++channel->fifoRead;
|
||||
if (channel->fifoRead == GBA_AUDIO_FIFO_SIZE) {
|
||||
channel->fifoRead = 0;
|
||||
}
|
||||
}
|
||||
channel->sample = channel->internalSample;
|
||||
if (channel->internalRemaining) {
|
||||
channel->internalSample >>= 8;
|
||||
--channel->internalRemaining;
|
||||
}
|
||||
}
|
||||
|
||||
static int _applyBias(struct GBAAudio* audio, int sample) {
|
||||
|
@ -345,37 +365,76 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
|||
void GBAAudioSerialize(const struct GBAAudio* audio, struct GBASerializedState* state) {
|
||||
GBAudioPSGSerialize(&audio->psg, &state->audio.psg, &state->audio.flags);
|
||||
|
||||
CircleBufferDump(&audio->chA.fifo, state->audio.fifoA, sizeof(state->audio.fifoA));
|
||||
CircleBufferDump(&audio->chB.fifo, state->audio.fifoB, sizeof(state->audio.fifoB));
|
||||
uint32_t fifoSize = CircleBufferSize(&audio->chA.fifo);
|
||||
STORE_32(fifoSize, 0, &state->audio.fifoSizeA);
|
||||
fifoSize = CircleBufferSize(&audio->chB.fifo);
|
||||
STORE_32(fifoSize, 0, &state->audio.fifoSizeB);
|
||||
STORE_32(audio->chA.internalSample, 0, &state->audio.internalA);
|
||||
STORE_32(audio->chB.internalSample, 0, &state->audio.internalB);
|
||||
state->audio.sampleA = audio->chA.sample;
|
||||
state->audio.sampleB = audio->chB.sample;
|
||||
|
||||
int readA = audio->chA.fifoRead;
|
||||
int readB = audio->chB.fifoRead;
|
||||
size_t i;
|
||||
for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
|
||||
STORE_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
|
||||
STORE_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB);
|
||||
++readA;
|
||||
if (readA == GBA_AUDIO_FIFO_SIZE) {
|
||||
readA = 0;
|
||||
}
|
||||
++readB;
|
||||
if (readB == GBA_AUDIO_FIFO_SIZE) {
|
||||
readB = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int fifoSizeA;
|
||||
if (audio->chA.fifoWrite >= audio->chA.fifoRead) {
|
||||
fifoSizeA = audio->chA.fifoWrite - audio->chA.fifoRead;
|
||||
} else {
|
||||
fifoSizeA = GBA_AUDIO_FIFO_SIZE - audio->chA.fifoRead + audio->chA.fifoWrite;
|
||||
}
|
||||
|
||||
int fifoSizeB;
|
||||
if (audio->chB.fifoWrite >= audio->chB.fifoRead) {
|
||||
fifoSizeB = audio->chB.fifoWrite - audio->chB.fifoRead;
|
||||
} else {
|
||||
fifoSizeB = GBA_AUDIO_FIFO_SIZE - audio->chB.fifoRead + audio->chB.fifoWrite;
|
||||
}
|
||||
|
||||
GBASerializedAudioFlags flags = 0;
|
||||
flags = GBASerializedAudioFlagsSetFIFOSamplesA(flags, fifoSizeA);
|
||||
flags = GBASerializedAudioFlagsSetFIFOSamplesB(flags, fifoSizeB);
|
||||
flags = GBASerializedAudioFlagsSetFIFOInternalSamplesA(flags, audio->chA.internalRemaining);
|
||||
flags = GBASerializedAudioFlagsSetFIFOInternalSamplesB(flags, audio->chB.internalRemaining);
|
||||
STORE_16(flags, 0, &state->audio.gbaFlags);
|
||||
STORE_32(audio->sampleEvent.when - mTimingCurrentTime(&audio->p->timing), 0, &state->audio.nextSample);
|
||||
}
|
||||
|
||||
void GBAAudioDeserialize(struct GBAAudio* audio, const struct GBASerializedState* state) {
|
||||
GBAudioPSGDeserialize(&audio->psg, &state->audio.psg, &state->audio.flags);
|
||||
|
||||
CircleBufferClear(&audio->chA.fifo);
|
||||
CircleBufferClear(&audio->chB.fifo);
|
||||
uint32_t fifoSize;
|
||||
LOAD_32(fifoSize, 0, &state->audio.fifoSizeA);
|
||||
if (fifoSize > CircleBufferCapacity(&audio->chA.fifo)) {
|
||||
fifoSize = CircleBufferCapacity(&audio->chA.fifo);
|
||||
}
|
||||
size_t i;
|
||||
for (i = 0; i < fifoSize; ++i) {
|
||||
CircleBufferWrite8(&audio->chA.fifo, state->audio.fifoA[i]);
|
||||
}
|
||||
LOAD_32(audio->chA.internalSample, 0, &state->audio.internalA);
|
||||
LOAD_32(audio->chB.internalSample, 0, &state->audio.internalB);
|
||||
audio->chA.sample = state->audio.sampleA;
|
||||
audio->chB.sample = state->audio.sampleB;
|
||||
|
||||
LOAD_32(fifoSize, 0, &state->audio.fifoSizeB);
|
||||
if (fifoSize > CircleBufferCapacity(&audio->chB.fifo)) {
|
||||
fifoSize = CircleBufferCapacity(&audio->chB.fifo);
|
||||
}
|
||||
for (i = 0; i < fifoSize; ++i) {
|
||||
CircleBufferWrite8(&audio->chB.fifo, state->audio.fifoB[i]);
|
||||
int readA = 0;
|
||||
int readB = 0;
|
||||
size_t i;
|
||||
for (i = 0; i < GBA_AUDIO_FIFO_SIZE; ++i) {
|
||||
LOAD_32(audio->chA.fifo[readA], i << 2, state->audio.fifoA);
|
||||
LOAD_32(audio->chB.fifo[readB], i << 2, state->audio.fifoB);
|
||||
++readA;
|
||||
++readB;
|
||||
}
|
||||
audio->chA.fifoRead = 0;
|
||||
audio->chB.fifoRead = 0;
|
||||
|
||||
GBASerializedAudioFlags flags;
|
||||
LOAD_16(flags, 0, &state->audio.gbaFlags);
|
||||
audio->chA.fifoWrite = GBASerializedAudioFlagsGetFIFOSamplesA(flags);
|
||||
audio->chB.fifoWrite = GBASerializedAudioFlagsGetFIFOSamplesB(flags);
|
||||
audio->chA.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesA(flags);
|
||||
audio->chB.internalRemaining = GBASerializedAudioFlagsGetFIFOInternalSamplesB(flags);
|
||||
|
||||
uint32_t when;
|
||||
LOAD_32(when, 0, &state->audio.nextSample);
|
||||
|
|
|
@ -228,6 +228,11 @@ static void _GBACoreDeinit(struct mCore* core) {
|
|||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
mDirectorySetDeinit(&core->dirs);
|
||||
#endif
|
||||
#ifdef USE_DEBUGGERS
|
||||
if (core->symbolTable) {
|
||||
mDebuggerSymbolTableDestroy(core->symbolTable);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct GBACore* gbacore = (struct GBACore*) core;
|
||||
free(gbacore->debuggerPlatform);
|
||||
|
|
|
@ -134,29 +134,37 @@ void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) {
|
|||
void GBADMARunHblank(struct GBA* gba, int32_t cycles) {
|
||||
struct GBAMemory* memory = &gba->memory;
|
||||
struct GBADMA* dma;
|
||||
bool found = false;
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
dma = &memory->dma[i];
|
||||
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_HBLANK && !dma->nextCount) {
|
||||
dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles;
|
||||
dma->nextCount = dma->count;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
GBADMAUpdate(gba);
|
||||
if (found) {
|
||||
GBADMAUpdate(gba);
|
||||
}
|
||||
}
|
||||
|
||||
void GBADMARunVblank(struct GBA* gba, int32_t cycles) {
|
||||
struct GBAMemory* memory = &gba->memory;
|
||||
struct GBADMA* dma;
|
||||
bool found = false;
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
dma = &memory->dma[i];
|
||||
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_VBLANK && !dma->nextCount) {
|
||||
dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles;
|
||||
dma->nextCount = dma->count;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
GBADMAUpdate(gba);
|
||||
if (found) {
|
||||
GBADMAUpdate(gba);
|
||||
}
|
||||
}
|
||||
|
||||
void GBADMARunDisplayStart(struct GBA* gba, int32_t cycles) {
|
||||
|
@ -211,7 +219,7 @@ void GBADMAUpdate(struct GBA* gba) {
|
|||
struct GBADMA* dma = &memory->dma[i];
|
||||
if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
|
||||
int32_t time = dma->when - currentTime;
|
||||
if (memory->activeDMA == -1 || (dma->count == dma->nextCount && time < leastTime)) {
|
||||
if (memory->activeDMA == -1 || time < leastTime) {
|
||||
leastTime = time;
|
||||
memory->activeDMA = i;
|
||||
}
|
||||
|
@ -295,6 +303,16 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) {
|
|||
info->nextCount = wordsRemaining;
|
||||
info->nextSource = source;
|
||||
info->nextDest = dest;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
struct GBADMA* dma = &memory->dma[i];
|
||||
int32_t time = dma->when - info->when;
|
||||
if (time < 0 && GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
|
||||
dma->when = info->when;
|
||||
}
|
||||
}
|
||||
|
||||
if (!wordsRemaining) {
|
||||
info->nextCount |= 0x80000000;
|
||||
if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) {
|
||||
|
|
|
@ -289,9 +289,11 @@ void _mp2kStep(struct GBAAudioMixer* mixer) {
|
|||
CircleBufferRead16(&mixer->activeTracks[track].buffer, &value);
|
||||
sample.right += value;
|
||||
}
|
||||
sample.left = (sample.left * mixer->p->masterVolume) >> 8;
|
||||
sample.right = (sample.right * mixer->p->masterVolume) >> 8;
|
||||
if (mixer->p->externalMixing) {
|
||||
blip_add_delta(mixer->p->psg.left, mixer->p->clock + i * interval, sample.left - mixer->last.left);
|
||||
blip_add_delta(mixer->p->psg.right, mixer->p->clock + i * interval, sample.left - mixer->last.left);
|
||||
blip_add_delta(mixer->p->psg.right, mixer->p->clock + i * interval, sample.right - mixer->last.right);
|
||||
}
|
||||
mixer->last = sample;
|
||||
}
|
||||
|
|
|
@ -192,13 +192,13 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD
|
|||
break;
|
||||
case DIRTY_PALETTE:
|
||||
if (item->address < SIZE_PALETTE_RAM) {
|
||||
logger->palette[item->address >> 1] = item->value;
|
||||
STORE_16LE(item->value, item->address, logger->palette);
|
||||
proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value);
|
||||
}
|
||||
break;
|
||||
case DIRTY_OAM:
|
||||
if (item->address < SIZE_OAM) {
|
||||
logger->oam[item->address] = item->value;
|
||||
STORE_16LE(item->value, item->address << 1, logger->oam);
|
||||
proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -495,15 +495,15 @@ uint8_t GBAHardwareTiltRead(struct GBACartridgeHardware* hw, uint32_t address) {
|
|||
|
||||
// == Game Boy Player
|
||||
|
||||
static const uint16_t _logoPalette[] = {
|
||||
0xFFDF, 0x640C, 0xE40C, 0xE42D, 0x644E, 0xE44E, 0xE46E, 0x68AF,
|
||||
0xE8B0, 0x68D0, 0x68F0, 0x6911, 0xE911, 0x6D32, 0xED32, 0xED73,
|
||||
0x6D93, 0xED94, 0x6DB4, 0xF1D5, 0x71F5, 0xF1F6, 0x7216, 0x7257,
|
||||
0xF657, 0x7678, 0xF678, 0xF699, 0xF6B9, 0x76D9, 0xF6DA, 0x7B1B,
|
||||
0xFB1B, 0xFB3C, 0x7B5C, 0x7B7D, 0xFF7D, 0x7F9D, 0x7FBE, 0x7FFF,
|
||||
0x642D, 0x648E, 0xE88F, 0xE8F1, 0x6D52, 0x6D73, 0xF1B4, 0xF216,
|
||||
0x7237, 0x7698, 0x7AFA, 0xFAFA, 0xFB5C, 0xFFBE, 0x7FDE, 0xFFFF,
|
||||
0xFFFF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
|
||||
static const uint8_t _logoPalette[] = {
|
||||
0xDF, 0xFF, 0x0C, 0x64, 0x0C, 0xE4, 0x2D, 0xE4, 0x4E, 0x64, 0x4E, 0xE4, 0x6E, 0xE4, 0xAF, 0x68,
|
||||
0xB0, 0xE8, 0xD0, 0x68, 0xF0, 0x68, 0x11, 0x69, 0x11, 0xE9, 0x32, 0x6D, 0x32, 0xED, 0x73, 0xED,
|
||||
0x93, 0x6D, 0x94, 0xED, 0xB4, 0x6D, 0xD5, 0xF1, 0xF5, 0x71, 0xF6, 0xF1, 0x16, 0x72, 0x57, 0x72,
|
||||
0x57, 0xF6, 0x78, 0x76, 0x78, 0xF6, 0x99, 0xF6, 0xB9, 0xF6, 0xD9, 0x76, 0xDA, 0xF6, 0x1B, 0x7B,
|
||||
0x1B, 0xFB, 0x3C, 0xFB, 0x5C, 0x7B, 0x7D, 0x7B, 0x7D, 0xFF, 0x9D, 0x7F, 0xBE, 0x7F, 0xFF, 0x7F,
|
||||
0x2D, 0x64, 0x8E, 0x64, 0x8F, 0xE8, 0xF1, 0xE8, 0x52, 0x6D, 0x73, 0x6D, 0xB4, 0xF1, 0x16, 0xF2,
|
||||
0x37, 0x72, 0x98, 0x76, 0xFA, 0x7A, 0xFA, 0xFA, 0x5C, 0xFB, 0xBE, 0xFF, 0xDE, 0x7F, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const uint32_t _logoHash = 0xEEDA6963;
|
||||
|
|
|
@ -429,12 +429,12 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
|
|||
case REG_FIFO_A_LO:
|
||||
case REG_FIFO_B_LO:
|
||||
GBAIOWrite32(gba, address, (gba->memory.io[(address >> 1) + 1] << 16) | value);
|
||||
break;
|
||||
return;
|
||||
|
||||
case REG_FIFO_A_HI:
|
||||
case REG_FIFO_B_HI:
|
||||
GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16));
|
||||
break;
|
||||
return;
|
||||
|
||||
// DMA
|
||||
case REG_DMA0SAD_LO:
|
||||
|
@ -630,7 +630,7 @@ void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {
|
|||
break;
|
||||
case REG_FIFO_A_LO:
|
||||
case REG_FIFO_B_LO:
|
||||
GBAAudioWriteFIFO(&gba->audio, address, value);
|
||||
value = GBAAudioWriteFIFO(&gba->audio, address, value);
|
||||
break;
|
||||
case REG_DMA0SAD_LO:
|
||||
value = GBADMAWriteSAD(gba, 0, value);
|
||||
|
|
|
@ -1636,6 +1636,10 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB
|
|||
int32_t x = (uint32_t) GBAObjAttributesBGetX(sprite->b) << 23;
|
||||
x >>= 23;
|
||||
|
||||
if (GBARegisterDISPCNTGetMode(renderer->dispcnt) >= 3 && GBAObjAttributesCGetTile(sprite->c) < 512) {
|
||||
return;
|
||||
}
|
||||
|
||||
int align = GBAObjAttributesAIs256Color(sprite->a) && !GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt);
|
||||
unsigned charBase = (BASE_TILE >> 1) + (GBAObjAttributesCGetTile(sprite->c) & ~align) * 0x10;
|
||||
int stride = GBARegisterDISPCNTIsObjCharacterMapping(renderer->dispcnt) ? (width >> 3) : (0x20 >> GBAObjAttributesAGet256Color(sprite->a));
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
\
|
||||
uint32_t current = renderer->row[outX]; \
|
||||
MOSAIC(COORD) \
|
||||
if (pixelData) { \
|
||||
if (pixelData && IS_WRITABLE(current)) { \
|
||||
COMPOSITE_256_ ## OBJWIN (BLEND, 0); \
|
||||
} \
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* render
|
|||
if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
|
||||
color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color);
|
||||
} else {
|
||||
color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
|
||||
color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
|
||||
}
|
||||
} else {
|
||||
color = (color & ~FLAG_TARGET_2) | (current & FLAG_OBJWIN);
|
||||
|
@ -56,14 +56,11 @@ static inline void _compositeBlendObjwin(struct GBAVideoSoftwareRenderer* render
|
|||
}
|
||||
|
||||
static inline void _compositeBlendNoObjwin(struct GBAVideoSoftwareRenderer* renderer, int x, uint32_t color, uint32_t current) {
|
||||
if (!IS_WRITABLE(current)) { \
|
||||
return; \
|
||||
} \
|
||||
if (color >= current) {
|
||||
if (current & FLAG_TARGET_1 && color & FLAG_TARGET_2) {
|
||||
color = _mix(renderer->alphaA[x], current, renderer->alphaB[x], color);
|
||||
} else {
|
||||
color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
|
||||
color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
|
||||
}
|
||||
} else {
|
||||
color = color & ~FLAG_TARGET_2;
|
||||
|
@ -77,7 +74,7 @@ static inline void _compositeNoBlendObjwin(struct GBAVideoSoftwareRenderer* rend
|
|||
if (color < current) {
|
||||
color |= (current & FLAG_OBJWIN);
|
||||
} else {
|
||||
color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
|
||||
color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
|
||||
}
|
||||
renderer->row[x] = color;
|
||||
}
|
||||
|
@ -86,15 +83,12 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
uint32_t current) {
|
||||
UNUSED(renderer);
|
||||
if (color >= current) {
|
||||
color = (current & 0x00FFFFFF) | (current & (FLAG_REBLEND | FLAG_OBJWIN));
|
||||
color = current & (0x00FFFFFF | FLAG_REBLEND | FLAG_OBJWIN);
|
||||
}
|
||||
renderer->row[x] = color;
|
||||
}
|
||||
|
||||
#define COMPOSITE_16_OBJWIN(BLEND, IDX) \
|
||||
if (!IS_WRITABLE(current)) { \
|
||||
continue; \
|
||||
} \
|
||||
if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
|
||||
unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \
|
||||
unsigned mergedFlags = flags; \
|
||||
|
@ -108,9 +102,6 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
|
|||
_composite ## BLEND ## NoObjwin(renderer, outX + IDX, palette[pixelData] | flags, current);
|
||||
|
||||
#define COMPOSITE_256_OBJWIN(BLEND, IDX) \
|
||||
if (!IS_WRITABLE(current)) { \
|
||||
continue; \
|
||||
} \
|
||||
if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \
|
||||
unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \
|
||||
unsigned mergedFlags = flags; \
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include <fcntl.h>
|
||||
|
||||
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
|
||||
const uint32_t GBA_SAVESTATE_VERSION = 0x00000003;
|
||||
const uint32_t GBA_SAVESTATE_VERSION = 0x00000004;
|
||||
|
||||
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize");
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -38,7 +38,7 @@ static void* _vf3dMap(struct VFile* vf, size_t size, int flags);
|
|||
static void _vf3dUnmap(struct VFile* vf, void* memory, size_t size);
|
||||
static void _vf3dTruncate(struct VFile* vf, size_t size);
|
||||
static ssize_t _vf3dSize(struct VFile* vf);
|
||||
static bool _vf3dSync(struct VFile* vf, const void* buffer, size_t size);
|
||||
static bool _vf3dSync(struct VFile* vf, void* buffer, size_t size);
|
||||
|
||||
static bool _vd3dClose(struct VDir* vd);
|
||||
static void _vd3dRewind(struct VDir* vd);
|
||||
|
@ -160,14 +160,12 @@ ssize_t _vf3dSize(struct VFile* vf) {
|
|||
return size;
|
||||
}
|
||||
|
||||
static bool _vf3dSync(struct VFile* vf, const void* buffer, size_t size) {
|
||||
static bool _vf3dSync(struct VFile* vf, void* buffer, size_t size) {
|
||||
struct VFile3DS* vf3d = (struct VFile3DS*) vf;
|
||||
if (buffer) {
|
||||
u32 sizeWritten;
|
||||
Result res = FSFILE_Write(vf3d->handle, &sizeWritten, 0, buffer, size, FS_WRITE_FLUSH);
|
||||
if (res) {
|
||||
return false;
|
||||
}
|
||||
return R_SUCCEEDED(res);
|
||||
}
|
||||
FSFILE_Flush(vf3d->handle);
|
||||
return true;
|
||||
|
|
|
@ -7,6 +7,7 @@ find_program(BANNERTOOL bannertool)
|
|||
find_program(MAKEROM makerom)
|
||||
find_program(PICASSO picasso)
|
||||
find_program(RAW2C raw2c)
|
||||
find_program(TEX3DS tex3ds)
|
||||
set(STRIP "${cross_prefix_path}strip" CACHE INTERNAL "symbol stripper")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format" PARENT_SCOPE)
|
||||
|
@ -65,9 +66,15 @@ add_custom_command(OUTPUT ${BINARY_NAME}.bnr
|
|||
COMMAND ${BANNERTOOL} makebanner -ci ${CMAKE_CURRENT_SOURCE_DIR}/banner.cgfx -a ${CMAKE_CURRENT_SOURCE_DIR}/bios.wav -o ${BINARY_NAME}.bnr
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/banner.cgfx ${CMAKE_CURRENT_SOURCE_DIR}/bios.wav)
|
||||
|
||||
# tex3ds binaries as of 2.0.1-3 crash if you try to do this
|
||||
#add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icons.t3x
|
||||
# COMMAND ${TEX3DS} -f rgb5551 -o icons.t3x ${CMAKE_SOURCE_DIR}/res/icons.png
|
||||
# MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/res/icons.png
|
||||
# WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icons.c
|
||||
COMMAND ${RAW2C} ${CMAKE_SOURCE_DIR}/src/platform/3ds/icons.raw
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/src/platform/3ds/icons.raw)
|
||||
COMMAND ${RAW2C} ${CMAKE_CURRENT_SOURCE_DIR}/icons.t3x
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/icons.t3x)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin ${CMAKE_CURRENT_BINARY_DIR}/uishader.shbin.h
|
||||
|
|
|
@ -80,6 +80,10 @@ bool ctrInitGpu(void) {
|
|||
AttrInfo_AddLoader(attrInfo, 2, GPU_UNSIGNED_BYTE, 4); // in_col
|
||||
AttrInfo_AddLoader(attrInfo, 3, GPU_FLOAT, 2); // in_rot
|
||||
|
||||
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
|
||||
BufInfo_Init(bufInfo);
|
||||
BufInfo_Add(bufInfo, ctrVertexBuffer, sizeof(struct ctrUIVertex), 4, 0x3210);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -213,13 +217,8 @@ void ctrStartFrame(void) {
|
|||
ctrNumVerts = 0;
|
||||
ctrVertStart = 0;
|
||||
activeTexture = NULL;
|
||||
|
||||
C3D_BufInfo* bufInfo = C3D_GetBufInfo();
|
||||
BufInfo_Init(bufInfo);
|
||||
BufInfo_Add(bufInfo, ctrVertexBuffer, sizeof(struct ctrUIVertex), 4, 0x3210);
|
||||
}
|
||||
|
||||
void ctrEndFrame(void) {
|
||||
ctrFlushBatch();
|
||||
GSPGPU_FlushDataCache(ctrVertexBuffer, sizeof(struct ctrUIVertex) * ctrNumVerts);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <mgba-util/vfs.h>
|
||||
#include "icons.h"
|
||||
|
||||
#include <tex3ds.h>
|
||||
#include "ctr-gpu.h"
|
||||
|
||||
#define FONT_SIZE 15.6f
|
||||
|
@ -44,12 +45,8 @@ struct GUIFont* GUIFontCreate(void) {
|
|||
tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR) | GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
tex = &guiFont->icons;
|
||||
C3D_TexInitVRAM(tex, 256, 64, GPU_RGBA5551);
|
||||
|
||||
GSPGPU_FlushDataCache(icons, icons_size);
|
||||
GX_RequestDma((u32*) icons, tex->data, icons_size);
|
||||
gspWaitForDMA();
|
||||
Tex3DS_Texture t3x = Tex3DS_TextureImport(icons, icons_size, &guiFont->icons, NULL, true);
|
||||
Tex3DS_TextureFree(t3x);
|
||||
|
||||
return guiFont;
|
||||
}
|
||||
|
@ -136,23 +133,28 @@ void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment
|
|||
y -= metric.height;
|
||||
break;
|
||||
}
|
||||
s16 origin = font->icons.height - metric.y - metric.height;
|
||||
|
||||
switch (orient) {
|
||||
case GUI_ORIENT_HMIRROR:
|
||||
ctrAddRectEx(color, x + metric.width, y,
|
||||
-metric.width, metric.height,
|
||||
metric.x, metric.y,
|
||||
ctrAddRectEx(color, x + metric.width, y + metric.height,
|
||||
-metric.width, -metric.height,
|
||||
metric.x, origin,
|
||||
metric.width, metric.height, 0);
|
||||
break;
|
||||
case GUI_ORIENT_VMIRROR:
|
||||
ctrAddRectEx(color, x, y + metric.height,
|
||||
metric.width, -metric.height,
|
||||
metric.x, metric.y,
|
||||
ctrAddRectEx(color, x, y,
|
||||
metric.width, metric.height,
|
||||
metric.x, origin,
|
||||
metric.width, metric.height, 0);
|
||||
break;
|
||||
case GUI_ORIENT_0:
|
||||
default:
|
||||
// TODO: Rotation
|
||||
ctrAddRect(color, x, y, metric.x, metric.y, metric.width, metric.height);
|
||||
ctrAddRectEx(color, x, y + metric.height,
|
||||
metric.width, -metric.height,
|
||||
metric.x, origin,
|
||||
metric.width, metric.height, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -163,11 +165,10 @@ void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h,
|
|||
if (icon >= GUI_ICON_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct GUIIconMetric metric = defaultIconMetrics[icon];
|
||||
ctrAddRectEx(color, x, y,
|
||||
ctrAddRectEx(color, x, y + (h ? h : metric.height),
|
||||
w ? w : metric.width,
|
||||
h ? h : metric.height,
|
||||
metric.x, metric.y,
|
||||
h ? -h : -metric.height,
|
||||
metric.x, font->icons.height - metric.y - metric.height,
|
||||
metric.width, metric.height, 0);
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -28,6 +28,7 @@ set(OS_LIB -lvita2d -l${M_LIBRARY}
|
|||
-lScePower_stub
|
||||
-lSceSysmodule_stub
|
||||
-lSceTouch_stub)
|
||||
set(OS_LIB ${OS_LIB} PARENT_SCOPE)
|
||||
set(OBJCOPY_CMD ${OBJCOPY} -I binary -O elf32-littlearm -B arm)
|
||||
|
||||
list(APPEND GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/gui-font.c)
|
||||
|
@ -61,5 +62,30 @@ vita_create_vpk(${BINARY_NAME}.vpk MGBA00001 ${BINARY_NAME}.self
|
|||
FILE ${CMAKE_CURRENT_SOURCE_DIR}/startup.png sce_sys/livearea/contents/startup.png
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/template.xml sce_sys/livearea/contents/template.xml)
|
||||
|
||||
if(BUILD_PERF)
|
||||
add_executable(perf-main.elf perf.c)
|
||||
set_target_properties(perf-main.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
target_link_libraries(perf-main.elf ${OS_LIB})
|
||||
|
||||
vita_create_self(perf.bin ../${BINARY_NAME}-perf)
|
||||
vita_create_self(perf-main.self perf-main.elf)
|
||||
|
||||
vita_create_vpk(${BINARY_NAME}-perf.vpk MGBA00002 perf-main.self
|
||||
NAME "${PROJECT_NAME} Perf Tester"
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/perf.bin perf.bin
|
||||
FILE ${CMAKE_CURRENT_SOURCE_DIR}/icon0.png sce_sys/icon0.png
|
||||
FILE ${CMAKE_CURRENT_SOURCE_DIR}/pic0.png sce_sys/pic0.png
|
||||
FILE ${CMAKE_CURRENT_SOURCE_DIR}/bg.png sce_sys/livearea/contents/bg.png
|
||||
FILE ${CMAKE_CURRENT_SOURCE_DIR}/startup.png sce_sys/livearea/contents/startup.png
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/template.xml sce_sys/livearea/contents/template.xml)
|
||||
add_dependencies(${BINARY_NAME}-perf.vpk perf.bin)
|
||||
|
||||
install(FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/perf.bin
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/perf-start.sh
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}-perf.vpk
|
||||
DESTINATION . COMPONENT ${BINARY_NAME}-perf)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${BINARY_NAME}.elf DESTINATION . COMPONENT ${BINARY_NAME}-dbg)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${BINARY_NAME}.vpk DESTINATION . COMPONENT ${BINARY_NAME}-psp2)
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh
|
||||
IP=$1
|
||||
shift
|
||||
|
||||
# Kill any previous session
|
||||
echo | nc $IP 7215 > /dev/null
|
||||
echo | nc $IP 7216 > /dev/null
|
||||
|
||||
curl -sT perf.bin ftp://$IP:1337/ux0:app/MGBA00002/
|
||||
echo launch MGBA00002 | nc $IP 1338 > /dev/null
|
||||
|
||||
sleep 2
|
||||
|
||||
(for ARG in $@; do
|
||||
echo $ARG
|
||||
done; echo) | nc $IP 7215
|
||||
exit 0
|
|
@ -0,0 +1,57 @@
|
|||
/* 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
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba-util/socket.h>
|
||||
|
||||
#include <psp2/appmgr.h>
|
||||
#include <psp2/kernel/processmgr.h>
|
||||
#include <psp2/kernel/threadmgr.h>
|
||||
|
||||
#define MAX_ARGS 19
|
||||
|
||||
|
||||
void connectServer(char* args[]) {
|
||||
Socket server = SocketOpenTCP(7215, NULL);
|
||||
if (SOCKET_FAILED(server)) {
|
||||
return;
|
||||
}
|
||||
if (SOCKET_FAILED(SocketListen(server, 0))) {
|
||||
SocketClose(server);
|
||||
return;
|
||||
}
|
||||
Socket conn = SocketAccept(server, NULL);
|
||||
if (SOCKET_FAILED(conn)) {
|
||||
SocketClose(server);
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
ssize_t len;
|
||||
char arg[1024];
|
||||
while (i < MAX_ARGS && (len = SocketRecv(conn, arg, sizeof(arg) - 1)) > 0) {
|
||||
arg[len] = '\0';
|
||||
char* tok;
|
||||
for (tok = strtok(arg, "\n"); tok && i < MAX_ARGS; ++i) {
|
||||
args[i] = strdup(tok);
|
||||
tok = strtok(NULL, "\n");
|
||||
}
|
||||
if (arg[len - 1] == '\n') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SocketClose(conn);
|
||||
SocketClose(server);
|
||||
}
|
||||
|
||||
int main() {
|
||||
char* args[MAX_ARGS + 1] = { 0 };
|
||||
SocketSubsystemInit();
|
||||
connectServer(args);
|
||||
SocketSubsystemDeinit();
|
||||
sceAppMgrLoadExec("app0:/perf.bin", args, NULL);
|
||||
sceKernelExitProcess(0);
|
||||
return 0;
|
||||
}
|
|
@ -50,7 +50,6 @@ static enum ScreenMode {
|
|||
SM_MAX
|
||||
} screenMode;
|
||||
|
||||
static void* outputBuffer;
|
||||
static int currentTex;
|
||||
static vita2d_texture* tex[2];
|
||||
static vita2d_texture* screenshot;
|
||||
|
@ -493,7 +492,6 @@ void mPSP2Teardown(struct mGUIRunner* runner) {
|
|||
vita2d_free_texture(tex[0]);
|
||||
vita2d_free_texture(tex[1]);
|
||||
vita2d_free_texture(screenshot);
|
||||
mappedMemoryFree(outputBuffer, 256 * 256 * 4);
|
||||
frameLimiter = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,21 +4,11 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba-util/memory.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
#include <psp2/kernel/sysmem.h>
|
||||
#include <psp2/types.h>
|
||||
|
||||
DECLARE_VECTOR(SceUIDList, SceUID);
|
||||
DEFINE_VECTOR(SceUIDList, SceUID);
|
||||
|
||||
static struct SceUIDList uids;
|
||||
static bool listInit = false;
|
||||
|
||||
void* anonymousMemoryMap(size_t size) {
|
||||
if (!listInit) {
|
||||
SceUIDListInit(&uids, 8);
|
||||
}
|
||||
if (size & 0xFFF) {
|
||||
// Align to 4kB pages
|
||||
size += ((~size) & 0xFFF) + 1;
|
||||
|
@ -27,7 +17,6 @@ void* anonymousMemoryMap(size_t size) {
|
|||
if (memblock < 0) {
|
||||
return 0;
|
||||
}
|
||||
*SceUIDListAppend(&uids) = memblock;
|
||||
void* ptr;
|
||||
if (sceKernelGetMemBlockBase(memblock, &ptr) < 0) {
|
||||
return 0;
|
||||
|
@ -36,18 +25,8 @@ void* anonymousMemoryMap(size_t size) {
|
|||
}
|
||||
|
||||
void mappedMemoryFree(void* memory, size_t size) {
|
||||
UNUSED(size);
|
||||
size_t i;
|
||||
for (i = 0; i < SceUIDListSize(&uids); ++i) {
|
||||
SceUID uid = *SceUIDListGetPointer(&uids, i);
|
||||
void* ptr;
|
||||
if (sceKernelGetMemBlockBase(uid, &ptr) < 0) {
|
||||
continue;
|
||||
}
|
||||
if (ptr == memory) {
|
||||
sceKernelFreeMemBlock(uid);
|
||||
SceUIDListShift(&uids, i, 1);
|
||||
return;
|
||||
}
|
||||
SceUID uid = sceKernelFindMemBlockByAddr(memory, size);
|
||||
if (uid >= 0) {
|
||||
sceKernelFreeMemBlock(uid);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ static void* _vfsceMap(struct VFile* vf, size_t size, int flags);
|
|||
static void _vfsceUnmap(struct VFile* vf, void* memory, size_t size);
|
||||
static void _vfsceTruncate(struct VFile* vf, size_t size);
|
||||
static ssize_t _vfsceSize(struct VFile* vf);
|
||||
static bool _vfsceSync(struct VFile* vf, const void* memory, size_t size);
|
||||
static bool _vfsceSync(struct VFile* vf, void* memory, size_t size);
|
||||
|
||||
static bool _vdsceClose(struct VDir* vd);
|
||||
static void _vdsceRewind(struct VDir* vd);
|
||||
|
@ -146,13 +146,14 @@ ssize_t _vfsceSize(struct VFile* vf) {
|
|||
return end;
|
||||
}
|
||||
|
||||
bool _vfsceSync(struct VFile* vf, const void* buffer, size_t size) {
|
||||
bool _vfsceSync(struct VFile* vf, void* buffer, size_t size) {
|
||||
struct VFileSce* vfsce = (struct VFileSce*) vf;
|
||||
if (buffer && size) {
|
||||
SceOff cur = sceIoLseek(vfsce->fd, 0, SEEK_CUR);
|
||||
sceIoLseek(vfsce->fd, 0, SEEK_SET);
|
||||
sceIoWrite(vfsce->fd, buffer, size);
|
||||
int res = sceIoWrite(vfsce->fd, buffer, size);
|
||||
sceIoLseek(vfsce->fd, cur, SEEK_SET);
|
||||
return res == size;
|
||||
}
|
||||
return sceIoSyncByFd(vfsce->fd) >= 0;
|
||||
}
|
||||
|
@ -362,4 +363,4 @@ bool VDirCreate(const char* path) {
|
|||
// TODO: Verify vitasdk explanation of return values
|
||||
sceIoMkdir(path, 0777);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,36 @@ class _ARMRegisters:
|
|||
|
||||
|
||||
class ARMCore:
|
||||
SP = 13
|
||||
LR = 14
|
||||
PC = 15
|
||||
|
||||
def __init__(self, native):
|
||||
self._native = ffi.cast("struct ARMCore*", native)
|
||||
self.gprs = _ARMRegisters(self)
|
||||
self.cpsr = self._native.cpsr
|
||||
self.spsr = self._native.spsr
|
||||
|
||||
@property
|
||||
def sp(self):
|
||||
return self.gprs[self.SP]
|
||||
|
||||
@sp.setter
|
||||
def sp(self, value):
|
||||
self.gprs[self.SP] = value
|
||||
|
||||
@property
|
||||
def lr(self):
|
||||
return self.gprs[self.LR]
|
||||
|
||||
@lr.setter
|
||||
def lr(self, value):
|
||||
self.gprs[self.LR] = value
|
||||
|
||||
@property
|
||||
def pc(self):
|
||||
return self.gprs[self.PC]
|
||||
|
||||
@pc.setter
|
||||
def pc(self, value):
|
||||
self.gprs[self.PC] = value
|
||||
|
|
|
@ -73,8 +73,9 @@ def _vfpSync(vf, buffer, size):
|
|||
if buffer and size:
|
||||
pos = f.tell()
|
||||
f.seek(0, os.SEEK_SET)
|
||||
_vfpWrite(vf, buffer, size)
|
||||
res = _vfpWrite(vf, buffer, size)
|
||||
f.seek(pos, os.SEEK_SET)
|
||||
return res == size
|
||||
f.flush()
|
||||
os.fsync()
|
||||
return True
|
||||
|
@ -114,6 +115,10 @@ class VFile:
|
|||
self._no_gc = _no_gc
|
||||
self._claimed = False
|
||||
|
||||
@staticmethod
|
||||
def fromEmpty():
|
||||
return VFile(lib.VFileMemChunk(ffi.NULL, 0))
|
||||
|
||||
def __del__(self):
|
||||
if not self._claimed:
|
||||
self.close()
|
||||
|
|
|
@ -22,4 +22,4 @@ PYEXPORT void* _vfpMap(struct VFile* vf, size_t size, int flags);
|
|||
PYEXPORT void _vfpUnmap(struct VFile* vf, void* memory, size_t size);
|
||||
PYEXPORT void _vfpTruncate(struct VFile* vf, size_t size);
|
||||
PYEXPORT ssize_t _vfpSize(struct VFile* vf);
|
||||
PYEXPORT bool _vfpSync(struct VFile* vf, const void* buffer, size_t size);
|
||||
PYEXPORT bool _vfpSync(struct VFile* vf, void* buffer, size_t size);
|
||||
|
|
|
@ -42,6 +42,9 @@ BattleChipView::BattleChipView(std::shared_ptr<CoreController> controller, Windo
|
|||
#else
|
||||
int size = QFontMetrics(QFont()).height() / (devicePixelRatio() * 12);
|
||||
#endif
|
||||
if (!size) {
|
||||
size = 1;
|
||||
}
|
||||
m_ui.chipList->setIconSize(m_ui.chipList->iconSize() * size);
|
||||
m_ui.chipList->setGridSize(m_ui.chipList->gridSize() * size);
|
||||
m_model.setScale(size);
|
||||
|
@ -259,4 +262,4 @@ void BattleChipView::updateData() {
|
|||
m_updater = nullptr;
|
||||
});
|
||||
m_updater->downloadUpdate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,16 @@
|
|||
|
||||
#include "CoreController.h"
|
||||
#include "LogController.h"
|
||||
#include "VFileDevice.h"
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#ifdef M_CORE_GBA
|
||||
#include <mgba/gba/core.h>
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
#include <mgba/gb/core.h>
|
||||
#endif
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
@ -27,6 +31,57 @@ void CoreManager::setMultiplayerController(MultiplayerController* multiplayer) {
|
|||
m_multiplayer = multiplayer;
|
||||
}
|
||||
|
||||
QByteArray CoreManager::getExtdata(const QString& filename, mStateExtdataTag extdataType) {
|
||||
VFileDevice vf(filename, QIODevice::ReadOnly);
|
||||
|
||||
if (!vf.isOpen()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
mStateExtdata extdata;
|
||||
mStateExtdataInit(&extdata);
|
||||
|
||||
QByteArray bytes;
|
||||
auto extract = [&bytes, &extdata, &vf, extdataType](mCore* core) -> bool {
|
||||
if (mCoreExtractExtdata(core, vf, &extdata)) {
|
||||
mStateExtdataItem extitem;
|
||||
if (!mStateExtdataGet(&extdata, extdataType, &extitem)) {
|
||||
return false;
|
||||
}
|
||||
if (extitem.size) {
|
||||
bytes = QByteArray::fromRawData(static_cast<const char*>(extitem.data), extitem.size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool done = false;
|
||||
struct mCore* core = nullptr;
|
||||
#ifdef USE_PNG
|
||||
done = extract(nullptr);
|
||||
#endif
|
||||
#ifdef M_CORE_GBA
|
||||
if (!done) {
|
||||
core = GBACoreCreate();
|
||||
core->init(core);
|
||||
done = extract(core);
|
||||
core->deinit(core);
|
||||
}
|
||||
#endif
|
||||
#ifdef M_CORE_GB
|
||||
if (!done) {
|
||||
core = GBCoreCreate();
|
||||
core->init(core);
|
||||
done = extract(core);
|
||||
core->deinit(core);
|
||||
}
|
||||
#endif
|
||||
|
||||
mStateExtdataDeinit(&extdata);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
CoreController* CoreManager::loadGame(const QString& path) {
|
||||
QFileInfo info(path);
|
||||
if (!info.isReadable()) {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include <mgba/core/serialize.h>
|
||||
|
||||
struct mCoreConfig;
|
||||
struct VFile;
|
||||
|
||||
|
@ -25,6 +27,8 @@ public:
|
|||
void setMultiplayerController(MultiplayerController*);
|
||||
void setPreload(bool preload) { m_preload = preload; }
|
||||
|
||||
static QByteArray getExtdata(const QString& filename, mStateExtdataTag extdataType);
|
||||
|
||||
public slots:
|
||||
CoreController* loadGame(const QString& path);
|
||||
CoreController* loadGame(VFile* vf, const QString& path, const QString& base);
|
||||
|
|
|
@ -13,6 +13,97 @@
|
|||
|
||||
using namespace QGBA;
|
||||
|
||||
IntValidator::IntValidator(bool isSigned, QObject* parent)
|
||||
: QValidator(parent)
|
||||
, m_signed(isSigned)
|
||||
{
|
||||
}
|
||||
|
||||
QValidator::State IntValidator::validate(QString& input, int&) const {
|
||||
if (input.isEmpty()) {
|
||||
return QValidator::Intermediate;
|
||||
}
|
||||
if (input.size() == 1 && input[0] == '-') {
|
||||
if (m_signed) {
|
||||
return QValidator::Intermediate;
|
||||
} else {
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
}
|
||||
if (input[0].isSpace()) {
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
if (input[input.size() - 1].isSpace()) {
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
if (input.size() > 1 && input[0] == '0') {
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
qlonglong val = locale().toLongLong(input, &ok);
|
||||
if (!ok) {
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
|
||||
qlonglong hardmax;
|
||||
qlonglong hardmin;
|
||||
qlonglong max;
|
||||
qlonglong min;
|
||||
|
||||
if (m_signed) {
|
||||
switch (m_width) {
|
||||
case 1:
|
||||
hardmax = 999LL;
|
||||
hardmin = -999LL;
|
||||
max = 0x7FLL;
|
||||
min = -0x80LL;
|
||||
break;
|
||||
case 2:
|
||||
hardmax = 99999LL;
|
||||
hardmin = -99999LL;
|
||||
max = 0x7FFFLL;
|
||||
min = -0x8000LL;
|
||||
break;
|
||||
case 4:
|
||||
hardmax = 9999999999LL;
|
||||
hardmin = -9999999999LL;
|
||||
max = 0x7FFFFFFFLL;
|
||||
min = -0x80000000LL;
|
||||
break;
|
||||
default:
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
} else {
|
||||
hardmin = 0;
|
||||
min = 0;
|
||||
|
||||
switch (m_width) {
|
||||
case 1:
|
||||
hardmax = 999LL;
|
||||
max = 0xFFLL;
|
||||
break;
|
||||
case 2:
|
||||
hardmax = 99999LL;
|
||||
max = 0xFFFFLL;
|
||||
break;
|
||||
case 4:
|
||||
hardmax = 9999999999LL;
|
||||
max = 0xFFFFFFFFLL;
|
||||
break;
|
||||
default:
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
}
|
||||
if (val < hardmin || val > hardmax) {
|
||||
return QValidator::Invalid;
|
||||
}
|
||||
if (val < min || val > max) {
|
||||
return QValidator::Intermediate;
|
||||
}
|
||||
return QValidator::Acceptable;
|
||||
}
|
||||
|
||||
MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_controller(controller)
|
||||
|
@ -21,6 +112,9 @@ MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* pare
|
|||
|
||||
m_ui.hexfield->setController(controller);
|
||||
|
||||
m_ui.sintVal->setValidator(&m_sintValidator);
|
||||
m_ui.uintVal->setValidator(&m_uintValidator);
|
||||
|
||||
mCore* core = m_controller->thread()->core;
|
||||
const mCoreMemoryBlock* info;
|
||||
size_t nBlocks = core->listMemoryBlocks(core, &info);
|
||||
|
@ -39,9 +133,21 @@ MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* pare
|
|||
}
|
||||
}
|
||||
|
||||
connect(m_ui.width8, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(1); });
|
||||
connect(m_ui.width16, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(2); });
|
||||
connect(m_ui.width32, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(4); });
|
||||
connect(m_ui.width8, &QAbstractButton::clicked, [this]() {
|
||||
m_ui.hexfield->setAlignment(1);
|
||||
m_sintValidator.setWidth(1);
|
||||
m_uintValidator.setWidth(1);
|
||||
});
|
||||
connect(m_ui.width16, &QAbstractButton::clicked, [this]() {
|
||||
m_ui.hexfield->setAlignment(2);
|
||||
m_sintValidator.setWidth(2);
|
||||
m_uintValidator.setWidth(2);
|
||||
});
|
||||
connect(m_ui.width32, &QAbstractButton::clicked, [this]() {
|
||||
m_ui.hexfield->setAlignment(4);
|
||||
m_sintValidator.setWidth(4);
|
||||
m_uintValidator.setWidth(4);
|
||||
});
|
||||
connect(m_ui.setAddress, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, static_cast<void (MemoryView::*)(uint32_t)>(&MemoryView::jumpToAddress));
|
||||
connect(m_ui.hexfield, &MemoryModel::selectionChanged, this, &MemoryView::updateSelection);
|
||||
|
@ -60,6 +166,41 @@ MemoryView::MemoryView(std::shared_ptr<CoreController> controller, QWidget* pare
|
|||
connect(m_ui.load, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::load);
|
||||
|
||||
connect(m_ui.loadTBL, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::loadTBL);
|
||||
|
||||
connect(m_ui.sintVal, &QLineEdit::returnPressed, this, [this]() {
|
||||
int align = m_ui.hexfield->alignment();
|
||||
mCore* core = m_controller->thread()->core;
|
||||
int32_t value = m_ui.sintVal->text().toInt();
|
||||
switch (align) {
|
||||
case 1:
|
||||
core->rawWrite8(core, m_selection.first, m_ui.segments->value(), value);
|
||||
break;
|
||||
case 2:
|
||||
core->rawWrite16(core, m_selection.first, m_ui.segments->value(), value);
|
||||
break;
|
||||
case 4:
|
||||
core->rawWrite32(core, m_selection.first, m_ui.segments->value(), value);
|
||||
break;
|
||||
}
|
||||
update();
|
||||
});
|
||||
connect(m_ui.uintVal, &QLineEdit::returnPressed, this, [this]() {
|
||||
int align = m_ui.hexfield->alignment();
|
||||
mCore* core = m_controller->thread()->core;
|
||||
uint32_t value = m_ui.uintVal->text().toUInt();
|
||||
switch (align) {
|
||||
case 1:
|
||||
core->rawWrite8(core, m_selection.first, m_ui.segments->value(), value);
|
||||
break;
|
||||
case 2:
|
||||
core->rawWrite16(core, m_selection.first, m_ui.segments->value(), value);
|
||||
break;
|
||||
case 4:
|
||||
core->rawWrite32(core, m_selection.first, m_ui.segments->value(), value);
|
||||
break;
|
||||
}
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
void MemoryView::setIndex(int index) {
|
||||
|
@ -116,7 +257,9 @@ void MemoryView::updateStatus() {
|
|||
|
||||
if (m_selection.first & (align - 1) || m_selection.second - m_selection.first != align) {
|
||||
m_ui.sintVal->clear();
|
||||
m_ui.sintVal->setReadOnly(true);
|
||||
m_ui.uintVal->clear();
|
||||
m_ui.uintVal->setReadOnly(true);
|
||||
return;
|
||||
}
|
||||
union {
|
||||
|
@ -144,6 +287,8 @@ void MemoryView::updateStatus() {
|
|||
m_ui.uintVal->setText(QString::number(value.u32));
|
||||
break;
|
||||
}
|
||||
m_ui.sintVal->setReadOnly(false);
|
||||
m_ui.uintVal->setReadOnly(false);
|
||||
}
|
||||
|
||||
void MemoryView::saveRange() {
|
||||
|
@ -153,4 +298,4 @@ void MemoryView::saveRange() {
|
|||
memdump->setSegment(m_ui.segments->value());
|
||||
memdump->setByteCount(m_selection.second - m_selection.first);
|
||||
memdump->show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#pragma once
|
||||
|
||||
#include <QValidator>
|
||||
|
||||
#include "MemoryModel.h"
|
||||
|
||||
#include "ui_MemoryView.h"
|
||||
|
@ -13,6 +15,20 @@ namespace QGBA {
|
|||
|
||||
class CoreController;
|
||||
|
||||
class IntValidator : public QValidator {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
IntValidator(bool isSigned, QObject* parent = nullptr);
|
||||
|
||||
virtual QValidator::State validate(QString& input, int& pos) const override;
|
||||
void setWidth(int bytes) { m_width = bytes; }
|
||||
|
||||
private:
|
||||
int m_width = 1;
|
||||
bool m_signed;
|
||||
};
|
||||
|
||||
class MemoryView : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -32,6 +48,8 @@ private slots:
|
|||
|
||||
private:
|
||||
Ui::MemoryView m_ui;
|
||||
IntValidator m_sintValidator{true};
|
||||
IntValidator m_uintValidator{false};
|
||||
|
||||
std::shared_ptr<CoreController> m_controller;
|
||||
QPair<uint32_t, uint32_t> m_region;
|
||||
|
|
|
@ -190,6 +190,9 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="uintVal">
|
||||
<property name="maxLength">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
@ -208,6 +211,9 @@
|
|||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="sintVal">
|
||||
<property name="maxLength">
|
||||
<number>11</number>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
@ -304,6 +310,23 @@
|
|||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>regions</tabstop>
|
||||
<tabstop>segments</tabstop>
|
||||
<tabstop>setAddress</tabstop>
|
||||
<tabstop>width8</tabstop>
|
||||
<tabstop>width16</tabstop>
|
||||
<tabstop>width32</tabstop>
|
||||
<tabstop>sintVal</tabstop>
|
||||
<tabstop>uintVal</tabstop>
|
||||
<tabstop>stringVal</tabstop>
|
||||
<tabstop>loadTBL</tabstop>
|
||||
<tabstop>copy</tabstop>
|
||||
<tabstop>paste</tabstop>
|
||||
<tabstop>save</tabstop>
|
||||
<tabstop>saveRange</tabstop>
|
||||
<tabstop>load</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
|
@ -50,6 +50,8 @@ OverrideView::OverrideView(ConfigController* config, QWidget* parent)
|
|||
s_mbcList.append(GB_HuC1);
|
||||
s_mbcList.append(GB_HuC3);
|
||||
s_mbcList.append(GB_UNL_WISDOM_TREE);
|
||||
s_mbcList.append(GB_UNL_BBD);
|
||||
s_mbcList.append(GB_UNL_HITEK);
|
||||
s_mbcList.append(GB_UNL_PKJD);
|
||||
}
|
||||
if (s_gbModelList.isEmpty()) {
|
||||
|
|
|
@ -359,6 +359,16 @@
|
|||
<string>Wisdom Tree (Unlicensed)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>BBD (Unlicensed)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Hitek (Unlicensed)</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Pokémon Jade/Diamond (Unlicensed)</string>
|
||||
|
|
|
@ -19,6 +19,23 @@ VFileDevice::VFileDevice(VFile* vf, QObject* parent)
|
|||
}
|
||||
}
|
||||
|
||||
VFileDevice::VFileDevice(const QString& filename, QIODevice::OpenMode mode, QObject* parent)
|
||||
: QIODevice(parent)
|
||||
{
|
||||
int posixMode = 0;
|
||||
if ((mode & QIODevice::ReadWrite) == QIODevice::ReadWrite) {
|
||||
posixMode = O_RDWR;
|
||||
} else if (mode & QIODevice::ReadOnly) {
|
||||
posixMode = O_RDONLY;
|
||||
} else if (mode & QIODevice::WriteOnly) {
|
||||
posixMode = O_WRONLY;
|
||||
}
|
||||
m_vf = open(filename, posixMode);
|
||||
if (m_vf) {
|
||||
setOpenMode(mode);
|
||||
}
|
||||
}
|
||||
|
||||
void VFileDevice::close() {
|
||||
if (!m_vf) {
|
||||
return;
|
||||
|
|
|
@ -17,6 +17,7 @@ Q_OBJECT
|
|||
|
||||
public:
|
||||
VFileDevice(VFile* vf = nullptr, QObject* parent = nullptr);
|
||||
VFileDevice(const QString&, QIODevice::OpenMode, QObject* parent = nullptr);
|
||||
|
||||
virtual void close() override;
|
||||
virtual bool seek(qint64 pos) override;
|
||||
|
|
|
@ -1518,12 +1518,9 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
ConfigOption* mute = m_config->addOption("mute");
|
||||
mute->addBoolean(tr("Mute"), &m_actions, "av");
|
||||
mute->connect([this](const QVariant& value) {
|
||||
if (value.toInt()) {
|
||||
m_config->setOption("fastForwardMute", true);
|
||||
}
|
||||
m_config->setOption("fastForwardMute", static_cast<bool>(value.toInt()));
|
||||
reloadConfig();
|
||||
}, this);
|
||||
m_config->updateOption("mute");
|
||||
|
||||
m_actions.addMenu(tr("FPS target"),"target", "av");
|
||||
ConfigOption* fpsTargetOption = m_config->addOption("fpsTarget");
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1350,6 +1350,7 @@ void _log(struct mLogger* log, int category, enum mLogLevel level, const char* f
|
|||
int main(int argc, char** argv) {
|
||||
ThreadLocalInitKey(&logStream);
|
||||
ThreadLocalSetKey(logStream, NULL);
|
||||
putenv("TZ=UTC");
|
||||
|
||||
int status = 0;
|
||||
if (!parseCInemaArgs(argc, argv)) {
|
||||
|
|
|
@ -31,6 +31,10 @@ uint32_t* romBuffer;
|
|||
size_t romBufferSize;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef PSP2
|
||||
#include <psp2/kernel/processmgr.h>
|
||||
#include <psp2/power.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -108,6 +112,8 @@ int main(int argc, char** argv) {
|
|||
romBuffer = SYS_GetArena2Lo();
|
||||
SYS_SetArena2Lo((void*)((intptr_t) romBuffer + romBufferSize));
|
||||
#endif
|
||||
#elif defined(PSP2)
|
||||
scePowerSetArmClockFrequency(444);
|
||||
#else
|
||||
signal(SIGINT, _mPerfShutdown);
|
||||
#endif
|
||||
|
@ -177,6 +183,8 @@ int main(int argc, char** argv) {
|
|||
VIDEO_Flush();
|
||||
VIDEO_WaitVSync();
|
||||
VIDEO_WaitVSync();
|
||||
#elif defined(PSP2)
|
||||
sceKernelExitProcess(0);
|
||||
#endif
|
||||
|
||||
return didFail;
|
||||
|
|
|
@ -23,14 +23,24 @@ add_executable(${BINARY_NAME}.elf ${GUI_SRC} main.c)
|
|||
set_target_properties(${BINARY_NAME}.elf PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}")
|
||||
target_link_libraries(${BINARY_NAME}.elf ${BINARY_NAME} ${OS_LIB})
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/font.tpl
|
||||
COMMAND ${GXTEXCONV} -i ${CMAKE_SOURCE_DIR}/res/font2x.png -o font.tpl colfmt=5 mipmap=no
|
||||
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/res/font2x.png
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/font.c
|
||||
COMMAND ${RAW2C} ${CMAKE_SOURCE_DIR}/src/platform/wii/font.tpl
|
||||
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/src/platform/wii/font.tpl
|
||||
COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/font.tpl
|
||||
MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/font.tpl
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icons.tpl
|
||||
COMMAND ${GXTEXCONV} -i ${CMAKE_SOURCE_DIR}/res/icons2x.png -o icons.tpl colfmt=5 mipmap=no
|
||||
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/res/icons2x.png
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/icons.c
|
||||
COMMAND ${RAW2C} ${CMAKE_SOURCE_DIR}/src/platform/wii/icons.tpl
|
||||
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/src/platform/wii/icons.tpl
|
||||
COMMAND ${RAW2C} ${CMAKE_CURRENT_BINARY_DIR}/icons.tpl
|
||||
MAIN_DEPENDENCY ${CMAKE_CURRENT_BINARY_DIR}/icons.tpl
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_custom_target(${BINARY_NAME}.dol ALL
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -152,4 +152,6 @@ const struct GUIIconMetric defaultIconMetrics[] = {
|
|||
[GUI_ICON_BUTTON_TRIANGLE] = { 34, 34, 12, 11 },
|
||||
[GUI_ICON_BUTTON_SQUARE] = { 50, 34, 12, 11 },
|
||||
[GUI_ICON_BUTTON_HOME] = { 66, 34, 12, 11 },
|
||||
[GUI_ICON_STATUS_FAST_FORWARD] = { 2, 50, 12, 12 },
|
||||
[GUI_ICON_STATUS_MUTE] = { 17, 50, 14, 12 },
|
||||
};
|
||||
|
|
|
@ -28,7 +28,9 @@ static inline uint32_t rotl32 ( uint32_t x, int8_t r ) {
|
|||
// handle aligned reads, do the conversion here
|
||||
|
||||
static FORCE_INLINE uint32_t getblock32 ( const uint32_t * p, int i ) {
|
||||
return p[i];
|
||||
uint32_t ret;
|
||||
LOAD_32LE(ret, i << 2, p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
154
src/util/table.c
154
src/util/table.c
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2014 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
|
||||
|
@ -6,18 +6,18 @@
|
|||
#include <mgba-util/table.h>
|
||||
|
||||
#include <mgba-util/hash.h>
|
||||
#include <mgba-util/math.h>
|
||||
#include <mgba-util/string.h>
|
||||
|
||||
#define LIST_INITIAL_SIZE 8
|
||||
#define LIST_INITIAL_SIZE 4
|
||||
#define TABLE_INITIAL_SIZE 8
|
||||
#define REBALANCE_THRESHOLD 4
|
||||
|
||||
#define TABLE_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == key
|
||||
#define HASH_TABLE_STRNCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && strncmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0
|
||||
#define HASH_TABLE_MEMCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && memcmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0
|
||||
#define HASH_TABLE_MEMCMP_COMPARATOR(LIST, INDEX) LIST->list[(INDEX)].key == hash && LIST->list[(INDEX)].keylen == keylen && memcmp(LIST->list[(INDEX)].stringKey, key, LIST->list[(INDEX)].keylen) == 0
|
||||
|
||||
#define TABLE_LOOKUP_START(COMPARATOR, LIST, KEY) \
|
||||
uint32_t entry = (KEY) & (table->tableSize - 1); \
|
||||
LIST = &table->table[entry]; \
|
||||
#define TABLE_LOOKUP_START(COMPARATOR, LIST) \
|
||||
size_t i; \
|
||||
for (i = 0; i < LIST->nEntries; ++i) { \
|
||||
if (COMPARATOR(LIST, i)) { \
|
||||
|
@ -42,6 +42,18 @@ struct TableList {
|
|||
size_t listSize;
|
||||
};
|
||||
|
||||
void HashTableInsertBinaryMoveKey(struct Table* table, void* key, size_t keylen, void* value);
|
||||
|
||||
static inline const struct TableList* _getConstList(const struct Table* table, uint32_t key) {
|
||||
uint32_t entry = key & (table->tableSize - 1);
|
||||
return &table->table[entry];
|
||||
}
|
||||
|
||||
static inline struct TableList* _getList(struct Table* table, uint32_t key) {
|
||||
uint32_t entry = key & (table->tableSize - 1);
|
||||
return &table->table[entry];
|
||||
}
|
||||
|
||||
static struct TableList* _resizeAsNeeded(struct Table* table, struct TableList* list, uint32_t key) {
|
||||
UNUSED(table);
|
||||
UNUSED(key);
|
||||
|
@ -65,14 +77,36 @@ static void _removeItemFromList(struct Table* table, struct TableList* list, siz
|
|||
}
|
||||
}
|
||||
|
||||
void TableInit(struct Table* table, size_t initialSize, void (deinitializer(void*))) {
|
||||
if (initialSize < 2 || (initialSize & (initialSize - 1))) {
|
||||
static void _rebalance(struct Table* table) {
|
||||
struct Table newTable;
|
||||
TableInit(&newTable, table->tableSize * REBALANCE_THRESHOLD, NULL);
|
||||
newTable.seed = table->seed * 134775813 + 1;
|
||||
size_t i;
|
||||
for (i = 0; i < table->tableSize; ++i) {
|
||||
const struct TableList* list = &table->table[i];
|
||||
size_t j;
|
||||
for (j = 0; j < list->nEntries; ++j) {
|
||||
HashTableInsertBinaryMoveKey(&newTable, list->list[j].stringKey, list->list[j].keylen, list->list[j].value);
|
||||
}
|
||||
free(list->list);
|
||||
}
|
||||
free(table->table);
|
||||
table->tableSize = newTable.tableSize;
|
||||
table->table = newTable.table;
|
||||
table->seed = newTable.seed;
|
||||
}
|
||||
|
||||
void TableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*)) {
|
||||
if (initialSize < 2) {
|
||||
initialSize = TABLE_INITIAL_SIZE;
|
||||
} else if (initialSize & (initialSize - 1)) {
|
||||
initialSize = toPow2(initialSize);
|
||||
}
|
||||
table->tableSize = initialSize;
|
||||
table->table = calloc(table->tableSize, sizeof(struct TableList));
|
||||
table->size = 0;
|
||||
table->deinitializer = deinitializer;
|
||||
table->seed = 0;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < table->tableSize; ++i) {
|
||||
|
@ -101,16 +135,16 @@ void TableDeinit(struct Table* table) {
|
|||
}
|
||||
|
||||
void* TableLookup(const struct Table* table, uint32_t key) {
|
||||
const struct TableList* list;
|
||||
TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) {
|
||||
const struct TableList* list = _getConstList(table, key);
|
||||
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
|
||||
return lookupResult->value;
|
||||
} TABLE_LOOKUP_END;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TableInsert(struct Table* table, uint32_t key, void* value) {
|
||||
struct TableList* list;
|
||||
TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) {
|
||||
struct TableList* list = _getList(table, key);
|
||||
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
|
||||
if (value != lookupResult->value) {
|
||||
if (table->deinitializer) {
|
||||
table->deinitializer(lookupResult->value);
|
||||
|
@ -128,8 +162,8 @@ void TableInsert(struct Table* table, uint32_t key, void* value) {
|
|||
}
|
||||
|
||||
void TableRemove(struct Table* table, uint32_t key) {
|
||||
struct TableList* list;
|
||||
TABLE_LOOKUP_START(TABLE_COMPARATOR, list, key) {
|
||||
struct TableList* list = _getList(table, key);
|
||||
TABLE_LOOKUP_START(TABLE_COMPARATOR, list) {
|
||||
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
|
||||
} TABLE_LOOKUP_END;
|
||||
}
|
||||
|
@ -151,7 +185,7 @@ void TableClear(struct Table* table) {
|
|||
}
|
||||
}
|
||||
|
||||
void TableEnumerate(const struct Table* table, void (handler(uint32_t key, void* value, void* user)), void* user) {
|
||||
void TableEnumerate(const struct Table* table, void (*handler)(uint32_t key, void* value, void* user), void* user) {
|
||||
size_t i;
|
||||
for (i = 0; i < table->tableSize; ++i) {
|
||||
const struct TableList* list = &table->table[i];
|
||||
|
@ -166,8 +200,9 @@ size_t TableSize(const struct Table* table) {
|
|||
return table->size;
|
||||
}
|
||||
|
||||
void HashTableInit(struct Table* table, size_t initialSize, void (deinitializer(void*))) {
|
||||
void HashTableInit(struct Table* table, size_t initialSize, void (*deinitializer)(void*)) {
|
||||
TableInit(table, initialSize, deinitializer);
|
||||
table->seed = 1;
|
||||
}
|
||||
|
||||
void HashTableDeinit(struct Table* table) {
|
||||
|
@ -175,27 +210,32 @@ void HashTableDeinit(struct Table* table) {
|
|||
}
|
||||
|
||||
void* HashTableLookup(const struct Table* table, const char* key) {
|
||||
uint32_t hash = hash32(key, strlen(key), 0);
|
||||
const struct TableList* list;
|
||||
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list, hash) {
|
||||
uint32_t hash = hash32(key, strlen(key), table->seed);
|
||||
const struct TableList* list = _getConstList(table, hash);
|
||||
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
|
||||
return lookupResult->value;
|
||||
} TABLE_LOOKUP_END;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* HashTableLookupBinary(const struct Table* table, const void* key, size_t keylen) {
|
||||
uint32_t hash = hash32(key, keylen, 0);
|
||||
const struct TableList* list;
|
||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list, hash) {
|
||||
uint32_t hash = hash32(key, keylen, table->seed);
|
||||
const struct TableList* list = _getConstList(table, hash);
|
||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
||||
return lookupResult->value;
|
||||
} TABLE_LOOKUP_END;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HashTableInsert(struct Table* table, const char* key, void* value) {
|
||||
uint32_t hash = hash32(key, strlen(key), 0);
|
||||
struct TableList* list;
|
||||
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list, hash) {
|
||||
uint32_t hash = hash32(key, strlen(key), table->seed);
|
||||
struct TableList* list = _getList(table, hash);
|
||||
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
||||
_rebalance(table);
|
||||
hash = hash32(key, strlen(key), table->seed);
|
||||
list = _getList(table, hash);
|
||||
}
|
||||
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
|
||||
if (value != lookupResult->value) {
|
||||
if (table->deinitializer) {
|
||||
table->deinitializer(lookupResult->value);
|
||||
|
@ -214,9 +254,14 @@ void HashTableInsert(struct Table* table, const char* key, void* value) {
|
|||
}
|
||||
|
||||
void HashTableInsertBinary(struct Table* table, const void* key, size_t keylen, void* value) {
|
||||
uint32_t hash = hash32(key, keylen, 0);
|
||||
struct TableList* list;
|
||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list, hash) {
|
||||
uint32_t hash = hash32(key, keylen, table->seed);
|
||||
struct TableList* list = _getList(table, hash);
|
||||
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
||||
_rebalance(table);
|
||||
hash = hash32(key, keylen, table->seed);
|
||||
list = _getList(table, hash);
|
||||
}
|
||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
||||
if (value != lookupResult->value) {
|
||||
if (table->deinitializer) {
|
||||
table->deinitializer(lookupResult->value);
|
||||
|
@ -235,18 +280,44 @@ void HashTableInsertBinary(struct Table* table, const void* key, size_t keylen,
|
|||
++table->size;
|
||||
}
|
||||
|
||||
void HashTableInsertBinaryMoveKey(struct Table* table, void* key, size_t keylen, void* value) {
|
||||
uint32_t hash = hash32(key, keylen, table->seed);
|
||||
struct TableList* list = _getList(table, hash);
|
||||
if (table->size >= table->tableSize * REBALANCE_THRESHOLD) {
|
||||
_rebalance(table);
|
||||
hash = hash32(key, keylen, table->seed);
|
||||
list = _getList(table, hash);
|
||||
}
|
||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
||||
if (value != lookupResult->value) {
|
||||
if (table->deinitializer) {
|
||||
table->deinitializer(lookupResult->value);
|
||||
}
|
||||
lookupResult->value = value;
|
||||
}
|
||||
return;
|
||||
} TABLE_LOOKUP_END;
|
||||
list = _resizeAsNeeded(table, list, hash);
|
||||
list->list[list->nEntries].key = hash;
|
||||
list->list[list->nEntries].stringKey = key;
|
||||
list->list[list->nEntries].keylen = keylen;
|
||||
list->list[list->nEntries].value = value;
|
||||
++list->nEntries;
|
||||
++table->size;
|
||||
}
|
||||
|
||||
void HashTableRemove(struct Table* table, const char* key) {
|
||||
uint32_t hash = hash32(key, strlen(key), 0);
|
||||
struct TableList* list;
|
||||
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list, hash) {
|
||||
uint32_t hash = hash32(key, strlen(key), table->seed);
|
||||
struct TableList* list = _getList(table, hash);
|
||||
TABLE_LOOKUP_START(HASH_TABLE_STRNCMP_COMPARATOR, list) {
|
||||
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
|
||||
} TABLE_LOOKUP_END;
|
||||
}
|
||||
|
||||
void HashTableRemoveBinary(struct Table* table, const void* key, size_t keylen) {
|
||||
uint32_t hash = hash32(key, keylen, 0);
|
||||
struct TableList* list;
|
||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list, hash) {
|
||||
uint32_t hash = hash32(key, keylen, table->seed);
|
||||
struct TableList* list = _getList(table, hash);
|
||||
TABLE_LOOKUP_START(HASH_TABLE_MEMCMP_COMPARATOR, list) {
|
||||
_removeItemFromList(table, list, i); // TODO: Move i out of the macro
|
||||
} TABLE_LOOKUP_END;
|
||||
}
|
||||
|
@ -269,7 +340,7 @@ void HashTableClear(struct Table* table) {
|
|||
}
|
||||
}
|
||||
|
||||
void HashTableEnumerate(const struct Table* table, void (handler(const char* key, void* value, void* user)), void* user) {
|
||||
void HashTableEnumerate(const struct Table* table, void (*handler)(const char* key, void* value, void* user), void* user) {
|
||||
size_t i;
|
||||
for (i = 0; i < table->tableSize; ++i) {
|
||||
const struct TableList* list = &table->table[i];
|
||||
|
@ -280,7 +351,18 @@ void HashTableEnumerate(const struct Table* table, void (handler(const char* key
|
|||
}
|
||||
}
|
||||
|
||||
const char* HashTableSearch(const struct Table* table, bool (predicate(const char* key, const void* value, const void* user)), const void* user) {
|
||||
void HashTableEnumerateBinary(const struct Table* table, void (*handler)(const char* key, size_t keylen, void* value, void* user), void* user) {
|
||||
size_t i;
|
||||
for (i = 0; i < table->tableSize; ++i) {
|
||||
const struct TableList* list = &table->table[i];
|
||||
size_t j;
|
||||
for (j = 0; j < list->nEntries; ++j) {
|
||||
handler(list->list[j].stringKey, list->list[j].keylen, list->list[j].value, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* HashTableSearch(const struct Table* table, bool (*predicate)(const char* key, const void* value, const void* user), const void* user) {
|
||||
size_t i;
|
||||
const char* result = NULL;
|
||||
for (i = 0; i < table->tableSize; ++i) {
|
||||
|
|
|
@ -42,7 +42,7 @@ static void* _vfdMap(struct VFile* vf, size_t size, int flags);
|
|||
static void _vfdUnmap(struct VFile* vf, void* memory, size_t size);
|
||||
static void _vfdTruncate(struct VFile* vf, size_t size);
|
||||
static ssize_t _vfdSize(struct VFile* vf);
|
||||
static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size);
|
||||
static bool _vfdSync(struct VFile* vf, void* buffer, size_t size);
|
||||
|
||||
struct VFile* VFileOpenFD(const char* path, int flags) {
|
||||
if (!path) {
|
||||
|
@ -195,7 +195,7 @@ static ssize_t _vfdSize(struct VFile* vf) {
|
|||
return stat.st_size;
|
||||
}
|
||||
|
||||
static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) {
|
||||
static bool _vfdSync(struct VFile* vf, void* buffer, size_t size) {
|
||||
UNUSED(buffer);
|
||||
UNUSED(size);
|
||||
struct VFileFD* vfd = (struct VFileFD*) vf;
|
||||
|
@ -206,7 +206,7 @@ static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) {
|
|||
futimes(vfd->fd, NULL);
|
||||
#endif
|
||||
if (buffer && size) {
|
||||
return msync(buffer, size, MS_SYNC) == 0;
|
||||
return msync(buffer, size, MS_ASYNC) == 0;
|
||||
}
|
||||
return fsync(vfd->fd) == 0;
|
||||
#else
|
||||
|
@ -217,7 +217,7 @@ static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) {
|
|||
SystemTimeToFileTime(&st, &ft);
|
||||
SetFileTime(h, NULL, &ft, &ft);
|
||||
if (buffer && size) {
|
||||
FlushViewOfFile(buffer, size);
|
||||
return FlushViewOfFile(buffer, size);
|
||||
}
|
||||
return FlushFileBuffers(h);
|
||||
#endif
|
||||
|
|
|
@ -19,7 +19,7 @@ static void* _vffMap(struct VFile* vf, size_t size, int flags);
|
|||
static void _vffUnmap(struct VFile* vf, void* memory, size_t size);
|
||||
static void _vffTruncate(struct VFile* vf, size_t size);
|
||||
static ssize_t _vffSize(struct VFile* vf);
|
||||
static bool _vffSync(struct VFile* vf, const void* buffer, size_t size);
|
||||
static bool _vffSync(struct VFile* vf, void* buffer, size_t size);
|
||||
|
||||
struct VFile* VFileFIFO(struct CircleBuffer* backing) {
|
||||
if (!backing) {
|
||||
|
@ -94,7 +94,7 @@ static ssize_t _vffSize(struct VFile* vf) {
|
|||
return CircleBufferSize(vff->backing);
|
||||
}
|
||||
|
||||
static bool _vffSync(struct VFile* vf, const void* buffer, size_t size) {
|
||||
static bool _vffSync(struct VFile* vf, void* buffer, size_t size) {
|
||||
UNUSED(vf);
|
||||
UNUSED(buffer);
|
||||
UNUSED(size);
|
||||
|
|
|
@ -24,7 +24,7 @@ static void* _vffMap(struct VFile* vf, size_t size, int flags);
|
|||
static void _vffUnmap(struct VFile* vf, void* memory, size_t size);
|
||||
static void _vffTruncate(struct VFile* vf, size_t size);
|
||||
static ssize_t _vffSize(struct VFile* vf);
|
||||
static bool _vffSync(struct VFile* vf, const void* buffer, size_t size);
|
||||
static bool _vffSync(struct VFile* vf, void* buffer, size_t size);
|
||||
|
||||
struct VFile* VFileFOpen(const char* path, const char* mode) {
|
||||
if (!path && !mode) {
|
||||
|
@ -144,13 +144,14 @@ static ssize_t _vffSize(struct VFile* vf) {
|
|||
return size;
|
||||
}
|
||||
|
||||
static bool _vffSync(struct VFile* vf, const void* buffer, size_t size) {
|
||||
static bool _vffSync(struct VFile* vf, void* buffer, size_t size) {
|
||||
struct VFileFILE* vff = (struct VFileFILE*) vf;
|
||||
if (buffer && size) {
|
||||
long pos = ftell(vff->file);
|
||||
fseek(vff->file, 0, SEEK_SET);
|
||||
fwrite(buffer, size, 1, vff->file);
|
||||
size_t res = fwrite(buffer, size, 1, vff->file);
|
||||
fseek(vff->file, pos, SEEK_SET);
|
||||
return res == 1;
|
||||
}
|
||||
return fflush(vff->file) == 0;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ static void* _vf7zMap(struct VFile* vf, size_t size, int flags);
|
|||
static void _vf7zUnmap(struct VFile* vf, void* memory, size_t size);
|
||||
static void _vf7zTruncate(struct VFile* vf, size_t size);
|
||||
static ssize_t _vf7zSize(struct VFile* vf);
|
||||
static bool _vf7zSync(struct VFile* vf, const void* buffer, size_t size);
|
||||
static bool _vf7zSync(struct VFile* vf, void* buffer, size_t size);
|
||||
|
||||
static bool _vd7zClose(struct VDir* vd);
|
||||
static void _vd7zRewind(struct VDir* vd);
|
||||
|
@ -327,7 +327,7 @@ bool _vd7zDeleteFile(struct VDir* vd, const char* path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool _vf7zSync(struct VFile* vf, const void* memory, size_t size) {
|
||||
bool _vf7zSync(struct VFile* vf, void* memory, size_t size) {
|
||||
UNUSED(vf);
|
||||
UNUSED(memory);
|
||||
UNUSED(size);
|
||||
|
|
|
@ -28,7 +28,7 @@ static void _vfmUnmap(struct VFile* vf, void* memory, size_t size);
|
|||
static void _vfmTruncate(struct VFile* vf, size_t size);
|
||||
static void _vfmTruncateNoop(struct VFile* vf, size_t size);
|
||||
static ssize_t _vfmSize(struct VFile* vf);
|
||||
static bool _vfmSync(struct VFile* vf, const void* buffer, size_t size);
|
||||
static bool _vfmSync(struct VFile* vf, void* buffer, size_t size);
|
||||
|
||||
struct VFile* VFileFromMemory(void* mem, size_t size) {
|
||||
if (!mem || !size) {
|
||||
|
@ -297,7 +297,7 @@ ssize_t _vfmSize(struct VFile* vf) {
|
|||
return vfm->size;
|
||||
}
|
||||
|
||||
bool _vfmSync(struct VFile* vf, const void* buffer, size_t size) {
|
||||
bool _vfmSync(struct VFile* vf, void* buffer, size_t size) {
|
||||
UNUSED(vf);
|
||||
UNUSED(buffer);
|
||||
UNUSED(size);
|
||||
|
|
|
@ -74,7 +74,7 @@ static void* _vfzMap(struct VFile* vf, size_t size, int flags);
|
|||
static void _vfzUnmap(struct VFile* vf, void* memory, size_t size);
|
||||
static void _vfzTruncate(struct VFile* vf, size_t size);
|
||||
static ssize_t _vfzSize(struct VFile* vf);
|
||||
static bool _vfzSync(struct VFile* vf, const void* buffer, size_t size);
|
||||
static bool _vfzSync(struct VFile* vf, void* buffer, size_t size);
|
||||
|
||||
static bool _vdzClose(struct VDir* vd);
|
||||
static void _vdzRewind(struct VDir* vd);
|
||||
|
@ -426,7 +426,7 @@ bool _vdzDeleteFile(struct VDir* vd, const char* path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
|
||||
bool _vfzSync(struct VFile* vf, void* memory, size_t size) {
|
||||
UNUSED(vf);
|
||||
UNUSED(memory);
|
||||
UNUSED(size);
|
||||
|
@ -654,7 +654,7 @@ bool _vdzDeleteFile(struct VDir* vd, const char* path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool _vfzSync(struct VFile* vf, const void* memory, size_t size) {
|
||||
bool _vfzSync(struct VFile* vf, void* memory, size_t size) {
|
||||
UNUSED(vf);
|
||||
UNUSED(memory);
|
||||
UNUSED(size);
|
||||
|
|
|
@ -72,18 +72,20 @@ class GameClockTest(PerfTest):
|
|||
class PerfServer(object):
|
||||
ITERATIONS_PER_INSTANCE = 50
|
||||
|
||||
def __init__(self, address, command=None):
|
||||
def __init__(self, address, root='/', command=None):
|
||||
s = address.rsplit(':', 1)
|
||||
if len(s) == 1:
|
||||
self.address = (s[0], 7216)
|
||||
else:
|
||||
self.address = (s[0], s[1])
|
||||
self.command = None
|
||||
if command:
|
||||
self.command = shlex.split(command)
|
||||
self.iterations = self.ITERATIONS_PER_INSTANCE
|
||||
self.socket = None
|
||||
self.results = []
|
||||
self.reader = None
|
||||
self.root = root
|
||||
|
||||
def _start(self, test):
|
||||
if self.command:
|
||||
|
@ -108,7 +110,7 @@ class PerfServer(object):
|
|||
def run(self, test):
|
||||
if not self.socket:
|
||||
self._start(test)
|
||||
self.socket.send(os.path.join("/perfroms", test.rom).encode("utf-8"))
|
||||
self.socket.send(os.path.join(self.root, test.rom).encode("utf-8"))
|
||||
self.results.append(next(self.reader))
|
||||
self.iterations -= 1
|
||||
if self.iterations == 0:
|
||||
|
@ -178,6 +180,7 @@ if __name__ == '__main__':
|
|||
parser.add_argument('-s', '--server', metavar='ADDRESS', help='run on server')
|
||||
parser.add_argument('-S', '--server-command', metavar='COMMAND', help='command to launch server')
|
||||
parser.add_argument('-o', '--out', metavar='FILE', help='output file path')
|
||||
parser.add_argument('-r', '--root', metavar='PATH', type=str, default='/perfroms', help='root path for server mode')
|
||||
parser.add_argument('directory', help='directory containing ROM files')
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@ -189,9 +192,9 @@ if __name__ == '__main__':
|
|||
s = Suite(args.directory, wall=args.wall_time, game=args.game_frames, renderer=renderer)
|
||||
if args.server:
|
||||
if args.server_command:
|
||||
server = PerfServer(args.server, args.server_command)
|
||||
server = PerfServer(args.server, args.root, args.server_command)
|
||||
else:
|
||||
server = PerfServer(args.server)
|
||||
server = PerfServer(args.server, args.root)
|
||||
s.set_server(server)
|
||||
s.collect_tests()
|
||||
results = s.run()
|
||||
|
|
Loading…
Reference in New Issue