Commit Graph
2447 Commits
Author SHA1 Message Date
Henrik Rydgård d5acc2bde1 Fix an old Android Studio annoyance 2026-10-31 00:36:07 +01:00
Henrik RydgårdandClaude Opus 5.5 dfa22e5532 Build the built-in relay server in every build system
Add aemu_postoffice's relay server to the Visual Studio, UWP, legacy
Android and libretro builds, next to the client. In the Visual Studio
projects its objects get their own names, since Common already has
Log.cpp and the client's enet.c. aemu_postoffice now leaves out pthread.h
where it isn't used, which MSVC doesn't have.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 14:48:47 +01:00
Henrik RydgårdandClaude Opus 5.5 22d009c7bb CMake: Build the relay server with the current aemu_postoffice
The server now needs enet, common.cpp and snapshot.cpp, and builds as
C++17. It takes enet's implementation from the client library.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 13:56:56 +01:00
Katharine Chui 4791043e8d build aemu_postoffice relay server module in cmake 2026-10-28 13:56:56 +01:00
Henrik RydgårdandClaude Opus 5.5 d0e3141f43 FFmpeg: Vendor what PPSSPP uses into ext/ffmpeg, drop the submodule
The ffmpeg submodule held prebuilt libraries for every platform next to the
sources (about 1.5 GB checked out), and nothing uses those any more. Only the
needed sources are copied in, from the submodule's commit, along with every
header and template they include. The path is new, so checking out commits on
either side of this works.

It's also cut down to what PPSSPP uses: the H.264, AAC, MP3, ATRAC3 and ATRAC3+
decoders, the H.264 parser, the demuxers as before, and the FFV1 encoder, AVI
muxer and file protocol for AVI dumping. config.h takes its CONFIG_ values from
FFmpeg 3.0.2's own configure run with just those enabled. The raw H.264 demuxer
stays even though nothing opens raw H.264: probing PSMF data uses it to identify
the video stream's codec, which otherwise comes out as none.

On top of that:
- The files nothing links (configure would build them, but they're never pulled
  out of the static library, going by the GCC link map) are left out: mostly
  libavutil's crypto and hashing and the alternative DCT/IDCT/FFT versions.
- libswresample isn't built, now that nothing uses it.
- The H.264 decoder only does 8-bit video, which is all the PSP has. The SPS
  parser rejects any other bit depth, and the DSP files no longer build their 9-
  to 16-bit variants, which the linker kept because the init functions pick one
  by bit depth at run time.

That's 160 files to compile instead of 277, and 391 of FFmpeg's vendored, about
5.8 MB. The README in ext/ffmpeg has what was done, the PPSSPP patches in the
copy, how to add a component, and the tradeoffs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:27:54 +01:00
Henrik RydgårdandClaude Opus 5.5 3015d1f085 Windows: Build FFmpeg from source, in a project of its own
ext/ffmpeg-build/ffmpeg.vcxproj (and UWP/ffmpeg_UWP for UWP) builds the same
sources as CMake, copied from sources.mk by update_vcxproj.py. It's optimized in
Debug too, and without /Zc:inline, since MSVC otherwise keeps references to
disabled components that FFmpeg expects the compiler to drop.

The projects that linked the prebuilt libraries reference it instead, and take
the headers from the source tree.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:27:53 +01:00
Henrik RydgårdandClaude Opus 5.5 99727d8fbc FFmpeg: Build it from source with CMake by default, without a configure step
ext/ffmpeg-build now has a committed config.h in place of the configure checks.
Assembly and arch-specific code are off, as in the --disable-asm builds of the
old prebuilt libraries, so only the platform has to be told apart, with a few
preprocessor checks. The sources are listed once in sources.mk, for every build
system to use, and build one static library. ATRAC3 and ATRAC3+ are enabled to
match the prebuilt libraries, and HAVE_AV_CONFIG_H is now defined, as FFmpeg's
own build does.

