Compare commits

...

97 Commits

Author SHA1 Message Date
Rafael Kitover
fd7003a35f build: lower Intel macOS req to 10.10 from 10.15
Convert the C++17 code in the bundled SFML code to C++14 to allow for
targeting macOS 10.10 (Yosemite) instead of 10.15 (Catalina.)

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-30 18:17:17 +00:00
Rafael Kitover
4cf6cccbaf build: bundle SFML 3.x sys/network in third_party
Remove the SFML external dependency, include the SFML 3.0.1 system and
network libraries in third_party and adjust the build code, tools and
documentation accordingly.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-24 16:34:42 +00:00
Rafael Kitover
9e724ea4b8 build: support building ARM64 binary for Mac
Make some adjustments to the Mac build scripts to support building an
ARM64 binary for Apple Silicon.

Also support cross-compiling for Intel on Apple Silicon.

Also prefer using Ninja for CMake ports.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-22 05:55:19 +00:00
Rafael Kitover
eb26ec2ef6 Enable Hide Status Bar in UI Settings for macOS
Enable Hide Status Bar in UI Settings for macOS, it was previously
missing because the whole UI Settings submenu was removed on macOS.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-22 05:29:51 +00:00
Zach Bacon
6a9eeeaece core.sh Switched to github source for nasm
Nasm.us is down currently and unable to pull the tarball
down from that source.

Signed-off-by: Zach Bacon <zachbacon@vba-m.com>
2025-04-21 00:00:19 -04:00
Squall Leonhart
9f6285842d initialize sound_buffer_len_ to silence a warning. 2025-04-16 02:20:53 +10:00
Rafael Kitover
e5ad7b3403 build: find_package(nanosvg) w/ vcpkg
When using vcpkg wxWidgets, call `find_package(nanosvg)` so that
`NanoSVG::` link target is available.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-12 22:47:55 +00:00
Squall Leonhart
94752f8314 [FAudio]Correct the behavior of device_changed check
Fixes the behavior to take the timeout into account properly, fixing the throttled behavior so that it matches (or strives to) the configured value instead of being stuck at <90%
2025-04-13 03:58:40 +10:00
Rafael Kitover
3c32bad886 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-12 08:00:19 +00:00
Rafael Kitover
8936feaa71 build: add libtiff to Nix derivation
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-11 22:43:18 +00:00
Rafael Kitover
f93a32ab0b build: add libtiff to brews on macOS
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-11 21:52:08 +00:00
Rafael Kitover
3fa850c15d build: capitalize ARCH_NAME in CMake
Use capitalized `ARM` instead of `arm` for ARCH_NAME in
Architecture.cmake so that the Mac Apple Silicon zip releases are
suffixed `ARM64` not `arm64`.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-07 01:43:00 -07:00
Rafael Kitover
364776b833 build: s/-Ofast/-O3 -ffast-math/
Use `-O3 -ffast-math` instead of `-Ofast`, which is deprecated by the
latest clang. In both our options and for libretro.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-07 01:28:45 -07:00
Rafael Kitover
2d439f7ba6 build: fix link error on macOS
Add `tiff zstd deflate` to link libs for macOS to fix a link error
caused by libzstd and libdeflate not being linked for libtiff which is
linked by wxWidgets.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-07 01:24:02 -07:00
Rafael Kitover
11a6068a03 build: update vcpkg install command to new form
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-06 12:21:47 -07:00
Rafael Kitover
6139428207 translations: remove dup Chinese translations
Remove zh.po, which is not on Transifex anymore and zh-Hans.po, which I
have removed from Transifex just now, leaving zh-CN.po, as suggested by
@wuweiran.

Fix #1435.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-02 13:45:14 +00:00
Rafael Kitover
5a5579d27f build: minor followup on SFML 3.x API change
Followup on 29e85e5d (build: update Link SFML usage to SFML 3.x APIs,
2025-04-01) to alias an optional.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-02 09:07:54 +00:00
Rafael Kitover
29e85e5d87 build: update Link SFML usage to SFML 3.x APIs
Make some changes for the SFML Network library for the 3.x APIs.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-04-01 23:34:58 +00:00
Fabrice de Gans
e228394656 [Input] Process key down event for some controls
In #1424, the app event handler was disabling all key down events,
preventing controls that depend on them to properly handle these. This
was done to work around an issue on macOS where unhandled keyboard
events would fire an audio alert.
Since this breaks text controls, these changes check for the currently
focused window and let the event propagate for text controls.

Fixes #1434
2025-04-01 18:57:26 +02:00
Rafael Kitover
d5ac2a853b Revert "Update Link SFML usage to SFML 3.x APIs"
This reverts commit e82ae7bb13.

Sorry, somehow I pushed to the wrong branch.
2025-03-29 19:40:59 +00:00
Rafael Kitover
e82ae7bb13 Update Link SFML usage to SFML 3.x APIs
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-03-29 18:54:53 +00:00
Rafael Kitover
20b82d8ab7 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-03-29 04:00:24 +00:00
Rafael Kitover
8d26b4f49f translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-03-25 23:00:22 +00:00
Fabrice de Gans
3fdc30f7d7 Mark wxEVT_KEY_DOWN as processed in wxvbamApp (#1424)
This prevents wxWidgets from triggering the alert sound on macOS.
There should be no side effect from this.

Fixes #1384
2025-03-22 16:24:14 -07:00
Rafael Kitover
57211ddc77 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-03-20 19:00:21 +00:00
Squall Leonhart
2de7efd9eb Reallocate GBA ROM to the the new size(#1422)
The GBA ROM was erroneously reallocated to the constant `ROM_SIZE`.

Closes #1421
2025-03-15 13:39:57 -07:00
Rafael Kitover
5b4559f4ee translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-03-07 13:00:21 +00:00
Rafael Kitover
6178526cd9 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-03-06 09:00:20 +00:00
Rafael Kitover
c8a1d8eb2e translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-02-28 23:00:26 +00:00
Rafael Kitover
6e3883937b translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-02-28 22:00:21 +00:00
Rafael Kitover
8dd647914a translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-02-20 17:00:21 +00:00
Rafael Kitover
0dae126a29 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-02-20 16:00:22 +00:00
Rafael Kitover
5038cd7ba5 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-02-16 23:00:22 +00:00
Rafael Kitover
ddd8fca044 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-02-11 19:00:21 +00:00
Rafael Kitover
25c85a83d9 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-02-11 18:00:21 +00:00
Fabrice de Gans
32dd410954 [build] Disable link (#1411)
SFML 2 no longer compiles with a modern compiler and we are blocked on
SFML 3 being available in vcpkg to fix it. There is no easy way to
backport fixes to SFML 2 so disable link for the time being.
2025-02-08 15:13:10 -08:00
Fabrice de Gans
e4f17d33ce [Sound] Set volume on game startup (#1410)
On startup, the internal emulator sound volume is set to 100%. The user
value in the wx frontend is not used until the emulator volume slider is
modified, resulting in games starting with volume at 100% on startup.

This fixes #1407 by always setting the internal emulator sound volume on
game startup.
2025-02-08 12:57:12 -08:00
Rafael Kitover
2ab707c0ca build: update default.nix
Update default.nix to remove gcc from the macOS package set and add
clang/llvm/libcxx to the Linux package set.

Change 'epoxy' in the Linux package set to the new package name
'libepoxy'.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-02-02 09:42:05 +00:00
Rafael Kitover
4dba928765 build: update installdeps for OpenSUSE
Update `installdeps` for current OpenSUSE wxWidgets and FFmpeg packages.

Disable 32 bit cross builds in OpenSUSE because those packages are no
longer available.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-29 20:21:32 +00:00
Rafael Kitover
3d6550d161 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-25 18:00:22 +00:00
Rafael Kitover
3cf12f2594 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-15 15:00:25 +00:00
Rafael Kitover
de1ec01c78 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-13 18:00:26 +00:00
Rafael Kitover
295abb9cd0 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-13 15:00:26 +00:00
Rafael Kitover
8260b62482 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-13 13:00:22 +00:00
Rafael Kitover
d18c6f115f Use faster sqrt() for xBRZ for XP builds
Use the `sqrt()` introduced in 615e5863 (Add fast-inverse SSE1 sqrt()
from Quake 3 Arena, 2025-01-12) or the xBRZ filters for Windows XP 32
bit builds.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-12 15:50:29 +00:00
Squall Leonhart
d36f80b5e6 Add fast-inverse SSE1 sqrt() from Quake 3 Arena
Add the Quake 3 Arena fast-inverse `sqrt()` using SSE1 intrinsics to
third_party.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-12 15:50:02 +00:00
Rafael Kitover
44aa859e6d build: add cmake var and cpp macro for XP builds
Add the WINXP CMake variable and WINXP CPP macro for MinGW 32 bit
Windows XP builds.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-12 15:04:14 +00:00
Rafael Kitover
5a2d3a6f95 build: set arch to pentium3 not -mmx for XP builds
Set the gcc architecture to pentium3 not pentium-mmx for Windows XP 32
bit builds.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-12 10:04:07 +00:00
PunkPangolin
d6b939c428 Set developer id/component type in metainfo.xml
Add developer block to `metainfo.xml` with id 'visualboyadvance-m.org'.

Also set component type to 'desktop-application'.

This file is used to describe the application on Linux.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-09 20:07:45 +00:00
Rafael Kitover
8b8be7d4a8 Revert "Change domain to temporary"
This reverts commit 72364fd1a4.
2025-01-08 13:12:40 +00:00
Rafael Kitover
72364fd1a4 Change domain to temporary
Domain has expired, change links to temporary domain.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2025-01-08 13:09:49 +00:00
Rafael Kitover
c6da07feb3 build: link avrt for OpenAL on WIN32
For static builds for Windows, link `avrt`, a system library that is now
required by OpenAL.

Also remove our version of `FindOpenAL.cmake` in favor of the
system-provided version.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-12-16 13:47:48 +00:00
Rafael Kitover
94979eff97 build: initialize GTest submodule in CMake
Try to initialize the GTest Git submodule from the CMake code if it has
not been.

If that fails, turn off `BUILD_TESTING`.

We do not want to require recursive clones or initializing submodules
for users by default.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-12-16 13:09:02 +00:00
Rafael Kitover
e8494b56d1 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-12-13 05:08:59 +00:00
Fabrice de Gans
c85397518b [win32-deps] Update win32-deps (#1375) 2024-11-24 22:03:05 -08:00
Fabrice de Gans
cf2339822d [win32-deps] Use specific commit in submodule (#1374)
This will help make older builds reproducible.
2024-11-24 21:37:11 -08:00
Fabrice de Gans
dcb9ccca90 [gtest] Use googletest as a submodule (#1373)
Some Linux distributions use a hermetic build environment to build. This
resulted in issues with `FetchContent` attempting to download googletest
at configure time.
This fixes the issue by integrating googletest as a git submodule
instead of downloading it at configure time.
2024-11-24 12:39:10 -08:00
Fabrice de Gans
a8ec85d536 [GB] Add support for per-game overrides (#1370)
* [GB] Add support for per-game overrides

Alleyway is a buggy game that does not work properly when a Game Boy
Printer is connected. In order to work around this issue, this adds
upport for built-in per-software Game Boy overrides. In addition, this
renames various variables to make their meaning clearer.

* This only supports built-in overrides. External INI files are not
  supported.
* Only the Game Boy Printer option is supported, this only takes effect
  on game startup and the general configuration option is restored when
  the game is unloaded.
* As a result, it is possible to override the per-game override by
  manually setting the option while the game is running, this is working
  as intended.
* Future refactors of the option handling will manage overrides better.
* Switch `gbPrinterEnabled` default to off.

Fixes #1368
2024-11-24 12:15:26 -08:00
Rafael Kitover
611f3a3409 translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-11-04 02:00:27 +00:00
Rafael Kitover
b6da6c490c translations: transifex pull
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-11-04 01:00:23 +00:00
Fabrice de Gans
9d20ce9b59 [Input] Reset keyboard tracking on focus loss (#1357)
When pressing Alt+Tab, the "Alt" and "Tab" keys were considered in the
"pressed" state until the user pressed them again because the window is
no longer receiving keyboard events. This resulted in some shortcuts no
longer working, since "Alt" was always in the pressed state. This
changes the keyboard tracking to be reset when the application loses
focus, fixing the issue.

This change also adds tests for the keyboard tracking.
2024-10-08 18:22:17 -07:00
Squall Leonhart
709a322337 cherry pick d5d01a8b68 2024-10-07 00:26:31 +11:00
Rafael Kitover
cdfd37fc4e build: rename dependencies submodule -> win32-deps
Rename the Git submodule `dependencies` used for some dependent files
for Win32 builds to `win32-deps` and make the necessary adjustments to
the CMake code.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-10-04 20:05:52 +00:00
Rafael Kitover
4f8d0a8867 build: update harfbuzz for macOS builder
Update harfbuzz in the macOS builder from the ancient version to the
current release and switch it to using Meson.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-10-03 13:50:16 +00:00
Fabrice de Gans
b22e9fb709 Ignore illegal opcodes used by Wii U VC (#1351)
The Wii U VC emulator incorrectly allows for armv6 thumb instructions
(like LSRS). These cause an undefined instruction error on real hardware
but the Wii U VC emulator just ignores them. This change mimicks the Wii
U VC emulator behavior.
2024-09-29 23:13:11 -07:00
Rafael Kitover
4e2799b582 build: don't hardcode Windows certificate password
Use a file for the Windows code-signing certificate instead of
hardcoding it into the CMake code.

Update the developer manual with instructions on where to put the
Windows code-signing certificate and the file containing the password
for it.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-29 20:59:40 +00:00
Fabrice de Gans
7fa90531e6 Disable open menu tracking outside of Windows (#1350)
Tracking whether or not the menus are opened is necessary on Windows since menus stop
the main loop. This is not necessary on other platforms. In particular, on Mac, we do
not get a `wxEVT_MENU_CLOSE` event when opening a dialog from a shortcut, resulting in
the menu status tracking being incorrect.

Fixes #1348
2024-09-29 13:59:19 -07:00
Fabrice de Gans
e1c2ecc584 [dialogs] Display all controls in AccelConfig on Mac (#1349)
Calling `ExpandAll()` on the `wxTreeCtrl` would cause it to display outside of its
intended view, hiding other controls. Instead, this sets a minimum size for the tree
control, so the default window size is reasonable.

Fixes #1348
2024-09-28 10:38:28 -07:00
Rafael Kitover
4c450ab360 build: fix linking FAudio statically on MINGW
Link `FAudio.a` explicitly on MINGW toolchains, for some reason linking
`FAudio` with static preference no longer works.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-19 09:22:38 +00:00
Fabrice de Gans
d5e1a1f36b [GB] Properly set OPRI on startup
Previously, the OPRI register was always set to be in CGB mode when not
using the CGB BIOS, resulting in graphics corruption when running DMG
software in CGB mode without using the CGB BIOS. This fixes the issue by
properly setting bit 1 of the OPRI register as expected.

This was broken in #1119.
2024-09-17 18:52:35 -07:00
Rafael Kitover
821b9176bd build: do not use debug libs for RelWithDebInfo
Stop trying to use debug libs for `CMAKE_BUILD_TYPE=RelWithDebInfo`,
this also fixes the current link error with SFML.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-17 18:35:39 +00:00
Rafael Kitover
9031103c9a build: fix linking non-debug pcre for debug wx
Remove the cmake code that adds the pcre library because the wxWidgets
cmake code correctly includes it now, on both vcpkg and MSYS2.

Also fix up `wxWidgets_ROOT_DIR` and `wxWidgets_LIB_DIR` for debug
builds.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-15 19:53:27 +00:00
Rafael Kitover
557f897ead build: fix using host pkgconf for ARM64 cross bld
Set `VCPKG_HOST_TRIPLET` and `VCPKG_USE_HOST_TOOLS` when using an X64
host for an ARM64 cross build in order to use the host `pkgconf` and
possibly other tools.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-15 15:47:28 +00:00
Rafael Kitover
c8a4f66cf8 build: fix UpdateAppcast.cmake
Hardcode `git` instead of using `${GIT_EXECUTABLE}` which we are not
using anymore.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-15 12:50:00 +00:00
Zach Bacon
ddc93ec6e1 release v2.1.11
Signed-off-by: Zach Bacon <zachbacon@vba-m.com>
2024-09-15 01:48:38 -04:00
Rafael Kitover
ab38ae8f24 doc: add keychain/notarization for Mac in dev man
Add information on unlocking the keychain for codesigning and setting up
credentials for notarization to the macOS binary section in the release
process section of the developer manual.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-14 08:38:02 +00:00
Rafael Kitover
d337688fa7 build: use ccache correctly in the macOS builder
Make compiler symlinks in the macOS builder instead of prepending
`ccache` to the compiler in the CC/CXX environment variables.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-14 07:53:34 +00:00
Rafael Kitover
3eea90afb6 build: set BUILD_TESTING=OFF when not git checkout
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-13 16:29:25 +00:00
Rafael Kitover
b3952d74a8 build: fix ENABLE_LIRC=ON
Fix the `ENABLE_LIRC` cmake option, pass an interface specifier to
`target_compile_definitions()`.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-13 15:29:16 +00:00
Fabrice de Gans
be09125d52 Use relative paths for installation
The CMake documentation [1] advises against using `CMAKE_INSTALL_FULL_*`
in `install()` commands.

[1] https://cmake.org/cmake/help/v3.30/module/GNUInstallDirs.html#result-variables
2024-09-13 01:05:38 +00:00
Rafael Kitover
f264e7f807 Fix Help -> Translations URL
Fix the URL a browser is launched with when the `Help -> Translations`
menu item is selected to point to the project page on Transifex.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-12 17:35:40 +00:00
Rafael Kitover
fe0791762a build: update Windows dependencies submodule
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-12 14:17:16 +00:00
Fabrice de Gans
61f427dec1 Write shortcuts in the proper section (#1335)
Shortcuts were mistakenly written in `Keyboard/Keyboard` rather than in
the `Keyboard` section.
In addition, this properly fixes toggling checkable menu item options
via a shortcut.

Fixes #1334
2024-09-11 22:48:06 -07:00
Rafael Kitover
d619ee2bb1 build: fix installing GoogleTest
Add the `EXCLUDE_FROM_ALL` keyword to the `FetchContent_Declare` call
for GoogleTest, so that gtest/gmock are not installed with the project.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 14:43:34 +00:00
Rafael Kitover
7128e6dd08 build: update wxWidgets for macOS builder to 3.2.6
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 12:40:27 +00:00
Rafael Kitover
0decffea8d build: update macOS builder dists
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 11:42:26 +00:00
Rafael Kitover
9b60e17746 build: default to ENABLE_FFMPEG=OFF for MinGW32
Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 11:20:02 +00:00
Rafael Kitover
26207038c9 Update WinSparkle to 0.8.1 and add ARM64
Update the included WinSparkle dll to 0.8.1 and add support for using
the ARM64 WinSparkle dll as well.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 11:07:19 +00:00
Rafael Kitover
90867bc802 doc: update developer manual release steps
Update the release steps in the developer manual to mention the
requirement for a GnuPG key, emphasize the editing the changelog step
and add instructions for putting `signtool.exe` in the `PATH` for the 32
bit Windows binary.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 10:10:13 +00:00
Rafael Kitover
7f49cd33ad build: rm FAudio from installdeps MINGW32 target
Do not try to install FAudio for the MINGW32 32 bit Windows MSYS2 target
in `installdeps`. We do not use it for the 32 bit build and there is no
package for it for MINGW32.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 09:23:27 +00:00
Rafael Kitover
d8342d3d67 doc: add ENABLE_FAUDIO to CMake opts in README.md
Describe `ENABLE_FAUDIO` in the table of CMake options in `README.md`.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 08:59:26 +00:00
Rafael Kitover
12e840a8fa Remove non-user-facing changes from CHANGELOG.md
Remove non-user-facing changes from `CHANGELOG.md` for 2.1.10.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 08:52:12 +00:00
Zach Bacon
6ca609ac7e release v2.1.10
Signed-off-by: Zach Bacon <zachbacon@vba-m.com>
2024-09-08 22:55:37 -04:00
Fabrice de Gans
2b8f9f71ff Copy string options rather than referencing them
This can lead to dandling references, though this is very unlikely. This
commit also removes a useless check.
2024-09-09 02:29:11 +00:00
Fabrice de Gans
e76cef79d2 Remove cast for UTF-16 -> UTF-8 conversion
Add convenience functions to call `config::utf16_to_utf8()` without a
cast.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-09 02:29:11 +00:00
Fabrice de Gans
7f06428df8 Disable dialog position save/restore on wxGTK (#1331)
This is currently broken on Wayland, resulting in dialogs showing up in
random locations.
2024-09-08 16:56:30 -07:00
Rafael Kitover
e4a9340409 Update README.md and Developer Manual
Update README.md and remove outdated information.

Update the Developer Manual and describe the current release process.

Signed-off-by: Rafael Kitover <rkitover@gmail.com>
2024-09-08 05:42:34 +00:00
166 changed files with 23774 additions and 9422 deletions

10
.gitmodules vendored
View File

@@ -1,4 +1,6 @@
[submodule "dependencies"]
path = dependencies
url = https://github.com/visualboyadvance-m/dependencies.git
branch = master
[submodule "win32-deps"]
path = win32-deps
url = https://github.com/visualboyadvance-m/win32-deps.git
[submodule "third_party/googletest"]
path = third_party/googletest
url = https://github.com/google/googletest.git

View File

@@ -4,6 +4,68 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [2.1.11] - 2024-09-15
==========================
* 3eea90af - build: set BUILD_TESTING=OFF when not git checkout [rkitover]
* b3952d74 - build: fix ENABLE_LIRC=ON [rkitover]
* f264e7f8 - Fix Help -> Translations URL [rkitover]
* 61f427de - Write shortcuts in the proper section (#1335) [Steelskin]
* d619ee2b - build: fix installing GoogleTest [rkitover]
* 26207038 - Update WinSparkle to 0.8.1 and add ARM64 [rkitover]
## [2.1.10] - 2024-09-08
==========================
* 7f06428d - Disable dialog position save/restore on wxGTK (#1331) [Steelskin]
* e4ef4aa6 - Propagate key events (#1323) [Steelskin]
* e2cf6ecb - Add option to mute sound during speedup [rkitover]
* d516683a - build: fix for wx using GTK2 [rkitover]
* 834c7de8 - build: update macOS builder dists [rkitover]
* fc82e062 - build: do not build SDL bin on Windows or macOS [rkitover]
* d543784a - [UserInput] Filter key events globally [steelskin]
* 902c6c8e - [UserInput] Only process shortcut commands once [steelskin]
* b7765092 - [bindings] Set default shortcut for recent file 3 [steelskin]
* cc65ef28 - doc: add system requirements to README.md [danialhorton]
* 32627f6b - [Dialogs] Save and restore dialog positions [steelskin]
* 41952d06 - build: update macOS linker tool to 1.5 [rkitover]
* 0c39a5ba - build: override FindGettext to not update po files [rkitover]
* 1b77d659 - build: update macOS build to ffmpeg 7.0 [rkitover]
* 8d08223d - build: fix compatibility with older ffmpeg [rkitover]
* af6028a9 - build: fix build for nix on macOS [rkitover]
* b52edf52 - build: fix building on macOS with Homebrew [rkitover]
* 6766b9ca - build: fix ffmpeg 7.x compat [rkitover]
* 795f25bb - build: fix nix deps for OpenGL [rkitover]
* 647be137 - gba: set cpsr=spsr when switching to FIQ mode [40356555+Aikku93]
* 8abe3e79 - build: remove -lgcc from static link flags [rkitover]
* f4835674 - [Audio] Rework audio devices enumeration [steelskin]
* 775a571f - build: fix detecting Visual Studio default vcpkg [rkitover]
* 64abd3e8 - [Audio] Remove manual memory allocations [steelskin]
* 047ad277 - [Dialogs] Prevent viewers from causing a crash [steelskin]
* 045c98d8 - build: only use -Werror=lto-type-mismatch on gcc [rkitover]
* 3518dc6a - build: fix LTO on Linux [rkitover]
* cc9a03ce - Add toggle: SDL GameController mode for joysticks [rkitover]
* 8576733c - [Build] Remove lingering references to OpenAl [steelskin]
* 05561922 - build: fix MSYS2 check [rkitover]
* d9432ebb - build: fix build on MINGW{64,32}/UCRT64 on MSYS2 [rkitover]
* f57cad67 - build: fix static linking on MSYS2 CLANG64 [rkitover]
* 23e15734 - build: set wxWidgets_DIR with vcpkg [rkitover]
* 98b51910 - [Build] Remove ENABLE_NLS, fix TRANSLATIONS_ONLY [steelskin]
* a565cea8 - [Build] Remove the OpenGL check [steelskin]
* f96e42fe - build: cmake refactor and improvements [Steelskin]
* 07e49025 - Fix most remaining release warnings (#1243) [Steelskin]
* 18b97b43 - Fix various build warnings (#1242) [Steelskin]
* 13a16eb7 - Fix various warnings in filters and headers (#1241) [Steelskin]
* f46da1c5 - build: remove our version of FindSDL2.cmake [rkitover]
* 404e9a1a - build: add clang to ./installdeps for MSYS2 [rkitover]
* 613bd403 - Make menu more reasonably organized (#1230) [wwrustc]
* 215e3c5a - build: use find_program() to find powershell [rkitover]
* e5aa685f - build: don't use wx utils as UNIX cmds on Windows [rkitover]
* 9e4c8e17 - build: fix gentoo dependency namespaces [68k]
* 5f853b99 - Update metainfo.xml to new standards [jhonny.oliveira]
* e7d135db - Update links to new domain visualboyadvance-m.org [rkitover]
* 60fc096f - build: add libglu-devel for solus in installdeps [rkitover]
## [2.1.9] - 2024-02-03
=======================
* 84b0a3e3 - Remove SDL sound driver [rkitover]

View File

@@ -25,7 +25,6 @@ endif()
set(VCPKG_DEPS pkgconf zlib pthreads "sdl2[samplerate]" gettext wxwidgets)
set(VCPKG_DEPS_OPTIONAL
sfml ENABLE_LINK
ffmpeg ENABLE_FFMPEG
faudio ENABLE_FAUDIO
)
@@ -52,7 +51,7 @@ if(GIT_FOUND AND WIN32)
# Win32 deps submodule
set(SUBMODULE_MANUAL_UPDATE FALSE)
if(EXISTS "${CMAKE_SOURCE_DIR}/.git" AND NOT EXISTS "${CMAKE_SOURCE_DIR}/dependencies/mingw-xaudio/include")
if(EXISTS "${CMAKE_SOURCE_DIR}/.git" AND NOT EXISTS "${CMAKE_SOURCE_DIR}/win32-deps/mingw-xaudio/include")
set(SUBMODULE_MANUAL_UPDATE TRUE)
execute_process(
COMMAND "${GIT_EXECUTABLE}" submodule update --init --remote --recursive
@@ -61,7 +60,7 @@ if(GIT_FOUND AND WIN32)
)
endif()
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/dependencies/mingw-xaudio/include")
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/win32-deps/mingw-xaudio/include")
if(NOT (SUBMODULE_MANUAL_UPDATE AND SUBMODULE_UPDATE_STATUS EQUAL 0))
message(FATAL_ERROR "Please pull in git submodules, e.g.\nrun: git submodule update --init --remote --recursive")
endif()
@@ -83,16 +82,29 @@ include(Options)
include(Toolchain)
include(Dependencies)
# Disable tests when not in a git checkout.
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/.git")
set(BUILD_TESTING OFF)
endif()
# Configure gtest
if(BUILD_TESTING)
FetchContent_Declare(googletest
URL https://github.com/google/googletest/archive/2d16ed055d09c3689d44b272adc097393de948a0.zip
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
include(GoogleTest)
if(NOT EXISTS third_party/googletest/CMakeLists.txt)
execute_process(
COMMAND git submodule update --init --recursive -- third_party/googletest
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
endif()
if(EXISTS third_party/googletest/CMakeLists.txt)
add_subdirectory(./third_party/googletest)
include(GoogleTest)
else()
set(BUILD_TESTING OFF)
endif()
endif()
if(NOT CMAKE_PREFIX_PATH AND (NOT ("$ENV{CMAKE_PREFIX_PATH}" STREQUAL "")))
@@ -105,11 +117,6 @@ elseif(NOT CMAKE_BUILD_TYPE MATCHES "^(Release|Debug|RelWithDebInfo|MinSizeRel)$
message(FATAL_ERROR "Invalid CMAKE_BUILD_TYPE: '${CMAKE_BUILD_TYPE}', must be one of: 'Release', 'Debug', 'RelWithDebInfo' or 'MinSizeRel'")
endif()
# Link debug libs for RelWithDebInfo
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO "Debug")
endif()
set(MSYS OFF)
if(NOT "$ENV{MSYSTEM_PREFIX}" STREQUAL "")
set(MSYS ON)
@@ -175,8 +182,18 @@ endif()
set(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale)
if(NOT TRANSLATIONS_ONLY)
add_subdirectory(third_party/include/nonstd)
include_directories(third_party/include/nonstd)
include_directories(third_party/include/ghc)
add_subdirectory(third_party/include/stb)
if(ENABLE_LINK)
include_directories(third_party/sfml/include)
add_subdirectory(third_party/sfml/src/SFML/System EXCLUDE_FROM_ALL)
add_subdirectory(third_party/sfml/src/SFML/Network EXCLUDE_FROM_ALL)
set(SFML_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/third_party/sfml/include)
set(SFML_LIBRARIES sfml-system sfml-network)
endif()
add_subdirectory(src/core)
add_subdirectory(src/components)
add_subdirectory(src/sdl)
@@ -190,5 +207,5 @@ set(CPACK_SOURCE_GENERATOR "TGZ")
set(CPACK_PACKAGE_VERSION_MAJOR "2")
set(CPACK_PACKAGE_VERSION_MINOR "0")
set(CPACK_PACKAGE_VERSION_PATCH "0-Git-${COMMITHASH}")
list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/dependencies")
list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/win32-deps")
include(CPack)

View File

@@ -9,17 +9,15 @@
- [Commit Message](#commit-message)
- [Collaboration on a Branch](#collaboration-on-a-branch)
- [Commits from Maintainers](#commits-from-maintainers)
- [Strings, Character Sets and Translations](#strings-character-sets-and-translations)
- [Pulling Updated Translations](#pulling-updated-translations)
- [Translations Message Catalog](#translations-message-catalog)
- [Interaction with non-wxWidgets Code](#interaction-with-non-wxwidgets-code)
- [Windows Native Development Environment Setup](#windows-native-development-environment-setup)
- [Miscellaneous](#miscellaneous)
- [Debug Messages](#debug-messages)
- [Release Process](#release-process)
- [Environment](#environment)
- [Certificates](#certificates)
- [Release Commit and Tag](#release-commit-and-tag)
- [64-bit Windows Binary](#64-bit-windows-binary)
- [32-bit Windows Binary](#32-bit-windows-binary)
- [64-bit Mac Binary](#64-bit-mac-binary)
- [ARM64 Windows Binary](#arm64-windows-binary)
- [macOS Binary](#macos-binary)
- [Final steps](#final-steps)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -53,11 +51,12 @@ Follow the following steps to process newly submitted issues:
- An issue is resolved by closing it in github. A commit that fixes the issue
should have the following line near the end of the body of the commit message:
```
- Fix #999.
Fix #999.
```
This will automatically close the issue and assign the closing commit in the
github metadata when it is merged to master. The issue can be reopened if
needed.
- A commit that is work towards resolving an issue, should have the issue number
preceded by a pound sign either at the end of a commit message title, if it is
of primary relevance to the issue, or the body otherwise.
@@ -70,7 +69,7 @@ Follow these guidelines always:
https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
the description of your work should be in the **commit message NOT the pull
, the description of your work should be in the **commit message NOT the pull
request description**.
The title line must be no more than 50 characters and the description must be
@@ -95,16 +94,21 @@ other projects, fewer is better.
The commit title line should be prefixed with an area, unless it involves the
wxWidgets GUI app, in which case it should **NOT** have a prefix.
If the commit is a user-facing functionality change or enhancement, the title
line of the commit must be a non-technical description of this change. For
example "Mute on speedup" because this will go into the changelog.
The text after the area prefix should not be capitalized.
Please use one of these area prefixes otherwise:
Please use one of these area prefixes for non-main-GUI-app commits:
- doc: documentation, README.md etc.
- build: cmake, installdeps, preprocessor compatibility defines, etc.
- gb-core: the GameBoy emulator core
- gba-core: the GameBoy Advance emulator core
- build: cmake, installdeps, preprocessor compatibility defines, compatibility
headers, etc.
- gb: the GameBoy emulator core
- gba: the GameBoy Advance emulator core
- libretro: the libretro core glue and build
- sdl-app: anything for the SDL app
- sdl: anything for the SDL port
- translations: anything related to translations
. Add other areas here if needed.
@@ -138,137 +142,79 @@ things in mind:
better to edit the history than to add more commits. Never add commits fixing
previous commits, only improving or adding to them.
- To update when someone else (very rudely you might say) did a `push -f`, `pull
--rebase` will **USUALLY** work. Verify the log, and if necessary do this
instead:
- To update when someone else updated the branch with a `push -f`
```bash
git status # should be clean, with your work having been already pushed
git fetch --all --prune
git reset --hard origin/<branch-name>
```
.
While actively working on a branch, keep it rebased on top of master.
- While actively working on a branch, keep it rebased on top of master.
#### Commits from Maintainers
Maintainers have the power to commit directly to master. This power must be
used responsibly, something I fail to do myself often, and will try to improve
upon.
Maintainers and project members have the power to commit directly to master.
This power must be used responsibly.
Make your most earnest attempt to follow these general guidelines to keep our
Make your best attempt to follow these general guidelines to keep our
history clean:
- Things that are a straight fix or improvement that does not require discussion
- Things that are a minor fix or improvement that does not require discussion
can be committed directly, keeping the following guidelines in mind.
- Bigger new features, code refactors and changes in architecture should go
through the PR process.
- Push code changes to a branch first, so they can run through the CI.
Differences in what different compilers allow is a problem that comes up
**VERY** frequently. As well as incompatibilities between different
configurations for both the C++ code and any supporting code.
- Push code changes to a branch first, so they can run through the CI. When you
open the commit in GitHub there is a little icon in the upper left corner that
shows the CI status for this commit. Differences in what different compilers
allow is a problem that comes up **VERY** frequently. As well as
incompatibilities between different configurations for both the C++ code and
any supporting code.
### Miscellaneous
### Strings, Character Sets and Translations
#### Debug Messages
#### Pulling Updated Translations
We have an override for `wxLogDebug()` to make it work even in non-debug builds
of wx and on windows, even in mintty.
Once in a while it is necessary to pull new and updated translations from
transifex.
For this you need the transifex client, available for Windows as well from
chocolatey as `transifex-client`.
To pull translations run:
```bash
tx pull -af
```
then check `git status` and if any message catalogs were updated, commit the
result as:
```bash
git commit -a --signoff -S -m'Transifex pull.'
git push
```
#### Translations Message Catalog
Strings that need to be translated by our wonderful translators on transifex
(thank you guys very much) need to be enclosed in `_("...")`, for example:
It works like `printf()`, e.g.:
```cpp
wxLogError(_("error: something very wrong"));
int foo = 42;
wxLogDebug(wxT("the value of foo = %d"), foo);
```
The next time you run cmake after adding a string to be translated, the `.pot`
message catalog source will be regenerated, and you will see a loud message
telling you to push to transifex.
Strings in the XRC XML GUI definition files are automatically added to the
message catalog as well.
If you are working on a branch or a PR, don't push to transifex until it has
been merged to master.
Once it is, push it with:
```bash
tx push -s
```
#### Interaction with non-wxWidgets Code
Use our `UTF8(...)` function to force any `wxString` to UTF-8 for use by other
libraries, screen output or OS APIs. For example:
From the core etc. the usual:
```cpp
fprintf(STDERR, "Error: %s\n", UTF8(err_msg));
fprintf(stderr, "...", ...);
```
, will work fine.
There is one exception to this, when using `wxString::Printf()` and such, you
can't pass another `wxString` to the `%s` format directly, use something like
this:
```cpp
wxString err;
err.Printf("Cannot read file: %s", fname.wc_str());
```
this uses the `wchar_t` UTF-16 representation on Windows and does nothing
elsewhere.
For calling Windows APIs with strings, use the wide char `W` variants and the
`wc_str()` method as well.
### Windows Native Development Environment Setup
This guide has been moved to:
https://github.com/rkitover/windows-dev-guide
You need a debug build for this to work or to even have a console on Windows.
Pass `-DCMAKE_BUILD_TYPE=Debug` to cmake.
### Release Process
#### Environment
#### GnuPG Key
The variable `VBAM_NO_PAUSE`, if set, will cause cmake to not pause before gpg
signing operations, you want to set this if you've disabled your gpg passphrase
to not require interaction during release builds.
You will need to create a GnuPG key for signing your commits and release tags,
and upload it to a keyserver.
gpg set up with your key is helpful for the release process on all environments
where a binary is built, but you can also make the detached signature files
yourself at the end of the process.
Make sure to install GnuPG on all environments where you will be making commits
and tags.
For codesigning windows binaries, put your certificate into
`~/.codesign/windows_comodo.pkcs12`.
#### Certificates
On Mac the 'Developer ID Application' certificate stored in your login keychain
is used, `keychain unlock` will prompt you for your login keychain password, to
avoid that set the `LOGIN_KEYCHAIN_PASSWORD` environment variable to your
password.
Make sure you have set up a Windows code signing certificate with the right
password and a Mac 'Developer ID Application' certificate.
Put the Windows certificate into `~/.codesign/windows_comodo.pkcs12` as a PKCS12
file that is password protected, and put the password for it into
`~/.codesign/windows_comodo.pkcs12.password`.
#### Release Commit and Tag
@@ -280,8 +226,10 @@ tag:
mkdir build && cd build
cmake .. -DTAG_RELEASE=TRUE
```
, follow the instructions to edit the `CHANGELOG.md` and then push the release:
then push the release:
To reiterate, **make sure you edit the `CHANGELOG.md`** to remove any
non-user-facing changes before you make the release commit.
```bash
git push
@@ -296,84 +244,126 @@ cmake .. -DTAG_RELEASE=UNDO
#### 64-bit Windows Binary
For this you will preferably need the powershell environment setup described
earlier, however you can use a regular Visual Studio 64 bit native developer
command prompt as well.
For this you will preferably need the PowerShell environment setup described
[here](https://github.com/rkitover/windows-dev-guide), or by starting the `x64
Native Tools Command Prompt` from your Start Menu.
```powershell
mkdir build
cd build
cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_BUILD_TYPE=Release -DUPSTREAM_RELEASE=TRUE -G Ninja
mkdir build-msvc64
cd build-msvc64
cmake .. -DCMAKE_BUILD_TYPE=Release -DUPSTREAM_RELEASE=TRUE -G Ninja
ninja
```
Collect the following files for the release:
- `visualboyadvance-m-Win-64bit.zip`
- `visualboyadvance-m-Win-64bit.zip.asc`
- `visualboyadvance-m-Win-x86_64.zip`
- `translations.zip`
- `translations.zip.asc`
Repeat the process for the debug build, with `-DCMAKE_BUILD_TYPE=Debug` and
collect this file:
- `visualboyadvance-m-Win-x86_64-debug.zip`
.
#### 32-bit Windows Binary
For this the optimal environment is a linux distribution with the mingw
toolchain, I use fedora.
The 32-bit build is a legacy build for Windows XP compatibility. You will need
the MinGW toolchain to build it. The easiest method is to use the MINGW32 MSYS2
environment.
You can set up a shell on a fedora distribution with docker as described here:
Make sure the Visual Studio `signtool.exe` is in your path, you can start MSYS2
with an inherited `PATH` from a Visual Studio enabled environment or add it to
your shell configuration.
https://gist.github.com/rkitover/6379764c619c10e829e4b2fa0ae243fd
If using fedora, the cross script will install all necessary dependencies, if
not install the base toolchain (mingw gcc, binutils, winpthreads) using the
preferred method for your distribution, you can also use mxe for this.
https://mxe.cc/
First install dependencies with:
```bash
sh tools/win/linux-cross-builder -32
./installdeps
```
You can also use msys2 on Windows, this is not recommended:
. Then build the 32-bit binary as follows:
```bash
sh tools/win/msys2-builder -32
mkdir build-mingw32
cd build-mingw32
cmake .. -DCMAKE_BUILD_TYPE=Release -DUPSTREAM_RELEASE=TRUE -G Ninja
ninja
```
. Collect this file for the release:
To set up msys2, see this guide:
- `visualboyadvance-m-Win-x86_32.zip`
https://gist.github.com/rkitover/d008324309044fc0cc742bdb16064454
. Then repeat the process for the debug build with `-DCMAKE_BUILD_TYPE=Debug`,
and collect this file:
Collect the following files from `~/vbam-build-mingw32/project` if using linux,
or `~/vbam-build-msys2-x86_64/project` if using msys2:
- `visualboyadvance-m-Win-x86_32-debug.zip`
.
- `visualboyadvance-m-Win-32bit.zip`
- `visualboyadvance-m-Win-32bit.zip.asc`
#### ARM64 Windows Binary
#### 64-bit Mac Binary
You will need the MSVC ARM64 cross toolchain to build this binary, if you used
the install script from [here](https://github.com/rkitover/windows-dev-guide)
you will have it installed, otherwise run Visual Studio Installer and install
the component.
To enter the ARM64 cross environment, edit the PowerShell profile described
[here](https://github.com/rkitover/windows-dev-guide) or use the `vcvarsall.bat`
script with the `amd64_arm64` argument as described
[here](https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170).
From there the process is the same as for the 64-bit build, collect the
following files for the release:
- `visualboyadvance-m-Win-arm64.zip`
- 'visualboyadvance-m-Win-arm64-debug.zip'
.
#### macOS Binary
Install the latest Xcode for your OS.
You will need bash and (optionally) gpg from homebrew (which you will also need
to install):
```bash
brew install bash gnupg
```
You will need bash from Homebrew/nix/MacPorts/whatever to run the build script.
You will need a codesigning certificate from Apple, which you will be able to
generate once you join their developer program. This is the certificate of the
type 'Developer ID Application' stored in your login keychain. `keychain
unlock` will prompt you for your login keychain password, to avoid that set the
`LOGIN_KEYCHAIN_PASSWORD` environment variable to your password.
generate once you join their developer program from XCode. This is the
certificate of the type 'Developer ID Application' stored in your login
keychain.
If you are not using a GUI session, you will need to use a method to unlock your
login keychain before building so that your codesigning certificate can be used.
Adding the certificate and key to the System keychain is also a method that some
people use.
To unlock your keychain on login, you can add something like this to your
`~/.zshrc`:
```bash
/usr/local/bin/bash tools/osx/builder -64
security unlock-keychain -p "$(cat ~/.login-keychain-password)" login.keychain
```
, with your login password in that file.
For notarization to work, you will need to create an app-specific password on
https://appleid.apple.com , get your Team ID from your Apple Developer account,
and store them with this command:
```bash
xcrun notarytool store-credentials AC_PASSWORD \
--apple-id you@domain.com \
--team-id <DeveloperTeamID> \
--password <secret_app_specific_2FA_password>
```
. Once all of this is set up, run:
```bash
tools/osx/builder
```
, this will take a while because it builds all of the dependencies.
Collect the following files from `~/vbam-build-mac-64bit/project`:
- `visualboyadvance-m-Mac-64bit.zip`
- `visualboyadvance-m-Mac-64bit.zip.asc`
- `visualboyadvance-m-Mac-x86_64.zip`
- `visualboyadvance-m-Mac-x86_64-debug.zip`
.
#### Final steps
@@ -381,27 +371,26 @@ Go to the github releases tab, and make a release for the tag you pushed
earlier.
Put any notes to users and distro maintainers into the description as well as
the entries from `CHANGELOG.md` generated earlier from git by the release
commit script.
the generated entries from `CHANGELOG.md` you edited earlier.
Upload all files collected during the earlier builds, the complete list is:
- `translations.zip`
- `translations.zip.asc`
- `visualboyadvance-m-Mac-64bit.zip`
- `visualboyadvance-m-Mac-64bit.zip.asc`
- `visualboyadvance-m-Win-32bit.zip`
- `visualboyadvance-m-Win-32bit.zip.asc`
- `visualboyadvance-m-Win-64bit.zip`
- `visualboyadvance-m-Win-64bit.zip.asc`
- `visualboyadvance-m-Win-x86_64.zip`
- `visualboyadvance-m-Win-x86_64-debug.zip`
- `visualboyadvance-m-Win-x86_32.zip`
- `visualboyadvance-m-Win-x86_32-debug.zip`
- `visualboyadvance-m-Win-arm64.zip`
- 'visualboyadvance-m-Win-arm64-debug.zip'
- `visualboyadvance-m-Mac-x86_64.zip`
- `visualboyadvance-m-Mac-x86_64-debug.zip`
Update the winsparkle appcast.xml by running this cmake command:
Update the winsparkle `appcast.xml` by running this cmake command:
```bash
cmake .. -DUPDATE_APPCAST=TRUE
```
follow the instructions to push the change to the web data repo.
, follow the instructions to push the change to the web data repo.
Announce the release on reddit r/emulation and the forum.

172
README.md
View File

@@ -19,8 +19,6 @@
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
[![Join the chat at https://gitter.im/visualboyadvance-m/Lobby](https://badges.gitter.im/visualboyadvance-m/Lobby.svg)](https://gitter.im/visualboyadvance-m/Lobby)
Our bridged Discord server is [Here](https://discord.gg/EpfxEuGMKH).
We are also on *`#vba-m`* on [Libera IRC](https://libera.chat/) which has a [Web
@@ -59,35 +57,39 @@ Factory Reset`.
## System Requirements
Windows 7, 8.1 or 10/11, Linux distro's or macOS.
Windows XP, Vista, 7, 8.1 or 10/11, Linux distros or macOS.
2Ghz x86 (or x86-64) Intel Core 2 or AMD Athlon processor with SSE, Snapdragon 835
or newer CPU compatible with Arm for Windows.
2Ghz x86(or x86-64) Intel Core 2 or AMD Athlon processor with SSE, Snapdragon 835
or newer cpu compatible with Arm for Windows.
- Just a guideline, lower clock speeds and Celeron processors may be able to run at full
speed on lower settings, and Linux based ARM Operating systems have wider cpu support.
speed on lower settings, and Linux based ARM Operating systems have wider CPU support.
DirectX June 2010 Redist [Full](https://www.microsoft.com/en-au/download/details.aspx?id=8109) / [Websetup](https://www.microsoft.com/en-au/download/details.aspx?id=35) for Xaudio (Remember to uncheck Bing on the websetup)
DirectX June 2010 Redist
[Full](https://www.microsoft.com/en-au/download/details.aspx?id=8109) /
[Websetup](https://www.microsoft.com/en-au/download/details.aspx?id=35) for
Xaudio (Remember to uncheck Bing on the websetup.)
## Building
The basic formula to build vba-m is:
```shell
```bash
cd ~ && mkdir src && cd src
git clone https://github.com/visualboyadvance-m/visualboyadvance-m.git
cd visualboyadvance-m
./installdeps
# ./installdeps will give you build instructions, which will be similar to:
./installdeps # On Linux or macOS
mkdir build && cd build
cmake .. -G Ninja
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja
```
`./installdeps` is supported on MSys2, Linux (Debian/Ubuntu, Fedora, Arch,
Solus, OpenSUSE, Gentoo and RHEL/CentOS) and Mac OS X (homebrew, macports or
fink.)
`./installdeps` is supported on MSYS2, Linux (Debian/Ubuntu, Fedora, Arch,
Solus, OpenSUSE, Gentoo and RHEL/CentOS) and Mac OS X (homebrew, MacPorts or
Fink.)
## Building a Libretro core
@@ -98,7 +100,7 @@ cd src/libretro
make -j`nproc`
```
Copy vbam_libretro.so to your RetroArch cores directory.
Copy `vbam_libretro.so` to your RetroArch cores directory.
## Visual Studio Support
@@ -118,14 +120,16 @@ environment loaded.
Using your own user-wide installation of vcpkg is supported, just make sure the
environment variable `VCPKG_ROOT` is set.
To build in the visual studio command prompt, use something like this:
To build in the Visual Studio `x64 Native Tools Command Prompt`, use something
like this:
```
mkdir build
cd build
cmake .. -DVCPKG_TARGET_TRIPLET=x64-windows-static -DCMAKE_BUILD_TYPE=Debug -G Ninja
cmake .. -DCMAKE_BUILD_TYPE=Release -G Ninja
ninja
```
.
## Visual Studio Code Support
@@ -162,8 +166,7 @@ And the following development libraries:
- [ffmpeg](https://ffmpeg.org/) (optional, at least version `4.0.4`, for game recording)
- [gettext](https://www.gnu.org/software/gettext/) and gettext-tools
- [SDL2](https://www.libsdl.org/) (required)
- [SFML](https://www.sfml-dev.org/) (optional, for link)
- [OpenAL](https://www.openal.org/) or [openal-soft](https://kcat.strangesoft.net/openal.html) (required, a sound interface)
- [openal-soft](https://kcat.strangesoft.net/openal.html) (required, a sound interface)
- [wxWidgets](https://wxwidgets.org/) (required for GUI, 2.8 and non-stl builds are no longer supported)
On Linux and similar, you also need the version of GTK your wxWidgets is linked
@@ -183,61 +186,46 @@ This is supported on Fedora, Arch, Solus and MSYS2.
may be `win32` which is an alias for `mingw-w64-i686` to target 32 bit Windows,
or `mingw-w64-x86_64` for 64 bit Windows targets.
The target is implicit on MSys2 depending on which MINGW shell you started (the
The target is implicit on MSYS2 depending on which MINGW shell you started (the
value of `$MSYSTEM`.)
On Debian/Ubuntu this uses the MXE apt repository and works quite well.
On Fedora it can build using the Fedora MinGW packages, albeit with wx 2.8, no
OpenGL support, and no Link support for lack of SFML.
On Arch it currently doesn't work at all because the AUR stuff is completely
broken, I will at some point redo the arch stuff to use MXE as well.
## CMake Options
The CMake code tries to guess reasonable defaults for options, but you can
override them, for example:
```shell
cmake .. -DENABLE_LINK=NO -G Ninja
cmake .. -DCMAKE_BUILD_TYPE=Release -DENABLE_LINK=NO -G Ninja
```
Of particular interest is making **Release** or **Debug** builds, the default
mode is **Release**, to make a **Debug** build use something like:
```shell
cmake .. -DCMAKE_BUILD_TYPE=Debug -G Ninja
```
Here is the complete list:
. Here is the complete list:
| **CMake Option** | **What it Does** | **Defaults** |
|-----------------------|----------------------------------------------------------------------|-----------------------|
| ENABLE_SDL | Build the SDL port | OFF |
| ENABLE_WX | Build the wxWidgets port | ON |
| ENABLE_DEBUGGER | Enable the debugger | ON |
| ENABLE_ASM_CORE | Enable x86 ASM CPU cores (**BUGGY AND DANGEROUS**) | OFF |
| ENABLE_ASM | Enable the following two ASM options | ON for 32 bit builds |
| ENABLE_ASM_SCALERS | Enable x86 ASM graphic filters | ON for 32 bit builds |
| ENABLE_MMX | Enable MMX | ON for 32 bit builds |
| ENABLE_LINK | Enable GBA linking functionality (requires SFML) | AUTO |
| ENABLE_LIRC | Enable LIRC support | OFF |
| ENABLE_FFMPEG | Enable ffmpeg A/V recording | AUTO |
| ENABLE_ONLINEUPDATES | Enable online update checks | ON |
| ENABLE_LTO | Compile with Link Time Optimization (gcc and clang only) | ON for release build |
| ENABLE_GBA_LOGGING | Enable extended GBA logging | ON |
| ENABLE_DIRECT3D | Direct3D rendering for wxWidgets (Windows, **NOT IMPLEMENTED!!!**) | ON |
| ENABLE_XAUDIO2 | Enable xaudio2 sound output for wxWidgets (Windows only) | ON |
| ENABLE_ASAN | Enable libasan sanitizers (by default address, only in debug mode) | OFF |
| UPSTREAM_RELEASE | Do some release tasks, like codesigning, making zip and gpg sigs. | OFF |
| BUILD_TESTING | Build the tests and enable ctest support. | ON |
| VBAM_STATIC | Try link all libs statically (the following are set to ON if ON) | OFF |
| SDL2_STATIC | Try to link static SDL2 libraries | OFF |
| SFML_STATIC_LIBRARIES | Try to link static SFML libraries | OFF |
| FFMPEG_STATIC | Try to link static ffmpeg libraries | OFF |
| OPENAL_STATIC | Try to link static OpenAL libraries | OFF |
| TRANSLATIONS_ONLY | Build only the translations.zip and nothing else | OFF |
| `ENABLE_SDL` | Build the SDL port | OFF |
| `ENABLE_WX` | Build the wxWidgets port | ON |
| `ENABLE_DEBUGGER` | Enable the debugger | ON |
| `ENABLE_ASM_CORE` | Enable x86 ASM CPU cores (**BUGGY AND DANGEROUS**) | OFF |
| `ENABLE_ASM` | Enable the following two ASM options | ON for 32 bit builds |
| `ENABLE_ASM_SCALERS` | Enable x86 ASM graphic filters | ON for 32 bit builds |
| `ENABLE_MMX` | Enable MMX | ON for 32 bit builds |
| `ENABLE_LINK` | Enable GBA linking functionality | AUTO |
| `ENABLE_LIRC` | Enable LIRC support | OFF |
| `ENABLE_FFMPEG` | Enable ffmpeg A/V recording | AUTO |
| `ENABLE_ONLINEUPDATES` | Enable online update checks | ON |
| `ENABLE_LTO` | Compile with Link Time Optimization (gcc and clang only) | ON for release build |
| `ENABLE_GBA_LOGGING` | Enable extended GBA logging | ON |
| `ENABLE_XAUDIO2` | Enable xaudio2 sound output for wxWidgets (Windows only) | ON |
| `ENABLE_FAUDIO` | Enable faudio sound output for wxWidgets, | ON, not 32 bit Win |
| `ENABLE_ASAN` | Enable libasan sanitizers (by default address, only in debug mode) | OFF |
| `UPSTREAM_RELEASE` | Do some release tasks, like codesigning, making zip and gpg sigs. | OFF |
| `BUILD_TESTING` | Build the tests and enable ctest support. | ON |
| `VBAM_STATIC` | Try link all libs statically (the following are set to ON if ON) | OFF |
| `SDL2_STATIC` | Try to link static SDL2 libraries | OFF |
| `FFMPEG_STATIC` | Try to link static ffmpeg libraries | OFF |
| `OPENAL_STATIC` | Try to link static OpenAL libraries | OFF |
| `TRANSLATIONS_ONLY` | Build only the translations.zip and nothing else | OFF |
Note for distro packagers, we use the CMake module
[GNUInstallDirs](https://cmake.org/cmake/help/v2.8.12/cmake.html#module:GNUInstallDirs)
@@ -247,52 +235,6 @@ On Unix to use a different version of wxWidgets, set
`wxWidgets_CONFIG_EXECUTABLE` to the path to the `wx-config` script you want to
use.
## MSys2 Notes
To run the resulting binary, you can simply type:
```shell
./visualboyadvance-m
```
in the shell where you built it.
If you built with `-DCMAKE_BUILD_TYPE=Debug`, you will get a console app and
will see debug messages, even in mintty.
If you want to start the binary from e.g. a shortcut or Explorer, you will need
to put `c:\msys64\mingw32\bin` for 32 bit builds and `c:\msys64\mingw64\bin`
for 64 bit builds in your PATH (to edit system PATH, go to Control Panel ->
System -> Advanced system settings -> Environment Variables.)
If you want to package the binary, you will need to include the MinGW DLLs it
depends on, they can install to the same directory as the binary.
Our own builds are static.
## Debug Messages
We have an override for `wxLogDebug()` to make it work even in non-debug builds
of wx and on windows, even in mintty.
It works like `printf()`, e.g.:
```cpp
int foo = 42;
wxLogDebug(wxT("the value of foo = %d"), foo);
```
From the core etc. the usual:
```cpp
fprintf(stderr, "...", ...);
```
will work fine.
You need a debug build for this to work or to even have a console on Windows.
Pass `-DCMAKE_BUILD_TYPE=Debug` to cmake.
## Reporting Crash Bugs
If the emulator crashes and you wish to report the bug, a backtrace made with
@@ -311,27 +253,25 @@ do something such as:
```shell
ulimit -c unlimited
```
in your shell to enable coredump files.
, in your shell to enable core files.
[This
post](https://ask.fedoraproject.org/en/question/98776/where-is-core-dump-located/?answer=98779#post-id-98779)
explains how to retrieve core dump on Fedora Linux (and possibly other
distributions.)
explains how to retrieve core dump on some distributions, when they are managed
by systemd.
Once you have the core dump file, open it with `gdb`, for example:
Once you have the core file, open it with `gdb`, for example:
```shell
gdb -c core ./visualboyadvance-m
```
In the `gdb` shell, to print the backtrace, type:
. In the `gdb` shell, to start the process and print the backtrace, type:
```
run
bt
```
This may be a bit of a hassle, but it helps us out immensely.
. This may be a bit of a hassle, but it helps us out immensely.
## Contributing

View File

@@ -56,10 +56,12 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "[xX]86|i[3-9]86|[aA][mM][dD]64")
endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "[aA][aA][rR][cC][hH]|[aA][rR][mM]")
if(CMAKE_C_SIZEOF_DATA_PTR EQUAL 4) # 32 bit
set(ARCH_NAME arm32)
set(ARM32 ON)
set(ARCH_NAME ARM32)
set(WINARCH arm)
elseif(CMAKE_C_SIZEOF_DATA_PTR EQUAL 8)
set(ARCH_NAME arm64)
set(ARM64 ON)
set(ARCH_NAME ARM64)
set(WINARCH arm64)
endif()

View File

@@ -23,7 +23,7 @@ if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
if(VCPKG_TARGET_TRIPLET MATCHES -static)
set(arch_suffix -static)
endif()
if(CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo)$")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(path_prefix debug)
endif()
set(installed_prefix ${_VCPKG_INSTALLED_DIR}/${WINARCH}-windows${arch_suffix}/${path_prefix})
@@ -119,4 +119,3 @@ if(ENABLE_LINK OR ENABLE_WX)
message(FATAL_ERROR "NLS requires libintl/gettext")
endif()
endif()

View File

@@ -1,110 +0,0 @@
#.rst:
# FindOpenAL
# ----------
#
#
#
# Locate OpenAL This module defines OPENAL_LIBRARY OPENAL_FOUND, if
# false, do not try to link to OpenAL OPENAL_INCLUDE_DIR, where to find
# the headers
#
# $OPENALDIR is an environment variable that would correspond to the
# ./configure --prefix=$OPENALDIR used in building OpenAL.
#
# Created by Eric Wing. This was influenced by the FindSDL.cmake
# module.
#=============================================================================
# Copyright 2005-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
# This makes the presumption that you are include al.h like
# #include "al.h"
# and not
# #include <AL/al.h>
# The reason for this is that the latter is not entirely portable.
# Windows/Creative Labs does not by default put their headers in AL/ and
# OS X uses the convention <OpenAL/al.h>.
#
# For Windows, Creative Labs seems to have added a registry key for their
# OpenAL 1.1 installer. I have added that key to the list of search paths,
# however, the key looks like it could be a little fragile depending on
# if they decide to change the 1.00.0000 number for bug fix releases.
# Also, they seem to have laid down groundwork for multiple library platforms
# which puts the library in an extra subdirectory. Currently there is only
# Win32 and I have hardcoded that here. This may need to be adjusted as
# platforms are introduced.
# The OpenAL 1.0 installer doesn't seem to have a useful key I can use.
# I do not know if the Nvidia OpenAL SDK has a registry key.
#
# For OS X, remember that OpenAL was added by Apple in 10.4 (Tiger).
# To support the framework, I originally wrote special framework detection
# code in this module which I have now removed with CMake's introduction
# of native support for frameworks.
# In addition, OpenAL is open source, and it is possible to compile on Panther.
# Furthermore, due to bugs in the initial OpenAL release, and the
# transition to OpenAL 1.1, it is common to need to override the built-in
# framework.
# Per my request, CMake should search for frameworks first in
# the following order:
# ~/Library/Frameworks/OpenAL.framework/Headers
# /Library/Frameworks/OpenAL.framework/Headers
# /System/Library/Frameworks/OpenAL.framework/Headers
#
# On OS X, this will prefer the Framework version (if found) over others.
# People will have to manually change the cache values of
# OPENAL_LIBRARY to override this selection or set the CMake environment
# CMAKE_INCLUDE_PATH to modify the search paths.
find_path(OPENAL_INCLUDE_DIR al.h
HINTS
ENV OPENALDIR
PATH_SUFFIXES AL OpenAL
PATHS
~/Library/Frameworks
/Library/Frameworks
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Creative\ Labs\\OpenAL\ 1.1\ Software\ Development\ Kit\\1.00.0000;InstallDir]
)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_OpenAL_ARCH_DIR libs/Win64)
else()
set(_OpenAL_ARCH_DIR libs/Win32)
endif()
find_library(OPENAL_LIBRARY
NAMES OpenAL al openal OpenAL32
HINTS
ENV OPENALDIR
PATH_SUFFIXES lib64 lib libs64 libs ${_OpenAL_ARCH_DIR}
PATHS
~/Library/Frameworks
/Library/Frameworks
/sw
/opt/local
/opt/csw
/opt
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Creative\ Labs\\OpenAL\ 1.1\ Software\ Development\ Kit\\1.00.0000;InstallDir]
)
unset(_OpenAL_ARCH_DIR)
# handle the QUIETLY and REQUIRED arguments and set OPENAL_FOUND to TRUE if
# all listed variables are TRUE
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(OpenAL DEFAULT_MSG OPENAL_LIBRARY OPENAL_INCLUDE_DIR)
mark_as_advanced(OPENAL_LIBRARY OPENAL_INCLUDE_DIR)

View File

@@ -1,365 +0,0 @@
# This script locates the SFML library
# ------------------------------------
#
# Usage
# -----
#
# When you try to locate the SFML libraries, you must specify which modules you want to use (system, window, graphics, network, audio, main).
# If none is given, the SFML_LIBRARIES variable will be empty and you'll end up linking to nothing.
# example:
# find_package(SFML COMPONENTS graphics window system) # find the graphics, window and system modules
#
# You can enforce a specific version, either MAJOR.MINOR or only MAJOR.
# If nothing is specified, the version won't be checked (i.e. any version will be accepted).
# example:
# find_package(SFML COMPONENTS ...) # no specific version required
# find_package(SFML 2 COMPONENTS ...) # any 2.x version
# find_package(SFML 2.4 COMPONENTS ...) # version 2.4 or greater
#
# By default, the dynamic libraries of SFML will be found. To find the static ones instead,
# you must set the SFML_STATIC_LIBRARIES variable to TRUE before calling find_package(SFML ...).
# Since you have to link yourself all the SFML dependencies when you link it statically, the following
# additional variables are defined: SFML_XXX_DEPENDENCIES and SFML_DEPENDENCIES (see their detailed
# description below).
# In case of static linking, the SFML_STATIC macro will also be defined by this script.
# example:
# set(SFML_STATIC_LIBRARIES TRUE)
# find_package(SFML 2 COMPONENTS network system)
#
# On Mac OS X if SFML_STATIC_LIBRARIES is not set to TRUE then by default CMake will search for frameworks unless
# CMAKE_FIND_FRAMEWORK is set to "NEVER" for example. Please refer to CMake documentation for more details.
# Moreover, keep in mind that SFML frameworks are only available as release libraries unlike dylibs which
# are available for both release and debug modes.
#
# If SFML is not installed in a standard path, you can use the SFML_ROOT CMake (or environment) variable
# to tell CMake where SFML is.
#
# Output
# ------
#
# This script defines the following variables:
# - For each specified module XXX (system, window, graphics, network, audio, main):
# - SFML_XXX_LIBRARY_DEBUG: the name of the debug library of the xxx module (set to SFML_XXX_LIBRARY_RELEASE is no debug version is found)
# - SFML_XXX_LIBRARY_RELEASE: the name of the release library of the xxx module (set to SFML_XXX_LIBRARY_DEBUG is no release version is found)
# - SFML_XXX_LIBRARY: the name of the library to link to for the xxx module (includes both debug and optimized names if necessary)
# - SFML_XXX_FOUND: true if either the debug or release library of the xxx module is found
# - SFML_XXX_DEPENDENCIES: the list of libraries the module depends on, in case of static linking
# - SFML_LIBRARIES: the list of all libraries corresponding to the required modules
# - SFML_FOUND: true if all the required modules are found
# - SFML_INCLUDE_DIR: the path where SFML headers are located (the directory containing the SFML/Config.hpp file)
# - SFML_DEPENDENCIES: the list of libraries SFML depends on, in case of static linking
#
# example:
# find_package(SFML 2 COMPONENTS system window graphics audio REQUIRED)
# include_directories(${SFML_INCLUDE_DIR})
# add_executable(myapp ...)
# target_link_libraries(myapp ${SFML_LIBRARIES})
# define the SFML_STATIC macro if static build was chosen
if(SFML_STATIC_LIBRARIES)
add_compile_definitions(SFML_STATIC)
endif()
# define the list of search paths for headers and libraries
set(FIND_SFML_PATHS
${SFML_ROOT}
$ENV{SFML_ROOT}
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw
/opt/local
/opt/csw
/opt)
# find the SFML include directory
find_path(SFML_INCLUDE_DIR SFML/Config.hpp
PATH_SUFFIXES include
PATHS ${FIND_SFML_PATHS})
# check the version number
set(SFML_VERSION_OK TRUE)
if(SFML_FIND_VERSION AND SFML_INCLUDE_DIR)
# extract the major and minor version numbers from SFML/Config.hpp
# we have to handle framework a little bit differently:
if("${SFML_INCLUDE_DIR}" MATCHES "SFML.framework")
set(SFML_CONFIG_HPP_INPUT "${SFML_INCLUDE_DIR}/Headers/Config.hpp")
else()
set(SFML_CONFIG_HPP_INPUT "${SFML_INCLUDE_DIR}/SFML/Config.hpp")
endif()
FILE(READ "${SFML_CONFIG_HPP_INPUT}" SFML_CONFIG_HPP_CONTENTS)
STRING(REGEX REPLACE ".*#define *SFML_VERSION_MAJOR *([0-9]+).*" "\\1" SFML_VERSION_MAJOR "${SFML_CONFIG_HPP_CONTENTS}")
STRING(REGEX REPLACE ".*#define *SFML_VERSION_MINOR *([0-9]+).*" "\\1" SFML_VERSION_MINOR "${SFML_CONFIG_HPP_CONTENTS}")
STRING(REGEX REPLACE ".*#define *SFML_VERSION_PATCH *([0-9]+).*" "\\1" SFML_VERSION_PATCH "${SFML_CONFIG_HPP_CONTENTS}")
if (NOT "${SFML_VERSION_PATCH}" MATCHES "^[0-9]+$")
set(SFML_VERSION_PATCH 0)
endif()
math(EXPR SFML_REQUESTED_VERSION "${SFML_FIND_VERSION_MAJOR} * 10000 + ${SFML_FIND_VERSION_MINOR} * 100 + ${SFML_FIND_VERSION_PATCH}")
# if we could extract them, compare with the requested version number
if (SFML_VERSION_MAJOR)
# transform version numbers to an integer
math(EXPR SFML_VERSION "${SFML_VERSION_MAJOR} * 10000 + ${SFML_VERSION_MINOR} * 100 + ${SFML_VERSION_PATCH}")
# compare them
if(SFML_VERSION LESS SFML_REQUESTED_VERSION)
set(SFML_VERSION_OK FALSE)
endif()
else()
# SFML version is < 2.0
if (SFML_REQUESTED_VERSION GREATER 10900)
set(SFML_VERSION_OK FALSE)
set(SFML_VERSION_MAJOR 1)
set(SFML_VERSION_MINOR x)
set(SFML_VERSION_PATCH x)
endif()
endif()
endif()
# find the requested modules
set(SFML_FOUND TRUE) # will be set to false if one of the required modules is not found
foreach(FIND_SFML_COMPONENT ${SFML_FIND_COMPONENTS})
string(TOLOWER ${FIND_SFML_COMPONENT} FIND_SFML_COMPONENT_LOWER)
string(TOUPPER ${FIND_SFML_COMPONENT} FIND_SFML_COMPONENT_UPPER)
set(FIND_SFML_COMPONENT_NAME sfml-${FIND_SFML_COMPONENT_LOWER})
# no suffix for sfml-main, it is always a static library
if(FIND_SFML_COMPONENT_LOWER STREQUAL "main")
# release library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE
NAMES ${FIND_SFML_COMPONENT_NAME}
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
# debug library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG
NAMES ${FIND_SFML_COMPONENT_NAME}-d
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
else()
# static release library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE
NAMES ${FIND_SFML_COMPONENT_NAME}-s
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
# static debug library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG
NAMES ${FIND_SFML_COMPONENT_NAME}-s-d
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
# dynamic release library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE
NAMES ${FIND_SFML_COMPONENT_NAME}
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
# dynamic debug library
find_library(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG
NAMES ${FIND_SFML_COMPONENT_NAME}-d
PATH_SUFFIXES lib64 lib
PATHS ${FIND_SFML_PATHS})
# choose the entries that fit the requested link type
if(SFML_STATIC_LIBRARIES)
if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE})
endif()
if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG})
endif()
else()
if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE})
endif()
if(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG})
endif()
endif()
endif()
if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG OR SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE)
# library found
set(SFML_${FIND_SFML_COMPONENT_UPPER}_FOUND TRUE)
# if both are found, set SFML_XXX_LIBRARY to contain both
if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG AND SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY debug ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG}
optimized ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE})
endif()
# if only one debug/release variant is found, set the other to be equal to the found one
if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG AND NOT SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE)
# debug and not release
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG})
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG})
endif()
if (SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE AND NOT SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG)
# release and not debug
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE})
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY ${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE})
endif()
else()
# library not found
set(SFML_FOUND FALSE)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_FOUND FALSE)
set(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY "")
set(FIND_SFML_MISSING "${FIND_SFML_MISSING} SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY")
endif()
# mark as advanced
MARK_AS_ADVANCED(SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_RELEASE
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DEBUG
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_RELEASE
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_STATIC_DEBUG
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_RELEASE
SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY_DYNAMIC_DEBUG)
# add to the global list of libraries
set(SFML_LIBRARIES ${SFML_LIBRARIES} "${SFML_${FIND_SFML_COMPONENT_UPPER}_LIBRARY}")
endforeach()
# in case of static linking, we must also define the list of all the dependencies of SFML libraries
if(SFML_STATIC_LIBRARIES)
# detect the OS
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(FIND_SFML_OS_WINDOWS 1)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(FIND_SFML_OS_LINUX 1)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
set(FIND_SFML_OS_FREEBSD 1)
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(FIND_SFML_OS_MACOSX 1)
endif()
# start with an empty list
set(SFML_DEPENDENCIES)
set(FIND_SFML_DEPENDENCIES_NOTFOUND)
# macro that searches for a 3rd-party library
macro(find_sfml_dependency output friendlyname)
# No lookup in environment variables (PATH on Windows), as they may contain wrong library versions
find_library(${output} NAMES ${ARGN} PATHS ${FIND_SFML_PATHS} PATH_SUFFIXES lib NO_SYSTEM_ENVIRONMENT_PATH)
if(${${output}} STREQUAL "${output}-NOTFOUND")
unset(output)
set(FIND_SFML_DEPENDENCIES_NOTFOUND "${FIND_SFML_DEPENDENCIES_NOTFOUND} ${friendlyname}")
endif()
endmacro()
# sfml-system
list(FIND SFML_FIND_COMPONENTS "system" FIND_SFML_SYSTEM_COMPONENT)
if(NOT ${FIND_SFML_SYSTEM_COMPONENT} EQUAL -1)
# update the list -- these are only system libraries, no need to find them
if(FIND_SFML_OS_LINUX OR FIND_SFML_OS_FREEBSD OR FIND_SFML_OS_MACOSX)
set(SFML_SYSTEM_DEPENDENCIES "pthread")
endif()
if(FIND_SFML_OS_LINUX)
set(SFML_SYSTEM_DEPENDENCIES ${SFML_SYSTEM_DEPENDENCIES} "rt")
endif()
if(FIND_SFML_OS_WINDOWS)
set(SFML_SYSTEM_DEPENDENCIES "winmm")
endif()
set(SFML_DEPENDENCIES ${SFML_SYSTEM_DEPENDENCIES} ${SFML_DEPENDENCIES})
endif()
# sfml-network
list(FIND SFML_FIND_COMPONENTS "network" FIND_SFML_NETWORK_COMPONENT)
if(NOT ${FIND_SFML_NETWORK_COMPONENT} EQUAL -1)
# update the list -- these are only system libraries, no need to find them
if(FIND_SFML_OS_WINDOWS)
set(SFML_NETWORK_DEPENDENCIES "ws2_32")
endif()
set(SFML_DEPENDENCIES ${SFML_NETWORK_DEPENDENCIES} ${SFML_DEPENDENCIES})
endif()
# sfml-window
list(FIND SFML_FIND_COMPONENTS "window" FIND_SFML_WINDOW_COMPONENT)
if(NOT ${FIND_SFML_WINDOW_COMPONENT} EQUAL -1)
# find libraries
if(FIND_SFML_OS_LINUX OR FIND_SFML_OS_FREEBSD)
find_sfml_dependency(X11_LIBRARY "X11" X11)
find_sfml_dependency(XRANDR_LIBRARY "Xrandr" Xrandr)
endif()
if(FIND_SFML_OS_LINUX)
find_sfml_dependency(UDEV_LIBRARIES "UDev" udev libudev)
endif()
# update the list
if(FIND_SFML_OS_WINDOWS)
set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "opengl32" "winmm" "gdi32")
elseif(FIND_SFML_OS_LINUX)
set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "GL" ${X11_LIBRARY} ${XRANDR_LIBRARY} ${UDEV_LIBRARIES})
elseif(FIND_SFML_OS_FREEBSD)
set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "GL" ${X11_LIBRARY} ${XRANDR_LIBRARY} "usbhid")
elseif(FIND_SFML_OS_MACOSX)
set(SFML_WINDOW_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} "-framework OpenGL -framework Foundation -framework AppKit -framework IOKit -framework Carbon")
endif()
set(SFML_DEPENDENCIES ${SFML_WINDOW_DEPENDENCIES} ${SFML_DEPENDENCIES})
endif()
# sfml-graphics
list(FIND SFML_FIND_COMPONENTS "graphics" FIND_SFML_GRAPHICS_COMPONENT)
if(NOT ${FIND_SFML_GRAPHICS_COMPONENT} EQUAL -1)
# find libraries
find_sfml_dependency(FREETYPE_LIBRARY "FreeType" freetype)
find_sfml_dependency(JPEG_LIBRARY "libjpeg" jpeg)
# update the list
set(SFML_GRAPHICS_DEPENDENCIES ${FREETYPE_LIBRARY} ${JPEG_LIBRARY})
set(SFML_DEPENDENCIES ${SFML_GRAPHICS_DEPENDENCIES} ${SFML_DEPENDENCIES})
endif()
# sfml-audio
list(FIND SFML_FIND_COMPONENTS "audio" FIND_SFML_AUDIO_COMPONENT)
if(NOT ${FIND_SFML_AUDIO_COMPONENT} EQUAL -1)
# find libraries
find_sfml_dependency(OPENAL_LIBRARY "OpenAL" openal openal32)
find_sfml_dependency(OGG_LIBRARY "Ogg" ogg)
find_sfml_dependency(VORBIS_LIBRARY "Vorbis" vorbis)
find_sfml_dependency(VORBISFILE_LIBRARY "VorbisFile" vorbisfile)
find_sfml_dependency(VORBISENC_LIBRARY "VorbisEnc" vorbisenc)
find_sfml_dependency(FLAC_LIBRARY "FLAC" FLAC)
# update the list
set(SFML_AUDIO_DEPENDENCIES ${OPENAL_LIBRARY} ${FLAC_LIBRARY} ${VORBISENC_LIBRARY} ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY})
set(SFML_DEPENDENCIES ${SFML_DEPENDENCIES} ${SFML_AUDIO_DEPENDENCIES})
endif()
endif()
# handle errors
if(NOT SFML_VERSION_OK)
# SFML version not ok
set(FIND_SFML_ERROR "SFML found but version too low (requested: ${SFML_FIND_VERSION}, found: ${SFML_VERSION_MAJOR}.${SFML_VERSION_MINOR}.${SFML_VERSION_PATCH})")
set(SFML_FOUND FALSE)
elseif(SFML_STATIC_LIBRARIES AND FIND_SFML_DEPENDENCIES_NOTFOUND)
set(FIND_SFML_ERROR "SFML found but some of its dependencies are missing (${FIND_SFML_DEPENDENCIES_NOTFOUND})")
set(SFML_FOUND FALSE)
elseif(NOT SFML_FOUND)
# include directory or library not found
set(FIND_SFML_ERROR "Could NOT find SFML (missing: ${FIND_SFML_MISSING})")
endif()
if (NOT SFML_FOUND)
if(SFML_FIND_REQUIRED)
# fatal error
message(FATAL_ERROR ${FIND_SFML_ERROR})
elseif(NOT SFML_FIND_QUIETLY)
# error but continue
message("${FIND_SFML_ERROR}")
endif()
endif()
# handle success
if(SFML_FOUND AND NOT SFML_FIND_QUIETLY)
message(STATUS "Found SFML ${SFML_VERSION_MAJOR}.${SFML_VERSION_MINOR}.${SFML_VERSION_PATCH} in ${SFML_INCLUDE_DIR}")
endif()

View File

@@ -30,7 +30,6 @@ option(VBAM_STATIC "Try to link all libraries statically" ${VBAM_STATIC_DEFAULT}
if(VBAM_STATIC)
set(SDL2_STATIC ON)
set(SFML_STATIC_LIBRARIES ON)
set(FFMPEG_STATIC ON)
set(OPENAL_STATIC ON)
set_property(GLOBAL PROPERTY LINK_SEARCH_START_STATIC ON)
@@ -68,21 +67,8 @@ endif()
find_package(PkgConfig)
# Link / SFML
if(TRANSLATIONS_ONLY)
set(ENABLE_LINK_DEFAULT OFF)
else()
find_package(SFML 2.4 COMPONENTS network system)
if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
if(SFML_STATIC_LIBRARIES AND SFML_NETWORK_LIBRARY_STATIC_DEBUG AND SFML_SYSTEM_LIBRARY_STATIC_DEBUG)
set(SFML_LIBRARIES ${SFML_NETWORK_LIBRARY_STATIC_DEBUG} ${SFML_SYSTEM_LIBRARY_STATIC_DEBUG})
elseif(SFML_NETWORK_LIBRARY_DYNAMIC_DEBUG AND SFML_SYSTEM_LIBRARY_DYNAMIC_DEBUG)
set(SFML_LIBRARIES ${SFML_NETWORK_LIBRARY_DYNAMIC_DEBUG} ${SFML_SYSTEM_LIBRARY_DYNAMIC_DEBUG})
endif()
endif()
set(ENABLE_LINK_DEFAULT OFF)
if(SFML_FOUND)
set(ENABLE_LINK_DEFAULT ON)
endif()
if(NOT TRANSLATIONS_ONLY)
set(ENABLE_LINK_DEFAULT ON)
endif()
option(ENABLE_LINK "Enable GBA linking functionality" ${ENABLE_LINK_DEFAULT})
@@ -91,7 +77,7 @@ set(FFMPEG_DEFAULT OFF)
set(FFMPEG_COMPONENTS AVCODEC AVFORMAT SWSCALE AVUTIL SWRESAMPLE)
set(FFMPEG_COMPONENT_VERSIONS AVCODEC>=58.18.100 AVFORMAT>=58.12.100 SWSCALE>=5.1.100 AVUTIL>=56.14.100 SWRESAMPLE>=3.1.100)
if(NOT TRANSLATIONS_ONLY AND NOT DEFINED ENABLE_FFMPEG OR ENABLE_FFMPEG)
if(NOT TRANSLATIONS_ONLY AND (NOT DEFINED ENABLE_FFMPEG OR ENABLE_FFMPEG) AND (NOT (X86 AND MINGW)))
set(FFMPEG_DEFAULT ON)
find_package(FFmpeg COMPONENTS ${FFMPEG_COMPONENTS})

View File

@@ -10,19 +10,28 @@ if(NOT DEFINED VCPKG_TARGET_TRIPLET)
# Check if we are in an MSVC environment.
find_program(cl_exe_path NAME cl.exe HINTS ENV PATH)
if($ENV{CXX} MATCHES "cl.exe$" OR cl_exe_path)
if(ENV{CXX} MATCHES "cl.exe$" OR cl_exe_path)
# Infer the architecture from the LIB folders.
foreach(LIB $ENV{LIB})
if(${LIB} MATCHES "x64$")
foreach(lib $ENV{LIB})
if(lib MATCHES "x64$")
set(VBAM_VCPKG_PLATFORM "x64-windows-static")
break()
endif()
if(${LIB} MATCHES "x86$")
if(lib MATCHES "x86$")
set(VBAM_VCPKG_PLATFORM "x86-windows-static")
break()
endif()
if(${LIB} MATCHES "ARM64$")
if(lib MATCHES "ARM64$")
set(VBAM_VCPKG_PLATFORM "arm64-windows-static")
foreach(path $ENV{PATH})
if(path MATCHES "[Hh]ost[Xx]64")
set(VCPKG_HOST_TRIPLET "x64-windows-static" CACHE STRING "Vcpkg host triplet" FORCE)
set(VCPKG_USE_HOST_TOOLS ON CACHE BOOL "Use vcpkg host tools" FORCE)
break()
endif()
endforeach()
break()
endif()
endforeach()
@@ -493,7 +502,7 @@ function(vcpkg_set_toolchain)
# Install core deps.
execute_process(
COMMAND ${vcpkg_exe} install ${VCPKG_DEPS_QUALIFIED}
COMMAND ${vcpkg_exe} --triplet ${VCPKG_TARGET_TRIPLET} install ${pkg}
WORKING_DIRECTORY ${VCPKG_ROOT}
)
@@ -535,7 +544,7 @@ function(vcpkg_set_toolchain)
set(dep_qualified "${dep}:${VCPKG_TARGET_TRIPLET}")
execute_process(
COMMAND ${vcpkg_exe} install ${dep_qualified}
COMMAND --triplet ${VCPKG_TARGET_TRIPLET} ${vcpkg_exe} install ${dep}
WORKING_DIRECTORY ${VCPKG_ROOT}
)
@@ -567,14 +576,4 @@ endfunction()
vcpkg_set_toolchain()
# Make vcpkg use debug libs for RelWithDebInfo
set(orig_build_type ${CMAKE_BUILD_TYPE})
if(CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo)
set(CMAKE_BUILD_TYPE Debug)
endif()
include(${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake)
set(CMAKE_BUILD_TYPE ${orig_build_type})
unset(orig_build_type)

View File

@@ -5,10 +5,16 @@ endif()
if(UPSTREAM_RELEASE)
if(X86_64)
# Require and optimize for Core2 level support, tune for generic.
add_compile_options(-march=core2 -mtune=generic)
if(APPLE)
add_compile_options(-march=core2 -mtune=skylake)
else()
add_compile_options(-march=core2 -mtune=generic)
endif()
elseif(X86_32)
# Optimize for pentium-mmx and tune for generic for older builds.
add_compile_options(-march=pentium-mmx -mtune=generic)
# Optimize for pentiumi3 and tune for generic for Windows XP builds.
set(WINXP TRUE)
add_compile_options(-march=pentium3 -mtune=generic)
add_compile_definitions(-DWINXP)
endif()
endif()
@@ -46,9 +52,9 @@ if(NOT ENABLE_ASM) # inline asm is not allowed with -fPIC
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
add_compile_options(-ggdb3 -Og -fno-omit-frame-pointer -Wall -Wextra)
add_compile_options(-ggdb3 -fno-omit-frame-pointer -Wall -Wextra)
else()
add_compile_options(-Ofast -fomit-frame-pointer)
add_compile_options(-O3 -ffast-math -fomit-frame-pointer)
endif()
# for some reason this is necessary

View File

@@ -3,8 +3,8 @@ if (NOT MINGW)
endif()
# this has to run after the toolchain is initialized.
include_directories("${CMAKE_SOURCE_DIR}/dependencies/mingw-include")
include_directories("${CMAKE_SOURCE_DIR}/dependencies/mingw-xaudio/include")
include_directories("${CMAKE_SOURCE_DIR}/win32-deps/mingw-include")
include_directories("${CMAKE_SOURCE_DIR}/win32-deps/mingw-xaudio/include")
# Add Winsock as the last library linked because of broken link precedence.
set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} -lws2_32")

View File

@@ -10,7 +10,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
endif()
endif()
include_directories("${CMAKE_SOURCE_DIR}/dependencies/msvc")
include_directories("${CMAKE_SOURCE_DIR}/win32-deps/msvc")
add_compile_definitions(
_FORCENAMELESSUNION
@@ -68,6 +68,8 @@ if(CMAKE_VERSION VERSION_LESS "3.25")
endif()
endif()
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++14>)
set(CMAKE_RC_FLAGS "-c65001 /DWIN32" CACHE STRING "" FORCE)
# We need to explicitly set all of these to override the CMake defaults.

View File

@@ -16,7 +16,7 @@ Ignore the following cmake error.
# Get last tag.
execute_process(
COMMAND ${GIT_EXECUTABLE} tag --sort=-v:refname
COMMAND git tag --sort=-v:refname
OUTPUT_VARIABLE git_tags
OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
@@ -43,7 +43,7 @@ Ignore the following cmake error.
# Clone repo.
execute_process(
COMMAND ${GIT_EXECUTABLE} clone git@github.com:visualboyadvance-m/visualboyadvance-m.github.io web-data
COMMAND git clone git@github.com:visualboyadvance-m/visualboyadvance-m.github.io web-data
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
@@ -103,21 +103,21 @@ Ignore the following cmake error.
)
execute_process(
COMMAND ${GIT_EXECUTABLE} add appcast.xml
COMMAND git add appcast.xml
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/web-data
)
# Commit the change.
execute_process(
COMMAND ${GIT_EXECUTABLE} commit -m "release ${new_tag}" --signoff -S
COMMAND git commit -m "release ${new_tag}" --signoff -S
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/web-data
)
# Make release tag.
execute_process(
COMMAND ${GIT_EXECUTABLE} tag -s -m${new_tag} ${new_tag}
COMMAND git tag -s -m${new_tag} ${new_tag}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/web-data
)

View File

@@ -2,7 +2,7 @@ with import <nixpkgs> {};
stdenv.mkDerivation {
name = "visualboyadvance-m";
buildInputs = if stdenv.isDarwin then
[ ninja cmake gcc nasm faudio gettext libintl pkg-config zip sfml zlib openal ffmpeg wxGTK32 SDL2 pcre pcre2 darwin.apple_sdk.frameworks.System darwin.apple_sdk.frameworks.IOKit darwin.apple_sdk.frameworks.Carbon darwin.apple_sdk.frameworks.Cocoa darwin.apple_sdk.frameworks.QuartzCore darwin.apple_sdk.frameworks.AudioToolbox darwin.apple_sdk.frameworks.OpenGL darwin.apple_sdk.frameworks.OpenAL llvmPackages_latest.clang llvmPackages_latest.bintools ]
[ ninja cmake nasm faudio gettext libintl libtiff pkg-config zip zlib openal ffmpeg wxGTK32 SDL2 pcre pcre2 darwin.apple_sdk.frameworks.System darwin.apple_sdk.frameworks.IOKit darwin.apple_sdk.frameworks.Carbon darwin.apple_sdk.frameworks.Cocoa darwin.apple_sdk.frameworks.QuartzCore darwin.apple_sdk.frameworks.AudioToolbox darwin.apple_sdk.frameworks.OpenGL darwin.apple_sdk.frameworks.OpenAL llvmPackages_latest.clang llvmPackages_latest.bintools ]
else
[ ninja cmake gcc nasm faudio gettext libintl pkg-config zip sfml zlib openal ffmpeg wxGTK32 libGL libGLU glfw SDL2 gtk3-x11 pcre pcre2 util-linuxMinimal libselinux libsepol libthai libdatrie xorg.libXdmcp xorg.libXtst libxkbcommon epoxy dbus at-spi2-core ];
[ ninja cmake gcc clang llvm llvmPackages.libcxx nasm faudio gettext libintl libtiff pkg-config zip zlib openal ffmpeg wxGTK32 libGL libGLU glfw SDL2 gtk3-x11 pcre pcre2 util-linuxMinimal libselinux libsepol libthai libdatrie xorg.libXdmcp xorg.libXtst libxkbcommon libepoxy dbus at-spi2-core ];
}

Submodule dependencies deleted from e8ce758a98

View File

@@ -191,7 +191,7 @@ freebsd_installdeps() {
check sudo pkg update
pkgs="llvm-devel cmake ccache nasm ffmpeg gettext-tools gettext pkgconf sdl2 sfml wx31-gtk3 iconv zip ninja"
pkgs="llvm-devel cmake ccache nasm ffmpeg gettext-tools gettext pkgconf sdl2 wx31-gtk3 iconv zip ninja"
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs ffmpeg"
@@ -318,12 +318,6 @@ debian_installdeps() {
if [ -z "$target" ]; then
sudo apt-get -qq -y update
sfml_libs=
for lib in graphics window network; do
sfml_libs="$sfml_libs $(apt-cache search "libsfml-$lib" | sed 's/ - .*//' | sort -r | head -1)"
done
glew_lib=$(apt-cache search libglew | grep '^libglew[0-9]' | sed 's/ - .*//' | sort -r | head -1)
sdl_lib=$(apt-cache search '^libsdl2-2.0' | sed 's/ - .*//' | sort -r | head -1)
@@ -350,7 +344,7 @@ debian_installdeps() {
;;
esac
pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl2-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev libsfml-dev $sfml_libs $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build libopenal-dev"
pkgs="build-essential g++ nasm cmake ccache gettext zlib1g-dev libgl1-mesa-dev libgettextpo-dev libsdl2-dev $sdl_lib libglu1-mesa-dev libglu1-mesa libgles2-mesa-dev $glew_lib $wx_libs libgtk2.0-dev libgtk-3-dev ccache zip ninja-build libopenal-dev"
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs libavcodec-dev libavformat-dev libswscale-dev libavutil-dev $libswresample_dev"
@@ -401,7 +395,7 @@ debian_installdeps() {
fi
fi
deps="gcc zlib ffmpeg gettext sdl2 sfml openal wxwidgets openal"
deps="gcc zlib ffmpeg gettext sdl2 openal wxwidgets openal"
[ -n "$ENABLE_FFMPEG" ] && deps="$deps ffmpeg"
set --
@@ -498,7 +492,7 @@ fedora_installdeps() {
# this is sometimes necessary for rawhide
set -- --exclude='glibc32*'
fi
for pkg in zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel SDL2-devel SFML-devel openal-soft-devel wxGTK-devel gtk3-devel; do
for pkg in zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel SDL2-devel openal-soft-devel wxGTK-devel gtk3-devel; do
case $pkg in
*ffmpeg*)
[ -z "$ENABLE_FFMPEG" ] && continue
@@ -597,8 +591,6 @@ fedora_installdeps() {
# get the necessary win32 headers
git submodule update --init --remote --recursive
warning='SFML is required for LINK support, Fedora does not currently have a MinGW SFML package, if you want LINK support you will need to install it manually'
fi
[ -z "$rpms_installed" ] && check sudo dnf -y --nogpgcheck --best --allowerasing install "$@"
@@ -684,8 +676,6 @@ rhel_installdeps() {
set -- --exclude='glibc32*'
fi
warning='RHEL does not currently have SFML packages, LINK support will be disabled'
for pkg in zlib-devel mesa-libGL-devel ffmpeg-devel gettext-devel SDL2-devel openal-soft-devel wxGTK3-devel gtk3-devel; do
case $pkg in
*ffmpeg*)
@@ -777,8 +767,6 @@ rhel_installdeps() {
# get the necessary win32 headers
git submodule update --init --remote --recursive
warning='SFML is required for LINK support, RHEL/EPEL does not currently have a MinGW SFML package, if you want LINK support you will need to install it manually'
fi
[ -z "$rpms_installed" ] && check sudo yum -y install "$@"
@@ -797,14 +785,12 @@ suse_installdeps() {
check_cross
installing
tools="make cmake ccache nasm gettext-tools pkg-config ccache zip sfml2-devel ninja"
tools="make cmake ccache nasm gettext-tools pkg-config ccache zip ninja"
libs="gcc gcc-c++ libSDL2-devel wxWidgets-3_0-devel openal-soft-devel" # ffmpeg-devel
# ffmpeg requires packman repos
libs="gcc gcc-c++ libSDL2-devel wxGTK3-3_2-devel openal-soft-devel ffmpeg-7-libavcodec-devel ffmpeg-7-libavdevice-devel ffmpeg-7-libavfilter-devel ffmpeg-7-libavformat-devel ffmpeg-7-libavutil-devel ffmpeg-7-libpostproc-devel ffmpeg-7-libswresample-devel ffmpeg-7-libswscale-devel"
if [ "$target" = m32 ]; then
libs=$(echo "$libs" | sed -E 's/([^ ]) ([^ ])/\1-32bit \2/g; s/$/-32bit/;')
error '32 bit cross builds are no longer supported on OpenSUSE'
fi
check sudo zypper in -y $tools $libs
@@ -868,7 +854,7 @@ archlinux_installdeps() {
$pacman -Q gtk3-classic >/dev/null 2>&1 && gtk=gtk3-classic
libs="zlib mesa gettext sdl2 wxgtk3 $gtk sfml openal"
libs="zlib mesa gettext sdl2 wxgtk3 $gtk openal"
[ -n "$ENABLE_FFMPEG" ] && libs="$libs ffmpeg"
@@ -884,7 +870,7 @@ archlinux_installdeps() {
else
# try to build 32 bit binaries
# lib32-sfml and lib32-ffmpeg are in AUR
# lib32-ffmpeg is in AUR
archlinux_require_yaourt
# enable multilib repos if not enabled
@@ -978,8 +964,6 @@ EOF
# get the necessary win32 headers
git submodule update --init --remote --recursive
warning 'SFML is required for LINK support, the SFML package in AUR is currently broken, if you want LINK support you will need to install it manually'
fi
build_instructions
@@ -1042,13 +1026,12 @@ solus_installdeps() {
done
else
# no 32bit versions of these
set -- "$@" SFML-devel ffmpeg-devel
set -- "$@" ffmpeg-devel
fi
check sudo eopkg -y install "$@"
if [ -n "$amd64" -a "$target" = m32 ]; then
warning 'SFML is required for LINK support, there is no 32 bit SFML package in Solus currently, if you want LINK support you will need to install it manually'
warning 'ffmpeg is required for game recording, there is no 32 bit ffmpeg package in Solus currently, you may wish to install it manually'
fi
@@ -1070,7 +1053,6 @@ gentoo_installdeps() {
dev-util/ccache \
sys-devel/binutils \
media-libs/libsdl2 \
media-libs/libsfml \
media-libs/openal \
x11-libs/wxGTK:$wx_slot \
sys-libs/zlib \
@@ -1088,7 +1070,7 @@ gentoo_installdeps() {
alpine_installdeps() {
installing
check sudo apk add cmake ninja g++ ccache nasm gettext-dev zlib-dev mesa-dev sdl2-dev glu-dev sfml-dev wxwidgets-dev gtk+3.0-dev zip
check sudo apk add cmake ninja g++ ccache nasm gettext-dev zlib-dev mesa-dev sdl2-dev glu-dev wxwidgets-dev gtk+3.0-dev zip
build_instructions
}
@@ -1118,9 +1100,12 @@ windows_installdeps() {
*i686*)
pkgs="$pkgs nasm"
;;
*)
pkgs="$pkgs FAudio"
;;
esac
pkgs="$pkgs SDL2 sfml FAudio wxWidgets3.2 zlib binutils cmake crt-git headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal"
pkgs="$pkgs SDL2 wxWidgets3.2 zlib binutils cmake crt-git headers-git make pkgconf tools-git windows-default-manifest libmangle-git ninja gdb ccache openal"
case "$target" in
*x86_64)
@@ -1192,15 +1177,10 @@ brew_installdeps() {
check brew -v update
brews="nasm cmake ccache gettext pkg-config sdl2 wxwidgets faudio ccache ninja zlib"
brews="nasm cmake ccache gettext libtiff pkg-config sdl2 wxwidgets faudio ccache ninja zlib"
[ -n "$ENABLE_FFMPEG" ] && brews="$brews ffmpeg"
# sfml brew currently broken in the travis mac environment
# if [ -z "$TRAVIS" ]; then
brews="$brews sfml"
# fi
# This is necessary for the GitHub Actions CI:
brew -v install python
brew link --overwrite python
@@ -1223,7 +1203,7 @@ macports_installdeps() {
check sudo port -v selfupdate
ports="cmake ccache nasm gettext pkgconfig libsdl2 sfml wxWidgets-3.0 libiconv ninja"
ports="cmake ccache nasm gettext pkgconfig libsdl2 wxWidgets-3.0 libiconv ninja"
[ -n "$ENABLE_FFMPEG" ] && ports="$ports ffmpeg"
@@ -1239,7 +1219,7 @@ fink_installdeps() {
check sudo fink -vy selfupdate
pkgs="cmake ccache nasm libgettext8-dev gettext-tools pkgconfig sdl2 wxwidgets300-osxcocoa libiconv-dev sfml24-dev ccache ninja"
pkgs="cmake ccache nasm libgettext8-dev gettext-tools pkgconfig sdl2 wxwidgets300-osxcocoa libiconv-dev ccache ninja"
[ -n "$ENABLE_FFMPEG" ] && pkgs="$pkgs ffmpeg"

View File

@@ -3,6 +3,7 @@
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Orfeas Artinopoulos, 2025
# Stathis Galazios <infin1ty@hol.gr>, 2016-2017
msgid ""
msgstr ""
@@ -10,7 +11,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-05 20:00+0000\n"
"PO-Revision-Date: 2011-12-03 19:42+0000\n"
"Last-Translator: Stathis Galazios <infin1ty@hol.gr>, 2016-2017\n"
"Last-Translator: Orfeas Artinopoulos, 2025\n"
"Language-Team: Greek (http://app.transifex.com/bgk/vba-m/language/el/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -20,7 +21,7 @@ msgstr ""
#: audio/internal/openal.cpp:188
msgid "OpenAL: Failed to open audio device"
msgstr ""
msgstr "OpenAL: Αποτυχία ανοίγματος της συσκευής ήχου"
#: audio/internal/openal.cpp:386 audio/internal/faudio.cpp:492
#: audio/internal/xaudio2.cpp:577
@@ -33,7 +34,7 @@ msgid ""
"(*.agb;*.gba;*.bin;*.elf;*.mb;*.zip;*.7z;*.rar)|*.agb;*.gba;*.bin;*.elf;*.mb;*.agb.gz;*.gba.gz;*.bin.gz;*.elf.gz;*.mb.gz;*.agb.z;*.gba.z;*.bin.z;*.elf.z;*.mb.z;*.zip;*.7z;*.rar|Game"
" Boy Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "Αρχεία Game Boy Advance (*.agb;*.gba;*.bin;*.elf;*.mb;*.zip;*.7z;*.rar)|*.agb;*.gba;*.bin;*.elf;*.mb;*.agb.gz;*.gba.gz;*.bin.gz;*.elf.gz;*.mb.gz;*.agb.z;*.gba.z;*.bin.z;*.elf.z;*.mb.z;*.zip;*.7z;*.rar|Game Boy Files (*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:144
msgid "Open ROM file"
@@ -43,7 +44,7 @@ msgstr "Άνοιγμα αρχείου ROM"
msgid ""
"Game Boy Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "Αρχεία Game Boy (*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:171
msgid "Open GB ROM file"
@@ -53,7 +54,7 @@ msgstr "Άνοιγμα αρχείου GB ROM"
msgid ""
"Game Boy Color Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "Αρχεία Game Boy Color (*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:198
msgid "Open GBC ROM file"
@@ -65,7 +66,7 @@ msgstr "Επιλογή αρχείου Dot Code"
#: cmdevents.cpp:365 cmdevents.cpp:387
msgid "E-Reader Dot Code (*.bin;*.raw)|*.bin;*.raw"
msgstr ""
msgstr "Κώδικας κουκίδας E-Reader (*.bin;*.raw)|*.bin;*.raw"
#: cmdevents.cpp:406 cmdevents.cpp:601
msgid "Select battery file"
@@ -79,7 +80,7 @@ msgstr "Αρχείο μπαταρίας (*.sav)|*.sav|Αποθήκευση Flash
msgid ""
"Importing a battery file will erase any saved games (permanently after the "
"next write). Do you want to continue?"
msgstr ""
msgstr "Η εισαγωγή ενός αρχείου μπαταρίας θα διαγράψει το ήδη αποθηκευμένο παιχνίδι (μόνιμα μετά την επόμενη εγγραφή). Θέλετε να προχωρήσετε;"
#: cmdevents.cpp:416 cmdevents.cpp:444 cmdevents.cpp:564
msgid "Confirm import"
@@ -101,17 +102,17 @@ msgstr "Επιλογή αρχείου κωδικών"
#: cmdevents.cpp:434
msgid "Game Shark Code File (*.spc;*.xpc)|*.spc;*.xpc"
msgstr ""
msgstr "Αρχείο κωδικών Game Shark (*.spc;*.xpc)|*.spc;*.xpc"
#: cmdevents.cpp:434
msgid "Game Shark Code File (*.gcf)|*.gcf"
msgstr ""
msgstr "Αρχείο κωδικών Game Shark (*.gcf)|*.gcf"
#: cmdevents.cpp:443
msgid ""
"Importing a code file will replace any loaded cheats. Do you want to "
"continue?"
msgstr ""
msgstr "Η εισαγωγή αρχείου κωδικών θα αντικαταστήσει τυχόν φορτωμένους κωδικούς. Θέλετε να συνεχίσετε;"
#: cmdevents.cpp:460
#, c-format
@@ -141,17 +142,17 @@ msgstr "Επιλογή αρχείου στιγμιότυπου"
msgid ""
"Game Shark & PAC Snapshots (*.sps;*.xps)|*.sps;*.xps|Game Shark SP Snapshots"
" (*.gsv)|*.gsv"
msgstr ""
msgstr "Στιγμιότυπα Game Shark & PAC (*.sps;*.xps)|*.sps;*.xps|Στιγμιότυπα Game Shark SP (*.gsv)|*.gsv"
#: cmdevents.cpp:554
msgid "Game Boy Snapshot (*.gbs)|*.gbs"
msgstr ""
msgstr "Στιγμιότυπο Game Boy (*.gbs)|*.gbs"
#: cmdevents.cpp:563
msgid ""
"Importing a snapshot file will erase any saved games (permanently after the "
"next write). Do you want to continue?"
msgstr ""
msgstr "Η εισαγωγή ενός στιγμιοτύπου θα διαγράψει τυχόν αποθηκευμένα παιχνίδια (μόνιμα μετά την επόμενη εγγραφή). Θέλετε να προχωρήσετε;"
#: cmdevents.cpp:588
#, c-format
@@ -179,11 +180,11 @@ msgstr "Δεν είναι δυνατή η εξαγωγή αρχείων αποθ
#: cmdevents.cpp:630
msgid "Game Shark Snapshot (*.sps)|*.sps"
msgstr ""
msgstr "Στιγμιότυπο Game Shark (*.sps)|*.sps"
#: cmdevents.cpp:644
msgid "Exported from Visual Boy Advance-M"
msgstr ""
msgstr "Εξαγωγή από το Visual Boy Advance-M"
#: cmdevents.cpp:656
#, c-format
@@ -224,16 +225,16 @@ msgstr "Επιλογή αρχείου κατάστασης"
#: cmdevents.cpp:1263 cmdevents.cpp:1356
msgid "Visual Boy Advance saved game files|*.sgm"
msgstr ""
msgstr "Αποθηκευμένα αρχεία παιχνιδιού Visual Boy Advance|*.sgm"
#: cmdevents.cpp:1386 cmdevents.cpp:1396 cmdevents.cpp:1407
#, c-format
msgid "Current state slot #%d"
msgstr ""
msgstr "Τρέχουσα θέση στιγμιοτύπου #%d"
#: cmdevents.cpp:1470
msgid "Cannot use Colorizer Hack when Game Boy BIOS File is enabled."
msgstr ""
msgstr "Δεν μπορεί να χρησιμοποιηθεί το Colorizer Hack όσο είναι ενεργοποιημένο το αρχείο BIOS του Game Boy "
#: cmdevents.cpp:1681
msgid "Sound enabled"
@@ -274,22 +275,22 @@ msgid ""
"YOUR CONFIGURATION WILL BE DELETED!\n"
"\n"
"Are you sure?"
msgstr ""
msgstr "ΟΙ ΡΥΘΜΙΣΕΙΣ ΣΑΣ ΘΑ ΔΙΑΓΡΑΦΟΥΝ!"
#: cmdevents.cpp:2201
msgid "FACTORY RESET"
msgstr ""
msgstr "ΕΡΓΟΣΤΑΣΙΑΚΕΣ ΡΥΘΜΙΣΕΙΣ"
#: cmdevents.cpp:2236
msgid "Nintendo Game Boy / Color / Advance emulator."
msgstr ""
msgstr "Εξομοιωτής Nintendo Game Boy / Color / Advance."
#: cmdevents.cpp:2237
msgid ""
"Copyright (C) 1999-2003 Forgotten\n"
"Copyright (C) 2004-2006 VBA development team\n"
"Copyright (C) 2007-2020 VBA-M development team"
msgstr ""
msgstr "Πνευματική Ιδιοκτησία (C) 1999-2003 Ξεχασμένη Πνευματική Ιδιοκτησία (C) 2004-2006 VBA Ομάδα ανάπτυξης VBA Πνευματική Ιδιοκτησία (C) 2007-2020 Ομάδα ανάπτυξης VBA-M"
#: cmdevents.cpp:2239
msgid ""

View File

@@ -5,6 +5,7 @@
# Translators:
# Alain Richell Rodriguez Blazquez, 2021
# Álvaro Pérez Urbano <alvaro_beta@hotmail.com>, 2021
# angel quispe, 2025
# Damián Cabello Jiménez <jdamiancabello@gmail.com>, 2018
# Dani Quiroz, 2022
# Dani Quiroz, 2022
@@ -15,6 +16,7 @@
# javidg96, 2015
# jorge perez <theghostxxaz@gmail.com>, 2021
# Jose Castillo <josecastillo.prz85@gmail.com>, 2021
# José Montoro, 2024
# Manu 3L <comokaka777@gmail.com>, 2021
# Marco123V <MarcoVillalejos@hotmail.com>, 2015
# Marco Yurazeck <luthias@gmail.com>, 2020
@@ -29,7 +31,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-05 20:00+0000\n"
"PO-Revision-Date: 2011-12-03 19:42+0000\n"
"Last-Translator: Dani Quiroz, 2022\n"
"Last-Translator: angel quispe, 2025\n"
"Language-Team: Spanish (http://app.transifex.com/bgk/vba-m/language/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -39,7 +41,7 @@ msgstr ""
#: audio/internal/openal.cpp:188
msgid "OpenAL: Failed to open audio device"
msgstr ""
msgstr "OpenAL: Fallo al abrir el dispositivo de audio"
#: audio/internal/openal.cpp:386 audio/internal/faudio.cpp:492
#: audio/internal/xaudio2.cpp:577
@@ -52,7 +54,7 @@ msgid ""
"(*.agb;*.gba;*.bin;*.elf;*.mb;*.zip;*.7z;*.rar)|*.agb;*.gba;*.bin;*.elf;*.mb;*.agb.gz;*.gba.gz;*.bin.gz;*.elf.gz;*.mb.gz;*.agb.z;*.gba.z;*.bin.z;*.elf.z;*.mb.z;*.zip;*.7z;*.rar|Game"
" Boy Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "Archivos de Game boy Advance (*.agb;*.gba;*.bin;*.elf;*.mb;*.zip;*.7z;*.rar)|*.agb;*.gba;*.bin;*.elf;*.mb;*.agb.gz;*.gba.gz;*.bin.gz;*.elf.gz;*.mb.gz;*.agb.z;*.gba.z;*.bin.z;*.elf.z;*.mb.z;*.zip;*.7z;*.rar|Game Boy Files (*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:144
msgid "Open ROM file"
@@ -62,7 +64,7 @@ msgstr "Abrir archivo ROM"
msgid ""
"Game Boy Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "Archivos de Game Boy (*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:171
msgid "Open GB ROM file"
@@ -98,7 +100,7 @@ msgstr "Archivo de batería (*.sav) | *.sav | Guardado Flash (*.dat) | *.dat"
msgid ""
"Importing a battery file will erase any saved games (permanently after the "
"next write). Do you want to continue?"
msgstr ""
msgstr "Importar un archivo de guardado borrará cualquier partida guardada (permanente después del siguiente guardado). ¿Quieres continuar?"
#: cmdevents.cpp:416 cmdevents.cpp:444 cmdevents.cpp:564
msgid "Confirm import"
@@ -130,7 +132,7 @@ msgstr ""
msgid ""
"Importing a code file will replace any loaded cheats. Do you want to "
"continue?"
msgstr ""
msgstr "Importar un archivo de códigos reemplazará cualquier cheat cargado. ¿Quieres continuar?"
#: cmdevents.cpp:460
#, c-format
@@ -170,7 +172,7 @@ msgstr ""
msgid ""
"Importing a snapshot file will erase any saved games (permanently after the "
"next write). Do you want to continue?"
msgstr ""
msgstr "Importar una snapshot borrará cualquier partida guardada (permanente una vez se sobrescriba) ¿Quieres continuar?"
#: cmdevents.cpp:588
#, c-format
@@ -202,7 +204,7 @@ msgstr ""
#: cmdevents.cpp:644
msgid "Exported from Visual Boy Advance-M"
msgstr ""
msgstr "Exportado desde Visual Boy Advance-M"
#: cmdevents.cpp:656
#, c-format
@@ -293,15 +295,15 @@ msgid ""
"YOUR CONFIGURATION WILL BE DELETED!\n"
"\n"
"Are you sure?"
msgstr ""
msgstr "¡SE BORRARÁ TU CONFIGURACIÓN!\n\n¿Estás seguro?"
#: cmdevents.cpp:2201
msgid "FACTORY RESET"
msgstr ""
msgstr "RESETEO DE FÁBRICA"
#: cmdevents.cpp:2236
msgid "Nintendo Game Boy / Color / Advance emulator."
msgstr ""
msgstr "Emulador de Nintendo Game Boy / Color / Advance"
#: cmdevents.cpp:2237
msgid ""
@@ -324,11 +326,11 @@ msgid ""
"\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program. If not, see http://www.gnu.org/licenses ."
msgstr ""
msgstr "Este programa es un software gratis: puedes redistribuirlo / o modificarlo bajo los terminos de la Licencia Pública General GNU.\npublicada por\nla Free Software Foundation, ya sea la versión 2 de la Licencia, o\n(a su elección) cualquier versión posterior.\n\nEste programa se distribuye con la esperanza de que sea útil,\npero SIN NINGUNA GARANTÍA; sin siquiera la garantía implícita de\nCOMERCIABILIDAD o IDONEIDAD PARA UN PROPÓSITO PARTICULAR. Ver el\nLicencia pública general GNU para obtener más detalles.\n\nDebería haber recibido una copia de la Licencia Pública General GNU\njunto con este programa. Si no, consulte http://www.gnu.org/licenses."
#: cmdevents.cpp:2424
msgid "Cannot use Game Boy BIOS when Colorizer Hack is enabled."
msgstr ""
msgstr "No puedes usar la BIOS de la Game Boy cuando el Hack Colorizer está habilitado."
#: cmdevents.cpp:2490
msgid "LAN link is already active. Disable link mode to disconnect."
@@ -344,7 +346,7 @@ msgstr "Menú de comandos"
#: dialogs/accel-config.cpp:245
msgid "This will clear all user-defined accelerators. Are you sure?"
msgstr ""
msgstr "Esto borrará todos los aceleradores definidos por el usuario. ¿Estás seguro?"
#: dialogs/accel-config.cpp:245 dialogs/accel-config.cpp:292
msgid "Confirm"
@@ -401,7 +403,7 @@ msgstr "Desconocido"
#: dialogs/game-maker.cpp:235
msgid "Invalid"
msgstr ""
msgstr "No válido "
#: dialogs/gb-rom-info.cpp:22
msgid "No mapper"

View File

@@ -3,15 +3,18 @@
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
# Andrés Oña, 2025
# chxndler perez cueva <chxndlerpc@gmail.com>, 2018
# Dani Quiroz, 2022
# David Martínez <david8dgm@gmail.com>, 2020
# Dex Galaxy, 2022
# Dilan Meza, 2025
# Enzo Cortés, 2023
# FERNANDO AYALA PEREZ, 2021
# Kevin Bustinza <ufreshx@gmail.com>, 2021
# MELERIX, 2023
# Sebastián Castro Saldarriaga, 2023
# Timoteo Traiber Minnaard, 2024
# Yeferson Galviz, 2023
# yocsan Cañderón <yecoyocsan@gmail.com>, 2021
msgid ""
@@ -20,7 +23,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-05 20:00+0000\n"
"PO-Revision-Date: 2011-12-03 19:42+0000\n"
"Last-Translator: Yeferson Galviz, 2023\n"
"Last-Translator: Dilan Meza, 2025\n"
"Language-Team: Spanish (Latin America) (http://app.transifex.com/bgk/vba-m/language/es_419/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -30,12 +33,12 @@ msgstr ""
#: audio/internal/openal.cpp:188
msgid "OpenAL: Failed to open audio device"
msgstr ""
msgstr "OpenAL: No se pudo abrir el dispositivo de audio"
#: audio/internal/openal.cpp:386 audio/internal/faudio.cpp:492
#: audio/internal/xaudio2.cpp:577
msgid "Default device"
msgstr "Default device"
msgstr "Dispositivo predeterminado"
#: cmdevents.cpp:133
msgid ""
@@ -47,7 +50,7 @@ msgstr "Archivos Game Boy Advance (*.agb;*.gba;*.bin;*.elf;*.mb;*.zip;*.7z;*.rar
#: cmdevents.cpp:144
msgid "Open ROM file"
msgstr "Open ROM file"
msgstr "Abrir archivo ROM"
#: cmdevents.cpp:165
msgid ""
@@ -75,7 +78,7 @@ msgstr "Seleccionar archivo de código de punto"
#: cmdevents.cpp:365 cmdevents.cpp:387
msgid "E-Reader Dot Code (*.bin;*.raw)|*.bin;*.raw"
msgstr ""
msgstr "Código de puntos del lector electrónico (*.bin;*.raw)|*.bin;*.raw"
#: cmdevents.cpp:406 cmdevents.cpp:601
msgid "Select battery file"
@@ -344,7 +347,7 @@ msgstr "Confirm"
#: dialogs/accel-config.cpp:290
#, c-format
msgid "This will unassign \"%s\" from \"%s\". Are you sure?"
msgstr ""
msgstr "Esto desasignará \"1%s\" de \"1%s\". ¿Estás seguro?"
#: dialogs/directories-config.cpp:49
msgid "Browse"
@@ -352,7 +355,7 @@ msgstr "Browse"
#: dialogs/display-config.cpp:52
msgid "Invalid value for Default magnification."
msgstr ""
msgstr "Valor no válido para ampliación predeterminada."
#: dialogs/display-config.cpp:331 xrc/DisplayConfig.xrc:86
#: xrc/DisplayConfig.xrc:136 xrc/DisplayConfig.xrc:222
@@ -374,7 +377,7 @@ msgstr "Plugin"
#: dialogs/display-config.cpp:407
#, c-format
msgid "Using pixel filter: %s"
msgstr ""
msgstr "Usando filtro de píxeles: 1%s"
#: dialogs/display-config.cpp:414
#, c-format
@@ -392,35 +395,35 @@ msgstr "Unknown"
#: dialogs/game-maker.cpp:235
msgid "Invalid"
msgstr ""
msgstr "Inválido"
#: dialogs/gb-rom-info.cpp:22
msgid "No mapper"
msgstr ""
msgstr "Sin mapeador"
#: dialogs/gb-rom-info.cpp:43
msgid "Pocket Camera"
msgstr ""
msgstr "Cámara de bolsillo"
#: dialogs/gb-rom-info.cpp:68
msgid " + RAM"
msgstr ""
msgstr "+ RAM"
#: dialogs/gb-rom-info.cpp:69
msgid " + RTC"
msgstr ""
msgstr "+RTC"
#: dialogs/gb-rom-info.cpp:70
msgid " + Battery"
msgstr ""
msgstr "+ Batería"
#: dialogs/gb-rom-info.cpp:71
msgid " + Rumble"
msgstr ""
msgstr "+ Rumble"
#: dialogs/gb-rom-info.cpp:72
msgid " + Motion Sensor"
msgstr ""
msgstr "+ Sensor de Movimiento"
#: dialogs/gb-rom-info.cpp:74
#, c-format
@@ -486,7 +489,7 @@ msgstr ""
#: dialogs/gb-rom-info.cpp:159
#, c-format
msgid "%02X (Unknown)"
msgstr ""
msgstr "%02X (Desconocido)"
#: dialogs/gb-rom-info.cpp:131
#, c-format
@@ -516,7 +519,7 @@ msgstr ""
#: dialogs/gb-rom-info.cpp:155
#, c-format
msgid "%02X (Japan)"
msgstr ""
msgstr "%02X (Japón)"
#: dialogs/gb-rom-info.cpp:157
#, c-format
@@ -568,7 +571,7 @@ msgstr "Aceptar"
#: gfxviewers.cpp:1202
msgid "Select output file and type"
msgstr "Select output file and type"
msgstr "Seleccionar archivo de salida y tipo"
#: gfxviewers.cpp:1203
msgid ""
@@ -582,15 +585,15 @@ msgstr "Start!"
#: guiinit.cpp:110
msgid "Connect"
msgstr "Connect"
msgstr "Conectar"
#: guiinit.cpp:127
msgid "You must enter a valid host name"
msgstr "You must enter a valid host name"
msgstr "Debe introducir un nombre de host válido"
#: guiinit.cpp:128
msgid "Host name invalid"
msgstr "Host name invalid"
msgstr "Nombre de host inválido"
#: guiinit.cpp:146
msgid "Waiting for clients..."
@@ -670,15 +673,15 @@ msgstr "32-bit "
#: guiinit.cpp:1057
msgid "Signed decimal"
msgstr ""
msgstr "decimal con signo"
#: guiinit.cpp:1061
msgid "Unsigned decimal"
msgstr ""
msgstr "decimal sin signo"
#: guiinit.cpp:1065
msgid "Unsigned hexadecimal"
msgstr ""
msgstr "hexadecimal sin signo"
#: guiinit.cpp:1637
msgid "Main icon not found"
@@ -881,11 +884,11 @@ msgstr "Sin VSYNC disponible en esta plataforma"
#: panel.cpp:2444
msgid "Memory allocation error"
msgstr ""
msgstr "error en la asignación de memoria"
#: panel.cpp:2447
msgid "Error initializing codec"
msgstr ""
msgstr "error al inicializar codec"
#: panel.cpp:2450
msgid "Error writing to output file"
@@ -1008,7 +1011,7 @@ msgstr "Select memory dump file"
#: viewsupt.cpp:789
msgid "Red:"
msgstr ""
msgstr "Rojo:"
#: viewsupt.cpp:798
msgid "Green:"
@@ -3460,7 +3463,7 @@ msgstr "&R"
#: xrc/MainMenu.xrc:494 xrc/MainMenu.xrc:517
msgid "Configure..."
msgstr ""
msgstr "Configurar..."
#: xrc/MainMenu.xrc:498
msgid "&Real-time clock"
@@ -3480,15 +3483,15 @@ msgstr "&LCD Filter"
#: xrc/MainMenu.xrc:521
msgid "&Game Boy color option"
msgstr ""
msgstr "Opción de color Game Boy"
#: xrc/MainMenu.xrc:529
msgid "&Game Boy Colorizer Hack (requires restart)"
msgstr ""
msgstr "Hack de color Game Boy (requiere reinicio)"
#: xrc/MainMenu.xrc:533
msgid "&Game Boy printer"
msgstr ""
msgstr "Impresora Game Boy"
#: xrc/MainMenu.xrc:537
msgid "&Gather a full page before printing"
@@ -3500,43 +3503,43 @@ msgstr "&Save printouts as screen captures"
#: xrc/MainMenu.xrc:546
msgid "&Use Game Boy BIOS file (requires restart)"
msgstr ""
msgstr "Usar archivo BIOS de Game Boy (requiere reinicio)"
#: xrc/MainMenu.xrc:550
msgid "&Use Game Boy Color BIOS file"
msgstr ""
msgstr "Usar archivo BIOS de Game Boy Color"
#: xrc/MainMenu.xrc:555
msgid "&General..."
msgstr ""
msgstr "General"
#: xrc/MainMenu.xrc:558
msgid "&Speedup / Turbo..."
msgstr ""
msgstr "Aceleración / Turbo"
#: xrc/MainMenu.xrc:561
msgid "D&irectories..."
msgstr ""
msgstr "Directorios"
#: xrc/MainMenu.xrc:564
msgid "&Key Shortcuts..."
msgstr ""
msgstr "Atajos de teclas"
#: xrc/MainMenu.xrc:567
msgid "UI Settings"
msgstr ""
msgstr "Configuración de UI"
#: xrc/MainMenu.xrc:569
msgid "Enable &Status bar"
msgstr ""
msgstr "Habilitar barra de estado"
#: xrc/MainMenu.xrc:573
msgid "Hide &Menu Bar"
msgstr ""
msgstr "Ocultar barra de menú"
#: xrc/MainMenu.xrc:577
msgid "Suspend &Screen Saver"
msgstr ""
msgstr "Suspender y proteger pantalla"
#: xrc/MainMenu.xrc:583
msgid "&Tools"
@@ -3548,15 +3551,15 @@ msgstr "&Cheats"
#: xrc/MainMenu.xrc:587
msgid "List &cheats..."
msgstr ""
msgstr "Lista y trucos"
#: xrc/MainMenu.xrc:590
msgid "Find c&heat..."
msgstr ""
msgstr "Encontrar truco"
#: xrc/MainMenu.xrc:594
msgid "A&utomatically save / load cheats"
msgstr ""
msgstr "Guardar / cargar trucos automáticamente"
#: xrc/MainMenu.xrc:598
msgid "&Enable cheats"
@@ -3656,7 +3659,7 @@ msgstr "Report &Bugs"
#: xrc/MainMenu.xrc:733
msgid "Visual Boy Advance-M Support &Forum"
msgstr ""
msgstr "Soporte y foro de Visual Boy Advance-M"
#: xrc/MainMenu.xrc:736
msgid "Translations"
@@ -3744,7 +3747,7 @@ msgstr "Start Network Link"
#: xrc/NetLink.xrc:13
msgid "WARNING: Link will likely not work over the internet or LAN."
msgstr ""
msgstr "ADVERTENCIA: Es probable que el enlace no funcione a través de Internet o LAN."
#: xrc/NetLink.xrc:38
msgid "Server"
@@ -3760,7 +3763,7 @@ msgstr "Players:"
#: xrc/NetLink.xrc:83
msgid "2"
msgstr ""
msgstr "2"
#: xrc/NetLink.xrc:107
msgid "Server:"
@@ -3792,7 +3795,7 @@ msgstr "Sound Settings"
#: xrc/SoundConfig.xrc:18
msgid "Volume:"
msgstr ""
msgstr "Volumen:"
#: xrc/SoundConfig.xrc:36
msgid "Mute"
@@ -3804,7 +3807,7 @@ msgstr "Maximum"
#: xrc/SoundConfig.xrc:73
msgid "Sample rate:"
msgstr ""
msgstr "Frecuencia de muestreo:"
#: xrc/SoundConfig.xrc:79
msgid "48 KHz"
@@ -3824,7 +3827,7 @@ msgstr "11 KHz"
#: xrc/SoundConfig.xrc:112
msgid "Direct Sound"
msgstr ""
msgstr "Sonido directo"
#: xrc/SoundConfig.xrc:140
msgid "Device"
@@ -3864,7 +3867,7 @@ msgstr "Center"
#: xrc/SoundConfig.xrc:263
msgid "Left / Right"
msgstr ""
msgstr "Izquierda / Derecha"
#: xrc/SoundConfig.xrc:292
msgid "Sound filtering"
@@ -3892,4 +3895,4 @@ msgstr "Frame skip"
#: xrc/SpeedupConfig.xrc:61
msgid "Mute Sound"
msgstr ""
msgstr "Silenciar sonido"

View File

@@ -7,7 +7,7 @@
# Chizuru <saiber.one1@gmail.com>, 2015
# Dimas Radityo, 2022
# Jeremy Belpois <jeremy.belpois.einstein@gmail.com>, 2019
# heydootdoot last, 2024
# heydootdoot last, 2024-2025
# Jeremy Belpois <jeremy.belpois.einstein@gmail.com>, 2019-2020
msgid ""
msgstr ""
@@ -15,7 +15,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-05 20:00+0000\n"
"PO-Revision-Date: 2011-12-03 19:42+0000\n"
"Last-Translator: heydootdoot last, 2024\n"
"Last-Translator: heydootdoot last, 2024-2025\n"
"Language-Team: Indonesian (http://app.transifex.com/bgk/vba-m/language/id/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -146,11 +146,11 @@ msgstr "Pilih snapshot"
msgid ""
"Game Shark & PAC Snapshots (*.sps;*.xps)|*.sps;*.xps|Game Shark SP Snapshots"
" (*.gsv)|*.gsv"
msgstr ""
msgstr "Snapshot Game Shark & PAC (*.sps;*.xps)|*.sps;*.xps|Snapshot Game Shark SP (*.gsv)|*.gsv"
#: cmdevents.cpp:554
msgid "Game Boy Snapshot (*.gbs)|*.gbs"
msgstr ""
msgstr "Snapshot Game Boy (*.gbs)|*.gbs"
#: cmdevents.cpp:563
msgid ""
@@ -171,7 +171,7 @@ msgstr "Galat memuat snapshot %s"
#: cmdevents.cpp:613
#, c-format
msgid "Wrote battery %s"
msgstr "Menyimpan baterai %s"
msgstr "Simpan baterai %s"
#: cmdevents.cpp:615 panel.cpp:803
#, c-format
@@ -184,11 +184,11 @@ msgstr "Simpanan EEPROM tak dapat diekspor"
#: cmdevents.cpp:630
msgid "Game Shark Snapshot (*.sps)|*.sps"
msgstr ""
msgstr "Snapshot Game Shark (*.sps)|*.sps"
#: cmdevents.cpp:644
msgid "Exported from Visual Boy Advance-M"
msgstr ""
msgstr "Diekspor dari Visual Boy Advance-M"
#: cmdevents.cpp:656
#, c-format
@@ -213,7 +213,7 @@ msgstr "Gambar PNG|*.png|Gambar BMP|*.bmp"
#: cmdevents.cpp:699 sys.cpp:579
#, c-format
msgid "Wrote snapshot %s"
msgstr "Menyimpan snapshot %s"
msgstr "Simpan snapshot %s"
#: cmdevents.cpp:720 cmdevents.cpp:790 cmdevents.cpp:859 cmdevents.cpp:925
msgid " files ("
@@ -225,7 +225,7 @@ msgstr "Pilih berkas"
#: cmdevents.cpp:1262 cmdevents.cpp:1355
msgid "Select state file"
msgstr "Pilih berkas state"
msgstr "Pilih berkas savestate"
#: cmdevents.cpp:1263 cmdevents.cpp:1356
msgid "Visual Boy Advance saved game files|*.sgm"
@@ -234,7 +234,7 @@ msgstr "Simpanan permainan Visual Boy Advance|*.sgm"
#: cmdevents.cpp:1386 cmdevents.cpp:1396 cmdevents.cpp:1407
#, c-format
msgid "Current state slot #%d"
msgstr "Slot keadaan saat ini #%d"
msgstr "Slot savestate saat ini #%d"
#: cmdevents.cpp:1470
msgid "Cannot use Colorizer Hack when Game Boy BIOS File is enabled."
@@ -776,22 +776,22 @@ msgstr "pemain"
#: panel.cpp:751
#, c-format
msgid "Loaded state %s"
msgstr "State yang dimuat %s"
msgstr "Savestate yang dimuat %s"
#: panel.cpp:751
#, c-format
msgid "Error loading state %s"
msgstr "Terjadi kesalahan dalam memuat state %s"
msgstr "Galat memuat savestate %s"
#: panel.cpp:775
#, c-format
msgid "Saved state %s"
msgstr "State yang disimpan %s"
msgstr "Savestate yang disimpan %s"
#: panel.cpp:775
#, c-format
msgid "Error saving state %s"
msgstr "Terjadi kesalahan dalam menyimpan state %s"
msgstr "Galat menyimpan savestate %s"
#: panel.cpp:979
#, c-format
@@ -828,7 +828,7 @@ msgstr "Tidak ada memori untuk memuat state mundur"
#: panel.cpp:1300
msgid "Error writing rewind state"
msgstr "Terjadi kesalahan dalam menyimpan state mundur"
msgstr "Galat menulis savestate mundur"
#: panel.cpp:2277
msgid "Enabling EGL VSync."
@@ -1049,7 +1049,7 @@ msgstr "Atur berkas konfigurasi"
#: wxvbam.cpp:638
msgid "Delete shared link state first, if it exists"
msgstr "Hapus state hubungan yang dibagikan terlebih dahulu, jika ada"
msgstr "Hapus dulu tautan savestate, jika adaa"
#: wxvbam.cpp:645
msgid "List all settable options and exit"
@@ -1072,7 +1072,7 @@ msgstr "Galat bina / konfigurasi: Tak dapat cari xrc tertanam"
msgid ""
"Wrote built-in configuration to %s.\n"
"To override, remove all but changed root node(s). First found root node of correct name in any .xrc or .xrs files in following search path overrides built-in:"
msgstr ""
msgstr "Menyimpan konfig tertanam ke %s.\nUntuk timpa berkas, hapus semua node root kecuali yang terubah. Cari dulu node root yang benar pada berkas .xrc / .xrs di pencarian berkas timpaan tertanam berikut:"
#: wxvbam.cpp:703
msgid "Configuration is read from, in order:"
@@ -1083,7 +1083,7 @@ msgstr "Konfigurasi dibaca dari, secara berurutan:"
msgid ""
"Wrote built-in override file to %s\n"
"To override, delete all but changed section. First found section is used from search path:"
msgstr ""
msgstr "Menyimpan berkas timpaan ke %s\nUntuk timpa berkas, hapus semua bagian kecuali yang terubah. Cari dulu bagian yang digunakan dari pencarian:"
#: wxvbam.cpp:723
msgid ""
@@ -1342,7 +1342,7 @@ msgstr ""
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:445
msgid "Automatically load last saved state"
msgstr ""
msgstr "Otomatis memuat terakhir savestate"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:447
msgid ""
@@ -1507,11 +1507,11 @@ msgstr ""
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:522
msgid "Do not overwrite cheat list when loading state"
msgstr ""
msgstr "Jangan timpa daftar cheat saat memuat savestate"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:524
msgid "Do not overwrite native (battery) save when loading state"
msgstr ""
msgstr "Jangan timpa simpanan lokal (baterai) saat memuat savestate"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:526
msgid "Throttle game speed, even when accelerated (0-450 %, 0 = no throttle)"
@@ -3078,7 +3078,7 @@ msgstr "Ter&baru"
#: xrc/MainMenu.xrc:50
msgid "Load current state slot"
msgstr "Muat slot keadaan saat ini"
msgstr "Muat savestate saat ini"
#: xrc/MainMenu.xrc:53
msgid "&Auto load most recent"
@@ -3098,7 +3098,7 @@ msgstr "Jangan ubah daftar &cheat"
#: xrc/MainMenu.xrc:100
msgid "&Load state"
msgstr "&Muat state"
msgstr "&Muat savestate"
#: xrc/MainMenu.xrc:104
msgid "&Oldest slot"
@@ -3106,11 +3106,11 @@ msgstr "&Slot terlama"
#: xrc/MainMenu.xrc:107
msgid "Save current state slot"
msgstr "Simpan slot keadaan saat ini"
msgstr "Simpan savestate saat ini"
#: xrc/MainMenu.xrc:110
msgid "Increase state slot number and save"
msgstr "Tambah jumlah slot keadaan lalu simpan"
msgstr "Tambah jumlah savestate lalu simpan"
#: xrc/MainMenu.xrc:117
msgid "&2"
@@ -3154,15 +3154,15 @@ msgstr ""
#: xrc/MainMenu.xrc:147
msgid "&Save state"
msgstr "&Simpan state"
msgstr "&Simpan savestate"
#: xrc/MainMenu.xrc:150
msgid "Increase state slot number"
msgstr "Tambah jumlah slot keadaan"
msgstr "Tambah jumlah savestate"
#: xrc/MainMenu.xrc:153
msgid "Decrease state slot number"
msgstr "Kurangi jumlah slot keadaan"
msgstr "Kurangi jumlah ssavestate"
#: xrc/MainMenu.xrc:158 xrc/MainMenu.xrc:170
msgid "&Battery file..."

View File

@@ -13,6 +13,7 @@
# Giulio Serroni, 2022
# JustAnOrange, 2023
# Luca Bonello <fenopiu@gmail.com>, 2016
# ludo thorn, 2025
# Michele Rebughini <michelerebughini@gmail.com>, 2020
# OTTO X <blg.dpnx@gmail.com>, 2015
# Picat <pichu474android@gmail.com>, 2016
@@ -26,7 +27,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-05 20:00+0000\n"
"PO-Revision-Date: 2011-12-03 19:42+0000\n"
"Last-Translator: JustAnOrange, 2023\n"
"Last-Translator: ludo thorn, 2025\n"
"Language-Team: Italian (Italy) (http://app.transifex.com/bgk/vba-m/language/it_IT/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -2715,7 +2716,7 @@ msgstr "Impostazioni Game Boy Advance"
#: xrc/GameBoyAdvanceConfig.xrc:21
msgid "Save type:"
msgstr ""
msgstr "Tipo di salvataggio"
#: xrc/GameBoyAdvanceConfig.xrc:27 xrc/GameBoyAdvanceConfig.xrc:199
#: xrc/GameBoyConfig.xrc:29 xrc/GameBoyConfig.xrc:59
@@ -2764,7 +2765,7 @@ msgstr ""
#: xrc/GameBoyAdvanceConfig.xrc:115
msgid "Current BIOS file:"
msgstr ""
msgstr "File BIOS attuale:"
#: xrc/GameBoyAdvanceConfig.xrc:136 xrc/GameBoyConfig.xrc:168
msgid "Boot ROM"
@@ -2818,7 +2819,7 @@ msgstr "Impostazioni speciali per questo gioco"
#: xrc/GameBoyConfig.xrc:4
msgid "Game Boy settings"
msgstr ""
msgstr "Impostazioni Game Boy"
#: xrc/GameBoyConfig.xrc:20
msgid "Emulated &system:"
@@ -3330,7 +3331,7 @@ msgstr "&Speed hack"
#: xrc/MainMenu.xrc:310 xrc/MainMenu.xrc:316 xrc/MainMenu.xrc:383
#: xrc/MainMenu.xrc:417
msgid "&Configure..."
msgstr ""
msgstr "&Configura..."
#: xrc/MainMenu.xrc:314
msgid "&Video"
@@ -3414,7 +3415,7 @@ msgstr "&Attiva suono"
#: xrc/MainMenu.xrc:397
msgid "&Game Boy Advance sound interpolation"
msgstr ""
msgstr "&Game Boy Advance interpolazione suono"
#: xrc/MainMenu.xrc:402
msgid "&Game Boy sound enhancement"

View File

@@ -7,6 +7,7 @@
# Giovanni Schiano-Moriello, 2022
# Hinaloe <hina@hinaloe.net>, 2017
# Kuso Bully <aaadsaaads@yahoo.co.jp>, 2018
# Lanta Liz, 2025
# Mach555 <aimmasayukitone@yahoo.co.jp>, 2015
# Mutuda Katuo <afetser@gmail.com>, 2017
# Okubo Shohei <stream.larn3@gmail.com>, 2021
@@ -18,7 +19,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-05 20:00+0000\n"
"PO-Revision-Date: 2011-12-03 19:42+0000\n"
"Last-Translator: 田中康陽, 2022\n"
"Last-Translator: Lanta Liz, 2025\n"
"Language-Team: Japanese (http://app.transifex.com/bgk/vba-m/language/ja/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -28,7 +29,7 @@ msgstr ""
#: audio/internal/openal.cpp:188
msgid "OpenAL: Failed to open audio device"
msgstr ""
msgstr "OpenAL: オーディオデバイスを開けませんでした"
#: audio/internal/openal.cpp:386 audio/internal/faudio.cpp:492
#: audio/internal/xaudio2.cpp:577
@@ -41,7 +42,7 @@ msgid ""
"(*.agb;*.gba;*.bin;*.elf;*.mb;*.zip;*.7z;*.rar)|*.agb;*.gba;*.bin;*.elf;*.mb;*.agb.gz;*.gba.gz;*.bin.gz;*.elf.gz;*.mb.gz;*.agb.z;*.gba.z;*.bin.z;*.elf.z;*.mb.z;*.zip;*.7z;*.rar|Game"
" Boy Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "ゲームボーイアドバンスのファイル\n(*.agb;*.gba;*.bin;*.elf;*.mb;*.zip;*.7z;*.rar)|*.agb;*.gba;*.bin;*.elf;*.mb;*.agb.gz;*.gba.gz;*.bin.gz;*.elf.gz;*.mb.gz;*.agb.z;*.gba.z;*.bin.z;*.elf.z;*.mb.z;*.zip;*.7z;*.rar|Game Boy Files (*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:144
msgid "Open ROM file"
@@ -51,7 +52,7 @@ msgstr "ROMファイルを開く"
msgid ""
"Game Boy Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "ゲームボーイのファイル\n(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:171
msgid "Open GB ROM file"
@@ -61,7 +62,7 @@ msgstr "ゲームボーイのROMファイルを開く"
msgid ""
"Game Boy Color Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "ゲームボーイカラーのファイル(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:198
msgid "Open GBC ROM file"
@@ -73,7 +74,7 @@ msgstr "ドットコードファイルを選択"
#: cmdevents.cpp:365 cmdevents.cpp:387
msgid "E-Reader Dot Code (*.bin;*.raw)|*.bin;*.raw"
msgstr ""
msgstr "E-Reader Dot Code (*.bin;*.raw)|*.bin;*.raw"
#: cmdevents.cpp:406 cmdevents.cpp:601
msgid "Select battery file"
@@ -87,7 +88,7 @@ msgstr "バッテリーファイル (*.sav) | *. sav|フラッシュセーブフ
msgid ""
"Importing a battery file will erase any saved games (permanently after the "
"next write). Do you want to continue?"
msgstr ""
msgstr "バッテリー ファイルを読み込むと、保存したゲームがすべて消去されます (次回の書き込み後、完全に消去されます)続行しますか?"
#: cmdevents.cpp:416 cmdevents.cpp:444 cmdevents.cpp:564
msgid "Confirm import"
@@ -109,17 +110,17 @@ msgstr "コードファイルを選択"
#: cmdevents.cpp:434
msgid "Game Shark Code File (*.spc;*.xpc)|*.spc;*.xpc"
msgstr ""
msgstr "ゲームシャークのコードファイル (*.spc;*.xpc)|*.spc;*.xpc"
#: cmdevents.cpp:434
msgid "Game Shark Code File (*.gcf)|*.gcf"
msgstr ""
msgstr "ゲームシャークのコードファイル(*.gcf)|*.gcf"
#: cmdevents.cpp:443
msgid ""
"Importing a code file will replace any loaded cheats. Do you want to "
"continue?"
msgstr ""
msgstr "コード ファイルを読み込むと、読み込まれたチートが置き換えられます。続行しますか?"
#: cmdevents.cpp:460
#, c-format
@@ -149,17 +150,17 @@ msgstr "スナップショットファイルを選択"
msgid ""
"Game Shark & PAC Snapshots (*.sps;*.xps)|*.sps;*.xps|Game Shark SP Snapshots"
" (*.gsv)|*.gsv"
msgstr ""
msgstr "ゲームシャーク & PAC のスナップショット (*.sps;*.xps)|*.sps;*.xps|Game Shark SP Snapshots (*.gsv)|*.gsv"
#: cmdevents.cpp:554
msgid "Game Boy Snapshot (*.gbs)|*.gbs"
msgstr ""
msgstr "ゲームボーイのスナップショット(*.gbs)|*.gbs"
#: cmdevents.cpp:563
msgid ""
"Importing a snapshot file will erase any saved games (permanently after the "
"next write). Do you want to continue?"
msgstr ""
msgstr "スナップショット ファイルをインポートすると、保存されたゲームはすべて消去されます (次回の書き込み後には永久に消去されます)。続行しますか?"
#: cmdevents.cpp:588
#, c-format

View File

@@ -32,6 +32,7 @@
# Mateus, 2020
# Mateus Santos <sonicheats@gmail.com>, 2017
# Mateus, 2020
# Mauro Sokrates, 2025
msgid ""
msgstr ""
"Project-Id-Version: VBA-M\n"

View File

@@ -9,7 +9,7 @@
# Alexey Ter <lexalamer@mail.ru>, 2016
# Anton Fedenko <gang296@mail.ru>, 2015
# Artem Abramov <aabramovrussia@yandex.com>, 2015
# George Matsune <georg29387878@gmail.com>, 2017
# aa26bd9ab58eaf66f2dd8a133c45eb7b_740a4a0 <e0546a0601f08ad77df0edd7ee27c670_658246>, 2017
# Irina Fedulova <istartlin@gmail.com>, 2020
# Koshak Magnificent <coschack@mail.ru>, 2020
# Matvey Nazarov, 2022

View File

@@ -4,6 +4,7 @@
#
# Translators:
# Claes-Göran Nydahl <goldug@gmail.com>, 2019
# Daniel Nylander <po@danielnylander.se>, 2025
# 59031d6847a58cccc64d8da23d16ff59_48f0fae, 2015
# [deleted], 2015
# Sebastian Rasmussen <sebras@gmail.com>, 2020,2022
@@ -17,7 +18,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-05 20:00+0000\n"
"PO-Revision-Date: 2011-12-03 19:42+0000\n"
"Last-Translator: Sebastian Rasmussen <sebras@gmail.com>, 2020,2022\n"
"Last-Translator: Daniel Nylander <po@danielnylander.se>, 2025\n"
"Language-Team: Swedish (http://app.transifex.com/bgk/vba-m/language/sv/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -27,7 +28,7 @@ msgstr ""
#: audio/internal/openal.cpp:188
msgid "OpenAL: Failed to open audio device"
msgstr ""
msgstr "OpenAL: Misslyckades med att öppna ljudenhet"
#: audio/internal/openal.cpp:386 audio/internal/faudio.cpp:492
#: audio/internal/xaudio2.cpp:577
@@ -40,39 +41,39 @@ msgid ""
"(*.agb;*.gba;*.bin;*.elf;*.mb;*.zip;*.7z;*.rar)|*.agb;*.gba;*.bin;*.elf;*.mb;*.agb.gz;*.gba.gz;*.bin.gz;*.elf.gz;*.mb.gz;*.agb.z;*.gba.z;*.bin.z;*.elf.z;*.mb.z;*.zip;*.7z;*.rar|Game"
" Boy Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "Game Boy Advance-filer (*.agb;*.gba;*.bin;*.elf;*.mb;*.zip;*.7z;*.rar)|*.agb;*.gba;*.bin;*.elf;*.mb;*.agb.gz;*.gba.gz;*.bin.gz;*.elf.gz;*.mb.gz;*.agb.z;*.gba.z;*.bin.z;*.elf.z;*.mb.z;*.zip;*.7z;*.rar|Game Boy-filer (*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:144
msgid "Open ROM file"
msgstr "Öppna ROM fil"
msgstr "Öppna ROM-fil"
#: cmdevents.cpp:165
msgid ""
"Game Boy Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "Game Boy-filer (*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:171
msgid "Open GB ROM file"
msgstr "Öppna GB ROM fil"
msgstr "Öppna GB ROM-fil"
#: cmdevents.cpp:192
msgid ""
"Game Boy Color Files "
"(*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
msgstr ""
msgstr "Game Boy Color-filer (*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.zip;*.7z;*.rar)|*.dmg;*.gb;*.gbc;*.cgb;*.sgb;*.dmg.gz;*.gb.gz;*.gbc.gz;*.cgb.gz;*.sgb.gz;*.dmg.z;*.gb.z;*.gbc.z;*.cgb.z;*.sgb.z;*.zip;*.7z;*.rar|"
#: cmdevents.cpp:198
msgid "Open GBC ROM file"
msgstr "Öppna GBC ROM fil"
msgstr "Öppna GBC ROM-fil"
#: cmdevents.cpp:363 cmdevents.cpp:385
msgid "Select Dot Code file"
msgstr "Välj Dot Code fil"
msgstr "Välj Dot Code-fil"
#: cmdevents.cpp:365 cmdevents.cpp:387
msgid "E-Reader Dot Code (*.bin;*.raw)|*.bin;*.raw"
msgstr ""
msgstr "E-Reader-punktkod (*.bin;*.raw)|*.bin;*.raw"
#: cmdevents.cpp:406 cmdevents.cpp:601
msgid "Select battery file"
@@ -86,7 +87,7 @@ msgstr "Batterifil (*.sav)|*.sav|Flash save (*.dat)|*.dat"
msgid ""
"Importing a battery file will erase any saved games (permanently after the "
"next write). Do you want to continue?"
msgstr ""
msgstr "Om du importerar en batterifil raderas alla sparade spel (permanent efter nästa skrivning). Vill du fortsätta?"
#: cmdevents.cpp:416 cmdevents.cpp:444 cmdevents.cpp:564
msgid "Confirm import"
@@ -95,12 +96,12 @@ msgstr "Bekräfta import"
#: cmdevents.cpp:422 panel.cpp:493
#, c-format
msgid "Loaded battery %s"
msgstr "Laddade batteri %s"
msgstr "Läste in batteri %s"
#: cmdevents.cpp:424
#, c-format
msgid "Error loading battery %s"
msgstr "Fel vid laddning av batteri %s"
msgstr "Fel vid inläsning av batteri %s"
#: cmdevents.cpp:433
msgid "Select code file"
@@ -108,22 +109,22 @@ msgstr "Välj kodfil"
#: cmdevents.cpp:434
msgid "Game Shark Code File (*.spc;*.xpc)|*.spc;*.xpc"
msgstr ""
msgstr "Game Shark-kodfil (*.spc;*.xpc)|*.spc;*.xpc"
#: cmdevents.cpp:434
msgid "Game Shark Code File (*.gcf)|*.gcf"
msgstr ""
msgstr "Game Shark-kodfil (*.gcf)|*.gcf"
#: cmdevents.cpp:443
msgid ""
"Importing a code file will replace any loaded cheats. Do you want to "
"continue?"
msgstr ""
msgstr "Att importera en kodfil kommer att ersätta alla inlästa fusk. Vill du fortsätta?"
#: cmdevents.cpp:460
#, c-format
msgid "Cannot open file %s"
msgstr "Kan inte öppna fil %s"
msgstr "Kan inte öppna filen %s"
#: cmdevents.cpp:470
#, c-format
@@ -133,12 +134,12 @@ msgstr "Kodfilen stöds inte %s"
#: cmdevents.cpp:540
#, c-format
msgid "Loaded code file %s"
msgstr "Laddade kodfil %s"
msgstr "Läste in kodfilen %s"
#: cmdevents.cpp:542
#, c-format
msgid "Error loading code file %s"
msgstr "Fel vid laddning av kodfil %s"
msgstr "Fel vid inläsning av kodfilen %s"
#: cmdevents.cpp:553 cmdevents.cpp:629
msgid "Select snapshot file"
@@ -163,12 +164,12 @@ msgstr ""
#: cmdevents.cpp:588
#, c-format
msgid "Loaded snapshot file %s"
msgstr "Laddade ögonblicksfil %s"
msgstr "Läste in ögonblicksfilen %s"
#: cmdevents.cpp:590
#, c-format
msgid "Error loading snapshot file %s"
msgstr "Fel vid laddning av ögonblicksfil %s"
msgstr "Fel vid inläsning av ögonblicksfilen %s"
#: cmdevents.cpp:613
#, c-format
@@ -190,17 +191,17 @@ msgstr ""
#: cmdevents.cpp:644
msgid "Exported from Visual Boy Advance-M"
msgstr ""
msgstr "Exporterad från Visual Boy Advance-M"
#: cmdevents.cpp:656
#, c-format
msgid "Saved snapshot file %s"
msgstr "Sparade ögonblicksfil %s"
msgstr "Sparade ögonblicksfilen %s"
#: cmdevents.cpp:658
#, c-format
msgid "Error saving snapshot file %s"
msgstr "Fel vid sparandet av ögonblicksfil%s"
msgstr "Fel vid sparandet av ögonblicksfilen %s"
#: cmdevents.cpp:674 cmdevents.cpp:752 cmdevents.cpp:822 cmdevents.cpp:891
#: gfxviewers.cpp:1662 gfxviewers.cpp:1807 viewers.cpp:588 viewers.cpp:802
@@ -210,12 +211,12 @@ msgstr "Välj utdatafil"
#: cmdevents.cpp:675 gfxviewers.cpp:1663 gfxviewers.cpp:1808 viewsupt.cpp:1200
msgid "PNG images|*.png|BMP images|*.bmp"
msgstr "PNG bilder|*.png|BMP bilder|*.bmp"
msgstr "PNG bilder|*.png|BMP-bilder|*.bmp"
#: cmdevents.cpp:699 sys.cpp:579
#, c-format
msgid "Wrote snapshot %s"
msgstr "Skrev ögonblicksfil %s"
msgstr "Skrev ögonblicksfilen %s"
#: cmdevents.cpp:720 cmdevents.cpp:790 cmdevents.cpp:859 cmdevents.cpp:925
msgid " files ("
@@ -260,7 +261,7 @@ msgstr "Port att vänta på anslutning vid:"
#: cmdevents.cpp:1763
msgid "GDB Connection"
msgstr "GDB anslutning"
msgstr "GDB-anslutning"
#: cmdevents.cpp:1815
#, c-format
@@ -281,15 +282,15 @@ msgid ""
"YOUR CONFIGURATION WILL BE DELETED!\n"
"\n"
"Are you sure?"
msgstr ""
msgstr "DIN KONFIGURATION KOMMER ATT TAS BORT!\n\nÄr du säker?"
#: cmdevents.cpp:2201
msgid "FACTORY RESET"
msgstr ""
msgstr "FABRIKSÅTERSTÄLLNING"
#: cmdevents.cpp:2236
msgid "Nintendo Game Boy / Color / Advance emulator."
msgstr ""
msgstr "Nintendo Game Boy / Color / Advance-emulator."
#: cmdevents.cpp:2237
msgid ""
@@ -312,7 +313,7 @@ msgid ""
"\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program. If not, see http://www.gnu.org/licenses ."
msgstr ""
msgstr "Detta program är fri programvara: du kan vidaredistribuera det och / eller ändra det enligt villkoren i GNU General Public License som publiceras av Free Software Foundation, antingen version 2 av licensen eller (efter eget val) någon senare version.\n\nDetta program distribueras i hopp om att det kommer att vara användbart, men UTAN NÅGON GARANTI; utan ens den underförstådda garantin om SÄLJBARHET eller LÄMPLIGHET FÖR ETT BESTÄMT SYFTE. Se GNU General Public License för mer information.\n\nDu bör ha fått en kopia av GNU General Public License tillsammans med detta program. Om inte, se http://www.gnu.org/licenses ."
#: cmdevents.cpp:2424
msgid "Cannot use Game Boy BIOS when Colorizer Hack is enabled."
@@ -389,7 +390,7 @@ msgstr "Okänd"
#: dialogs/game-maker.cpp:235
msgid "Invalid"
msgstr ""
msgstr "Ogiltig"
#: dialogs/gb-rom-info.cpp:22
msgid "No mapper"
@@ -401,15 +402,15 @@ msgstr ""
#: dialogs/gb-rom-info.cpp:68
msgid " + RAM"
msgstr ""
msgstr " + RAM"
#: dialogs/gb-rom-info.cpp:69
msgid " + RTC"
msgstr ""
msgstr " + RTC"
#: dialogs/gb-rom-info.cpp:70
msgid " + Battery"
msgstr ""
msgstr " + Batteri"
#: dialogs/gb-rom-info.cpp:71
msgid " + Rumble"
@@ -422,122 +423,122 @@ msgstr ""
#: dialogs/gb-rom-info.cpp:74
#, c-format
msgid "%02X (%s%s%s%s%s%s)"
msgstr ""
msgstr "%02X (%s%s%s%s%s%s)"
#: dialogs/gb-rom-info.cpp:81 dialogs/gb-rom-info.cpp:93
#, c-format
msgid "%02X (Supported)"
msgstr ""
msgstr "%02X (stöds)"
#: dialogs/gb-rom-info.cpp:83 dialogs/gb-rom-info.cpp:91
#, c-format
msgid "%02X (Not supported)"
msgstr ""
msgstr "%02X (stöds inte)"
#: dialogs/gb-rom-info.cpp:95
#, c-format
msgid "%02X (Required)"
msgstr ""
msgstr "%02X (krävs)"
#: dialogs/gb-rom-info.cpp:107 dialogs/gb-rom-info.cpp:141
#, c-format
msgid "%02X (32 KiB)"
msgstr ""
msgstr "%02X (32 KiB)"
#: dialogs/gb-rom-info.cpp:109 dialogs/gb-rom-info.cpp:145
#, c-format
msgid "%02X (64 KiB)"
msgstr ""
msgstr "%02X (64 KiB)"
#: dialogs/gb-rom-info.cpp:111 dialogs/gb-rom-info.cpp:143
#, c-format
msgid "%02X (128 KiB)"
msgstr ""
msgstr "%02X (128 KiB)"
#: dialogs/gb-rom-info.cpp:113
#, c-format
msgid "%02X (256 KiB)"
msgstr ""
msgstr "%02X (256 KiB)"
#: dialogs/gb-rom-info.cpp:115
#, c-format
msgid "%02X (512 KiB)"
msgstr ""
msgstr "%02X (512 KiB)"
#: dialogs/gb-rom-info.cpp:117
#, c-format
msgid "%02X (1 MiB)"
msgstr ""
msgstr "%02X (1 MiB)"
#: dialogs/gb-rom-info.cpp:119
#, c-format
msgid "%02X (2 MiB)"
msgstr ""
msgstr "%02X (2 MiB)"
#: dialogs/gb-rom-info.cpp:121
#, c-format
msgid "%02X (4 MiB)"
msgstr ""
msgstr "%02X (4 MiB)"
#: dialogs/gb-rom-info.cpp:123 dialogs/gb-rom-info.cpp:147
#: dialogs/gb-rom-info.cpp:159
#, c-format
msgid "%02X (Unknown)"
msgstr ""
msgstr "%02X (okänt)"
#: dialogs/gb-rom-info.cpp:131
#, c-format
msgid "%02X (None)"
msgstr ""
msgstr "%02X (ingen)"
#: dialogs/gb-rom-info.cpp:133
#, c-format
msgid "%02X (256 B)"
msgstr ""
msgstr "%02X (256 B)"
#: dialogs/gb-rom-info.cpp:135
#, c-format
msgid "%02X (512 B)"
msgstr ""
msgstr "%02X (512 B)"
#: dialogs/gb-rom-info.cpp:137
#, c-format
msgid "%02X (2 KiB)"
msgstr ""
msgstr "%02X (2 KiB)"
#: dialogs/gb-rom-info.cpp:139
#, c-format
msgid "%02X (8 KiB)"
msgstr ""
msgstr "%02X (8 KiB)"
#: dialogs/gb-rom-info.cpp:155
#, c-format
msgid "%02X (Japan)"
msgstr ""
msgstr "%02X (Japan)"
#: dialogs/gb-rom-info.cpp:157
#, c-format
msgid "%02X (World)"
msgstr ""
msgstr "%02X (Världen)"
#: dialogs/gb-rom-info.cpp:201
#, c-format
msgid "%02X (Actual: %02X)"
msgstr ""
msgstr "%02X (Faktisk: %02X)"
#: dialogs/gb-rom-info.cpp:204
#, c-format
msgid "%04X (Actual: %04X)"
msgstr ""
msgstr "%04X (Faktisk: %04X)"
#: dialogs/sound-config.cpp:251
#, c-format
msgid "%d frame = %.2f ms"
msgstr ""
msgstr "%d bildruta = %.2f ms"
#: extra-translations.cpp:13
msgid "&Apply"
msgstr ""
msgstr "&Tillämpa"
#: extra-translations.cpp:14
msgid "Artists"
@@ -545,23 +546,23 @@ msgstr ""
#: extra-translations.cpp:15
msgid "Cancel"
msgstr ""
msgstr "Avbryt"
#: extra-translations.cpp:16
msgid "Close"
msgstr ""
msgstr "Stäng"
#: extra-translations.cpp:17
msgid "Developers"
msgstr ""
msgstr "Utvecklare"
#: extra-translations.cpp:18
msgid "License"
msgstr ""
msgstr "Licens"
#: extra-translations.cpp:19
msgid "OK"
msgstr ""
msgstr "Ok"
#: gfxviewers.cpp:1202
msgid "Select output file and type"
@@ -596,7 +597,7 @@ msgstr "Väntar på klienter..."
#: guiinit.cpp:147
#, c-format
msgid "Server IP address is: %s\n"
msgstr "Serverns IP adress är: %s\n"
msgstr "Serverns IP-adress är: %s\n"
#: guiinit.cpp:149
msgid "Waiting for connection..."
@@ -808,7 +809,7 @@ msgstr ""
#: panel.cpp:1022
#, c-format
msgid "Valid mode: %d x %d - %d @ %d"
msgstr ""
msgstr "Giltigt läge: %d x %d - %d @ %d"
#: panel.cpp:1030
#, c-format
@@ -919,7 +920,7 @@ msgstr "Fel i videoinspelning (%s); avbryter"
#: panel.cpp:2698
#, c-format
msgid "Volume: %d %%"
msgstr ""
msgstr "Volym: %d %%"
#: sys.cpp:205 sys.cpp:266
msgid "No game in progress to record"
@@ -954,7 +955,7 @@ msgstr "Uppspelning slutade"
#: sys.cpp:458
#, c-format
msgid "%d %% (%d, %d fps)"
msgstr ""
msgstr "%d %% (%d, %d bilder/s)"
#: sys.cpp:899 xrc/GBPrinter.xrc:65
msgid "&Discard"
@@ -1005,15 +1006,15 @@ msgstr "Välj minnesdumpsfil"
#: viewsupt.cpp:789
msgid "Red:"
msgstr ""
msgstr "Röd:"
#: viewsupt.cpp:798
msgid "Green:"
msgstr ""
msgstr "Grön:"
#: viewsupt.cpp:807
msgid "Blue:"
msgstr ""
msgstr "Blå:"
#: viewsupt.h:63
#, c-format
@@ -1210,7 +1211,7 @@ msgstr ""
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/command.cpp:33
msgid "GameShark"
msgstr ""
msgstr "GameShark"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/command.cpp:107
#, c-format
@@ -1378,7 +1379,7 @@ msgstr ""
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:461
msgid "Enable status bar"
msgstr ""
msgstr "Aktivera statusrad"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:462
msgid "INI file version (DO NOT MODIFY)"
@@ -1601,7 +1602,7 @@ msgstr ""
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:563
msgid "Number of sound buffers"
msgstr ""
msgstr "Antal ljudbuffertar"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:564
msgid "Bit mask of sound channels to enable"
@@ -1649,7 +1650,7 @@ msgstr ""
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:576
msgid "Sound volume (%)"
msgstr ""
msgstr "Ljudvolym (%)"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:645
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:665
@@ -1781,31 +1782,31 @@ msgstr ""
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/user-input.cpp:54
msgid "Volume Mute"
msgstr ""
msgstr "Volym tyst"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/user-input.cpp:55
msgid "Volume Down"
msgstr ""
msgstr "Sänk volym"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/user-input.cpp:56
msgid "Volume Up"
msgstr ""
msgstr "Höj volym"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/user-input.cpp:57
msgid "Next Track"
msgstr ""
msgstr "Nästa spår"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/user-input.cpp:58
msgid "Previous Track"
msgstr ""
msgstr "Föregående spår"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/user-input.cpp:59
msgid "Stop"
msgstr ""
msgstr "Stoppa"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/user-input.cpp:60
msgid "Play/Pause"
msgstr ""
msgstr "Spela upp/Paus"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/user-input.cpp:93
msgid "Alt+"
@@ -1845,7 +1846,7 @@ msgstr ""
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/user-input.cpp:272
#, c-format
msgid "%s: Button %d"
msgstr ""
msgstr "%s: Knapp %d"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/user-input.cpp:274
#, c-format
@@ -1975,7 +1976,7 @@ msgstr ""
#: /home/rkitover/source/repos/vbam-tx-pulls/src/core/gba/gbaLink.cpp:1678
#: /home/rkitover/source/repos/vbam-tx-pulls/src/core/gba/gbaLink.cpp:1711
msgid "Network error."
msgstr ""
msgstr "Nätverksfel."
#: /home/rkitover/source/repos/vbam-tx-pulls/src/core/gba/gbaLink.cpp:1110
#: /home/rkitover/source/repos/vbam-tx-pulls/src/core/gba/gbaLink.cpp:1687
@@ -1986,7 +1987,7 @@ msgstr ""
#: /home/rkitover/source/repos/vbam-tx-pulls/src/core/gba/gbaLink.cpp:1124
#: /home/rkitover/source/repos/vbam-tx-pulls/src/core/gba/gbaLink.cpp:1701
msgid "All players connected"
msgstr ""
msgstr "Alla spelare anslutna"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/core/gba/gbaLink.cpp:1145
#: /home/rkitover/source/repos/vbam-tx-pulls/src/core/gba/gbaLink.cpp:1723
@@ -2477,7 +2478,7 @@ msgstr "ROM-information"
#: xrc/GBColorPrefPanel.xrc:9
msgid "Custom"
msgstr ""
msgstr "Anpassad"
#: xrc/GBColorPrefPanel.xrc:10 xrc/JoyPanel.xrc:247
msgid "Standard"
@@ -2731,11 +2732,11 @@ msgstr ""
#: xrc/GameBoyAdvanceConfig.xrc:49 xrc/GameBoyAdvanceConfig.xrc:221
msgid "64 K"
msgstr ""
msgstr "64 K"
#: xrc/GameBoyAdvanceConfig.xrc:50 xrc/GameBoyAdvanceConfig.xrc:222
msgid "128 K"
msgstr ""
msgstr "128 K"
#: xrc/GameBoyAdvanceConfig.xrc:64
msgid "Detect Now"
@@ -2751,7 +2752,7 @@ msgstr "Sparningstyp"
#: xrc/GameBoyAdvanceConfig.xrc:92
msgid "BIOS file:"
msgstr ""
msgstr "BIOS-fil:"
#: xrc/GameBoyAdvanceConfig.xrc:115
msgid "Current BIOS file:"
@@ -3088,7 +3089,7 @@ msgstr "&Läs automatiskt in senaste"
#: xrc/MainMenu.xrc:89
msgid "From &File..."
msgstr ""
msgstr "Från &fil..."
#: xrc/MainMenu.xrc:93
msgid "Do not change &battery save"
@@ -3152,7 +3153,7 @@ msgstr "1&0"
#: xrc/MainMenu.xrc:145
msgid "To &File..."
msgstr ""
msgstr "Till &fil..."
#: xrc/MainMenu.xrc:147
msgid "&Save state"
@@ -3292,7 +3293,7 @@ msgstr "&Länk"
#: xrc/MainMenu.xrc:272
msgid "Start &Network Link..."
msgstr ""
msgstr "Starta &nätverkslänk..."
#: xrc/MainMenu.xrc:277
msgid "&Nothing"
@@ -3321,7 +3322,7 @@ msgstr "&Hastighetshack"
#: xrc/MainMenu.xrc:310 xrc/MainMenu.xrc:316 xrc/MainMenu.xrc:383
#: xrc/MainMenu.xrc:417
msgid "&Configure..."
msgstr ""
msgstr "&Konfigurera..."
#: xrc/MainMenu.xrc:314
msgid "&Video"
@@ -3457,7 +3458,7 @@ msgstr "&R"
#: xrc/MainMenu.xrc:494 xrc/MainMenu.xrc:517
msgid "Configure..."
msgstr ""
msgstr "Konfigurera..."
#: xrc/MainMenu.xrc:498
msgid "&Real-time clock"
@@ -3505,7 +3506,7 @@ msgstr ""
#: xrc/MainMenu.xrc:555
msgid "&General..."
msgstr ""
msgstr "A&llmänt..."
#: xrc/MainMenu.xrc:558
msgid "&Speedup / Turbo..."
@@ -3513,7 +3514,7 @@ msgstr ""
#: xrc/MainMenu.xrc:561
msgid "D&irectories..."
msgstr ""
msgstr "Ka&taloger..."
#: xrc/MainMenu.xrc:564
msgid "&Key Shortcuts..."
@@ -3521,7 +3522,7 @@ msgstr ""
#: xrc/MainMenu.xrc:567
msgid "UI Settings"
msgstr ""
msgstr "Gränssnittsinställningar"
#: xrc/MainMenu.xrc:569
msgid "Enable &Status bar"
@@ -3545,11 +3546,11 @@ msgstr "&Fusk"
#: xrc/MainMenu.xrc:587
msgid "List &cheats..."
msgstr ""
msgstr "Lista fu&sk..."
#: xrc/MainMenu.xrc:590
msgid "Find c&heat..."
msgstr ""
msgstr "Hitta f&usk..."
#: xrc/MainMenu.xrc:594
msgid "A&utomatically save / load cheats"
@@ -3757,7 +3758,7 @@ msgstr "Spelare:"
#: xrc/NetLink.xrc:83
msgid "2"
msgstr ""
msgstr "2"
#: xrc/NetLink.xrc:107
msgid "Server:"
@@ -3789,7 +3790,7 @@ msgstr "Ljudinställningar"
#: xrc/SoundConfig.xrc:18
msgid "Volume:"
msgstr ""
msgstr "Volym:"
#: xrc/SoundConfig.xrc:36
msgid "Mute"
@@ -3801,7 +3802,7 @@ msgstr "Maximal"
#: xrc/SoundConfig.xrc:73
msgid "Sample rate:"
msgstr ""
msgstr "Samplingsfrekvens:"
#: xrc/SoundConfig.xrc:79
msgid "48 KHz"

View File

@@ -4,6 +4,7 @@
#
# Translators:
# abc Def <hdogan1974@gmail.com>, 2020
# adalet Mustafa, 2025
# Ali Ozderya <ozozderya@gmail.com>, 2015,2017
# boran roni, 2021
# Justice League <denizden788@gmail.com>, 2020
@@ -18,7 +19,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-05 20:00+0000\n"
"PO-Revision-Date: 2011-12-03 19:42+0000\n"
"Last-Translator: Sinan H., 2016,2018,2022,2024\n"
"Last-Translator: adalet Mustafa, 2025\n"
"Language-Team: Turkish (http://app.transifex.com/bgk/vba-m/language/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
# horcus Yuan <1257425379@qq.com>, 2019
# pngai <pngai@ymail.com>, 2016
# Rythm Tse <ksusix@gmail.com>, 2016
# Weiran Wu <wwrustc@mail.ustc.edu.cn>, 2024
# Weiran Wu <wwrustc@mail.ustc.edu.cn>, 2024-2025
# wr superboy, 2023
# Xuan Zheng, 2024
# Xuntao Chi <1139954766@qq.com>, 2019
@@ -26,7 +26,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-05 20:00+0000\n"
"PO-Revision-Date: 2011-12-03 19:42+0000\n"
"Last-Translator: 王晨旭 <wcxu21@126.com>, 2021\n"
"Last-Translator: Weiran Wu <wwrustc@mail.ustc.edu.cn>, 2024-2025\n"
"Language-Team: Chinese (China) (http://app.transifex.com/bgk/vba-m/language/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -1526,19 +1526,19 @@ msgstr "加载状态时不覆盖原生 (电池) 存档"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:526
msgid "Throttle game speed, even when accelerated (0-450 %, 0 = no throttle)"
msgstr "阀门加快游戏速度,即使已经在加速时0-450 %0 = 无阀门"
msgstr "限制游戏速度,包括加速后的0-450 %0 = 无限制"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:529
msgid "Set throttle for speedup key (0-3000 %, 0 = no throttle)"
msgstr "设置加速键的阀门值0-3000 %0 = 无阀门"
msgstr "设置加速键的限制0-3000 %0 = 无限制"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:531
msgid "Number of frames to skip with speedup (instead of speedup throttle)"
msgstr "加速时跳过的帧数(而不是阀门加速)"
msgstr "加速时跳过的帧数(而不是加速限制"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:534
msgid "Use frame skip for speedup throttle"
msgstr "使用跳帧来阀门加速"
msgstr "使用跳帧来限制加速"
#: /home/rkitover/source/repos/vbam-tx-pulls/src/wx/config/internal/option-internal.cpp:536
msgid "Mute sound during speedup"
@@ -2898,7 +2898,7 @@ msgstr "秒 (0-600); 0 = 禁用"
#: xrc/GeneralConfig.xrc:78
msgid "&Throttle"
msgstr "阀门(&T)"
msgstr "限速(&T)"
#: xrc/GeneralConfig.xrc:96 xrc/SpeedupConfig.xrc:27
msgid "Percent of normal:"
@@ -2906,7 +2906,7 @@ msgstr "正常百分比:"
#: xrc/GeneralConfig.xrc:106
msgid "0 = no throttle"
msgstr "0 = 无阀门"
msgstr "0 = 无限制"
#: xrc/GeneralConfig.xrc:114
msgid "Unlimited"
@@ -3085,7 +3085,7 @@ msgstr "保存点代码(&S)..."
#: xrc/MainMenu.xrc:47
msgid "Most &recent"
msgstr "最常用"
msgstr "最常用(&R)"
#: xrc/MainMenu.xrc:50
msgid "Load current state slot"
@@ -3285,7 +3285,7 @@ msgstr "自动 IPS/UPS/IPF 补丁(&A)"
#: xrc/MainMenu.xrc:259
msgid "&Pause when inactive"
msgstr "非活状态时暂停(&P)"
msgstr "非活状态时暂停(&P)"
#: xrc/MainMenu.xrc:264
msgid "&Reset"
@@ -3570,7 +3570,7 @@ msgstr "启用金手指(&E)"
#: xrc/MainMenu.xrc:605
msgid "&Break into GDB"
msgstr "入 GDB(&B)"
msgstr "中断进入 GDB(&B)"
#: xrc/MainMenu.xrc:609
msgid "&Configure port..."
@@ -3578,7 +3578,7 @@ msgstr "配置端口(&C)..."
#: xrc/MainMenu.xrc:612
msgid "&Break on load"
msgstr "加载时切入(&B)"
msgstr "加载时中断(&B)"
#: xrc/MainMenu.xrc:617
msgid "&Disconnect"
@@ -3846,7 +3846,7 @@ msgstr "启用硬件加速"
#: xrc/SoundConfig.xrc:184
msgid "Number of sound buffers:"
msgstr " 声音缓冲区数:"
msgstr "声音缓冲区数:"
#: xrc/SoundConfig.xrc:192
msgid "Advanced"
@@ -3878,7 +3878,7 @@ msgstr "声音过滤"
#: xrc/TileViewer.xrc:4
msgid "Tile Viewer"
msgstr "片查看器"
msgstr "片查看器"
#: xrc/TileViewer.xrc:24
msgid "Colors"
@@ -3890,7 +3890,7 @@ msgstr "加速/超频设置"
#: xrc/SpeedupConfig.xrc:9
msgid "Speedup Throttle"
msgstr "阀门加速"
msgstr "加速限制"
#: xrc/SpeedupConfig.xrc:54
msgid "Frame skip"

View File

@@ -19,8 +19,6 @@ parts:
cmake-generator: Ninja
stage-packages:
- libsdl2-2.0-0
- libsfml-network2.5
- libsfml-system2.5
- libnotify4
- libsm6
- libopenal1
@@ -46,7 +44,6 @@ parts:
- zip
- libnotify-dev
- libsdl2-dev
- libsfml-dev
- libgtk-3-dev
- libopenal-dev
- libwxgtk3.0-gtk3-dev

View File

@@ -1,5 +1,13 @@
add_library(vbam-components-filters OBJECT)
unset(extra_src)
if(WINXP)
list(APPEND extra_src
${CMAKE_SOURCE_DIR}/third_party/quake3-sqrt/quake3-sqrt.h
)
target_include_directories(vbam-components-filters PRIVATE ${CMAKE_SOURCE_DIR}/third_party/quake3-sqrt)
endif()
target_sources(vbam-components-filters
PRIVATE
internal/2xSaI.cpp
@@ -17,6 +25,7 @@ target_sources(vbam-components-filters
internal/xBRZ/xbrz_config.h
internal/xBRZ/xbrz_tools.h
internal/xbrzfilter.cpp
${extra_src}
PUBLIC
filters.h

View File

@@ -21,6 +21,10 @@
#include <cmath> //std::sqrt
#include "xbrz_tools.h"
#ifdef WINXP
#include "quake3-sqrt.h"
#endif
// some gcc versions lie about having this C++17 feature
#define static_assert(x) static_assert(x, "assertion failed")
@@ -66,7 +70,9 @@ uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate c
inline double fastSqrt(double n)
{
#if (defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(__i386__))
#ifdef WINXP
return quake3_sqrt((float)n);
#elif (defined(__GNUC__) || defined(__clang__)) && (defined(__x86_64__) || defined(__i386__))
__asm__ ("fsqrt" : "+t" (n));
return n;
#elif defined(_MSC_VER) && defined(_M_IX86)

View File

@@ -70,7 +70,7 @@ extern struct CoreOptions {
int skipSaveGameBattery = 1;
int skipSaveGameCheats = 0;
int useBios = 0;
int winGbPrinterEnabled = 1;
int gbPrinterEnabled = 0;
uint32_t speedup_throttle = 100;
uint32_t speedup_frame_skip = 9;
uint32_t throttle = 100;

View File

@@ -1416,7 +1416,7 @@ void gbWriteMemory(uint16_t address, uint8_t value)
EmuReseted = false;
gbMemory[0xff02] = value;
if (gbSerialOn && (GetLinkMode() == LINK_GAMEBOY_IPC || GetLinkMode() == LINK_GAMEBOY_SOCKET
|| GetLinkMode() == LINK_DISCONNECTED || coreOptions.winGbPrinterEnabled)) {
|| GetLinkMode() == LINK_DISCONNECTED || coreOptions.gbPrinterEnabled)) {
gbSerialTicks = GBSERIAL_CLOCK_TICKS;
@@ -2969,8 +2969,13 @@ void gbReset()
inBios = true;
} else if (gbHardware & 0xa) {
// Set compatibility mode if it is a DMG ROM.
const uint8_t gbcFlag = g_gbCartData.SupportsCGB() ? 0x80 : 0x00;
gbMemory[0xff6c] = 0xfe | gbcFlag;
if (g_gbCartData.SupportsCGB()) {
// OPRI with bit 0 set to 0 = CGB mode.
gbMemory[0xff6c] = 0xfe;
} else {
// OPRI with bit 0 set to 1 = DMG mode.
gbMemory[0xff6c] = 0xff;
}
}
gbLine99Ticks = 1;

View File

@@ -481,7 +481,7 @@ void gbaUpdateRomSize(int size)
if (size > romSize) {
romSize = size;
uint8_t* tmp = (uint8_t*)realloc(g_rom, SIZE_ROM);
uint8_t* tmp = (uint8_t*)realloc(g_rom, romSize);
g_rom = tmp;
uint16_t* temp = (uint16_t*)(g_rom + ((romSize + 1) & ~1));

View File

@@ -1822,6 +1822,16 @@ static INSN_REGPARM void thumbE0(uint32_t opcode)
busPrefetchCount = 0;
}
static INSN_REGPARM void thumbE8(uint32_t opcode)
{
#ifdef GBA_LOGGING
// TODO: This is (erroneously) used by some Wii U VC ROMs. We should have a configuration
// toggle to enable the correct hardware behavior, which would be to call CPUUndefinedException.
if (systemVerbose & VERBOSE_UNDEFINED)
log("Undefined Wii U THUMB instruction %04x at %08x (ignored)\n", opcode, armNextPC - 2);
#endif
}
// BLL #offset (forward)
static INSN_REGPARM void thumbF0(uint32_t opcode)
{
@@ -1979,7 +1989,7 @@ static insnfunc_t thumbInsnTable[1024] = {
thumbE0, thumbE0, thumbE0, thumbE0, thumbE0, thumbE0, thumbE0, thumbE0,
thumbE0, thumbE0, thumbE0, thumbE0, thumbE0, thumbE0, thumbE0, thumbE0,
thumbE0, thumbE0, thumbE0, thumbE0, thumbE0, thumbE0, thumbE0, thumbE0,
thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, // E8
thumbE8, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, // E8
thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI,
thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI,
thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI, thumbUI,

View File

@@ -26,7 +26,7 @@
#include <cstring>
#include <string>
#include <SFML/Network.hpp>
#include "SFML/Network.hpp"
#include <libintl.h>
#define _(x) gettext(x)
@@ -41,17 +41,22 @@
#define snprintf _snprintf
#endif
#ifdef UPDATE_REG
#undef UPDATE_REG
#endif
#define UPDATE_REG(address, value) WRITE16LE(((uint16_t*)&g_ioMem[address]), value)
static int vbaid = 0;
const char* MakeInstanceFilename(const char* Input)
{
if (vbaid == 0)
if (vbaid == 0) {
return Input;
}
static char* result = NULL;
if (result != NULL)
if (result != NULL) {
free(result);
}
result = (char*)malloc(strlen(Input) + 3);
char* p = strrchr((char*)Input, '.');
@@ -270,7 +275,7 @@ class RFUServer {
public:
sf::TcpSocket tcpsocket[5];
sf::IpAddress udpaddr[5];
sf::IpAddress udpaddr[5] = { sf::IpAddress{0}, sf::IpAddress{0}, sf::IpAddress{0}, sf::IpAddress{0}, sf::IpAddress{0} };
RFUServer(void);
sf::Packet& Serialize(sf::Packet& packet, int slave);
void DeSerialize(sf::Packet& packet, int slave);
@@ -283,7 +288,7 @@ class RFUClient {
int numbytes;
public:
sf::IpAddress serveraddr;
sf::IpAddress serveraddr{0};
unsigned short serverport;
bool transferring;
RFUClient(void);
@@ -392,7 +397,7 @@ enum {
typedef struct {
sf::TcpSocket tcpsocket;
sf::TcpListener tcplistener;
int numslaves;
uint16_t numslaves;
int connectedSlaves;
int type;
bool server;
@@ -412,7 +417,7 @@ class CableServer {
public:
sf::TcpSocket tcpsocket[4];
sf::IpAddress udpaddr[4];
sf::IpAddress udpaddr[4] = { sf::IpAddress{0}, sf::IpAddress{0}, sf::IpAddress{0}, sf::IpAddress{0} };
CableServer(void);
void Send(void);
void Recv(void);
@@ -430,7 +435,7 @@ class CableClient {
int numbytes;
public:
sf::IpAddress serveraddr;
sf::IpAddress serveraddr{0};
unsigned short serverport;
bool transferring;
CableClient(void);
@@ -520,33 +525,48 @@ LinkMode GetLinkMode()
return LINK_DISCONNECTED;
}
void GetLinkServerHost(char* const host, size_t size)
bool GetLinkServerHost(char* const host, size_t size)
{
if (host == NULL || size == 0)
return;
if (host == NULL || size == 0) {
return false;
}
host[0] = '\0';
if (linkDriver && linkDriver->mode == LINK_GAMECUBE_DOLPHIN)
if (linkDriver && linkDriver->mode == LINK_GAMECUBE_DOLPHIN) {
strncpy(host, joybusHostAddr.toString().c_str(), size);
else if (lanlink.server) {
if (IP_LINK_BIND_ADDRESS == "*")
strncpy(host, sf::IpAddress::getLocalAddress().toString().c_str(), size);
else
} else if (lanlink.server) {
if (IP_LINK_BIND_ADDRESS == "*") {
auto local_addr = sf::IpAddress::getLocalAddress();
if (local_addr) {
strncpy(host, local_addr.value().toString().c_str(), size);
} else {
return false;
}
} else {
strncpy(host, IP_LINK_BIND_ADDRESS.c_str(), size);
}
}
else
else {
strncpy(host, lc.serveraddr.toString().c_str(), size);
}
return true;
}
bool SetLinkServerHost(const char* host)
{
sf::IpAddress addr = sf::IpAddress(host);
sf::IpAddress addr{0};
auto resolved = sf::IpAddress::resolve(host);
if (!resolved) {
return false;
}
addr = resolved.value();
lc.serveraddr = addr;
joybusHostAddr = addr;
return addr != sf::IpAddress::None;
return true;
}
int GetLinkPlayerId()
@@ -1044,11 +1064,13 @@ static ConnectionState InitSocket()
{
linkid = 0;
for (int i = 0; i < 4; i++)
for (int i = 0; i < 4; i++) {
cable_data[i] = 0xffff;
}
for (int i = 0; i < 4; i++)
for (int i = 0; i < 4; i++) {
cable_gb_data[i] = 0xff;
}
if (lanlink.server) {
lanlink.connectedSlaves = 0;
@@ -1057,26 +1079,33 @@ static ConnectionState InitSocket()
// too bad Listen() doesn't take an address as well
// then again, old code used INADDR_ANY anyway
sf::IpAddress bind_ip = IP_LINK_BIND_ADDRESS == "*" ? sf::IpAddress::Any : IP_LINK_BIND_ADDRESS;
sf::IpAddress bind_ip{0};
if (lanlink.tcplistener.listen(IP_LINK_PORT, bind_ip) == sf::Socket::Error)
if (IP_LINK_BIND_ADDRESS != "*") {
auto resolved = sf::IpAddress::resolve(IP_LINK_BIND_ADDRESS);
if (resolved) {
bind_ip = resolved.value();
} else {
return LINK_ERROR;
}
}
if (lanlink.tcplistener.listen(IP_LINK_PORT, bind_ip) == sf::Socket::Status::Error) {
// Note: old code closed socket & retried once on bind failure
return LINK_ERROR; // FIXME: error code?
else
} else {
return LINK_NEEDS_UPDATE;
}
} else {
lc.serverport = IP_LINK_PORT;
if (lc.serveraddr == sf::IpAddress::None) {
lanlink.tcpsocket.setBlocking(false);
sf::Socket::Status status = lanlink.tcpsocket.connect(lc.serveraddr, lc.serverport);
if (status == sf::Socket::Status::Error || status == sf::Socket::Status::Disconnected) {
return LINK_ERROR;
} else {
lanlink.tcpsocket.setBlocking(false);
sf::Socket::Status status = lanlink.tcpsocket.connect(lc.serveraddr, lc.serverport);
if (status == sf::Socket::Error || status == sf::Socket::Disconnected)
return LINK_ERROR;
else
return LINK_NEEDS_UPDATE;
return LINK_NEEDS_UPDATE;
}
}
}
@@ -1090,11 +1119,11 @@ static ConnectionState ConnectUpdateSocket(char* const message, size_t size)
fdset.add(lanlink.tcplistener);
if (fdset.wait(sf::milliseconds(150))) {
int nextSlave = lanlink.connectedSlaves + 1;
uint16_t nextSlave = lanlink.connectedSlaves + 1;
sf::Socket::Status st = lanlink.tcplistener.accept(ls.tcpsocket[nextSlave]);
if (st == sf::Socket::Error) {
if (st == sf::Socket::Status::Error) {
for (int j = 1; j < nextSlave; j++)
ls.tcpsocket[j].disconnect();
@@ -1102,8 +1131,7 @@ static ConnectionState ConnectUpdateSocket(char* const message, size_t size)
newState = LINK_ERROR;
} else {
sf::Packet packet;
packet << static_cast<sf::Uint16>(nextSlave)
<< static_cast<sf::Uint16>(lanlink.numslaves);
packet << nextSlave << lanlink.numslaves;
ls.tcpsocket[nextSlave].send(packet);
@@ -1129,13 +1157,13 @@ static ConnectionState ConnectUpdateSocket(char* const message, size_t size)
sf::Packet packet;
sf::Socket::Status status = lanlink.tcpsocket.receive(packet);
if (status == sf::Socket::Error || status == sf::Socket::Disconnected) {
if (status == sf::Socket::Status::Error || status == sf::Socket::Status::Disconnected) {
snprintf(message, size, N_("Network error."));
newState = LINK_ERROR;
} else if (status == sf::Socket::Done) {
} else if (status == sf::Socket::Status::Done) {
if (linkid == 0) {
sf::Uint16 receivedId, receivedSlaves;
uint16_t receivedId, receivedSlaves;
packet >> receivedId >> receivedSlaves;
if (packet) {
@@ -1540,7 +1568,7 @@ void RFUServer::Recv(void)
sf::Packet packet;
tcpsocket[i + 1].setBlocking(false);
sf::Socket::Status status = tcpsocket[i + 1].receive(packet);
if (status == sf::Socket::Disconnected) {
if (status == sf::Socket::Status::Disconnected) {
char message[30];
sprintf(message, _("Player %d disconnected."), i + 1);
systemScreenMessage(message);
@@ -1650,7 +1678,7 @@ void RFUClient::Recv(void)
}
sf::Packet packet;
sf::Socket::Status status = lanlink.tcpsocket.receive(packet);
if (status == sf::Socket::Disconnected) {
if (status == sf::Socket::Status::Disconnected) {
systemScreenMessage(_("Server disconnected."));
CloseLink();
return;
@@ -1671,7 +1699,7 @@ static ConnectionState ConnectUpdateRFUSocket(char* const message, size_t size)
sf::Socket::Status st = lanlink.tcplistener.accept(rfu_server.tcpsocket[nextSlave]);
if (st == sf::Socket::Error) {
if (st == sf::Socket::Status::Error) {
for (int j = 1; j < nextSlave; j++)
rfu_server.tcpsocket[j].disconnect();
@@ -1679,8 +1707,7 @@ static ConnectionState ConnectUpdateRFUSocket(char* const message, size_t size)
newState = LINK_ERROR;
} else {
sf::Packet packet;
packet << static_cast<sf::Uint16>(nextSlave)
<< static_cast<sf::Uint16>(lanlink.numslaves);
packet << nextSlave << lanlink.numslaves;
rfu_server.tcpsocket[nextSlave].send(packet);
@@ -1707,13 +1734,13 @@ static ConnectionState ConnectUpdateRFUSocket(char* const message, size_t size)
lanlink.tcpsocket.setBlocking(false);
sf::Socket::Status status = lanlink.tcpsocket.receive(packet);
if (status == sf::Socket::Error || status == sf::Socket::Disconnected) {
if (status == sf::Socket::Status::Error || status == sf::Socket::Status::Disconnected) {
snprintf(message, size, N_("Network error."));
newState = LINK_ERROR;
} else if (status == sf::Socket::Done) {
} else if (status == sf::Socket::Status::Done) {
if (linkid == 0) {
sf::Uint16 receivedId, receivedSlaves;
uint16_t receivedId, receivedSlaves;
packet >> receivedId >> receivedSlaves;
if (packet) {

View File

@@ -86,8 +86,9 @@ extern bool SetLinkServerHost(const char* host);
* If in lan client mode, returns the IP adress of the host to connect to
* If in gamecube mode, returns the IP adress of the dolphin host
*
* @return false on error
*/
extern void GetLinkServerHost(char* const host, size_t size);
extern bool GetLinkServerHost(char* const host, size_t size);
/**
* Set the value in milliseconds of the timeout after which a connection is

View File

@@ -8,10 +8,7 @@
GBASockClient::GBASockClient(sf::IpAddress _server_addr)
{
if (_server_addr == sf::IpAddress::None)
server_addr = sf::IpAddress::getPublicAddress();
else
server_addr = _server_addr;
server_addr = _server_addr;
client.connect(server_addr, 0xd6ba);
client.setBlocking(false);
@@ -44,8 +41,9 @@ void GBASockClient::Send(std::vector<char> data)
// Returns cmd for convenience
char GBASockClient::ReceiveCmd(char* data_in, bool block)
{
if (IsDisconnected())
if (IsDisconnected()) {
return data_in[0];
}
std::size_t num_received = 0;
if (block || clock_sync == 0) {
@@ -53,8 +51,9 @@ char GBASockClient::ReceiveCmd(char* data_in, bool block)
Selector.add(client);
Selector.wait(sf::seconds(6));
}
if (client.receive(data_in, 5, num_received) == sf::Socket::Disconnected)
if (client.receive(data_in, 5, num_received) == sf::Socket::Status::Disconnected) {
Disconnect();
}
return data_in[0];
}
@@ -67,13 +66,15 @@ void GBASockClient::ReceiveClock(bool block)
char sync_ticks[4] = { 0, 0, 0, 0 };
std::size_t num_received = 0;
if (clock_client.receive(sync_ticks, 4, num_received) == sf::Socket::Disconnected)
if (clock_client.receive(sync_ticks, 4, num_received) == sf::Socket::Status::Disconnected) {
Disconnect();
}
if (num_received == 4) {
clock_sync_ticks = 0;
for (int i = 0; i < 4; i++)
for (int i = 0; i < 4; i++) {
clock_sync_ticks |= (uint8_t)(sync_ticks[i]) << ((3 - i) * 8);
}
clock_sync += clock_sync_ticks;
}
}

View File

@@ -7,7 +7,7 @@
#include <cstdint>
#include <SFML/Network.hpp>
#include "SFML/Network.hpp"
class GBASockClient {
public:
@@ -24,7 +24,7 @@ public:
bool IsDisconnected();
private:
sf::IpAddress server_addr;
sf::IpAddress server_addr{0};
sf::TcpSocket client;
sf::TcpSocket clock_client;

4
src/gb-over.ini Normal file
View File

@@ -0,0 +1,4 @@
# Game Boy Overrrides
[ALLEY WAY]
gbPrinter = 0

View File

@@ -79,7 +79,7 @@ else ifeq ($(platform), classic_armv7_a7)
TARGET := $(TARGET_NAME)_libretro.so
fpic := -fPIC
SHARED := -shared -Wl,--no-undefined -fPIC
CFLAGS += -Ofast \
CFLAGS += -O3 -ffast-math \
-flto=4 -fwhole-program -fuse-linker-plugin \
-fdata-sections -ffunction-sections -Wl,--gc-sections \
-fno-stack-protector -fno-ident -fomit-frame-pointer \
@@ -112,7 +112,7 @@ else ifeq ($(platform), classic_armv8_a35)
fpic := -fPIC
SHARED := -shared
TILED_RENDERING=1
CFLAGS += -Ofast \
CFLAGS += -O3 -ffast-math \
-flto=4 -fwhole-program -fuse-linker-plugin \
-fdata-sections -ffunction-sections -Wl,--gc-sections \
-fno-stack-protector -fno-ident -fomit-frame-pointer \

View File

@@ -590,7 +590,7 @@ void retro_init(void)
coreOptions.parseDebug = true;
coreOptions.cheatsEnabled = 0;
coreOptions.skipSaveGameBattery = 0;
coreOptions.winGbPrinterEnabled = 0;
coreOptions.gbPrinterEnabled = 0;
struct retro_log_callback log;
struct retro_rumble_interface rumble;

View File

@@ -31,8 +31,8 @@ if(MSVC)
# We should probably use an external library for this.
target_sources(vbam
PRIVATE
${CMAKE_SOURCE_DIR}/dependencies/msvc/getopt.c
${CMAKE_SOURCE_DIR}/dependencies/msvc/getopt.h
${CMAKE_SOURCE_DIR}/win32-deps/msvc/getopt.c
${CMAKE_SOURCE_DIR}/win32-deps/msvc/getopt.h
)
endif()
@@ -61,7 +61,7 @@ endif()
if(ENABLE_LIRC)
target_link_libraries(vbam lirc_client)
target_compile_definitions(vbam VBAM_ENABLE_LIRC)
target_compile_definitions(vbam PUBLIC VBAM_ENABLE_LIRC)
endif()
if(WIN32)
@@ -71,15 +71,15 @@ endif()
# Installation scripts.
install(
PROGRAMS ${PROJECT_BINARY_DIR}/vbam${CMAKE_EXECUTABLE_SUFFIX}
DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
FILES ${CMAKE_CURRENT_LIST_DIR}/vbam.cfg-example
DESTINATION ${CMAKE_INSTALL_FULL_SYSCONFDIR}
DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}
RENAME vbam.cfg
)
if(UNIX)
# man pages.
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/vbam.6 DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man6)
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/vbam.6 DESTINATION ${CMAKE_INSTALL_MANDIR}/man6)
endif()

View File

@@ -189,7 +189,7 @@ struct option argOptions[] = {
{ "gb-emulator-type", required_argument, 0, OPT_GB_EMULATOR_TYPE },
{ "gb-frame-skip", required_argument, 0, OPT_GB_FRAME_SKIP },
{ "gb-palette-option", required_argument, 0, OPT_GB_PALETTE_OPTION },
{ "gb-printer", no_argument, &coreOptions.winGbPrinterEnabled, 1 },
{ "gb-printer", no_argument, &coreOptions.gbPrinterEnabled, 1 },
{ "gdb", required_argument, 0, 'G' },
{ "help", no_argument, &optPrintUsage, 1 },
{ "ifb-filter", required_argument, 0, 'I' },
@@ -236,7 +236,7 @@ struct option argOptions[] = {
{ "no-speedup-mute", no_argument, 0, OPT_NO_SPEEDUP_MUTE },
{ "use-bios", no_argument, &coreOptions.useBios, 1 },
{ "verbose", required_argument, 0, 'v' },
{ "win-gb-printer-enabled", no_argument, &coreOptions.winGbPrinterEnabled, 1 },
{ "win-gb-printer-enabled", no_argument, &coreOptions.gbPrinterEnabled, 1 },
{ NULL, no_argument, NULL, 0 }
@@ -351,7 +351,7 @@ void LoadConfig()
coreOptions.speedup_throttle_frame_skip = ReadPref("speedupThrottleFrameSkip", 0);
coreOptions.speedup_mute = ReadPref("speedupMute", 1);
coreOptions.useBios = ReadPrefHex("useBiosGBA");
coreOptions.winGbPrinterEnabled = ReadPref("gbPrinter", 0);
coreOptions.gbPrinterEnabled = ReadPref("gbPrinter", 0);
int soundQuality = (ReadPrefHex("soundQuality", 1));
switch (soundQuality) {

View File

@@ -364,6 +364,10 @@ mirroringEnabled=1
saveType=1
mirroringEnabled=1
# Higurashi no Nakukoroni (Japan)
[HGRS]
saveType=2
# Koro Koro Puzzle - Happy Panechu! (Japan)
[KHPJ]
saveType=4

View File

@@ -59,6 +59,7 @@ set(VBAM_WX_COMMON
${VBAM_GENERATED_DIR}/wx/builtin-over.h
${VBAM_GENERATED_DIR}/wx/cmdhandlers.h
${VBAM_GENERATED_DIR}/wx/cmd-evtable.h
${VBAM_GENERATED_DIR}/wx/gb-builtin-over.h
)
if(NOT ZIP_PROGRAM)
@@ -141,7 +142,7 @@ endif()
# wxWidgets configuration.
set(wxWidgets_USE_UNICODE ON)
if(CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo)$")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(wxWidgets_USE_DEBUG ON) # noop if wx is compiled with --disable-debug, like in Mac Homebrew atm
endif()
if(VBAM_STATIC)
@@ -152,6 +153,7 @@ unset(wx_find_extra)
if(CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
set(wx_find_extra NO_DEFAULT_PATH)
set(wxWidgets_DIR "${VCPKG_ROOT}/installed/${VCPKG_TARGET_TRIPLET}/share/wxwidgets")
find_package(nanosvg)
endif()
set(ENABLE_OPENGL TRUE)
@@ -162,6 +164,13 @@ if(NOT wxWidgets_FOUND)
set(ENABLE_OPENGL FALSE)
endif()
# Fixup wxWidgets paths for vcpkg debug builds.
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND CMAKE_TOOLCHAIN_FILE MATCHES "vcpkg")
set(wxWidgets_ROOT_DIR "${wxWidgets_ROOT_DIR}/debug" CACHE INTERNAL "wxWidgets root directory" FORCE)
string(REGEX REPLACE "/lib$" "/debug/lib" wxWidgets_LIB_DIR "${wxWidgets_LIB_DIR}")
set(wxWidgets_LIB_DIR "${wxWidgets_LIB_DIR}" CACHE INTERNAL "wxWidgets library directory" FORCE)
endif()
# Find OpenAL (required).
find_package(OpenAL REQUIRED)
@@ -240,13 +249,17 @@ function(configure_wx_target target)
_add_include_directories(${wxWidgets_INCLUDE_DIRS})
_add_compile_options(${wxWidgets_CXX_FLAGS})
_add_compile_definitions(${wxWidgets_DEFINITIONS})
if(CMAKE_BUILD_TYPE MATCHES "^(Debug|RelWithDebInfo)$")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
_add_compile_definitions(${wxWidgets_DEFINITIONS_DEBUG})
endif()
# OpenAL.
if(OPENAL_STATIC)
_add_compile_definitions(AL_LIBTYPE_STATIC)
if(WIN32)
list(APPEND OPENAL_LIBRARY avrt)
endif()
endif()
_add_include_directories(${OPENAL_INCLUDE_DIR})
_add_link_libraries(${OPENAL_LIBRARY})
@@ -262,9 +275,15 @@ function(configure_wx_target target)
if(MSVC)
_add_link_libraries(FAudio::FAudio)
else()
_add_link_libraries(FAudio)
if(WIN32)
if(MINGW AND VBAM_STATIC)
_add_link_libraries(FAudio.a)
else()
_add_link_libraries(FAudio)
endif()
_add_link_libraries(dxguid uuid winmm ole32 advapi32 user32 mfplat mfreadwrite mfuuid propsys)
else()
_add_link_libraries(FAudio)
endif()
endif()
endif()
@@ -380,16 +399,15 @@ if(WIN32)
# Disable the auto-generated manifest from CMake.
target_link_options(visualboyadvance-m PRIVATE "/MANIFEST:NO")
endif()
# wxWidgets fails to bring in its dependency.
find_library(PCRE_LIB pcre2-16 REQUIRED)
target_link_libraries(visualboyadvance-m ${PCRE_LIB})
endif()
if(APPLE)
target_sources(visualboyadvance-m PRIVATE
macsupport.mm
)
target_link_libraries(visualboyadvance-m
tiff zstd deflate)
endif()
# link libgcc/libstdc++ statically on mingw
@@ -510,7 +528,7 @@ host_compile(${CMAKE_CURRENT_SOURCE_DIR}/bin2c.c ${BIN2C})
# Override wxrc when cross-compiling.
if(CMAKE_HOST_WIN32 AND CMAKE_CROSSCOMPILING)
set(WXRC ${CMAKE_SOURCE_DIR}/dependencies/wxrc/wxrc.exe)
set(WXRC ${CMAKE_SOURCE_DIR}/win32-deps/wxrc/wxrc.exe)
endif()
# Configure wxrc.
@@ -633,6 +651,12 @@ add_custom_command(
DEPENDS ${CMAKE_SOURCE_DIR}/src/vba-over.ini ${BIN2C}
)
add_custom_command(
OUTPUT ${VBAM_GENERATED_DIR}/wx/gb-builtin-over.h
COMMAND ${BIN2C} ${CMAKE_SOURCE_DIR}/src/gb-over.ini ${VBAM_GENERATED_DIR}/wx/gb-builtin-over.h gb_builtin_over
DEPENDS ${CMAKE_SOURCE_DIR}/src/gb-over.ini ${BIN2C}
)
set(VBAM_LOCALIZABLE_FILES ${VBAM_WX_COMMON} ${VBAM_LOCALIZABLE_WX_CONFIG_FILES})
list(APPEND VBAM_LOCALIZABLE_FILES
audio/internal/dsound.cpp
@@ -655,9 +679,9 @@ if(APPLE)
set(CMAKE_INSTALL_RPATH "@loader_path/../Frameworks")
endif()
if(WIN32 AND (X86_64 OR X86_32) AND ENABLE_ONLINEUPDATES)
if(WIN32 AND (X86_64 OR ARM64 OR X86_32) AND ENABLE_ONLINEUPDATES)
if(NOT DEFINED WINSPARKLE_BIN_RELEASE_DIR)
set(WINSPARKLE_BIN_RELEASE_DIR ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.6.0)
set(WINSPARKLE_BIN_RELEASE_DIR ${CMAKE_SOURCE_DIR}/win32-deps/WinSparkle-0.8.1)
endif()
target_include_directories(
@@ -666,9 +690,11 @@ if(WIN32 AND (X86_64 OR X86_32) AND ENABLE_ONLINEUPDATES)
)
if(X86_64)
set(WINSPARKLE_DLL ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.6.0/x64/Release/WinSparkle.dll)
else()
set(WINSPARKLE_DLL ${CMAKE_SOURCE_DIR}/dependencies/WinSparkle-0.6.0/Release/WinSparkle.dll)
set(WINSPARKLE_DLL ${WINSPARKLE_BIN_RELEASE_DIR}/x64/Release/WinSparkle.dll)
elseif(ARM64)
set(WINSPARKLE_DLL ${WINSPARKLE_BIN_RELEASE_DIR}/ARM64/Release/WinSparkle.dll)
elseif(X86_32)
set(WINSPARKLE_DLL ${WINSPARKLE_BIN_RELEASE_DIR}/Release/WinSparkle.dll)
endif()
configure_file(autoupdater/wxmsw/winsparkle-path.h.in ${CMAKE_BINARY_DIR}/winsparkle-path.h)
@@ -777,7 +803,7 @@ endif()
if(UPSTREAM_RELEASE AND WIN32)
set(home "$ENV{HOME}")
if((MSVC OR NOT CMAKE_CROSSCOMPILING) AND NOT DEFINED ENV{MSYSTEM_PREFIX})
if(NOT home OR ((MSVC OR NOT CMAKE_CROSSCOMPILING) AND NOT DEFINED ENV{MSYSTEM_PREFIX}))
set(home "$ENV{USERPROFILE}")
endif()
@@ -785,8 +811,9 @@ if(UPSTREAM_RELEASE AND WIN32)
string(REGEX REPLACE "\\\\" "/" home "${home}")
set(cert "${home}/.codesign/windows_comodo.pkcs12")
file(STRINGS "${home}/.codesign/windows_comodo.pkcs12.password" cert_password)
if(EXISTS "${cert}")
if(EXISTS "${cert}" AND cert_password)
find_program(OSSLSIGNCODE_PROGRAM osslsigncode)
find_program(SIGNTOOL_PROGRAM signtool)
@@ -795,7 +822,7 @@ if(UPSTREAM_RELEASE AND WIN32)
TARGET visualboyadvance-m
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E rename visualboyadvance-m.exe visualboyadvance-m-unsigned.exe
COMMAND ${OSSLSIGNCODE_PROGRAM} sign -pkcs12 ${cert} -pass "vbam3!13" -t http://timestamp.digicert.com -n visualboyadvance-m -i https://github.com/visualboyadvance-m/visualboyadvance-m -in visualboyadvance-m-unsigned.exe -out visualboyadvance-m.exe
COMMAND ${OSSLSIGNCODE_PROGRAM} sign -pkcs12 ${cert} -pass "${cert_password}" -t http://timestamp.digicert.com -n visualboyadvance-m -i https://github.com/visualboyadvance-m/visualboyadvance-m -in visualboyadvance-m-unsigned.exe -out visualboyadvance-m.exe
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
elseif(SIGNTOOL_PROGRAM)
@@ -803,7 +830,7 @@ if(UPSTREAM_RELEASE AND WIN32)
TARGET visualboyadvance-m
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy visualboyadvance-m.exe visualboyadvance-m-unsigned.exe
COMMAND ${SIGNTOOL_PROGRAM} sign /f ${cert} /fd certHash /td certHash /p "vbam3!13" /tr http://timestamp.digicert.com /du https://github.com/visualboyadvance-m/visualboyadvance-m /a visualboyadvance-m.exe
COMMAND ${SIGNTOOL_PROGRAM} sign /f ${cert} /fd certHash /td certHash /p "${cert_password}" /tr http://timestamp.digicert.com /du https://github.com/visualboyadvance-m/visualboyadvance-m /a visualboyadvance-m.exe
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
endif()
@@ -877,7 +904,7 @@ if(APPLE)
if(CMAKE_BUILD_TYPE MATCHES "^(Release|MinSizeRel)$")
add_custom_command(
TARGET visualboyadvance-m POST_BUILD
COMMAND ${CMAKE_SOURCE_DIR}/tools/macOS/third_party_libs_tool ./visualboyadvance-m.app
COMMAND bash ${CMAKE_SOURCE_DIR}/tools/macOS/third_party_libs_tool ./visualboyadvance-m.app
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)
endif()
@@ -1008,13 +1035,13 @@ install(
# Installation scripts.
install(
PROGRAMS ${PROJECT_BINARY_DIR}/visualboyadvance-m${CMAKE_EXECUTABLE_SUFFIX}
DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
FILES ${CMAKE_SOURCE_DIR}/src/vba-over.ini
DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/vbam
DESTINATION ${CMAKE_INSTALL_DATADIR}/vbam
)
if (UNIX)
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/visualboyadvance-m.6 DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man6)
install(FILES ${CMAKE_SOURCE_DIR}/src/debian/visualboyadvance-m.6 DESTINATION ${CMAKE_INSTALL_MANDIR}/man6)
endif()

View File

@@ -48,10 +48,7 @@ int FAGetDev(FAudio* fa) {
if (hr != 0) {
continue;
}
const std::vector<uint8_t> device_id_u8 =
config::utf16_to_utf8(reinterpret_cast<uint16_t*>(dd.DeviceID));
const wxString device_id = wxString::FromUTF8(
reinterpret_cast<const char*>(device_id_u8.data()), device_id_u8.size());
const wxString device_id = config::utf16_to_utf8(dd.DeviceID);
if (audio_device == device_id) {
return i;
}
@@ -62,6 +59,17 @@ int FAGetDev(FAudio* fa) {
class FAudio_BufferNotify : public FAudioVoiceCallback {
public:
FAudio_BufferNotify() {
OnBufferEnd = &FAudio_BufferNotify::StaticOnBufferEnd;
OnVoiceProcessingPassStart = &FAudio_BufferNotify::StaticOnVoiceProcessingPassStart;
OnVoiceProcessingPassEnd = &FAudio_BufferNotify::StaticOnVoiceProcessingPassEnd;
OnStreamEnd = &FAudio_BufferNotify::StaticOnStreamEnd;
OnBufferStart = &FAudio_BufferNotify::StaticOnBufferStart;
OnLoopEnd = &FAudio_BufferNotify::StaticOnLoopEnd;
OnVoiceError = &FAudio_BufferNotify::StaticOnVoiceError;
}
~FAudio_BufferNotify() = default;
// Waits for the buffer end event to be signaled for 10 seconds.
// Returns true if the buffer end event was signaled, false if the wait timed out.
bool WaitForSignal() {
@@ -74,17 +82,6 @@ public:
return was_signaled;
}
FAudio_BufferNotify() {
OnBufferEnd = &FAudio_BufferNotify::StaticOnBufferEnd;
OnVoiceProcessingPassStart = &FAudio_BufferNotify::StaticOnVoiceProcessingPassStart;
OnVoiceProcessingPassEnd = &FAudio_BufferNotify::StaticOnVoiceProcessingPassEnd;
OnStreamEnd = &FAudio_BufferNotify::StaticOnStreamEnd;
OnBufferStart = &FAudio_BufferNotify::StaticOnBufferStart;
OnLoopEnd = &FAudio_BufferNotify::StaticOnLoopEnd;
OnVoiceError = &FAudio_BufferNotify::StaticOnVoiceError;
}
~FAudio_BufferNotify() = default;
private:
// Signals that the buffer end event has occurred.
void SignalBufferEnd() {
@@ -160,6 +157,7 @@ FAudio_Output::FAudio_Output() : buffer_count_(OPTION(kSoundBuffers)) {
playing = false;
freq_ = 0;
currentBuffer = 0;
sound_buffer_len_ = 0;
device_changed = false;
faud = nullptr;
mVoice = nullptr;
@@ -394,7 +392,7 @@ void FAudio_Output::write(uint16_t* finalWave, int) {
// the maximum number of buffers is currently queued
if (!coreOptions.speedup && coreOptions.throttle && !gba_joybus_active) {
// wait for one buffer to finish playing
if (notify.WaitForSignal()) {
if (!notify.WaitForSignal()) {
device_changed = true;
}
} else {
@@ -500,14 +498,8 @@ std::vector<AudioDevice> GetFAudioDevices() {
}
// Convert to UTF-8.
const std::vector<uint8_t> display_name_u8 =
config::utf16_to_utf8(reinterpret_cast<uint16_t*>(dd.DisplayName));
const std::vector<uint8_t> device_id_u8 =
config::utf16_to_utf8(reinterpret_cast<uint16_t*>(dd.DeviceID));
const wxString display_name = wxString::FromUTF8(
reinterpret_cast<const char*>(display_name_u8.data()), display_name_u8.size());
const wxString device_id = wxString::FromUTF8(
reinterpret_cast<const char*>(device_id_u8.data()), device_id_u8.size());
const wxString display_name = config::utf16_to_utf8(dd.DisplayName);
const wxString device_id = config::utf16_to_utf8(dd.DeviceID);
devices.push_back({display_name, device_id});
}
@@ -520,4 +512,4 @@ std::unique_ptr<SoundDriver> CreateFAudioDriver() {
}
} // namespace internal
} // namespace audio
} // namespace audio

View File

@@ -126,7 +126,7 @@ static void toggleBitVar(bool *menuValue, int *globalVar, int mask)
EVT_HANDLER(wxID_OPEN, "Open ROM...")
{
static int open_ft = 0;
const wxString& gba_rom_dir = OPTION(kGBAROMDir);
const wxString gba_rom_dir = OPTION(kGBAROMDir);
// FIXME: ignore if non-existent or not a dir
wxString pats = _(
@@ -158,7 +158,7 @@ EVT_HANDLER(wxID_OPEN, "Open ROM...")
EVT_HANDLER(OpenGB, "Open GB...")
{
static int open_ft = 0;
const wxString& gb_rom_dir = OPTION(kGBROMDir);
const wxString gb_rom_dir = OPTION(kGBROMDir);
// FIXME: ignore if non-existent or not a dir
wxString pats = _(
@@ -185,7 +185,7 @@ EVT_HANDLER(OpenGB, "Open GB...")
EVT_HANDLER(OpenGBC, "Open GBC...")
{
static int open_ft = 0;
const wxString& gbc_rom_dir = OPTION(kGBGBCROMDir);
const wxString gbc_rom_dir = OPTION(kGBGBCROMDir);
// FIXME: ignore if non-existent or not a dir
wxString pats = _(
@@ -1990,7 +1990,7 @@ EVT_HANDLER(GameBoyAdvanceConfigure, "Game Boy Advance options...")
XRCCTRL(*dlg, "GameCode", wxControl)
->SetLabel(s);
cmt = wxString((const char*)&g_rom[0xa0], wxConvLibc, 12);
wxFileConfig* cfg = wxGetApp().overrides;
wxFileConfig* cfg = wxGetApp().overrides_.get();
if (cfg->HasGroup(s)) {
cfg->SetPath(s);
@@ -2024,7 +2024,7 @@ EVT_HANDLER(GameBoyAdvanceConfigure, "Game Boy Advance options...")
if (panel->game_type() == IMAGE_GBA) {
agbPrintEnable(OPTION(kPrefAgbPrint));
wxString s = wxString((const char*)&g_rom[0xac], wxConvLibc, 4);
wxFileConfig* cfg = wxGetApp().overrides;
wxFileConfig* cfg = wxGetApp().overrides_.get();
bool chg;
if (cfg->HasGroup(s)) {
@@ -2219,7 +2219,7 @@ EVT_HANDLER(FAQ, "VBA-M support forum")
EVT_HANDLER(Translate, "Translations")
{
wxLaunchDefaultBrowser(wxT("http://www.transifex.com/projects/p/vba-m"));
wxLaunchDefaultBrowser(wxT("https://explore.transifex.com/bgk/vba-m/"));
}
// was About
@@ -2297,7 +2297,7 @@ EVT_HANDLER(RetainAspect, "Retain aspect ratio when resizing")
EVT_HANDLER(Printer, "Enable printer emulation")
{
GetMenuOptionInt("Printer", &coreOptions.winGbPrinterEnabled, 1);
GetMenuOptionInt("Printer", &coreOptions.gbPrinterEnabled, 1);
#if (defined __WIN32__ || defined _WIN32)
#ifndef NO_LINK
gbSerialFunction = gbStartLink;
@@ -2305,7 +2305,7 @@ EVT_HANDLER(Printer, "Enable printer emulation")
gbSerialFunction = NULL;
#endif
#endif
if (coreOptions.winGbPrinterEnabled)
if (coreOptions.gbPrinterEnabled)
gbSerialFunction = gbPrinterSend;
update_opts();

View File

@@ -31,7 +31,6 @@ LIST(SORT EVLINES)
STRING(REGEX REPLACE ",\n\$" "\n" EVLINES "${EVLINES}")
FILE(APPEND "${CMDTAB}" ${EVLINES})
FILE(APPEND "${CMDTAB}" "};\n")
FILE(APPEND "${CMDTAB}" "const int ncmds = sizeof(cmdtab) / sizeof(cmdtab[0]);\n")
# cmdhandlers.h contains prototypes for all handlers
FILE(WRITE "${EVPROTO}" "// Generated from cmdevents.cpp; do not edit\n")

View File

@@ -312,7 +312,7 @@ std::array<Option, kNbOptions>& Option::All() {
Option(OptionID::kPrefFlashSize, &g_owned_opts.flash_size, 0, 1),
Option(OptionID::kPrefFrameSkip, &g_owned_opts.frame_skip, -1, 9),
Option(OptionID::kPrefGBPaletteOption, &gbPaletteOption, 0, 2),
Option(OptionID::kPrefGBPrinter, &coreOptions.winGbPrinterEnabled, 0, 1),
Option(OptionID::kPrefGBPrinter, &coreOptions.gbPrinterEnabled, 0, 1),
Option(OptionID::kPrefGDBBreakOnLoad, &g_owned_opts.gdb_break_on_load),
Option(OptionID::kPrefGDBPort, &gopts.gdb_port, 0, 65535),
#ifndef NO_LINK

View File

@@ -96,8 +96,8 @@ TEST(StrSplitWithSepTest, MultipleSepTokens) {
}
TEST(UTF16ToUTF8Test, Basic) {
std::vector<uint16_t> utf16 = {'f', 'o', 'o', 0};
auto vec = config::utf16_to_utf8(utf16.data());
const std::vector<uint16_t> utf16 = {'f', 'o', 'o', 0};
auto vec = config::utf16_to_utf8_vector(utf16.data());
ASSERT_EQ(vec.size(), 3);
@@ -108,8 +108,8 @@ TEST(UTF16ToUTF8Test, Basic) {
TEST(UTF16ToUTF8Test, MultiByte) {
// U+20AC EURO SIGN.
std::vector<uint16_t> utf16 = {0x20AC, 0};
auto vec = config::utf16_to_utf8(utf16.data());
const std::vector<uint16_t> utf16 = {0x20AC, 0};
auto vec = config::utf16_to_utf8_vector(utf16.data());
ASSERT_EQ(vec.size(), 3);
@@ -118,10 +118,25 @@ TEST(UTF16ToUTF8Test, MultiByte) {
EXPECT_EQ(vec[2], 0xAC);
}
TEST(UTF16ToUTF8Test, DualMultiByte) {
// This is a variant of the above to test the buffer reset is done properly.
const std::vector<uint16_t> utf16 = {0x20AC, 0x20AC, 0};
auto vec = config::utf16_to_utf8_vector(utf16.data());
ASSERT_EQ(vec.size(), 6);
EXPECT_EQ(vec[0], 0xE2);
EXPECT_EQ(vec[1], 0x82);
EXPECT_EQ(vec[2], 0xAC);
EXPECT_EQ(vec[3], 0xE2);
EXPECT_EQ(vec[4], 0x82);
EXPECT_EQ(vec[5], 0xAC);
}
TEST(UTF16ToUTF8Test, SurrogatePair) {
// U+1F914 THINKING FACE.
std::vector<uint16_t> utf16 = {0xD83E, 0xDD14, 0};
auto vec = config::utf16_to_utf8(utf16.data());
const std::vector<uint16_t> utf16 = {0xD83E, 0xDD14, 0};
auto vec = config::utf16_to_utf8_vector(utf16.data());
ASSERT_EQ(vec.size(), 4);
@@ -133,26 +148,26 @@ TEST(UTF16ToUTF8Test, SurrogatePair) {
TEST(UTF16ToUTF8Test, InvalidSurrogatePair) {
// U+D800 HIGH SURROGATE.
std::vector<uint16_t> utf16 = {0xD800, 0};
EXPECT_DEATH(config::utf16_to_utf8(utf16.data()), ".*");
const std::vector<uint16_t> utf16 = {0xD800, 0};
EXPECT_DEATH(config::utf16_to_utf8_vector(utf16.data()), ".*");
}
TEST(UTF16ToUTF8Test, InvalidSurrogatePair2) {
// U+D800 HIGH SURROGATE followed by U+0020 SPACE.
std::vector<uint16_t> utf16 = {0xD800, 0x0020, 0};
EXPECT_DEATH(config::utf16_to_utf8(utf16.data()), ".*");
const std::vector<uint16_t> utf16 = {0xD800, 0x0020, 0};
EXPECT_DEATH(config::utf16_to_utf8_vector(utf16.data()), ".*");
}
TEST(UTF16ToUTF8Test, InvalidSurrogatePair3) {
// U+D800 HIGH SURROGATE followed by U+D800 HIGH SURROGATE.
std::vector<uint16_t> utf16 = {0xD800, 0xD800, 0};
EXPECT_DEATH(config::utf16_to_utf8(utf16.data()), ".*");
const std::vector<uint16_t> utf16 = {0xD800, 0xD800, 0};
EXPECT_DEATH(config::utf16_to_utf8_vector(utf16.data()), ".*");
}
TEST(UTF16ToUTF8Test, FullString) {
// "foo€🤔"
std::vector<uint16_t> utf16 = {'f', 'o', 'o', 0x20AC, 0xD83E, 0xDD14, 0};
auto vec = config::utf16_to_utf8(utf16.data());
const std::vector<uint16_t> utf16 = {'f', 'o', 'o', 0x20AC, 0xD83E, 0xDD14, 0};
auto vec = config::utf16_to_utf8_vector(utf16.data());
ASSERT_EQ(vec.size(), 10);

View File

@@ -41,10 +41,10 @@ wxArrayString str_split_with_sep(const wxString& text, const wxString& sep)
return str_split(text, sep, true);
}
std::vector<uint8_t> utf16_to_utf8(const uint16_t* utf16) {
std::vector<uint8_t> utf16_to_utf8_vector(const uint16_t* utf16) {
std::vector<uint8_t> out;
for (size_t i = 0; utf16[i]; i++) {
uint16_t c = utf16[i];
const uint16_t c = utf16[i];
if (c < 0x80) {
out.push_back(c);
} else if (c < 0x800) {
@@ -77,4 +77,14 @@ std::vector<uint8_t> utf16_to_utf8(const uint16_t* utf16) {
return out;
}
wxString utf16_to_utf8(const uint16_t* utf16) {
std::vector<uint8_t> output_vector = utf16_to_utf8_vector(utf16);
return wxString::FromUTF8(reinterpret_cast<const char*>(output_vector.data()),
output_vector.size());
}
wxString utf16_to_utf8(const int16_t* utf16) {
return utf16_to_utf8(reinterpret_cast<const uint16_t*>(utf16));
}
} // namespace config

View File

@@ -19,7 +19,11 @@ wxArrayString str_split_with_sep(const wxString& text, const wxString& sep);
// Converts a null-terminated array of UTF-16 code units to a vector of UTF-8 code units.
// This will assert if the input is not a valid UTF-16 string.
std::vector<uint8_t> utf16_to_utf8(const uint16_t* utf16);
std::vector<uint8_t> utf16_to_utf8_vector(const uint16_t* utf16);
// Convenience functions to convert a null-terminated array of UTF-16 code units to a wxString.
wxString utf16_to_utf8(const uint16_t* utf16);
wxString utf16_to_utf8(const int16_t* utf16);
} // namespace config

View File

@@ -145,9 +145,11 @@ AccelConfig::AccelConfig(wxWindow* parent,
PopulateTreeWithMenu(&command_to_item_id_, tree_, id, menu->GetMenu(i), recents,
menu->GetMenuLabelText(i) + '\n');
}
tree_->ExpandAll();
tree_->SelectItem(menu_id);
// Set a minimum size for the tree so the default dialog size is reasonable.
tree_->SetMinSize(wxSize(300, 300));
int w, h;
current_keys_->GetTextExtent("CTRL-ALT-SHIFT-ENTER", &w, &h);
wxSize size(w, h);
@@ -177,6 +179,14 @@ AccelConfig::AccelConfig(wxWindow* parent,
Bind(wxEVT_BUTTON, &AccelConfig::OnResetAll, this, XRCID("ResetAll"));
Bind(wxEVT_TEXT, &AccelConfig::OnKeyInput, this, key_input_->GetId());
#if !WX_RESIZE_DIALOGS
// On wxGTK, the dialog resizing is broken, so we need to set a fixed size.
SetWindowStyle(GetWindowStyle() & ~wxRESIZE_BORDER);
const wxSize dialog_size(800, 800);
SetMinSize(dialog_size);
SetMaxSize(dialog_size);
#endif // !WX_RESIZE_DIALOGS
// And fit everything nicely.
Fit();
}

View File

@@ -38,15 +38,19 @@ private:
return;
}
#if WX_RESIZE_DIALOGS
const wxRect dialog_rect = this->Get()->GetRect();
this->SaveValue("x", dialog_rect.x);
this->SaveValue("y", dialog_rect.y);
this->SaveValue("width", dialog_rect.width);
this->SaveValue("height", dialog_rect.height);
#endif // WX_RESIZE_DIALOGS
}
bool Restore() override {
dialog_shown_ = true;
#if WX_RESIZE_DIALOGS
wxRect dialog_rect(0, 0, 0, 0);
if (!this->RestoreValue("x", &dialog_rect.x)) {
return false;
@@ -62,6 +66,7 @@ private:
};
this->Get()->SetSize(dialog_rect);
#endif // WX_RESIZE_DIALOGS
return true;
}

View File

@@ -8,6 +8,13 @@
#include "core/base/check.h"
#if defined(__WXGTK__)
// Moving and resizing dialogs does not work properly on wxGTK.
#define WX_RESIZE_DIALOGS 0
#else
#define WX_RESIZE_DIALOGS 1
#endif
namespace dialogs {
class BaseDialog : public wxDialog {

View File

@@ -330,7 +330,7 @@ void DisplayConfig::PopulatePluginOptions() {
plugin_selector_->Clear();
plugin_selector_->Append(_("None"), new wxStringClientData());
const wxString& selected_plugin = OPTION(kDispFilterPlugin);
const wxString selected_plugin = OPTION(kDispFilterPlugin);
bool is_plugin_selected = false;
for (const wxString& plugin : plugins) {

View File

@@ -56,11 +56,8 @@ bool SpeedupConfigValidator::TransferToWindow() {
bool SpeedupConfigValidator::TransferFromWindow() {
auto dialog = static_cast<SpeedupConfig*>(GetWindow());
uint32_t val = dialog->speedup_throttle_spin_->GetValue();
VBAM_CHECK(val >= 0);
if (val == 0) {
OPTION(kPrefSpeedupThrottle) = 0;
OPTION(kPrefSpeedupFrameSkip) = 0;
@@ -116,11 +113,8 @@ SpeedupConfig::SpeedupConfig(wxWindow* parent)
void SpeedupConfig::SetSpeedupThrottle(wxCommandEvent& evt)
{
VBAM_CHECK(evt.GetEventObject() == speedup_throttle_spin_);
uint32_t val = speedup_throttle_spin_->GetValue();
VBAM_CHECK(val >= 0);
if (val == 0) {
frame_skip_cb_->SetValue(false);
frame_skip_cb_->Disable();

View File

@@ -142,7 +142,10 @@ public:
if (server) {
char host[length];
GetLinkServerHost(host, length);
if (!GetLinkServerHost(host, length)) {
wxMessageBox(_("You must enter a valid host name"),
_("Host name invalid"), wxICON_ERROR | wxOK);
}
title.Printf(_("Waiting for clients..."));
connmsg.Printf(_("Server IP address is: %s\n"), wxString(host, wxConvLibc).c_str());
} else {
@@ -1825,10 +1828,10 @@ bool MainFrame::BindControls()
#endif
#ifdef __WXMAC__
// Remove UI Config menu item, because it only has an option that does nothing on mac.
wxMenuItem* ui_config_mi = XRCITEM("UIConfigure");
ui_config_mi->GetMenu()->Remove(ui_config_mi);
ui_config_mi = nullptr;
// Remove hide menubar in the UI Config submenu on macOS, because it is meaningless there.
wxMenuItem* hide_menu_bar = XRCITEM("HideMenuBar");
hide_menu_bar->GetMenu()->Remove(hide_menu_bar);
hide_menu_bar = nullptr;
#endif

View File

@@ -416,11 +416,9 @@ void update_shortcut_opts() {
// For keyboard shortcuts, it's easier to delete everything and start over.
cfg->DeleteGroup("/Keyboard");
cfg->SetPath("/Keyboard");
for (const auto& iter : wxGetApp().bindings()->GetKeyboardConfiguration()) {
cfg->Write(iter.first, iter.second);
}
cfg->SetPath("/");
// For joypads, we just compare the strings.
bool game_bindings_changed = false;

View File

@@ -189,19 +189,19 @@ void GameArea::LoadGame(const wxString& name)
wxString rp = fnfn.GetPath();
// can't really decide which dir to use, so try GBA first, then GB
const wxString& gba_rom_dir = OPTION(kGBAROMDir);
const wxString gba_rom_dir = OPTION(kGBAROMDir);
if (!wxGetApp().GetAbsolutePath(gba_rom_dir).empty()) {
fnfn.SetPath(wxGetApp().GetAbsolutePath(gba_rom_dir) + '/' + rp);
badfile = !fnfn.IsFileReadable();
}
const wxString& gb_rom_dir = OPTION(kGBROMDir);
const wxString gb_rom_dir = OPTION(kGBROMDir);
if (badfile && !wxGetApp().GetAbsolutePath(gb_rom_dir).empty()) {
fnfn.SetPath(wxGetApp().GetAbsolutePath(gb_rom_dir) + '/' + rp);
badfile = !fnfn.IsFileReadable();
}
const wxString& gbc_rom_dir = OPTION(kGBGBCROMDir);
const wxString gbc_rom_dir = OPTION(kGBGBCROMDir);
if (badfile && !wxGetApp().GetAbsolutePath(gbc_rom_dir).empty()) {
fnfn.SetPath(wxGetApp().GetAbsolutePath(gbc_rom_dir) + '/' + rp);
badfile = !fnfn.IsFileReadable();
@@ -287,6 +287,15 @@ void GameArea::LoadGame(const wxString& name)
gbApplyPatch(UTF8(pfn.GetFullPath()));
}
// Apply overrides.
wxFileConfig* cfg = wxGetApp().gb_overrides_.get();
const std::string& title = g_gbCartData.title();
if (cfg->HasGroup(title)) {
cfg->SetPath(title);
coreOptions.gbPrinterEnabled = cfg->Read("gbPrinter", coreOptions.gbPrinterEnabled);
cfg->SetPath("/");
}
// start sound; this must happen before CPU stuff
gb_effects_config.enabled = OPTION(kSoundGBEnableEffects);
gb_effects_config.surround = OPTION(kSoundGBSurround);
@@ -359,7 +368,7 @@ void GameArea::LoadGame(const wxString& name)
gbaUpdateRomSize(size);
}
wxFileConfig* cfg = wxGetApp().overrides;
wxFileConfig* cfg = wxGetApp().overrides_.get();
wxString id = wxString((const char*)&g_rom[0xac], wxConvLibc, 4);
if (cfg->HasGroup(id)) {
@@ -428,6 +437,9 @@ void GameArea::LoadGame(const wxString& name)
emusys = &GBASystem;
}
// Set sound volume.
soundSetVolume((float)OPTION(kSoundVolume) / 100.0);
if (OPTION(kGeomFullScreen)) {
GameArea::ShowFullScreen(true);
}
@@ -456,7 +468,7 @@ void GameArea::LoadGame(const wxString& name)
SuspendScreenSaver();
// probably only need to do this for GB carts
if (coreOptions.winGbPrinterEnabled)
if (coreOptions.gbPrinterEnabled)
gbSerialFunction = gbPrinterSend;
// probably only need to do this for GBA carts
@@ -666,6 +678,9 @@ void GameArea::UnloadGame(bool destruct)
if (loaded == IMAGE_GB) {
gbCleanUp();
gbCheatRemoveAll();
// Reset overrides.
coreOptions.gbPrinterEnabled = OPTION(kPrefGBPrinter);
} else if (loaded == IMAGE_GBA) {
CPUCleanUp();
cheatsDeleteAll(false);
@@ -952,10 +967,6 @@ void GameArea::ShowFullScreen(bool full)
while (!tlw->popups.empty())
tlw->popups.front()->Close();
// Some kbd accels can send a menu open event without a close event,
// this happens on Mac in HiDPI mode for the fullscreen toggle accel.
main_frame->SetMenusOpened(false);
// mouse stays blank whenever full-screen
HidePointer();
cursz_valid = true;

View File

@@ -1,10 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop">
<component type="desktop-application">
<id>org.visualboyadvance_m.visualboyadvance_m</id>
<metadata_license>CC-BY-SA-3.0</metadata_license>
<project_license>GPL-2.0</project_license>
<content_rating type="oars-1.1"/>
<name>VisualBoyAdvance-M</name>
<developer id="org.visualboyadvance-m">
<name>VisualBoyAdvance-M</name>
</developer>
<summary>VisualBoyAdvance-M, a high compatibility Gameboy Advance Emulator</summary>
<description>
<p>

View File

@@ -11,6 +11,7 @@ target_sources(vbam-wx-widgets
$<IF:$<BOOL:${APPLE}>,dpi-support-mac.mm,dpi-support.cpp>
group-check-box.cpp
keep-on-top-styler.cpp
keyboard-input-handler.cpp
option-validator.cpp
render-plugin.cpp
user-input-ctrl.cpp
@@ -28,6 +29,7 @@ target_sources(vbam-wx-widgets
event-handler-provider.h
group-check-box.h
keep-on-top-styler.h
keyboard-input-handler.h
option-validator.h
render-plugin.h
user-input-ctrl.h
@@ -46,8 +48,10 @@ if(BUILD_TESTING)
client-data-test.cpp
group-check-box-test.cpp
keep-on-top-styler-test.cpp
keyboard-input-handler-test.cpp
option-validator-test.cpp
user-input-ctrl-test.cpp
user-input-event-test.cpp
)
target_link_libraries(vbam-wx-widgets-tests
@@ -61,6 +65,7 @@ if(BUILD_TESTING)
vbam-wx-config
vbam-wx-widgets
GTest::gtest_main
GTest::gmock_main
)
configure_wx_target(vbam-wx-widgets-tests)

View File

@@ -0,0 +1,197 @@
#include "wx/widgets/keyboard-input-handler.h"
#include <algorithm>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <wx/event.h>
#include "wx/config/user-input.h"
#include "wx/widgets/event-handler-provider.h"
#include "wx/widgets/test/widgets-test.h"
#include "wx/widgets/user-input-event.h"
namespace widgets {
namespace {
class KeyboardInputHandlerTest : public WidgetsTest,
public wxEvtHandler,
public EventHandlerProvider {
public:
KeyboardInputHandlerTest() : handler_(this) {
Bind(VBAM_EVT_USER_INPUT, &KeyboardInputHandlerTest::OnUserInputEvent, this);
}
protected:
// Processes `key_event` and returns the data from the generated events.
std::vector<UserInputEvent::Data> ProcessKeyEvent(wxKeyEvent key_event) {
handler_.ProcessKeyEvent(key_event);
ProcessPendingEvents();
std::vector<UserInputEvent::Data> data;
std::swap(data, pending_data_);
return data;
}
private:
void OnUserInputEvent(UserInputEvent& event) {
// Do not let the event propagate.
event.Skip(false);
for (const auto& data : event.data()) {
pending_data_.emplace_back(data);
}
}
// EventHandlerProvider implementation.
wxEvtHandler* event_handler() override { return this; }
KeyboardInputHandler handler_;
std::vector<UserInputEvent::Data> pending_data_;
};
static constexpr config::KeyboardInput kF1(wxKeyCode::WXK_F1);
static constexpr config::KeyboardInput kCtrlF1(wxKeyCode::WXK_F1, wxMOD_CONTROL);
static constexpr config::KeyboardInput kCtrlShiftF1(wxKeyCode::WXK_F1,
static_cast<wxKeyModifier>(wxMOD_CONTROL |
wxMOD_SHIFT));
static constexpr config::KeyboardInput kCtrl(wxKeyCode::WXK_CONTROL, wxMOD_CONTROL);
static constexpr config::KeyboardInput kShift(wxKeyCode::WXK_SHIFT, wxMOD_SHIFT);
static const UserInputEvent::Data kF1Pressed(kF1, true);
static const UserInputEvent::Data kF1Released(kF1, false);
static const UserInputEvent::Data kCtrlF1Pressed(kCtrlF1, true);
static const UserInputEvent::Data kCtrlF1Released(kCtrlF1, false);
static const UserInputEvent::Data kCtrlShiftF1Pressed(kCtrlShiftF1, true);
static const UserInputEvent::Data kCtrlShiftF1Released(kCtrlShiftF1, false);
static const UserInputEvent::Data kCtrlPressed(kCtrl, true);
static const UserInputEvent::Data kCtrlReleased(kCtrl, false);
static const UserInputEvent::Data kShiftPressed(kShift, true);
static const UserInputEvent::Data kShiftReleased(kShift, false);
wxKeyEvent F1DownEvent() {
wxKeyEvent event(wxEVT_KEY_DOWN);
event.m_keyCode = WXK_F1;
return event;
}
wxKeyEvent F1UpEvent() {
wxKeyEvent event(wxEVT_KEY_UP);
event.m_keyCode = WXK_F1;
return event;
}
wxKeyEvent CtrlF1DownEvent() {
wxKeyEvent event(wxEVT_KEY_DOWN);
event.m_keyCode = WXK_F1;
event.m_controlDown = true;
return event;
}
wxKeyEvent CtrlF1UpEvent() {
wxKeyEvent event(wxEVT_KEY_UP);
event.m_keyCode = WXK_F1;
event.m_controlDown = true;
return event;
}
wxKeyEvent CtrlShiftF1DownEvent() {
wxKeyEvent event(wxEVT_KEY_DOWN);
event.m_keyCode = WXK_F1;
event.m_controlDown = true;
event.m_shiftDown = true;
return event;
}
wxKeyEvent CtrlShiftF1UpEvent() {
wxKeyEvent event(wxEVT_KEY_UP);
event.m_keyCode = WXK_F1;
event.m_controlDown = true;
event.m_shiftDown = true;
return event;
}
wxKeyEvent CtrlDownEvent() {
wxKeyEvent event(wxEVT_KEY_DOWN);
event.m_keyCode = WXK_CONTROL;
return event;
}
wxKeyEvent CtrlUpEvent() {
wxKeyEvent event(wxEVT_KEY_UP);
event.m_keyCode = WXK_CONTROL;
return event;
}
wxKeyEvent ShiftDownEvent() {
wxKeyEvent event(wxEVT_KEY_DOWN);
event.m_keyCode = WXK_SHIFT;
return event;
}
wxKeyEvent ShiftUpEvent() {
wxKeyEvent event(wxEVT_KEY_UP);
event.m_keyCode = WXK_SHIFT;
return event;
}
} // namespace
TEST_F(KeyboardInputHandlerTest, SimpleKeyDownUp) {
// Send F1 down event.
ASSERT_THAT(ProcessKeyEvent(F1DownEvent()), testing::ElementsAre(kF1Pressed));
// Send F1 up event.
ASSERT_THAT(ProcessKeyEvent(F1UpEvent()), testing::ElementsAre(kF1Released));
}
TEST_F(KeyboardInputHandlerTest, ModifierThenKey) {
// Ctrl Down -> F1 Down -> F1 Up -> Ctrl Up
ASSERT_THAT(ProcessKeyEvent(CtrlDownEvent()), testing::ElementsAre(kCtrlPressed));
ASSERT_THAT(ProcessKeyEvent(CtrlF1DownEvent()),
testing::ElementsAre(kCtrlF1Pressed, kF1Pressed));
ASSERT_THAT(ProcessKeyEvent(CtrlF1UpEvent()),
testing::ElementsAre(kCtrlF1Released, kF1Released));
ASSERT_THAT(ProcessKeyEvent(CtrlUpEvent()), testing::ElementsAre(kCtrlReleased));
}
TEST_F(KeyboardInputHandlerTest, KeyThenModifier) {
// F1 Down -> Ctrl Down -> Ctrl Up -> F1 Up
// In this case, no Ctrl+F1 event should be generated.
ASSERT_THAT(ProcessKeyEvent(F1DownEvent()), testing::ElementsAre(kF1Pressed));
ASSERT_THAT(ProcessKeyEvent(CtrlDownEvent()), testing::ElementsAre(kCtrlPressed));
ASSERT_THAT(ProcessKeyEvent(CtrlUpEvent()), testing::ElementsAre(kCtrlReleased));
ASSERT_THAT(ProcessKeyEvent(F1UpEvent()), testing::ElementsAre(kF1Released));
// F1 Down -> Ctrl Down -> F1 Up -> Ctrl Up
// In this case, a Ctrl+F1 event should be generated when F1 is released.
ASSERT_THAT(ProcessKeyEvent(F1DownEvent()), testing::ElementsAre(kF1Pressed));
ASSERT_THAT(ProcessKeyEvent(CtrlDownEvent()), testing::ElementsAre(kCtrlPressed));
ASSERT_THAT(ProcessKeyEvent(F1UpEvent()),
testing::ElementsAre(kCtrlF1Pressed, kCtrlF1Released, kF1Released));
ASSERT_THAT(ProcessKeyEvent(CtrlUpEvent()), testing::ElementsAre(kCtrlReleased));
}
TEST_F(KeyboardInputHandlerTest, Multiplemodifiers) {
// F1 Down -> Ctrl Down -> Shift Down -> F1 Up -> Ctrl Up -> Shift Up
// In this case, a Ctrl+Shift+F1 event should be generated when F1 is released.
ASSERT_THAT(ProcessKeyEvent(F1DownEvent()), testing::ElementsAre(kF1Pressed));
ASSERT_THAT(ProcessKeyEvent(CtrlDownEvent()), testing::ElementsAre(kCtrlPressed));
ASSERT_THAT(ProcessKeyEvent(ShiftDownEvent()), testing::ElementsAre(kShiftPressed));
ASSERT_THAT(ProcessKeyEvent(F1UpEvent()),
testing::ElementsAre(kCtrlShiftF1Pressed, kCtrlShiftF1Released, kF1Released));
ASSERT_THAT(ProcessKeyEvent(CtrlUpEvent()), testing::ElementsAre(kCtrlReleased));
ASSERT_THAT(ProcessKeyEvent(ShiftUpEvent()), testing::ElementsAre(kShiftReleased));
// Ctrl Down -> Shift Down -> F1 Down -> F1 Up -> Shift Up -> Ctrl Up
// In this case, a Ctrl+Shift+F1 event should be generated when F1 is pressed.
ASSERT_THAT(ProcessKeyEvent(CtrlDownEvent()), testing::ElementsAre(kCtrlPressed));
ASSERT_THAT(ProcessKeyEvent(ShiftDownEvent()), testing::ElementsAre(kShiftPressed));
ASSERT_THAT(ProcessKeyEvent(CtrlShiftF1DownEvent()),
testing::ElementsAre(kCtrlShiftF1Pressed, kF1Pressed));
ASSERT_THAT(ProcessKeyEvent(CtrlShiftF1UpEvent()),
testing::ElementsAre(kCtrlShiftF1Released, kF1Released));
ASSERT_THAT(ProcessKeyEvent(ShiftUpEvent()), testing::ElementsAre(kShiftReleased));
ASSERT_THAT(ProcessKeyEvent(CtrlUpEvent()), testing::ElementsAre(kCtrlReleased));
}
} // namespace widgets

View File

@@ -0,0 +1,273 @@
#include "wx/widgets/keyboard-input-handler.h"
#include "wx/config/user-input.h"
#include "wx/widgets/user-input-event.h"
#include <wx/log.h>
namespace widgets {
namespace {
// Filters the received key code in the key event for something we can use.
wxKeyCode FilterKeyCode(const wxKeyEvent& event) {
const wxKeyCode unicode_key = static_cast<wxKeyCode>(event.GetUnicodeKey());
if (unicode_key == WXK_NONE) {
// We need to filter out modifier keys here so we can differentiate
// between a key press and a modifier press.
const wxKeyCode keycode = static_cast<wxKeyCode>(event.GetKeyCode());
switch (keycode) {
case WXK_CONTROL:
case WXK_ALT:
case WXK_SHIFT:
#ifdef __WXMAC__
case WXK_RAW_CONTROL:
#endif
return WXK_NONE;
default:
return keycode;
}
}
if (unicode_key < 32) {
switch (unicode_key) {
case WXK_BACK:
case WXK_TAB:
case WXK_RETURN:
case WXK_ESCAPE:
return unicode_key;
default:
return WXK_NONE;
}
}
return unicode_key;
}
// Returns the set of modifiers for the given key event.
std::unordered_set<wxKeyModifier> GetModifiers(const wxKeyEvent& event) {
// Standalone modifier are treated as keys and do not set the keyboard modifiers.
switch (event.GetKeyCode()) {
case WXK_CONTROL:
return {wxMOD_CONTROL};
case WXK_ALT:
return {wxMOD_ALT};
case WXK_SHIFT:
return {wxMOD_SHIFT};
#ifdef __WXMAC__
case WXK_RAW_CONTROL:
return {wxMOD_RAW_CONTROL};
#endif
}
std::unordered_set<wxKeyModifier> mods;
if (event.ControlDown()) {
mods.insert(wxMOD_CONTROL);
}
if (event.AltDown()) {
mods.insert(wxMOD_ALT);
}
if (event.ShiftDown()) {
mods.insert(wxMOD_SHIFT);
}
#ifdef __WXMAC__
if (event.RawControlDown()) {
mods.insert(wxMOD_RAW_CONTROL);
}
#endif
return mods;
}
// Builds a wxKeyModifier from a set of modifiers.
wxKeyModifier GetModifiersFromSet(const std::unordered_set<wxKeyModifier>& mods) {
int mod = wxMOD_NONE;
for (const wxKeyModifier m : mods) {
mod |= m;
}
return static_cast<wxKeyModifier>(mod);
}
// Returns the key code for a standalone modifier.
wxKeyCode KeyFromModifier(const wxKeyModifier mod) {
switch (mod) {
case wxMOD_CONTROL:
return WXK_CONTROL;
case wxMOD_ALT:
return WXK_ALT;
case wxMOD_SHIFT:
return WXK_SHIFT;
#ifdef __WXMAC__
case wxMOD_RAW_CONTROL:
return WXK_RAW_CONTROL;
#endif
default:
return WXK_NONE;
}
}
} // namespace
KeyboardInputHandler::KeyboardInputHandler(EventHandlerProvider* const handler_provider)
: handler_provider_(handler_provider) {
VBAM_CHECK(handler_provider_);
}
KeyboardInputHandler::~KeyboardInputHandler() = default;
void KeyboardInputHandler::ProcessKeyEvent(wxKeyEvent& event) {
if (!handler_provider_->event_handler()) {
// No event handler to send the event to.
return;
}
if (event.GetEventType() == wxEVT_KEY_DOWN) {
OnKeyDown(event);
} else if (event.GetEventType() == wxEVT_KEY_UP) {
OnKeyUp(event);
}
}
void KeyboardInputHandler::Reset() {
active_keys_.clear();
active_mods_.clear();
active_mod_inputs_.clear();
}
void KeyboardInputHandler::OnKeyDown(wxKeyEvent& event) {
// Stop propagation of the event.
event.Skip(false);
const wxKeyCode key = FilterKeyCode(event);
const std::unordered_set<wxKeyModifier> mods = GetModifiers(event);
wxKeyCode key_pressed = WXK_NONE;
if (key != WXK_NONE) {
if (active_keys_.find(key) == active_keys_.end()) {
// Key was not pressed before.
key_pressed = key;
active_keys_.insert(key);
}
}
wxKeyModifier mod_pressed = wxMOD_NONE;
for (const wxKeyModifier mod : mods) {
if (active_mods_.find(mod) == active_mods_.end()) {
// Mod was not pressed before.
active_mods_.insert(mod);
mod_pressed = mod;
break;
}
}
if (key_pressed == WXK_NONE && mod_pressed == wxMOD_NONE) {
// No new keys or mods were pressed.
return;
}
const wxKeyModifier active_mods = GetModifiersFromSet(active_mods_);
std::vector<UserInputEvent::Data> event_data;
if (key_pressed == WXK_NONE) {
// A new standalone modifier was pressed, send the event.
event_data.emplace_back(config::KeyboardInput(KeyFromModifier(mod_pressed), mod_pressed),
true);
} else {
// A new key was pressed, send the event with modifiers, first.
event_data.emplace_back(config::KeyboardInput(key, active_mods), true);
if (active_mods != wxMOD_NONE) {
// Keep track of the key pressed with the active modifiers.
active_mod_inputs_.emplace(key, active_mods);
// Also send the key press event without modifiers.
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), true);
}
}
wxQueueEvent(handler_provider_->event_handler(), new UserInputEvent(std::move(event_data)));
}
void KeyboardInputHandler::OnKeyUp(wxKeyEvent& event) {
// Stop propagation of the event.
event.Skip(false);
const wxKeyCode key = FilterKeyCode(event);
const std::unordered_set<wxKeyModifier> mods = GetModifiers(event);
const wxKeyModifier previous_mods = GetModifiersFromSet(active_mods_);
wxKeyCode key_released = WXK_NONE;
if (key != WXK_NONE) {
auto iter = active_keys_.find(key);
if (iter != active_keys_.end()) {
// Key was pressed before.
key_released = key;
active_keys_.erase(iter);
}
}
wxKeyModifier mod_released = wxMOD_NONE;
if (key_released == WXK_NONE) {
// Only look for a standalone modifier if no key was released.
for (const wxKeyModifier mod : mods) {
auto iter = active_mods_.find(mod);
if (iter != active_mods_.end()) {
// Mod was pressed before.
mod_released = mod;
active_mods_.erase(iter);
break;
}
}
}
if (key_released == WXK_NONE && mod_released == wxMOD_NONE) {
// No keys or mods were released.
return;
}
std::vector<UserInputEvent::Data> event_data;
if (key_released == WXK_NONE) {
// A standalone modifier was released, send it.
event_data.emplace_back(config::KeyboardInput(KeyFromModifier(mod_released), mod_released),
false);
} else {
// A key was released.
if (previous_mods == wxMOD_NONE) {
// The key was pressed without modifiers, just send the key release event.
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), false);
} else {
// Check if the key was pressed with the active modifiers.
const config::KeyboardInput input_with_modifiers(key, previous_mods);
auto iter = active_mod_inputs_.find(input_with_modifiers);
if (iter == active_mod_inputs_.end()) {
// The key press event was never sent, so do it now.
event_data.emplace_back(input_with_modifiers, true);
} else {
active_mod_inputs_.erase(iter);
}
// Send the key release event with the active modifiers.
event_data.emplace_back(input_with_modifiers, false);
// Also send the key release event without modifiers.
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), false);
}
}
// Also check for any key that were pressed with the previously active
// modifiers and release them.
for (const wxKeyCode active_key : active_keys_) {
const config::KeyboardInput input(active_key, previous_mods);
auto iter = active_mod_inputs_.find(input);
if (iter != active_mod_inputs_.end()) {
active_mod_inputs_.erase(iter);
event_data.emplace_back(std::move(input), false);
}
}
for (const auto& data : event_data) {
active_mod_inputs_.erase(data.input.keyboard_input());
}
wxQueueEvent(handler_provider_->event_handler(), new UserInputEvent(std::move(event_data)));
}
} // namespace widgets

View File

@@ -0,0 +1,49 @@
#ifndef VBAM_WX_WIDGETS_KEYBOARD_INPUT_HANDLER_H_
#define VBAM_WX_WIDGETS_KEYBOARD_INPUT_HANDLER_H_
#include <unordered_set>
#include <wx/event.h>
#include "wx/config/user-input.h"
#include "wx/widgets/event-handler-provider.h"
namespace widgets {
// Object that is used to fire user input events when a keyboard key is pressed
// or released. This class should be kept as a singleton owned by the
// application object. It is meant to be used in the FilterEvent() method of the
// app to create user input events globally whenever the keyboard is used.
class KeyboardInputHandler final {
public:
explicit KeyboardInputHandler(EventHandlerProvider* const handler_provider);
~KeyboardInputHandler();
// Disable copy and copy assignment.
KeyboardInputHandler(const KeyboardInputHandler&) = delete;
KeyboardInputHandler& operator=(const KeyboardInputHandler&) = delete;
// Processes the provided key event and sends the appropriate user input
// event to the current event handler.
void ProcessKeyEvent(wxKeyEvent& event);
// Resets the state of the sender. This should be called when the main frame
// loses focus to prevent stuck keys.
void Reset();
private:
// Keyboard event handlers.
void OnKeyDown(wxKeyEvent& event);
void OnKeyUp(wxKeyEvent& event);
std::unordered_set<wxKeyCode> active_keys_;
std::unordered_set<wxKeyModifier> active_mods_;
std::unordered_set<config::KeyboardInput> active_mod_inputs_;
// The provider of event handlers to send the events to.
EventHandlerProvider* const handler_provider_;
};
} // namespace widgets
#endif // VBAM_WX_WIDGETS_KEYBOARD_INPUT_HANDLER_H_

View File

@@ -0,0 +1,53 @@
#include "wx/widgets/user-input-event.h"
#include <gtest/gtest.h>
#include <wx/eventfilter.h>
#include "wx/config/user-input.h"
namespace widgets {
namespace {
static constexpr config::KeyboardInput kF1(wxKeyCode::WXK_F1);
static constexpr config::KeyboardInput kCtrlF1(wxKeyCode::WXK_F1, wxMOD_CONTROL);
static constexpr config::JoyInput kButton0(config::JoyId(0), config::JoyControl::Button, 0);
} // namespace
TEST(UserInputEventTest, KeyboardInputEvent) {
// Press Ctrl+F1.
UserInputEvent pressed_event({{kCtrlF1, true}});
EXPECT_EQ(pressed_event.FirstReleasedInput(), nonstd::nullopt);
// Process Ctrl+F1.
EXPECT_EQ(pressed_event.FilterProcessedInput(kCtrlF1), wxEventFilter::Event_Processed);
EXPECT_EQ(pressed_event.data().size(), 0);
}
TEST(UserInputEventTest, JoystickInputEvent) {
// Press button 0.
UserInputEvent pressed_event({{kButton0, true}});
EXPECT_EQ(pressed_event.FirstReleasedInput(), nonstd::nullopt);
// Process button 0.
EXPECT_EQ(pressed_event.FilterProcessedInput(kButton0), wxEventFilter::Event_Processed);
EXPECT_EQ(pressed_event.data().size(), 0);
}
TEST(UserInputeventTest, MultipleInput) {
// Release F1 and Ctrl+F1.
UserInputEvent pressed_event({{kCtrlF1, false}, {kF1, false}});
EXPECT_EQ(pressed_event.FirstReleasedInput(), kCtrlF1);
// Process Ctrl+F1.
EXPECT_EQ(pressed_event.FilterProcessedInput(kCtrlF1), wxEventFilter::Event_Skip);
EXPECT_EQ(pressed_event.data().size(), 1);
// Process button 0.
EXPECT_EQ(pressed_event.FilterProcessedInput(kF1), wxEventFilter::Event_Processed);
EXPECT_EQ(pressed_event.data().size(), 0);
}
} // namespace widgets

View File

@@ -1,116 +1,15 @@
#include "wx/widgets/user-input-event.h"
#include <algorithm>
#include <utility>
#include <vector>
#include <wx/event.h>
#include <wx/eventfilter.h>
#include <wx/window.h>
#include "wx/config/user-input.h"
namespace widgets {
namespace {
// Filters the received key code in the key event for something we can use.
wxKeyCode FilterKeyCode(const wxKeyEvent& event) {
const wxKeyCode unicode_key = static_cast<wxKeyCode>(event.GetUnicodeKey());
if (unicode_key == WXK_NONE) {
// We need to filter out modifier keys here so we can differentiate
// between a key press and a modifier press.
const wxKeyCode keycode = static_cast<wxKeyCode>(event.GetKeyCode());
switch (keycode) {
case WXK_CONTROL:
case WXK_ALT:
case WXK_SHIFT:
#ifdef __WXMAC__
case WXK_RAW_CONTROL:
#endif
return WXK_NONE;
default:
return keycode;
}
}
if (unicode_key < 32) {
switch (unicode_key) {
case WXK_BACK:
case WXK_TAB:
case WXK_RETURN:
case WXK_ESCAPE:
return unicode_key;
default:
return WXK_NONE;
}
}
return unicode_key;
}
// Returns the set of modifiers for the given key event.
std::unordered_set<wxKeyModifier> GetModifiers(const wxKeyEvent& event) {
// Standalone modifier are treated as keys and do not set the keyboard modifiers.
switch (event.GetKeyCode()) {
case WXK_CONTROL:
return {wxMOD_CONTROL};
case WXK_ALT:
return {wxMOD_ALT};
case WXK_SHIFT:
return {wxMOD_SHIFT};
#ifdef __WXMAC__
case WXK_RAW_CONTROL:
return {wxMOD_RAW_CONTROL};
#endif
}
std::unordered_set<wxKeyModifier> mods;
if (event.ControlDown()) {
mods.insert(wxMOD_CONTROL);
}
if (event.AltDown()) {
mods.insert(wxMOD_ALT);
}
if (event.ShiftDown()) {
mods.insert(wxMOD_SHIFT);
}
#ifdef __WXMAC__
if (event.RawControlDown()) {
mods.insert(wxMOD_RAW_CONTROL);
}
#endif
return mods;
}
// Builds a wxKeyModifier from a set of modifiers.
wxKeyModifier GetModifiersFromSet(const std::unordered_set<wxKeyModifier>& mods) {
int mod = wxMOD_NONE;
for (const wxKeyModifier m : mods) {
mod |= m;
}
return static_cast<wxKeyModifier>(mod);
}
// Returns the key code for a standalone modifier.
wxKeyCode KeyFromModifier(const wxKeyModifier mod) {
switch (mod) {
case wxMOD_CONTROL:
return WXK_CONTROL;
case wxMOD_ALT:
return WXK_ALT;
case wxMOD_SHIFT:
return WXK_SHIFT;
#ifdef __WXMAC__
case wxMOD_RAW_CONTROL:
return WXK_RAW_CONTROL;
#endif
default:
return WXK_NONE;
}
}
} // namespace
UserInputEvent::UserInputEvent(std::vector<Data> event_data)
: wxEvent(0, VBAM_EVT_USER_INPUT), data_(std::move(event_data)) {}
@@ -119,7 +18,7 @@ nonstd::optional<config::UserInput> UserInputEvent::FirstReleasedInput() const {
std::find_if(data_.begin(), data_.end(), [](const auto& data) { return !data.pressed; });
if (iter == data_.end()) {
// No pressed inputs.
// No released inputs.
return nonstd::nullopt;
}
@@ -151,163 +50,6 @@ wxEvent* UserInputEvent::Clone() const {
return new UserInputEvent(this->data_);
}
KeyboardInputSender::KeyboardInputSender(EventHandlerProvider* const handler_provider)
: handler_provider_(handler_provider) {
VBAM_CHECK(handler_provider_);
}
KeyboardInputSender::~KeyboardInputSender() = default;
void KeyboardInputSender::ProcessKeyEvent(wxKeyEvent& event) {
if (!handler_provider_->event_handler()) {
// No event handler to send the event to.
return;
}
if (event.GetEventType() == wxEVT_KEY_DOWN) {
OnKeyDown(event);
} else if (event.GetEventType() == wxEVT_KEY_UP) {
OnKeyUp(event);
}
}
void KeyboardInputSender::OnKeyDown(wxKeyEvent& event) {
// Stop propagation of the event.
event.Skip(false);
const wxKeyCode key = FilterKeyCode(event);
const std::unordered_set<wxKeyModifier> mods = GetModifiers(event);
wxKeyCode key_pressed = WXK_NONE;
if (key != WXK_NONE) {
if (active_keys_.find(key) == active_keys_.end()) {
// Key was not pressed before.
key_pressed = key;
active_keys_.insert(key);
}
}
wxKeyModifier mod_pressed = wxMOD_NONE;
for (const wxKeyModifier mod : mods) {
if (active_mods_.find(mod) == active_mods_.end()) {
// Mod was not pressed before.
active_mods_.insert(mod);
mod_pressed = mod;
break;
}
}
if (key_pressed == WXK_NONE && mod_pressed == wxMOD_NONE) {
// No new keys or mods were pressed.
return;
}
const wxKeyModifier active_mods = GetModifiersFromSet(active_mods_);
std::vector<UserInputEvent::Data> event_data;
if (key_pressed == WXK_NONE) {
// A new standalone modifier was pressed, send the event.
event_data.emplace_back(config::KeyboardInput(KeyFromModifier(mod_pressed), mod_pressed),
true);
} else {
// A new key was pressed, send the event with modifiers, first.
event_data.emplace_back(config::KeyboardInput(key, active_mods), true);
if (active_mods != wxMOD_NONE) {
// Keep track of the key pressed with the active modifiers.
active_mod_inputs_.emplace(key, active_mods);
// Also send the key press event without modifiers.
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), true);
}
}
wxQueueEvent(handler_provider_->event_handler(), new UserInputEvent(std::move(event_data)));
}
void KeyboardInputSender::OnKeyUp(wxKeyEvent& event) {
// Stop propagation of the event.
event.Skip(false);
const wxKeyCode key = FilterKeyCode(event);
const std::unordered_set<wxKeyModifier> mods = GetModifiers(event);
const wxKeyModifier previous_mods = GetModifiersFromSet(active_mods_);
wxKeyCode key_released = WXK_NONE;
if (key != WXK_NONE) {
auto iter = active_keys_.find(key);
if (iter != active_keys_.end()) {
// Key was pressed before.
key_released = key;
active_keys_.erase(iter);
}
}
wxKeyModifier mod_released = wxMOD_NONE;
if (key_released == WXK_NONE) {
// Only look for a standalone modifier if no key was released.
for (const wxKeyModifier mod : mods) {
auto iter = active_mods_.find(mod);
if (iter != active_mods_.end()) {
// Mod was pressed before.
mod_released = mod;
active_mods_.erase(iter);
break;
}
}
}
if (key_released == WXK_NONE && mod_released == wxMOD_NONE) {
// No keys or mods were released.
return;
}
std::vector<UserInputEvent::Data> event_data;
if (key_released == WXK_NONE) {
// A standalone modifier was released, send it.
event_data.emplace_back(config::KeyboardInput(KeyFromModifier(mod_released), mod_released),
false);
} else {
// A key was released.
if (previous_mods == wxMOD_NONE) {
// The key was pressed without modifiers, just send the key release event.
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), false);
} else {
// Check if the key was pressed with the active modifiers.
const config::KeyboardInput input_with_modifiers(key, previous_mods);
auto iter = active_mod_inputs_.find(input_with_modifiers);
if (iter == active_mod_inputs_.end()) {
// The key press event was never sent, so do it now.
event_data.emplace_back(input_with_modifiers, true);
} else {
active_mod_inputs_.erase(iter);
}
// Send the key release event with the active modifiers.
event_data.emplace_back(config::KeyboardInput(key, previous_mods), false);
// Also send the key release event without modifiers.
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), false);
}
}
// Also check for any key that were pressed with the previously active
// modifiers and release them.
for (const wxKeyCode active_key : active_keys_) {
const config::KeyboardInput input(active_key, previous_mods);
auto iter = active_mod_inputs_.find(input);
if (iter != active_mod_inputs_.end()) {
active_mod_inputs_.erase(iter);
event_data.emplace_back(std::move(input), false);
}
}
for (const auto& data : event_data) {
active_mod_inputs_.erase(data.input.keyboard_input());
}
wxQueueEvent(handler_provider_->event_handler(), new UserInputEvent(std::move(event_data)));
}
} // namespace widgets
wxDEFINE_EVENT(VBAM_EVT_USER_INPUT, widgets::UserInputEvent);
wxDEFINE_EVENT(VBAM_EVT_USER_INPUT, ::widgets::UserInputEvent);

View File

@@ -1,7 +1,6 @@
#ifndef WX_WIDGETS_USER_INPUT_EVENT_H_
#define WX_WIDGETS_USER_INPUT_EVENT_H_
#ifndef VBAM_WX_WIDGETS_USER_INPUT_EVENT_H_
#define VBAM_WX_WIDGETS_USER_INPUT_EVENT_H_
#include <unordered_set>
#include <vector>
#include <optional.hpp>
@@ -10,7 +9,6 @@
#include <wx/event.h>
#include "wx/config/user-input.h"
#include "wx/widgets/event-handler-provider.h"
namespace widgets {
@@ -18,6 +16,8 @@ namespace widgets {
// contains the set of user input that were pressed or released. The order
// in the vector matters, this is the order in which the inputs were pressed or
// released.
// Note that a single event can contain multiple inputs pressed and/or released.
// These should be processed in the same order as they are in the vector.
class UserInputEvent final : public wxEvent {
public:
// Data for the event. Contains the user input and whether it was pressed or
@@ -26,7 +26,13 @@ public:
const config::UserInput input;
const bool pressed;
Data(config::UserInput input, bool pressed) : input(input), pressed(pressed){};
Data(config::UserInput input, bool pressed) : input(input), pressed(pressed) {};
// Equality operators.
bool operator==(const Data& other) const {
return input == other.input && pressed == other.pressed;
}
bool operator!=(const Data& other) const { return !(*this == other); }
};
UserInputEvent(std::vector<Data> event_data);
@@ -36,7 +42,7 @@ public:
UserInputEvent(const UserInputEvent&) = delete;
UserInputEvent& operator=(const UserInputEvent&) = delete;
// Returns the first pressed input, if any.
// Returns the first released input, if any.
nonstd::optional<config::UserInput> FirstReleasedInput() const;
// Marks `user_input` as processed and returns the new event filter. This is
@@ -53,38 +59,9 @@ private:
std::vector<Data> data_;
};
// Object that is used to fire user input events when a key is pressed or
// released. This class should be kept as a singleton owned by the application
// object. It is meant to be used in the FilterEvent() method of the app.
class KeyboardInputSender final : public wxClientData {
public:
explicit KeyboardInputSender(EventHandlerProvider* const handler_provider);
~KeyboardInputSender() override;
// Disable copy and copy assignment.
KeyboardInputSender(const KeyboardInputSender&) = delete;
KeyboardInputSender& operator=(const KeyboardInputSender&) = delete;
// Processes the provided key event and sends the appropriate user input
// event to the current event handler.
void ProcessKeyEvent(wxKeyEvent& event);
private:
// Keyboard event handlers.
void OnKeyDown(wxKeyEvent& event);
void OnKeyUp(wxKeyEvent& event);
std::unordered_set<wxKeyCode> active_keys_;
std::unordered_set<wxKeyModifier> active_mods_;
std::unordered_set<config::KeyboardInput> active_mod_inputs_;
// The provider of event handlers to send the events to.
EventHandlerProvider* const handler_provider_;
};
} // namespace widgets
// Fired when a set of user inputs are pressed or released.
wxDECLARE_EVENT(VBAM_EVT_USER_INPUT, widgets::UserInputEvent);
wxDECLARE_EVENT(VBAM_EVT_USER_INPUT, ::widgets::UserInputEvent);
#endif // WX_WIDGETS_USER_INPUT_EVENT_H_
#endif // VBAM_WX_WIDGETS_USER_INPUT_EVENT_H_

View File

@@ -20,6 +20,7 @@
#include <wx/progdlg.h>
#include <wx/protocol/http.h>
#include <wx/regex.h>
#include <wx/spinctrl.h>
#include <wx/sstream.h>
#include <wx/stdpaths.h>
#include <wx/string.h>
@@ -31,6 +32,7 @@
#include "components/user_config/user_config.h"
#include "core/gba/gbaSound.h"
#include "wx/gb-builtin-over.h"
#include "wx/builtin-over.h"
#include "wx/builtin-xrc.h"
#include "wx/config/cmdtab.h"
@@ -248,7 +250,14 @@ wxvbamApp::wxvbamApp()
using_wayland(false),
emulated_gamepad_(std::bind(&wxvbamApp::bindings, this)),
sdl_poller_(this),
keyboard_input_sender_(this) {}
keyboard_input_handler_(this) {
Bind(wxEVT_ACTIVATE_APP, [this](wxActivateEvent& event) {
if (!event.GetActive()) {
keyboard_input_handler_.Reset();
}
event.Skip();
});
}
const wxString wxvbamApp::GetPluginsDir()
{
@@ -463,13 +472,17 @@ bool wxvbamApp::OnInit() {
}
}
// load gb-over.ini
wxMemoryInputStream stream(gb_builtin_over, sizeof(gb_builtin_over));
gb_overrides_ = std::make_unique<wxFileConfig>(stream);
// load vba-over.ini
// rather than dealing with wxConfig's broken search path, just use
// the same one that the xrc overrides use
// this also allows us to override a group at a time, add commments, and
// add the file from which the group came
wxMemoryInputStream mis(builtin_over, sizeof(builtin_over));
overrides = new wxFileConfig(mis);
overrides_ = std::make_unique<wxFileConfig>(mis);
wxRegEx cmtre;
// not the most efficient thing to do: read entire file into a string
// just to parse the comments out
@@ -479,8 +492,8 @@ bool wxvbamApp::OnInit() {
long grp_idx;
#define CMT_RE_START wxT("(^|[\n\r])# ?([^\n\r]*)(\r?\n|\r)\\[")
for (cont = overrides->GetFirstGroup(s, grp_idx); cont;
cont = overrides->GetNextGroup(s, grp_idx)) {
for (cont = overrides_->GetFirstGroup(s, grp_idx); cont;
cont = overrides_->GetNextGroup(s, grp_idx)) {
// apparently even MacOSX sometimes uses the old \r by itself
wxString cmt(CMT_RE_START);
cmt += s + wxT("\\]");
@@ -490,7 +503,7 @@ bool wxvbamApp::OnInit() {
else
cmt = wxEmptyString;
overrides->Write(s + wxT("/comment"), cmt);
overrides_->Write(s + wxT("/comment"), cmt);
}
if (vba_over.FileExists()) {
@@ -506,10 +519,10 @@ bool wxvbamApp::OnInit() {
for (cont = ov.GetFirstGroup(s, grp_idx); cont;
cont = ov.GetNextGroup(s, grp_idx)) {
overrides->DeleteGroup(s);
overrides->SetPath(s);
overrides_->DeleteGroup(s);
overrides_->SetPath(s);
ov.SetPath(s);
overrides->Write(wxT("path"), GetConfigurationPath());
overrides_->Write(wxT("path"), GetConfigurationPath());
// apparently even MacOSX sometimes uses \r by itself
wxString cmt(CMT_RE_START);
cmt += s + wxT("\\]");
@@ -519,15 +532,15 @@ bool wxvbamApp::OnInit() {
else
cmt = wxEmptyString;
overrides->Write(wxT("comment"), cmt);
overrides_->Write(wxT("comment"), cmt);
long ent_idx;
for (cont = ov.GetFirstEntry(s, ent_idx); cont;
cont = ov.GetNextEntry(s, ent_idx))
overrides->Write(s, ov.Read(s, wxEmptyString));
overrides_->Write(s, ov.Read(s, wxEmptyString));
ov.SetPath(wxT("/"));
overrides->SetPath(wxT("/"));
overrides_->SetPath(wxT("/"));
}
}
@@ -815,7 +828,6 @@ wxvbamApp::~wxvbamApp() {
free(home);
home = NULL;
}
delete overrides;
#ifndef NO_ONLINEUPDATES
shutdownAutoupdater();
@@ -851,7 +863,6 @@ MainFrame::MainFrame()
paused(false),
menus_opened(0),
dialog_opened(0),
focused(false),
#ifndef NO_LINK
gba_link_observer_(config::OptionID::kGBALinkHost,
std::bind(&MainFrame::EnableNetworkMenu, this)),
@@ -894,25 +905,35 @@ EVT_MOVE_START(MainFrame::OnMoveStart)
EVT_MOVE_END(MainFrame::OnMoveEnd)
EVT_SIZE(MainFrame::OnSize)
#if defined(__WXMSW__)
// For tracking menubar state.
EVT_MENU_OPEN(MainFrame::MenuPopped)
EVT_MENU_CLOSE(MainFrame::MenuPopped)
EVT_MENU_HIGHLIGHT_ALL(MainFrame::MenuPopped)
#endif // defined(__WXMSW__)
END_EVENT_TABLE()
void MainFrame::OnActivate(wxActivateEvent& event)
{
focused = event.GetActive();
void MainFrame::OnActivate(wxActivateEvent& event) {
const bool focused = event.GetActive();
if (panel && focused)
if (!panel) {
// Nothing more to do if no game is active.
return;
}
if (focused) {
// Set the focus to the game panel.
panel->SetFocus();
}
if (OPTION(kPrefPauseWhenInactive)) {
if (panel && focused && !paused) {
// Handle user preferences for pausing the game when the window is inactive.
if (focused && !paused) {
panel->Resume();
}
else if (panel && !focused) {
} else if (!focused) {
panel->Pause();
}
}
@@ -1152,8 +1173,9 @@ void MainFrame::ResetMenuAccelerators() {
ResetRecentAccelerators();
}
void MainFrame::MenuPopped(wxMenuEvent& evt)
{
#if defined(__WXMSW__)
void MainFrame::MenuPopped(wxMenuEvent& evt) {
// We consider the menu closed when the main menubar or system menu is closed, not any submenus.
// On Windows nullptr is the system menu.
if (evt.GetEventType() == wxEVT_MENU_CLOSE && (evt.GetMenu() == nullptr || evt.GetMenu()->GetMenuBar() == GetMenuBar()))
@@ -1166,18 +1188,16 @@ void MainFrame::MenuPopped(wxMenuEvent& evt)
// On Windows, opening the menubar will stop the app, but DirectSound will
// loop, so we pause audio here.
void MainFrame::SetMenusOpened(bool state)
{
void MainFrame::SetMenusOpened(bool state) {
menus_opened = state;
#ifdef __WXMSW__
if (menus_opened)
soundPause();
else if (!paused)
soundResume();
#endif
}
#endif // defined(__WXMSW__)
// ShowModal that also disables emulator loop
// uses dialog_opened as a nesting counter
int MainFrame::ShowModal(wxDialog* dlg)
@@ -1334,11 +1354,7 @@ int wxvbamApp::FilterEvent(wxEvent& event)
if (event.GetEventType() == wxEVT_KEY_DOWN || event.GetEventType() == wxEVT_KEY_UP) {
// Handle keyboard input events here to generate user input events.
keyboard_input_sender_.ProcessKeyEvent(static_cast<wxKeyEvent&>(event));
return wxEventFilter::Event_Skip;
}
if (!frame->CanProcessShortcuts()) {
keyboard_input_handler_.ProcessKeyEvent(static_cast<wxKeyEvent&>(event));
return wxEventFilter::Event_Skip;
}
@@ -1347,6 +1363,10 @@ int wxvbamApp::FilterEvent(wxEvent& event)
return wxEventFilter::Event_Skip;
}
if (!frame->CanProcessShortcuts()) {
return wxEventFilter::Event_Skip;
}
widgets::UserInputEvent& user_input_event = static_cast<widgets::UserInputEvent&>(event);
int command_id = wxID_NONE;
nonstd::optional<config::UserInput> user_input;
@@ -1372,10 +1392,41 @@ int wxvbamApp::FilterEvent(wxEvent& event)
return wxEventFilter::Event_Skip;
}
// Queue the associated shortcut command.
wxCommandEvent* command_event = new wxCommandEvent(wxEVT_COMMAND_MENU_SELECTED, command_id);
// Find the associated checkable menu item (if any).
for (const cmditem& cmd_item : cmdtab) {
if (cmd_item.cmd_id == command_id) {
if (cmd_item.mi && cmd_item.mi->IsCheckable()) {
// Toggle the checkable menu item.
cmd_item.mi->Check(!cmd_item.mi->IsChecked());
}
break;
}
}
// Queue the associated shortcut command event.
wxCommandEvent* command_event = new wxCommandEvent(wxEVT_MENU, command_id);
command_event->SetEventObject(this);
frame->GetEventHandler()->QueueEvent(command_event);
return user_input_event.FilterProcessedInput(user_input.value());
}
bool wxvbamApp::ProcessEvent(wxEvent& event) {
if (event.GetEventType() == wxEVT_KEY_DOWN) {
// First, figure out if the focused window can process the key down event.
wxWindow* focused_window = wxWindow::FindFocus();
wxTextCtrl* text_ctrl = wxDynamicCast(focused_window, wxTextCtrl);
if (text_ctrl) {
return wxApp::ProcessEvent(event);
}
wxSpinCtrl* spin_ctrl = wxDynamicCast(focused_window, wxSpinCtrl);
if (spin_ctrl) {
return wxApp::ProcessEvent(event);
}
// Mark the event as processed. This prevents wxWidgets from firing alerts on macOS.
// See https://github.com/wxWidgets/wxWidgets/issues/25262 for details.
return true;
}
return wxApp::ProcessEvent(event);
}

View File

@@ -1,12 +1,13 @@
#ifndef VBAM_WX_WXVBAM_H_
#define VBAM_WX_WXVBAM_H_
#include <cstdio>
#include <ctime>
#include <list>
#include <memory>
#include <stdexcept>
#include <typeinfo>
#include <iostream>
#include <stdio.h>
#include <time.h>
#include <wx/log.h>
#include <wx/propdlg.h>
#include <wx/datetime.h>
@@ -20,6 +21,7 @@
#include "wx/widgets/dpi-support.h"
#include "wx/widgets/event-handler-provider.h"
#include "wx/widgets/keep-on-top-styler.h"
#include "wx/widgets/keyboard-input-handler.h"
#include "wx/widgets/sdl-poller.h"
#include "wx/widgets/user-input-event.h"
#include "wx/widgets/wxmisc.h"
@@ -82,6 +84,7 @@ public:
return false;
}
}
bool ProcessEvent(wxEvent& event) final;
wxString GetConfigDir();
wxString GetDataDir();
@@ -97,7 +100,7 @@ public:
bool pending_fullscreen;
#if __WXMAC__
// I suppose making this work will require tweaking the bundle
void MacOpenFile(const wxString& f)
void MacOpenFile(const wxString& f) override
{
pending_load = f;
};
@@ -106,7 +109,8 @@ public:
widgets::SdlPoller* sdl_poller() { return &sdl_poller_; }
// vba-over.ini
wxFileConfig* overrides = nullptr;
std::unique_ptr<wxFileConfig> overrides_;
std::unique_ptr<wxFileConfig> gb_overrides_;
wxFileName rom_database;
wxFileName rom_database_scene;
@@ -144,7 +148,7 @@ private:
char* home = nullptr;
widgets::SdlPoller sdl_poller_;
widgets::KeyboardInputSender keyboard_input_sender_;
widgets::KeyboardInputHandler keyboard_input_handler_;
// Main configuration file.
wxFileName config_file_;
@@ -219,10 +223,15 @@ public:
// possible
void StartModal();
void StopModal();
// however, adding a handler for open/close menu to do the same is unsafe.
// there is no guarantee every show will be matched by a hide.
#if defined(__WXMSW__)
// On Windows, we need to disable the audio loop when the menu is open. We also disable
// shortcuts to prevent issues. This is not necessary on other systems.
void MenuPopped(wxMenuEvent& evt);
#endif // defined(__WXMSW__)
// flags for enabling commands
int cmd_enable;
@@ -246,9 +255,6 @@ public:
// Resets all menu accelerators.
void ResetMenuAccelerators();
// 2.8 has no HasFocus(), and FindFocus() doesn't work right
bool HasFocus() const override { return focused; }
#ifndef NO_LINK
// Returns the link mode to set according to the options
LinkMode GetConfiguredLinkMode();
@@ -285,7 +291,9 @@ public:
virtual bool MenusOpened() { return menus_opened; }
virtual void SetMenusOpened(bool state);
#if defined(__WXMSW__)
void SetMenusOpened(bool state);
#endif // defined(__WXMSW__)
virtual bool DialogOpened() { return dialog_opened != 0; }
@@ -319,8 +327,6 @@ private:
checkable_mi_array_t checkable_mi;
// recent menu item accels
wxMenu* recent;
// quicker & more accurate than FindFocus() != NULL
bool focused;
// One-time toggle to indicate that this object is fully initialized. This
// used to filter events that are sent during initialization.
bool init_complete_ = false;

View File

@@ -2,7 +2,7 @@
<resource xmlns="http://www.wxwidgets.org/wxxrc" version="2.5.3.0">
<object class="wxDialog" name="AccelConfig">
<title>Key Shortcuts</title>
<style>wxRESIZE_BORDER</style>
<style>wxRESIZE_BORDER|wxCLOSE_BOX</style>
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<flag>wxEXPAND</flag>
@@ -56,6 +56,7 @@
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<object class="wxBoxSizer">
<object class="sizeritem">
<object class="wxButton" name="Assign">
@@ -80,7 +81,6 @@
</object>
<orient>wxVERTICAL</orient>
</object>
<flag>wxEXPAND</flag>
</object>
<object class="sizeritem">
<flag>wxALL</flag>
@@ -98,6 +98,7 @@
</object>
</object>
<object class="sizeritem">
<flag>wxEXPAND</flag>
<object class="wxBoxSizer">
<object class="sizeritem">
<flag>wxALL</flag>
@@ -113,7 +114,6 @@
</object>
<orient>wxVERTICAL</orient>
</object>
<flag>wxEXPAND</flag>
</object>
</object>
</object>

1
third_party/googletest vendored Submodule

Submodule third_party/googletest added at b514bdc898

6075
third_party/include/ghc/filesystem.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

38
third_party/include/ghc/fs_fwd.hpp vendored Normal file
View File

@@ -0,0 +1,38 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//---------------------------------------------------------------------------------------
// fs_fwd.hpp - The forwarding header for the header/implementation separated usage of
// ghc::filesystem.
// This file can be include at any place, where ghc::filesystem api is needed while
// not bleeding implementation details (e.g. system includes) into the global namespace,
// as long as one cpp includes fs_impl.hpp to deliver the matching implementations.
//---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_FWD_H
#define GHC_FILESYSTEM_FWD_H
#define GHC_FILESYSTEM_FWD
#include "filesystem.hpp"
#endif // GHC_FILESYSTEM_FWD_H

35
third_party/include/ghc/fs_impl.hpp vendored Normal file
View File

@@ -0,0 +1,35 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//---------------------------------------------------------------------------------------
// fs_impl.hpp - The implementation header for the header/implementation separated usage of
// ghc::filesystem.
// This file can be used to hide the implementation of ghc::filesystem into a single cpp.
// The cpp has to include this before including fs_fwd.hpp directly or via a different
// header to work.
//---------------------------------------------------------------------------------------
#define GHC_FILESYSTEM_IMPLEMENTATION
#include "filesystem.hpp"

77
third_party/include/ghc/fs_std.hpp vendored Normal file
View File

@@ -0,0 +1,77 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//---------------------------------------------------------------------------------------
// fs_std.hpp - The dynamic switching header that includes std::filesystem if detected
// or ghc::filesystem if not, and makes the resulting API available in the
// namespace fs.
//---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_STD_H
#define GHC_FILESYSTEM_STD_H
#if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include)
// ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus
// _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
#if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
#define GHC_USE_STD_FS
// Old Apple OSs don't support std::filesystem, though the header is available at compile
// time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
// and watchOS 6.0.
#ifdef __APPLE__
#include <Availability.h>
// Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
// released after std::filesystem, where std::filesystem is always available.
// (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
|| defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__WATCH_OS_VERSION_MAX_ALLOWED) && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
#undef GHC_USE_STD_FS
#endif
#endif
#endif
#endif
#ifdef GHC_USE_STD_FS
#include <filesystem>
namespace fs {
using namespace std::filesystem;
using ifstream = std::ifstream;
using ofstream = std::ofstream;
using fstream = std::fstream;
}
#else
#include "filesystem.hpp"
namespace fs {
using namespace ghc::filesystem;
using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif
#endif // GHC_FILESYSTEM_STD_H

79
third_party/include/ghc/fs_std_fwd.hpp vendored Normal file
View File

@@ -0,0 +1,79 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//---------------------------------------------------------------------------------------
// fs_std_fwd.hpp - The forwarding header for the header/implementation separated usage of
// ghc::filesystem that uses std::filesystem if it detects it.
// This file can be include at any place, where fs::filesystem api is needed while
// not bleeding implementation details (e.g. system includes) into the global namespace,
// as long as one cpp includes fs_std_impl.hpp to deliver the matching implementations.
//---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_STD_FWD_H
#define GHC_FILESYSTEM_STD_FWD_H
#if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include)
// ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus
// _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
#if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
#define GHC_USE_STD_FS
// Old Apple OSs don't support std::filesystem, though the header is available at compile
// time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
// and watchOS 6.0.
#ifdef __APPLE__
#include <Availability.h>
// Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
// released after std::filesystem, where std::filesystem is always available.
// (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
|| defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__WATCH_OS_VERSION_MAX_ALLOWED) && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
#undef GHC_USE_STD_FS
#endif
#endif
#endif
#endif
#ifdef GHC_USE_STD_FS
#include <filesystem>
namespace fs {
using namespace std::filesystem;
using ifstream = std::ifstream;
using ofstream = std::ofstream;
using fstream = std::fstream;
}
#else
#include "fs_fwd.hpp"
namespace fs {
using namespace ghc::filesystem;
using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif
#endif // GHC_FILESYSTEM_STD_FWD_H

60
third_party/include/ghc/fs_std_impl.hpp vendored Normal file
View File

@@ -0,0 +1,60 @@
//---------------------------------------------------------------------------------------
//
// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14
//
//---------------------------------------------------------------------------------------
//
// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
//---------------------------------------------------------------------------------------
// fs_std_impl.hpp - The implementation header for the header/implementation separated usage of
// ghc::filesystem that does nothing if std::filesystem is detected.
// This file can be used to hide the implementation of ghc::filesystem into a single cpp.
// The cpp has to include this before including fs_std_fwd.hpp directly or via a different
// header to work.
//---------------------------------------------------------------------------------------
#if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include)
// ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus
// _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
#if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
#define GHC_USE_STD_FS
// Old Apple OSs don't support std::filesystem, though the header is available at compile
// time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
// and watchOS 6.0.
#ifdef __APPLE__
#include <Availability.h>
// Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
// released after std::filesystem, where std::filesystem is always available.
// (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
|| defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__WATCH_OS_VERSION_MAX_ALLOWED) && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
#undef GHC_USE_STD_FS
#endif
#endif
#endif
#endif
#ifndef GHC_USE_STD_FS
#include "fs_impl.hpp"
#endif

View File

@@ -22,4 +22,4 @@ else()
optional_CONFIG_SELECT_OPTIONAL=optional_OPTIONAL_STD
variant_CONFIG_SELECT_VARIANT=variant_VARIANT_STD
)
endif()
endif()

View File

@@ -12,7 +12,7 @@
#define NONSTD_OPTIONAL_LITE_HPP
#define optional_lite_MAJOR 3
#define optional_lite_MINOR 5
#define optional_lite_MINOR 6
#define optional_lite_PATCH 0
#define optional_lite_VERSION optional_STRINGIFY(optional_lite_MAJOR) "." optional_STRINGIFY(optional_lite_MINOR) "." optional_STRINGIFY(optional_lite_PATCH)
@@ -50,6 +50,14 @@
#define optional_CONFIG_NO_EXTENSIONS 0
#endif
// Control marking class bad_optional_access and several methods with [[nodiscard]]]:
#if !defined(optional_CONFIG_NO_NODISCARD)
# define optional_CONFIG_NO_NODISCARD 0
#else
# define optional_CONFIG_NO_NODISCARD 1
#endif
// Control presence of exception handling (try and auto discover):
#ifndef optional_CONFIG_NO_EXCEPTIONS
@@ -386,7 +394,7 @@ namespace nonstd {
# define optional_constexpr14 /*constexpr*/
#endif
#if optional_HAVE( NODISCARD )
#if optional_HAVE( NODISCARD ) && !optional_CONFIG_NO_NODISCARD
# define optional_nodiscard [[nodiscard]]
#else
# define optional_nodiscard /*[[nodiscard]]*/
@@ -789,7 +797,7 @@ union storage_t
void construct_value( value_type && v )
{
::new( value_ptr() ) value_type( std::move( v ) );
::new( const_cast<void *>(static_cast<const volatile void *>(value_ptr())) ) value_type( std::move( v ) );
}
template< class... Args >
@@ -801,13 +809,13 @@ union storage_t
template< class... Args >
void emplace( Args&&... args )
{
::new( value_ptr() ) value_type( std::forward<Args>(args)... );
::new( const_cast<void *>(static_cast<const volatile void *>(value_ptr())) ) value_type( std::forward<Args>(args)... );
}
template< class U, class... Args >
void emplace( std::initializer_list<U> il, Args&&... args )
{
::new( value_ptr() ) value_type( il, std::forward<Args>(args)... );
::new( const_cast<void *>(static_cast<const volatile void *>(value_ptr())) ) value_type( il, std::forward<Args>(args)... );
}
#endif
@@ -917,7 +925,7 @@ const nullopt_t nullopt(( nullopt_t::init() ));
#if ! optional_CONFIG_NO_EXCEPTIONS
class bad_optional_access : public std::logic_error
class optional_nodiscard bad_optional_access : public std::logic_error
{
public:
explicit bad_optional_access()
@@ -1555,7 +1563,7 @@ private:
void initialize( V && value )
{
assert( ! has_value() );
contained.construct_value( std::move( value ) );
contained.construct_value( std::forward<V>( value ) );
has_value_ = true;
}

View File

@@ -47,6 +47,14 @@
# define variant_CONFIG_OMIT_VARIANT_ALTERNATIVE_T_MACRO 0
#endif
// Control marking class bad_variant_access with [[nodiscard]]]:
#if !defined(variant_CONFIG_NO_NODISCARD)
# define variant_CONFIG_NO_NODISCARD 0
#else
# define variant_CONFIG_NO_NODISCARD 1
#endif
// Control presence of exception handling (try and auto discover):
#ifndef variant_CONFIG_NO_EXCEPTIONS
@@ -364,7 +372,7 @@ namespace nonstd {
// Presence of C++17 language features:
// no flag
#define variant_HAVE_NODISCARD variant_CPP17_000
// Presence of C++ library features:
@@ -404,6 +412,12 @@ namespace nonstd {
# define variant_nullptr NULL
#endif
#if variant_HAVE_NODISCARD && !variant_CONFIG_NO_NODISCARD
# define variant_nodiscard [[nodiscard]]
#else
# define variant_nodiscard /*[[nodiscard]]*/
#endif
#if variant_HAVE_OVERRIDE
# define variant_override override
#else
@@ -1266,7 +1280,7 @@ static const std::size_t variant_npos = static_cast<std::size_t>( -1 );
// 19.7.11 Class bad_variant_access
class bad_variant_access : public std::exception
class variant_nodiscard bad_variant_access : public std::exception
{
public:
#if variant_CPP11_OR_GREATER

12
third_party/quake3-sqrt/quake3-sqrt.h vendored Normal file
View File

@@ -0,0 +1,12 @@
// From Quake 3 Arena.
#include <immintrin.h>
inline float quake3_sqrt(float x) {
__m128 y = _mm_set_ss(x);
__m128 approx = _mm_rsqrt_ss(y);
__m128 half_x = _mm_mul_ss(y, _mm_set_ss(0.5f));
__m128 three_half = _mm_set_ss(1.5f);
__m128 refined = _mm_mul_ss(approx, _mm_sub_ss(three_half, _mm_mul_ss(half_x, _mm_mul_ss(approx, approx))));
return _mm_cvtss_f32(_mm_mul_ss(refined, y));
}

164
third_party/sfml/include/SFML/Config.hpp vendored Normal file
View File

@@ -0,0 +1,164 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// SFML version
////////////////////////////////////////////////////////////
#define SFML_VERSION_MAJOR 3
#define SFML_VERSION_MINOR 1
#define SFML_VERSION_PATCH 0
#define SFML_VERSION_IS_RELEASE false
////////////////////////////////////////////////////////////
// Identify the operating system
// see https://sourceforge.net/p/predef/wiki/Home/
////////////////////////////////////////////////////////////
#if defined(_WIN32)
// Windows
#define SFML_SYSTEM_WINDOWS
#ifndef NOMINMAX
#define NOMINMAX
#endif
#elif defined(__APPLE__) && defined(__MACH__)
// Apple platform, see which one it is
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
// iOS
#define SFML_SYSTEM_IOS
#elif TARGET_OS_MAC
// macOS
#define SFML_SYSTEM_MACOS
#else
// Unsupported Apple system
#error This Apple operating system is not supported by SFML library
#endif
#elif defined(__unix__)
// UNIX system, see which one it is
#if defined(__ANDROID__)
// Android
#define SFML_SYSTEM_ANDROID
#elif defined(__linux__)
// Linux
#define SFML_SYSTEM_LINUX
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
// FreeBSD
#define SFML_SYSTEM_FREEBSD
#elif defined(__OpenBSD__)
// OpenBSD
#define SFML_SYSTEM_OPENBSD
#elif defined(__NetBSD__)
// NetBSD
#define SFML_SYSTEM_NETBSD
#else
// Unsupported UNIX system
#error This UNIX operating system is not supported by SFML library
#endif
#else
// Unsupported system
#error This operating system is not supported by SFML library
#endif
////////////////////////////////////////////////////////////
// Ensure minimum C++ language standard version is met
////////////////////////////////////////////////////////////
//#if (defined(_MSVC_LANG) && _MSVC_LANG < 201703L) || (!defined(_MSVC_LANG) && __cplusplus < 201703L)
//#error "Enable C++17 or newer for your compiler (e.g. -std=c++17 for GCC/Clang or /std:c++17 for MSVC)"
//#endif
////////////////////////////////////////////////////////////
// Portable debug macro
////////////////////////////////////////////////////////////
#if !defined(NDEBUG)
#define SFML_DEBUG
#endif
////////////////////////////////////////////////////////////
// Helpers to create portable import / export macros for each module
////////////////////////////////////////////////////////////
#if !defined(SFML_STATIC)
#if defined(SFML_SYSTEM_WINDOWS)
// Windows compilers need specific (and different) keywords for export and import
#define SFML_API_EXPORT __declspec(dllexport)
#define SFML_API_IMPORT __declspec(dllimport)
// For Visual C++ compilers, we also need to turn off this annoying C4251 & C4275 warning
#ifdef _MSC_VER
#pragma warning(disable : 4251) // Using standard library types in our own exported types is okay
#pragma warning(disable : 4275) // Exporting types derived from the standard library is okay
#endif
#else // Linux, FreeBSD, macOS
#define SFML_API_EXPORT __attribute__((__visibility__("default")))
#define SFML_API_IMPORT __attribute__((__visibility__("default")))
#endif
#else
// Static build doesn't need import/export macros
#define SFML_API_EXPORT
#define SFML_API_IMPORT
#endif

View File

@@ -0,0 +1,51 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Ftp.hpp>
#include <SFML/Network/Http.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Packet.hpp>
#include <SFML/Network/Socket.hpp>
#include <SFML/Network/SocketHandle.hpp>
#include <SFML/Network/SocketSelector.hpp>
#include <SFML/Network/TcpListener.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/Network/UdpSocket.hpp>
#include <SFML/System.hpp>
////////////////////////////////////////////////////////////
/// \defgroup network Network module
///
/// Socket-based communication, utilities and higher-level
/// network protocols (HTTP, FTP).
///
////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,44 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
////////////////////////////////////////////////////////////
// Portable import / export macros
////////////////////////////////////////////////////////////
#if defined(SFML_NETWORK_EXPORTS)
#define SFML_NETWORK_API SFML_API_EXPORT
#else
#define SFML_NETWORK_API SFML_API_IMPORT
#endif

View File

@@ -0,0 +1,629 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Time.hpp>
#include "filesystem.hpp"
#include <string>
#include <vector>
namespace sf
{
class IpAddress;
////////////////////////////////////////////////////////////
/// \brief A FTP client
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Ftp
{
public:
////////////////////////////////////////////////////////////
/// \brief Enumeration of transfer modes
///
////////////////////////////////////////////////////////////
enum class TransferMode
{
Binary, //!< Binary mode (file is transferred as a sequence of bytes)
Ascii, //!< Text mode using ASCII encoding
Ebcdic //!< Text mode using EBCDIC encoding
};
////////////////////////////////////////////////////////////
/// \brief FTP response
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Status codes possibly returned by a FTP response
///
////////////////////////////////////////////////////////////
enum class Status
{
// 1xx: the requested action is being initiated,
// expect another reply before proceeding with a new command
RestartMarkerReply = 110, //!< Restart marker reply
ServiceReadySoon = 120, //!< Service ready in N minutes
DataConnectionAlreadyOpened = 125, //!< Data connection already opened, transfer starting
OpeningDataConnection = 150, //!< File status ok, about to open data connection
// 2xx: the requested action has been successfully completed
Ok = 200, //!< Command ok
PointlessCommand = 202, //!< Command not implemented
SystemStatus = 211, //!< System status, or system help reply
DirectoryStatus = 212, //!< Directory status
FileStatus = 213, //!< File status
HelpMessage = 214, //!< Help message
SystemType = 215, //!< NAME system type, where NAME is an official system name from the list in the Assigned Numbers document
ServiceReady = 220, //!< Service ready for new user
ClosingConnection = 221, //!< Service closing control connection
DataConnectionOpened = 225, //!< Data connection open, no transfer in progress
ClosingDataConnection = 226, //!< Closing data connection, requested file action successful
EnteringPassiveMode = 227, //!< Entering passive mode
LoggedIn = 230, //!< User logged in, proceed. Logged out if appropriate
FileActionOk = 250, //!< Requested file action ok
DirectoryOk = 257, //!< PATHNAME created
// 3xx: the command has been accepted, but the requested action
// is dormant, pending receipt of further information
NeedPassword = 331, //!< User name ok, need password
NeedAccountToLogIn = 332, //!< Need account for login
NeedInformation = 350, //!< Requested file action pending further information
// 4xx: the command was not accepted and the requested action did not take place,
// but the error condition is temporary and the action may be requested again
ServiceUnavailable = 421, //!< Service not available, closing control connection
DataConnectionUnavailable = 425, //!< Can't open data connection
TransferAborted = 426, //!< Connection closed, transfer aborted
FileActionAborted = 450, //!< Requested file action not taken
LocalError = 451, //!< Requested action aborted, local error in processing
InsufficientStorageSpace = 452, //!< Requested action not taken; insufficient storage space in system, file unavailable
// 5xx: the command was not accepted and
// the requested action did not take place
CommandUnknown = 500, //!< Syntax error, command unrecognized
ParametersUnknown = 501, //!< Syntax error in parameters or arguments
CommandNotImplemented = 502, //!< Command not implemented
BadCommandSequence = 503, //!< Bad sequence of commands
ParameterNotImplemented = 504, //!< Command not implemented for that parameter
NotLoggedIn = 530, //!< Not logged in
NeedAccountToStore = 532, //!< Need account for storing files
FileUnavailable = 550, //!< Requested action not taken, file unavailable
PageTypeUnknown = 551, //!< Requested action aborted, page type unknown
NotEnoughMemory = 552, //!< Requested file action aborted, exceeded storage allocation
FilenameNotAllowed = 553, //!< Requested action not taken, file name not allowed
// 10xx: SFML custom codes
InvalidResponse = 1000, //!< Not part of the FTP standard, generated by SFML when a received response cannot be parsed
ConnectionFailed = 1001, //!< Not part of the FTP standard, generated by SFML when the low-level socket connection with the server fails
ConnectionClosed = 1002, //!< Not part of the FTP standard, generated by SFML when the low-level socket connection is unexpectedly closed
InvalidFile = 1003 //!< Not part of the FTP standard, generated by SFML when a local file cannot be read or written
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor is used by the FTP client to build
/// the response.
///
/// \param code Response status code
/// \param message Response message
///
////////////////////////////////////////////////////////////
explicit Response(Status code = Status::InvalidResponse, std::string message = "");
////////////////////////////////////////////////////////////
/// \brief Check if the status code means a success
///
/// This function is defined for convenience, it is
/// equivalent to testing if the status code is < 400.
///
/// \return `true` if the status is a success, `false` if it is a failure
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isOk() const;
////////////////////////////////////////////////////////////
/// \brief Get the status code of the response
///
/// \return Status code
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status getStatus() const;
////////////////////////////////////////////////////////////
/// \brief Get the full message contained in the response
///
/// \return The response message
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::string& getMessage() const;
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
Status m_status; //!< Status code returned from the server
std::string m_message; //!< Last message received from the server
};
////////////////////////////////////////////////////////////
/// \brief Specialization of FTP response returning a directory
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API DirectoryResponse : public Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// \param response Source response
///
////////////////////////////////////////////////////////////
DirectoryResponse(const Response& response);
////////////////////////////////////////////////////////////
/// \brief Get the directory returned in the response
///
/// \return Directory name
///
////////////////////////////////////////////////////////////
[[nodiscard]] const ghc::filesystem::path& getDirectory() const;
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
ghc::filesystem::path m_directory; //!< Directory extracted from the response message
};
////////////////////////////////////////////////////////////
/// \brief Specialization of FTP response returning a
/// file name listing
////////////////////////////////////////////////////////////
class SFML_NETWORK_API ListingResponse : public Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// \param response Source response
/// \param data Data containing the raw listing
///
////////////////////////////////////////////////////////////
ListingResponse(const Response& response, const std::string& data);
////////////////////////////////////////////////////////////
/// \brief Return the array of directory/file names
///
/// \return Array containing the requested listing
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::vector<std::string>& getListing() const;
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::vector<std::string> m_listing; //!< Directory/file names extracted from the data
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
Ftp() = default;
////////////////////////////////////////////////////////////
/// \brief Destructor
///
/// Automatically closes the connection with the server if
/// it is still opened.
///
////////////////////////////////////////////////////////////
~Ftp();
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
Ftp(const Ftp&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
Ftp& operator=(const Ftp&) = delete;
////////////////////////////////////////////////////////////
/// \brief Connect to the specified FTP server
///
/// The port has a default value of 21, which is the standard
/// port used by the FTP protocol. You shouldn't use a different
/// value, unless you really know what you do.
/// This function tries to connect to the server so it may take
/// a while to complete, especially if the server is not
/// reachable. To avoid blocking your application for too long,
/// you can use a timeout. The default value, `Time::Zero`, means that the
/// system timeout will be used (which is usually pretty long).
///
/// \param server Name or address of the FTP server to connect to
/// \param port Port used for the connection
/// \param timeout Maximum time to wait
///
/// \return Server response to the request
///
/// \see `disconnect`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response connect(IpAddress server, unsigned short port = 21, Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
/// \brief Close the connection with the server
///
/// \return Server response to the request
///
/// \see `connect`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response disconnect();
////////////////////////////////////////////////////////////
/// \brief Log in using an anonymous account
///
/// Logging in is mandatory after connecting to the server.
/// Users that are not logged in cannot perform any operation.
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response login();
////////////////////////////////////////////////////////////
/// \brief Log in using a username and a password
///
/// Logging in is mandatory after connecting to the server.
/// Users that are not logged in cannot perform any operation.
///
/// \param name User name
/// \param password Password
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response login(const std::string& name, const std::string& password);
////////////////////////////////////////////////////////////
/// \brief Send a null command to keep the connection alive
///
/// This command is useful because the server may close the
/// connection automatically if no command is sent.
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response keepAlive();
////////////////////////////////////////////////////////////
/// \brief Get the current working directory
///
/// The working directory is the root path for subsequent
/// operations involving directories and/or filenames.
///
/// \return Server response to the request
///
/// \see `getDirectoryListing`, `changeDirectory`, `parentDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] DirectoryResponse getWorkingDirectory();
////////////////////////////////////////////////////////////
/// \brief Get the contents of the given directory
///
/// This function retrieves the sub-directories and files
/// contained in the given directory. It is not recursive.
/// The `directory` parameter is relative to the current
/// working directory.
///
/// \param directory Directory to list
///
/// \return Server response to the request
///
/// \see `getWorkingDirectory`, `changeDirectory`, `parentDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] ListingResponse getDirectoryListing(const std::string& directory = "");
////////////////////////////////////////////////////////////
/// \brief Change the current working directory
///
/// The new directory must be relative to the current one.
///
/// \param directory New working directory
///
/// \return Server response to the request
///
/// \see `getWorkingDirectory`, `getDirectoryListing`, `parentDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response changeDirectory(const std::string& directory);
////////////////////////////////////////////////////////////
/// \brief Go to the parent directory of the current one
///
/// \return Server response to the request
///
/// \see `getWorkingDirectory`, `getDirectoryListing`, `changeDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response parentDirectory();
////////////////////////////////////////////////////////////
/// \brief Create a new directory
///
/// The new directory is created as a child of the current
/// working directory.
///
/// \param name Name of the directory to create
///
/// \return Server response to the request
///
/// \see `deleteDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response createDirectory(const std::string& name);
////////////////////////////////////////////////////////////
/// \brief Remove an existing directory
///
/// The directory to remove must be relative to the
/// current working directory.
/// Use this function with caution, the directory will
/// be removed permanently!
///
/// \param name Name of the directory to remove
///
/// \return Server response to the request
///
/// \see `createDirectory`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response deleteDirectory(const std::string& name);
////////////////////////////////////////////////////////////
/// \brief Rename an existing file
///
/// The file names must be relative to the current working
/// directory.
///
/// \param file File to rename
/// \param newName New name of the file
///
/// \return Server response to the request
///
/// \see `deleteFile`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response renameFile(const ghc::filesystem::path& file, const ghc::filesystem::path& newName);
////////////////////////////////////////////////////////////
/// \brief Remove an existing file
///
/// The file name must be relative to the current working
/// directory.
/// Use this function with caution, the file will be
/// removed permanently!
///
/// \param name File to remove
///
/// \return Server response to the request
///
/// \see `renameFile`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response deleteFile(const ghc::filesystem::path& name);
////////////////////////////////////////////////////////////
/// \brief Download a file from the server
///
/// The file name of the distant file is relative to the
/// current working directory of the server, and the local
/// destination path is relative to the current directory
/// of your application.
/// If a file with the same file name as the distant file
/// already exists in the local destination path, it will
/// be overwritten.
///
/// \param remoteFile File name of the distant file to download
/// \param localPath The directory in which to put the file on the local computer
/// \param mode Transfer mode
///
/// \return Server response to the request
///
/// \see `upload`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response download(const ghc::filesystem::path& remoteFile,
const ghc::filesystem::path& localPath,
TransferMode mode = TransferMode::Binary);
////////////////////////////////////////////////////////////
/// \brief Upload a file to the server
///
/// The name of the local file is relative to the current
/// working directory of your application, and the
/// remote path is relative to the current directory of the
/// FTP server.
///
/// The append parameter controls whether the remote file is
/// appended to or overwritten if it already exists.
///
/// \param localFile Path of the local file to upload
/// \param remotePath The directory in which to put the file on the server
/// \param mode Transfer mode
/// \param append Pass `true` to append to or `false` to overwrite the remote file if it already exists
///
/// \return Server response to the request
///
/// \see `download`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response upload(const ghc::filesystem::path& localFile,
const ghc::filesystem::path& remotePath,
TransferMode mode = TransferMode::Binary,
bool append = false);
////////////////////////////////////////////////////////////
/// \brief Send a command to the FTP server
///
/// While the most often used commands are provided as member
/// functions in the `Ftp` class, this method can be used
/// to send any FTP command to the server. If the command
/// requires one or more parameters, they can be specified
/// in `parameter`. If the server returns information, you
/// can extract it from the response using `Response::getMessage()`.
///
/// \param command Command to send
/// \param parameter Command parameter
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response sendCommand(const std::string& command, const std::string& parameter = "");
private:
////////////////////////////////////////////////////////////
/// \brief Receive a response from the server
///
/// This function must be called after each call to
/// `sendCommand` that expects a response.
///
/// \return Server response to the request
///
////////////////////////////////////////////////////////////
Response getResponse();
////////////////////////////////////////////////////////////
/// \brief Utility class for exchanging data with the server
/// on the data channel
///
////////////////////////////////////////////////////////////
class DataChannel;
friend class DataChannel;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
TcpSocket m_commandSocket; //!< Socket holding the control connection with the server
std::string m_receiveBuffer; //!< Received command data that is yet to be processed
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class Ftp
/// \ingroup network
///
/// `Ftp` is a very simple FTP client that allows you
/// to communicate with a FTP server. The FTP protocol allows
/// you to manipulate a remote file system (list files,
/// upload, download, create, remove, ...).
///
/// Using the FTP client consists of 4 parts:
/// \li Connecting to the FTP server
/// \li Logging in (either as a registered user or anonymously)
/// \li Sending commands to the server
/// \li Disconnecting (this part can be done implicitly by the destructor)
///
/// Every command returns a FTP response, which contains the
/// status code as well as a message from the server. Some
/// commands such as `getWorkingDirectory()` and `getDirectoryListing()`
/// return additional data, and use a class derived from
/// `Ftp::Response` to provide this data. The most often used
/// commands are directly provided as member functions, but it is
/// also possible to use specific commands with the `sendCommand()` function.
///
/// Note that response statuses >= 1000 are not part of the FTP standard,
/// they are generated by SFML when an internal error occurs.
///
/// All commands, especially upload and download, may take some
/// time to complete. This is important to know if you don't want
/// to block your application while the server is completing
/// the task.
///
/// Usage example:
/// \code
/// // Create a new FTP client
/// Ftp ftp;
///
/// // Connect to the server
/// Ftp::Response response = ftp.connect("ftp://ftp.myserver.com");
/// if (response.isOk())
/// std::cout << "Connected" << std::endl;
///
/// // Log in
/// response = ftp.login("laurent", "dF6Zm89D");
/// if (response.isOk())
/// std::cout << "Logged in" << std::endl;
///
/// // Print the working directory
/// Ftp::DirectoryResponse directory = ftp.getWorkingDirectory();
/// if (directory.isOk())
/// std::cout << "Working directory: " << directory.getDirectory() << std::endl;
///
/// // Create a new directory
/// response = ftp.createDirectory("files");
/// if (response.isOk())
/// std::cout << "Created new directory" << std::endl;
///
/// // Upload a file to this new directory
/// response = ftp.upload("local-path/file.txt", "files", Ftp::TransferMode::Ascii);
/// if (response.isOk())
/// std::cout << "File uploaded" << std::endl;
///
/// // Send specific commands (here: FEAT to list supported FTP features)
/// response = ftp.sendCommand("FEAT");
/// if (response.isOk())
/// std::cout << "Feature list:\n" << response.getMessage() << std::endl;
///
/// // Disconnect from the server (optional)
/// ftp.disconnect();
/// \endcode
///
////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,480 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/TcpSocket.hpp>
#include <SFML/System/Time.hpp>
#include <iosfwd>
#include <map>
#include <optional.hpp>
#include <string>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief A HTTP client
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Http
{
public:
////////////////////////////////////////////////////////////
/// \brief HTTP request
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Request
{
public:
////////////////////////////////////////////////////////////
/// \brief Enumerate the available HTTP methods for a request
///
////////////////////////////////////////////////////////////
enum class Method
{
Get, //!< Request in get mode, standard method to retrieve a page
Post, //!< Request in post mode, usually to send data to a page
Head, //!< Request a page's header only
Put, //!< Request in put mode, useful for a REST API
Delete //!< Request in delete mode, useful for a REST API
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor creates a GET request, with the root
/// URI ("/") and an empty body.
///
/// \param uri Target URI
/// \param method Method to use for the request
/// \param body Content of the request's body
///
////////////////////////////////////////////////////////////
Request(const std::string& uri = "/", Method method = Method::Get, const std::string& body = "");
////////////////////////////////////////////////////////////
/// \brief Set the value of a field
///
/// The field is created if it doesn't exist. The name of
/// the field is case-insensitive.
/// By default, a request doesn't contain any field (but the
/// mandatory fields are added later by the HTTP client when
/// sending the request).
///
/// \param field Name of the field to set
/// \param value Value of the field
///
////////////////////////////////////////////////////////////
void setField(const std::string& field, const std::string& value);
////////////////////////////////////////////////////////////
/// \brief Set the request method
///
/// See the Method enumeration for a complete list of all
/// the available methods.
/// The method is `Http::Request::Method::Get` by default.
///
/// \param method Method to use for the request
///
////////////////////////////////////////////////////////////
void setMethod(Method method);
////////////////////////////////////////////////////////////
/// \brief Set the requested URI
///
/// The URI is the resource (usually a web page or a file)
/// that you want to get or post.
/// The URI is "/" (the root page) by default.
///
/// \param uri URI to request, relative to the host
///
////////////////////////////////////////////////////////////
void setUri(const std::string& uri);
////////////////////////////////////////////////////////////
/// \brief Set the HTTP version for the request
///
/// The HTTP version is 1.0 by default.
///
/// \param major Major HTTP version number
/// \param minor Minor HTTP version number
///
////////////////////////////////////////////////////////////
void setHttpVersion(unsigned int major, unsigned int minor);
////////////////////////////////////////////////////////////
/// \brief Set the body of the request
///
/// The body of a request is optional and only makes sense
/// for POST requests. It is ignored for all other methods.
/// The body is empty by default.
///
/// \param body Content of the body
///
////////////////////////////////////////////////////////////
void setBody(const std::string& body);
private:
friend class Http;
////////////////////////////////////////////////////////////
/// \brief Prepare the final request to send to the server
///
/// This is used internally by Http before sending the
/// request to the web server.
///
/// \return String containing the request, ready to be sent
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::string prepare() const;
////////////////////////////////////////////////////////////
/// \brief Check if the request defines a field
///
/// This function uses case-insensitive comparisons.
///
/// \param field Name of the field to test
///
/// \return `true` if the field exists, `false` otherwise
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool hasField(const std::string& field) const;
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
using FieldTable = std::map<std::string, std::string>; // Use an ordered map for predictable payloads
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
FieldTable m_fields; //!< Fields of the header associated to their value
Method m_method; //!< Method to use for the request
std::string m_uri; //!< Target URI of the request
unsigned int m_majorVersion{1}; //!< Major HTTP version
unsigned int m_minorVersion{}; //!< Minor HTTP version
std::string m_body; //!< Body of the request
};
////////////////////////////////////////////////////////////
/// \brief HTTP response
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Response
{
public:
////////////////////////////////////////////////////////////
/// \brief Enumerate all the valid status codes for a response
///
////////////////////////////////////////////////////////////
enum class Status
{
// 2xx: success
Ok = 200, //!< Most common code returned when operation was successful
Created = 201, //!< The resource has successfully been created
Accepted = 202, //!< The request has been accepted, but will be processed later by the server
NoContent = 204, //!< The server didn't send any data in return
ResetContent = 205, //!< The server informs the client that it should clear the view (form) that caused the request to be sent
PartialContent = 206, //!< The server has sent a part of the resource, as a response to a partial GET request
// 3xx: redirection
MultipleChoices = 300, //!< The requested page can be accessed from several locations
MovedPermanently = 301, //!< The requested page has permanently moved to a new location
MovedTemporarily = 302, //!< The requested page has temporarily moved to a new location
NotModified = 304, //!< For conditional requests, means the requested page hasn't changed and doesn't need to be refreshed
// 4xx: client error
BadRequest = 400, //!< The server couldn't understand the request (syntax error)
Unauthorized = 401, //!< The requested page needs an authentication to be accessed
Forbidden = 403, //!< The requested page cannot be accessed at all, even with authentication
NotFound = 404, //!< The requested page doesn't exist
RangeNotSatisfiable = 407, //!< The server can't satisfy the partial GET request (with a "Range" header field)
// 5xx: server error
InternalServerError = 500, //!< The server encountered an unexpected error
NotImplemented = 501, //!< The server doesn't implement a requested feature
BadGateway = 502, //!< The gateway server has received an error from the source server
ServiceNotAvailable = 503, //!< The server is temporarily unavailable (overloaded, in maintenance, ...)
GatewayTimeout = 504, //!< The gateway server couldn't receive a response from the source server
VersionNotSupported = 505, //!< The server doesn't support the requested HTTP version
// 10xx: SFML custom codes
InvalidResponse = 1000, //!< Response is not a valid HTTP one
ConnectionFailed = 1001 //!< Connection with server failed
};
////////////////////////////////////////////////////////////
/// \brief Get the value of a field
///
/// If the field `field` is not found in the response header,
/// the empty string is returned. This function uses
/// case-insensitive comparisons.
///
/// \param field Name of the field to get
///
/// \return Value of the field, or empty string if not found
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::string& getField(const std::string& field) const;
////////////////////////////////////////////////////////////
/// \brief Get the response status code
///
/// The status code should be the first thing to be checked
/// after receiving a response, it defines whether it is a
/// success, a failure or anything else (see the Status
/// enumeration).
///
/// \return Status code of the response
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status getStatus() const;
////////////////////////////////////////////////////////////
/// \brief Get the major HTTP version number of the response
///
/// \return Major HTTP version number
///
/// \see `getMinorHttpVersion`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned int getMajorHttpVersion() const;
////////////////////////////////////////////////////////////
/// \brief Get the minor HTTP version number of the response
///
/// \return Minor HTTP version number
///
/// \see `getMajorHttpVersion`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned int getMinorHttpVersion() const;
////////////////////////////////////////////////////////////
/// \brief Get the body of the response
///
/// The body of a response may contain:
/// \li the requested page (for GET requests)
/// \li a response from the server (for POST requests)
/// \li nothing (for HEAD requests)
/// \li an error message (in case of an error)
///
/// \return The response body
///
////////////////////////////////////////////////////////////
[[nodiscard]] const std::string& getBody() const;
private:
friend class Http;
////////////////////////////////////////////////////////////
/// \brief Construct the header from a response string
///
/// This function is used by `Http` to build the response
/// of a request.
///
/// \param data Content of the response to parse
///
////////////////////////////////////////////////////////////
void parse(const std::string& data);
////////////////////////////////////////////////////////////
/// \brief Read values passed in the answer header
///
/// This function is used by `Http` to extract values passed
/// in the response.
///
/// \param in String stream containing the header values
///
////////////////////////////////////////////////////////////
void parseFields(std::istream& in);
////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////
using FieldTable = std::map<std::string, std::string>; // Use an ordered map for predictable payloads
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
FieldTable m_fields; //!< Fields of the header
Status m_status{Status::ConnectionFailed}; //!< Status code
unsigned int m_majorVersion{}; //!< Major HTTP version
unsigned int m_minorVersion{}; //!< Minor HTTP version
std::string m_body; //!< Body of the response
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
Http() = default;
////////////////////////////////////////////////////////////
/// \brief Construct the HTTP client with the target host
///
/// This is equivalent to calling `setHost(host, port)`.
/// The port has a default value of 0, which means that the
/// HTTP client will use the right port according to the
/// protocol used (80 for HTTP). You should leave it like
/// this unless you really need a port other than the
/// standard one, or use an unknown protocol.
///
/// \param host Web server to connect to
/// \param port Port to use for connection
///
////////////////////////////////////////////////////////////
Http(const std::string& host, unsigned short port = 0);
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
Http(const Http&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
Http& operator=(const Http&) = delete;
////////////////////////////////////////////////////////////
/// \brief Set the target host
///
/// This function just stores the host address and port, it
/// doesn't actually connect to it until you send a request.
/// The port has a default value of 0, which means that the
/// HTTP client will use the right port according to the
/// protocol used (80 for HTTP). You should leave it like
/// this unless you really need a port other than the
/// standard one, or use an unknown protocol.
///
/// \param host Web server to connect to
/// \param port Port to use for connection
///
////////////////////////////////////////////////////////////
void setHost(const std::string& host, unsigned short port = 0);
////////////////////////////////////////////////////////////
/// \brief Send a HTTP request and return the server's response.
///
/// You must have a valid host before sending a request (see `setHost`).
/// Any missing mandatory header field in the request will be added
/// with an appropriate value.
/// Warning: this function waits for the server's response and may
/// not return instantly; use a thread if you don't want to block your
/// application, or use a timeout to limit the time to wait. A value
/// of `Time::Zero` means that the client will use the system default timeout
/// (which is usually pretty long).
///
/// \param request Request to send
/// \param timeout Maximum time to wait
///
/// \return Server's response
///
////////////////////////////////////////////////////////////
[[nodiscard]] Response sendRequest(const Request& request, Time timeout = Time::Zero);
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
TcpSocket m_connection; //!< Connection to the host
nonstd::optional<IpAddress> m_host; //!< Web host address
std::string m_hostName; //!< Web host name
unsigned short m_port{}; //!< Port used for connection with host
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class Http
/// \ingroup network
///
/// `Http` is a very simple HTTP client that allows you
/// to communicate with a web server. You can retrieve
/// web pages, send data to an interactive resource,
/// download a remote file, etc. The HTTPS protocol is
/// not supported.
///
/// The HTTP client is split into 3 classes:
/// \li `Http::Request`
/// \li `Http::Response`
/// \li `Http`
///
/// `Http::Request` builds the request that will be
/// sent to the server. A request is made of:
/// \li a method (what you want to do)
/// \li a target URI (usually the name of the web page or file)
/// \li one or more header fields (options that you can pass to the server)
/// \li an optional body (for POST requests)
///
/// `Http::Response` parse the response from the web server
/// and provides getters to read them. The response contains:
/// \li a status code
/// \li header fields (that may be answers to the ones that you requested)
/// \li a body, which contains the contents of the requested resource
///
/// `Http` provides a simple function, SendRequest, to send a
/// `Http::Request` and return the corresponding `Http::Response`
/// from the server.
///
/// Usage example:
/// \code
/// // Create a new HTTP client
/// Http http;
///
/// // We'll work on http://www.sfml-dev.org
/// http.setHost("http://www.sfml-dev.org");
///
/// // Prepare a request to get the 'features.php' page
/// Http::Request request("features.php");
///
/// // Send the request
/// Http::Response response = http.sendRequest(request);
///
/// // Check the status code and display the result
/// Http::Response::Status status = response.getStatus();
/// if (status == Http::Response::Status::Ok)
/// {
/// std::cout << response.getBody() << std::endl;
/// }
/// else
/// {
/// std::cout << "Error " << status << std::endl;
/// }
/// \endcode
///
////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,296 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/System/Time.hpp>
#include <iosfwd>
#include <optional.hpp>
#include <string>
#include <cstdint>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Encapsulate an IPv4 network address
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API IpAddress
{
public:
////////////////////////////////////////////////////////////
/// \brief Construct the address from a null-terminated string view
///
/// Here \a address can be either a decimal address
/// (ex: "192.168.1.56") or a network name (ex: "localhost").
///
/// \param address IP address or network name
///
/// \return Address if provided argument was valid, otherwise `nonstd::nullopt`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static nonstd::optional<IpAddress> resolve(std::string address);
////////////////////////////////////////////////////////////
/// \brief Construct the address from 4 bytes
///
/// Calling `IpAddress(a, b, c, d)` is equivalent to calling
/// `IpAddress::resolve("a.b.c.d")`, but safer as it doesn't
/// have to parse a string to get the address components.
///
/// \param byte0 First byte of the address
/// \param byte1 Second byte of the address
/// \param byte2 Third byte of the address
/// \param byte3 Fourth byte of the address
///
////////////////////////////////////////////////////////////
IpAddress(std::uint8_t byte0, std::uint8_t byte1, std::uint8_t byte2, std::uint8_t byte3);
////////////////////////////////////////////////////////////
/// \brief Construct the address from a 32-bits integer
///
/// This constructor uses the internal representation of
/// the address directly. It should be used for optimization
/// purposes, and only if you got that representation from
/// `IpAddress::toInteger()`.
///
/// \param address 4 bytes of the address packed into a 32-bits integer
///
/// \see `toInteger`
///
////////////////////////////////////////////////////////////
explicit IpAddress(std::uint32_t address);
////////////////////////////////////////////////////////////
/// \brief Get a string representation of the address
///
/// The returned string is the decimal representation of the
/// IP address (like "192.168.1.56"), even if it was constructed
/// from a host name.
///
/// \return String representation of the address
///
/// \see `toInteger`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::string toString() const;
////////////////////////////////////////////////////////////
/// \brief Get an integer representation of the address
///
/// The returned number is the internal representation of the
/// address, and should be used for optimization purposes only
/// (like sending the address through a socket).
/// The integer produced by this function can then be converted
/// back to a `IpAddress` with the proper constructor.
///
/// \return 32-bits unsigned integer representation of the address
///
/// \see `toString`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::uint32_t toInteger() const;
////////////////////////////////////////////////////////////
/// \brief Get the computer's local address
///
/// The local address is the address of the computer from the
/// LAN point of view, i.e. something like 192.168.1.56. It is
/// meaningful only for communications over the local network.
/// Unlike getPublicAddress, this function is fast and may be
/// used safely anywhere.
///
/// \return Local IP address of the computer on success, `nonstd::nullopt` otherwise
///
/// \see `getPublicAddress`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static nonstd::optional<IpAddress> getLocalAddress();
////////////////////////////////////////////////////////////
/// \brief Get the computer's public address
///
/// The public address is the address of the computer from the
/// internet point of view, i.e. something like 89.54.1.169.
/// It is necessary for communications over the world wide web.
/// The only way to get a public address is to ask it to a
/// distant website; as a consequence, this function depends on
/// both your network connection and the server, and may be
/// very slow. You should use it as few as possible. Because
/// this function depends on the network connection and on a distant
/// server, you may use a time limit if you don't want your program
/// to be possibly stuck waiting in case there is a problem; this
/// limit is deactivated by default.
///
/// \param timeout Maximum time to wait
///
/// \return Public IP address of the computer on success, `nonstd::nullopt` otherwise
///
/// \see `getLocalAddress`
///
////////////////////////////////////////////////////////////
[[nodiscard]] static nonstd::optional<IpAddress> getPublicAddress(Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
// Static member data
////////////////////////////////////////////////////////////
// NOLINTBEGIN(readability-identifier-naming)
static const IpAddress Any; //!< Value representing any address (0.0.0.0)
static const IpAddress LocalHost; //!< The "localhost" address (for connecting a computer to itself locally)
static const IpAddress Broadcast; //!< The "broadcast" address (for sending UDP messages to everyone on a local network)
// NOLINTEND(readability-identifier-naming)
private:
friend SFML_NETWORK_API bool operator<(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::uint32_t m_address; //!< Address stored as an unsigned 32 bit integer
};
////////////////////////////////////////////////////////////
/// \brief Overload of `operator==` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if both addresses are equal
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator==(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator!=` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if both addresses are different
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator!=(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator<` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if `left` is lesser than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator<(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator>` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if `left` is greater than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator>(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator<=` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if \a left is lesser or equal than \a right
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator<=(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator>=` to compare two IP addresses
///
/// \param left Left operand (a IP address)
/// \param right Right operand (a IP address)
///
/// \return `true` if `left` is greater or equal than `right`
///
////////////////////////////////////////////////////////////
[[nodiscard]] SFML_NETWORK_API bool operator>=(IpAddress left, IpAddress right);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator>>` to extract an IP address from an input stream
///
/// \param stream Input stream
/// \param address IP address to extract
///
/// \return Reference to the input stream
///
////////////////////////////////////////////////////////////
SFML_NETWORK_API std::istream& operator>>(std::istream& stream, nonstd::optional<IpAddress>& address);
////////////////////////////////////////////////////////////
/// \brief Overload of `operator<<` to print an IP address to an output stream
///
/// \param stream Output stream
/// \param address IP address to print
///
/// \return Reference to the output stream
///
////////////////////////////////////////////////////////////
SFML_NETWORK_API std::ostream& operator<<(std::ostream& stream, IpAddress address);
} // namespace sf
////////////////////////////////////////////////////////////
/// \class IpAddress
/// \ingroup network
///
/// `IpAddress` is a utility class for manipulating network
/// addresses. It provides a set a implicit constructors and
/// conversion functions to easily build or transform an IP
/// address from/to various representations.
///
/// Usage example:
/// \code
/// auto a2 = IpAddress::resolve("127.0.0.1"); // the local host address
/// auto a3 = IpAddress::Broadcast; // the broadcast address
/// IpAddress a4(192, 168, 1, 56); // a local address
/// auto a5 = IpAddress::resolve("my_computer"); // a local address created from a network name
/// auto a6 = IpAddress::resolve("89.54.1.169"); // a distant address
/// auto a7 = IpAddress::resolve("www.google.com"); // a distant address created from a network name
/// auto a8 = IpAddress::getLocalAddress(); // my address on the local network
/// auto a9 = IpAddress::getPublicAddress(); // my address on the internet
/// \endcode
///
/// Note that `IpAddress` currently doesn't support IPv6
/// nor other types of network addresses.
///
////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,552 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <string>
#include <vector>
#include <cstddef>
#include <cstdint>
namespace sf
{
class String;
////////////////////////////////////////////////////////////
/// \brief Utility class to build blocks of data to transfer
/// over the network
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Packet
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// Creates an empty packet.
///
////////////////////////////////////////////////////////////
Packet() = default;
////////////////////////////////////////////////////////////
/// \brief Virtual destructor
///
////////////////////////////////////////////////////////////
virtual ~Packet() = default;
////////////////////////////////////////////////////////////
/// \brief Copy constructor
///
////////////////////////////////////////////////////////////
Packet(const Packet&) = default;
////////////////////////////////////////////////////////////
/// \brief Copy assignment
///
////////////////////////////////////////////////////////////
Packet& operator=(const Packet&) = default;
////////////////////////////////////////////////////////////
/// \brief Move constructor
///
////////////////////////////////////////////////////////////
Packet(Packet&&) noexcept = default;
////////////////////////////////////////////////////////////
/// \brief Move assignment
///
////////////////////////////////////////////////////////////
Packet& operator=(Packet&&) noexcept = default;
////////////////////////////////////////////////////////////
/// \brief Append data to the end of the packet
///
/// \param data Pointer to the sequence of bytes to append
/// \param sizeInBytes Number of bytes to append
///
/// \see `clear`
/// \see `getReadPosition`
///
////////////////////////////////////////////////////////////
void append(const void* data, std::size_t sizeInBytes);
////////////////////////////////////////////////////////////
/// \brief Get the current reading position in the packet
///
/// The next read operation will read data from this position
///
/// \return The byte offset of the current read position
///
/// \see `append`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::size_t getReadPosition() const;
////////////////////////////////////////////////////////////
/// \brief Clear the packet
///
/// After calling Clear, the packet is empty.
///
/// \see `append`
///
////////////////////////////////////////////////////////////
void clear();
////////////////////////////////////////////////////////////
/// \brief Get a pointer to the data contained in the packet
///
/// Warning: the returned pointer may become invalid after
/// you append data to the packet, therefore it should never
/// be stored.
/// The return pointer is a `nullptr` if the packet is empty.
///
/// \return Pointer to the data
///
/// \see `getDataSize`
///
////////////////////////////////////////////////////////////
[[nodiscard]] const void* getData() const;
////////////////////////////////////////////////////////////
/// \brief Get the size of the data contained in the packet
///
/// This function returns the number of bytes pointed to by
/// what `getData` returns.
///
/// \return Data size, in bytes
///
/// \see `getData`
///
////////////////////////////////////////////////////////////
[[nodiscard]] std::size_t getDataSize() const;
////////////////////////////////////////////////////////////
/// \brief Tell if the reading position has reached the
/// end of the packet
///
/// This function is useful to know if there is some data
/// left to be read, without actually reading it.
///
/// \return `true` if all data was read, `false` otherwise
///
/// \see `operator` bool
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool endOfPacket() const;
////////////////////////////////////////////////////////////
/// \brief Test the validity of the packet, for reading
///
/// This operator allows to test the packet as a boolean
/// variable, to check if a reading operation was successful.
///
/// A packet will be in an invalid state if it has no more
/// data to read.
///
/// This behavior is the same as standard C++ streams.
///
/// Usage example:
/// \code
/// float x;
/// packet >> x;
/// if (packet)
/// {
/// // ok, x was extracted successfully
/// }
///
/// // -- or --
///
/// float x;
/// if (packet >> x)
/// {
/// // ok, x was extracted successfully
/// }
/// \endcode
///
/// Don't focus on the return type, it's equivalent to bool but
/// it disallows unwanted implicit conversions to integer or
/// pointer types.
///
/// \return `true` if last data extraction from packet was successful
///
/// \see `endOfPacket`
///
////////////////////////////////////////////////////////////
explicit operator bool() const;
////////////////////////////////////////////////////////////
/// Overload of `operator>>` to read data from the packet
///
////////////////////////////////////////////////////////////
Packet& operator>>(bool& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int8_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint8_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int16_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint16_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int32_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint32_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::int64_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::uint64_t& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(float& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(double& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(char* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::string& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(wchar_t* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(std::wstring& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator>>(String& data);
////////////////////////////////////////////////////////////
/// Overload of `operator<<` to write data into the packet
///
////////////////////////////////////////////////////////////
Packet& operator<<(bool data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int8_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint8_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int16_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint16_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int32_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint32_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::int64_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(std::uint64_t data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(float data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(double data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const char* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const std::string& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const wchar_t* data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const std::wstring& data);
////////////////////////////////////////////////////////////
/// \overload
////////////////////////////////////////////////////////////
Packet& operator<<(const String& data);
protected:
friend class TcpSocket;
friend class UdpSocket;
////////////////////////////////////////////////////////////
/// \brief Called before the packet is sent over the network
///
/// This function can be defined by derived classes to
/// transform the data before it is sent; this can be
/// used for compression, encryption, etc.
/// The function must return a pointer to the modified data,
/// as well as the number of bytes pointed.
/// The default implementation provides the packet's data
/// without transforming it.
///
/// \param size Variable to fill with the size of data to send
///
/// \return Pointer to the array of bytes to send
///
/// \see `onReceive`
///
////////////////////////////////////////////////////////////
virtual const void* onSend(std::size_t& size);
////////////////////////////////////////////////////////////
/// \brief Called after the packet is received over the network
///
/// This function can be defined by derived classes to
/// transform the data after it is received; this can be
/// used for decompression, decryption, etc.
/// The function receives a pointer to the received data,
/// and must fill the packet with the transformed bytes.
/// The default implementation fills the packet directly
/// without transforming the data.
///
/// \param data Pointer to the received bytes
/// \param size Number of bytes
///
/// \see `onSend`
///
////////////////////////////////////////////////////////////
virtual void onReceive(const void* data, std::size_t size);
private:
////////////////////////////////////////////////////////////
/// \brief Check if the packet can extract a given number of bytes
///
/// This function updates accordingly the state of the packet.
///
/// \param size Size to check
///
/// \return `true` if \a size bytes can be read from the packet
///
////////////////////////////////////////////////////////////
bool checkSize(std::size_t size);
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::vector<unsigned char> m_data; //!< Data stored in the packet
std::size_t m_readPos{}; //!< Current reading position in the packet
std::size_t m_sendPos{}; //!< Current send position in the packet (for handling partial sends)
bool m_isValid{true}; //!< Reading state of the packet
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class Packet
/// \ingroup network
///
/// Packets provide a safe and easy way to serialize data,
/// in order to send it over the network using sockets
/// (`TcpSocket`, `UdpSocket`).
///
/// Packets solve 2 fundamental problems that arise when
/// transferring data over the network:
/// \li data is interpreted correctly according to the endianness
/// \li the bounds of the packet are preserved (one send == one receive)
///
/// The `Packet` class provides both input and output modes.
/// It is designed to follow the behavior of standard C++ streams,
/// using operators >> and << to extract and insert data.
///
/// It is recommended to use only fixed-size types (like `std::int32_t`, etc.),
/// to avoid possible differences between the sender and the receiver.
/// Indeed, the native C++ types may have different sizes on two platforms
/// and your data may be corrupted if that happens.
///
/// Usage example:
/// \code
/// std::uint32_t x = 24;
/// std::string s = "hello";
/// double d = 5.89;
///
/// // Group the variables to send into a packet
/// Packet packet;
/// packet << x << s << d;
///
/// // Send it over the network (socket is a valid TcpSocket)
/// socket.send(packet);
///
/// -----------------------------------------------------------------
///
/// // Receive the packet at the other end
/// Packet packet;
/// socket.receive(packet);
///
/// // Extract the variables contained in the packet
/// std::uint32_t x;
/// std::string s;
/// double d;
/// if (packet >> x >> s >> d)
/// {
/// // Data extracted successfully...
/// }
/// \endcode
///
/// Packets have built-in `operator>>` and << overloads for
/// standard types:
/// \li `bool`
/// \li fixed-size integer types (`int[8|16|32]_t`, `uint[8|16|32]_t`)
/// \li floating point numbers (`float`, `double`)
/// \li string types (`char*`, `wchar_t*`, `std::string`, `std::wstring`, `String`)
///
/// Like standard streams, it is also possible to define your own
/// overloads of operators >> and << in order to handle your
/// custom types.
///
/// \code
/// struct MyStruct
/// {
/// float number{};
/// std::int8_t integer{};
/// std::string str;
/// };
///
/// Packet& operator <<(Packet& packet, const MyStruct& m)
/// {
/// return packet << m.number << m.integer << m.str;
/// }
///
/// Packet& operator >>(Packet& packet, MyStruct& m)
/// {
/// return packet >> m.number >> m.integer >> m.str;
/// }
/// \endcode
///
/// Packets also provide an extra feature that allows to apply
/// custom transformations to the data before it is sent,
/// and after it is received. This is typically used to
/// handle automatic compression or encryption of the data.
/// This is achieved by inheriting from `Packet`, and overriding
/// the onSend and onReceive functions.
///
/// Here is an example:
/// \code
/// class ZipPacket : public Packet
/// {
/// const void* onSend(std::size_t& size) override
/// {
/// const void* srcData = getData();
/// std::size_t srcSize = getDataSize();
///
/// return MySuperZipFunction(srcData, srcSize, &size);
/// }
///
/// void onReceive(const void* data, std::size_t size) override
/// {
/// std::size_t dstSize;
/// const void* dstData = MySuperUnzipFunction(data, size, &dstSize);
///
/// append(dstData, dstSize);
/// }
/// };
///
/// // Use like regular packets:
/// ZipPacket packet;
/// packet << x << s << d;
/// ...
/// \endcode
///
/// \see `TcpSocket`, `UdpSocket`
///
////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,229 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/SocketHandle.hpp>
namespace sf
{
////////////////////////////////////////////////////////////
/// \brief Base class for all the socket types
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API Socket
{
public:
////////////////////////////////////////////////////////////
/// \brief Status codes that may be returned by socket functions
///
////////////////////////////////////////////////////////////
enum class Status
{
Done, //!< The socket has sent / received the data
NotReady, //!< The socket is not ready to send / receive data yet
Partial, //!< The socket sent a part of the data
Disconnected, //!< The TCP socket has been disconnected
Error //!< An unexpected error happened
};
////////////////////////////////////////////////////////////
/// \brief Some special values used by sockets
///
////////////////////////////////////////////////////////////
// NOLINTNEXTLINE(readability-identifier-naming)
static constexpr unsigned short AnyPort{0}; //!< Special value that tells the system to pick any available port
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
virtual ~Socket();
////////////////////////////////////////////////////////////
/// \brief Deleted copy constructor
///
////////////////////////////////////////////////////////////
Socket(const Socket&) = delete;
////////////////////////////////////////////////////////////
/// \brief Deleted copy assignment
///
////////////////////////////////////////////////////////////
Socket& operator=(const Socket&) = delete;
////////////////////////////////////////////////////////////
/// \brief Move constructor
///
////////////////////////////////////////////////////////////
Socket(Socket&& socket) noexcept;
////////////////////////////////////////////////////////////
/// \brief Move assignment
///
////////////////////////////////////////////////////////////
Socket& operator=(Socket&& socket) noexcept;
////////////////////////////////////////////////////////////
/// \brief Set the blocking state of the socket
///
/// In blocking mode, calls will not return until they have
/// completed their task. For example, a call to Receive in
/// blocking mode won't return until some data was actually
/// received.
/// In non-blocking mode, calls will always return immediately,
/// using the return code to signal whether there was data
/// available or not.
/// By default, all sockets are blocking.
///
/// \param blocking `true` to set the socket as blocking, `false` for non-blocking
///
/// \see `isBlocking`
///
////////////////////////////////////////////////////////////
void setBlocking(bool blocking);
////////////////////////////////////////////////////////////
/// \brief Tell whether the socket is in blocking or non-blocking mode
///
/// \return `true` if the socket is blocking, `false` otherwise
///
/// \see `setBlocking`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isBlocking() const;
protected:
////////////////////////////////////////////////////////////
/// \brief Types of protocols that the socket can use
///
////////////////////////////////////////////////////////////
enum class Type
{
Tcp, //!< TCP protocol
Udp //!< UDP protocol
};
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
/// This constructor can only be accessed by derived classes.
///
/// \param type Type of the socket (TCP or UDP)
///
////////////////////////////////////////////////////////////
explicit Socket(Type type);
////////////////////////////////////////////////////////////
/// \brief Return the internal handle of the socket
///
/// The returned handle may be invalid if the socket
/// was not created yet (or already destroyed).
/// This function can only be accessed by derived classes.
///
/// \return The internal (OS-specific) handle of the socket
///
////////////////////////////////////////////////////////////
[[nodiscard]] SocketHandle getNativeHandle() const;
////////////////////////////////////////////////////////////
/// \brief Create the internal representation of the socket
///
/// This function can only be accessed by derived classes.
///
////////////////////////////////////////////////////////////
void create();
////////////////////////////////////////////////////////////
/// \brief Create the internal representation of the socket
/// from a socket handle
///
/// This function can only be accessed by derived classes.
///
/// \param handle OS-specific handle of the socket to wrap
///
////////////////////////////////////////////////////////////
void create(SocketHandle handle);
////////////////////////////////////////////////////////////
/// \brief Close the socket gracefully
///
/// This function can only be accessed by derived classes.
///
////////////////////////////////////////////////////////////
void close();
private:
friend class SocketSelector;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
Type m_type; //!< Type of the socket (TCP or UDP)
SocketHandle m_socket; //!< Socket descriptor
bool m_isBlocking{true}; //!< Current blocking mode of the socket
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class Socket
/// \ingroup network
///
/// This class mainly defines internal stuff to be used by
/// derived classes.
///
/// The only public features that it defines, and which
/// is therefore common to all the socket classes, is the
/// blocking state. All sockets can be set as blocking or
/// non-blocking.
///
/// In blocking mode, socket functions will hang until
/// the operation completes, which means that the entire
/// program (well, in fact the current thread if you use
/// multiple ones) will be stuck waiting for your socket
/// operation to complete.
///
/// In non-blocking mode, all the socket functions will
/// return immediately. If the socket is not ready to complete
/// the requested operation, the function simply returns
/// the proper status code (`Socket::Status::NotReady`).
///
/// The default mode, which is blocking, is the one that is
/// generally used, in combination with threads or selectors.
/// The non-blocking mode is rather used in real-time
/// applications that run an endless loop that can poll
/// the socket often enough, and cannot afford blocking
/// this loop.
///
/// \see `TcpListener`, `TcpSocket`, `UdpSocket`
///
////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,52 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#if defined(SFML_SYSTEM_WINDOWS)
#include <basetsd.h>
#endif
namespace sf
{
////////////////////////////////////////////////////////////
// Low-level socket handle type, specific to each platform
////////////////////////////////////////////////////////////
#if defined(SFML_SYSTEM_WINDOWS)
using SocketHandle = UINT_PTR;
#else
using SocketHandle = int;
#endif
} // namespace sf

View File

@@ -0,0 +1,273 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/System/Time.hpp>
#include <memory>
namespace sf
{
class Socket;
////////////////////////////////////////////////////////////
/// \brief Multiplexer that allows to read from multiple sockets
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API SocketSelector
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
SocketSelector();
////////////////////////////////////////////////////////////
/// \brief Destructor
///
////////////////////////////////////////////////////////////
~SocketSelector();
////////////////////////////////////////////////////////////
/// \brief Copy constructor
///
/// \param copy Instance to copy
///
////////////////////////////////////////////////////////////
SocketSelector(const SocketSelector& copy);
////////////////////////////////////////////////////////////
/// \brief Overload of assignment operator
///
/// \param right Instance to assign
///
/// \return Reference to self
///
////////////////////////////////////////////////////////////
SocketSelector& operator=(const SocketSelector& right);
////////////////////////////////////////////////////////////
/// \brief Move constructor
///
////////////////////////////////////////////////////////////
SocketSelector(SocketSelector&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Move assignment
///
////////////////////////////////////////////////////////////
SocketSelector& operator=(SocketSelector&&) noexcept;
////////////////////////////////////////////////////////////
/// \brief Add a new socket to the selector
///
/// This function keeps a weak reference to the socket,
/// so you have to make sure that the socket is not destroyed
/// while it is stored in the selector.
/// This function does nothing if the socket is not valid.
///
/// \param socket Reference to the socket to add
///
/// \see `remove`, `clear`
///
////////////////////////////////////////////////////////////
void add(Socket& socket);
////////////////////////////////////////////////////////////
/// \brief Remove a socket from the selector
///
/// This function doesn't destroy the socket, it simply
/// removes the reference that the selector has to it.
///
/// \param socket Reference to the socket to remove
///
/// \see `add`, `clear`
///
////////////////////////////////////////////////////////////
void remove(Socket& socket);
////////////////////////////////////////////////////////////
/// \brief Remove all the sockets stored in the selector
///
/// This function doesn't destroy any instance, it simply
/// removes all the references that the selector has to
/// external sockets.
///
/// \see `add`, `remove`
///
////////////////////////////////////////////////////////////
void clear();
////////////////////////////////////////////////////////////
/// \brief Wait until one or more sockets are ready to receive
///
/// This function returns as soon as at least one socket has
/// some data available to be received. To know which sockets are
/// ready, use the `isReady` function.
/// If you use a timeout and no socket is ready before the timeout
/// is over, the function returns `false`.
///
/// \param timeout Maximum time to wait, (use Time::Zero for infinity)
///
/// \return `true` if there are sockets ready, `false` otherwise
///
/// \see `isReady`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool wait(Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
/// \brief Test a socket to know if it is ready to receive data
///
/// This function must be used after a call to Wait, to know
/// which sockets are ready to receive data. If a socket is
/// ready, a call to receive will never block because we know
/// that there is data available to read.
/// Note that if this function returns `true` for a TcpListener,
/// this means that it is ready to accept a new connection.
///
/// \param socket Socket to test
///
/// \return `true` if the socket is ready to read, `false` otherwise
///
/// \see `isReady`
///
////////////////////////////////////////////////////////////
[[nodiscard]] bool isReady(Socket& socket) const;
private:
struct SocketSelectorImpl;
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::unique_ptr<SocketSelectorImpl> m_impl; //!< Opaque pointer to the implementation (which requires OS-specific types)
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class SocketSelector
/// \ingroup network
///
/// Socket selectors provide a way to wait until some data is
/// available on a set of sockets, instead of just one. This
/// is convenient when you have multiple sockets that may
/// possibly receive data, but you don't know which one will
/// be ready first. In particular, it avoids to use a thread
/// for each socket; with selectors, a single thread can handle
/// all the sockets.
///
/// All types of sockets can be used in a selector:
/// \li `TcpListener`
/// \li `TcpSocket`
/// \li `UdpSocket`
///
/// A selector doesn't store its own copies of the sockets
/// (socket classes are not copyable anyway), it simply keeps
/// a reference to the original sockets that you pass to the
/// "add" function. Therefore, you can't use the selector as a
/// socket container, you must store them outside and make sure
/// that they are alive as long as they are used in the selector.
///
/// Using a selector is simple:
/// \li populate the selector with all the sockets that you want to observe
/// \li make it wait until there is data available on any of the sockets
/// \li test each socket to find out which ones are ready
///
/// Usage example:
/// \code
/// // Create a socket to listen to new connections
/// TcpListener listener;
/// if (listener.listen(55001) != Socket::Status::Done)
/// {
/// // Handle error...
/// }
///
/// // Create a list to store the future clients
/// std::vector<TcpSocket> clients;
///
/// // Create a selector
/// SocketSelector selector;
///
/// // Add the listener to the selector
/// selector.add(listener);
///
/// // Endless loop that waits for new connections
/// while (running)
/// {
/// // Make the selector wait for data on any socket
/// if (selector.wait())
/// {
/// // Test the listener
/// if (selector.isReady(listener))
/// {
/// // The listener is ready: there is a pending connection
/// TcpSocket client;
/// if (listener.accept(client) == Socket::Status::Done)
/// {
/// // Add the new client to the selector so that we will
/// // be notified when they send something
/// selector.add(client);
///
/// // Add the new client to the clients list
/// clients.push_back(std::move(client));
/// }
/// else
/// {
/// // Handle error...
/// }
/// }
/// else
/// {
/// // The listener socket is not ready, test all other sockets (the clients)
/// for (TcpSocket& client : clients)
/// {
/// if (selector.isReady(client))
/// {
/// // The client has sent some data, we can receive it
/// Packet packet;
/// if (client.receive(packet) == Socket::Status::Done)
/// {
/// ...
/// }
/// }
/// }
/// }
/// }
/// }
/// \endcode
///
/// \see `Socket`
///
////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,166 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Socket.hpp>
namespace sf
{
class TcpSocket;
////////////////////////////////////////////////////////////
/// \brief Socket that listens to new TCP connections
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API TcpListener : public Socket
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
TcpListener();
////////////////////////////////////////////////////////////
/// \brief Get the port to which the socket is bound locally
///
/// If the socket is not listening to a port, this function
/// returns 0.
///
/// \return Port to which the socket is bound
///
/// \see `listen`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getLocalPort() const;
////////////////////////////////////////////////////////////
/// \brief Start listening for incoming connection attempts
///
/// This function makes the socket start listening on the
/// specified port, waiting for incoming connection attempts.
///
/// If the socket is already listening on a port when this
/// function is called, it will stop listening on the old
/// port before starting to listen on the new port.
///
/// When providing `Socket::AnyPort` as port, the listener
/// will request an available port from the system.
/// The chosen port can be retrieved by calling `getLocalPort()`.
///
/// \param port Port to listen on for incoming connection attempts
/// \param address Address of the interface to listen on
///
/// \return Status code
///
/// \see `accept`, `close`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status listen(unsigned short port, IpAddress address = IpAddress::Any);
////////////////////////////////////////////////////////////
/// \brief Stop listening and close the socket
///
/// This function gracefully stops the listener. If the
/// socket is not listening, this function has no effect.
///
/// \see `listen`
///
////////////////////////////////////////////////////////////
void close();
////////////////////////////////////////////////////////////
/// \brief Accept a new connection
///
/// If the socket is in blocking mode, this function will
/// not return until a connection is actually received.
///
/// \param socket Socket that will hold the new connection
///
/// \return Status code
///
/// \see `listen`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status accept(TcpSocket& socket);
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class TcpListener
/// \ingroup network
///
/// A listener socket is a special type of socket that listens to
/// a given port and waits for connections on that port.
/// This is all it can do.
///
/// When a new connection is received, you must call accept and
/// the listener returns a new instance of `TcpSocket` that
/// is properly initialized and can be used to communicate with
/// the new client.
///
/// Listener sockets are specific to the TCP protocol,
/// UDP sockets are connectionless and can therefore communicate
/// directly. As a consequence, a listener socket will always
/// return the new connections as `TcpSocket` instances.
///
/// A listener is automatically closed on destruction, like all
/// other types of socket. However if you want to stop listening
/// before the socket is destroyed, you can call its `close()`
/// function.
///
/// Usage example:
/// \code
/// // Create a listener socket and make it wait for new
/// // connections on port 55001
/// TcpListener listener;
/// listener.listen(55001);
///
/// // Endless loop that waits for new connections
/// while (running)
/// {
/// TcpSocket client;
/// if (listener.accept(client) == Socket::Done)
/// {
/// // A new client just connected!
/// std::cout << "New connection received from " << client.getRemoteAddress().value() << std::endl;
/// doSomethingWith(client);
/// }
/// }
/// \endcode
///
/// \see `TcpSocket`, `Socket`
///
////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,317 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/Socket.hpp>
#include <SFML/System/Time.hpp>
#include <optional.hpp>
#include <vector>
#include <cstddef>
#include <cstdint>
namespace sf
{
class TcpListener;
class IpAddress;
class Packet;
////////////////////////////////////////////////////////////
/// \brief Specialized socket using the TCP protocol
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API TcpSocket : public Socket
{
public:
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
TcpSocket();
////////////////////////////////////////////////////////////
/// \brief Get the port to which the socket is bound locally
///
/// If the socket is not connected, this function returns 0.
///
/// \return Port to which the socket is bound
///
/// \see `connect`, `getRemotePort`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getLocalPort() const;
////////////////////////////////////////////////////////////
/// \brief Get the address of the connected peer
///
/// If the socket is not connected, this function returns
/// an unset optional.
///
/// \return Address of the remote peer
///
/// \see `getRemotePort`
///
////////////////////////////////////////////////////////////
[[nodiscard]] nonstd::optional<IpAddress> getRemoteAddress() const;
////////////////////////////////////////////////////////////
/// \brief Get the port of the connected peer to which
/// the socket is connected
///
/// If the socket is not connected, this function returns 0.
///
/// \return Remote port to which the socket is connected
///
/// \see `getRemoteAddress`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getRemotePort() const;
////////////////////////////////////////////////////////////
/// \brief Connect the socket to a remote peer
///
/// In blocking mode, this function may take a while, especially
/// if the remote peer is not reachable. The last parameter allows
/// you to stop trying to connect after a given timeout.
/// If the socket is already connected, the connection is
/// forcibly disconnected before attempting to connect again.
///
/// \param remoteAddress Address of the remote peer
/// \param remotePort Port of the remote peer
/// \param timeout Optional maximum time to wait
///
/// \return Status code
///
/// \see `disconnect`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status connect(IpAddress remoteAddress, unsigned short remotePort, Time timeout = Time::Zero);
////////////////////////////////////////////////////////////
/// \brief Disconnect the socket from its remote peer
///
/// This function gracefully closes the connection. If the
/// socket is not connected, this function has no effect.
///
/// \see `connect`
///
////////////////////////////////////////////////////////////
void disconnect();
////////////////////////////////////////////////////////////
/// \brief Send raw data to the remote peer
///
/// To be able to handle partial sends over non-blocking
/// sockets, use the `send(const void*, std::size_t, std::size_t&)`
/// overload instead.
/// This function will fail if the socket is not connected.
///
/// \param data Pointer to the sequence of bytes to send
/// \param size Number of bytes to send
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(const void* data, std::size_t size);
////////////////////////////////////////////////////////////
/// \brief Send raw data to the remote peer
///
/// This function will fail if the socket is not connected.
///
/// \param data Pointer to the sequence of bytes to send
/// \param size Number of bytes to send
/// \param sent The number of bytes sent will be written here
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(const void* data, std::size_t size, std::size_t& sent);
////////////////////////////////////////////////////////////
/// \brief Receive raw data from the remote peer
///
/// In blocking mode, this function will wait until some
/// bytes are actually received.
/// This function will fail if the socket is not connected.
///
/// \param data Pointer to the array to fill with the received bytes
/// \param size Maximum number of bytes that can be received
/// \param received This variable is filled with the actual number of bytes received
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(void* data, std::size_t size, std::size_t& received);
////////////////////////////////////////////////////////////
/// \brief Send a formatted packet of data to the remote peer
///
/// In non-blocking mode, if this function returns `Socket::Status::Partial`,
/// you \em must retry sending the same unmodified packet before sending
/// anything else in order to guarantee the packet arrives at the remote
/// peer uncorrupted.
/// This function will fail if the socket is not connected.
///
/// \param packet Packet to send
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(Packet& packet);
////////////////////////////////////////////////////////////
/// \brief Receive a formatted packet of data from the remote peer
///
/// In blocking mode, this function will wait until the whole packet
/// has been received.
/// This function will fail if the socket is not connected.
///
/// \param packet Packet to fill with the received data
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(Packet& packet);
private:
friend class TcpListener;
////////////////////////////////////////////////////////////
/// \brief Structure holding the data of a pending packet
///
////////////////////////////////////////////////////////////
struct PendingPacket
{
std::uint32_t size{}; //!< Data of packet size
std::size_t sizeReceived{}; //!< Number of size bytes received so far
std::vector<unsigned char> data; //!< Data of the packet
};
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
PendingPacket m_pendingPacket; //!< Temporary data of the packet currently being received
std::vector<unsigned char> m_blockToSendBuffer; //!< Buffer used to prepare data being sent from the socket
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class TcpSocket
/// \ingroup network
///
/// TCP is a connected protocol, which means that a TCP
/// socket can only communicate with the host it is connected
/// to. It can't send or receive anything if it is not connected.
///
/// The TCP protocol is reliable but adds a slight overhead.
/// It ensures that your data will always be received in order
/// and without errors (no data corrupted, lost or duplicated).
///
/// When a socket is connected to a remote host, you can
/// retrieve information about this host with the
/// `getRemoteAddress` and `getRemotePort` functions. You can
/// also get the local port to which the socket is bound
/// (which is automatically chosen when the socket is connected),
/// with the getLocalPort function.
///
/// Sending and receiving data can use either the low-level
/// or the high-level functions. The low-level functions
/// process a raw sequence of bytes, and cannot ensure that
/// one call to Send will exactly match one call to Receive
/// at the other end of the socket.
///
/// The high-level interface uses packets (see `Packet`),
/// which are easier to use and provide more safety regarding
/// the data that is exchanged. You can look at the `Packet`
/// class to get more details about how they work.
///
/// The socket is automatically disconnected when it is destroyed,
/// but if you want to explicitly close the connection while
/// the socket instance is still alive, you can call disconnect.
///
/// Usage example:
/// \code
/// // ----- The client -----
///
/// // Create a socket and connect it to 192.168.1.50 on port 55001
/// TcpSocket socket;
/// socket.connect("192.168.1.50", 55001);
///
/// // Send a message to the connected host
/// std::string message = "Hi, I am a client";
/// socket.send(message.c_str(), message.size() + 1);
///
/// // Receive an answer from the server
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// socket.receive(buffer.data(), buffer.size(), received);
/// std::cout << "The server said: " << buffer.data() << std::endl;
///
/// // ----- The server -----
///
/// // Create a listener to wait for incoming connections on port 55001
/// TcpListener listener;
/// listener.listen(55001);
///
/// // Wait for a connection
/// TcpSocket socket;
/// listener.accept(socket);
/// std::cout << "New client connected: " << socket.getRemoteAddress().value() << std::endl;
///
/// // Receive a message from the client
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// socket.receive(buffer.data(), buffer.size(), received);
/// std::cout << "The client said: " << buffer.data() << std::endl;
///
/// // Send an answer
/// std::string message = "Welcome, client";
/// socket.send(message.c_str(), message.size() + 1);
/// \endcode
///
/// \see `Socket`, `UdpSocket`, `Packet`
///
////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,293 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Network/Export.hpp>
#include <SFML/Network/IpAddress.hpp>
#include <SFML/Network/Socket.hpp>
#include <optional.hpp>
#include <vector>
#include <cstddef>
namespace sf
{
class Packet;
////////////////////////////////////////////////////////////
/// \brief Specialized socket using the UDP protocol
///
////////////////////////////////////////////////////////////
class SFML_NETWORK_API UdpSocket : public Socket
{
public:
////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////
// NOLINTNEXTLINE(readability-identifier-naming)
static constexpr std::size_t MaxDatagramSize{65507}; //!< The maximum number of bytes that can be sent in a single UDP datagram
////////////////////////////////////////////////////////////
/// \brief Default constructor
///
////////////////////////////////////////////////////////////
UdpSocket();
////////////////////////////////////////////////////////////
/// \brief Get the port to which the socket is bound locally
///
/// If the socket is not bound to a port, this function
/// returns 0.
///
/// \return Port to which the socket is bound
///
/// \see `bind`
///
////////////////////////////////////////////////////////////
[[nodiscard]] unsigned short getLocalPort() const;
////////////////////////////////////////////////////////////
/// \brief Bind the socket to a specific port
///
/// Binding the socket to a port is necessary for being
/// able to receive data on that port.
///
/// When providing `Socket::AnyPort` as port, the listener
/// will request an available port from the system.
/// The chosen port can be retrieved by calling `getLocalPort()`.
///
/// Since the socket can only be bound to a single port at
/// any given moment, if it is already bound when this
/// function is called, it will be unbound from the previous
/// port before being bound to the new one.
///
/// \param port Port to bind the socket to
/// \param address Address of the interface to bind to
///
/// \return Status code
///
/// \see `unbind`, `getLocalPort`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status bind(unsigned short port, IpAddress address = IpAddress::Any);
////////////////////////////////////////////////////////////
/// \brief Unbind the socket from the local port to which it is bound
///
/// The port that the socket was previously bound to is immediately
/// made available to the operating system after this function is called.
/// This means that a subsequent call to `bind()` will be able to re-bind
/// the port if no other process has done so in the mean time.
/// If the socket is not bound to a port, this function has no effect.
///
/// \see `bind`
///
////////////////////////////////////////////////////////////
void unbind();
////////////////////////////////////////////////////////////
/// \brief Send raw data to a remote peer
///
/// Make sure that `size` is not greater than
/// `UdpSocket::MaxDatagramSize`, otherwise this function will
/// fail and no data will be sent.
///
/// \param data Pointer to the sequence of bytes to send
/// \param size Number of bytes to send
/// \param remoteAddress Address of the receiver
/// \param remotePort Port of the receiver to send the data to
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(const void* data, std::size_t size, IpAddress remoteAddress, unsigned short remotePort);
////////////////////////////////////////////////////////////
/// \brief Receive raw data from a remote peer
///
/// In blocking mode, this function will wait until some
/// bytes are actually received.
/// Be careful to use a buffer which is large enough for
/// the data that you intend to receive, if it is too small
/// then an error will be returned and *all* the data will
/// be lost.
///
/// \param data Pointer to the array to fill with the received bytes
/// \param size Maximum number of bytes that can be received
/// \param received This variable is filled with the actual number of bytes received
/// \param remoteAddress Address of the peer that sent the data
/// \param remotePort Port of the peer that sent the data
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(void* data,
std::size_t size,
std::size_t& received,
nonstd::optional<IpAddress>& remoteAddress,
unsigned short& remotePort);
////////////////////////////////////////////////////////////
/// \brief Send a formatted packet of data to a remote peer
///
/// Make sure that the packet size is not greater than
/// `UdpSocket::MaxDatagramSize`, otherwise this function will
/// fail and no data will be sent.
///
/// \param packet Packet to send
/// \param remoteAddress Address of the receiver
/// \param remotePort Port of the receiver to send the data to
///
/// \return Status code
///
/// \see `receive`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status send(Packet& packet, IpAddress remoteAddress, unsigned short remotePort);
////////////////////////////////////////////////////////////
/// \brief Receive a formatted packet of data from a remote peer
///
/// In blocking mode, this function will wait until the whole packet
/// has been received.
///
/// \param packet Packet to fill with the received data
/// \param remoteAddress Address of the peer that sent the data
/// \param remotePort Port of the peer that sent the data
///
/// \return Status code
///
/// \see `send`
///
////////////////////////////////////////////////////////////
[[nodiscard]] Status receive(Packet& packet, nonstd::optional<IpAddress>& remoteAddress, unsigned short& remotePort);
private:
////////////////////////////////////////////////////////////
// Member data
////////////////////////////////////////////////////////////
std::vector<unsigned char> m_buffer; //!< Temporary buffer holding the received data in Receive(Packet)
};
} // namespace sf
////////////////////////////////////////////////////////////
/// \class UdpSocket
/// \ingroup network
///
/// A UDP socket is a connectionless socket. Instead of
/// connecting once to a remote host, like TCP sockets,
/// it can send to and receive from any host at any time.
///
/// It is a datagram protocol: bounded blocks of data (datagrams)
/// are transferred over the network rather than a continuous
/// stream of data (TCP). Therefore, one call to send will always
/// match one call to receive (if the datagram is not lost),
/// with the same data that was sent.
///
/// The UDP protocol is lightweight but unreliable. Unreliable
/// means that datagrams may be duplicated, be lost or
/// arrive reordered. However, if a datagram arrives, its
/// data is guaranteed to be valid.
///
/// UDP is generally used for real-time communication
/// (audio or video streaming, real-time games, etc.) where
/// speed is crucial and lost data doesn't matter much.
///
/// Sending and receiving data can use either the low-level
/// or the high-level functions. The low-level functions
/// process a raw sequence of bytes, whereas the high-level
/// interface uses packets (see `Packet`), which are easier
/// to use and provide more safety regarding the data that is
/// exchanged. You can look at the `Packet` class to get
/// more details about how they work.
///
/// It is important to note that `UdpSocket` is unable to send
/// datagrams bigger than `MaxDatagramSize`. In this case, it
/// returns an error and doesn't send anything. This applies
/// to both raw data and packets. Indeed, even packets are
/// unable to split and recompose data, due to the unreliability
/// of the protocol (dropped, mixed or duplicated datagrams may
/// lead to a big mess when trying to recompose a packet).
///
/// If the socket is bound to a port, it is automatically
/// unbound from it when the socket is destroyed. However,
/// you can unbind the socket explicitly with the Unbind
/// function if necessary, to stop receiving messages or
/// make the port available for other sockets.
///
/// Usage example:
/// \code
/// // ----- The client -----
///
/// // Create a socket and bind it to the port 55001
/// UdpSocket socket;
/// socket.bind(55001);
///
/// // Send a message to 192.168.1.50 on port 55002
/// std::string message = "Hi, I am " + IpAddress::getLocalAddress().toString();
/// socket.send(message.c_str(), message.size() + 1, "192.168.1.50", 55002);
///
/// // Receive an answer (most likely from 192.168.1.50, but could be anyone else)
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// nonstd::optional<IpAddress> sender;
/// unsigned short port;
/// if (socket.receive(buffer.data(), buffer.size(), received, sender, port) == Socket::Status::Done)
/// std::cout << sender->toString() << " said: " << buffer.data() << std::endl;
///
/// // ----- The server -----
///
/// // Create a socket and bind it to the port 55002
/// UdpSocket socket;
/// socket.bind(55002);
///
/// // Receive a message from anyone
/// std::array<char, 1024> buffer;
/// std::size_t received = 0;
/// nonstd::optional<IpAddress> sender;
/// unsigned short port;
/// if (socket.receive(buffer.data(), buffer.size(), received, sender, port) == Socket::Status::Done)
/// std::cout << sender->toString() << " said: " << buffer.data() << std::endl;
///
/// // Send an answer
/// std::string message = "Welcome " + sender.toString();
/// socket.send(message.c_str(), message.size() + 1, sender, port);
/// \endcode
///
/// \see `Socket`, `TcpSocket`, `Packet`
///
////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,55 @@
////////////////////////////////////////////////////////////
//
// SFML - Simple and Fast Multimedia Library
// Copyright (C) 2007-2025 Laurent Gomila (laurent@sfml-dev.org)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#include <SFML/System/Angle.hpp>
#include <SFML/System/Clock.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/Exception.hpp>
#include <SFML/System/FileInputStream.hpp>
#include <SFML/System/InputStream.hpp>
#include <SFML/System/MemoryInputStream.hpp>
#include <SFML/System/Sleep.hpp>
#include <SFML/System/String.hpp>
#include <SFML/System/Time.hpp>
#include <SFML/System/Utf.hpp>
#include <SFML/System/Vector2.hpp>
#include <SFML/System/Vector3.hpp>
////////////////////////////////////////////////////////////
/// \defgroup system System module
///
/// Base module of SFML, defining various utilities. It provides
/// vector classes, Unicode strings and conversion functions,
/// threads and mutexes, timing classes.
///
////////////////////////////////////////////////////////////

Some files were not shown because too many files have changed in this diff Show More