Compare commits

..

23 Commits

Author SHA1 Message Date
Rosalie Wanders ff237ac4e2 RMG: improve window title 2026-06-21 22:47:11 +02:00
Rosalie Wanders dbe1dc685f 3rdParty: update mupen64plus-core to work with newer SDL_net 2026-06-18 14:06:05 +02:00
Rosalie Wanders feeb0a2e40 3rdParty: update SDL_net 2026-06-18 14:01:04 +02:00
Rosalie Wanders b0c09d17c9 3rdParty: update mupen64plus-rsp-cxd4 2026-06-18 13:56:22 +02:00
Rosalie Wanders 453f663973 Package: v0.9.0 2026-06-14 19:54:27 +02:00
Rosalie Wanders f76e7472fd 3rdParty: revert to using GL_RGBA in mupen64plus-video-GLideN64
For some reason when using GL_RGB, it seems to corrupt memory and cause
a crash, valgrind says the following:

==29295== Invalid write of size 1
==29295==    at 0x20BC10E6: convert_ubyte (format_utils.c:996)
==29295==    by 0x20BDE59B: _mesa_format_convert (format_utils.c:452)
==29295==    by 0x20CA072D: read_rgba_pixels (readpix.c:609)
==29295==    by 0x20CA072D: _mesa_readpixels (readpix.c:923)
==29295==    by 0x20CA6DCA: st_ReadPixels (st_cb_readpixels.c:577)
==29295==    by 0x20CA120B: read_pixels (readpix.c:1218)
==29295==    by 0x20CA120B: _mesa_ReadnPixelsARB (readpix.c:1235)
==29295==    by 0x20CA1564: _mesa_ReadPixels (readpix.c:1250)
==29295==    by 0x5741BCF4: opengl::GlReadPixelsCommand::commandToExecute() (opengl_WrappedFunctions.h:682)
==29295==    by 0x5740EB75: opengl::OpenGlCommand::performCommandSingleThreaded() (opengl_Command.cpp:13)
==29295==    by 0x574117B1: opengl::FunctionWrapper::executeCommand(std::shared_ptr<opengl::OpenGlCommand>) (opengl_Wrapper.cpp:30)
==29295==    by 0x57412548: opengl::FunctionWrapper::wrReadPixels(int, int, int, int, unsigned int, unsigned int, void*) (opengl_Wrapper.cpp:316)
==29295==    by 0x57507C74: DisplayWindowMupen64plus::_readScreen2(void*, int*, int*, int) (mupen64plus_DisplayWindow.cpp:263)
==29295==    by 0x573B69D3: DisplayWindow::readScreen2(void*, int*, int*, int) (DisplayWindow.cpp:209)

valgrind leads me to believe this might be a mesa issue where it writes
out of bounds of the provided buffer, this issue does not occur when
using GL_RGBA, nor does it appear when using an older mesa version in
the AppImage.