CMake builds this unless USE_SYSTEM_FFMPEG or FFMPEG_DIR is set, and no longer
looks for the prebuilt libraries in ffmpeg/. MinGW now builds it too, instead
of falling back to the system's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 22:27:53 +01:00
Henrik RydgårdandClaude Opus 5.5 d8655d8810 Android: Build the bundled zlib in the ndk-build build too
The NDK's zlib is too old for uncompress2(), which the PSAR unpacker now uses,
so link ext/zlib like the CMake build already does on Android. It's linked
whole, since the prebuilt FFmpeg libraries come after it on the link line and
libavformat uses it too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 19:11:03 +01:00
Henrik RydgårdandClaude Opus 5.5 70988aa50c ImGui: Keep the context and debugger state when Android switches away
NativeShutdownGraphics, which Android runs whenever the app loses its
surface, destroyed the imgui context and the ImDebugger along with the
device objects, so window layout and debugger state reset on return.
Now only the device objects go there (the backend already recreates
them), and the context and debugger are torn down in NativeShutdown.
Both configs are still saved on device loss, in case the app is killed
in the background.

imgui.ini is now read and written through our File API rather than
imgui's fopen, which can't reach a memstick behind a content URI.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-24 14:14:42 +02:00
Henrik RydgårdandClaude Opus 5.5 38af9939d1 Update aemu_postoffice: Fix the iOS/tvOS libretro build with Xcode 16.0
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 10:32:55 +02:00
Katharine Chui 0a3a41a46a bump aemu_postoffice
- fix client enet_host_service error logging
- client enet fix connections interrupted by memory pressure still being serviced
2026-10-21 14:19:55 +02:00
Katharine Chui 880158cfdb initial enet mode for adhocctl & aemu_postoffice relay 2026-10-21 14:19:54 +02:00
Henrik Rydgård 6015ff06a7 Merge pull request #22616 from LunaMoo/native-cso-pack
Add built-in CSO packing functionality
2026-10-19 18:53:00 +02:00
Claude 4ab1066ade ext: Add libdeflate's deflate encoder
Only the compressor (deflate_compress.c, utils.c and their headers) from
libdeflate 1.7, the version LunaMoo/maxcso vendors, for writing CSO files.
Built as part of Core on every platform. MIT license.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CCxekYqPnzNr5d8Tp1q7aZ
2026-10-19 15:58:54 +02:00
Henrik RydgårdandClaude Opus 5.5 cc0604ab54 sceAudiocodec: Atrac3 mono output from InitMono, and 0x183 for joint stereo
On hardware InitMono only takes the mono Atrac3 parameters (0x0E, 0x0F),
and the stereo ones fail with 0x807f00ff. Init fills in the context: 44100
at 0x2c, the frame size at 0x30, and the output channels at 0x34, which
InitMono sets to 1. The ME reads that at every decode, and writes a mono
layout as stereo only when it's 2. We accepted every parameter, wrote
nothing there, and always wrote stereo.

A bad frame's ctx.err is 0x183 when a joint stereo frame's second part
lacks its marker, and 0x182 for any other bad sound unit. The decoder now
reports which, and we give 0x183 where we gave 0x182
(pspautotests audio/audiocodec/at3errors).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 14:13:44 +02:00
Henrik RydgårdandClaude Opus 5.5 35e9b88d8a ImDebugger: Stronger selection and current thread colors
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 11:15:57 +02:00
Henrik RydgårdandClaude Opus 5.5 db3ec7567f UI: Use Cmd for shortcuts on Mac and iOS, not Ctrl
- Fixes #17608

Text fields took Ctrl or Cmd for copy/paste/undo; now it's Cmd on Apple
platforms and Ctrl elsewhere, as is Ctrl+F (Cmd+F) for game search. Word
delete in the search filter is Option+Backspace on Apple.

The ImGui debugger never got Super, so with ImGui's Mac Cmd/Ctrl swap,
its paste and other Ctrl shortcuts could not be reached at all on Mac.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 17:46:55 +02:00
Kevin Reinholz aa2a55c883 Add curl support to naett on FreeBSD. (Unbreaks build on dirty/live systems where curl is installed and passes find_package() check). 2026-10-15 17:18:36 -07:00
Henrik RydgårdandClaude Opus 5.5 edef36d97e Load game patches installed on a PSP, given its Fuse ID
- Fixes #12343

Killzone: Liberation's Chapter 5 DLC is an installer that the PSP turns
into ms0:/PSP/GAME/<id>/KZL.PRX, which the game loads with
sceKernelLoadModuleDNAS. That file is a PGD around a type 3 PRX (decrypt
mode 18), both keyed to the console that installed it. Its Fuse ID
gives everything needed:

- libkirk: the per-console key derivation, and KIRK commands 3, 5 and 8
  built on it (KIRK ROM per uofw, key mesh per psdevwiki).