Reverting https://github.com/gonetz/GLideN64/pull/2856 fixes the issue.
Fixes https://github.com/Rosalie241/RMG/issues/498
2026-06-14 19:48:39 +02:00
Rosalie Wanders ff9b9a2040 Package: force pulseaudio for AppImage in AppRun 2026-06-13 21:50:53 +02:00
Rosalie Wanders 99bb81d9b9 Package: add libpulse to AppImage in Create.sh 2026-06-13 21:50:53 +02:00
Rosalie Wanders fe67ba053e RMG-Audio: retrieve SDL error when SDL_Init() fails 2026-06-13 19:15:39 +02:00
Rosalie Wanders 6ebbba6fc5 ci: use actions/upload-artifact@v7 2026-06-13 19:14:34 +02:00
Rosalie Wanders 21d0b55a35 ci: update SDL to release-3.4.10 2026-06-13 19:00:55 +02:00
Rosalie Wanders 6ef6432a15 ci: use hendrikmuhs/ccache-action@v1.2.23
It seems the v1.2 tag isn't being updated anymore.
2026-06-13 19:00:43 +02:00
Rosalie Wanders 02e2518be2 3rdParty: update LZMA SDK to v26.01 2026-06-13 18:55:42 +02:00
Rosalie Wanders 2da7750640 3rdParty: update mupen64plus-core 2026-06-13 18:53:48 +02:00
Rosalie Wanders 4c50859481 README: add Netplay section 2026-06-13 18:51:09 +02:00
Rosalie Wanders 2867fc627b RMG-Core: remove Netplay_DispatcherUrl setting 2026-06-13 18:51:09 +02:00
Rosalie Wanders 47df34da6b RMG: remove support for the dispatcher server 2026-06-13 18:51:09 +02:00
Rosalie Wanders 7e4e402f27 RMG-Core: fix minizip include in Archive.cpp 2026-02-24 08:25:28 +01:00
Rosalie Wanders 8ee3410680 3rdParty: fix minizip includes for mupen64plus-core 2026-02-24 08:25:14 +01:00
Rosalie Wanders e9167db9ff Package: add vcs-browser url to metainfo.xml 2026-01-23 07:10:23 +01:00
Rosalie Wanders 94f5fd9d82 Package: use 'and' instead of '&amp;' in desktop file 2026-01-23 07:06:11 +01:00
Rosalie Wanders 969d63f182 Package: v0.8.9 2026-01-23 06:53:42 +01:00
Rosalie Wanders c7a4e6c47a README: remove obsolete support link 2026-01-21 10:05:15 +01:00
88 changed files with 2118 additions and 1104 deletions
+6 -6
View File
@@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: hendrikmuhs/ccache-action@v1.2
- uses: hendrikmuhs/ccache-action@v1.2.23
with:
key: linux-${{ matrix.architecture }}-${{ matrix.features }}-${{ matrix.configuration }}
- name: Install Packages
@@ -54,7 +54,7 @@ jobs:
run: |
export sdl_dir="$(pwd)/../SDL3"
git clone https://github.com/libsdl-org/SDL.git "$sdl_dir" -b release-3.2.28 --depth=1
git clone https://github.com/libsdl-org/SDL.git "$sdl_dir" -b release-3.4.10 --depth=1
mkdir -p "$sdl_dir/build"
cmake -S "$sdl_dir" -B "$sdl_dir/build" \
@@ -94,7 +94,7 @@ jobs:
shell: bash
- name: Upload RMG (AppImage)
if: ${{ matrix.features == 'ON' && matrix.architecture == 'x86_64' && matrix.configuration == 'Release' }}
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v7
with:
name: RMG-Portable-Linux64-${{ env.GIT_REVISION }}
path: Bin/*.AppImage
@@ -112,7 +112,7 @@ jobs:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: hendrikmuhs/ccache-action@v1.2
- uses: hendrikmuhs/ccache-action@v1.2.23
with:
key: windows-${{ matrix.features }}-${{ matrix.configuration }}
- uses: msys2/setup-msys2@v2
@@ -167,13 +167,13 @@ jobs:
shell: msys2 {0}
- name: Upload RMG (Portable)
if: ${{ matrix.features == 'ON' && matrix.configuration == 'Release' }}
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v7
with:
name: RMG-Portable-Windows64-${{ env.GIT_REVISION }}
path: Bin/Release/*
- name: Upload RMG (Installer)
if: ${{ matrix.features == 'ON' && matrix.configuration == 'Release' }}
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v7
with:
name: RMG-Setup-Windows64-${{ env.GIT_REVISION }}
path: Bin/*.exe
+8
View File
@@ -3,6 +3,14 @@
[ -f "$APPIMAGE_QT_THEME" ] && set -- "$@" "-stylesheet" "$APPIMAGE_QT_THEME"
cur_dir="$(readlink -f "$(dirname "$0")")"
# force pulseaudio to fix audio issues
# else we get the following error:
# Failed to initialize SDL audio subsystem: Failed loading libjack.so.0:
# /tmp/.mount_RMG-PoJFjkog/shared/lib/libc.so.6: version `GLIBC_ABI_GNU2_TLS' not found
# (required by /usr/lib64/pipewire-0.3/jack/libjack.so.0)
# see https://github.com/Rosalie241/RMG/issues/494 for more details
export SDL_AUDIODRIVER=pulseaudio
exec "$cur_dir/bin/RMG" \
--lib-path="$cur_dir/shared/lib/RMG" \
--core-path="$cur_dir/shared/lib/RMG/Core" \
+1
View File
@@ -64,6 +64,7 @@ $XVFB_RUN "$script_dir/lib4bin" --dst-dir "$bin_dir" \
"$lib_dir"/libGL* \
"$lib_dir"/libEGL* \
"$lib_dir"/libvulkan* \
"$lib_dir"/libpulse* \
"$lib_dir"/dri/* \
"$lib_dir"/libssl.so* \
"$lib_dir"/qt6/plugins/iconengines/* \
+1 -1
View File
@@ -1,6 +1,6 @@
# Maintainer: Rosalie Wanders <rosalie@mailbox.org>
pkgname=rmg
pkgver=0.8.8
pkgver=0.9.0
pkgrel=1
pkgdesc="Rosalie's Mupen GUI"
arch=('x86_64' 'aarch64')
+1 -1
View File
@@ -1,5 +1,5 @@
Name: RMG
Version: 0.8.8
Version: 0.9.0
Release: %autorelease
Summary: Rosalie's Mupen GUI
+1 -1
View File
@@ -1,7 +1,7 @@
[Desktop Entry]
Type=Application
Name=Rosalie's Mupen GUI
Comment=An easy to use &amp; cross-platform mupen64plus front-end written in C++ & Qt
Comment=An easy to use and cross-platform mupen64plus front-end written in C++ & Qt
Exec=RMG %f
TryExec=RMG
Icon=com.github.Rosalie241.RMG
@@ -29,6 +29,32 @@
</screenshot>
</screenshots>
<releases>
<release version="v0.9.0" date="2026-06-14" type="stable">
<description>
<p>Changes:</p>
<ul>
<li>Fix build issues with zlib v1.3.1</li>
<li>Fix audio not working when using the AppImage (thank you Samueru-sama)</li>
<li>Fix screenshots causing a crash on Linux</li>
<li>Remove dispatcher server support</li>
<li>Update mupen64plus-core and LZMA SDK</li>
</ul>
</description>
</release>
<release version="v0.8.9" date="2026-01-23" type="stable">
<description>
<p>Changes:</p>
<ul>
<li>Fix AppImage icon not working since v0.7.1 (thank you Samueru-sama)</li>
<li>Fix desktop file causing a crash on Budgie (thank you jpleatherland)</li>
<li>Fix memory pak from player 2, 3 and 4 not saving correctly</li>
<li>Fix Star Wars - Rogue Squadron having missing geometry when using GLideN64</li>
<li>Remove translations from the configuration GUI of GLideN64</li>
<li>Add plugin settings shortcuts to the toolbar</li>
<li>Update mupen64plus-video-GLideN64, SDL_net and SDL_GameControllerDB</li>
</ul>
</description>
</release>
<release version="v0.8.8" date="2025-11-16" type="stable">
<description>
<p>Changes:</p>
@@ -1024,6 +1050,7 @@
</releases>
<url type="homepage">https://github.com/Rosalie241/RMG</url>
<url type="bugtracker">https://github.com/Rosalie241/RMG/issues</url>
<url type="vcs-browser">https://github.com/Rosalie241/RMG</url>
<categories>
<category>Game</category>
<category>Emulator</category>
+36 -4
View File
@@ -7,7 +7,7 @@ It offers a simple-to-use user interface.
## Download
#### Windows
You can download Rosalie's Mupen GUI on ![Github Releases](https://github.com/Rosalie241/RMG/releases)
You can download Rosalie's Mupen GUI on [Github Releases](https://github.com/Rosalie241/RMG/releases)
#### Linux
You can download Rosalie's Mupen GUI on
@@ -16,11 +16,43 @@ You can download Rosalie's Mupen GUI on
* [Arch User Repository](https://aur.archlinux.org/packages/rmg)
* [Fedora COPR](https://copr.fedorainfracloud.org/coprs/rosalie/RMG/)
## Netplay
You can use netplay with the following methods:
### Port Forwarding
1) Download [rmg-netplay-server](https://github.com/Rosalie241/rmg-netplay-server/releases/latest)
2) Port forward the ports stated in the [rmg-netplay-server README](https://github.com/Rosalie241/rmg-netplay-server)
3) Create a JSON file with something similar to the following contents, but using your public IP instead of a local one:
```
{
"some-server-name": "127.0.0.1"
}
```
If you want to add multiple servers instead:
```
{
"some-server-name": "127.0.0.1",
"some-server-name-2": "127.0.0.1"
}
```
4) Store the JSON file or upload it somewhere
5) Enter the JSON file path or URL in the "Server list URL" text box in the RMG settings dialog
6) The specified servers in the JSON file should appear in the RMG GUI
### Tunneling Software
1) Download [rmg-netplay-server](https://github.com/Rosalie241/rmg-netplay-server/releases/latest)
2) Download [Radmin VPN](https://www.radmin-vpn.com/)
3) Run and create or join a network in Radmin VPN
4) The host should run [rmg-netplay-server](https://github.com/Rosalie241/rmg-netplay-server)
5) The server should show up as `local-server` within the RMG GUI
## Support
You can ask for help or report issues on
* [Github](https://github.com/Rosalie241/RMG/issues/new)
* [Discord](https://discord.gg/k9GuyJ2PpF)
You can ask for help or report issues on [Github](https://github.com/Rosalie241/RMG/issues/new)
## License
+3 -3
View File
@@ -6,7 +6,7 @@
[subrepo]
remote = git@github.com:libsdl-org/SDL_net.git
branch = main
commit = 4dd85af45d8597f93796815fba0defa5d499be21
parent = 02eef161e7f4ac7a4deebd0115574b7a27c5f58f
commit = 4dd9d8418e9781d10481fd6177e3a1064ca201fe
parent = b0c09d17c97848d0386a11b3b6fdd572a2b2228a
method = merge
cmdver = 0.4.9
cmdver = 0.4.6
+1 -1
View File
@@ -18,7 +18,7 @@ LOCAL_SRC_FILES := \
$(subst $(LOCAL_PATH)/,,) \
$(wildcard $(LOCAL_PATH)/src/*.c) \
LOCAL_CFLAGS =
LOCAL_CFLAGS = -DHAVE_GETIFADDRS
# Warnings we haven't fixed (yet)
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-sign-compare
+48 -7
View File
@@ -8,7 +8,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
# See docs/release_checklist.md
set(MAJOR_VERSION 3)
set(MINOR_VERSION 0)
set(MINOR_VERSION 3)
set(MICRO_VERSION 0)
set(SDL_REQUIRED_VERSION 3.0.0)
@@ -67,7 +67,7 @@ include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
set(PLATFORM_SUPPORTS_SHARED ON)
if(EMSCRIPTEN OR VITA OR PSP OR PS2 OR N3DS OR RISCOS)
if(EMSCRIPTEN OR VITA OR PSP OR PS2 OR N3DS OR RISCOS OR DOS)
set(PLATFORM_SUPPORTS_SHARED OFF)
endif()
@@ -104,9 +104,46 @@ endif()
set(PC_LIBS)
set(PC_REQUIRES)
add_library(${sdl3_net_target_name} src/SDL_net.c)
if(HAIKU)
set(HAVE_GETIFADDRS True) # Haiku always has it (but you have to -lnetwork, so check_c_source_compiles won't work as-is).
set(EXTRA_SOURCES "src/SDL_net_haiku.cpp")
enable_language(CXX)
endif()
set(PLATFORM_UNSUPPORTED False)
if (DOS OR EMSCRIPTEN) # These either don't have networking at all, or have needs we don't currently meet.
set(PLATFORM_UNSUPPORTED True)
endif()
option(SDLNET_STUB_ONLY "Build a stub library that does nothing" ${PLATFORM_UNSUPPORTED})
if(SDLNET_STUB_ONLY)
message(WARNING "Only building a stub library. The functions will be there but all of them will fail!")
add_library(${sdl3_net_target_name} src/SDL_net_stub_only.c)
else()
add_library(${sdl3_net_target_name} src/SDL_net.c ${EXTRA_SOURCES})
endif()
if(NOT (HAIKU OR WIN32 OR SDLNET_STUB_ONLY))
check_c_source_compiles("
#include <ifaddrs.h>
int main(void) {
struct ifaddrs* ifap;
getifaddrs(&ifap);
return 0;
}
" HAVE_GETIFADDRS)
endif()
if(HAVE_GETIFADDRS)
target_compile_definitions(${sdl3_net_target_name} PRIVATE HAVE_GETIFADDRS)
endif()
add_library(SDL3_net::${sdl3_net_target_name} ALIAS ${sdl3_net_target_name})
set_property(TARGET ${sdl3_net_target_name} PROPERTY C_STANDARD 99)
if("c_std_99" IN_LIST CMAKE_C_COMPILE_FEATURES)
target_compile_features(${sdl3_net_target_name} PRIVATE c_std_99)
else()
message(WARNING "target_compile_features does not know c_std_99 for C compiler")
endif()
if(NOT TARGET SDL3_net::SDL3_net)
add_library(SDL3_net::SDL3_net ALIAS ${sdl3_net_target_name})
endif()
@@ -140,8 +177,8 @@ if(WIN32)
target_link_libraries(${sdl3_net_target_name} PRIVATE iphlpapi ws2_32)
list(APPEND PC_LIBS -liphlpapi -lws2_32)
endif()
if(CMAKE_SYSTEM_NAME MATCHES "Haiku.*")
target_link_libraries(${sdl3_net_target_name} PRIVATE network)
if(HAIKU)
target_link_libraries(${sdl3_net_target_name} PRIVATE network be bnetapi)
endif()
set_target_properties(${sdl3_net_target_name} PROPERTIES
OUTPUT_NAME "SDL3_net"
@@ -306,7 +343,10 @@ if(SDLNET_SAMPLES)
sdl_target_link_options_no_undefined(${TARGET})
target_link_libraries(${TARGET} PRIVATE SDL3_net::${sdl3_net_target_name})
target_link_libraries(${TARGET} PRIVATE ${sdl3_target_name})
set_property(TARGET ${TARGET} PROPERTY C_STANDARD 99)
if("c_std_99" IN_LIST CMAKE_C_COMPILE_FEATURES)
target_compile_features(${TARGET} PRIVATE c_std_99)
endif()
set_property(TARGET ${TARGET} PROPERTY C_EXTENSIONS FALSE)
if(SDLNET_SAMPLES_INSTALL)
@@ -320,6 +360,7 @@ if(SDLNET_SAMPLES)
add_sdl_net_example_executable(resolve-hostnames examples/resolve-hostnames.c)
add_sdl_net_example_executable(get-local-addrs examples/get-local-addrs.c)
add_sdl_net_example_executable(echo-server examples/echo-server.c)
add_sdl_net_example_executable(datagram examples/datagram.c)
# Build at least one example in C90
set_property(TARGET get-local-addrs PROPERTY C_STANDARD 90)
+47
View File
@@ -0,0 +1,47 @@
# To build and use SDL_net:
SDL_net supports a number of development environments:
- [CMake](docs/INTRO-cmake.md)
- [Visual Studio on Windows](docs/INTRO-visualstudio.md)
- [Xcode on Apple platforms](docs/INTRO-xcode.md)
SDL_net is also usable in other environments. The basic steps are to use CMake to build the library and then use the headers and library that you built in your project. You can search online to see if anyone has specific steps for your setup.
# Documentation
An API reference and additional documentation is available at:
https://wiki.libsdl.org/SDL3_net
# Example code
There are simple example programs in the examples directory.
If you're using CMake, you can build them adding `-DSDLNET_SAMPLES=ON` to the CMake command line when building SDL_net.
If you're using Visual Studio there are separate projects in the VisualC directory.
If you're using Xcode there are separate projects in the Xcode directory.
# Discussions
## Discord
You can join the official Discord server at:
https://discord.com/invite/BwpFGBWsv8
## Forums/mailing lists
You can join SDL development discussions at:
https://discourse.libsdl.org/
Once you sign up, you can use the forum through the website or as a mailing list from your email client.
## Announcement list
You can sign up for the low traffic announcement list at:
https://www.libsdl.org/mailing-list.php
+10 -11
View File
@@ -1,19 +1,18 @@
# SDL_net 3.0
The latest version of this library is available from GitHub:
https://github.com/libsdl-org/SDL_net/releases
This is a portable network library for use with SDL. It's goal is to
simplify the use of the usual socket interfaces and use SDL infrastructure
to handle some portability things (such as threading and reporting
errors).
simplify the use of the usual socket interfaces and use SDL to handle
common portable functionality such as threading and reporting errors.
It is available under the zlib license, found in the file LICENSE.txt.
The API can be found in the file SDL_net.h and online at https://wiki.libsdl.org/SDL3_net
This library supports most platforms that offer both SDL3 and networking.
This is a work in progress!
The latest version of this library is available from GitHub:
https://github.com/libsdl-org/SDL_net/releases
Installation instructions and a quick introduction is available in
[INSTALL.md](INSTALL.md)
This library is distributed under the terms of the zlib license,
available in [LICENSE.txt](LICENSE.txt).
Enjoy!
+1 -1
View File
@@ -199,7 +199,7 @@ function(target_get_dynamic_library DEST TARGET)
endif()
if(result)
string(TOLOWER "${result}" result_lower)
if(WIN32 OR OS2)
if(WIN32 OR OS2 OR CYGWIN)
if(NOT result_lower MATCHES ".*dll")
message(FATAL_ERROR "\"${result}\" is not a .dll library")
endif()
+141 -11
View File
@@ -27,7 +27,7 @@
* SDL_net is a simple library to help with networking.
*
* In current times, it's a relatively thin layer over system-level APIs like
* BSD Sockets or WinSock. It's primary strength is in making those interfaces
* BSD Sockets or WinSock. Its primary strength is in making those interfaces
* less complicated to use, and handling several unexpected corner cases, so
* the app doesn't have to.
*
@@ -136,7 +136,7 @@ extern "C" {
*
* \since This macro is available since SDL_net 3.0.0.
*/
#define SDL_NET_MINOR_VERSION 0
#define SDL_NET_MINOR_VERSION 3
/**
* The current micro (or patchlevel) version of the SDL_net headers.
@@ -415,6 +415,50 @@ extern SDL_DECLSPEC NET_Status SDLCALL NET_GetAddressStatus(NET_Address *address
*/
extern SDL_DECLSPEC const char * SDLCALL NET_GetAddressString(NET_Address *address);
/**
* Get the protocol-level bytes of a network address from a resolved address.
*
* This data is not human-readable, is protocol-specific, and might not even
* be in a specific byte order.
*
* This is only useful for possibly hashing, to map a address to a specific
* player in a game, or possibly for handing to a system-level networking API
* (which is _not_ recommended; an app does this at their own risk).
*
* Do not store these bytes for future runs of the program; there is no
* promise the format won't change.
*
* On return `*num_bytes` will hold the number of bytes provided with the
* address. Since the data is not NULL-terminated, this is the only way to
* determine its size; as such, this parameter must not be NULL.
*
* Do not free or modify the returned data; it belongs to the NET_Address that
* was queried, and is valid as long as the object lives. Either make sure the
* address has a reference as long as you need this or make a copy of the
* bytes.
*
* This will return NULL if resolution is still in progress, or if resolution
* failed. You can use NET_GetAddressStatus() or NET_WaitUntilResolved() to
* make sure resolution has successfully completed before calling this.
*
* A human-readable version is available in NET_GetAddressString() and isn't
* any less efficient to query than the raw bytes.
*
* \param address The NET_Address to query.
* \param num_bytes on return, will be set to the number of bytes returned.
* \returns a pointer to bytes, or NULL on error; call SDL_GetError() for
* details.
*
* \threadsafety It is safe to call this function from any thread.
*
* \since This function is available since SDL_net 3.0.0.
*
* \sa NET_GetAddressString
* \sa NET_GetAddressStatus
* \sa NET_WaitUntilResolved
*/
extern SDL_DECLSPEC const void * SDLCALL NET_GetAddressBytes(NET_Address *address, int *num_bytes);
/**
* Add a reference to an NET_Address.
*
@@ -640,8 +684,13 @@ typedef struct NET_StreamSocket NET_StreamSocket;
* you do not have to byteswap it into "network order," as the library will
* handle that for you.
*
* There are currently no extra properties for creating a client, so `props`
* should be zero. A future revision of SDL_net may add additional (optional)
* properties.
*
* \param address the address of the remote server to connect to.
* \param port the port on the remote server to connect to.
* \param props properties of the new client. Specify zero for defaults.
* \returns a new NET_StreamSocket, pending connection, or NULL on error; call
* SDL_GetError() for details.
*
@@ -653,7 +702,7 @@ typedef struct NET_StreamSocket NET_StreamSocket;
* \sa NET_GetConnectionStatus
* \sa NET_DestroyStreamSocket
*/
extern SDL_DECLSPEC NET_StreamSocket * SDLCALL NET_CreateClient(NET_Address *address, Uint16 port);
extern SDL_DECLSPEC NET_StreamSocket * SDLCALL NET_CreateClient(NET_Address *address, Uint16 port, SDL_PropertiesID props);
/**
* Block until a stream socket has connected to a server.
@@ -750,8 +799,24 @@ typedef struct NET_Server NET_Server;
* you do not have to byteswap it into "network order," as the library will
* handle that for you.
*
* The caller may supply properties to customize behavior. This is optional,
* and a value of zero for `props` will request defaults for all properties.
*
* These are the supported properties:
*
* - `NET_PROP_SERVER_REUSEADDR_BOOLEAN`: true if the server should be created
* even if a previous server has recently used this address. For various
* reasons, networks prefer that there be some delay between apps reusing
* the same address, but this can be problematic when iterating quickly, for
* software development purposes or just restarting a crashed service. This
* property defaults to true (although it should be noted that, at the
* operating system level, this defaults to false!). If this property is
* false and the OS feels that not enough time has elapsed, server creation
* will fail and this function will report an error.
*
* \param addr the _local_ address to listen for connections on, or NULL.
* \param port the port on the local address to listen for connections on.
* \param props properties of the new server. Specify zero for defaults.
* \returns a new NET_Server, or NULL on error; call SDL_GetError() for
* details.
*
@@ -763,7 +828,10 @@ typedef struct NET_Server NET_Server;
* \sa NET_AcceptClient
* \sa NET_DestroyServer
*/
extern SDL_DECLSPEC NET_Server * SDLCALL NET_CreateServer(NET_Address *addr, Uint16 port);
extern SDL_DECLSPEC NET_Server * SDLCALL NET_CreateServer(NET_Address *addr, Uint16 port, SDL_PropertiesID props);
#define NET_PROP_SERVER_REUSEADDR_BOOLEAN "NET.server.reuseaddr"
/**
* Create a stream socket for the next pending client connection.
@@ -1029,7 +1097,7 @@ extern SDL_DECLSPEC int SDLCALL NET_WaitUntilStreamSocketDrained(NET_StreamSocke
* on what is available at the time, and also the app isn't required to read
* all available data at once.
*
* This call never blocks; if no new data isn't available at the time of the
* This call never blocks; if no new data is available at the time of the
* call, it returns 0 immediately. The caller can try again later.
*
* If the connection has failed (remote side dropped us, or one of a million
@@ -1163,8 +1231,8 @@ typedef struct NET_DatagramSocket NET_DatagramSocket;
*/
typedef struct NET_Datagram
{
NET_Address *addr; /**< this is unref'd by NET_DestroyDatagram. You only need to ref it if you want to keep it. */
Uint16 port; /**< these do not have to come from the same port the receiver is bound to. These are in host byte order, don't byteswap them! */
NET_Address *addr; /**< Sender's address. This is unref'd by NET_DestroyDatagram. You only need to ref it if you want to keep it. */
Uint16 port; /**< Sender's port. These do not have to come from the same port the receiver is bound to. These are in host byte order, don't byteswap them! */
Uint8 *buf; /**< the payload of this datagram. */
int buflen; /**< the number of bytes available at `buf`. */
} NET_Datagram;
@@ -1213,10 +1281,37 @@ typedef struct NET_Datagram
* you do not have to byteswap it into "network order," as the library will
* handle that for you.
*
* The caller may supply properties to customize behavior. This is optional,
* and a value of zero for `props` will request defaults for all properties.
*
* These are the supported properties:
*
* - `NET_PROP_DATAGRAM_SOCKET_REUSEADDR_BOOLEAN`: true if the socket should
* be created even if a previous socket has recently used this address. For
* various reasons, networks prefer that there be some delay between apps
* reusing the same address, but this can be problematic when iterating
* quickly, for software development purposes or just restarting a crashed
* service. This property defaults to true (although it should be noted
* that, at the operating system level, this defaults to false!). If this
* property is false and the OS feels that not enough time has elapsed,
* socket creation will fail and this function will report an error.
* - `NET_PROP_DATAGRAM_SOCKET_ALLOW_BROADCAST_BOOLEAN`: true if the socket
* should allow broadcasting. At the lower level, this will set
* `SO_BROADCAST` for IPv4 sockets, to allow sending to the subnet's
* broadcast address at the OS level. For IPv6, it'll join the all-nodes
* link-local multicast group, ff02::1, allowing sending and receiving
* there, more or less simulating the usual IPv4 broadcast semantics. Other
* protocols take similar approaches. If you do not intend to send or
* receive broadcast packets on this socket, set this property to false, or
* omit it, as it defaults to false. Note: IPv4 will still be able to
* receive broadcast packets without this option, but IPv6 will not. Also
* see notes about sending to a broadcast address in NET_SendDatagram().
*
* \param addr the local address to listen for connections on, or NULL to
* listen on all available local addresses.
* \param port the port on the local address to listen for connections on, or
* zero for the system to decide.
* \param props properties of the new socket. Specify zero for defaults.
* \returns a new NET_DatagramSocket, or NULL on error; call SDL_GetError()
* for details.
*
@@ -1227,7 +1322,11 @@ typedef struct NET_Datagram
* \sa NET_GetLocalAddresses
* \sa NET_DestroyDatagramSocket
*/
extern SDL_DECLSPEC NET_DatagramSocket * SDLCALL NET_CreateDatagramSocket(NET_Address *addr, Uint16 port);
extern SDL_DECLSPEC NET_DatagramSocket * SDLCALL NET_CreateDatagramSocket(NET_Address *addr, Uint16 port, SDL_PropertiesID props);
#define NET_PROP_DATAGRAM_SOCKET_REUSEADDR_BOOLEAN "NET.datagram_socket.reuseaddr"
#define NET_PROP_DATAGRAM_SOCKET_ALLOW_BROADCAST_BOOLEAN "NET.datagram_socket.allow_broadcast"
/**
* Send a new packet over a datagram socket to a remote system.
@@ -1239,7 +1338,9 @@ extern SDL_DECLSPEC NET_DatagramSocket * SDLCALL NET_CreateDatagramSocket(NET_Ad
*
* Datagram packets might arrive in a different order than you sent them, or
* they may just be lost while travelling across the network. You have to plan
* for this.
* for this. As an added confusion, since SDL_net might send the same packet
* on multiple interfaces, you might get duplicate packets, possibly from
* different network addresses. You have to plan for this, too.
*
* You can send to any address and port on the network, but there has to be a
* datagram socket waiting for the data on the other side for the packet not
@@ -1262,8 +1363,37 @@ extern SDL_DECLSPEC NET_DatagramSocket * SDLCALL NET_CreateDatagramSocket(NET_Ad
* should assume it is no longer usable and should destroy it with
* SDL_DestroyDatagramSocket().
*
* Sending to a NULL address is treated as a request to broadcast a packet.
* Note that this will report failure immediately if the socket was not
* created with broadcast permission. Broadcast packets are (more or less)
* sent to every machine on the LAN, unconditionally.
*
* **WARNING**: It is possible to build a game where everyone is playing on
* the same LAN, and every player is simply broadcasting packets. This is
* absolutely the wrong thing to do, however. Broadcast packets go to every
* device on the LAN, whether they want them or not. The game DOOM, in its
* heyday, was capable of
* [bringing entire networks to their knees](https://doomwiki.org/wiki/Doom_in_workplaces)
* , as many players on the same network would all be broadcasting
* relentlessly.
*
* In practice, broadcasting sparingly can be useful for certain
* functionality: a LAN-only client broadcasting a few packets to ask for
* available servers, and running servers replying directly to that client
* without broadcasting at all, is reasonable and safe. Once clients and
* servers have found each other, they can communicate directly without any
* broadcasting at all. For peer-to-peer games, once connection is
* established, it's better to either send unique packets to each known
* player, or use a multicasting (which works like broadcast, but only routes
* packets to devices that are explicitly listening for it).
*
* With IPv6, which doesn't support broadcasts, broadcasting is faked with
* multicast to the all-nodes link-local multicast group, ff02::1, either on a
* specific interface or letting the OS choose the default. Other protocols
* might fake broadcast operations in similar ways in the future.
*
* \param sock the datagram socket to send data through.
* \param address the NET_Address object address.
* \param address the NET_Address object address. May be NULL to broadcast.
* \param port the address port.
* \param buf a pointer to the data to send as a single packet.
* \param buflen the size of the data to send, in bytes.
@@ -1287,7 +1417,7 @@ extern SDL_DECLSPEC bool SDLCALL NET_SendDatagram(NET_DatagramSocket *sock, NET_
* Datagram sockets send packets of data. They either arrive as complete
* packets or they don't arrive at all, so you'll never receive half a packet.
*
* This call never blocks; if no new data isn't available at the time of the
* This call never blocks; if no new data is available at the time of the
* call, it returns true immediately. The caller can try again later.
*
* On a successful call to this function, it returns true, even if no new
+476 -92
View File
@@ -171,7 +171,9 @@ static int WindowsPoll(struct pollfd *fds, unsigned int nfds, int timeout)
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#ifndef SDL_PLATFORM_VITA
#include <net/if.h>
#endif
#include <netdb.h>
#include <errno.h>
#include <string.h>
@@ -182,15 +184,18 @@ static int WindowsPoll(struct pollfd *fds, unsigned int nfds, int timeout)
#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID)
#define USE_NETLINK 1
#endif
#ifdef USE_NETLINK
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#else
#endif
#ifdef HAVE_GETIFADDRS
#include <ifaddrs.h>
#endif
#ifdef SDL_PLATFORM_VITA
#include <psp2/net/netctl.h>
#endif
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
typedef int Socket;
@@ -211,13 +216,7 @@ int NET_Version(void)
return SDL_NET_VERSION;
}
typedef enum NET_AddressType
{
NET_ADDRTYPE_UNKNOWN=-1,
NET_ADDRTYPE_UNICAST,
NET_ADDRTYPE_MULTICAST,
NET_ADDRTYPE_BROADCAST
} NET_AddressType;
typedef struct NetworkInterface NetworkInterface;
struct NET_Address
{
@@ -226,18 +225,17 @@ struct NET_Address
char *errstr;
SDL_AtomicInt refcount;
SDL_AtomicInt status; // This is actually a NET_Status.
NET_AddressType type;
struct addrinfo *ainfo;
NET_Address *resolver_next; // a linked list for the resolution job queue.
};
typedef struct NetworkInterfaces
struct NetworkInterface
{
char *name;
int index;
Uint32 index;
NET_Address *address;
NET_Address *broadcast;
} NetworkInterfaces;
};
#define MIN_RESOLVER_THREADS 2
@@ -257,9 +255,13 @@ static SDL_AtomicInt resolver_percent_loss;
static SDL_InitState interface_init;
static SDL_RWLock *interface_rwlock = NULL;
static int num_interfaces = 0;
static NetworkInterfaces *interfaces = NULL;
static NetworkInterface *interfaces = NULL;
static SDL_AtomicInt interfaces_have_changed;
// Other stuff...
static NET_Address *ipv6_broadcast_addr = NULL;
// between lo and hi (inclusive; it can return lo or hi itself, too!).
static int RandomNumberBetween(const int lo, const int hi)
{
@@ -381,7 +383,7 @@ static NET_Address *CreateSDLNetAddrFromSockAddr(const struct sockaddr *saddr, S
/* Network interface enumeration... */
static void FreeNetworkInterfaces(NetworkInterfaces *ifaces, int num_ifaces)
static void FreeNetworkInterfaces(NetworkInterface *ifaces, int num_ifaces)
{
for (int i = 0; i < num_ifaces; i++) {
NET_UnrefAddress(ifaces[i].address);
@@ -447,7 +449,7 @@ static void RefreshInterfaces(void) // WINDOWS VERSION
}
}
NetworkInterfaces *new_interfaces = (NetworkInterfaces *) SDL_calloc(new_num_interfaces + 1, sizeof (*new_interfaces));
NetworkInterface *new_interfaces = (NetworkInterface *) SDL_calloc(new_num_interfaces + 1, sizeof (*new_interfaces));
if (!new_interfaces) {
SDL_free(addrs);
return;
@@ -455,30 +457,32 @@ static void RefreshInterfaces(void) // WINDOWS VERSION
int count = 0;
for (IP_ADAPTER_ADDRESSES *i = addrs; i != NULL; i = i->Next) {
char *friendlyname = SDL_iconv_string("UTF-8", "UTF-16LE", (const char*)(i->FriendlyName), (SDL_wcslen(i->FriendlyName) + 1) * sizeof(WCHAR));
for (IP_ADAPTER_UNICAST_ADDRESS *j = i->FirstUnicastAddress; j != NULL; j = j->Next) {
NET_Address *addr = CreateSDLNetAddrFromSockAddr(j->Address.lpSockaddr, j->Address.iSockaddrLength);
if (addr) {
new_interfaces[count].name = friendlyname ? SDL_strdup(friendlyname) : NULL;
new_interfaces[count].address = addr;
if (j->Address.lpSockaddr->sa_family == AF_INET6) {
new_interfaces[count].index = i->Ipv6IfIndex;
new_interfaces[count].index = (Uint32) i->Ipv6IfIndex;
} else if (j->Address.lpSockaddr->sa_family == AF_INET) { // if this is IPv4, calculate the broadcast address.
SOCKADDR_IN bcast;
SDL_assert(j->Address.iSockaddrLength == sizeof (bcast));
SDL_memcpy(&bcast, (const SOCKADDR_IN *) j->Address.lpSockaddr, sizeof (SOCKADDR_IN));
bcast.sin_addr.S_un.S_addr |= htonl(~((1 << (32 - j->OnLinkPrefixLength)) - 1));
new_interfaces[count].broadcast = CreateSDLNetAddrFromSockAddr((const struct sockaddr *) &bcast, sizeof (bcast));
new_interfaces[count].broadcast->type = NET_ADDRTYPE_BROADCAST;
new_interfaces[count].index = i->IfIndex;
bcast.sin_addr.S_un.S_addr |= htonl((1 << (32 - j->OnLinkPrefixLength)) - 1);
new_interfaces[count].broadcast = CreateSDLNetAddrFromSockAddr((const struct sockaddr*)&bcast, sizeof(bcast));
new_interfaces[count].index = (Uint32) i->IfIndex;
}
count++;
}
}
SDL_free(friendlyname);
}
new_num_interfaces = count; // in case we dropped one somewhere.
SDL_LockRWLockForWriting(interface_rwlock);
NetworkInterfaces *old_interfaces = interfaces;
NetworkInterface *old_interfaces = interfaces;
const int old_num_interfaces = num_interfaces;
interfaces = new_interfaces;
num_interfaces = new_num_interfaces;
@@ -488,11 +492,21 @@ static void RefreshInterfaces(void) // WINDOWS VERSION
SDL_free(addrs);
}
#elif defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_APPLE) || defined(SDL_PLATFORM_FREEBSD) || defined(SDL_PLATFORM_OPENBSD) || defined(SDL_PLATFORM_NETBSD)
#elif defined(USE_NETLINK) || defined(HAVE_GETIFADDRS)
// AF_NETLINK covers Linux (and by extension Android). PF_ROUTE covers the BSDs (and by extension Apple platforms).
// This doesn't cover all Unix platforms that ever existed, but this hits just about everything that is still being maintained seriously.
#if defined(SDL_PLATFORM_HAIKU) // haiku has its own thing for monitoring, but needs getifaddrs.
extern bool NET_HAIKU_InitInterfaceChangeNotifications(SDL_AtomicInt *changed);
extern void NET_HAIKU_QuitInterfaceChangeNotifications(void);
#elif defined(USE_NETLINK) || defined(PF_ROUTE)
#define USE_NETWORK_MONITOR 1
#else
#warning unknown network monitoring system for this platform - will not report network changes
#endif
#if defined(USE_NETWORK_MONITOR)
static SDL_Thread *interface_change_notifications_thread = NULL;
static SDL_AtomicInt interface_change_notifications_flag; // !!! FIXME
@@ -514,7 +528,7 @@ static int SDLCALL LinuxInterfaceChangeNotificationThread(void *data)
struct sockaddr_nl addr;
SDL_zero(addr);
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_pid = 0; // zero==let the kernel choose a unique number. THIS IS THE SOCKET PORT ID, NOT THE PROCESS ID!
addr.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR;
if (bind(fd, (struct sockaddr *) &addr, sizeof (addr)) == -1) {
close(fd);
@@ -552,7 +566,7 @@ static int SDLCALL LinuxInterfaceChangeNotificationThread(void *data)
// we don't currently bother parsing for specifics, we just use this as a signal to reenumerate interfaces.
//for (struct nlmsghdr *i = buf; NLMSG_OK(i, br); i = NLMSG_NEXT(i, br)) {}
SDL_Log("Network interfaces changed!");
//SDL_Log("Network interfaces changed!");
SDL_SetAtomicInt(&interfaces_have_changed, 1);
}
@@ -560,21 +574,32 @@ static int SDLCALL LinuxInterfaceChangeNotificationThread(void *data)
return 0;
}
#endif
static bool InitInterfaceChangeNotifications(void) // LINUX/BSD VERSION
{
#ifdef USE_NETWORK_MONITOR
interface_change_notifications_thread = SDL_CreateThread(LinuxInterfaceChangeNotificationThread, "SDLNetIfaceEnum", NULL);
return (interface_change_notifications_thread != NULL);
#elif defined(SDL_PLATFORM_HAIKU)
return NET_HAIKU_InitInterfaceChangeNotifications(&interfaces_have_changed);
#else
return true;
#endif
}
static void QuitInterfaceChangeNotifications(void) // LINUX/BSD VERSION
{
#ifdef USE_NETWORK_MONITOR
if (interface_change_notifications_thread) {
SDL_SetAtomicInt(&interface_change_notifications_flag, 1);
SDL_WaitThread(interface_change_notifications_thread, NULL);
SDL_SetAtomicInt(&interface_change_notifications_flag, 0);
}
interface_change_notifications_thread = NULL;
#elif defined(SDL_PLATFORM_HAIKU)
NET_HAIKU_QuitInterfaceChangeNotifications();
#endif
}
static void RefreshInterfaces(void) // LINUX/BSD VERSION
@@ -602,13 +627,13 @@ static void RefreshInterfaces(void) // LINUX/BSD VERSION
req.header.nlmsg_len = NLMSG_LENGTH(sizeof (req.msg));
req.msg.ifa_family = AF_UNSPEC;
if (send(sock, &req, req.header.nlmsg_len, 0) != req.header.nlmsg_len) {
if (send(sock, &req, req.header.nlmsg_len, 0) != (Sint32)req.header.nlmsg_len) {
close(sock);
return; // oh well.
}
int new_num_interfaces = 0;
NetworkInterfaces *new_interfaces = NULL;
NetworkInterface *new_interfaces = NULL;
Uint8 buffer[64 * 1024];
ssize_t br;
@@ -624,8 +649,8 @@ static void RefreshInterfaces(void) // LINUX/BSD VERSION
if (!ptr) {
goto failed;
}
new_interfaces = (NetworkInterfaces *) ptr;
NetworkInterfaces *iface = &new_interfaces[new_num_interfaces];
new_interfaces = (NetworkInterface *) ptr;
NetworkInterface *iface = &new_interfaces[new_num_interfaces];
SDL_zerop(iface);
const struct ifaddrmsg *msg = (const struct ifaddrmsg *) (NLMSG_DATA(header));
@@ -657,10 +682,7 @@ static void RefreshInterfaces(void) // LINUX/BSD VERSION
}
if (iface->address) {
iface->index = msg->ifa_index;
if (iface->broadcast) {
iface->broadcast->type = NET_ADDRTYPE_BROADCAST;
}
iface->index = (Uint32) msg->ifa_index;
char interface_name[IF_NAMESIZE];
if (if_indextoname((unsigned int) iface->index, interface_name) == NULL) { // miraculously, this function is in Android 21; nothing else is, though! We could have done this with netlink too, but this is the easy button.
@@ -683,7 +705,7 @@ static void RefreshInterfaces(void) // LINUX/BSD VERSION
succeeded:
close(sock);
SDL_LockRWLockForWriting(interface_rwlock);
NetworkInterfaces *old_interfaces = interfaces;
NetworkInterface *old_interfaces = interfaces;
const int old_num_interfaces = num_interfaces;
interfaces = new_interfaces;
num_interfaces = new_num_interfaces;
@@ -701,10 +723,10 @@ failed:
return; // oh well.
}
NetworkInterfaces *new_interfaces = NULL;
NetworkInterface *new_interfaces = NULL;
int new_num_interfaces = 0;
for (struct ifaddrs *i = ifaddr; i != NULL; i = i->ifa_next) {
if (i->ifa_name != NULL) {
if (i->ifa_name && i->ifa_addr && (i->ifa_flags & IFF_UP)) {
new_num_interfaces++;
}
}
@@ -718,10 +740,10 @@ failed:
goto failed;
}
NetworkInterfaces *iface = &new_interfaces[0];
for (struct ifaddrs *i = ifaddr; i != NULL; i = i->ifa_next, iface++) {
if (i->ifa_name == NULL) {
continue; // i guess.
NetworkInterface *iface = &new_interfaces[0];
for (struct ifaddrs *i = ifaddr; i != NULL; i = i->ifa_next) {
if (!i->ifa_name || !i->ifa_addr || ((i->ifa_flags & IFF_UP) == 0)) {
continue;
}
// !!! FIXME: getifaddrs doesn't return the sockaddr length, so we have to go with known protocols. :/
@@ -734,16 +756,11 @@ failed:
iface->address = CreateSDLNetAddrFromSockAddr(i->ifa_addr, sizeof (struct sockaddr_in6));
} else {
new_num_interfaces--;
iface--;
continue;
}
if (iface->address) {
if (iface->broadcast) {
iface->broadcast->type = NET_ADDRTYPE_BROADCAST;
}
iface->index = (int) if_nametoindex(i->ifa_name);
iface->index = (Uint32) if_nametoindex(i->ifa_name);
iface->name = SDL_strdup(i->ifa_name);
if (!iface->name) {
goto failed;
@@ -757,6 +774,8 @@ failed:
NET_UnrefAddress(iface->broadcast); // just in case.
goto failed;
}
iface++;
}
succeeded:
@@ -765,7 +784,7 @@ succeeded:
}
SDL_LockRWLockForWriting(interface_rwlock);
NetworkInterfaces *old_interfaces = interfaces;
NetworkInterface *old_interfaces = interfaces;
const int old_num_interfaces = num_interfaces;
interfaces = new_interfaces;
num_interfaces = new_num_interfaces;
@@ -782,18 +801,182 @@ failed:
#endif
}
#elif defined(SDL_PLATFORM_VITA)
static int interface_change_notifications_handle;
static SDL_Thread *interface_change_notifications_thread = NULL;
static SDL_AtomicInt interface_change_notifications_flag;
static int SDLCALL VitaInterfaceChangeNotificationThread(void *data)
{
(void)data;
while(SDL_GetAtomicInt(&interface_change_notifications_flag) == 0)
{
sceNetCtlCheckCallback();
SDL_Delay(100);
}
return 0;
}
static void NetworkInterfaceChangedCallback(int event_type, void* arg)
{
SDL_SetAtomicInt(&interfaces_have_changed, 1);
}
static bool InitInterfaceChangeNotifications(void)
{
if (sceNetCtlInetRegisterCallback(NetworkInterfaceChangedCallback, NULL, &interface_change_notifications_handle) != 0)
return false;
interface_change_notifications_thread = SDL_CreateThread(VitaInterfaceChangeNotificationThread, "SDLNetIfaceEnum", NULL);
return (interface_change_notifications_thread != NULL);
}
static void QuitInterfaceChangeNotifications(void)
{
sceNetCtlInetUnregisterCallback(interface_change_notifications_handle);
if (interface_change_notifications_thread) {
SDL_SetAtomicInt(&interface_change_notifications_flag, 1);
SDL_WaitThread(interface_change_notifications_thread, NULL);
SDL_SetAtomicInt(&interface_change_notifications_flag, 0);
}
interface_change_notifications_thread = NULL;
}
static NET_Address *CreateSDLNetAddrFromIp(char* hostbuf)
{
NET_Address *addr = (NET_Address *) SDL_calloc(1, sizeof (NET_Address));
if (!addr) {
return NULL;
}
SDL_SetAtomicInt(&addr->status, (int) NET_SUCCESS);
struct addrinfo hints;
SDL_zero(hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_NUMERICHOST;
int gairc = getaddrinfo(hostbuf, NULL, &hints, &addr->ainfo);
if (gairc != 0) {
SDL_free(addr);
SetGetAddrInfoError("Failed to determine address", gairc);
return NULL;
}
addr->human_readable = SDL_strdup(hostbuf);
if (!addr->human_readable) {
freeaddrinfo(addr->ainfo);
SDL_free(addr);
return NULL;
}
return NET_RefAddress(addr);
}
static void RefreshInterfaces(void)
{
NetworkInterface *new_interfaces = NULL;
int new_num_interfaces = 0;
SceNetCtlInfo info;
int state;
if (sceNetCtlInetGetState(&state) < 0 ) {
goto failed;
}
if (state != SCE_NETCTL_STATE_CONNECTED) {
goto succeeded;
}
if (state == SCE_NETCTL_STATE_CONNECTED) {
new_num_interfaces = 1;
new_interfaces = SDL_calloc(new_num_interfaces, sizeof (*new_interfaces));
if (!new_interfaces) {
goto failed;
}
NetworkInterface *iface = &new_interfaces[0];
if (sceNetCtlInetGetInfo(SCE_NETCTL_INFO_GET_IP_ADDRESS, &info) < 0) {
goto failed;
}
iface->address = CreateSDLNetAddrFromIp(info.ip_address);
if (iface->address) {
iface->index = 0;
if (sceNetCtlInetGetInfo(SCE_NETCTL_INFO_GET_DEVICE, &info) < 0) {
goto failed;
}
switch (info.device) {
case 0:
iface->name = SDL_strdup("wlan0");
break;
case 1:
iface->name = SDL_strdup("eth0");
break;
case 2:
iface->name = SDL_strdup("phone0");
break;
}
if (!iface->name) {
goto failed;
}
char *ptr = SDL_strchr(iface->address->human_readable, '%'); // chop off interface name.
if (ptr) {
*ptr = '\0';
}
} else {
NET_UnrefAddress(iface->broadcast); // just in case.
goto failed;
}
}
succeeded:
SDL_LockRWLockForWriting(interface_rwlock);
NetworkInterface *old_interfaces = interfaces;
const int old_num_interfaces = num_interfaces;
interfaces = new_interfaces;
num_interfaces = new_num_interfaces;
SDL_UnlockRWLock(interface_rwlock);
FreeNetworkInterfaces(old_interfaces, old_num_interfaces);
return;
failed:
FreeNetworkInterfaces(new_interfaces, new_num_interfaces);
}
#else
#error implement me for your platform.
#warning implement me for your platform.
static bool InitInterfaceChangeNotifications(void)
{
return true;
}
static void QuitInterfaceChangeNotifications(void)
{
}
static void RefreshInterfaces(void)
{
}
#endif
static bool InterfacesReady(void)
{
if (SDL_ShouldInit(&interface_init)) {
if (!interface_rwlock) {
interface_rwlock = SDL_CreateRWLock();
}
if (!interface_rwlock || !InitInterfaceChangeNotifications()) {
if (!InitInterfaceChangeNotifications()) {
SDL_SetInitialized(&interface_init, false);
return false;
}
@@ -806,14 +989,16 @@ static bool InterfacesReady(void)
RefreshInterfaces();
#if 0
SDL_Log("NETWORK INTERFACE REFRESH");
SDL_LockRWLockForReading(interface_rwlock);
for (int i = 0; i < num_interfaces; i++) {
const NetworkInterfaces *iface = &interfaces[i];
SDL_Log("Interface %d ('%s')", iface->index, iface->name);
const NetworkInterface *iface = &interfaces[i];
SDL_Log("Interface %u ('%s')", (unsigned int) iface->index, iface->name);
SDL_Log(" - address %s", iface->address->human_readable);
if (iface->broadcast) {
SDL_Log(" - broadcast %s", iface->broadcast->human_readable);
}
}
SDL_UnlockRWLock(interface_rwlock);
#endif
}
@@ -849,7 +1034,6 @@ static NET_Status ResolveAddress(NET_Address *addr)
addr->human_readable = SDL_strdup(buf);
addr->ainfo = ainfo;
addr->type = NET_ADDRTYPE_UNICAST;
return NET_SUCCESS; // success (zero means "still in progress").
}
@@ -976,8 +1160,6 @@ static NET_Address *CreateSDLNetAddrFromSockAddr(const struct sockaddr *saddr, S
return NULL;
}
addr->type = NET_ADDRTYPE_UNICAST; // until told otherwise.
addr->human_readable = SDL_strdup(hostbuf);
if (!addr->human_readable) {
freeaddrinfo(addr->ainfo);
@@ -1003,6 +1185,9 @@ bool NET_Init(void)
if (WSAStartup(MAKEWORD(1, 1), &data) != 0) {
return SetSocketErrorBool("WSAStartup() failed", LastSocketError());
}
#elif defined(SDL_PLATFORM_VITA)
// trigger vita network stack startup
gethostname(NULL,0);
#else
signal(SIGPIPE, SIG_IGN);
#endif
@@ -1030,6 +1215,18 @@ bool NET_Init(void)
}
}
struct sockaddr_in6 sa_in6;
SDL_zero(sa_in6);
if (inet_pton(AF_INET6, "ff02::1", &sa_in6.sin6_addr) == 1) {
sa_in6.sin6_family = AF_INET6;
ipv6_broadcast_addr = CreateSDLNetAddrFromSockAddr((const struct sockaddr *) &sa_in6, sizeof (sa_in6));
}
interface_rwlock = SDL_CreateRWLock();
if (!interface_rwlock) {
goto failed;
}
return true; // good to go.
failed:
@@ -1093,20 +1290,23 @@ void NET_Quit(void)
if (interfaces) {
FreeNetworkInterfaces(interfaces, num_interfaces);
}
if (interface_rwlock) {
SDL_DestroyRWLock(interface_rwlock);
}
interfaces = NULL;
num_interfaces = 0;
interface_rwlock = NULL;
SDL_SetInitialized(&interface_init, false);
}
// asserts to catch that these shouldn't have ever been set if we never initialized interface_init...
SDL_assert(!interfaces);
SDL_assert(!interface_rwlock);
SDL_assert(num_interfaces == 0);
if (interface_rwlock) {
SDL_DestroyRWLock(interface_rwlock);
interface_rwlock = NULL;
}
NET_UnrefAddress(ipv6_broadcast_addr);
ipv6_broadcast_addr = NULL;
#ifdef SDL_PLATFORM_WINDOWS
WSACleanup();
#endif
@@ -1238,6 +1438,34 @@ const char *NET_GetAddressString(NET_Address *addr)
return retval;
}
const void * NET_GetAddressBytes(NET_Address *addr, int *num_bytes)
{
if (!num_bytes) {
SDL_InvalidParamError("num_bytes");
return NULL;
}
*num_bytes = 0;
if (!addr) {
SDL_InvalidParamError("address");
return NULL;
}
const void *retval = NULL;
const NET_Status rc = NET_GetAddressStatus(addr);
if (rc == NET_SUCCESS) {
const struct addrinfo *ainfo = addr->ainfo;
SDL_assert(ainfo != NULL);
retval = (const void *) ainfo->ai_addr;
*num_bytes = (int) ainfo->ai_addrlen;
} else if (rc != NET_FAILURE) { // if NET_FAILURE, it'll set the error message.
SDL_SetError("Address not yet resolved");
}
return retval;
}
int NET_CompareAddresses(const NET_Address *sdlneta, const NET_Address *sdlnetb)
{
if (sdlneta == sdlnetb) { // same pointer?
@@ -1289,7 +1517,6 @@ void NET_SimulateAddressResolutionLoss(int percent_loss)
SDL_SetAtomicInt(&resolver_percent_loss, SDL_clamp(percent_loss, 0, 100));
}
NET_Address **NET_GetLocalAddresses(int *num_addresses)
{
int dummy_addresses;
@@ -1372,7 +1599,7 @@ struct NET_StreamSocket
Uint64 simulated_failure_until;
};
NET_StreamSocket *NET_CreateClient(NET_Address *addr, Uint16 port)
NET_StreamSocket *NET_CreateClient(NET_Address *addr, Uint16 port, SDL_PropertiesID props)
{
if (addr == NULL) {
SDL_InvalidParamError("address");
@@ -1471,7 +1698,7 @@ struct NET_Server
Socket handle_pool[4];
};
NET_Server *NET_CreateServer(NET_Address *addr, Uint16 port)
NET_Server *NET_CreateServer(NET_Address *addr, Uint16 port, SDL_PropertiesID props)
{
if (addr && (((NET_Status) SDL_GetAtomicInt(&addr->status)) != NET_SUCCESS)) {
SDL_SetError("Address is not resolved"); // strictly speaking, this should be a local interface, but a resolved address can fail later.
@@ -1515,6 +1742,8 @@ NET_Server *NET_CreateServer(NET_Address *addr, Uint16 port)
}
}
const int reuseaddr = SDL_GetBooleanProperty(props, NET_PROP_SERVER_REUSEADDR_BOOLEAN, true) ? 1 : 0;
// Make sockets for all desired interfaces; if addr!=NULL, this is one socket on one interface,
// but if addr==NULL, it might be multiple sockets for IPv4, IPv6, etc, bound to their INADDR_ANY equivalent.
struct addrinfo *ainfo = addrwithport;
@@ -1538,7 +1767,7 @@ NET_Server *NET_CreateServer(NET_Address *addr, Uint16 port)
setsockopt(handle, IPPROTO_IPV6, IPV6_V6ONLY, (const char *) &one, sizeof (one)); // if this fails, oh well.
}
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (const char *) &one, sizeof (one));
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (const char *) &reuseaddr, sizeof (reuseaddr));
int rc = bind(handle, ainfo->ai_addr, (SockLen) ainfo->ai_addrlen);
if (rc == SOCKET_ERROR) {
@@ -1855,6 +2084,7 @@ typedef struct NET_DatagramSocketHandle
Socket handle;
int family;
int protocol;
NET_Address *broadcast;
} NET_DatagramSocketHandle;
struct NET_DatagramSocket
@@ -1872,10 +2102,57 @@ struct NET_DatagramSocket
NET_Datagram **pending_output;
int pending_output_len;
int pending_output_allocation;
bool allow_broadcast;
};
static NET_Address *FindBroadcastAddress(struct addrinfo *ainfo, Uint32 *interface_index)
{
NET_Address *retval = NULL;
NET_DatagramSocket *NET_CreateDatagramSocket(NET_Address *addr, Uint16 port)
NET_Address *iface = CreateSDLNetAddrFromSockAddr(ainfo->ai_addr, (SockLen) ainfo->ai_addrlen);
if (!iface) {
return NULL;
}
NetworkInterface *ni = NULL;
if (!InterfacesReady()) {
NET_UnrefAddress(iface);
return NULL;
}
SDL_LockRWLockForReading(interface_rwlock);
for (int i = 0; i < num_interfaces; i++) {
if (NET_CompareAddresses(iface, interfaces[i].address) == 0) {
ni = &interfaces[i];
break;
}
}
if (!ni) {
SDL_SetError("Not a network interface address");
} else {
SDL_assert(ni->address != NULL);
*interface_index = ni->index;
if (ni->broadcast != NULL) {
retval = NET_RefAddress(ni->broadcast); // we calculated the broadcast address when discovering this interface. It's probably IPv4. We're good to go.
} else if (ainfo->ai_family == AF_INET6) { // we fake this for IPv6 on the all-nodes link-local multicast group.
retval = NET_RefAddress(ipv6_broadcast_addr);
} else {
SDL_SetError("Can't determine broadcast address for this interface");
}
}
SDL_UnlockRWLock(interface_rwlock);
if (!retval && (ainfo->ai_family == AF_INET6)) { // we fake this for IPv6 on the all-nodes link-local multicast group.
retval = NET_RefAddress(ipv6_broadcast_addr);
}
return retval;
}
NET_DatagramSocket *NET_CreateDatagramSocket(NET_Address *addr, Uint16 port, SDL_PropertiesID props)
{
if (addr && (((NET_Status) SDL_GetAtomicInt(&addr->status)) != NET_SUCCESS)) {
SDL_SetError("Address is not resolved"); // strictly speaking, this should be a local interface, but a resolved address can fail later.
@@ -1897,6 +2174,11 @@ NET_DatagramSocket *NET_CreateDatagramSocket(NET_Address *addr, Uint16 port)
sock->addr = addr;
sock->port = port;
const int reuseaddr = SDL_GetBooleanProperty(props, NET_PROP_DATAGRAM_SOCKET_REUSEADDR_BOOLEAN, true) ? 1 : 0;
sock->allow_broadcast = SDL_GetBooleanProperty(props, NET_PROP_DATAGRAM_SOCKET_ALLOW_BROADCAST_BOOLEAN, false);
const int bcast = sock->allow_broadcast ? 1 : 0;
int num_handles = 0;
NET_DatagramSocketHandle *allocated_handles = NULL;
if (addr != NULL) {
@@ -1942,24 +2224,26 @@ NET_DatagramSocket *NET_CreateDatagramSocket(NET_Address *addr, Uint16 port)
goto failed;
}
sock->handles[sock->num_handles].handle = handle;
sock->handles[sock->num_handles].family = ainfo->ai_family;
sock->handles[sock->num_handles].protocol = ainfo->ai_protocol;
sock->num_handles++;
NET_DatagramSocketHandle *socket_handle = &sock->handles[sock->num_handles++];
SDL_zerop(socket_handle);
socket_handle->handle = handle;
socket_handle->family = ainfo->ai_family;
socket_handle->protocol = ainfo->ai_protocol;
if (MakeSocketNonblocking(handle) < 0) {
SDL_SetError("Failed to make new socket non-blocking");
goto failed;
}
const int one = 1;
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (const char *) &reuseaddr, sizeof (reuseaddr));
setsockopt(handle, SOL_SOCKET, SO_BROADCAST, (const char *) &bcast, sizeof (bcast));
if (ainfo->ai_family == AF_INET6) {
const int one = 1;
setsockopt(handle, IPPROTO_IPV6, IPV6_V6ONLY, (const char *) &one, sizeof (one)); // if this fails, oh well.
}
setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, (const char *) &one, sizeof (one));
setsockopt(handle, SOL_SOCKET, SO_BROADCAST, (const char *) &one, sizeof (one));
const int rc = bind(handle, ainfo->ai_addr, (SockLen) ainfo->ai_addrlen);
if (rc == SOCKET_ERROR) {
const int err = LastSocketError();
@@ -1967,6 +2251,39 @@ NET_DatagramSocket *NET_CreateDatagramSocket(NET_Address *addr, Uint16 port)
SetSocketError("Failed to bind socket", err);
goto failed;
}
if (sock->allow_broadcast) {
Uint32 interface_index = 0; // this will stay zero for INADDR6_ANY, to pick a default interface.
socket_handle->broadcast = FindBroadcastAddress(ainfo, &interface_index);
// if failed but addr==NULL, this is IPv4 and we can't find an interface for INADDR_ANY. We'll broadcast to all interfaces when sending (because 255.255.255.255 doesn't work on Windows).
// alternately, we're IPv6 and ipv6_broadcast_addr failed to init for some reason. Just panic and fail here if that happens, I guess.
if (!socket_handle->broadcast && (addr || (ainfo->ai_family == AF_INET6))) {
SDL_SetError("Failed to determine broadcast address for this interface");
goto failed;
} else if (ainfo->ai_family == AF_INET6) { // fake broadcast support via multicasting to all-nodes link-local group, ff02::1.
#ifdef SDL_PLATFORM_VITA
SDL_SetError("No IPv6 support");
goto failed;
#else
SDL_assert(socket_handle->broadcast == ipv6_broadcast_addr);
const struct addrinfo *bai = socket_handle->broadcast->ainfo;
SDL_assert(bai != NULL);
SDL_assert(bai->ai_addr != NULL);
SDL_assert(bai->ai_addr->sa_family == AF_INET6);
struct ipv6_mreq mreq6;
SDL_zero(mreq6);
SDL_copyp(&mreq6.ipv6mr_multiaddr, &((struct sockaddr_in6 *) bai->ai_addr)->sin6_addr);
mreq6.ipv6mr_interface = interface_index;
//SDL_Log("Add membership for %s, index=%d", socket_handle->broadcast->human_readable, (int) interface_index);
if (setsockopt(handle, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *) &mreq6, (SockLen) sizeof (mreq6)) < 0) {
SDL_SetError("Failed to join all-nodes link-local multicast group for broadcasting");
goto failed;
}
const int ifidx = (int) interface_index;
setsockopt(handle, IPPROTO_IPV6, IPV6_MULTICAST_IF, (const char *) &ifidx, (SockLen) sizeof (ifidx)); // multicast sends go through the same interface.
#endif
}
}
}
freeaddrinfo(addrwithport);
@@ -1977,6 +2294,7 @@ NET_DatagramSocket *NET_CreateDatagramSocket(NET_Address *addr, Uint16 port)
failed:
for (int i = 0; i < sock->num_handles; i++) {
CloseSocketHandle(sock->handles[i].handle);
NET_UnrefAddress(sock->handles[i].broadcast);
}
freeaddrinfo(addrwithport);
SDL_free(allocated_handles);
@@ -1986,29 +2304,94 @@ failed:
static NET_Status SendOneDatagram(NET_DatagramSocket *sock, NET_Address *addr, Uint16 port, const void *buf, int buflen)
{
struct addrinfo *addrwithport = MakeAddrInfoWithPort(addr, SOCK_DGRAM, port);
if (!addrwithport) {
if (addr) { // unicast to a specific address.
struct addrinfo *addrwithport = MakeAddrInfoWithPort(addr, SOCK_DGRAM, port);
if (!addrwithport) {
return NET_FAILURE;
}
const int family = addrwithport->ai_family;
const int protocol = addrwithport->ai_protocol;
for (int i = 0; i < sock->num_handles; i++) {
const NET_DatagramSocketHandle *handle = &sock->handles[i];
if ((handle->family == family) && (handle->protocol == protocol)) { // !!! FIXME: strictly speaking, this _probably_ just needs to check `family`, right?
const int rc = (int) sendto(handle->handle, buf, (size_t) buflen, 0, addrwithport->ai_addr, (SockLen) addrwithport->ai_addrlen);
const int err = (rc == SOCKET_ERROR) ? LastSocketError() : 0;
freeaddrinfo(addrwithport);
if (err != 0) {
return WouldBlock(err) ? NET_WAITING : SetSocketError("Failed to send from socket", err);
}
SDL_assert(rc == buflen);
return NET_SUCCESS; // !!! FIXME: should we sent to _all_ interfaces in this family?
}
}
SDL_SetError("Unsupported network family in destination address");
return NET_FAILURE;
}
const int family = addrwithport->ai_family;
const int protocol = addrwithport->ai_protocol;
// broadcast (or fake with multicast) this packet.
SDL_assert(sock->allow_broadcast); // we should have checked this in NET_SendDatagram!
bool all_wouldblock = false;
NET_Status retval = NET_FAILURE;
for (int i = 0; i < sock->num_handles; i++) {
const NET_DatagramSocketHandle *handle = &sock->handles[i];
if ((handle->family == family) && (handle->protocol == protocol)) { // !!! FIXME: strictly speaking, this _probably_ just needs to check `family`, right?
const int rc = sendto(handle->handle, buf, (size_t) buflen, 0, addrwithport->ai_addr, (SockLen) addrwithport->ai_addrlen);
if (handle->broadcast) {
struct addrinfo *addrwithport = MakeAddrInfoWithPort(handle->broadcast, SOCK_DGRAM, port);
if (!addrwithport) {
continue; // oh well, lost UDP packet, I guess.
}
//SDL_Log("Broadcasting on %s ...", handle->broadcast->human_readable);
const int rc = (int) sendto(handle->handle, buf, (size_t) buflen, 0, addrwithport->ai_addr, (SockLen) addrwithport->ai_addrlen);
const int err = (rc == SOCKET_ERROR) ? LastSocketError() : 0;
freeaddrinfo(addrwithport);
if (err != 0) {
return WouldBlock(err) ? NET_WAITING : SetSocketError("Failed to send from socket", err);
if (!err) {
retval = NET_SUCCESS; // it went to at least one interface's broadcast address, we'll call it success.
} else {
if (!WouldBlock(err)) {
all_wouldblock = false;
SetSocketError("Failed to send from socket", err); // so there's a clear error message, but keep going, maybe something else works out.
}
}
SDL_assert(rc == buflen);
return NET_SUCCESS;
} else if (!addr && InterfacesReady()) { // iterate all interfaces for this broadcast.
SDL_LockRWLockForReading(interface_rwlock);
for (int i = 0; i < num_interfaces; i++) {
const NET_Address *bc = interfaces[i].broadcast;
if (!bc) { continue; }
const struct addrinfo *ainfo = bc->ainfo;
SDL_assert(ainfo != NULL);
if (ainfo->ai_family != handle->family) { continue; }
struct addrinfo *addrwithport = MakeAddrInfoWithPort(bc, SOCK_DGRAM, port);
if (!addrwithport) {
continue; // oh well, lost UDP packet, I guess.
}
//SDL_Log("Broadcasting on %s ...", bc->human_readable);
const int rc = (int) sendto(handle->handle, buf, (size_t) buflen, 0, addrwithport->ai_addr, (SockLen) addrwithport->ai_addrlen);
const int err = (rc == SOCKET_ERROR) ? LastSocketError() : 0;
freeaddrinfo(addrwithport);
if (!err) {
retval = NET_SUCCESS; // it went to at least one interface's broadcast address, we'll call it success.
} else {
if (!WouldBlock(err)) {
all_wouldblock = false;
SetSocketError("Failed to send from socket", err); // so there's a clear error message, but keep going, maybe something else works out.
}
}
}
SDL_UnlockRWLock(interface_rwlock);
}
}
SDL_SetError("Unsupported network family in destination address");
return NET_FAILURE;
if ((retval == NET_FAILURE) && all_wouldblock) {
retval = NET_WAITING;
}
return retval;
}
// see if any pending data can finally be sent, etc
@@ -2043,8 +2426,8 @@ bool NET_SendDatagram(NET_DatagramSocket *sock, NET_Address *addr, Uint16 port,
{
if (!PumpDatagramSocket(sock)) { // try to flush any queued data to the socket now, before we handle more.
return false;
} else if (addr == NULL) {
return SDL_InvalidParamError("address");
} else if (!addr && !sock->allow_broadcast) {
return SDL_SetError("Datagram socket was not created with broadcast support");
} else if (buf == NULL) {
return SDL_InvalidParamError("buf");
} else if (buflen < 0) {
@@ -2119,7 +2502,7 @@ bool NET_ReceiveDatagram(NET_DatagramSocket *sock, NET_Datagram **dgram)
AddressStorage from;
SockLen fromlen = sizeof (from);
// WinSock's recvfrom wants a `char *` buffer instead of `void *`. The cast here is harmless on BSD Sockets.
const int br = recvfrom(sock->handles[i].handle, (char *) sock->recv_buffer, sizeof (sock->recv_buffer), 0, (struct sockaddr *) &from, &fromlen);
const int br = (int) recvfrom(sock->handles[i].handle, (char *) sock->recv_buffer, sizeof (sock->recv_buffer), 0, (struct sockaddr *) &from, &fromlen);
if (br == SOCKET_ERROR) {
const int err = LastSocketError();
if (WouldBlock(err)) {
@@ -2224,6 +2607,7 @@ void NET_DestroyDatagramSocket(NET_DatagramSocket *sock)
for (int i = 0; i < sock->num_handles; i++) {
CloseSocketHandle(sock->handles[i].handle); // !!! FIXME: what does this do with non-blocking sockets? Release the descriptor but the kernel continues sending queued buffers behind the scenes?
NET_UnrefAddress(sock->handles[i].broadcast);
}
for (int i = 0; i < ((int) SDL_arraysize(sock->latest_recv_addrs)); i++) {
NET_UnrefAddress(sock->latest_recv_addrs[i]);
+1
View File
@@ -32,4 +32,5 @@ _NET_WaitUntilInputAvailable
_NET_WaitUntilResolved
_NET_WaitUntilStreamSocketDrained
_NET_WriteToStreamSocket
_NET_GetAddressBytes
# extra symbols go here (don't modify this line)
+1
View File
@@ -33,6 +33,7 @@ SDL3_net_0.0.0 {
NET_WaitUntilResolved;
NET_WaitUntilStreamSocketDrained;
NET_WriteToStreamSocket;
NET_GetAddressBytes;
# extra symbols go here (don't modify this line)
local: *;
};
+82
View File
@@ -0,0 +1,82 @@
/*
SDL_net: A simple networking library for use with SDL
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.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.
*/
// we use a little bit of C++ for Haiku support, since the system frameworks
// require it. Specifically, we need to get network interface change
// notifications through BNetworkRoster.
#include <AppKit.h>
#include <NetworkNotifications.h>
#include <SDL3/SDL.h>
extern "C" {
extern bool NET_HAIKU_InitInterfaceChangeNotifications(SDL_AtomicInt *changed);
extern void NET_HAIKU_QuitInterfaceChangeNotifications(void);
}
class NET_InterfaceLooper : public BLooper
{
public:
NET_InterfaceLooper(SDL_AtomicInt *_changed);
virtual void MessageReceived(BMessage *message);
virtual ~NET_InterfaceLooper() {}
private:
SDL_AtomicInt *changed;
};
NET_InterfaceLooper::NET_InterfaceLooper(SDL_AtomicInt *_changed)
: BLooper("NET_InterfaceLooper")
, changed(_changed)
{
}
void NET_InterfaceLooper::MessageReceived(BMessage *message)
{
//SDL_Log("NET_InterfaceLooper::MessageReceived %d", (int) message->what);
SDL_SetAtomicInt(changed, 1);
BLooper::MessageReceived(message);
}
static NET_InterfaceLooper *looper = NULL;
bool NET_HAIKU_InitInterfaceChangeNotifications(SDL_AtomicInt *changed)
{
SDL_assert(!looper);
looper = new NET_InterfaceLooper(changed);
looper->Run();
return start_watching_network(
B_NETWORK_INTERFACE_ADDED | B_NETWORK_INTERFACE_REMOVED |
B_NETWORK_INTERFACE_CHANGED | B_NETWORK_DEVICE_LINK_CHANGED, looper
) == B_OK;
}
void NET_HAIKU_QuitInterfaceChangeNotifications(void)
{
if (looper) {
looper->PostMessage(B_QUIT_REQUESTED);
looper = NULL;
}
}
+59
View File
@@ -0,0 +1,59 @@
/*
SDL_net: A simple networking library for use with SDL
Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.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.
*/
#include "SDL3_net/SDL_net.h"
// don't do anything. Sorry!
int NET_Version(void) { return SDL_NET_VERSION; }
bool NET_Init(void) { return SDL_Unsupported(); }
void NET_Quit(void) {}
NET_Address * NET_ResolveHostname(const char *host) { SDL_Unsupported(); return NULL; }
NET_Status NET_WaitUntilResolved(NET_Address *address, Sint32 timeout) { SDL_Unsupported(); return NET_FAILURE; }
NET_Status NET_GetAddressStatus(NET_Address *address) { SDL_Unsupported(); return NET_FAILURE; }
const char * NET_GetAddressString(NET_Address *address) { SDL_Unsupported(); return NULL; }
const void * NET_GetAddressBytes(NET_Address *address, int *num_bytes) { if (num_bytes) { *num_bytes = 0; } SDL_Unsupported(); return NULL; }
NET_Address *NET_RefAddress(NET_Address *address) { SDL_Unsupported(); return NULL; }
void NET_UnrefAddress(NET_Address *address) {}
void NET_SimulateAddressResolutionLoss(int percent_loss) {}
int NET_CompareAddresses(const NET_Address *a, const NET_Address *b) { return 0; }
NET_Address **NET_GetLocalAddresses(int *num_addresses) { SDL_Unsupported(); return NULL; }
void NET_FreeLocalAddresses(NET_Address **addresses) {}
NET_StreamSocket * NET_CreateClient(NET_Address *address, Uint16 port, SDL_PropertiesID props) { SDL_Unsupported(); return NULL; }
NET_Status NET_WaitUntilConnected(NET_StreamSocket *sock, Sint32 timeout) { SDL_Unsupported(); return NET_FAILURE; }
NET_Server * NET_CreateServer(NET_Address *addr, Uint16 port, SDL_PropertiesID props) { SDL_Unsupported(); return NULL; }
bool NET_AcceptClient(NET_Server *server, NET_StreamSocket **client_stream) { SDL_Unsupported(); return false; }
void NET_DestroyServer(NET_Server *server) {}
NET_Address * NET_GetStreamSocketAddress(NET_StreamSocket *sock) { SDL_Unsupported(); return NULL; }
NET_Status NET_GetConnectionStatus(NET_StreamSocket *sock) { SDL_Unsupported(); return NET_FAILURE; }
bool NET_WriteToStreamSocket(NET_StreamSocket *sock, const void *buf, int buflen) { SDL_Unsupported(); return false; }
int NET_GetStreamSocketPendingWrites(NET_StreamSocket *sock) { SDL_Unsupported(); return -1; }
int NET_WaitUntilStreamSocketDrained(NET_StreamSocket *sock, Sint32 timeout) { SDL_Unsupported(); return -1; }
int NET_ReadFromStreamSocket(NET_StreamSocket *sock, void *buf, int buflen) { SDL_Unsupported(); return -1; }
void NET_SimulateStreamPacketLoss(NET_StreamSocket *sock, int percent_loss) {}
void NET_DestroyStreamSocket(NET_StreamSocket *sock) {}
NET_DatagramSocket * NET_CreateDatagramSocket(NET_Address *addr, Uint16 port, SDL_PropertiesID props) { SDL_Unsupported(); return NULL; }
bool NET_SendDatagram(NET_DatagramSocket *sock, NET_Address *address, Uint16 port, const void *buf, int buflen) { SDL_Unsupported(); return false; }
bool NET_ReceiveDatagram(NET_DatagramSocket *sock, NET_Datagram **dgram) { SDL_Unsupported(); return false; }
void NET_DestroyDatagram(NET_Datagram *dgram) {}
void NET_SimulateDatagramPacketLoss(NET_DatagramSocket *sock, int percent_loss) {}
void NET_DestroyDatagramSocket(NET_DatagramSocket *sock) {}
int NET_WaitUntilInputAvailable(void **vsockets, int numsockets, Sint32 timeout) { SDL_Unsupported(); return -1; }
+4 -4
View File
@@ -9,8 +9,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,0,0,0
PRODUCTVERSION 3,0,0,0
FILEVERSION 3,3,0,0
PRODUCTVERSION 3,3,0,0
FILEFLAGSMASK 0x3fL
FILEFLAGS 0x0L
FILEOS 0x40004L
@@ -23,12 +23,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "\0"
VALUE "FileDescription", "SDL_net\0"
VALUE "FileVersion", "3, 0, 0, 0\0"
VALUE "FileVersion", "3, 3, 0, 0\0"
VALUE "InternalName", "SDL_net\0"
VALUE "LegalCopyright", "Copyright (C) 2026 Sam Lantinga\0"
VALUE "OriginalFilename", "SDL3_net.dll\0"
VALUE "ProductName", "Simple DirectMedia Layer\0"
VALUE "ProductVersion", "3, 0, 0, 0\0"
VALUE "ProductVersion", "3, 3, 0, 0\0"
END
END
BLOCK "VarFileInfo"
+23 -29
View File
@@ -1,5 +1,5 @@
/* 7zArcIn.c -- 7z Input functions
2023-09-07 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -289,9 +289,9 @@ static SRes WaitId(CSzData *sd, UInt32 id)
}
}
static SRes RememberBitVector(CSzData *sd, UInt32 numItems, const Byte **v)
static SRes RememberBitVector(CSzData *sd, size_t numItems, const Byte **v)
{
const UInt32 numBytes = (numItems + 7) >> 3;
const size_t numBytes = (numItems + 7) >> 3;
if (numBytes > sd->Size)
return SZ_ERROR_ARCHIVE;
*v = sd->Data;
@@ -317,11 +317,11 @@ static UInt32 CountDefinedBits(const Byte *bits, UInt32 numItems)
return sum;
}
static Z7_NO_INLINE SRes ReadBitVector(CSzData *sd, UInt32 numItems, Byte **v, ISzAllocPtr alloc)
static Z7_NO_INLINE SRes ReadBitVector(CSzData *sd, size_t numItems, Byte **v, ISzAllocPtr alloc)
{
Byte allAreDefined;
Byte *v2;
const UInt32 numBytes = (numItems + 7) >> 3;
const size_t numBytes = (numItems + 7) >> 3;
*v = NULL;
SZ_READ_BYTE(allAreDefined)
if (numBytes == 0)
@@ -345,9 +345,9 @@ static Z7_NO_INLINE SRes ReadBitVector(CSzData *sd, UInt32 numItems, Byte **v, I
return SZ_OK;
}
static Z7_NO_INLINE SRes ReadUi32s(CSzData *sd2, UInt32 numItems, CSzBitUi32s *crcs, ISzAllocPtr alloc)
static Z7_NO_INLINE SRes ReadUi32s(CSzData *sd2, size_t numItems, CSzBitUi32s *crcs, ISzAllocPtr alloc)
{
UInt32 i;
size_t i;
CSzData sd;
UInt32 *vals;
const Byte *defs;
@@ -366,7 +366,7 @@ static Z7_NO_INLINE SRes ReadUi32s(CSzData *sd2, UInt32 numItems, CSzBitUi32s *c
return SZ_OK;
}
static SRes ReadBitUi32s(CSzData *sd, UInt32 numItems, CSzBitUi32s *crcs, ISzAllocPtr alloc)
static SRes ReadBitUi32s(CSzData *sd, size_t numItems, CSzBitUi32s *crcs, ISzAllocPtr alloc)
{
SzBitUi32s_Free(crcs, alloc);
RINOK(ReadBitVector(sd, numItems, &crcs->Defs, alloc))
@@ -1027,42 +1027,39 @@ static SRes SzReadAndDecodePackedStreams(
return SZ_OK;
}
// (size & 1) == 0
// (data) is aligned for 2-bytes
static SRes SzReadFileNames(const Byte *data, size_t size, UInt32 numFiles, size_t *offsets)
{
size_t pos = 0;
const Byte *p, *lim;
*offsets++ = 0;
if (numFiles == 0)
return (size == 0) ? SZ_OK : SZ_ERROR_ARCHIVE;
if (size < 2)
return SZ_ERROR_ARCHIVE;
if (data[size - 2] != 0 || data[size - 1] != 0)
lim = data + size;
if (*(const UInt16 *)(const void *)(lim - 2))
return SZ_ERROR_ARCHIVE;
p = data;
do
{
const Byte *p;
if (pos == size)
if (p >= lim)
return SZ_ERROR_ARCHIVE;
for (p = data + pos;
#ifdef _WIN32
*(const UInt16 *)(const void *)p != 0
#else
p[0] != 0 || p[1] != 0
#endif
; p += 2);
pos = (size_t)(p - data) + 2;
*offsets++ = (pos >> 1);
for (; *(const UInt16 *)(const void *)p; p += 2);
p += 2;
*offsets++ = (size_t)(p - data) >> 1;
}
while (--numFiles);
return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE;
return (p == lim) ? SZ_OK : SZ_ERROR_ARCHIVE;
}
static Z7_NO_INLINE SRes ReadTime(CSzBitUi64s *p, UInt32 num,
static Z7_NO_INLINE SRes ReadTime(CSzBitUi64s *p, size_t num,
CSzData *sd2,
const CBuf *tempBufs, UInt32 numTempBufs,
ISzAllocPtr alloc)
{
CSzData sd;
UInt32 i;
size_t i;
CNtfsFileTime *vals;
Byte *defs;
Byte external;
@@ -1215,6 +1212,7 @@ static SRes SzReadHeader2(
{
namesSize = (size_t)size - 1;
namesData = sd->Data;
SKIP_DATA(sd, namesSize)
}
else
{
@@ -1226,15 +1224,11 @@ static SRes SzReadHeader2(
namesSize = (tempBufs)[index].size;
}
if ((namesSize & 1) != 0)
if (namesSize & 1)
return SZ_ERROR_ARCHIVE;
MY_ALLOC(size_t, p->FileNameOffsets, numFiles + 1, allocMain)
MY_ALLOC_ZE_AND_CPY(p->FileNames, namesSize, namesData, allocMain)
RINOK(SzReadFileNames(p->FileNames, namesSize, numFiles, p->FileNameOffsets))
if (external == 0)
{
SKIP_DATA(sd, namesSize)
}
break;
}
case k7zIdEmptyStream:
+1 -2
View File
@@ -1,12 +1,11 @@
/* 7zFile.h -- File IO
2023-03-05 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_FILE_H
#define ZIP7_INC_FILE_H
#ifdef _WIN32
#define USE_WINDOWS_FILE
// #include <windows.h>
#endif
#ifdef USE_WINDOWS_FILE
+4 -2
View File
@@ -1,5 +1,5 @@
/* 7zTypes.h -- Basic types
2024-01-24 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_7Z_TYPES_H
#define ZIP7_7Z_TYPES_H
@@ -46,8 +46,9 @@ typedef int SRes;
#ifdef _MSC_VER
#define MY_ALIGN_IN_STRUCT(n) __declspec(align(n))
#if _MSC_VER > 1200
#define MY_ALIGN(n) __declspec(align(n))
#define MY_ALIGN(n) MY_ALIGN_IN_STRUCT(n)
#else
#define MY_ALIGN(n)
#endif
@@ -58,6 +59,7 @@ typedef int SRes;
#define MY_ALIGN(n) alignas(n)
*/
#define MY_ALIGN(n) __attribute__ ((aligned(n)))
#define MY_ALIGN_IN_STRUCT(n) MY_ALIGN(n)
#endif
+4 -4
View File
@@ -1,7 +1,7 @@
#define MY_VER_MAJOR 25
#define MY_VER_MAJOR 26
#define MY_VER_MINOR 1
#define MY_VER_BUILD 0
#define MY_VERSION_NUMBERS "25.01"
#define MY_VERSION_NUMBERS "26.01"
#define MY_VERSION MY_VERSION_NUMBERS
#ifdef MY_CPU_NAME
@@ -10,12 +10,12 @@
#define MY_VERSION_CPU MY_VERSION
#endif
#define MY_DATE "2025-08-03"
#define MY_DATE "2026-04-27"
#undef MY_COPYRIGHT
#undef MY_VERSION_COPYRIGHT_DATE
#define MY_AUTHOR_NAME "Igor Pavlov"
#define MY_COPYRIGHT_PD "Igor Pavlov : Public domain"
#define MY_COPYRIGHT_CR "Copyright (c) 1999-2025 Igor Pavlov"
#define MY_COPYRIGHT_CR "Copyright (c) 1999-2026 Igor Pavlov"
#ifdef USE_COPYRIGHT_CR
#define MY_COPYRIGHT MY_COPYRIGHT_CR
+8 -2
View File
@@ -1,11 +1,17 @@
/* 7zWindows.h -- StdAfx
2023-04-02 : Igor Pavlov : Public domain */
/* 7zWindows.h -- Windows.h and related code
Igor Pavlov : Public domain */
#ifndef ZIP7_INC_7Z_WINDOWS_H
#define ZIP7_INC_7Z_WINDOWS_H
#ifdef _WIN32
#if defined(_MSC_VER) && _MSC_VER >= 1950 && !defined(__clang__) // VS2026
// <Windows.h> and some another windows files need that option
// VS2026: wtypesbase.h: warning C4865: 'tagCLSCTX': the underlying type will change from 'int' to 'unsigned int' when '/Zc:enumTypes' is specified on the command line
#pragma warning(disable : 4865)
#endif
#if defined(__clang__)
# pragma clang diagnostic push
#endif
+1 -1
View File
@@ -106,7 +106,7 @@ DEL_OBJ_EXE = -$(RM) $(O)\*.o $(O)\$(PROG).exe $(O)\$(PROG).dll
endif
LIB2 = -lOle32 -loleaut32 -luuid -ladvapi32 -lUser32 -lShell32
LIB2 = -lole32 -loleaut32 -luuid -ladvapi32 -luser32 -lshell32
CFLAGS_EXTRA = -DUNICODE -D_UNICODE
# -Wno-delete-non-virtual-dtor
+208 -39
View File
@@ -1,5 +1,5 @@
/* Alloc.c -- Memory allocation functions
2024-02-18 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#include "Precomp.h"
@@ -24,8 +24,6 @@
#endif
// #define SZ_ALLOC_DEBUG
/* #define SZ_ALLOC_DEBUG */
/* use SZ_ALLOC_DEBUG to debug alloc/free operations */
#ifdef SZ_ALLOC_DEBUG
@@ -34,9 +32,10 @@
static int g_allocCount = 0;
#ifdef _WIN32
static int g_allocCountMid = 0;
#ifdef Z7_LARGE_PAGES
static int g_allocCountBig = 0;
#endif
#endif
#define CONVERT_INT_TO_STR(charType, tempSize) \
char temp[tempSize]; unsigned i = 0; \
@@ -140,8 +139,10 @@ static void PrintAddr(void *p)
#else
#ifdef _WIN32
#ifdef Z7_LARGE_PAGES
#define PRINT_ALLOC(name, cnt, size, ptr)
#endif
#endif
#define PRINT_FREE(name, cnt, ptr)
#define Print(s)
#define PrintLn()
@@ -245,6 +246,7 @@ void MidFree(void *address)
}
#ifdef Z7_LARGE_PAGES
// #pragma message("Z7_LARGE_PAGES")
#ifdef MEM_LARGE_PAGES
#define MY_MEM_LARGE_PAGES MEM_LARGE_PAGES
@@ -253,32 +255,14 @@ void MidFree(void *address)
#endif
extern
SIZE_T g_LargePageSize;
SIZE_T g_LargePageSize = 0;
typedef SIZE_T (WINAPI *Func_GetLargePageMinimum)(VOID);
void SetLargePageSize(void)
{
SIZE_T size;
#ifdef Z7_USE_DYN_GetLargePageMinimum
Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
const
Func_GetLargePageMinimum fn =
(Func_GetLargePageMinimum) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
"GetLargePageMinimum");
if (!fn)
return;
size = fn();
#else
size = GetLargePageMinimum();
#endif
if (size == 0 || (size & (size - 1)) != 0)
return;
g_LargePageSize = size;
}
#endif // Z7_LARGE_PAGES
size_t g_LargePageSize;
size_t g_LargePageSize = 0;
extern
size_t g_LargePageThresholdMin;
size_t g_LargePageThresholdMin = 0;
extern
UInt32 g_LargePageFlags;
UInt32 g_LargePageFlags = 0;
void *BigAlloc(size_t size)
{
@@ -289,12 +273,10 @@ void *BigAlloc(size_t size)
#ifdef Z7_LARGE_PAGES
{
SIZE_T ps = g_LargePageSize;
if (ps != 0 && ps <= (1 << 30) && size > (ps / 2))
const size_t ps = g_LargePageSize - 1;
if (ps < (1u << 30) && size > g_LargePageThresholdMin)
{
size_t size2;
ps--;
size2 = (size + ps) & ~ps;
const size_t size2 = (size + ps) & ~ps;
if (size2 >= size)
{
void *p = VirtualAlloc(NULL, size2, MEM_COMMIT | MY_MEM_LARGE_PAGES, PAGE_READWRITE);
@@ -303,6 +285,8 @@ void *BigAlloc(size_t size)
PRINT_ALLOC("Alloc-BM ", g_allocCountMid, size2, p)
return p;
}
if (g_LargePageFlags & Z7_LARGE_PAGES_FLAG_FAIL_STOP)
return p;
}
}
}
@@ -317,6 +301,7 @@ void BigFree(void *address)
MidFree(address);
}
#endif // Z7_LARGE_PAGES
#endif // _WIN32
@@ -327,9 +312,12 @@ const ISzAlloc g_Alloc = { SzAlloc, SzFree };
#ifdef _WIN32
static void *SzMidAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p) return MidAlloc(size); }
static void SzMidFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p) MidFree(address); }
const ISzAlloc g_MidAlloc = { SzMidAlloc, SzMidFree };
#endif
#if defined(Z7_LARGE_PAGES)
static void *SzBigAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p) return BigAlloc(size); }
static void SzBigFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p) BigFree(address); }
const ISzAlloc g_MidAlloc = { SzMidAlloc, SzMidFree };
const ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree };
#endif
@@ -371,10 +359,16 @@ typedef
#endif
#if !defined(_WIN32) \
&& (defined(Z7_ALLOC_NO_OFFSET_ALLOCATOR) \
|| defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L))
#ifndef _WIN32
#include <unistd.h> // for _POSIX_ADVISORY_INFO : for some linux
#if (defined(Z7_ALLOC_NO_OFFSET_ALLOCATOR) \
|| defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L) \
|| defined(_POSIX_ADVISORY_INFO) && (_POSIX_ADVISORY_INFO >= 200112L) \
|| defined(__APPLE__) \
/* || defined(__linux__) */)
#define USE_posix_memalign
// #pragma message("USE_posix_memalign")
#endif
#endif
#ifndef USE_posix_memalign
@@ -488,6 +482,181 @@ static void SzAlignedFree(ISzAllocPtr pp, void *address)
#endif
}
#ifndef _WIN32
#ifdef Z7_LARGE_PAGES
#if 0 // 1 for debug
#include <stdio.h>
#include <string.h> // for strerror()
#define PRF(x) x
#else
#define PRF(x)
#endif
#ifdef USE_posix_memalign
/* madvise():
glibc <= 2.19 : _BSD_SOURCE
glibc > 2.19 : _DEFAULT_SOURCE
*/
/* && (defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE)) */
#if 1 && !defined(Z7_NO_MADVISE) && \
(defined(__linux__) || defined(__unix__) || defined(__APPLE__))
#include <sys/mman.h> // for madvise
// #pragma message("sys/mman.h")
#if (defined(MADV_HUGEPAGE) && defined(MADV_NOHUGEPAGE))
#define Z7_USE_BIG_ALLOC_MADVISE
// #pragma message("Z7_USE_BIG_ALLOC_MADVISE")
#endif
#endif
#endif // USE_posix_memalign
#ifdef Z7_USE_BIG_ALLOC_MADVISE
#define LARGE_PAGE_SIZE_DEFAULT (1 << 21)
#else
#define LARGE_PAGE_SIZE_DEFAULT 0
#endif
extern
size_t g_LargePageSize;
size_t g_LargePageSize = LARGE_PAGE_SIZE_DEFAULT;
extern
size_t g_LargePageThresholdMin;
size_t g_LargePageThresholdMin = LARGE_PAGE_SIZE_DEFAULT / 2;
extern
UInt32 g_LargePageFlags;
UInt32 g_LargePageFlags = 0;
void *BigAlloc(size_t size)
{
if (size == 0)
return NULL;
#ifdef USE_posix_memalign
{
const size_t pageSize = g_LargePageSize;
void *buf = NULL; // on Linux (and other systems), posix_memalign() does not modify memptr on failure (POSIX.1-2008 TC2).
PRF(printf("\nBigAlloc 0x%08x=%5uMB", (unsigned)(size), (unsigned)(size >> 20));)
if (pageSize && size > g_LargePageThresholdMin)
{
int res;
const size_t mask = pageSize - 1;
/* we can allocate aligned size, so data at the end of buffer also will use huge page
if (size2 for madvise() is not aligned for huge page size)
{ Last data block will use small pages. It reduces memory allocation,
but last data block with small pages can work slower.
It's useful, if we have very large HUGE_PAGE: 32MB or 512MB. }
*/
size_t size2 = (size + mask) & ~mask;
if (size2 < size || (size & mask) <= g_LargePageThresholdMin)
size2 = size;
res = posix_memalign(&buf, pageSize, size2);
PRF(printf(" posix_memalign size=0x%08x=%5uMB align=%u",
(unsigned)(size2), (unsigned)(size2 >> 20), (unsigned)pageSize);)
PRF(printf(" buf=%p", (void *)buf);)
if (res == 0)
{
#ifdef Z7_USE_BIG_ALLOC_MADVISE
if ((g_LargePageFlags & Z7_LARGE_PAGES_FLAG_NO_MADVISE) == 0)
{
// Advise the kernel to use huge pages for this memory range
// MADV_HUGEPAGE / MADV_NOHUGEPAGE : since Linux 2.6.38
// madvise() only operates on whole pages, therefore addr must be page-aligned (4KB/8KB/16KB/64KB).
// The value of size is rounded up to a multiple of page size.
PRF(printf(" madvise g_LargePageFlags=%x", (unsigned)g_LargePageFlags);)
res = madvise(buf, size2, (g_LargePageFlags & Z7_LARGE_PAGES_FLAG_NO_HUGEPAGE) ? MADV_NOHUGEPAGE : MADV_HUGEPAGE);
if (res)
{
PRF(printf("\nERROR res=%d, errno=%d=%s\n", res, (int)errno, strerror(errno));)
if (g_LargePageFlags & Z7_LARGE_PAGES_FLAG_FAIL_STOP)
{
free(buf);
return NULL;
}
}
}
#endif // Z7_USE_BIG_ALLOC_MADVISE
PRF(printf("\n");)
return buf;
}
PRF(printf("\nERROR res=%d=%s\n", res, strerror(res));)
if (g_LargePageFlags & Z7_LARGE_PAGES_FLAG_FAIL_STOP)
return NULL;
// (res == ENOMEM) "Out of memory" is possible, if pageSize is too big.
// so we do second attempt with smaller alignment
}
}
#endif // !USE_posix_memalign
PRF(printf(" z7_AlignedAlloc size=0x%08x=%5uMB\n", (unsigned)(size), (unsigned)(size >> 20));)
return z7_AlignedAlloc(size);
}
void BigFree(void *address)
{
z7_AlignedFree(address);
}
#endif // Z7_LARGE_PAGES
#endif // !_WIN32
#ifdef Z7_LARGE_PAGES
void z7_LargePage_Set(UInt32 flags, size_t pageSize, size_t threshold)
{
g_LargePageFlags = flags;
#ifdef _WIN32
if ((flags & Z7_LARGE_PAGES_FLAG_USE_HUGEPAGE) == 0)
{
g_LargePageSize = 0;
g_LargePageThresholdMin = 0;
}
else
{
if ((flags & Z7_LARGE_PAGES_FLAG_DIRECT_PAGE_SIZE) == 0)
{
#ifdef Z7_USE_DYN_GetLargePageMinimum
Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
typedef SIZE_T (WINAPI *Func_GetLargePageMinimum)(VOID);
const
Func_GetLargePageMinimum fn =
(Func_GetLargePageMinimum) Z7_CAST_FUNC_C GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),
"GetLargePageMinimum");
if (fn)
pageSize = fn();
else
pageSize = 0;
#else
pageSize = GetLargePageMinimum();
#endif
if (pageSize & (pageSize - 1))
pageSize = 0;
}
g_LargePageSize = pageSize;
if ((flags & Z7_LARGE_PAGES_FLAG_DIRECT_THRESHOLD) == 0)
threshold = pageSize / 2;
g_LargePageThresholdMin = threshold;
}
#else // !_WIN32
if (flags & Z7_LARGE_PAGES_FLAG_NO_PAGECODE)
{
g_LargePageSize = 0;
g_LargePageThresholdMin = 0;
}
else
{
if ((flags & Z7_LARGE_PAGES_FLAG_DIRECT_PAGE_SIZE) == 0)
pageSize = LARGE_PAGE_SIZE_DEFAULT;
g_LargePageSize = pageSize;
if ((flags & Z7_LARGE_PAGES_FLAG_DIRECT_THRESHOLD) == 0)
threshold = pageSize / 2;
g_LargePageThresholdMin = threshold;
}
// PRF(printf("\ng_LargePageSize=%x g_LargePageThresholdMin = %x g_LargePageFlags = %x", (unsigned)g_LargePageSize, (unsigned)g_LargePageThresholdMin, (unsigned)g_LargePageFlags);)
#endif // !_WIN32
}
#endif // Z7_LARGE_PAGES
const ISzAlloc g_AlignedAlloc = { SzAlignedAlloc, SzAlignedFree };
+27 -27
View File
@@ -1,5 +1,5 @@
/* Alloc.h -- Memory allocation functions
2024-01-22 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_ALLOC_H
#define ZIP7_INC_ALLOC_H
@@ -25,40 +25,40 @@ void *MyRealloc(void *address, size_t size);
void *z7_AlignedAlloc(size_t size);
void z7_AlignedFree(void *p);
extern const ISzAlloc g_Alloc;
extern const ISzAlloc g_AlignedAlloc;
#ifdef _WIN32
void *MidAlloc(size_t size);
void MidFree(void *address);
extern const ISzAlloc g_MidAlloc;
#else
#define MidAlloc(size) z7_AlignedAlloc(size)
#define MidFree(address) z7_AlignedFree(address)
#define g_MidAlloc g_AlignedAlloc
#endif
#ifdef Z7_LARGE_PAGES
void SetLargePageSize(void);
#endif
void *MidAlloc(size_t size);
void MidFree(void *address);
void *BigAlloc(size_t size);
void BigFree(void *address);
/* #define Z7_BIG_ALLOC_IS_ZERO_FILLED */
#define Z7_LARGE_PAGES_FLAG_USE_HUGEPAGE (1 << 0) // PAGE_ALIGNED / MADV_HUGEPAGE
#define Z7_LARGE_PAGES_FLAG_NO_PAGECODE (1 << 1) // no PAGE_ALIGNED / no madvise
#define Z7_LARGE_PAGES_FLAG_NO_MADVISE (1 << 2) // PAGE_ALIGNED / no madvise : for THP=always
#define Z7_LARGE_PAGES_FLAG_NO_HUGEPAGE (1 << 3) // PAGE_ALIGNED / MADV_NOHUGEPAGE
#define Z7_LARGE_PAGES_FLAG_FAIL_STOP (1 << 15) // for benchmarks
#define Z7_LARGE_PAGES_FLAG_DIRECT_PAGE_SIZE (1 << 16)
#define Z7_LARGE_PAGES_FLAG_DIRECT_THRESHOLD (1 << 17)
void z7_LargePage_Set(UInt32 flags, size_t pageSize, size_t threshold);
void *BigAlloc(size_t size);
void BigFree(void *address);
extern const ISzAlloc g_BigAlloc;
#else
#define MidAlloc(size) z7_AlignedAlloc(size)
#define MidFree(address) z7_AlignedFree(address)
#define BigAlloc(size) z7_AlignedAlloc(size)
#define BigFree(address) z7_AlignedFree(address)
#define BigAlloc(size) MidAlloc(size)
#define BigFree(address) MidFree(address)
#define g_BigAlloc g_MidAlloc
#endif
extern const ISzAlloc g_Alloc;
#ifdef _WIN32
extern const ISzAlloc g_BigAlloc;
extern const ISzAlloc g_MidAlloc;
#else
#define g_BigAlloc g_AlignedAlloc
#define g_MidAlloc g_AlignedAlloc
#endif
extern const ISzAlloc g_AlignedAlloc;
typedef struct
{
+7 -1
View File
@@ -54,6 +54,12 @@
#pragma GCC diagnostic ignored "-Wexcess-padding"
#endif
#if defined(Z7_APPLE_CLANG_VERSION) && __clang_major__ >= 21
// warning: function MyAlloc might be an allocator wrapper
// clang in xcode: clang 21.0.0
#pragma GCC diagnostic ignored "-Wallocator-wrappers"
#endif
#if __clang_major__ >= 16
#pragma GCC diagnostic ignored "-Wunsafe-buffer-usage"
#endif
@@ -72,7 +78,7 @@
#endif // __clang__
#if defined(_WIN32) && defined(__clang__) && __clang_major__ >= 16
#if defined(__clang__) && __clang_major__ >= 16
// #pragma GCC diagnostic ignored "-Wcast-function-type-strict"
#define Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION \
_Pragma("GCC diagnostic ignored \"-Wcast-function-type-strict\"")
+2 -2
View File
@@ -859,7 +859,7 @@ BoolInt CPU_IsSupported_AES (void) { return APPLE_CRYPTO_SUPPORT_VAL; }
#if defined(__GLIBC__) && (__GLIBC__ * 100 + __GLIBC_MINOR__ >= 216)
#define Z7_GETAUXV_AVAILABLE
#else
#elif !defined(__QNXNTO__)
// #pragma message("=== is not NEW GLIBC === ")
#if defined __has_include
#if __has_include (<sys/auxv.h>)
@@ -877,7 +877,7 @@ BoolInt CPU_IsSupported_AES (void) { return APPLE_CRYPTO_SUPPORT_VAL; }
#ifdef USE_HWCAP
#if defined(__FreeBSD__)
#if defined(__FreeBSD__) || defined(__OpenBSD__)
static unsigned long MY_getauxval(int aux)
{
unsigned long val;
+20 -2
View File
@@ -31,7 +31,12 @@ MY_CPU_64BIT means that processor can work with 64-bit registers.
#define MY_CPU_NAME "x32"
#define MY_CPU_SIZEOF_POINTER 4
#else
#define MY_CPU_NAME "x64"
#if defined(__APX_EGPR__) || defined(__EGPR__)
#define MY_CPU_NAME "x64-apx"
#define MY_CPU_AMD64_APX
#else
#define MY_CPU_NAME "x64"
#endif
#define MY_CPU_SIZEOF_POINTER 8
#endif
#define MY_CPU_64BIT
@@ -249,11 +254,12 @@ MY_CPU_64BIT means that processor can work with 64-bit registers.
#endif
// _LITTLE_ENDIAN macro can be defined for big-endian platform with some compilers
#if defined(MY_CPU_X86_OR_AMD64) \
|| defined(MY_CPU_ARM_LE) \
|| defined(MY_CPU_ARM64_LE) \
|| defined(MY_CPU_IA64_LE) \
|| defined(_LITTLE_ENDIAN) \
|| defined(__LITTLE_ENDIAN__) \
|| defined(__ARMEL__) \
|| defined(__THUMBEL__) \
@@ -596,8 +602,20 @@ problem-4 : performace:
#define SetBe32a(p, v) { *(UInt32 *)(void *)(p) = (v); }
#define SetBe16a(p, v) { *(UInt16 *)(void *)(p) = (v); }
// gcc and clang for powerpc can transform load byte access to load reverse word access.
// sp we can use byte access instead of word access. Z7_BSWAP64 cab be slow
#if 1 && defined(Z7_CPU_FAST_BSWAP_SUPPORTED) && defined(MY_CPU_64BIT)
#define GetUi64a(p) Z7_BSWAP64 (*(const UInt64 *)(const void *)(p))
#else
#define GetUi64a(p) GetUi64(p)
#endif
#if 1 && defined(Z7_CPU_FAST_BSWAP_SUPPORTED)
#define GetUi32a(p) Z7_BSWAP32 (*(const UInt32 *)(const void *)(p))
#else
#define GetUi32a(p) GetUi32(p)
#endif
#define GetUi16a(p) GetUi16(p)
#define SetUi32a(p, v) SetUi32(p, v)
#define SetUi16a(p, v) SetUi16(p, v)
+2 -3
View File
@@ -2351,10 +2351,9 @@ static void LzmaEnc_Construct(CLzmaEnc *p)
CLzmaEncHandle LzmaEnc_Create(ISzAllocPtr alloc)
{
void *p;
p = ISzAlloc_Alloc(alloc, sizeof(CLzmaEnc));
CLzmaEncHandle p = (CLzmaEncHandle)ISzAlloc_Alloc(alloc, sizeof(CLzmaEnc));
if (p)
LzmaEnc_Construct((CLzmaEnc *)p);
LzmaEnc_Construct(p);
return p;
}
+7 -7
View File
@@ -1,5 +1,5 @@
/* Precomp.h -- precompilation file
2024-01-25 : Igor Pavlov : Public domain */
: Igor Pavlov : Public domain */
#ifndef ZIP7_INC_PRECOMP_H
#define ZIP7_INC_PRECOMP_H
@@ -40,18 +40,18 @@
#endif
*/
#ifndef Z7_LARGE_PAGES
#if !defined(Z7_NO_LARGE_PAGES) && !defined(UNDER_CE)
#define Z7_LARGE_PAGES 1
#endif
#endif
#ifdef _WIN32
/*
this "Precomp.h" file must be included before <windows.h>,
if we want to define _WIN32_WINNT before <windows.h>.
*/
#ifndef Z7_LARGE_PAGES
#ifndef Z7_NO_LARGE_PAGES
#define Z7_LARGE_PAGES 1
#endif
#endif
#ifndef Z7_LONG_PATH
#ifndef Z7_NO_LONG_PATH
#define Z7_LONG_PATH 1
+1 -1
View File
@@ -1,2 +1,2 @@
# LZMA SDK v25.01
# LZMA SDK v26.01
Downloaded from https://www.7-zip.org/sdk.html
+18 -4
View File
@@ -153,6 +153,17 @@ static void PrintProcess_Info()
#endif
#endif
/* if we send (stackSize=0) to CreateThread(), it will
use default value PE::SizeOfStackReserve from exe file.
PE::SizeOfStackReserve == 1 MiB in exe file with default linker options.
Windows aligns specified value to the next 64 KB range. */
static const unsigned k_StackSize_ReserveSize =
#ifdef UNDER_CE
1 << 17;
#else
1 << 20;
#endif
WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param)
{
/* Windows Me/98/95: threadId parameter may not be NULL in _beginthreadex/CreateThread functions */
@@ -160,12 +171,15 @@ WRes Thread_Create(CThread *p, THREAD_FUNC_TYPE func, LPVOID param)
#ifdef USE_THREADS_CreateThread
DWORD threadId;
*p = CreateThread(NULL, 0, func, param, 0, &threadId);
*p = CreateThread(NULL, k_StackSize_ReserveSize, func, param, STACK_SIZE_PARAM_IS_A_RESERVATION, &threadId);
#else
#define CALL_beginthreadex(func2, param2, flags, threadIdPtr) \
((HANDLE)(_beginthreadex(NULL, k_StackSize_ReserveSize, func2, param2, (flags) | STACK_SIZE_PARAM_IS_A_RESERVATION, threadIdPtr)))
unsigned threadId;
*p = (HANDLE)(_beginthreadex(NULL, 0, func, param, 0, &threadId));
*p = CALL_beginthreadex(func, param, 0, &threadId);
#if 0 // 1 : for debug
{
@@ -223,7 +237,7 @@ WRes Thread_Create_With_Affinity(CThread *p, THREAD_FUNC_TYPE func, LPVOID param
HANDLE h;
WRes wres;
unsigned threadId;
h = (HANDLE)(_beginthreadex(NULL, 0, func, param, CREATE_SUSPENDED, &threadId));
h = CALL_beginthreadex(func, param, CREATE_SUSPENDED, &threadId);
*p = h;
wres = HandleToWRes(h);
if (h)
@@ -272,7 +286,7 @@ WRes Thread_Create_With_Group(CThread *p, THREAD_FUNC_TYPE func, LPVOID param, u
HANDLE h;
WRes wres;
unsigned threadId;
h = (HANDLE)(_beginthreadex(NULL, 0, func, param, CREATE_SUSPENDED, &threadId));
h = CALL_beginthreadex(func, param, CREATE_SUSPENDED, &threadId);
*p = h;
wres = HandleToWRes(h);
if (h)
+345 -32
View File
@@ -1,15 +1,16 @@
/* 7zMain.c - Test application for 7z Decoder
2024-02-28 : Igor Pavlov : Public domain */
/* 7zMain.c - 7z archive decoding program
: Igor Pavlov : Public domain */
#include "Precomp.h"
#include <stdio.h>
#include <string.h>
#include "../../7zFile.h"
#ifndef USE_WINDOWS_FILE
/* for mkdir */
#ifdef _WIN32
#include <direct.h>
#include <direct.h> // for _mkdir()
#else
#include <stdlib.h>
#include <time.h>
@@ -23,7 +24,14 @@
#endif
#endif
#include "../../7zFile.h"
#ifdef _WIN32
// for _isatty()
#include "../../7zWindows.h"
#include <io.h>
#else
#include <unistd.h> // for isatty()
#endif
#include "../../7z.h"
#include "../../7zAlloc.h"
#include "../../7zBuf.h"
@@ -44,6 +52,25 @@ static void Print(const char *s)
}
static Z7_FORCE_INLINE BoolInt MY_IS_TERMINAL(FILE *x)
{
#ifdef _WIN32
const int fd = _fileno(x);
HANDLE h;
DWORD st;
if (fd < 0)
return False;
if (!_isatty(fd))
return False;
h = (HANDLE)(_get_osfhandle(fd));
if (h == NULL || h == INVALID_HANDLE_VALUE)
return False;
return GetConsoleMode(h, &st) != 0;
#else
return isatty(fileno(x)) != 0;
#endif
}
static int Buf_EnsureSize(CBuf *dest, size_t size)
{
if (dest->size >= size)
@@ -196,6 +223,206 @@ static SRes Utf16_To_Char(CBuf *buf, const UInt16 *s
#endif
}
#ifdef _WIN32
static Z7_FORCE_INLINE unsigned MyCharLower_Ascii(unsigned c)
{
if (c >= 'A' && c <= 'Z')
return (unsigned)(c + 0x20);
return c;
}
static Z7_FORCE_INLINE
BoolInt IsString1PrefixedByString2_NoCase_Ascii(const UInt16 *s1, const char *s2)
{
for (;;)
{
wchar_t c1;
const char c2 = *s2++; if (c2 == 0) return True;
c1 = *s1++;
if (c1 != (unsigned char)c2 && MyCharLower_Ascii(c1) != MyCharLower_Ascii((unsigned char)c2))
return False;
}
}
static const unsigned g_ReservedWithNum_Index = 4;
static const char * const g_ReservedNames[] =
{
"CON", "PRN", "AUX", "NUL",
"COM", "LPT"
};
static Z7_FORCE_INLINE
BoolInt IsReservedFsName(const UInt16 *name, size_t sLen)
{
unsigned i;
for (i = 0; i < Z7_ARRAY_SIZE(g_ReservedNames); i++)
{
const char *reservedName = g_ReservedNames[i];
size_t len = strlen(reservedName);
if (sLen < len)
continue;
if (!IsString1PrefixedByString2_NoCase_Ascii(name, reservedName))
continue;
if (i >= g_ReservedWithNum_Index)
{
if ((unsigned)((unsigned)name[len] - (unsigned)'0') > 9)
continue;
len++;
}
for (;;)
{
unsigned c;
if (len >= sLen)
return True;
c = name[len++];
if (c == 0 || c == '.')
return True;
if (c != ' ')
break;
}
}
return False;
}
static Z7_FORCE_INLINE
void Correct_FileName_for_FS(UInt16 *s, size_t size)
{
while (size)
{
const unsigned c = s[--size];
if (c != '.' && c != ' ')
break;
s[size] = '_';
}
}
#endif // _WIN32
// in: (s) uses WCHAR_PATH_SEPARATOR path separators
static Z7_FORCE_INLINE
void NormalizesPathParts_and_Dots(UInt16 *dest, const UInt16 *s, BoolInt isDir)
{
UInt16 * const destStart = dest;
size_t len;
// remove absolute path prefixes
for (; *s == WCHAR_PATH_SEPARATOR; s++)
{}
len = 0;
for (;;)
{
const unsigned c = s[len];
if (c != 0 && c != WCHAR_PATH_SEPARATOR)
{
len++;
continue;
}
{
if (len == 0
|| (len == 1 && s[len - 1] == '.')
|| (len == 2 && s[len - 1] == '.' && s[len - 2] == '.'))
{
if (c == 0)
{
if (!isDir)
{
*dest++ = '_';
// if (len == 2) *dest++ = '_'; // for ".." -> "__"
}
break;
}
}
else
{
#ifdef _WIN32
if (IsReservedFsName(s, len))
*dest++ = '_';
#endif
memcpy(dest, s, sizeof(s[0]) * len);
#ifdef _WIN32
Correct_FileName_for_FS(dest, len);
#endif
dest += len;
if (c == 0)
break;
*dest++ = WCHAR_PATH_SEPARATOR;
}
s += len + 1;
len = 0;
}
}
if (dest != destStart && dest[-1] == WCHAR_PATH_SEPARATOR)
{
if (isDir)
dest--;
else
*dest++ = '_';
}
if (dest == destStart)
*dest++ = '_';
*dest = 0;
}
#if WCHAR_PATH_SEPARATOR != L'/'
#ifdef _WIN32
// WSL scheme
#define WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT ((unsigned)((unsigned)(0xF000) + (unsigned)'\\'))
#else
#define WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT '_'
#endif
static Z7_FORCE_INLINE
void Normalize_Path_from_7z_to_FS(UInt16 *s)
{
// Normalize_Path_from_7z_to_FS(s);
for (;; s++)
{
unsigned c = *s;
if (c == 0)
break;
if (c == '/') // separator inside 7z archive
c = WCHAR_PATH_SEPARATOR;
else if (c == WCHAR_PATH_SEPARATOR)
c = WCHAR_IN_FILE_NAME_BACKSLASH_REPLACEMENT;
#ifdef _WIN32
else if (c < 0x20 || c == ':' || c == '*' || c == '?' || c == '<' || c == '>' || c == '|' || c == '"')
c = '_';
else
continue;
*s = (UInt16)c;
#endif
}
}
#endif
// this function changes both strings: (dest) and (s),
// but length of (s) string will not changed.
// dest[] must provide additional space: size(dest) >= len(len) * 2 + 2
// in: (s) uses slash path separators (/)
// out: (s) : temporary string
// out: (dest) uses WCHAR_PATH_SEPARATOR path separator
static Z7_FORCE_INLINE
void NormalizePath_for_FS(UInt16 *dest, UInt16 *s, BoolInt isDir)
{
#if WCHAR_PATH_SEPARATOR != L'/'
Normalize_Path_from_7z_to_FS(s);
#endif
NormalizesPathParts_and_Dots(dest, s , isDir);
}
#ifdef _WIN32
#ifndef USE_WINDOWS_FILE
static UINT g_FileCodePage = CP_ACP;
@@ -205,6 +432,38 @@ static SRes Utf16_To_Char(CBuf *buf, const UInt16 *s
#define MY_FILE_CODE_PAGE_PARAM
#endif
#ifdef USE_WINDOWS_FILE
static WRes My_DeleteFileAlways(const UInt16 *path)
{
const DWORD attrib = GetFileAttributesW(path);
if (// attrib != INVALID_FILE_ATTRIBUTES &&
(attrib & FILE_ATTRIBUTE_DIRECTORY) == 0 &&
(attrib & FILE_ATTRIBUTE_READONLY))
{
if (!SetFileAttributes(path, attrib & ~(DWORD)FILE_ATTRIBUTE_READONLY))
return GetLastError();
}
if (DeleteFileW(path))
return 0;
return GetLastError();
}
#else
static WRes My_DeleteFileAlways(const UInt16 *path)
{
CBuf buf;
WRes res;
Buf_Init(&buf);
RINOK_WRes(Utf16_To_Char(&buf, path MY_FILE_CODE_PAGE_PARAM))
res = remove((const char *)buf.data) == 0 ? 0 : errno;
Buf_Free(&buf, &g_Alloc);
return res;
}
#endif
static WRes MyCreateDir(const UInt16 *name)
{
#ifdef USE_WINDOWS_FILE
@@ -216,7 +475,7 @@ static WRes MyCreateDir(const UInt16 *name)
CBuf buf;
WRes res;
Buf_Init(&buf);
RINOK(Utf16_To_Char(&buf, name MY_FILE_CODE_PAGE_PARAM))
RINOK_WRes(Utf16_To_Char(&buf, name MY_FILE_CODE_PAGE_PARAM))
res =
#ifdef _WIN32
@@ -247,12 +506,51 @@ static WRes OutFile_OpenUtf16(CSzFile *p, const UInt16 *name)
}
static SRes PrintString(const UInt16 *s)
static Z7_FORCE_INLINE BoolInt IsDangerousTerminalChar(wchar_t c)
{
// if (c < 0) return False; // it's not expected case
if (c < 0x20) return True;
if (c < 0x7F) return False;
if (c < 0x9F + 1) return True;
// Unicode Bidirectional (BiDi) control characters:
if (c < 0x202A) return False;
if (c < 0x202E + 1) return True;
if (c < 0x2066) return False;
if (c < 0x2069 + 1) return True;
return False;
}
// (size(dest) >= len(src) + 3), if (isDir == True)
// s[] uses slash path separator (/)
static SRes PrintPath(const UInt16 *s, UInt16 *dest, BoolInt isDir, BoolInt isTerminalMode)
{
CBuf buf;
SRes res;
size_t i;
for (i = 0;; i++)
{
UInt16 c = s[i];
if (c == 0)
break;
if (isTerminalMode ? IsDangerousTerminalChar(c) :
(c == '\n'
#ifdef _WIN32
|| c == '\r'
#endif
))
c = '_';
dest[i] = c;
}
if (i == 0)
dest[i++] = '_';
if (isDir && dest[i - 1] != '/')
dest[i++] = '/';
dest[i] = 0;
Buf_Init(&buf);
res = Utf16_To_Char(&buf, s
res = Utf16_To_Char(&buf, dest
#ifndef MY_USE_UTF8
, CP_OEMCP
#endif
@@ -499,7 +797,7 @@ static void PrintError_WRes(const char *message, WRes wres)
Print(s);
}
// sprintf(buffer + strlen(buffer), "\nSystem error code: %d", (unsigned)wres);
#ifdef _WIN32
#ifdef USE_WINDOWS_FILE // _WIN32
{
char *s = NULL;
if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
@@ -540,8 +838,6 @@ static void GetAttribString(UInt32 wa, BoolInt isDir, char *s)
}
// #define NUM_PARENTS_MAX 128
int Z7_CDECL main(int numargs, char *args[])
{
ISzAlloc allocImp;
@@ -552,8 +848,9 @@ int Z7_CDECL main(int numargs, char *args[])
CSzArEx db;
SRes res;
UInt16 *temp = NULL;
UInt16 *temp2 = NULL;
size_t tempSize = 0;
// UInt32 parents[NUM_PARENTS_MAX];
const BoolInt isTerminalMode = MY_IS_TERMINAL(stdout);
Print("\n7z Decoder " MY_VERSION_CPU " : " MY_COPYRIGHT_DATE "\n\n");
@@ -669,12 +966,15 @@ int Z7_CDECL main(int numargs, char *args[])
{
SzFree(NULL, temp);
tempSize = len;
temp = (UInt16 *)SzAlloc(NULL, tempSize * sizeof(temp[0]));
// temp2 requires additional space for "_" additions for "COM1" and empty names,
// and up to 2 additional characters for PrintPath().
temp = (UInt16 *)SzAlloc(NULL, (tempSize * 3 + 16) * sizeof(temp[0]));
if (!temp)
{
res = SZ_ERROR_MEM;
break;
}
temp2 = temp + tempSize;
}
SzArEx_GetFileNameUtf16(&db, i, temp);
@@ -712,25 +1012,19 @@ int Z7_CDECL main(int numargs, char *args[])
Print(" ");
Print(s);
Print(" ");
res = PrintString(temp);
res = PrintPath(temp, temp2, isDir, isTerminalMode);
if (res != SZ_OK)
break;
if (isDir)
Print("/");
PrintLF();
continue;
}
Print(testCommand ?
"T ":
"- ");
res = PrintString(temp);
Print(testCommand ? "T ": "- ");
res = PrintPath(temp, temp2, isDir, isTerminalMode);
if (res != SZ_OK)
break;
if (isDir)
Print("/");
else
if (!isDir)
{
res = SzArEx_Extract(&db, &lookStream.vt, i,
&blockIndex, &outBuffer, &outBufferSize,
@@ -745,11 +1039,16 @@ int Z7_CDECL main(int numargs, char *args[])
CSzFile outFile;
size_t processedSize;
size_t j;
UInt16 *name = (UInt16 *)temp;
UInt16 *name = (UInt16 *)temp2;
const UInt16 *destPath = (const UInt16 *)name;
// memset(temp2, 0, (tempSize) * sizeof(temp[0])); // for debug
NormalizePath_for_FS(temp2, temp, isDir);
// PrintLF(); PrintPath(temp2, temp, isDir, isTerminalMode); // for debuf
for (j = 0; name[j] != 0; j++)
if (name[j] == '/')
if (name[j] == CHAR_PATH_SEPARATOR)
{
if (fullPaths)
{
@@ -769,12 +1068,26 @@ int Z7_CDECL main(int numargs, char *args[])
}
else
{
const WRes wres = OutFile_OpenUtf16(&outFile, destPath);
if (wres != 0)
{
PrintError_WRes("cannot open output file", wres);
res = SZ_ERROR_FAIL;
break;
// const WRes wres =
My_DeleteFileAlways(destPath);
/*
if (wres && wres != ERROR_FILE_NOT_FOUND)
{
PrintError_WRes("cannot delete output file", wres);
res = SZ_ERROR_FAIL;
break;
}
*/
}
{
const WRes wres = OutFile_OpenUtf16(&outFile, destPath);
if (wres)
{
PrintError_WRes("cannot open output file", wres);
res = SZ_ERROR_FAIL;
break;
}
}
}
@@ -846,7 +1159,7 @@ int Z7_CDECL main(int numargs, char *args[])
UInt32 attrib = db.Attribs.Vals[i];
/* p7zip stores posix attributes in high 16 bits and adds 0x8000 as marker.
We remove posix bits, if we detect posix mode field */
if ((attrib & 0xF0000000) != 0)
if (attrib & 0xF0000000)
attrib &= 0x7FFF;
SetFileAttributesW((LPCWSTR)destPath, attrib);
}
+3 -3
View File
@@ -279,7 +279,7 @@ SRes Xz_StateCoder_Bc_SetFromMethod_Func(IStateCoder *p, UInt64 id,
decoder = (CXzBcFilterState *)ISzAlloc_Alloc(alloc, sizeof(CXzBcFilterState));
if (!decoder)
return SZ_ERROR_MEM;
decoder->buf = ISzAlloc_Alloc(alloc, BRA_BUF_SIZE);
decoder->buf = (Byte *)ISzAlloc_Alloc(alloc, BRA_BUF_SIZE);
if (!decoder->buf)
{
ISzAlloc_Free(alloc, decoder);
@@ -1243,7 +1243,7 @@ SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen,
UInt32 digest32[XZ_CHECK_SIZE_MAX / 4];
p->state = XZ_STATE_BLOCK_HEADER;
p->pos = 0;
if (XzCheck_Final(&p->check, (void *)digest32) && memcmp(digest32, p->buf, checkSize) != 0)
if (XzCheck_Final(&p->check, (Byte *)(void *)digest32) && memcmp(digest32, p->buf, checkSize) != 0)
return SZ_ERROR_CRC;
if (p->decodeOnlyOneBlock)
{
@@ -1292,7 +1292,7 @@ SRes XzUnpacker_Code(CXzUnpacker *p, Byte *dest, SizeT *destLen,
p->state = XZ_STATE_STREAM_INDEX_CRC;
p->indexSize += 4;
p->pos = 0;
Sha256_Final(&p->sha, (void *)digest32);
Sha256_Final(&p->sha, (Byte *)(void *)digest32);
if (memcmp(digest32, p->shaDigest32, SHA256_DIGEST_SIZE) != 0)
return SZ_ERROR_CRC;
}
+1 -1
View File
@@ -313,7 +313,7 @@ LDLIBS += $(ZLIB_LDLIBS)
ifeq ($(origin MINIZIP_CFLAGS) $(origin MINIZIP_LDLIBS), undefined undefined)
ifeq ($(shell $(PKG_CONFIG) --modversion minizip 2>/dev/null),)
CFLAGS += -DNOCRYPT -DNOUNCRYPT -I$(SUBDIR)/minizip
CFLAGS += -DNOCRYPT -DNOUNCRYPT -I$(SUBDIR)
MINIZIP_SOURCE = \
$(SUBDIR)/minizip/ioapi.c \
$(SUBDIR)/minizip/zip.c \
+18 -18
View File
@@ -372,22 +372,22 @@ static const struct r4300_idec r4300_op_table[] = {
/* TLB opcodes table
* 176-239
*/
RESERVED, TLBR, TLBWI, RESERVED,
RESERVED, RESERVED, TLBWR, RESERVED,
TLBP, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
ERET, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
RESERVED, RESERVED, RESERVED, RESERVED,
NOP, TLBR, TLBWI, NOP,
NOP, NOP, TLBWR, NOP,
TLBP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
ERET, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
NOP, NOP, NOP, NOP,
/* COP1 opcodes table
* 240-247
*/
@@ -430,7 +430,7 @@ static const struct r4300_idec r4300_op_table[] = {
/* Pseudo opcodes
* 336
*/
NOP
NOP
};
#define E_INV { 0, 0, 0x00 }
@@ -459,7 +459,7 @@ struct r4300_op_escape {
static const struct r4300_op_escape r4300_escapes_table[] = {
/* 000000 - special */
E_SPECIAL, E_SPECIAL, E_SPECIAL, E_SPECIAL,
E_SPECIAL, E_SPECIAL, E_SPECIAL, E_SPECIAL,
/* 000001 - regimm */
E_REGIMM, E_REGIMM, E_REGIMM, E_REGIMM,
@@ -1089,16 +1089,17 @@ DECLARE_INSTRUCTION(TLBR)
DECLARE_R4300
uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);
int index;
index = cp0_regs[CP0_INDEX_REG] & UINT32_C(0x1F);
cp0_regs[CP0_PAGEMASK_REG] = r4300->cp0.tlb.entries[index].mask << 13;
cp0_regs[CP0_ENTRYHI_REG] = ((r4300->cp0.tlb.entries[index].vpn2 << 13) | r4300->cp0.tlb.entries[index].asid);
cp0_regs[CP0_ENTRYLO0_REG] = (r4300->cp0.tlb.entries[index].pfn_even << 6) | (r4300->cp0.tlb.entries[index].c_even << 3)
| (r4300->cp0.tlb.entries[index].d_even << 2) | (r4300->cp0.tlb.entries[index].v_even << 1)
| r4300->cp0.tlb.entries[index].g;
cp0_regs[CP0_ENTRYLO1_REG] = (r4300->cp0.tlb.entries[index].pfn_odd << 6) | (r4300->cp0.tlb.entries[index].c_odd << 3)
| (r4300->cp0.tlb.entries[index].d_odd << 2) | (r4300->cp0.tlb.entries[index].v_odd << 1)
| r4300->cp0.tlb.entries[index].g;
int index = cp0_regs[CP0_INDEX_REG] & UINT32_C(0x3F);
if (index < CP0_REGS_COUNT) {
cp0_regs[CP0_PAGEMASK_REG] = r4300->cp0.tlb.entries[index].mask << 13;
cp0_regs[CP0_ENTRYHI_REG] = ((r4300->cp0.tlb.entries[index].vpn2 << 13) | r4300->cp0.tlb.entries[index].asid);
cp0_regs[CP0_ENTRYLO0_REG] = (r4300->cp0.tlb.entries[index].pfn_even << 6) | (r4300->cp0.tlb.entries[index].c_even << 3)
| (r4300->cp0.tlb.entries[index].d_even << 2) | (r4300->cp0.tlb.entries[index].v_even << 1)
| r4300->cp0.tlb.entries[index].g;
cp0_regs[CP0_ENTRYLO1_REG] = (r4300->cp0.tlb.entries[index].pfn_odd << 6) | (r4300->cp0.tlb.entries[index].c_odd << 3)
| (r4300->cp0.tlb.entries[index].d_odd << 2) | (r4300->cp0.tlb.entries[index].v_odd << 1)
| r4300->cp0.tlb.entries[index].g;
}
ADD_TO_PC(1);
}
@@ -1220,8 +1221,12 @@ DECLARE_INSTRUCTION(TLBWR)
DECLARE_R4300
uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);
cp0_update_count(r4300);
cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG]))
if (cp0_regs[CP0_WIRED_REG] >= 32) {
cp0_regs[CP0_RANDOM_REG] = cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % 64;
} else {
cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG]))
+ cp0_regs[CP0_WIRED_REG];
}
TLBWrite(r4300, cp0_regs[CP0_RANDOM_REG]);
ADD_TO_PC(1);
}
@@ -1231,7 +1236,9 @@ DECLARE_INSTRUCTION(TLBWI)
DECLARE_R4300
uint32_t* cp0_regs = r4300_cp0_regs(&r4300->cp0);
TLBWrite(r4300, cp0_regs[CP0_INDEX_REG] & UINT32_C(0x3F));
int index = cp0_regs[CP0_INDEX_REG] & UINT32_C(0x3F);
if (index < CP0_REGS_COUNT)
TLBWrite(r4300, index);
ADD_TO_PC(1);
}
@@ -1247,8 +1254,12 @@ DECLARE_INSTRUCTION(MFC0)
{
case CP0_RANDOM_REG:
cp0_update_count(r4300);
cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG]))
+ cp0_regs[CP0_WIRED_REG];
if (cp0_regs[CP0_WIRED_REG] >= 32) {
cp0_regs[CP0_RANDOM_REG] = cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % 64;
} else {
cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG]))
+ cp0_regs[CP0_WIRED_REG];
}
rrt = SE32(cp0_regs[rfs]);
break;
case CP0_COUNT_REG:
@@ -1282,8 +1293,12 @@ DECLARE_INSTRUCTION(DMFC0)
{
case CP0_RANDOM_REG:
cp0_update_count(r4300);
cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG]))
+ cp0_regs[CP0_WIRED_REG];
if (cp0_regs[CP0_WIRED_REG] >= 32) {
cp0_regs[CP0_RANDOM_REG] = cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % 64;
} else {
cp0_regs[CP0_RANDOM_REG] = (cp0_regs[CP0_COUNT_REG]/r4300->cp0.count_per_op % (32 - cp0_regs[CP0_WIRED_REG]))
+ cp0_regs[CP0_WIRED_REG];
}
rrt = cp0_regs[rfs];
break;
case CP0_COUNT_REG:
@@ -1323,11 +1338,6 @@ DECLARE_INSTRUCTION(MTC0)
{
case CP0_INDEX_REG:
cp0_regs[CP0_INDEX_REG] = rrt32 & UINT32_C(0x8000003F);
if ((cp0_regs[CP0_INDEX_REG] & UINT32_C(0x3F)) > UINT32_C(31))
{
DebugMessage(M64MSG_ERROR, "MTC0 instruction writing Index register with TLB index > 31");
*r4300_stop(r4300)=1;
}
break;
case CP0_RANDOM_REG:
break;
@@ -1399,7 +1409,7 @@ DECLARE_INSTRUCTION(MTC0)
case CP0_PREVID_REG:
break;
case CP0_CONFIG_REG:
cp0_regs[CP0_CONFIG_REG] = (rrt32 & UINT32_C(0x0000000F))
cp0_regs[CP0_CONFIG_REG] = (rrt32 & UINT32_C(0x0000000F))
| (cp0_regs[CP0_CONFIG_REG] & UINT32_C(0x00008000))
| (cp0_regs[CP0_CONFIG_REG] & UINT32_C(0x7FFFFFFF));
break;
@@ -446,23 +446,32 @@ void InterpretOpcode(struct r4300_core* r4300)
case 5: /* Coprocessor 0 opcode 5: DMTC0 */
MTC0(r4300, op);
break;
case 16: /* Coprocessor 0 opcode 16: TLB */
case 2:
case 3:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15: /* Coprocessor 0 opcodes 2..3, 6..15: Reserved Instructions */
RESERVED(r4300, op);
break;
default: /* Coprocessor 0 opcode 16..31: TLB */
switch (op & 0x3F) {
case 1: TLBR(r4300, op); break;
case 2: TLBWI(r4300, op); break;
case 6: TLBWR(r4300, op); break;
case 8: TLBP(r4300, op); break;
case 24: ERET(r4300, op); break;
default: /* TLB sub-opcodes 0, 3..5, 7, 9..23, 25..63:
Reserved Instructions */
RESERVED(r4300, op);
default: /* TLB sub-opcodes 0, 3..5, 7, 9..23, 25..63: undefined */
NOP(r4300, 0);
break;
} /* switch (op & 0x3F) for Coprocessor 0 TLB opcodes */
break;
default: /* Coprocessor 0 opcodes 2..3, 5..15, 17..31:
Reserved Instructions */
RESERVED(r4300, op);
break;
} /* switch ((op >> 21) & 0x1F) for the Coprocessor 0 prefix */
break;
case 17: /* Coprocessor 1 prefix */
@@ -110,7 +110,7 @@ static void dma_pi_write(struct pi_controller* pi)
if (length >= 0x7f && (length & 1))
length += 1;
if (length <= 0x80)
length -= dram_addr & 0x7;
length = length >= (dram_addr & 0x7) ? length - (dram_addr & 0x7) : 0;
unsigned int cycles = handler->dma_write(opaque, dram, dram_addr, cart_addr, length);
post_framebuffer_write(&pi->dp->fb, dram_addr, length);
+2 -2
View File
@@ -263,7 +263,7 @@ m64p_error netplay_start(const char* host, int port)
return M64ERR_SYSTEM_FAIL;
}
l_udpSocket = NET_CreateDatagramSocket(NULL, 0);
l_udpSocket = NET_CreateDatagramSocket(NULL, 0, 0);
if (l_udpSocket == NULL)
{
DebugMessage(M64MSG_ERROR, "Netplay: UDP socket creation failed: %s", SDL_GetError());
@@ -272,7 +272,7 @@ m64p_error netplay_start(const char* host, int port)
return M64ERR_SYSTEM_FAIL;
}
l_tcpSocket = NET_CreateClient(l_resolvedAddress, port);
l_tcpSocket = NET_CreateClient(l_resolvedAddress, port, 0);
if (l_tcpSocket == NULL)
{
DebugMessage(M64MSG_ERROR, "Netplay: TCP socket connection failed: %s", SDL_GetError());
+2 -2
View File
@@ -36,8 +36,8 @@
#include <string.h>
#include <sys/types.h>
#include <zlib.h>
#include <unzip.h>
#include <zip.h>
#include <minizip/unzip.h>
#include <minizip/zip.h>
#define M64P_CORE_PROTOTYPES 1
#include "api/callbacks.h"
@@ -1,38 +1,52 @@
* text=auto
#* text=auto
# normal text files
*.6 text
*.asm text
AUTHORS text
*.awk text
*.c text
*.cfg text
*.cht text
*.cmake text
*.conf text
COPYING text
*.cpp text
CREDITS text
*.def text
*-license text
*.desktop text
.gitattributes text
.gitignore text
*.h text
*.hpp text
*.html text
*.ini text
INSTALL text
*-license text
LICENSES text
Makefile text
*.md text
*.mediawiki text
*.py text
README text
RELEASE text
*.S text
*.sh text
*.svg text
*.txt text
*.ver text
*.yml text
# windows specific text files
*.sln text eol=crlf
*.vcproj text eol=crlf
*.vcxproj text eol=crlf
*.vcxproj.filters text eol=crlf
#*.cmd text eol=crlf
#*.sln text eol=crlf
#*.vcproj text eol=crlf
#*.vcxproj text eol=crlf
#*.vcxproj.filters text eol=crlf
# binary files
*.gz binary
*.png binary
*.ttf binary
cursor.tex binary
font.tex binary
@@ -3,16 +3,12 @@ name: CXD4 RSP
on:
push:
paths-ignore:
- '**/*.md'
- '.{gitattributes,gitignore,travis.yml}'
- 'appveyor.yml,README'
- '*.md,appveyor.yml,README'
pull_request:
paths-ignore:
- '**/*.md'
- '.{gitattributes,gitignore,travis.yml}'
- 'appveyor.yml,README'
schedule:
- cron: '35 14 21 * *'
- '*.md,appveyor.yml,README'
workflow_dispatch:
jobs:
@@ -23,65 +19,34 @@ jobs:
matrix:
include:
- cc: GCC
platform: x64
bits: 64
arch: x64
- cc: GCC
platform: x86
bits: 32
arch: x86
- cc: Clang
platform: x64
bits: 64
arch: x64
- cc: Clang
platform: x86
bits: 32
name: Linux / ${{ matrix.cc }} / ${{ matrix.platform }}
runs-on: ubuntu-22.04
if: (github.event_name == 'schedule' && github.repository == 'mupen64plus/mupen64plus-rsp-cxd4') || (github.event_name != 'schedule')
arch: x86
name: Linux / ${{ matrix.cc }} ${{ matrix.arch }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- name: Get build dependencies and arrange the environment
run: |
echo "G_REV=$(git rev-parse --short HEAD)" >> "${GITHUB_ENV}"
sudo apt-get update
sudo apt-get --reinstall -y install gcc-multilib g++-multilib libc6 libc6-dev-i386
sudo ldconfig
git clone --depth 1 https://github.com/mupen64plus/mupen64plus-core.git ../mupen64plus-core
./../mupen64plus-core/.github/workflows/scripts/ci_install_ubuntu_deps.sh ${{ matrix.arch }} ${{ matrix.cc }}
- name: Build and related stuff, backup binaries
run: |
if [[ ${{ matrix.bits }} -eq 32 ]]; then export PIC="1" CPU_TUNE="-mtune=pentium4"; else CPU_TUNE="-mtune=core2"; fi
export OPTFLAGS="-O2 -flto ${CPU_TUNE}"
ORIG="$(pwd)"
CC="gcc"
CXX="g++"
if [[ "${{ matrix.cc }}" != "GCC" ]]; then
CC="clang"
CXX="clang++"
fi
${CC} --version
echo ""
git clone --depth 1 https://github.com/mupen64plus/mupen64plus-core.git ../mupen64plus-core
mkdir pkg
for SSE in none SSE2; do
echo ""
echo ":: ${{ matrix.cc }} ${{ matrix.platform }}${MSG} ::"
echo ""
make SSE="${SSE}" -C projects/unix clean
echo ""
make CC="${CC}" CXX="${CXX}" BITS="${{ matrix.bits }}" SSE="${SSE}" -C projects/unix all -j4
echo ""
make SSE="${SSE}" -C projects/unix install DESTDIR="${ORIG}/pkg/"
echo ""
ls -gG pkg/usr/local/lib/mupen64plus/mupen64plus-rsp-cxd4${LIB}.so
echo ""
ldd pkg/usr/local/lib/mupen64plus/mupen64plus-rsp-cxd4${LIB}.so
LIB="-sse2"
MSG=" / SSE2"
done
tar cvzf pkg/mupen64plus-rsp-cxd4-linux-${{ matrix.platform }}-g${{ env.G_REV }}.tar.gz -C pkg/ "usr"
export PATH="$(pwd)/../mupen64plus-core/.github/workflows/scripts:${PATH}"
export CONFIG_OVERRIDE="SSE="SSE2""
ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }}
export OPTFLAGS="-O3 -flto"
export CONFIG_OVERRIDE="SSE="none""
ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} makepkg
- name: Upload artifact
if: matrix.cc == 'GCC'
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v6
with:
name: mupen64plus-rsp-cxd4-linux-${{ matrix.platform }}-g${{ env.G_REV }}
name: ${{ env.PKG_NAME }}
path: pkg/*.tar.gz
MSYS2:
@@ -90,63 +55,54 @@ jobs:
matrix:
include:
- cc: GCC
platform: x64
arch: x64
cross: x86_64
bits: 64
env: MINGW64
- cc: GCC
platform: x86
arch: x86
cross: i686
bits: 32
name: Windows / MSYS2 ${{ matrix.cc }} / ${{ matrix.platform }}
runs-on: windows-2022
if: (github.event_name == 'schedule' && github.repository == 'mupen64plus/mupen64plus-rsp-cxd4') || (github.event_name != 'schedule')
env: MINGW32
- cc: GCC
arch: x64
cross: ucrt-x86_64
env: UCRT64
- cc: Clang
arch: x64
cross: clang-x86_64
env: CLANG64
name: Windows ${{ matrix.arch }} / MSYS2 ${{ matrix.env }} ${{ matrix.cc }}
runs-on: windows-2025
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW${{ matrix.bits }}
msystem: ${{ matrix.env }}
update: true
install: >-
git
libtool
make
mingw-w64-${{ matrix.cross }}-gcc
mingw-w64-${{ matrix.cross }}-toolchain
mingw-w64-${{ matrix.cross }}-ntldd
- name: Build and related stuff, backup binaries
run: |
echo "G_REV=$(git rev-parse --short HEAD)" >> "${GITHUB_ENV}"
if [[ ${{ matrix.bits }} -eq 32 ]]; then CPU_TUNE="-mtune=pentium4"; else CPU_TUNE="-mtune=core2"; fi
export OPTFLAGS="-O2 -flto ${CPU_TUNE}"
ORIG="$(pwd)"
CC="gcc"
CXX="g++"
${CC} --version
echo ""
git clone --depth 1 https://github.com/mupen64plus/mupen64plus-core.git ../mupen64plus-core
mkdir pkg
for SSE in none SSE2; do
echo ""
echo ":: ${{ matrix.cc }} ${{ matrix.platform }}${MSG} ::"
echo ""
make SSE="${SSE}" -C projects/unix clean
echo ""
make CC="${CC}" CXX="${CXX}" BITS="${{ matrix.bits }}" SSE="${SSE}" -C projects/unix all -j4
echo ""
make SSE="${SSE}" -C projects/unix install PLUGINDIR="" SHAREDIR="" BINDIR="" MANDIR="" LIBDIR="" APPSDIR="" ICONSDIR="icons" INCDIR="api" LDCONFIG="true" DESTDIR="${ORIG}/pkg/"
echo ""
ls -gG pkg/mupen64plus-rsp-cxd4${LIB}.dll
echo ""
ldd pkg/mupen64plus-rsp-cxd4${LIB}.dll
LIB="-sse2"
MSG=" / SSE2"
done
export PATH="$(pwd)/../mupen64plus-core/.github/workflows/scripts:${PATH}"
export CONFIG_OVERRIDE="SSE="SSE2""
ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }}
export OPTFLAGS="-O3 -flto"
export CONFIG_OVERRIDE="SSE="none""
ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }}
- name: Backup dependencies, etc...
run: |
./../mupen64plus-core/.github/workflows/scripts/ci_backup_mingw_deps.sh ${{ matrix.env }}
- name: Upload artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v6
with:
name: mupen64plus-rsp-cxd4-msys2-${{ matrix.platform }}-g${{ env.G_REV }}
name: ${{ env.PKG_NAME }}
path: pkg/*
MSVC:
@@ -155,39 +111,33 @@ jobs:
matrix:
include:
- toolset: v143
platform: x64
vs: 2022
arch: x64
- toolset: v141_xp
platform: x86
vs: 2019
name: Windows / MSVC with ${{ matrix.toolset }} / ${{ matrix.platform }}
runs-on: windows-${{ matrix.vs }}
if: (github.event_name == 'schedule' && github.repository == 'mupen64plus/mupen64plus-rsp-cxd4') || (github.event_name != 'schedule')
arch: x86
name: Windows ${{ matrix.arch }} / MSVC with ${{ matrix.toolset }}
runs-on: windows-2025
defaults:
run:
shell: cmd
steps:
- uses: actions/checkout@v3
- uses: microsoft/setup-msbuild@v1
- uses: actions/checkout@v6
- uses: microsoft/setup-msbuild@v2
- name: Pre-build arrangements for ${{ matrix.toolset }}
if: matrix.toolset == 'v141_xp'
run: |
"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\setup.exe" modify --quiet --productId Microsoft.VisualStudio.Product.Enterprise --channelId VisualStudio.17.Release --add Microsoft.VisualStudio.Component.WinXP
- name: Build and related stuff, backup binaries
run: |
for /f "tokens=1" %%R in ('git rev-parse --short HEAD') do echo G_REV=%%R>> "%GITHUB_ENV%"
set "ARCH=${{ matrix.platform }}"
if [%ARCH%] == [x86] set "ARCH=Win32"
echo.
msbuild --version
echo.
git clone --depth 1 https://github.com/mupen64plus/mupen64plus-core.git ..\mupen64plus-core
md pkg
echo.
msbuild projects\msvc\mupen64plus-rsp-cxd4.vcxproj /p:Configuration=Release;Platform=%ARCH%;PlatformToolset=${{ matrix.toolset }}
echo.
copy "projects\msvc\%ARCH%\Release\mupen64plus-rsp-cxd4.dll" pkg\
dir pkg\*.dll
set TOOLSET=${{ matrix.toolset }}
.\..\mupen64plus-core\.github\workflows\scripts\ci_msvc_build.cmd ${{ matrix.arch }}
- name: Backup dependencies, etc...
run: |
.\..\mupen64plus-core\.github\workflows\scripts\ci_backup_msvc_deps.cmd ${{ matrix.arch }}
- name: Upload artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v6
with:
name: mupen64plus-rsp-cxd4-msvc-${{ matrix.platform }}-g${{ env.G_REV }}
name: ${{ env.PKG_NAME }}
path: pkg/*
Nightly-build:
@@ -195,61 +145,21 @@ jobs:
if: github.ref_name == 'master'
needs: [Linux, MSYS2, MSVC]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
- name: Download artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v7
with:
path: binaries
- name: Get some tools
run: |
git clone --depth 1 https://github.com/mupen64plus/mupen64plus-core.git ../mupen64plus-core
sudo apt-get update
sudo apt-get -y install hashdeep
- name: Creating new artifacts and update nightly-build
run: |
mkdir pkg
cd binaries
for BIN in *; do
cd "${BIN}"
if [[ "${BIN:21:4}" == "msys" ]]; then
echo ":: Creating ${BIN}.zip"
zip -r "../../pkg/${BIN}.zip" *
elif [[ "${BIN:21:4}" == "msvc" ]]; then
echo ":: Creating ${BIN}.zip"
zip -r "../../pkg/${BIN}.zip" *
else
echo ":: Recovering ${BIN}.tar.gz"
mv *.tar.gz ../../pkg/
fi
cd ..
done
cd ../pkg
echo ""
for BIN in *; do
ls -gG ${BIN}
tigerdeep -lz ${BIN} >> ../${BIN:0:20}.tiger.txt
sha256sum ${BIN} >> ../${BIN:0:20}.sha256.txt
sha512sum ${BIN} >> ../${BIN:0:20}.sha512.txt
b2sum ${BIN} >> ../${BIN:0:20}.blake2.txt
done
mv ../*.tiger.txt .
mv ../*.sha*.txt .
mv ../*.blake2.txt .
echo ""
echo "TIGER:"
cat *.tiger.txt
echo ""
echo "SHA256:"
cat *.sha256.txt
echo ""
echo "SHA512:"
cat *.sha512.txt
echo ""
echo "BLAKE2:"
cat *.blake2.txt
echo ""
git tag -f nightly-build
git push -f origin nightly-build
./../mupen64plus-core/.github/workflows/scripts/ci_nightly_artifacts.sh
- name: Nightly-build
if: ${{ env.WORKFLOW_REV == env.CURRENT_REV }}
uses: ncipollo/release-action@v1
with:
prerelease: true
@@ -0,0 +1,166 @@
name: CXD4 RSP - Scheduled
on:
schedule:
- cron: '35 14 21 * *'
jobs:
Linux:
strategy:
fail-fast: false
matrix:
include:
- cc: GCC
arch: x64
- cc: GCC
arch: x86
- cc: Clang
arch: x64
- cc: Clang
arch: x86
name: Linux / ${{ matrix.cc }} ${{ matrix.arch }}
runs-on: ubuntu-24.04
if: github.repository == 'mupen64plus/mupen64plus-rsp-cxd4'
steps:
- uses: actions/checkout@v6
- name: Get build dependencies and arrange the environment
run: |
git clone --depth 1 https://github.com/mupen64plus/mupen64plus-core.git ../mupen64plus-core
./../mupen64plus-core/.github/workflows/scripts/ci_install_ubuntu_deps.sh ${{ matrix.arch }} ${{ matrix.cc }}
- name: Build and related stuff, backup binaries
run: |
export PATH="$(pwd)/../mupen64plus-core/.github/workflows/scripts:${PATH}"
export CONFIG_OVERRIDE="SSE="SSE2""
ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }}
export OPTFLAGS="-O3 -flto"
export CONFIG_OVERRIDE="SSE="none""
ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }} makepkg
- name: Upload artifact
if: matrix.cc == 'GCC'
uses: actions/upload-artifact@v6
with:
name: ${{ env.PKG_NAME }}
path: pkg/*.tar.gz
MSYS2:
strategy:
fail-fast: false
matrix:
include:
- cc: GCC
arch: x64
cross: x86_64
env: MINGW64
- cc: GCC
arch: x86
cross: i686
env: MINGW32
- cc: GCC
arch: x64
cross: ucrt-x86_64
env: UCRT64
- cc: Clang
arch: x64
cross: clang-x86_64
env: CLANG64
name: Windows ${{ matrix.arch }} / MSYS2 ${{ matrix.env }} ${{ matrix.cc }}
runs-on: windows-2025
if: github.repository == 'mupen64plus/mupen64plus-rsp-cxd4'
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v6
- uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.env }}
update: true
install: >-
git
libtool
make
mingw-w64-${{ matrix.cross }}-toolchain
mingw-w64-${{ matrix.cross }}-ntldd
- name: Build and related stuff, backup binaries
run: |
git clone --depth 1 https://github.com/mupen64plus/mupen64plus-core.git ../mupen64plus-core
export PATH="$(pwd)/../mupen64plus-core/.github/workflows/scripts:${PATH}"
export CONFIG_OVERRIDE="SSE="SSE2""
ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }}
export OPTFLAGS="-O3 -flto"
export CONFIG_OVERRIDE="SSE="none""
ci_build.sh ${{ matrix.arch }} ${{ matrix.cc }}
- name: Backup dependencies, etc...
run: |
./../mupen64plus-core/.github/workflows/scripts/ci_backup_mingw_deps.sh ${{ matrix.env }}
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: ${{ env.PKG_NAME }}
path: pkg/*
MSVC:
strategy:
fail-fast: false
matrix:
include:
- toolset: v143
arch: x64
- toolset: v141_xp
arch: x86
name: Windows ${{ matrix.arch }} / MSVC with ${{ matrix.toolset }}
runs-on: windows-2025
if: github.repository == 'mupen64plus/mupen64plus-rsp-cxd4'
defaults:
run:
shell: cmd
steps:
- uses: actions/checkout@v6
- uses: microsoft/setup-msbuild@v2
- name: Pre-build arrangements for ${{ matrix.toolset }}
if: matrix.toolset == 'v141_xp'
run: |
"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\setup.exe" modify --quiet --productId Microsoft.VisualStudio.Product.Enterprise --channelId VisualStudio.17.Release --add Microsoft.VisualStudio.Component.WinXP
- name: Build and related stuff, backup binaries
run: |
git clone --depth 1 https://github.com/mupen64plus/mupen64plus-core.git ..\mupen64plus-core
set TOOLSET=${{ matrix.toolset }}
.\..\mupen64plus-core\.github\workflows\scripts\ci_msvc_build.cmd ${{ matrix.arch }}
- name: Backup dependencies, etc...
run: |
.\..\mupen64plus-core\.github\workflows\scripts\ci_backup_msvc_deps.cmd ${{ matrix.arch }}
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
name: ${{ env.PKG_NAME }}
path: pkg/*
Nightly-build:
runs-on: ubuntu-latest
if: github.ref_name == 'master'
needs: [Linux, MSYS2, MSVC]
steps:
- uses: actions/checkout@v6
- name: Download artifacts
uses: actions/download-artifact@v7
with:
path: binaries
- name: Get some tools
run: |
git clone --depth 1 https://github.com/mupen64plus/mupen64plus-core.git ../mupen64plus-core
sudo apt-get update
sudo apt-get -y install hashdeep
- name: Creating new artifacts and update nightly-build
run: |
./../mupen64plus-core/.github/workflows/scripts/ci_nightly_artifacts.sh
- name: Nightly-build
if: ${{ env.WORKFLOW_REV == env.CURRENT_REV }}
uses: ncipollo/release-action@v1
with:
prerelease: true
allowUpdates: true
removeArtifacts: true
replacesArtifacts: false
tag: nightly-build
artifacts: pkg/*
@@ -1,10 +1,5 @@
*.asm
*.s
*.o
*.so
obj
*.obj
*.dll
*.exe
/projects/unix/_obj*/
/projects/unix/mupen64plus-rsp-cxd4*.so
/projects/unix/mupen64plus-rsp-cxd4*.dll
/projects/msvc/Win32/
/projects/msvc/x64/
+2 -2
View File
@@ -6,7 +6,7 @@
[subrepo]
remote = git@github.com:/mupen64plus/mupen64plus-rsp-cxd4.git
branch = master
commit = 0a4e30f56033396e3ba47ec0fdd7acea3522362a
parent = bf0d24d967baf1fcb6365aa4eca6609d2f5a9e6d
commit = 821d553fc8ab264fb04862edeff9e7bedc366946
parent = e31d8f24f5b914ec8b8341971197e3d3d2f07c18
method = merge
cmdver = 0.4.6
+9 -9
View File
@@ -13,6 +13,8 @@
* If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. *
\******************************************************************************/
#define _POSIX_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -43,10 +45,10 @@
#endif
#ifndef WIN32
static jmp_buf CPU_state;
static sigjmp_buf CPU_state;
static void seg_av_handler(int signal_code)
{
longjmp(CPU_state, signal_code);
siglongjmp(CPU_state, signal_code);
}
#endif
static void ISA_op_illegal(int signal_code)
@@ -414,7 +416,6 @@ EXPORT unsigned int CALL DoRspCycles(unsigned int cycles)
GET_RCP_REG(MI_INTR_REG) |= 0x00000001;
GET_RSP_INFO(CheckInterrupts)();
}
GET_RCP_REG(DPC_STATUS_REG) &= ~0x00000002ul; /* DPC_STATUS_FREEZE */
return 0;
case M_AUDTASK:
if (CFG_HLE_AUD == 0)
@@ -523,10 +524,6 @@ void no_LLE(void)
}
EXPORT void CALL InitiateRSP(RSP_INFO Rsp_Info, pu32 CycleCount)
{
#ifndef _WIN32
int recovered_from_exception;
#endif
if (CycleCount != NULL) /* cycle-accuracy not doable with today's hosts */
*CycleCount = 0;
update_conf(CFG_FILE);
@@ -567,13 +564,16 @@ EXPORT void CALL InitiateRSP(RSP_INFO Rsp_Info, pu32 CycleCount)
signal(SIGILL, ISA_op_illegal);
#ifndef _WIN32
signal(SIGSEGV, seg_av_handler);
struct sigaction sa = {.sa_handler = seg_av_handler};
struct sigaction prev_sa;
sigaction(SIGSEGV, &sa, &prev_sa);
for (SR[ra] = 0; SR[ra] < 0x80000000ul; SR[ra] += 0x200000) {
recovered_from_exception = setjmp(CPU_state);
int recovered_from_exception = sigsetjmp(CPU_state, 1);
if (recovered_from_exception)
break;
SR[at] += DRAM[SR[ra]];
}
sigaction(SIGSEGV, &prev_sa, NULL);
for (SR[at] = 0; SR[at] < 31; SR[at]++) {
SR[ra] = (SR[ra] & ~1) >> 1;
if (SR[ra] == 0)
@@ -36,7 +36,7 @@ m64p_error osal_dynlib_open(m64p_dynlib_handle *pLibHandle, const char *pccLibra
if (*pLibHandle == NULL)
{
char *pchErrMsg;
DWORD dwErr = GetLastError();
DWORD dwErr = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pchErrMsg, 0, NULL);
fprintf(stderr, "LoadLibrary('%s') error: %s\n", pccLibraryPath, pchErrMsg);
@@ -62,7 +62,7 @@ m64p_error osal_dynlib_close(m64p_dynlib_handle LibHandle)
if (rval == 0)
{
char *pchErrMsg;
DWORD dwErr = GetLastError();
DWORD dwErr = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pchErrMsg, 0, NULL);
fprintf(stderr, "FreeLibrary() error: %s\n", pchErrMsg);
+11 -11
View File
@@ -121,16 +121,9 @@ ifeq ("$(CPU)","NONE")
$(error CPU type "$(HOST_CPU)" not supported. Please file bug report at 'https://github.com/mupen64plus/mupen64plus-core/issues')
endif
# base CFLAGS, LDLIBS, and LDFLAGS
OPTFLAGS ?= -O3 -flto
WARNFLAGS ?= -Wall
CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -fvisibility=hidden -I$(SRCDIR)
CPPFLAGS += -DM64P_PLUGIN_API
LDFLAGS += $(SHARED)
ifeq ($(CPU), X86)
SSE ?= SSE2
ifeq ($(SSE), SSE2)
ifeq ("$(SSE)", "SSE2")
CFLAGS += -msse2
CPPFLAGS += -DARCH_MIN_SSE2
POSTFIX = -sse2
@@ -138,6 +131,16 @@ ifeq ($(CPU), X86)
CFLAGS += -mstackrealign
endif
SRCDIR = ../../
OBJDIR = _obj$(POSTFIX)
# base CFLAGS, LDLIBS, and LDFLAGS
OPTFLAGS ?= -O3 -flto
WARNFLAGS ?= -Wall
CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -fvisibility=hidden -I$(SRCDIR)
CPPFLAGS += -DM64P_PLUGIN_API
LDFLAGS += $(SHARED)
ifeq ($(NEON), 1)
CFLAGS += -mfpu=neon
CPPFLAGS += -DUSE_SSE2NEON
@@ -265,9 +268,6 @@ ifeq ($(PLUGINDIR),)
PLUGINDIR := $(LIBDIR)/mupen64plus
endif
SRCDIR = ../../
OBJDIR = _obj$(POSTFIX)
# list of source files to compile
SOURCE = \
$(SRCDIR)/su.c \
+4 -6
View File
@@ -234,7 +234,8 @@ void SP_DMA_READ(void)
i = 0;
--count;
do {
offC = (count*length + *CR[0x0] + i) & 0x00001FF8ul;
offC = ((count*length + *CR[0x0] + i) & 0x00000FF8ul)
| (*CR[0x0] & 0x00001000ul);
offD = (count*skip + *CR[0x1] + i) & 0x00FFFFF8ul;
i += 0x008;
if (offD > su_max_address) {
@@ -245,8 +246,6 @@ void SP_DMA_READ(void)
} while (i < length);
} while (count);
if ((*CR[0x0] ^ offC) & 0x1000)
message("DMA over the DMEM-to-IMEM gap.");
GET_RCP_REG(SP_DMA_BUSY_REG) = 0x00000000;
GET_RCP_REG(SP_STATUS_REG) &= ~SP_STATUS_DMA_BUSY;
return;
@@ -274,7 +273,8 @@ void SP_DMA_WRITE(void)
i = 0;
--count;
do {
offC = (count*length + *CR[0x0] + i) & 0x00001FF8ul;
offC = ((count*length + *CR[0x0] + i) & 0x00000FF8ul)
| (*CR[0x0] & 0x00001000ul);
offD = (count*skip + *CR[0x1] + i) & 0x00FFFFF8ul;
i += 0x000008;
if (offD > su_max_address)
@@ -283,8 +283,6 @@ void SP_DMA_WRITE(void)
} while (i < length);
} while (count);
if ((*CR[0x0] ^ offC) & 0x1000)
message("DMA over the DMEM-to-IMEM gap.");
GET_RCP_REG(SP_DMA_BUSY_REG) = 0x00000000;
GET_RCP_REG(SP_STATUS_REG) &= ~SP_STATUS_DMA_BUSY;
return;
@@ -110,11 +110,6 @@ void DisplayWindow::closeWindow()
m_bToggleFullscreen = false;
}
void DisplayWindow::setInitialWindowSize(u32 width, u32 height)
{
m_initialWidth = width;
m_initialHeight = height;
}
void DisplayWindow::setWindowSize(u32 _width, u32 _height)
{
@@ -17,7 +17,6 @@ public:
bool changeWindow();
bool resizeWindow();
void closeWindow();
void setInitialWindowSize(u32 width, u32 height);
void setWindowSize(u32 _width, u32 _height);
void setCaptureScreen(const char * const _strDirectory);
void setToggleFullscreen() { m_bToggleFullscreen = true; }
@@ -56,8 +55,6 @@ protected:
bool m_bAdjustScreen = false;
u32 m_buffersSwapCount = 0;
u32 m_initialWidth = 0;
u32 m_initialHeight = 0;
u32 m_width = 0;
u32 m_height = 0;
u32 m_heightOffset = 0;
@@ -90,8 +90,8 @@ bool DisplayWindowMupen64plus::_start()
_setAttributes();
m_bFullscreen = config.video.fullscreen > 0;
m_screenWidth = m_initialWidth > 0 ? m_initialWidth : config.video.windowedWidth;
m_screenHeight = m_initialHeight > 0 ? m_initialHeight : config.video.windowedHeight;
m_screenWidth = config.video.windowedWidth;
m_screenHeight = config.video.windowedHeight;
m_screenRefresh = config.video.fullscreenRefresh;
_getDisplaySize();
@@ -252,6 +252,11 @@ void DisplayWindowMupen64plus::_readScreen2(void * _dest, int * _width, int * _h
if (_dest == nullptr)
return;
u8 *pBufferData = (u8*)malloc((*_width)*(*_height) * 4);
if (pBufferData == nullptr)
return;
u8 *pDest = (u8*)_dest;
#if !defined(OS_ANDROID) && !defined(OS_IOS)
GLint oldMode;
glGetIntegerv(GL_READ_BUFFER, &oldMode);
@@ -260,7 +265,7 @@ void DisplayWindowMupen64plus::_readScreen2(void * _dest, int * _width, int * _h
glReadBuffer(GL_FRONT);
else
glReadBuffer(GL_BACK);
glReadPixels(0, m_heightOffset, m_screenWidth, m_screenHeight, GL_RGB, GL_UNSIGNED_BYTE, _dest);
glReadPixels(0, m_heightOffset, m_screenWidth, m_screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, pBufferData);
if (graphics::BufferAttachmentParam(oldMode) == graphics::bufferAttachment::COLOR_ATTACHMENT0) {
FrameBuffer * pBuffer = frameBufferList().getCurrent();
if (pBuffer != nullptr)
@@ -268,11 +273,8 @@ void DisplayWindowMupen64plus::_readScreen2(void * _dest, int * _width, int * _h
}
glReadBuffer(oldMode);
#else
u8 *pBufferData = (u8*)malloc((*_width)*(*_height) * 4);
if (pBufferData == nullptr)
return;
u8 *pDest = (u8*)_dest;
glReadPixels(0, m_heightOffset, m_screenWidth, m_screenHeight, GL_RGBA, GL_UNSIGNED_BYTE, pBufferData);
#endif
//Convert RGBA to RGB
for (s32 y = 0; y < *_height; ++y) {
@@ -287,7 +289,6 @@ void DisplayWindowMupen64plus::_readScreen2(void * _dest, int * _width, int * _h
}
free(pBufferData);
#endif
}
graphics::ObjectHandle DisplayWindowMupen64plus::_getDefaultFramebuffer()
@@ -57,11 +57,6 @@ EXPORT void CALL SetRenderingCallback(void (*callback)(int))
api().SetRenderingCallback(callback);
}
EXPORT void CALL SetInitialVideoSize(int width, int height)
{
api().SetInitialVideoSize(width, height);
}
EXPORT void CALL ResizeVideoOutput(int width, int height)
{
api().ResizeVideoOutput(width, height);
@@ -80,9 +80,6 @@ public:
void FBGetFrameBufferInfo(void *pinfo);
#else
// MupenPlus
#ifdef M64P_GLIDENUI
void SetInitialVideoSize(int width, int height);
#endif
void ResizeVideoOutput(int _Width, int _Height);
void ReadScreen2(void * _dest, int * _width, int * _height, int _front);
@@ -166,13 +166,6 @@ void PluginAPI::SetRenderingCallback(void (*callback)(int))
renderCallback = callback;
}
#ifdef M64P_GLIDENUI
void PluginAPI::SetInitialVideoSize(int width, int height)
{
dwnd().setInitialWindowSize(width, height);
}
#endif
void PluginAPI::ResizeVideoOutput(int _Width, int _Height)
{
dwnd().setWindowSize(_Width, _Height);
@@ -16,7 +16,6 @@ ViStatusChanged;
ViWidthChanged;
ReadScreen2;
SetRenderingCallback;
SetInitialVideoSize;
ResizeVideoOutput;
FBRead;
FBWrite;
@@ -68,9 +68,6 @@ extern int32_t win_width;
extern int32_t win_height;
extern int32_t win_fullscreen;
extern int32_t init_win_width;
extern int32_t init_win_height;
EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle _CoreLibHandle, void *Context,
void (*DebugCallback)(void *, int, const char *))
{
@@ -172,12 +169,6 @@ extern "C"
return M64ERR_SUCCESS;
}
EXPORT void CALL SetInitialVideoSize(int width, int height)
{
init_win_width = width;
init_win_height = height;
}
}
EXPORT int CALL InitiateGFX (GFX_INFO Gfx_Info)
@@ -23,8 +23,6 @@ static ptr_VidExt_GL_GetAttribute CoreVideo_GL_GetAttribute = NULL;
static ptr_VidExt_GL_SwapBuffers CoreVideo_GL_SwapBuffers = NULL;
// framebuffer texture states
int32_t init_win_width = 0;
int32_t init_win_height = 0;
int32_t win_width;
int32_t win_height;
int32_t win_fullscreen;
@@ -64,9 +62,6 @@ void screen_init(struct n64video_config* config)
CoreVideo_GL_SetAttribute(M64P_GL_SWAP_CONTROL, config->vi.vsync);
win_width = init_win_width > 0 ? init_win_width : win_width;
win_height = init_win_height > 0 ? init_win_height : win_height;
CoreVideo_SetVideoMode(win_width, win_height, 0, win_fullscreen ? M64VIDEO_FULLSCREEN : M64VIDEO_WINDOWED, M64VIDEOFLAG_SUPPORT_RESIZING);
}
-19
View File
@@ -71,10 +71,6 @@ m64p_handle configVideoParallel = NULL;
uint32_t rdram_size;
static ptr_PluginGetVersion CoreGetVersion = NULL;
static int initialWidth = 0;
static int initialHeight = 0;
static bool initialSizeSet = false;
void plugin_init(void)
{
CoreGetVersion = (ptr_PluginGetVersion)DLSYM(CoreLibHandle, "PluginGetVersion");
@@ -272,13 +268,6 @@ extern "C"
return M64ERR_SUCCESS;
}
EXPORT void CALL SetInitialVideoSize(int width, int height)
{
initialWidth = width;
initialHeight = height;
initialSizeSet = true;
}
}
#endif // CONFIG_GUI
@@ -337,12 +326,6 @@ EXPORT int CALL RomOpen(void)
vk_ssreadbacks = 0; // can cause desyncs
}
if (initialSizeSet)
{
window_width = initialWidth;
window_height = initialHeight;
}
plugin_init();
if (vk_init())
@@ -364,8 +347,6 @@ EXPORT void CALL RomClosed(void)
{
vk_destroy();
}
initialSizeSet = false;
}
EXPORT void CALL ShowCFB(void)
+1 -1
View File
@@ -88,7 +88,7 @@ static void sdl_init_audio_device(struct sdl_backend* sdl_backend)
{
if (!SDL_Init(SDL_INIT_AUDIO))
{
DebugMessage(M64MSG_ERROR, "Failed to initialize SDL audio subsystem.");
DebugMessage(M64MSG_ERROR, "Failed to initialize SDL audio subsystem: %s", SDL_GetError());
sdl_backend->error = 1;
return;
}
+1 -1
View File
@@ -28,7 +28,7 @@
#include <3rdParty/lzma/7z.h>
// minizip includes
#include <unzip.h>
#include <minizip/unzip.h>
#ifndef ZNGLIB_H_
/* sadly older minizip-ng versions didn't include zlib.h automatically,
* so i.e voidpf would be undefined, in newer minizip-ng versions,
-11
View File
@@ -511,17 +511,6 @@ CORE_EXPORT bool CorePluginsOpenROMConfig(CorePluginType type, void* parent, std
return open_plugin_config(type, parent, true, file);
}
CORE_EXPORT void CoreSetInitialVideoSize(int width, int height)
{
m64p::PluginApi& plugin = get_plugin(CorePluginType::Gfx);
if (!plugin.IsHooked() || plugin.SetInitialVideoSize == nullptr)
{
return;
}
plugin.SetInitialVideoSize(width, height);
}
CORE_EXPORT bool CoreAttachPlugins(void)
{
std::string error;
-3
View File
@@ -62,9 +62,6 @@ bool CorePluginsHasROMConfig(CorePluginType type);
// used plugin of given type
bool CorePluginsOpenROMConfig(CorePluginType type, void* parent = nullptr, std::filesystem::path file = "");
// sets initial video size for the video plugin
void CoreSetInitialVideoSize(int width, int height);
// attaches all used plugins
bool CoreAttachPlugins(void);
-3
View File
@@ -170,9 +170,6 @@ static l_Setting get_setting(SettingsID settingId)
case SettingsID::Netplay_ServerJsonUrl:
setting = {SETTING_SECTION_NETPLAY, "ServerJsonUrl", std::string("")};
break;
case SettingsID::Netplay_DispatcherUrl:
setting = {SETTING_SECTION_NETPLAY, "DispatcherUrl", std::string("https://dispatch.gopher64.com")};
break;
case SettingsID::Netplay_SelectedServer:
setting = {SETTING_SECTION_NETPLAY, "SelectedServer", std::string("")};
break;
-1
View File
@@ -45,7 +45,6 @@ enum class SettingsID
// Netplay Settings
Netplay_Nickname,
Netplay_ServerJsonUrl,
Netplay_DispatcherUrl,
Netplay_SelectedServer,
// Core Plugin Settings
-2
View File
@@ -29,7 +29,6 @@ bool PluginApi::Hook(m64p_dynlib_handle handle)
HOOK_FUNC(handle, Plugin, Shutdown);
HOOK_FUNC_OPT(handle, Plugin, Config);
HOOK_FUNC_OPT(handle, Plugin, ConfigWithRomConfig);
HOOK_FUNC_OPT(handle, , SetInitialVideoSize);
HOOK_FUNC(handle, Plugin, GetVersion);
this->handle = handle;
@@ -43,7 +42,6 @@ bool PluginApi::Unhook(void)
UNHOOK_FUNC(Plugin, Shutdown);
UNHOOK_FUNC(Plugin, Config);
UNHOOK_FUNC(Plugin, ConfigWithRomConfig);
UNHOOK_FUNC(Plugin, SetInitialVideoSize);
UNHOOK_FUNC(Plugin, GetVersion);
this->handle = nullptr;
-1
View File
@@ -37,7 +37,6 @@ class PluginApi
ptr_PluginShutdown Shutdown;
ptr_PluginConfig Config;
ptr_PluginConfigWithRomConfig ConfigWithRomConfig;
ptr_SetInitialVideoSize SetInitialVideoSize;
ptr_PluginGetVersion GetVersion;
private:
-11
View File
@@ -39,17 +39,6 @@ typedef m64p_error (*ptr_PluginConfigWithRomConfig)(void*, int, CoreRomHeader*,
EXPORT m64p_error CALL PluginConfigWithRomConfig(void*, int, CoreRomHeader*, CoreRomSettings*);
#endif
/* SetInitialVideoSize(int width, int height)
*
* This optional function allows a front-end to set the initial video
* size for a video plugin.
*
*/
typedef void (*ptr_SetInitialVideoSize)(int width, int height);
#if defined(M64P_PLUGIN_PROTOTYPES) || defined(M64P_CORE_PROTOTYPES)
EXPORT void CALL SetInitialVideoSize(int width, int height);
#endif
#endif // __cplusplus
#ifdef __cplusplus
-8
View File
@@ -10,7 +10,6 @@
#include "EmulationThread.hpp"
#include <RMG-Core/Emulation.hpp>
#include <RMG-Core/Plugins.hpp>
#include <RMG-Core/Error.hpp>
#ifdef _WIN32
@@ -55,19 +54,12 @@ void EmulationThread::SetNetplay(QString address, int port, int player)
this->player = player;
}
void EmulationThread::SetVideoSize(int width, int height)
{
this->videoSize = QSize(width, height);
}
void EmulationThread::run(void)
{
this->inhibitScreensaver();
emit this->on_Emulation_Started();
CoreSetInitialVideoSize(this->videoSize.width(), this->videoSize.height());
bool ret = CoreStartEmulation(this->rom.toStdU32String(), this->disk.toStdU32String(),
this->address.toStdString(), this->port, this->player);
-3
View File
@@ -16,7 +16,6 @@
#include <QSurfaceFormat>
#include <QString>
#include <QThread>
#include <QSize>
enum class VidExtRenderMode
{
@@ -38,7 +37,6 @@ class EmulationThread : public QThread
void SetRomFile(QString);
void SetDiskFile(QString);
void SetNetplay(QString address, int port, int player);
void SetVideoSize(int width, int height);
void run(void) override;
@@ -48,7 +46,6 @@ class EmulationThread : public QThread
QString address;
int port = -1;
int player = -1;
QSize videoSize;
#ifndef _WIN32
uint32_t dbusCookieId = 0;
QDBusInterface* dbusInterface = nullptr;
@@ -41,7 +41,6 @@ CreateNetplaySessionDialog::CreateNetplaySessionDialog(QWidget *parent, QWebSock
this->webSocket = webSocket;
connect(this->webSocket, &QWebSocket::textMessageReceived, this, &CreateNetplaySessionDialog::on_webSocket_textMessageReceived);
connect(this->webSocket, &QWebSocket::pong, this, &CreateNetplaySessionDialog::on_webSocket_pong);
connect(this->webSocket, &QWebSocket::connected, this, &CreateNetplaySessionDialog::on_webSocket_connected);
// prepare broadcast
broadcastSocket.bind(QHostAddress(QHostAddress::AnyIPv4), 0);
@@ -104,18 +103,6 @@ CreateNetplaySessionDialog::CreateNetplaySessionDialog(QWidget *parent, QWebSock
networkAccessManager->get(QNetworkRequest(QUrl(serverUrl)));
}
}
QString dispatcherUrl = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::Netplay_DispatcherUrl));
if (!dispatcherUrl.isEmpty() && QUrl(dispatcherUrl).isValid())
{
this->dispatcherUrl = dispatcherUrl;
QNetworkAccessManager* networkAccessManager = new QNetworkAccessManager(this);
connect(networkAccessManager, &QNetworkAccessManager::finished, this, &CreateNetplaySessionDialog::on_dispatcherRegionListDownload_Finished);
networkAccessManager->setTransferTimeout(15000);
networkAccessManager->get(NetplayCommon::GetNetworkRequest(this->dispatcherUrl + "/getRegions"));
}
}
CreateNetplaySessionDialog::~CreateNetplaySessionDialog(void)
@@ -254,18 +241,7 @@ void CreateNetplaySessionDialog::on_webSocket_textMessageReceived(QString messag
void CreateNetplaySessionDialog::on_webSocket_pong(quint64 elapsedTime, const QByteArray&)
{
if (!NetplayCommon::IsServerDispatcher(this->serverComboBox))
{
this->pingLineEdit->setText(QString::number(elapsedTime) + " ms");
}
}
void CreateNetplaySessionDialog::on_webSocket_connected()
{
if (NetplayCommon::IsServerDispatcher(this->serverComboBox))
{
this->createSession();
}
this->pingLineEdit->setText(QString::number(elapsedTime) + " ms");
}
void CreateNetplaySessionDialog::on_broadcastSocket_readyRead()
@@ -296,49 +272,6 @@ void CreateNetplaySessionDialog::on_jsonServerListDownload_Finished(QNetworkRepl
reply->deleteLater();
}
void CreateNetplaySessionDialog::on_dispatcherRegionListDownload_Finished(QNetworkReply* reply)
{
if (reply->error())
{
QtMessageBox::Error(this, "Server Error", "Failed to retrieve region list: " + reply->errorString());
reply->deleteLater();
return;
}
NetplayCommon::AddServers(this->serverComboBox,
QJsonDocument::fromJson(reply->readAll()), true);
reply->deleteLater();
}
void CreateNetplaySessionDialog::on_dispatcherServerCreate_Finished(QNetworkReply* reply)
{
if (reply->error())
{
QtMessageBox::Error(this, "Server Error", "Failed to create server: " + reply->errorString());
reply->deleteLater();
this->toggleUI(true, this->validate());
return;
}
QJsonDocument jsonDocument = QJsonDocument::fromJson(reply->readAll());
QJsonObject jsonObject = jsonDocument.object();
if (jsonObject.empty() || jsonObject.keys().empty())
{
QtMessageBox::Error(this, "Server Error", "Failed to create server: " + jsonDocument.toJson());
reply->deleteLater();
this->toggleUI(true, this->validate());
return;
}
// first item should be an address
QString address = jsonObject[jsonObject.keys().at(0)].toString();
this->webSocket->open(QUrl(address));
reply->deleteLater();
}
void CreateNetplaySessionDialog::on_serverComboBox_currentIndexChanged(int index)
{
if (index == -1)
@@ -346,23 +279,18 @@ void CreateNetplaySessionDialog::on_serverComboBox_currentIndexChanged(int index
return;
}
bool dispatcher = NetplayCommon::IsServerDispatcher(this->serverComboBox, index);
if (this->pingTimerId != -1)
{
this->killTimer(this->pingTimerId);
this->pingTimerId = -1;
}
this->pingLineEdit->setText(dispatcher ? "N/A" : "Calculating...");
this->pingLineEdit->setText("Calculating...");
if (!dispatcher)
{
this->pingTimerId = this->startTimer(2000);
this->pingTimerId = this->startTimer(2000);
QString address = NetplayCommon::GetServerData(this->serverComboBox, index);
this->webSocket->open(QUrl(address));
}
QString address = NetplayCommon::GetServerData(this->serverComboBox, index);
this->webSocket->open(QUrl(address));
}
void CreateNetplaySessionDialog::on_nickNameLineEdit_textChanged(void)
@@ -387,7 +315,7 @@ void CreateNetplaySessionDialog::on_romListWidget_OnRomChanged(bool valid)
void CreateNetplaySessionDialog::accept()
{
if (!NetplayCommon::IsServerDispatcher(this->serverComboBox) && !this->webSocket->isValid())
if (!this->webSocket->isValid())
{
QtMessageBox::Error(this, "Server Error", "Connection Failed");
return;
@@ -407,26 +335,5 @@ void CreateNetplaySessionDialog::accept()
// disable create button while we're processing the request
this->toggleUI(false, false);
if (NetplayCommon::IsServerDispatcher(this->serverComboBox))
{
QNetworkAccessManager* networkAccessManager = new QNetworkAccessManager(this);
connect(networkAccessManager, &QNetworkAccessManager::finished, this, &CreateNetplaySessionDialog::on_dispatcherServerCreate_Finished);
networkAccessManager->setTransferTimeout(120000);
QUrl url(this->dispatcherUrl + "/createServer");
QUrlQuery urlQuery;
urlQuery.addQueryItem("region", NetplayCommon::GetServerData(this->serverComboBox));
url.setQuery(urlQuery);
networkAccessManager->get(NetplayCommon::GetNetworkRequest(url));
// creating a server can take a while,
// so show a loading screen
this->romListWidget->ShowLoading();
}
else
{
this->createSession();
}
this->createSession();
}
@@ -48,8 +48,6 @@ class CreateNetplaySessionDialog : public QDialog, private Ui::CreateNetplaySess
QString sessionMD5;
QString sessionGoodName;
QString dispatcherUrl;
QString getGameName(QString goodName, QString file);
bool validate(void);
@@ -65,12 +63,9 @@ class CreateNetplaySessionDialog : public QDialog, private Ui::CreateNetplaySess
private slots:
void on_webSocket_textMessageReceived(QString message);
void on_webSocket_pong(quint64 elapsedTime, const QByteArray&);
void on_webSocket_connected(void);
void on_broadcastSocket_readyRead(void);
void on_jsonServerListDownload_Finished(QNetworkReply* reply);
void on_dispatcherRegionListDownload_Finished(QNetworkReply* reply);
void on_dispatcherServerCreate_Finished(QNetworkReply* reply);
void on_serverComboBox_currentIndexChanged(int index);
@@ -23,9 +23,6 @@
using namespace NetplayCommon;
// register type with qt
Q_DECLARE_METATYPE(NetplayServerData);
void NetplayCommon::AddCommonJson(QJsonObject& json)
{
QCryptographicHash hash = QCryptographicHash(QCryptographicHash::Sha256);
@@ -87,36 +84,14 @@ QList<QString> NetplayCommon::GetPluginNames(QString md5QString)
return pluginNames;
}
void NetplayCommon::AddServers(QComboBox* comboBox, const QJsonDocument& document, bool dispatcher)
void NetplayCommon::AddServers(QComboBox* comboBox, const QJsonDocument& document)
{
if (dispatcher)
{
QJsonArray jsonServers = document.array();
QJsonObject jsonObject = document.object();
QStringList jsonServers = jsonObject.keys();
for (int i = 0; i < jsonServers.size(); i++)
{
comboBox->addItem(jsonServers.at(i).toString(),
QVariant::fromValue<NetplayServerData>(
{
dispatcher,
jsonServers.at(i).toString()
}));
}
}
else
for (int i = 0; i < jsonServers.size(); i++)
{
QJsonObject jsonObject = document.object();
QStringList jsonServers = jsonObject.keys();
for (int i = 0; i < jsonServers.size(); i++)
{
comboBox->addItem(jsonServers.at(i),
QVariant::fromValue<NetplayServerData>(
{
dispatcher,
jsonObject.value(jsonServers.at(i)).toString()
}));
}
comboBox->addItem(jsonServers.at(i), jsonObject.value(jsonServers.at(i)).toString());
}
NetplayCommon::RestoreSelectedServer(comboBox);
@@ -137,16 +112,6 @@ void NetplayCommon::RestoreSelectedServer(QComboBox* comboBox)
}
}
bool NetplayCommon::IsServerDispatcher(QComboBox* comboBox, int index)
{
if (index == -1)
{
index = comboBox->currentIndex();
}
return comboBox->itemData(index).value<NetplayServerData>().Dispatcher;
}
QString NetplayCommon::GetServerData(QComboBox* comboBox, int index)
{
if (index == -1)
@@ -154,7 +119,7 @@ QString NetplayCommon::GetServerData(QComboBox* comboBox, int index)
index = comboBox->currentIndex();
}
return comboBox->itemData(index).value<NetplayServerData>().Data;
return comboBox->itemData(index).toString();
}
QNetworkRequest NetplayCommon::GetNetworkRequest(QUrl url)
@@ -22,12 +22,6 @@
namespace NetplayCommon
{
struct NetplayServerData
{
bool Dispatcher;
QString Data;
};
#define NETPLAYCOMMON_SESSION_REGEX "[a-zA-Z0-9 ]+"
#define NETPLAYCOMMON_NICKNAME_REGEX "[a-zA-Z0-9]+"
#define NETPLAYCOMMON_PASSWORD_REGEX "[a-zA-Z0-9,.\\/<>?;:[\\]{}\\-=_+`~!@#$%^&*()]+"
@@ -39,14 +33,11 @@ namespace NetplayCommon
QList<QString> GetPluginNames(QString md5QString);
// Adds servers from json to combobox
void AddServers(QComboBox* comboBox, const QJsonDocument& document, bool dispatcher = false);
void AddServers(QComboBox* comboBox, const QJsonDocument& document);
// Restores previously selected server
void RestoreSelectedServer(QComboBox* comboBox);
// Returns whether server from comboBox is dispatcher
bool IsServerDispatcher(QComboBox* comboBox, int index = -1);
// Returns server data from comboBox
QString GetServerData(QComboBox* comboBox, int index = -1);
@@ -71,11 +71,6 @@ NetplaySessionBrowserDialog::NetplaySessionBrowserDialog(QWidget *parent, QWebSo
this->nickNameLineEdit->setValidator(new QRegularExpressionValidator(re, this));
this->nickNameLineEdit->setText(QString::fromStdString(CoreSettingsGetStringValue(SettingsID::Netplay_Nickname)));
// configure dispatcher network access manager
this->dispatcherNetworkAccessManager = new QNetworkAccessManager(this);
this->dispatcherNetworkAccessManager->setTransferTimeout(15000);
connect(this->dispatcherNetworkAccessManager, &QNetworkAccessManager::finished, this, &NetplaySessionBrowserDialog::on_dispatcherRetrieveServers_Finished);
// request server list
QString serverUrl = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::Netplay_ServerJsonUrl));
if (!serverUrl.isEmpty())
@@ -102,18 +97,6 @@ NetplaySessionBrowserDialog::NetplaySessionBrowserDialog(QWidget *parent, QWebSo
}
}
QString dispatcherUrl = QString::fromStdString(CoreSettingsGetStringValue(SettingsID::Netplay_DispatcherUrl));
if (!dispatcherUrl.isEmpty() && QUrl(dispatcherUrl).isValid())
{
this->dispatcherUrl = dispatcherUrl;
QNetworkAccessManager* networkAccessManager = new QNetworkAccessManager(this);
connect(networkAccessManager, &QNetworkAccessManager::finished, this, &NetplaySessionBrowserDialog::on_dispatcherRegionListDownload_Finished);
networkAccessManager->setTransferTimeout(15000);
networkAccessManager->get(NetplayCommon::GetNetworkRequest(this->dispatcherUrl + "/getRegions"));
}
this->validateJoinButton();
}
@@ -215,14 +198,7 @@ void NetplaySessionBrowserDialog::refreshSessions(void)
// disable join and refresh button while refreshing
this->toggleUI(false, false, false);
if (NetplayCommon::IsServerDispatcher(this->serverComboBox))
{
this->on_serverComboBox_currentIndexChanged(this->serverComboBox->currentIndex());
}
else
{
this->on_webSocket_connected();
}
this->on_webSocket_connected();
}
void NetplaySessionBrowserDialog::joinSession(void)
@@ -241,31 +217,6 @@ void NetplaySessionBrowserDialog::joinSession(void)
this->webSocket->sendTextMessage(QJsonDocument(json).toJson());
}
void NetplaySessionBrowserDialog::resetDispatcherState(void)
{
if (this->dispatcherTimerId != -1)
{
this->killTimer(this->dispatcherTimerId);
this->dispatcherTimerId = -1;
}
if (this->dispatcherTimeoutTimerId != -1)
{
this->killTimer(this->dispatcherTimeoutTimerId);
this->dispatcherTimeoutTimerId = -1;
}
if (this->dispatcherNetworkReply != nullptr)
{
this->dispatcherNetworkReply->abort();
this->dispatcherNetworkReply = nullptr;
}
this->dispatcherMoveThroughList = false;
this->dispatcherJoinSession = false;
this->dispatcherAddressListIndex = false;
}
void NetplaySessionBrowserDialog::timerEvent(QTimerEvent *event)
{
if (event->timerId() == this->pingTimerId)
@@ -275,44 +226,6 @@ void NetplaySessionBrowserDialog::timerEvent(QTimerEvent *event)
this->webSocket->ping();
}
}
else if (event->timerId() == this->dispatcherTimerId)
{
if (this->dispatcherMoveThroughList)
{
this->dispatcherAddressListIndex++;
if (this->dispatcherAddressListIndex < this->dispatcherAddressList.size())
{
QString address = this->dispatcherAddressList.at(this->dispatcherAddressListIndex);
this->webSocket->open(QUrl(address));
this->dispatcherMoveThroughList = false;
// start socket timeout
if (this->dispatcherTimeoutTimerId != -1)
{
this->killTimer(this->dispatcherTimeoutTimerId);
this->dispatcherTimeoutTimerId = -1;
}
this->dispatcherTimeoutTimerId = this->startTimer(15000);
}
else
{
this->sessionBrowserWidget->RefreshDone();
this->resetDispatcherState();
}
}
}
else if (event->timerId() == this->dispatcherTimeoutTimerId)
{
this->webSocket->close();
this->dispatcherMoveThroughList = true;
this->killTimer(this->dispatcherTimeoutTimerId);
this->dispatcherTimeoutTimerId = -1;
}
}
void NetplaySessionBrowserDialog::on_webSocket_connected(void)
@@ -323,20 +236,8 @@ void NetplaySessionBrowserDialog::on_webSocket_connected(void)
return;
}
bool dispatcher = NetplayCommon::IsServerDispatcher(this->serverComboBox);
if (dispatcher && this->dispatcherJoinSession)
{
this->joinSession();
this->dispatcherJoinSession = false;
return;
}
// clear sessions when we're not using the dispatcher server
if (!dispatcher)
{
this->sessionBrowserWidget->StartRefresh();
}
// clear sessions
this->sessionBrowserWidget->StartRefresh();
// request session list from server
QJsonObject json;
@@ -353,8 +254,6 @@ void NetplaySessionBrowserDialog::on_webSocket_textMessageReceived(QString messa
QString type = json.value("type").toString();
bool dispatcher = NetplayCommon::IsServerDispatcher(this->serverComboBox);
if (type == "reply_get_rooms")
{
if (json.value("accept").toInt() == 0)
@@ -377,10 +276,7 @@ void NetplaySessionBrowserDialog::on_webSocket_textMessageReceived(QString messa
}
// we're done refreshing the sessions
if (!dispatcher)
{
this->sessionBrowserWidget->RefreshDone();
}
this->sessionBrowserWidget->RefreshDone();
}
else
{
@@ -401,27 +297,16 @@ void NetplaySessionBrowserDialog::on_webSocket_textMessageReceived(QString messa
this->toggleUI(true, this->validate());
}
}
if (dispatcher)
{
this->dispatcherMoveThroughList = true;
}
}
void NetplaySessionBrowserDialog::on_webSocket_pong(quint64 elapsedTime, const QByteArray&)
{
if (!NetplayCommon::IsServerDispatcher(this->serverComboBox))
{
this->pingLineEdit->setText(QString::number(elapsedTime) + " ms");
}
this->pingLineEdit->setText(QString::number(elapsedTime) + " ms");
}
void NetplaySessionBrowserDialog::on_webSocket_disconnected()
{
if (!NetplayCommon::IsServerDispatcher(this->serverComboBox))
{
this->sessionBrowserWidget->Reset();
}
this->sessionBrowserWidget->Reset();
}
void NetplaySessionBrowserDialog::on_broadcastSocket_readyRead(void)
@@ -453,61 +338,6 @@ void NetplaySessionBrowserDialog::on_jsonServerListDownload_Finished(QNetworkRep
reply->deleteLater();
}
void NetplaySessionBrowserDialog::on_dispatcherRegionListDownload_Finished(QNetworkReply* reply)
{
if (reply->error())
{
this->sessionBrowserWidget->Reset();
QtMessageBox::Error(this, "Server Error", "Failed to retrieve region list: " + reply->errorString());
reply->deleteLater();
return;
}
NetplayCommon::AddServers(this->serverComboBox,
QJsonDocument::fromJson(reply->readAll()), true);
reply->deleteLater();
}
void NetplaySessionBrowserDialog::on_dispatcherRetrieveServers_Finished(QNetworkReply* reply)
{
// don't show an error when the request has been aborted
if (reply->error() == QNetworkReply::OperationCanceledError)
{
reply->deleteLater();
return;
}
if (reply->error())
{
this->sessionBrowserWidget->Reset();
QtMessageBox::Error(this, "Server Error", "Failed to retrieve server list: " + reply->errorString());
reply->deleteLater();
return;
}
QJsonDocument jsonDocument = QJsonDocument::fromJson(reply->readAll());
QJsonObject jsonObject = jsonDocument.object();
QStringList jsonKeys = jsonObject.keys();
this->sessionBrowserWidget->StartRefresh();
this->resetDispatcherState();
// we only care about the addresses
this->dispatcherAddressList.clear();
for (int i = 0; i < jsonKeys.size(); i++)
{
QString address = jsonObject[jsonKeys.at(i)].toString();
this->dispatcherAddressList.append(address);
}
this->dispatcherAddressListIndex = -1;
this->dispatcherMoveThroughList = true;
this->dispatcherTimerId = this->startTimer(250);
reply->deleteLater();
}
void NetplaySessionBrowserDialog::on_serverComboBox_currentIndexChanged(int index)
{
if (index == -1)
@@ -518,47 +348,19 @@ void NetplaySessionBrowserDialog::on_serverComboBox_currentIndexChanged(int inde
// disable join and refresh button while refreshing
this->toggleUI(false, false, false);
bool dispatcher = NetplayCommon::IsServerDispatcher(this->serverComboBox, index);
if (this->pingTimerId != -1)
{
this->killTimer(this->pingTimerId);
this->pingTimerId = -1;
}
this->resetDispatcherState();
this->pingLineEdit->setText(dispatcher ? "N/A" : "Calculating...");
this->pingLineEdit->setText("Calculating...");
this->sessionBrowserWidget->StartRefresh();
if (dispatcher)
{
QString region = NetplayCommon::GetServerData(this->serverComboBox, index);
this->pingTimerId = this->startTimer(2000);
QUrl url(this->dispatcherUrl + "/getServers");
QUrlQuery urlQuery;
urlQuery.addQueryItem("region", region);
url.setQuery(urlQuery);
QNetworkRequest networkRequest = NetplayCommon::GetNetworkRequest(url);
// sadly we have to force HTTP/1 here due to a Qt bug,
// we abort the connection which sadly in Qt6 causes an error and eventually segfault,
// so to workaround this, force HTTP/1 until it's fixed in Qt6
//
// thank you MapLibre devs for figuring out this issue:
// https://github.com/maplibre/maplibre-native/issues/3644
networkRequest.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
this->dispatcherNetworkReply = this->dispatcherNetworkAccessManager->get(networkRequest);
}
else
{
this->pingTimerId = this->startTimer(2000);
QString address = NetplayCommon::GetServerData(this->serverComboBox, index);
this->webSocket->open(QUrl(address));
}
QString address = NetplayCommon::GetServerData(this->serverComboBox, index);
this->webSocket->open(QUrl(address));
}
void NetplaySessionBrowserDialog::on_sessionBrowserWidget_OnSessionChanged(bool valid)
@@ -589,8 +391,7 @@ void NetplaySessionBrowserDialog::on_buttonBox_clicked(QAbstractButton* button)
void NetplaySessionBrowserDialog::accept()
{
if (!NetplayCommon::IsServerDispatcher(this->serverComboBox) &&
!this->webSocket->isValid())
if (!this->webSocket->isValid())
{
QtMessageBox::Error(this, "Server Error", "Connection Failed");
return;
@@ -666,13 +467,5 @@ void NetplaySessionBrowserDialog::accept()
this->sessionData = sessionData;
if (NetplayCommon::IsServerDispatcher(this->serverComboBox))
{
this->dispatcherJoinSession = true;
this->webSocket->open(sessionData.Address);
}
else
{
this->joinSession();
}
this->joinSession();
}
@@ -48,17 +48,6 @@ class NetplaySessionBrowserDialog : public QDialog, private Ui::NetplaySessionBr
QMap<QString, CoreRomSettings> romData;
int pingTimerId = -1;
int dispatcherTimerId = -1;
int dispatcherTimeoutTimerId = -1;
bool dispatcherMoveThroughList = false;
bool dispatcherJoinSession = false;
QNetworkAccessManager* dispatcherNetworkAccessManager = nullptr;
QNetworkReply* dispatcherNetworkReply = nullptr;
QString dispatcherUrl;
QStringList dispatcherAddressList;
int dispatcherAddressListIndex = 0;
QString showROMDialog(QString name, QString md5);
@@ -84,9 +73,6 @@ class NetplaySessionBrowserDialog : public QDialog, private Ui::NetplaySessionBr
void on_broadcastSocket_readyRead(void);
void on_jsonServerListDownload_Finished(QNetworkReply* reply);
void on_dispatcherRegionListDownload_Finished(QNetworkReply* reply);
void on_dispatcherRetrieveServers_Finished(QNetworkReply* reply);
void on_serverComboBox_currentIndexChanged(int index);
void on_sessionBrowserWidget_OnSessionChanged(bool valid);
void on_sessionBrowserWidget_OnRefreshDone(void);
@@ -526,7 +526,6 @@ void SettingsDialog::loadInterfaceNetplaySettings(void)
this->netplayNicknameLineEdit->setText(QString::fromStdString(CoreSettingsGetStringValue(SettingsID::Netplay_Nickname)));
this->netplayServerUrlLineEdit->setText(QString::fromStdString(CoreSettingsGetStringValue(SettingsID::Netplay_ServerJsonUrl)));
this->netplayDispatcherUrlLineEdit->setText(QString::fromStdString(CoreSettingsGetStringValue(SettingsID::Netplay_DispatcherUrl)));
}
void SettingsDialog::loadDefaultCoreSettings(void)
@@ -680,7 +679,6 @@ void SettingsDialog::loadDefaultInterfaceNetplaySettings(void)
{
this->netplayNicknameLineEdit->setText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::Netplay_Nickname)));
this->netplayServerUrlLineEdit->setText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::Netplay_ServerJsonUrl)));
this->netplayDispatcherUrlLineEdit->setText(QString::fromStdString(CoreSettingsGetDefaultStringValue(SettingsID::Netplay_DispatcherUrl)));
}
void SettingsDialog::saveSettings(void)
@@ -916,7 +914,6 @@ void SettingsDialog::saveInterfaceNetplaySettings(void)
{
CoreSettingsSetValue(SettingsID::Netplay_Nickname, this->netplayNicknameLineEdit->text().toStdString());
CoreSettingsSetValue(SettingsID::Netplay_ServerJsonUrl, this->netplayServerUrlLineEdit->text().toStdString());
CoreSettingsSetValue(SettingsID::Netplay_DispatcherUrl, this->netplayDispatcherUrlLineEdit->text().toStdString());
}
void SettingsDialog::commonHotkeySettings(SettingsDialogAction action)
@@ -665,27 +665,6 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>Dispatcher URL</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="netplayDispatcherUrlLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_19">
<property name="orientation">
+77 -7
View File
@@ -457,6 +457,7 @@ QString MainWindow::getWindowTitle(void)
const QStringList secondWordList = {
{
" rights!!!",
" rights are human rights <3",
"s rise up!!!"
}};
@@ -601,12 +602,15 @@ void MainWindow::updateUI(bool inEmulation, bool isPaused)
// launch RMG with a ROM on the commandline or drag & drop
this->ui_Widgets->setCurrentWidget(this->ui_Widget_Dummy);
}
this->storeGeometry();
}
else if (!this->ui_NoSwitchToRomBrowser)
{
this->setWindowTitle(this->ui_WindowTitle);
this->ui_Widgets->setCurrentWidget(this->ui_Widget_RomBrowser);
this->ui_StatusBar_RenderModeLabel->clear();
this->loadGeometry();
}
else
{
@@ -619,9 +623,69 @@ void MainWindow::updateUI(bool inEmulation, bool isPaused)
void MainWindow::storeGeometry(void)
{
std::string geometryStr = this->saveGeometry().toBase64().toStdString();
if (this->ui_Geometry_Saved)
{
return;
}
this->ui_Geometry = this->saveGeometry();
this->ui_Geometry_Maximized = this->isMaximized();
this->ui_Geometry_Saved = true;
std::string geometryStr = this->ui_Geometry.toBase64().toStdString();
CoreSettingsSetValue(SettingsID::RomBrowser_Geometry, geometryStr);
CoreSettingsSetValue(SettingsID::RomBrowser_Maximized, this->isMaximized());
CoreSettingsSetValue(SettingsID::RomBrowser_Maximized, this->ui_Geometry_Maximized);
}
void MainWindow::loadGeometry(void)
{
if (!this->ui_Geometry_Saved)
{
return;
}
if (this->ui_Geometry_Maximized)
{
this->showMaximized();
}
else
{
this->restoreGeometry(this->ui_Geometry);
}
if (this->isFullScreen())
{
this->showNormal();
}
if (this->ui_ShowMenubar && this->menuBar()->isHidden())
{
this->menuBar()->show();
}
else if (!this->ui_ShowMenubar && !this->menuBar()->isHidden())
{
this->menuBar()->hide();
}
if (this->ui_ShowToolbar && this->toolBar->isHidden())
{
this->toolBar->show();
}
else if (!this->ui_ShowToolbar && !this->toolBar->isHidden())
{
this->toolBar->hide();
}
if (this->ui_ShowStatusbar && this->statusBar()->isHidden())
{
this->statusBar()->show();
}
else if (!this->ui_ShowStatusbar && !this->statusBar()->isHidden())
{
this->statusBar()->hide();
}
this->ui_Geometry_Saved = false;
}
void MainWindow::initializeEmulationThread(void)
@@ -738,8 +802,6 @@ void MainWindow::launchEmulationThread(QString cartRom, QString diskRom, bool re
this->ui_ShowStatusbar = CoreSettingsGetBoolValue(SettingsID::GUI_StatusBar);
}
this->emulationThread->SetVideoSize(this->ui_Widgets->width() * this->devicePixelRatio(),
this->ui_Widgets->height() * this->devicePixelRatio());
this->emulationThread->SetRomFile(cartRom);
this->emulationThread->SetDiskFile(diskRom);
this->emulationThread->start();
@@ -2304,7 +2366,8 @@ void MainWindow::on_NetplaySessionDialog_rejected()
void MainWindow::on_VidExt_Init(VidExtRenderMode renderMode)
{
this->ui_VidExtRenderMode = renderMode;
this->ui_VidExtRenderMode = renderMode;
this->ui_VidExtForceSetMode = true;
if (CoreSettingsGetBoolValue(SettingsID::GUI_OpenGLES))
{
@@ -2491,9 +2554,12 @@ void MainWindow::on_VidExt_ResizeWindow(int width, int height)
height += this->statusBar()->height();
}
if (this->size() == QSize(width, height))
if (!this->ui_VidExtForceSetMode)
{
return;
if (this->size() == QSize(width, height))
{
return;
}
}
if (this->isMaximized() || this->isMinimized())
@@ -2502,6 +2568,10 @@ void MainWindow::on_VidExt_ResizeWindow(int width, int height)
}
this->resize(width, height);
// we've force set the size once,
// we can safely disable it now
this->ui_VidExtForceSetMode = false;
}
void MainWindow::on_VidExt_ToggleFS(bool fullscreen)
+6
View File
@@ -63,9 +63,14 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
QLabel *ui_StatusBar_Label = nullptr;
QLabel *ui_StatusBar_RenderModeLabel = nullptr;
QByteArray ui_Geometry;
bool ui_Geometry_Maximized = false;
bool ui_Geometry_Saved = false;
bool ui_HideCursorInEmulation = false;
bool ui_HideCursorInFullscreenEmulation = false;
bool ui_NoSwitchToRomBrowser = false;
bool ui_VidExtForceSetMode = false;
bool ui_LaunchInFullscreen = false;
bool ui_QuitAfterEmulation = false;
bool ui_RefreshRomListAfterEmulation = false;
@@ -131,6 +136,7 @@ class MainWindow : public QMainWindow, private Ui::MainWindow
void updateUI(bool inEmulation, bool isPaused);
void storeGeometry(void);
void loadGeometry(void);
void initializeEmulationThread(void);
void connectEmulationThreadSignals(void);
@@ -69,11 +69,6 @@ void OGLWidget::resizeEvent(QResizeEvent *event)
return;
}
if (event->oldSize() == event->size())
{
return;
}
if (this->timerId != 0)
{
this->killTimer(this->timerId);
@@ -42,11 +42,6 @@ void VKWidget::resizeEvent(QResizeEvent *event)
return;
}
if (event->oldSize() == event->size())
{
return;
}
if (this->timerId != 0)
{
this->killTimer(this->timerId);
+1 -1
View File
@@ -1 +1 @@
v0.8.8
v0.9.0