- PrxDecrypter: type 3, which is type 5 with the first step through
  KIRK command 3 (JPCSP's PRX.java), and the DNAS PGD unwrap.
- sceKernelLoadModuleDNAS uses the new FuseID setting (or --fuse-id).
  Without one, or with the wrong one, the game falls back to its stock
  module and an OSD message says why.
- Headless --re-decrypt takes --re-decrypt-key and --fuse-id, to decrypt
  such a file offline.

Verified with a patch installed on a real PSP: the v1.20 module loads,
starts and reads CHAPTER5.BIN. Type 5 output is unchanged (an NPJH50521
PBOOT.PBP decrypts byte-identically). How to install one over PSPLink is
in docs/dnas-patches.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 18:10:08 +02:00
Henrik RydgårdandClaude Opus 5.5 f7db56628c ARM disassembler: Rewrite, cover everything the emitter can emit, add a test
The old disarm handled the integer instructions but only a fraction of VFP
and NEON, and got several of those wrong. The new one decodes ARMv7-A ARM
state with VFPv4 and NEON, printing UAL with upper case mnemonics.

The new ArmDisasm test runs every ARM emitter function through it, two
examples each. Tools/arm_disasm_check.py compares the test's output with
llvm-mc; they agree on all of it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 22:23:14 +02:00
Henrik RydgårdandClaude Opus 5.5 2aff37162b CMake: Keep glslang's HLSL frontend on Windows
MinGW/Cygwin CMake builds should match the MSVC and UWP glslang projects,
which build with ENABLE_HLSL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:17:28 +02:00
Henrik RydgårdandClaude Opus 5.5 ef8dcbf3c8 CMake: Build less, and parse less of libc++
- Define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES for clang. Before C++23, libc++
  headers pull in many others for old code's sake (<vector> brings in
  <locale>). libstdc++ and MSVC ignore it.
- Turn off glslang's HLSL frontend everywhere, not just on Android. We only
  feed it GLSL.
- Don't build spirv-cross-cpp/msl/hlsl unless something links them (only
  hlsl, on Windows).

Clean Debug build on an M-series Mac: about 11% faster.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:13:40 +02:00
Henrik RydgårdandClaude Opus 5.5 1d92e28ce0 Update SPIRV-Cross to vulkan-sdk-1.4.363.0
The old version used std::terminate without including <exception>, which
breaks once libc++'s transitive includes are off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:13:40 +02:00
Henrik RydgårdandClaude Opus 5.5 20d1a58806 CMake: Only build FreeType along with AtlasTool, its only user
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 01:06:09 +02:00
Henrik RydgårdandClaude Opus 5.5 b230f0f7ca Windows: Turn off warnings for libarmips and glslang
Same as libchdr: they're submodules we don't patch.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 19:27:19 +02:00
Henrik RydgårdandClaude Opus 5.5 b8dfcef597 Windows: Turn off warnings for libchdr
It's a submodule we don't patch, and at /W4 it produced a wall of
conversion warnings in every build.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 19:25:26 +02:00
Henrik RydgårdandClaude Opus 5.5 9f1278b5d6 iOS: Link MoltenVK statically in sideload builds too, raise target to 15.0
Sideload builds used to dlopen a bundled libMoltenVK.dylib, which was
only worth having while they could still fall back to OpenGL. Now both
variants link the static MoltenVK from ios/MoltenVK and call Vulkan
directly. PPSSPP_VK_STATIC replaces the IOS_APP_STORE checks in the
Vulkan loader (the iOS libretro core still loads at runtime).

The static MoltenVK needs iOS 15.0, so that's now the deployment target
for all iOS builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:28 +02:00
Henrik RydgårdandClaude Opus 5.5 5704b972e7 iOS: Remove OpenGL support from the app
The app now always uses the Metal view controller with Vulkan (MoltenVK),
for both the App Store and sideload builds. The GL view controller is gone,
and Common/GPU/OpenGL (except GLFeatures, used by the shader generators) and
GPU/GLES are no longer compiled for it. PPSSPP_API_ANY_GL is now off for
all of iOS except the libretro core, which still uses GLES.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:13:28 +02:00
Henrik RydgårdandClaude Opus 5.5 834c3f00ee Android: Replace OpenSL ES audio with Oboe
Oboe uses AAudio on Android 8.1+ and falls back to OpenSL ES on older
devices. Output and microphone input both go through it. The mic now gets
resampled by Oboe from the device's rate to whatever the game asks for.

New: when a stream is disconnected (headphones unplugged, Bluetooth device
going away), it's reopened on the new default device instead of going
silent.

Oboe 1.11.0 is added as a submodule, built through ext/oboe-build for CMake
and as a module in Android.mk.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 12:13:58 +02:00
Henrik RydgårdandClaude Opus 5.5 9372d4b490 sceIo: Validate PGD block sizes and dir listing state
The PGD block size comes from the file header (or a savestate), and is
used to size the block buffer and as a divisor and mask, so require a sane
power of two. On load, force the next read to fill the buffer, which isn't
saved. sceIoDread only checks the listing index against the end.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:32:06 +02:00
Henrik RydgårdandClaude Opus 5.5 f5fe32c64c Mac: Update the bundled MoltenVK to 1.4.2, raise the macOS minimum to 12.0
1.4.0 never applied dynamic blend constants, so fixed-color blends lost
their constant factor on Vulkan (the Enemy panel in Tales of Phantasia
went dark, also Block Transfer effects). MoltenVK 1.4.2 requires macOS 12.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 16:16:24 +02:00
Henrik RydgårdandClaude Opus 5.5 0b24dc7f1a naett: Complete a Windows request when WinHttpWriteData fails
The return value was ignored, and after a synchronous failure no further
callback arrives, so the request never completed. During achievement game
identification, that blocked boot indefinitely.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 10:26:00 -06:00
Henrik RydgårdandClaude Opus 5.5 8e92fef527 HTTP: Publish request completion with release/acquire ordering
HTTPRequest::completed_ was a plain bool set on the download thread, and
naett's res->complete a plain int set on its transfer thread. On ARM, the
thread polling Done() could see the flag before the result code or body
written ahead of it, and report a stale code or a partial body.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 10:26:00 -06:00
Henrik Rydgård 6f82ae1c0c Bump rcheevos 2026-10-01 10:26:00 -06:00
Henrik Rydgård e90a32d09b Upgrade VMA (Vulkan Memory Allocator) to 3.4.0
Keeps the PPSSPP hacks block at the top of the header.
2026-09-30 17:24:44 -06:00
Henrik RydgårdandClaude Opus 5.5 21ee60a8ab Build: Update xxhash to v0.8.4.
Keeps our two local changes (the ppsspp_config.h include and the ARM32
prefetch in the XXH32 loop). Hash values are unchanged.

XXH3 is much faster on ARM64 now: about 37 GB/s against 13.5 with v0.8.1
on a Snapdragon X, where our quick texture hash does 24.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 13:04:08 -06:00
Henrik RydgårdandClaude Opus 5.5 c162eb3d74 sceAudiocodec: Match hardware setup, framing, errors and timing; fix Atrac3 polarity
Checked against pspautotests audio/audiocodec, recorded on a PSP.

- Atrac3+: at3Related selects headered (mpeg.prx) or raw (libatrac3plus)
  frames, instead of sniffing for the sync word. The header's size field
  is 10 bits, as the context's. Header errors 0x211/0x213, bad frames
  0x20a, all returning SCE_AVCODEC_ERROR_INVALID_DATA with nothing read.
- The first successfully decoded Atrac3+ frame, and the first two AAC
  frames, produce no output. Checked sample-for-sample against hardware.
- Atrac3: the parameter at 0x28 selects the frame layout, as
  libatrac3plus.prx's table maps it. We used to read its low bit as a
  joint-stereo flag, which decoded mono (0x0F) streams as stereo garbage.
  AtracCtx2 had the table's fields swapped the same way.
- at3_standalone's Atrac3 output was inverted relative to the PSP's
  (sceAtrac too). Negate the IMDCT scale.
- CheckNeedMem sizes (AAC is 0x658c), codec 0x1004/0x1005, Init
  validation (AAC sample rate, Atrac3 parameter, Atrac3+ channels), and
  ReleaseEDRAM clearing edramAddr.
- Every call that reaches the ME now blocks for its measured time, and
  decode time is modelled per codec and frame size.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 16:57:41 -06:00
Henrik RydgårdandClaude Opus 5.5 761e2f3706 Remove minimp3
MP3 emulation already went through FFmpeg, leaving MiniMp3Audio dead.
The one live user was loading MP3 UI sound effects (custom achievement
sounds), which now splits the file into frames and decodes them with
the FFmpeg MP3 decoder.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 10:57:02 -06:00
Henrik Rydgård bec55c61d1 Merge pull request #22237 from hrydgard/naett-cancel
http: Make Cancel() actually stop an HTTPS transfer
2026-09-17 16:00:12 -06:00
Henrik RydgårdandClaude Opus 5 70095e458b thin3d: add sub-rectangle texture updates, use them for imgui fonts
imgui 1.92 hands the backend a list of dirty rectangles when its font atlas
grows, but thin3d could only replace a whole mip level, so every new glyph
re-uploaded the entire atlas.

Adds DrawContext::UpdateTextureRegions, taking a batch of regions so each
backend can submit them together:

- Vulkan: packs the regions into the push pool and issues a single
  vkCmdCopyBufferToImage from the init command buffer, transitioning only
  the level being written.
- OpenGL: new TEXTURE_SUBIMAGE init step. The existing sub-image path is a
  render command needing an active render pass and a texture slot, which
  doesn't fit here. Rows are packed caller-side since GLES2 lacks
  GL_UNPACK_ROW_LENGTH.
- D3D11: UpdateSubresource with a box, no staging texture needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 13:16:17 -06:00
Henrik RydgårdandClaude Opus 5 43850e7c03 Update Dear ImGui to 1.92.9b (docking)
From 947aa9c97 to 98a66d8c8 on the docking branch - the latest release there,
plus the viewport fixes that landed on top of it. The only local changes to
upstream files are the "#undef new" hack at the top of imgui.h and enabling
IMGUI_DISABLE_OBSOLETE_FUNCTIONS in imconfig.h; both re-applied.

1.92 reworked fonts and textures, so the thin3d backend now advertises
ImGuiBackendFlags_RendererHasTextures and creates/updates/destroys imgui's
textures on request instead of building the font atlas itself. ImTextureIDs
below 256 now index those, above it the per-frame temp textures as before.
thin3d can't replace part of a texture yet, so an update re-uploads the whole
thing - fine for an atlas that only changes when a new glyph appears.

Also: AddRect() swapped its thickness and flags parameters in 1.92.8.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-12 12:59:53 -06:00
Henrik Rydgård a6996f570f ImDebugger: make selected text more visible 2026-09-12 12:58:56 -06:00
Henrik RydgårdandClaude Opus 5 f9bd5682db libkirk: let C++ callers include its headers directly
kirk_engine.h and amctrl.h guard their declarations, but AES.h and SHA1.h
never did, and kirk_engine.h includes them from outside its own guard. So the
AES_* and SHA1* functions got C++ linkage in any C++ file that reached them
through there, and only linked for callers that happened to wrap the whole
header in an extern "C" of their own. Nothing had called AES_* from C++
before, so it stayed hidden until something did.

Guarding the two headers instead lets every caller include them plainly, and
the wrappers scattered around the tree come out. Both are pure declarations
over kirk_common.h's typedefs with no system headers behind them, so there's
nothing in there that shouldn't be wrapped.

kirk_engine.h also uses size_t without including anything that defines it,
which only held together because its includers happened to have it already.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 11:15:15 -06:00
Katharine Chui 2731918f05 cleanup aemu_postoffice building in cmake project files 2026-09-06 14:09:33 +02:00
Henrik Rydgård 618bdc2d72 naett: Let naettInit be called more than once
It asserted that it was the first call, so net::Init - which is documented as
safe to call repeatedly, and is - needed a bool of its own purely to guard this
one line. That's bookkeeping the library may as well do itself, and the WSA half
of the same function already says as much: it doesn't track anything because
WSAStartup counts its own references.

So a repeat call does nothing, and net::Init loses g_naettInitialized. Asking
HTTPSAvailable() twice costs nothing - on Linux it's a cached dlopen result and
everywhere else it's a constant.
2026-09-05 13:31:16 -06:00
Henrik Rydgård bc095c5e31 naett: Stop the transfer when the body writer fails it
Found reviewing the commits before this one. On Windows a short write marked the
request complete and then queued another read regardless, which is worse than it
sounds: the caller is free to close a response the moment it sees complete, so
WinHTTP carried on writing into memory that was being freed. It also meant
cancelling didn't actually stop anything there. Stop at that point, and ignore
anything raised after the request is complete.

The Apple callbacks do the same check, so the cancellation we ask for after a
failed write doesn't overwrite the error that caused it.

naettMake only asserted that its request was non-NULL, and the request
constructors do return NULL when a platform can't set one up - an unparseable
URL is enough on Windows. Asserts are compiled out in release, so that was a
null dereference. It returns NULL now, and HTTPSRequest checks for it and fails
the request instead of handing it on.

Also: a request cancelled between construction and Start() didn't carry that
into the sink, and -1000 - our own cancellation code, not naett's - was logging
"Unhandled naett error" on a perfectly normal cancel.
2026-09-05 13:22:44 -06:00
Henrik Rydgård a31ce78ff4 naett: Honour the body writer's return on Apple and Android
A writer that takes less than it was given is failing the request. Windows and
Linux already treat it that way, and it's how the default writer reports that it
couldn't grow its buffer - but the Apple and Android backends threw the result
away, so a short write silently truncated the body and still reported success.

That also means it's how a caller stops a transfer in progress, which is what
the next commit uses, so document what the return value means while we're here.
Apple cancels the data task as well, or the chunks just keep coming.
2026-09-05 13:14:55 -06:00
Henrik Rydgård a15e654f11 naett: Don't hand a closing response to backends that can't see its request
naettClose cleared res->request and then asked the backend to close the
response - but the request is what a backend needs to do that. On Windows it
owns the WinHTTP handles, so the close had nothing to work with, which is part
of why it did nothing at all. Backend first, then clear.

With that in place, the Windows close unhooks the status callback and shuts the
request handle, so a completion raised later can't write through the response
after it's freed. It stops short of a full cancel: a callback already running on
another thread isn't waited for, which needs the HANDLE_CLOSING handshake.

On Apple, invalidateAndCancel returns before the session has finished with its
delegate, so clear the delegate's pointer back to the response first and have
didReceiveData check it, the way didCompleteWithError already did.

Android is still the only backend that genuinely cancels and waits, so naett.h
now says a response should be complete before it's closed rather than leaving
that to be discovered.
2026-09-05 12:07:03 -06:00
Henrik Rydgård da2f30ad46 naett: Stop leaving threads attached to the JVM, and check JNI results
getEnv called AttachCurrentThread and nothing detached. processRequest gets away
with it by detaching its own thread at the end, but naettPlatformInitRequest and
naettPlatformFreeRequest run on whatever thread the caller is using - and a
thread that exits while still attached takes the process down on Android, with
"Native thread exiting without having called DetachCurrentThread". They now
attach only when the thread isn't already, and detach on the way out.

pthread_create's result was ignored. Without a worker nothing ever sets
res->complete, so the caller polls naettComplete forever.

getOutputStream throws for anything from a refused connection onwards, and the
calls after it ran with that exception still pending, which most of JNI doesn't
allow. GetMethodID also returns NULL for a method it can't find, and calling
with a NULL jmethodID aborts the VM - so the call helpers check. Same for a
header whose value list is empty, which handed GetStringUTFChars a null.

Checked with the NDK's clang; the other backends were syntax-checked the same
way, against stub headers.
2026-09-05 12:04:31 -06:00
Henrik Rydgård 60919a2ba9 naett: Fix the curl worker's pipe read, queueing and handle cleanup
The worker reads a queued CURL* out of a pipe eight bytes at a time, tracking
how much it has so far - but it always read into the start of the buffer rather
than at that offset. A short read would have resumed mid-pointer and eventually
handed curl_multi_add_handle a spliced pointer. It never bit because a write
that size to a pipe is atomic, so reads are all-or-nothing, but the code is
written as though it isn't.

The write that queues the request was unchecked. If it ever failed, the request
was never run and never completed, and the caller sits in naettComplete
forever. It reports the failure now, and retries on EINTR.

curl wants an easy handle out of its multi before cleanup; that needs
curl_multi_remove_handle, which meant adding it - and curl_multi_cleanup, for
the init failure paths that leaked the multi handle and the pipe - to the dlopen
table.

workerRunning is written by the worker and read by the request path, so it's an
atomic rather than a plain int. And the read/write callbacks passed the body
callbacks' int return straight back to curl, which takes a size_t: a negative
came through as an enormous count rather than the error it was.
2026-09-05 12:02:22 -06:00