Compare commits

..
Author SHA1 Message Date
Codex dd97d63021 record: prime live MediaCodec AAC without publishing calibration
Gate source PCM until measured delay determines the silent input boundary. Drop complete calibration packets before output, retain silent AAC preroll and shift video/source ends to the same epoch. Preserve the existing MP4 calibration/edit path and stable descriptors across output backpressure.

Strict C89 Windows suite passes; calibrated fixtures cover six delays, empty PCM, output backpressure and early cancellation. Native MediaCodec and network session activation still require acceptance.
2026-11-12 19:17:26 +01:00
Codex d7446f1609 record: define calibrated AAC live-stream source epochs
Calculate packet-aligned source input and coded origins from measured encoder delay, retaining two silent AAC frames before source audio and rounding the matching video epoch upward. Keep file edit semantics separate.

Check all delays 0-4096 at six rates. Independently encode/decode synthetic AAC-LC at 44.1/48 kHz mono/stereo, recover delay from the chirp, omit calibration packets and compare source PCM against continuous decoding. This is not MediaCodec device acceptance or dispatcher activation.
2026-11-12 19:13:23 +01:00
Codex baa6ff33c2 record: bridge MediaCodec packet borrows to shared RTMP output
Add a bounded I/O-worker output adapter with cold mux setup, serialized packet admission, transport backpressure, normal drain and cancellation retirement. Keep the MediaCodec descriptor live until both shared mux and RTMP transport borrows retire.

The controller must establish live AAC priming and source epochs separately; this adapter does not enable Android streaming or reuse MP4 calibration edits. Strict C89 Windows contract suite passes with the actual shared FLV mux and mocked transport.
2026-11-12 19:08:49 +01:00
Codex 9e333a55eb record: build shared FLV configuration from MediaCodec setup data
Create worker-side AVC/HEVC/AV1 mux configuration from bounded native CSD and optional AAC setup. Join separate AVC parameter sets only during cold setup, retain neither native format nor encoder metadata, and preserve microsecond packet clocks without rebasing. Reuse shared Annex B and AV1 mux validation, including codec-specific packet budgets and depth/color admission.

Add actual shared-mux fixtures for configuration copying, separate/combined AVC setup, AAC payload borrowing, microsecond timestamps, codec depth mismatch and early packet-budget refusal. This does not enable the unfinished Android network publisher.
2026-11-12 19:03:57 +01:00
Codex 5aaea68b34 record: retain MediaCodec output across consumer backpressure
Add a copied worker-owned output callback table, with the existing file mux as the default adapter. Retain one immutable native packet descriptor per encoder while output is BUSY; calibrate AAC only once for that packet and release the codec borrow only after output acceptance. Worker free waits for bounded output retirement before encoder destruction is allowed.

Exercise no-repump/no-early-release behavior, callback-table ownership, delayed cancellation retirement and calibrated coded/source clocks under BUSY. The strict-C89 native MinGW MediaCodec suite passes. Android network publisher integration remains unfinished.
2026-11-12 19:03:57 +01:00
Codex 28579236c9 record: expose bounded borrowed MediaCodec video configuration
Allow the owning worker to inspect native csd-0/csd-1 buffers before constructing shared stream output. Bound each buffer to 65535 bytes, clear outputs on refusal and preserve encoder state for optional/malformed metadata. No dequeue, release, allocation or encoded-payload copy occurs. Audio and stopped encoders cannot expose video configuration.

Cover separate CSD borrows, missing/malformed/oversized metadata, invalid arguments, audio refusal and stopped-state invalidation. Streaming remains disabled pending output/publisher and lifecycle integration.
2026-11-12 19:03:57 +01:00
Codex a4d0a85439 record: exercise native NVENC configurable ring sizes
Run required native H.264 direct and GPU-capture checks with 4, 8 and 16 slots. Check slot bounds and wrap against the configured capacity while keeping the held-input fence test independent of ring size.

Validation: native Windows D3D12/NVENC six-case matrix passed; 144 pictures independently decoded with pixel, timestamp and keyframe checks.
2026-11-12 19:03:09 +01:00
Codex 0d1f649a2d tests: exercise native Metal streaming with bounded queue sizes
Run direct and threaded frontend cases with two, four, eight and sixteen capture slots. Save the requested configuration in acceptance results and verify it when explicitly selected. Preserve offline verification of earlier artifacts without the setting. Revalidated all eight saved native cases and 1137 decoded pictures with the updated verifier.
2026-11-12 18:53:15 +01:00
Codex e3120a9c87 tests: verify requested NVENC capture ring sizes at initialization
Exercise the production frontend initializer with capture allocation intercepted. Check every supported ring size and the invalid-value fallback, and tie the test to both queue capacity constants. This validates configuration forwarding without claiming native GPU allocation or encoding acceptance.
2026-11-12 18:53:15 +01:00
Codex 54a6926a3f record: align native buffer settings with bounded capture rings
Use the same native queue limits in menu ranges and backend initialization. Honor the selected NVENC ring size instead of fixing it at four. Keep invalid legacy values on the existing four-slot fallback and restore legacy ranges when switching away from native recording.

Validate native ranges and fallback values, driver changes and actual Metal initializer slot configurations in the portable frontend tests.
2026-11-12 18:53:15 +01:00
Codex 094d4192aa menu: filter native streaming services by TLS availability
Keep the built-in RTMPS-only Facebook and Kick choices out of native plaintext-only builds while preserving FFmpeg protocol choices. Navigate the finite set of supported modes in both directions without timers or backend work. Exercise the actual dropdown filter and actions in plain and TLS variants; the previous capability predicate fails the no-TLS fixture.
2026-11-12 18:53:15 +01:00
Codex df859b6ca0 menu: expose supported native recording and streaming controls
Query registered recording backends instead of restricting the shared Recording page and Quick Menu start action to FFmpeg. Native GPU recorders expose their custom profiles, GPU capture, frame-drop and queue controls; compiled Metal/NVENC RTMP providers also expose service, profile and URL settings. Keep unsupported quality/software controls hidden and omit the UDP Local mode for RTMP-only backends in dropdowns and left/right navigation. Capability queries do no device, codec or network work.

The production settings-list, Quick Menu predicate and setting actions pass a shared native capability fixture on Windows and Linux sanitizers. The actual old settings-list body and old start predicate each fail it. Full application/native build checks remain required before publication.
2026-11-12 18:53:15 +01:00
LibretroAdmin 4206fe8f52 streams, zip, vfs: reads at offsets through intfstream and the zip reader; Windows second handle from ntdll
intfstream_read_at reads at an offset and leaves the stream's position
where it was: files and memory streams without touching the stream, so
from several threads at once, CHD tracks by seeking there and back. The
buffered stream reads from the file and keeps its window, and its own
refills and wide reads go to the file at the offset. memstream_read_at
backs memory streams. The CUE/ISO serial detectors read their probes
this way.

The zip reader takes its end records, directory, local headers and
member chunks with one read at the offset each, where the file has
them; the reads the stream lends from stay as they are.

The Windows second handle is opened relative to the stream's own with
an empty name through ntdll's NtCreateFile on every NT. From Vista on
that is what ReOpenFile does; before it the handle is kept only when
its volume and file index match the stream's, else the path reopen
follows.

Lanes: intfstream_read_at_test (file, buffered, memory; four threads),
vfs_positional_ntverify_test (the check, on any version); archive_zip64
counts reads at offsets as reads through the stream.
2026-11-12 18:40:38 +01:00
LibretroAdmin 72e8b02333 vfs: Windows positional I/O on every NT, whatever _WIN32_WINNT
The second handle positional reads and writes go through is found at
run time: ReOpenFile from kernel32 where it is exported (Vista on), and
before it the stream's path opened again, kept only when the volume
serial and file index match the stream's handle. 9x/ME, whose ReadFile
takes no offset on a file, answer -1 as before. Builds for a
_WIN32_WINNT below 0x0600, the MSVC projects among them, now have it.

vfs_positional_pathreopen_test runs the positional test through the
path reopen (VFS_WIN32_FORCE_PATH_REOPEN, scoped to its object); on
Windows it and vfs_positional_nommap_test fail on a -1, and the
cross-libc Windows job runs both.
2026-11-12 17:58:30 +01:00
Codex 4ef383d2da tests: preserve coded frame counts in native streaming verification
FFmpeg default output synchronization filled three presentation gaps in the native frontend capture: 156 packets became 159 decoded output frames. Decode with passthrough timestamps and retain the exact packet/picture check. The saved native H264 stream passes independent Linux pixel, AAC, clock and history checks. Allow saved-case verification and focused frontend dispatches; default CI still runs every native gate.
2026-11-12 17:12:00 +01:00
Codex 655c5b53fe tests: exercise real Metal frontend stream initialization
Drive the production initializer through strict profiles and worker admission. Cover the packet-reader limit that previously broke first-sample binding, fractional and mono audio clocks, video-only operation, PQ/scRGB configuration and file/stream ownership. The actual old initializer fails this fixture; the corrected source passes on native MinGW and Linux ASan/UBSan. These are configuration contracts, not native HDR rendering acceptance.
2026-11-12 17:00:30 +01:00
Codex 3685e90f99 record: reject impossible packet bounds before Metal stream startup 2026-11-12 16:48:00 +01:00
Codex f10151d1c8 record: share the native packet capacity limit with Metal streaming 2026-11-12 16:46:17 +01:00
Codex fc99649544 record: preserve native streaming startup failure stage 2026-11-12 16:41:42 +01:00
Codex 677c749aad tests: preserve RTMP receiver negotiation diagnostics 2026-11-12 16:37:28 +01:00
Codex e7d01e8638 record: observe flushed frontend stream logs and command replies
Mac terminal logging is buffered while the frontend remains running. Use its explicit flushed log file, require the streaming command acknowledgement and wait for independently received output before measuring the active interval. Enable the builtin player so the stream-history exclusion is exercised.
2026-11-12 16:31:45 +01:00
Codex 04c6866bcb record: connect native Metal streaming to the frontend
Admit RTMP/RTMPS URLs and strict FLV/AAC profiles, preserve fractional source audio clocks and choose supported AAC output rates. Enable the built-in transport by default for eligible networked Metal/AVFoundation builds while respecting explicit feature disables.

Keep stream URLs out of recording history and avoid retaining a redundant frontend URL copy. Release the worker startup URL after the client adopts it and keep unsupported SDK builds on the existing fallback.

Add portable profile/source-clock checks and native application start/stop acceptance for direct/threaded AVC, fractional HEVC, mono and video-only output. Native acceptance remains required before landing.
2026-11-12 16:25:20 +01:00
Codex 7c0a5c3a5b config: keep include parsing within the shared stack budget
Stage resolved include paths off the parser stack and let the include list adopt the parsed path instead of duplicating it. Enforce the 2 KiB frame limit for the parser and its fixture with GCC and Clang.

Move the fixture corpus off the stack and use portable working-directory paths and basename handling so the include/ownership suite runs on native Windows as well as Linux.
2026-11-12 16:13:44 +01:00
Codex 0042a8cca7 record: hold accepted Metal work across stream cancellation
Exercise direct TCP and TLS/SRC cancellation with an accepted command buffer left uncommitted. Require the dispatcher to retain native ownership until renderer submission and finish retirement afterward. Keep truncated cancellation output separate from decode-quality results.
2026-11-12 15:51:20 +01:00
Codex 6fc9ae8ee8 record: exercise DNS completion after Metal dispatcher retirement
Hold the real resolver callback until cancellation frees its dispatcher, then run it after descriptor reuse under native ASan/UBSan. Reject a borrowed-wake mutant through a use-after-free report.

Use the supported Metal frontend build dialect in full link checks: C89_BUILD intentionally excludes Metal in the generated make configuration. Native worker fixtures retain their strict C89 checks.
2026-11-12 15:48:19 +01:00
Codex c9260506ee record: drive Metal streaming on the owned I/O worker
Own RTMP setup and socket/producer readiness on the existing dispatcher. Retain independent DNS completion notifications through cancellation, keep native audio conversion on the worker and service the connection before first-picture format binding. Preserve file-mode Mach scheduling and producer retirement barriers.

Add independent native receiver coverage of the URL-owning worker and full network-enabled frontend links with and without threads. Native acceptance remains a gate before frontend streaming selection.
2026-11-12 15:44:20 +01:00
Codex 92656bf572 net: retain independent wake references for late notifications
Duplicate both pipe ends so an asynchronous completion can outlive its canceled owner without SIGPIPE or waking a replacement job after descriptor reuse. Cover allocation, dup and descriptor setup rollback as well as owner retirement.
2026-11-12 15:33:19 +01:00
Codex 9ce6ee410f net: add a nonblocking pollable producer wake
Provide a POSIX close-on-exec wake pipe so an I/O worker can poll producer notifications together with sockets. Keep queue epochs with the caller; notifying and acknowledging never wait. Exercise real descriptor flags, saturation, combined socket readiness, EINTR and partial-startup rollback, with explicit unsupported admission on other platforms.
2026-11-12 15:13:39 +01:00
Codex 42270588f9 record: exercise Metal streaming factory over real RTMP
Capture renderer textures through the shared native factory and independently check received BT.709 pixels, cropped P010 geometry, explicit frame clocks and aligned audio. Cover direct and fractional/downsampled audio over TCP and TLS under ARC and manual reference counting. Calibrate the receiver oracle against FFmpeg RGB conversion and reject stale frames, bad priming and missing video epochs.
2026-11-12 14:53:05 +01:00
Codex d391508ed1 record: bind native VT streaming to Metal session startup
Adopt fresh AAC/SRC and a ready RTMP client with the renderer-selected video resources. Retain the first encoded format through output retirement, keep producer closure independent, and clean up both adopted and unbound resources. Cover drain, borrowed SRC cancellation, missing format and cancellation before binding with SDK stand-ins; reject early format release.
2026-11-12 14:43:07 +01:00
Codex 1ab9e85b97 record: share Metal cold startup with owned outputs
Let an output binder adopt the existing renderer-selected encoder, aligned pool and capture. Keep file recording on that bootstrap and verify null/refused binders retire their cold resources before the next successful startup.
2026-11-12 14:43:07 +01:00
Codex 807245ddcd record: check native streaming cancellation retirement
Stop direct and resampled owned sessions after native output starts. Require complete GPU slot retirement and session teardown; keep truncated cancellation output separate from receiver quality acceptance.
2026-11-12 14:43:07 +01:00
Codex 696d89e08c record: exercise native VT streaming through the owned session
Bind the retained first-sample format to the real output adapter and let the session retire AAC/SRC, network, encoder and queue resources. Independently receive and decode direct-rate, fractional-rate and downsampled owned outputs alongside the existing cases.
2026-11-12 14:43:07 +01:00
Codex 33e757d6be record: bind VT streaming resources to the owned output lifecycle
Adopt the READY client and fresh AAC/SRC encoders only after successful stream construction. Retire network-held packet loans before source-converter reset, AAC disposal and connection destruction. Reuse the session owner for producer closure and native encoder/GPU teardown. Cover normal drain, cancellation, reset/dispose retry and exactly-once ownership through the real controller and stream pump.
2026-11-12 14:43:07 +01:00
Codex f79bba8e95 net: keep RTMP handshake offsets inside their array
Subtract the C1 length before forming the C2 pointer. The previous left-associative expression formed an out-of-bounds intermediate pointer. Run the fragmented handshake test under Clang ASan/UBSan in CI and make undefined-behavior findings fatal.
2026-11-12 14:36:09 +01:00
Codex adf99cb980 record: reuse owned Metal session retirement for streaming outputs
Allow an output-owned producer pump to bind the first encoded sample and expose network readiness without adding network dependencies to the file session. Preserve producer closure barriers, output borrow retirement, encoder teardown and GPU/queue cleanup. Keep the existing file-output path and test both lifecycles.
2026-11-12 13:31:59 +01:00
Codex 46053b4a54 record: feed resampled PCM directly to VT streaming AAC loans
Borrow a fresh source-rate adapter without another PCM ring. Preserve output-rate AAC timestamps and return unused writable loans on BUSY or EOF. Leave source-converter retirement with its caller after stream cancellation. Exercise separate source/output clocks, ownership and concurrent production, and add real fractional/up/downsampled RTMP receiver cases.
2026-11-12 13:20:04 +01:00
Codex cbc415f2ba record: lend queued PCM to native sample-rate conversion
Convert core sample rates to the AAC clock on the recording worker. Borrow queue input until the next native callback and write directly into caller-owned AAC input loans. Keep AAC priming in the output-rate domain. Cover retirement, cancellation, EOF and format contracts, with native independent PCM/AAC timing checks.
2026-11-12 13:10:15 +01:00
Codex b376553a31 Merge validated native streaming coverage from master 2026-11-12 12:48:10 +01:00
Codex 4ac90ce105 record: require live receiver to reject a missing video epoch 2026-11-12 12:39:56 +01:00
Codex 519d06c0f6 record: exercise native Apple audio and video over RTMP
Receive native VT/AAC output independently over TCP and TLS, checking pictures, source timestamps, priming and exact lease retirement before frontend streaming activation.
2026-11-12 12:39:56 +01:00
Codex 78f4141ad7 record: consume prebuffered PCM directly in VT streaming
Borrow a caller-owned PCM queue so audio can precede the first native video format without a second ring. Copy queued spans directly into the native AAC input loan, preserve source timestamps and retire partial queue loans on cancellation. Exercise admission, prebuffering, chunk boundaries, cancellation and concurrent producers; extend the native receiver fixture with pre-format PCM.
2026-11-12 12:33:29 +01:00
Codex 31f517fcd7 Merge commit '323d3a2367' into HEAD 2026-11-12 12:27:00 +01:00
Codex 323d3a2367 record: lend contiguous queued PCM to the native mux
Keep contiguous PCM in the SPSC ring until acknowledgement and retain bounded staging for wrapped packets. The AVFoundation worker and cancellation drain use this offer; existing copy-based callers retain their queue-capacity behavior. Exercise loan identity, producer pressure, wrapped headers/payloads, EOF and concurrent packet retirement.
2026-11-12 12:18:45 +01:00
Codex 2850df3006 test: receive HEVC and AAC over RTMP with reordered pictures
Teach the independent native transport fixture HEVC sequence headers and enhanced composition offsets. Check truthful codec advertisement, packet hashes, DTS/PTS and decoded audio/video over TLS 1.2 and 1.3 on Linux and MinGW/Wine. The old fixture rejects the HEVC sequence header before this change.
2026-11-12 12:03:39 +01:00
Codex 6b24a2bcf5 record: require live receiver to reject a missing video epoch 2026-11-12 11:54:48 +01:00
Codex 5c6b76a0eb record: exercise native Apple audio and video over RTMP
Receive native VT/AAC output independently over TCP and TLS, checking pictures, source timestamps, priming and exact lease retirement before frontend streaming activation.
2026-11-12 11:49:46 +01:00
Codex d8bd21a89e record: use canonical AudioToolbox AAC-LC format flags
The AAC-LC format ID uses no flags; the MPEG-4 object type belongs in ASC, not the stream-description flags. Native macOS returns zero and was rejected by the old admission check. Model that canonical result independently and keep rejecting wrong flags, codec, rate, channels, frame count and property size.
2026-11-12 11:03:48 +01:00
Codex edd39fca42 test: isolate native AAC acceptance and retain converter diagnostics
Run AudioToolbox acceptance separately from long GPU/frontend cases so native format failures return promptly. Keep diagnostic property calls in the test harness, with unchanged native arguments and preserved stderr. A focused dispatch runs only this component gate; skipped GPU/frontend jobs are not acceptance passes.
2026-11-12 10:59:34 +01:00
Codex dc09718372 test: cover AudioToolbox contracts with clang in C89 mode
Match the existing VideoToolbox sample flags for the project's intentional clang _Bool spelling. GCC-only portable checks missed this C89 test compilation failure; run clang explicitly in the Linux recording job as well.
2026-11-12 10:55:17 +01:00
Codex be70b9eaea record: connect native Apple packets to FLV and RTMP
Service borrowed VideoToolbox and AudioToolbox producers through the shared FLV mux and READY RTMP client. Require explicit compatible output epochs for native AAC priming and retain payload loans until network retirement. Keep native encoder ownership with the caller during failure cleanup. Add independent wire-clock, backpressure, cancellation and concurrent PCM-producer contracts. Frontend selection remains gated on native Apple acceptance.
2026-11-12 10:45:16 +01:00
Codex ec1fc806d5 record: expose borrowed native AVC and HEVC configuration
Read bounded avcC/hvcC records from the retained-by-caller format and keep header bytes borrowed during cold mux setup. Reject absent or malformed atom values before accessing CFData. Gate native exported headers through the shared FLV mux alongside packet loan checks.
2026-11-12 10:32:50 +01:00
Codex 12ff7f9842 record: borrow VideoToolbox encoded packet views
Keep contiguous CoreMedia payloads borrowed until the streaming consumer returns them. Reserve bounded setup storage for fragmented blocks and reject changed formats, inexact clocks and premature reuse. Exercise the view in portable ownership contracts and the native Metal encode pipeline; frontend streaming selection remains unchanged.
2026-11-12 10:25:03 +01:00
Codex d7f44832a6 record: add worker-owned AudioToolbox AAC packet encoding
Lend two fixed PCM buffers so native callback retention cannot race reuse. Borrow raw AAC output until release, preserve source-frame timestamps, expose priming and final input counts, and keep cancellation ownership explicit.

Add portable callback/loan fault contracts and a native AAC decode oracle with retained failure artifacts. This foundation does not enable Apple streaming; native format, priming and PCM acceptance must pass before landing.
2026-11-12 10:13:38 +01:00
Codex a232f38143 record: accept borrowed AV1 OBUs in the stream adapter
Use the AV1 native-header mux and submit sized temporal units without Annex B conversion. Keep source color and depth validation and retain worker packets until network release.

Cover rejected configuration, backpressure and video-only drain. Frontend AV1 streaming remains disabled until native encoder acceptance passes.
2026-11-12 09:41:00 +01:00
LibretroAdmin edeecb928d vfs: reads at offsets for chd, data_transfer, rdvd and RVZ; pread on every Unix
chd_stream's decode-ahead thread reads the reader's own file with
filestream_read_at instead of opening a second one, and both sides'
rchd pumps take each range in one call. data_transfer_read_at, the DVD
VFS service and the RVZ hashing reader do the same; each still seeks
and reads where read_at answers -1.

pread/pwrite are used on the Unix hosts by name rather than by
HAVE_MMAP, so Android and the other builds without a file mapping have
positional I/O. An offset a narrow off_t cannot hold is refused, so the
caller seeks, rather than cast to a lower one; 32-bit Android from API
21 takes pread64/pwrite64.

Lanes: vfs_positional_nommap_test (no mmap, positional I/O required),
vfs_positional_off32_test (32-bit offsets on a 64-bit host),
chd_stream_ahead_test counts one open per stream, rdvd_vfs_test runs
both paths; the I/O-counting tests count reads at offsets too.
2026-11-12 09:26:08 +01:00
LibretroAdmin 48b9aef3f4 retro_cas_record: read, work on a copy, put it back only if it changed
retro_cas_record_read_versioned copies the record out and says which
record that was; retro_cas_record_replace puts a value in its place if
it still is that one. For a caller whose reads mostly change nothing:
such a read is two loads and a copy and writes nothing shared, where
begin and abort take a slot and give it back (two atomic writes) for
the same outcome. A caller that did change its copy puts it back with
one compare-and-swap, without doing its work a second time.

begin and commit stay for changes made in place.

retro_cas_record_test: half the twenty threads now change the record
this way, the other half in place, on the same record. With replace
made to ignore the version the total falls short by a third.
2026-11-12 09:10:44 +01:00
LibretroAdmin 8d85478046 libretro-common: retro_cas_record, a record several threads change with no lock
retro_seqlock is for one writer. This is for the case a mutex is
usually taken for: read-change-write of a small record from several
threads. Nobody writes the record in place: a change is made on a copy
in a spare slot and swapped in with one compare-and-swap; a thread
that lost the race makes its change again on the newer record. Readers
copy the record out and never wait. One of the racing threads always
gets through, so a thread that is preempted or stopped mid-change
holds nobody up.

Header only, C89, the storage the caller's (16 slots). Where there is
no lock-free compare-and-swap RETRO_CAS_RECORD_LOCK_FREE is not
defined and the caller keeps its own serialisation.

retro_cas_record_test: twenty threads (more than there are spare
slots) make 100000 changes each to one record while three threads copy
it. No change lost, each thread's count exact, no copy torn or going
back, aborts leave no trace, every spare slot given back. With the
compare-and-swap replaced by a plain store the total comes out at a
fraction. Runs in the libretro-common samples workflow; passes under
ASan and UBSan.
2026-11-12 08:39:46 +01:00
LibretroAdmin c5cd9ae6f8 vfs: the Windows positional handle only where pointer atomics exist
34fd7248 keeps the second handle Windows positional I/O reads through
in a retro_atomic_ptr_t, declared and set up wherever _WIN32 is. The
volatile atomic backend has no pointer type or operations, and
vfs_close_result_test builds vfs_implementation.c as a PS2 TU forced
onto it (-DPS2 -DRETRO_ATOMIC_FORCE_VOLATILE) with a Windows compiler,
MSYS2's in the Windows VFS samples job, MinGW's under Wine: neither
built.

vfs.h has VFS_HAVE_AT_HANDLE where _WIN32 meets a backend with pointer
atomics; the field, its initialisation and VFS_POSITIONAL_WIN32 follow
it. Without it read_at and write_at return -1 and the caller seeks and
reads as before, as on the platforms with neither pread nor ReOpenFile.
Every real Windows build has pointer atomics and is unchanged.

The MinGW builds of vfs_close_result_test, vfs_positional_test,
vfs_readahead_test and vfs_prefetch_offthread_test build without
warnings and pass under Wine - the positional test through the
Windows second handle - and the Linux VFS samples pass under ASan and
UBSan.
2026-11-12 08:31:22 +01:00
LibretroAdmin 65539f234e wgl_context_keep: the attribute globals create_gl_context now sets
ae907bff has create_gl_context() keep the attributes its context was
made with, in wgl_ctx_attribs and wgl_ctx_attribs_used, for the
contexts a core asks for on its own threads. wgl_context_keep.py
compiles that function out of wgl_ctx.c into a fixture of its own,
which did not declare them, so it stopped building. The fixture
declares both.

It passes with gcc and clang under ASan and UBSan and as a Windows
binary under Wine.
2026-11-12 08:28:12 +01:00
LibretroAdmin 34fd7248c0 libretro: VFS v6 - reads and writes at an offset, with no lock
read_at and write_at take the offset with the call and leave the
stream's own position alone, so any number of threads can use one
handle at once and a core no longer needs a lock of its own around
seek-then-read. write_at with RETRO_VFS_WRITE_AT_APPEND reserves its
place at the end with one atomic add, so appends from several threads
neither overlap nor tear.

pread/pwrite where the platform has them, a second handle with its
own position (ReOpenFile, OVERLAPPED) on Windows, a memcpy for a
mapped stream. -1 where there is neither (consoles, UWP, the other
schemes); the caller then does what it did before.

filestream_read_at / filestream_write_at reach it from libretro-common.

vfs_positional_test: four threads read at offsets on one handle while
a fifth reads it in sequence, for buffered, descriptor and mapped
streams, then four threads append records. With the positional read
swapped for seek-then-read it reports tens of thousands of wrong reads.
Runs in the libretro-common samples workflow; also built with MinGW
and run under wine.
2026-11-12 08:15:00 +01:00
LibretroAdmin 2a688d8f43 translation_reply_test: the point scaler's picks are the nearest to each centre
ab6a34d3 has the point scaler take the sample nearest each output
pixel's centre, and translation_reply_test still worked out its
expected pixels from the old start, half a sample short: a 128x96 reply
drawn on a 64x48 frame now starts at row 1, column 1 rather than 0, and
the 7x5 PNG reply's pixels on the other frame sizes moved with it -
105 checks failed. The scaled lane expects floor(in / (2 out)) at the
top left, and the PNG lane starts its picks at the centre.

translation_reply_test passes with and without RPNG, natively under
ASan and UBSan and as a Windows binary under Wine.
2026-11-12 04:43:25 +01:00
LibretroAdmin ae907bff65 libretro: OpenGL contexts for a core's own threads
RETRO_ENVIRONMENT_GET_HW_SHARED_CONTEXT_INTERFACE (104, experimental)
gives an OpenGL or OpenGL ES hardware core contexts in its own
context's share group, for threads of its own: create and destroy on
the core's thread, make_current on the thread that uses one. A core
can then render or compile shaders off its main thread with nothing of
the window system in the core. A frontend that cannot make such
contexts answers false, and the core keeps its GL work on the thread
that calls retro_run.

RetroArch answers it with the context drivers' shared contexts, which
GLX, Wayland and X11 EGL already had for OpenXR; WGL, KMS/DRM and
Android get them here. The WGL one shares with the context current on
the calling thread (the core's), made with the window context's
attributes, and binds on the window's device context.

samples/gfx/gl_shared_context is a core whose second thread clears a
texture through such a context that retro_run then shows; the EGL
hardware core workflow runs it on GLX and EGL, threaded and not, with
a compatibility and a 3.3 core context.
2026-11-12 04:32:16 +01:00
LibretroAdmin fd7e99a94c net_http, task_http: a chunked download goes to the file where it was received
The chunked decoder slid every payload byte down over the chunk
headers before it, so the decoded body was one run for the sink: one
copy of the whole body inside the receive buffer, on top of the
receive itself.

A sink can now take the body as runs (net_http_connection_set_sink_spans).
For such a sink, a chunk of 16 KB or more stays where it was received
and is recorded as a run; shorter chunks are still slid down onto the
end of the run before them, so the runs stay few and long.  At most
16 runs are held, in the handle, so nothing is allocated mid-transfer;
with all 16 in use, further chunks are slid as before until the hand-
off.  The hand-off point is unchanged: when the free window behind
the held bytes would fall below 32 KB, and at the end of the body or
when it is cut short.

task_http's download sink takes runs and writes them with
filestream_writev: one writev() on the targets that have it, so the
chunks reach the file from where they were received.  An identity
body goes as one run, as before.  net_http_connection_set_sink and
its sinks are unchanged.
2026-11-12 03:47:46 +01:00
LibretroAdmin 2440f44bb3 libretro-common: filestream_writev, a gathered write
filestream_writev writes several pieces of memory to a stream, in
order, as filestream_write would one after another.  A stream opened
with RETRO_VFS_FILE_ACCESS_HINT_SEQUENTIAL_BULK, which writes to its
descriptor, hands them to the kernel in one writev() call on Linux,
Android, Apple and the BSDs, so pieces scattered through a buffer
reach the file without being copied together first.  Elsewhere -
Windows, the consoles, buffered streams, smb:// and nfs://, and a
core's own VFS - the pieces are written one by one.

retro_vfs_file_writev_impl backs it in both VFS backends; the UWP one
writes the pieces one by one.  struct retro_vfs_span (vfs/vfs.h) is
the piece.
2026-11-12 03:47:43 +01:00
LibretroAdmin e856942f86 net_http: a chunked body cut short mid-chunk hands over its decoded bytes
When a chunked body streamed to a sink was cut short, the held bytes
handed to the sink were counted as response->len.  Between chunks
(P_BODY_CHUNKLEN) that is the decoded count, but in the middle of a
chunk (P_BODY) len is what the chunk still owes and the decoded count
is pos.  A connection dropped mid-chunk therefore handed the sink the
wrong number of bytes: raw or stale buffer bytes written into the
download, which a resume then continued after, or, for a chunk longer
than the receive buffer, a read past its end.

In the middle of a chunk the held count is now pos, as for an
identity body.
2026-11-12 03:42:08 +01:00
LibretroAdmin c78b4fa0b7 task_core_backup: core restore and plain backups through the VFS copy
Restoring a core from a backup that is not compressed is a plain file
copy, and that is the common case: installing a core update moves the
old core into the backups as it is, and installing a core from a file
copies that file.  It went through the 100 KB transfer buffer, read
into and written out of.  It now goes through the VFS copy into the
temporary beside the core, which the platform does itself where it
can: copy_file_range on Linux, where no byte enters user space, and
clonefile on a same-volume APFS copy, where none moves at all.  The
temporary is renamed over the core when the copy is done, as before.

Backing up a core where backups are not compressed (builds without
HAVE_COMPRESSION) is the same plain copy and takes the same path.
Compressed backups, and restoring them, go through rzip as before.

The copy is stepped a megabyte at a time inside the shared I/O window,
so a tick takes no longer than it did; a cancelled copy removes its
partial file.  Where the copy cannot start, the transfer buffer still
does the work.
2026-11-12 03:40:17 +01:00
github-actions c531bd56fe Fetch translations from Crowdin 2026-11-12 01:27:56 +00:00
LibretroAdmin ec71393b7b XMB: free a node's console name with the node
xmb_free_node() frees the console name each horizontal-list node
owns, so the playlist and Explore view tabs no longer leak it when
the list is torn down (the 4-byte leaks in #20065).

menu_icon_ownership now ends through main_exit(), so the menu
driver's free() runs, and its xmb lane counts the copies of the
fixture playlist's name made with strdup (link-wrapped) that are
still unfreed afterwards.
2026-11-12 00:33:51 +01:00
LibretroAdmin 2cc624e8dc retro_atomic: C++ casts in the C++11 backend
The C++11 backend cast values with C casts, in its macros and its
compare-and-swap functions. A project built with
-Werror=old-style-cast, RPCS3 for one, could not include the header.
The casts are now static_cast, and the compare-and-swap functions
turn the bool into 1 or 0 with ?:.

The C++11 sample is built with -Werror=old-style-cast; on the old
header it stops with 19 errors. It also tries cas_ptr, fetch_add_64
and cas_64 now, and the stand-in old <atomic> declares the member
fetch operations the 64-bit macros use.
2026-11-11 23:42:30 +01:00
Rob Loach c38dbd516f Fix playlist leak in Add to Playlist (#20070) 2026-11-11 23:28:40 +01:00
LibretroAdmin cec7ea5aae udev: fix touchscreen pointer press, release and slot handling
The touchscreen pointer is pressed while any contact is down and
released when the last one lifts, and the pointer device answers X, Y
and pressed. A multi-touch panel is read by its MT slots only: the
kernel's single-touch emulation (ABS_X/ABS_Y/BTN_TOUCH) repeats the
oldest contact and is ignored there. A single-touch panel keeps its
contact in slot 0, the slot index starts at the kernel's unannounced
slot 0, a slot resync recounts the held contacts, and panel positions
are taken from the axis minimum.

Regression lane: samples/input/udev_touch drives the real driver with
an in-memory multi-touch and single-touch panel.

Closes #20060
2026-11-11 23:23:27 +01:00
LibretroAdmin 05a67973bf KMS: report display DPI, physical and pixel size
The KMS display server answers DISPLAY_METRIC_DPI, MM_WIDTH/HEIGHT
and PIXEL_WIDTH/HEIGHT from the connector's reported size and the
live mode, so the menu gets the panel's real DPI and touch drag,
swipe and press direction work under the kms context. A sink that
reports no size still answers its pixel size only.

display_servers_kms_test gains a lane for a 1024x600 panel at
154 x 86 mm, a sink without a size, and a torn-down context.

Closes #20061
2026-11-11 23:01:50 +01:00
LibretroAdmin 5ab2152e6a The checks today's commits left behind: cloud sync reads, centre point picks, gapless WMA, the H.264 border
66376b18 sends cloud sync reads to disk through
task_push_http_download_file_with_headers(), which the webdav_options,
webdav_backup, webdav_reauth, s3_backup and gdrive_backup samples
include webdav.c, s3.c or google_drive.c without: none of them linked.
Each stubs it as the GET it is, recorded as their transfer stubs record
one. webdav_reauth's stale-nonce read wrote nothing to the file the
callback now opens, and read the stale copy there; it writes the body
first, as the download would.

ab6a34d3 has the point scaler take the sample nearest each output
pixel's centre, and video_frame_convert_to_bgr24()'s fused downscale
path (screenshots, translation) still started half a sample short, so
video_frame_failure_test found it apart from the scaler it checks
against. It starts from the centre too. Its 2x2 to 1x1 case now
expects the bottom right sample, the one nearest the centre.

a2cd6efc plays WMA1 and WMA2 gapless: a stream's first frame is the
encoder's priming and its second half plays on the drain.
wma_player_test's fixture is one frame, so what plays is that frame's
second half, as audio_transfer_wma_test was told in the same commit.

The H.264 encoder's border copies its first and last rows outwards,
rows that are never the same; the overlap check allows them.

The five cloud sync samples pass, video_frame_failure_test and its
translation check, pixconv_test, scaler_point_test and
image_scale_transfer_test pass, check-wma passes, and the overlap copy
check is clean.
2026-11-11 21:23:52 +01:00
LibretroAdmin 376782248d formats/mp4: rmp4_mux skips an audio encoder's priming
An AAC encoder's first packet is priming: 1024 frames of nothing that
come before the first sample given. FFmpeg's MP4 muxer skips them with
an edit list; rmp4_mux could not, so everything recorded with raac
played 21 ms late against the video.

- rmp4_mux_track_t.skip: ticks of the track's start that are not shown.
  The edit list's media time takes them on top of any delay B-frames
  give the first picture, and the shown length drops by them. Ignored
  when the track is no longer than that. Added at the end of the struct,
  so a caller that clears it keeps the old file.

samples/formats/mp4_mux/rmp4_mux_test writes the AAC track again with
a skip of 1024 and checks that rmp4 reads it back as the edit's
media_skip, with the shown length 1024 frames shorter, and the video
left alone. Made to fail by leaving rmp4_mux.c as it was. A core's
recordings (raac into rmp4_mux with skip 1024), decoded by FFmpeg, line
up with what went in at a delay of 0 frames, where they were 1024
behind before.
2026-11-11 20:54:40 +01:00
LibretroAdmin fa4ecbff83 formats/mp4: rmp4 gives an mp4v or mp4a entry's esds object type
MPEG-4 Part 2 video and motion JPEG both sit in MP4 behind the same
mp4v fourcc (rmp4_mux writes either); only the esds's
objectTypeIndication tells them apart - 0x20 or 0x6C - and rmp4 did not
read the esds of an mp4v at all. A media library listing a video's
codec from rmp4 named every motion JPEG file MPEG-4.

- rmp4_track.object_type: the esds objectTypeIndication of an mp4v or
  mp4a entry, 0 for other entries or without an esds. Added at the end
  of the struct.
- an mp4v's DecoderSpecificInfo (the VOS/VOL headers) becomes its
  codec_private; its codec stays RMP4_CODEC_UNKNOWN, as no decoder here
  takes it from rmp4.

samples/formats/mp4_mux/rmp4_mux_test checks, on the files rmp4_mux
writes: MPEG-4 Visual (0x20) with its VOL headers as codec_private,
JPEG (0x6C) behind the same mp4v with none, AAC as 0x40, and 0 for avc1
and PCM. Made to fail by leaving rmp4.c as it was.
2026-11-11 20:45:30 +01:00
LibretroAdmin f6d00b124b audio/conversion: float_to_ulaw, G.711 mu-law
rmp4_mux writes "ulaw" tracks and rwav reads mu-law back, but nothing
in libretro-common made the codes. A core replacing FFmpeg for
recording had pcm_mulaw for that and nothing to replace it with.

- convert_s16_to_ulaw: each sample's top 14 bits, and the code whose
  decision levels - the midpoints between neighbouring codes'
  reconstruction levels, rounded - bracket it, found by a binary search
  over G.711's levels: no table. The same codes FFmpeg's pcm_mulaw
  writes, all 65536.
- convert_float_to_ulaw: floats in -1..1 rounded to 16 bits (clipped
  outside) first.

samples/audio/float_to_ulaw/float_to_ulaw_test checks silence, both
full scales, the codes moving one way through each half, each sample's
level the nearest (by G.711's decoding, written out in the test) within
the 8 the dropped bits and the levels' rounding allow, and floats giving
the 16-bit samples' codes, clipping and rounding. --ramp writes every
16-bit sample and --ffmpeg compares FFmpeg's pcm_mulaw codes for it; a
new CI step runs the two. Made to fail by dropping the levels'
rounding (8 codes unlike FFmpeg's) and by truncating floats.
2026-11-11 20:38:53 +01:00
LibretroAdmin bce2444917 formats/image: image_yuv_420_from_bytes, RGB to 4:2:0 for encoders
libretro-common's MPEG-4 Part 2, H.264 and JPEG encoders take 4:2:0 in
BT.601 limited range, and nothing in libretro-common made it from the
RGB a frontend, a core or an emulated console hands over. A core
replacing FFmpeg for recording had swscale for that step and nothing
to replace it with.

- image_yuv_420_from_bytes: four-byte pixels in memory order (alpha
  ignored) to 4:2:0, any layout image_yuv_420_to_bytes reads (planar or
  interleaved chroma, padded strides), BT.601 or BT.709, limited or
  full range. Luma per pixel; each chroma sample the average of the 2x2
  pixels it covers, an odd last column or row averaging what it has.
  Fixed point at the same 14 bits: each luma row sums to the luma
  range, so black, white and every grey come out exact, and each chroma
  row sums to 0, so grey chroma is 128 exactly.
- IMAGE_YUV_BYTES_BGRA (XRGB8888 as a little-endian host stores it) and
  IMAGE_YUV_BYTES_ABGR, for both directions.

Measured against swscale's yuv420p with -sws_flags area+accurate_rnd
(the same 2x2 average) on four of FFmpeg's test patterns: every sample
within 1.

samples/formats/image_yuv/image_yuv_test now also checks, for sizes 1x1
to 321x241, planar and interleaved chroma, both matrices and both
ranges: luma within 1 of the matrix in floating point (from Kr and Kb,
not the table), chroma within 1 of the 2x2 average's, the same planes
from every byte order whatever the alpha, nothing written past a row's
samples, flat 2x2 blocks back within 2 through image_yuv_420_to_bytes,
and every grey exact; and that BGRA and ABGR are the RGBA pixels moved.
With --ffmpeg it compares against swscale's output, which a new CI step
runs. Made to fail by taking chroma from three of the four pixels, by
dropping the chroma rounding, and by reading BGRA as RGBA.
2026-11-11 20:38:27 +01:00
LibretroAdmin 23807ce20f formats/container: rcontainer, what an AVI or ASF file holds
A reader for the headers of AVI and ASF (WMV, WMA) files: their streams
in order - video with its compression FourCC and size, audio with its
WAVE format tag, rate and channels, each with its bit rate - and their
length, for a library or a file browser to list a video by. Nothing is
decoded or demuxed, nothing allocated; the header is read where it
lies.

- AVI: avih, and each stream's strh and strf (BITMAPINFOHEADER,
  WAVEFORMATEX, WAVE_FORMAT_EXTENSIBLE's sub-format); the length the
  longest stream gives, as FFmpeg gives it; a video stream's bit rate
  from its bytes in idx1 over that length, an audio stream's from its
  WAVEFORMATEX;
- ASF: the file properties (play duration less preroll, unless it is a
  broadcast), the stream properties (the audio's WAVEFORMATEX, the
  video's size and BITMAPINFOHEADER) and the stream bit rate
  properties.

libretro-common has MP4, Matroska and MPEG program stream demuxers but
nothing for these two. parallel-ps3 needs it to list AVI and WMV videos
in cellSearch, which FFmpeg's libavformat did before.

samples/formats/container builds an AVI (XVID with an MP3 stream in
WAVE_FORMAT_EXTENSIBLE, an idx1) and an ASF (WMV3 and WMA2 with bit
rate properties) byte by byte, checks every field, and reads every
prefix and each byte corrupted under ASan. CI reads AVIs FFmpeg writes
(MPEG-4 Part 2 with MP3, AVC with AC-3, MPEG-4 alone, MS-MPEG4 with
PCM) and a WMV (WMV2 with WMA2) and checks the streams, tags, sizes,
rates, bit rates and lengths are ffprobe's.
2026-11-11 20:25:04 +01:00
LibretroAdmin a2cd6efcb4 formats/wma: WMA1 and WMA2 played gapless
WMA1 and WMA2 carry no gapless signalling, and rwma played them as the
frames came: the encoder's priming frame first (FFmpeg's encoder sets
initial_padding to one frame, as WMA encoders put one in), and without
the last frame's second half, which only the next frame's overlap would
have completed. A track started one frame late - 46 ms at 44.1 kHz -
and lost its last frame. FFmpeg's decoder is no reference here: it
drops two frames at the start, its own and one of the audio.

- rwma_decode_packet()'s drain (no bytes) gives the standard decoder's
  overlap tail once, as FFmpeg's flush does, where it gave nothing;
- rwma_stream drops the first frame of a WMA1 or WMA2 stream, drains
  the tail at the end, and stops at the length the ASF header states
  (rwma_stream_frames), so the stream plays exactly the audio the
  encoder was given. WMA Pro (which signals its own trims) and XMA are
  as they were; a rewind starts the trimming again.

parallel-ps3 needs this for cellMusicDecode to play WMA tracks from
their first sample to their last.

audio_transfer_sniff_test --file gains --source, the decode against the
signal the encoder was given: from its first sample, at its length
within 2 ms (ASF's durations are in milliseconds), 20 dB above the
coding error. CI checks WMA1 and WMA2 from FFmpeg at 44.1, 22.05, 16
and 8 kHz (frames of 2048, 1024 and 512 samples); the old code was a
frame late and 575 samples long at 44.1 kHz. rwma_test checks the drain
gives the tail once, and audio_transfer_wma_test, whose fixture is a
single coded frame, now expects that frame's second half.
2026-11-11 20:25:00 +01:00
LibretroAdmin 100a62c152 audio_transfer: MP3's decoder delay in the gapless trim; WMA's length from the ASF header
An MP3 with a LAME tag played 529 samples late through audio_transfer,
and its last 529 were cut: the arm trimmed the filler frame and the
encoder delay the tag states, but not the decoder's own delay, which
the LAME convention puts on top and which rmp3's pull API
(RMP3_DECODER_DELAY) and FFmpeg both trim. The length was right, so
nothing looked wrong but the timing; it now trims 529 more where a
LAME tag is present, and decodes to FFmpeg's samples, start and end.

WMA reported no length: audio_transfer_info gave 0 total frames for
every WMA file. rwma_stream_frames() reads it from the ASF file
properties object - the play duration less the preroll, unless the
file is a broadcast's - as FFmpeg does, and the WMA arm reports it.
RIFF and xWMA files still state none.

parallel-ps3 needs both for cellSearch's listing of music (a track's
length) and cellMusicDecode's playback, which FFmpeg did before.

audio_transfer_sniff_test --file gains --pcm, the decode against
FFmpeg's at the file's own rate (same length, within 1e-4 sample for
sample), and --frames, the stated length. CI checks an FFmpeg MP3 with
a LAME tag and one without against FFmpeg's samples, and FFmpeg's WMA1
and WMA2 files' lengths against ffprobe's; the MP3 with the tag and
both WMA files fail on the old code.
2026-11-11 20:01:15 +01:00
LibretroAdmin ba979c1110 audio_transfer: ATRAC3 and ATRAC3plus in OpenMG and WAVE; rtags: OpenMG's ea3 tag
A new arm, AUDIO_TYPE_ATRAC, plays ATRAC3 (ratrac3) and ATRAC3plus
(ratrac3p) from OpenMG files (.oma, .aa3: an optional "ea3" tag, then
the 96-byte EA3 header naming the codec, rate, channels and frame
size) and WAVE files (.at3: format tag 270h, or FFFEh with the
ATRAC3plus sub-format). Frames decode one at a time into a pending
block, or straight into the caller's buffer when a whole frame fits;
seeks decode the frame before the target so the overlap is what a
playthrough leaves. Encrypted OpenMG is refused; a cut last frame is
left out.

ATRAC3plus decodes in Sony's channel order (FL FR FC, the back pair,
the back centre or side pair, LFE last), which the arm puts into the
WAV shape for the count as the AC-3 arm does: 5.1, 6.1 and 7.1
reordered, 6.1's back pair in WAV 7's side slots, 4.0's lone back
centre a phantom pair at -3 dB, so audio_transfer_stereo_matrix folds
it like the rest. audio_transfer_sniff_type knows both containers.

rtags reads OpenMG's "ea3" tag, which is an ID3v2 tag by another name,
as FFmpeg does.

parallel-ps3 needs these for the music cellSearch lists and
cellMusicDecode plays, ATRAC being one of the PS3's own formats; FFmpeg
did both before.

samples/formats/audio_transfer_atrac wraps the ATRAC decoders' test
streams (stereo, joint stereo, ATRAC3plus mono and stereo) and one
ATRAC3plus frame each of 4.0, 5.1, 6.1 and 7.1 in WAVE and OpenMG
files, and checks the arm hands over the decoders' own samples in the
WAV shape, as floats and as s16, after a seek too, with the 5.1 fold;
refuses encrypted OpenMG; and reads every prefix of two files, and
each header byte corrupted, under ASan. In CI FFmpeg decodes each file
to the same samples, within 7.3e-07 (ATRAC3 in the opposite polarity,
which ratrac3 has from the start), and ffprobe reads the ea3 tags as
rtags does; a wrong 5.1 order fails the comparison. rtags_test gains an
OpenMG file, which fails without the ea3 magic.
2026-11-11 19:35:33 +01:00
LibretroAdmin a187644490 audio_transfer: a file's arm from its bytes, a fold for every arm, AAC in WAVE order
audio_transfer_sniff_type tells which arm a whole file is for from its
bytes, where audio_decode_get_type goes by the extension and knows six
of them: it looks past ID3v2 tags, then takes FLAC, Ogg and WebM (with
the existing sniffers), WAVE by its format tag (PCM and float to the
WAV arm, WMA in RIFF and xWMA to the WMA arm), ASF, MP4/M4A, tracker
modules, and raw AC-3, ADTS and MPEG audio by a frame header with
another where it says the next frame starts. It also gives where the
arm's buffer starts, since the AAC and AC-3 buffer modes do not look
past an ID3v2 tag or junk in front of the first frame themselves.

audio_transfer_stereo_matrix now answers for every arm, not only WMA:
WMA by its speaker mask as before, the others by channel count in the
order the arm hands channels over. The two fold tables, Vorbis order
and WAVE order, move from audio_mixer.c into audio_transfer.c behind
audio_transfer_fold_q15, and the mixer takes its folds from there; the
coefficients are the same.

AAC wider than stereo came out in raac's stream order (5.1 as C L R Ls
Rs LFE) while everything that folds it, the mixer included, takes
WAVE order: 5.1 AAC from an MP4 or Matroska file played front-left on
the right, the centre on the left alone, LFE in the mix and the
surrounds wrong. The AAC arm now asks raac for WAVE order at start.

parallel-ps3 needs all three to move cellMusicDecode's playback off
FFmpeg: music of any of these formats, found by its bytes, folded to
stereo.

samples/formats/audio_transfer_sniff builds 23 files byte by byte -
each container and raw syntax, ID3v2 tags and junk in front, and
look-alikes to turn down - checks the arm and the offset for each,
reads every prefix and each byte corrupted under ASan, and checks the
fold tables. CI also sniffs and decodes files FFmpeg writes in every
format, and folds 5.1 with a tone a channel in AAC, Vorbis, Opus, FLAC,
AC-3 and WAVE: every tone lands on its side, the centre on both and
the LFE on neither. The AAC one fails without the channel order fix.
2026-11-11 19:35:29 +01:00
LibretroAdmin 679e334c72 formats/tags: rtags, the tags a music or video file carries
A reader for the title, artist, album, genre, track and disc numbers
and date a file is tagged with, from the file where it lies: ID3v2.2,
2.3 and 2.4 (every text encoding, unsynchronised tags and frames,
extended headers, grouped frames, data length indicators), ID3v1 and
1.1, Vorbis comments in FLAC and in Ogg (Vorbis, Opus, FLAC; a comment
header across pages, other streams' pages between), MP4's iTunes items
and QuickTime's own strings, ASF's content and extended content
descriptions, and WAVE's LIST INFO and id3 chunks. One byte source
reads a span, a span with ID3's unsynchronisation undone, or an Ogg
packet across its pages, and text is decoded character by character
into the caller's buffer, cut at a character when it is full. Nothing
is allocated; the input is only read.

Values come out as FFmpeg's demuxers give them, so a player moving off
FFmpeg shows what it showed: ID3 and MP4 genre numbers as names,
ID3v2.3's TYER, TDAT and TIME as one date, trkn and disk as "3/12",
repeated Vorbis comments joined by ';'. Beyond FFmpeg, ASF's WM/Year
is the date and ID3v1 fills what an ID3v2 tag leaves out, field by
field.

parallel-ps3 needs it for cellSearch's music and video listings, the
last of what it reads with FFmpeg's libavformat.

samples/formats/tags builds fourteen files byte by byte, one for each
container and ID3 version with the cases above, checks every field of
each, then reads every prefix of each and each byte set to 0, 0xff and
flipped, every copy allocated to its size for ASan. CI also tags files
with FFmpeg - ID3v2.3 and 2.4, ID3v1 alone, FLAC, Ogg Vorbis and Opus,
MP4, ASF, WAVE, with non-ASCII text, a numbered genre and full dates -
and checks rtags reads each as ffprobe does.
2026-11-11 18:57:05 +01:00
libretroadmin 3c4c202cfd r7z: a folder peeked at while it is decoded, never torn
r7z_archive_decode_peek() read the folder and the buffer, written by
the decoding thread, while they could be changing: a member could be
read from one folder's buffer and another's position. The state now
counts its changes and is read again after them; what is read when it
changed in between is nothing yet.

r7z_peek_threads_test peeks while another thread decodes each of two
folders, BZip2 and LZMA2, under ThreadSanitizer in CI.
2026-11-11 18:37:04 +01:00
LibretroAdmin cc1da39e23 formats: tags through rmpeg1_video and rmpeg4_video; rh264's frame rate
A caller that feeds access units with time stamps needs each decoded
picture's own back, however the decoder reorders the pictures for
display - what FFmpeg's AV_CODEC_FLAG_COPY_OPAQUE gives and rh264's
rh264_video_set_tag already does. parallel-ps3's cellVdec needs it to
move off FFmpeg: a game's picture items carry the PTS, DTS and user
data of the access unit each picture came from.

rmpeg4_video_set_tag(): the VOPs the next decode calls decode take the
value; each frame gives it back in rmpeg4_video_frame_t.tag, kept with
the reference slot it was decoded into.

rmpeg1_video_set_tag(): rmpeg1_video takes a byte stream, so the value
applies to the pictures whose start codes are in the bytes written
after the call. The decoder keeps a short list of (stream position,
tag) and a picture header takes the last one at or before its start
code; the held reference keeps its own, and a field pair takes its
first field's. rmpeg1_video_frame_t.tag gives it back; a reset forgets
those pending.

rh264_video_framerate(): the VUI timing info, which rh264 read past,
is kept in the SPS (num_units_in_tick, time_scale, fixed_frame_rate)
and given as time_scale over 2 num_units_in_tick, reduced by two where
it divides; -1 without timing. The SPS is part of rh264's savestates,
so their version goes to 3.

rmpeg2_test and rmpeg4_test feed their fixtures an access unit at a
time, each tagged with its number, and check every frame comes back
with the tag of the picture it is - its temporal reference and coding
type, read from the headers by the test - each once, with the
references out of decode order. Giving a held reference the current
picture's tag fails the first, giving every frame the last tag set the
second. rh264_encode_test reads its streams' frame rate back, 25/1;
without the timing parsed it fails.
2026-11-11 18:34:47 +01:00
LibretroAdmin ab6a34d345 gfx/scaler: point scaling takes the sample nearest each pixel's centre
The point scaler started from the position the bilinear and sinc
filters weigh from - an output pixel's centre less half an input
sample - and took the sample that position fell in, so every pick was
half a sample short: 64 columns brought down to 40 took columns 0, 1,
3, 5, 6 where the nearest are 0, 2, 4, 5, 7, and an upscale by 3 drew
the first column twice and its neighbours four times. It now starts
half a sample on, which is the nearest sample to the centre - what
FFmpeg's swscale and a GPU's nearest filtering pick - in both the
filter path and the ARGB8888 special path. Powers of two up are as
they were; other integer upscales now repeat each pixel exactly.

parallel-ps3 needs this to move the RSX blit engine's scaled copies
from swscale to the scaler with the same picks.

scaler_point_test checks the picks against floor((x + 1/2) in / out)
for sizes up and down through both paths, and its reference and
image_scale_transfer_test's upscale expectations take the centre; both
fail on the old scaler.
2026-11-11 18:00:51 +01:00
LibretroAdmin bfcc408883 formats/h264: rh264_encode, an H.264 encoder
What a recording is made in when AVC is asked for: cellRec in
parallel-ps3 can ask for H.264, and this takes the place of FFmpeg's
encoder there.

Constrained Baseline, CAVLC, one slice a picture, IDR and P pictures.
Intra macroblocks are 16x16 or 4x4 predicted, chosen by SATD; inter
macroblocks are one 16x16 partition with a quarter sample vector from
a predictive search (candidates, a whole sample diamond, then half and
quarter samples), or P_Skip when the skip vector leaves nothing to
code. The in-loop deblocking filter runs. A constant quantiser, or a
bit rate the quantiser follows picture by picture.

Every sample is reconstructed as the standard defines it, so a
conforming decoder shows exactly the encoder's reconstruction. The
pictures keep a border of edge copies, and before a P picture the
reference's half sample planes are made once; every quarter sample
position is one of them or the mean of two, as 8.4.2.2.1 derives it.
Output is Annex B with the parameter sets before each IDR picture, or
MP4 samples with 4-byte lengths and an avcC record for rmp4_mux.
Nothing is allocated after rh264_encoder_new(). On testsrc2 at 720p
and 2 Mbit/s it is 1.2 dB under libx264's baseline at veryfast, at
57 fps on one core (23 fps at 1080p).

rh264's CAVLC tables move to rh264_cavlc_tables.h, with their OpenH264
notice, so the encoder codes with the tables rh264 decodes with;
rh264.c is otherwise unchanged and its tests pass.

samples/formats/h264_encode encodes moving frames with a scene cut,
whole macroblocks and cropped, at fine, middling and coarse quantisers,
with a bit rate, as MP4 samples through the avcC, and at every
quantiser from 1 to 51, and checks that rh264 decodes every picture
to exactly the reconstruction, the PSNR, the IDR spacing, the stream
hashes, the rate within 3%, and that bad sizes and a buffer too small
are refused. Mutations - total_zeros, emulation prevention, a 4x4
mode, the chroma plane, a tc0 or alpha entry, the skip rule, the
strong filter, the crop, the suffix length, a quarter sample source,
the MP4 length, idr_pic_id, 4x4 top right, the vector predictor - each
fail it. A new workflow step has FFmpeg decode the streams; its output
is the reconstruction to the byte.
2026-11-11 17:48:36 +01:00
libretroadmin 081d1be095 libretro-common: add xxh32
XXH32, whole and streamed, beside xxh64 and xxh3, for cores that
name things by it (a core's custom textures are found by their XXH32
hashes). The xxhash sample checks it against xxHash's own values,
whole and fed in pieces.
2026-11-11 17:14:55 +01:00
LibretroAdmin 372e82f37a formats/aac: raac_encode, an AAC-LC encoder
What a recording's audio is when AAC is asked for: cellRec in
parallel-ps3 records MP4 with AAC, and this is the last FFmpeg piece
it needs for that.

Mono or stereo at any of AAC's sampling rates, long sine windows, a
single channel element or a channel pair with a common window. The
MDCT is a DCT-IV of the folded window by a 512-point complex FFT. A
band's scalefactor follows the square root of its level, and one
offset a frame is searched for the frame's share of the bit rate, any
debt paid over eight frames; no step is finer than 60 dB under the
frame's loudest band. Codebooks are chosen band by band for the fewest
bits. The output is a raw_data_block (an MP4 sample) or that behind an
ADTS header, into the caller's buffer; nothing is allocated after
raac_encoder_new(). About 60 times real time for 48 kHz stereo.

raac's Huffman, scalefactor and band tables move to raac_tables.h so
the encoder codes with the same tables raac decodes with; raac.c is
otherwise unchanged.

samples/formats/aac_encode encodes tones, a tone 40 dB under another,
noise, and an attack after silence, mono and stereo, 8 to 96 kHz, and
checks that raac decodes every frame, that the output is the input a
frame late at its level and within the SNR the rate gives, that noise
lands within 3% of the rate and no stream goes more than 3% over it,
that the ASC and ADTS headers are right, that the f32 entry point
writes what the s16 one does, and that a buffer too small gives 0.
Mutations - no floor, global_gain after ics_info in a single channel
element, a wrong ADTS length or ASC, no debt, other rounding, a wrong
f32 scale - each fail it. A new workflow step has FFmpeg decode the
nine streams; it lands within 1 per sample of raac on every one.
2026-11-11 17:13:22 +01:00
LibretroAdmin 57e45b09cb formats/mpeg4: rmpeg4_encode, an MPEG-4 Part 2 video encoder
A console's recording library records MPEG-4 Part 2 by default; a core
emulating it needs the video encoded without FFmpeg. rmpeg4_video
decodes it; this writes it.

- Simple profile: I and P-VOPs, one half sample vector a macroblock
  (a predictive search - none, the predictor, the neighbours' vectors -
  then a small diamond and the half samples around it), intra
  macroblocks where the macroblock's own variation is clearly cheaper
  than the best match, not coded macroblocks, H.263 quantisation, DC
  prediction. The VOS, VO and VOL headers lead every I-VOP and are
  given apart (rmpeg4_encoder_header) for a container's esds.
- A constant quantiser, or a bit rate: the quantiser for each frame
  from bits times quantiser measured on the last frames of its kind,
  over the bits it should have, the debt paid off over eight frames; a
  GOP's bits shared so that its I-VOP gets three P-VOPs' worth. Within
  0.5% of the rate asked for at 300 kbit/s to 3 Mbit/s here.
- The references are reconstructed with rmpeg4_video's own inverse
  transform, now in rmpeg4_idct.h, which both files include; so
  rmpeg4_video decodes every VOP to exactly what the encoder predicted
  from, and FFmpeg within 2 per sample.
- Input planes are read where they lie, output goes in the caller's
  buffer (rmpeg4_encoder_bound() bytes always suffice; too small gives
  0), and the reconstruction planes are allocated by
  rmpeg4_encoder_new() and nothing after. 70 frames a second at 1080p,
  one core, here.

samples/formats/mpeg4_encode: rmpeg4_encode_test encodes frames made
there - gradients, a box faster than the search reaches, noise, a scene
cut - at a size of whole macroblocks and one of neither, quantisers 1
to 31, and a bit rate; it checks rmpeg4_video takes the size from the
header alone and decodes every VOP to exactly the encoder's
reconstruction with no error, I-VOPs every 12 frames, a PSNR floor, the
stream's hash (taken when FFmpeg decoded it within 2 of rmpeg4_video),
the bit rate within 3%, and that a buffer too small gives 0. Shown to
fail with the DC predictor, its direction, the vector predictor, the
vector wrap, the inter CBPY, the reconstruction's dequantisation, an
escape's marker or the rate control's debt broken.

CI: rmpeg4_encode_test runs with the other samples, and a step has
FFmpeg decode its five streams and rmpeg4_test compare them.
2026-11-11 08:41:44 +01:00
LibretroAdmin 0ae51eb6b9 net_http: a body cut short hands its held bytes to the sink
Since ac3ca0b and 2f07aac a streamed body stays in the receive buffer
until 32 KB or more can go to the sink at once. When the peer closed
a Content-Length or chunked body early, receive_body returned false at
once and the bytes still held - everything after the last 32 KB hand-
off - never reached the sink, though they had arrived whole. Only a
close-delimited body flushed them. A download to a file then resumed
from the last hand-off instead of from what it had received: dropped
at 200000, it asked for 'bytes=196608-' (or 178688), and the two-
resume and single-resume checks of http_resume_test failed nine runs
in ten. The bytes are fetched again, so nothing was corrupt, only
lost and fetched twice.

On a transport end (newlen < 0; the receive sets state->err for it)
the held decoded bytes - pos for a Content-Length body, len for a
chunked one, whose raw tail lies behind - go to the sink before the
transfer fails.

http_resume_test passes twenty runs of twenty under ASan and UBSan
(it failed nineteen of twenty with only the error flag guarding the
flush) and three of three under TSan; the transport failure, stress,
limits, task lifetime and dedup tests, net_http_options_test and the
parse test pass.
2026-11-11 08:22:15 +01:00
LibretroAdmin 54000281fe formats/mp4: rmp4_mux, an MP4 writer
libretro-common reads MP4 (rmp4) but could not write it. A console's
recording library writes MP4 files, and a core emulating it needs them
muxed without FFmpeg.

- rmp4_mux_open() writes ftyp and an mdat header; rmp4_mux_write()
  passes each sample straight to the caller's write callback - nothing
  copied or staged - and keeps only where it landed, in tables sized
  from each track's max_samples when the file was opened: nothing is
  allocated between open and close, and a full track refuses more.
- rmp4_mux_close() writes the moov through a 4 KiB staging buffer,
  every box's size worked out from the tables' counts beforehand and
  checked as the box closes, then seeks back once to give the mdat its
  64-bit size. Offsets past 4 GiB go in co64.
- Video: MPEG-4 Part 2 and motion JPEG as mp4v (object types 0x20 with
  the VOL headers, and 0x6C), H.264 as avc1 with the caller's avcC.
  Audio: AAC as mp4a with its AudioSpecificConfig; PCM as QuickTime's
  sowt, twos, fl32 and ulaw, any whole number of frames a write.
- Samples in decode order with presentation times: B frames get
  composition offsets (ctts, version 1) and an edit list that starts
  the presentation at the first frame shown; stss when not every
  sample is a sync sample.

samples/formats/mp4_mux: rmp4_mux_test writes MPEG-4 video with B-VOPs
(rmpeg4_test's fixture) with 16-bit PCM, motion JPEG from rjpeg_encode
with float PCM, and H.264 and AAC entries around placeholder samples,
then reads each back with rmp4: tracks, sizes, rates, fourccs and
decoder configurations; every sample's bytes, key flag and time; the
edit. It checks each sample went out in one write of its own, and that
a full track, a part frame of PCM, times going backwards, a failing
write and an avc1 track without avcC are refused. Shown to fail with
the composition offsets, the edit's media time, the mdat size, the PCM
chunk table, the sync sample table, the sync flag or the object type
broken.

FFmpeg reads the files: every frame decodes, the first shows at time
zero, the video decodes to the same frames as the elementary stream,
and the PCM comes back byte for byte.

CI: rmp4_mux_test runs with the other samples, and a step has FFmpeg
decode the two files it writes and count their frames.
2026-11-11 08:19:55 +01:00
LibretroAdmin e3ec135821 formats/jpeg: rjpeg_encode, a baseline JPEG encoder
libretro-common could read JPEGs but not write them. A console's
recording library records motion JPEG among its formats, and a core
emulating it needs the frames encoded without FFmpeg.

- rjpeg_encoder_init() prepares the tables for a quality, 1 to 100:
  T.81 Annex K's quantisation tables scaled as the IJG encoder scales
  them, and Annex K's Huffman tables.
- rjpeg_encode() writes a complete JFIF file, SOI to EOI, from 4:2:0 or
  4:2:2 planes in JFIF's full-range BT.601 YCbCr, read where they lie,
  into the caller's buffer; rjpeg_encode_bound() bytes are always
  enough, and a buffer too small gives 0 with nothing written past it.
  Any size up to 65535 x 65535: partial blocks repeat the last column
  and row.
- Nothing is allocated: the tables live in the caller's
  rjpeg_encoder_t. The forward DCT is Arai, Agui and Nakajima's in
  integers, its output scaling folded into the quantiser's
  reciprocals. 104 frames a second at 1080p on one core here.

Against libjpeg (through Pillow) at the same quality and subsampling:
sizes within a few percent and luma PSNR within 0.2 dB, at 352x288,
1080p and odd sizes down to 1x1; FFmpeg and libjpeg decode every file.

samples/formats/jpeg_encode: rjpeg_encode_test encodes gradients with
a sharp-edged box, and noise, at sizes in and out of whole MCUs, 4:2:0
and 4:2:2, qualities 20 to 100; checks each file's hash (taken when
they matched libjpeg), decodes it with rjpeg against a PSNR floor,
checks noise at quality 100 fits the bound, and that an exact buffer
works and one a byte short gives 0. Shown to fail with a DCT constant,
the quantiser's rounding, the byte stuffing, a Huffman table or the
capacity check broken, and under ASan with the edge repeat removed.

CI: rjpeg_encode_test runs with the other samples, and a step has
FFmpeg decode the nine files it writes.
2026-11-11 08:10:32 +01:00
LibretroAdmin 1269811da5 formats/mpeg4: rmpeg4_video, an MPEG-4 Part 2 video decoder
A decoder for ISO/IEC 14496-2 video, Simple and Advanced Simple
profiles, and the short video header (H.263 baseline) a Simple profile
decoder also takes - what a console's video decoder library offers as
MPEG-4, and what DivX and Xvid files carry.

- I, P, B and S-VOPs (global motion compensation, up to three warping
  points); H.263 and MPEG quantisation with the VOL's matrices and
  mismatch control; intra DC and AC prediction; one or four vectors a
  macroblock, half or quarter sample, unrestricted; direct mode;
  interlaced VOPs: field DCT, field prediction, field direct mode,
  alternate vertical scan; resync markers, data partitioning and
  reversible VLCs; GOB headers in the short header.
- Shapes, static sprites, scalability, NEWPRED and reduced resolution
  VOPs are not decoded: their VOPs are counted as skipped.
- The bitstream is read where it lies: rmpeg4_video_decode() takes the
  caller's access unit and copies nothing out of it, one VOP a call, so
  a packed bitstream (a P and a B-VOP in one unit) comes out frame by
  frame. Frames are in display order; a reference is held back while
  B-VOPs may follow, and rmpeg4_video_flush() releases it.
- Planes and the per-macroblock state are allocated when a VOL brings a
  size and never for a VOP. Frames stay within the 2 KiB stack budget.

The variable length code tables are generated (gen_tables.py, from a
pinned FFmpeg revision's table files, checked prefix-free); the inverse
transform is rmpeg1_video's.

Checked against FFmpeg's decode (one thread, bitexact, simple IDCT):
every frame within 3 per sample, mostly within 1, across FFmpeg's and
Xvid's encoders and every tool above but reversible VLCs, which no
encoder at hand writes. Two places where FFmpeg's own encoder and
decoder agree with each other and not with the standard are left as
the standard has them, and only matter for streams FFmpeg wrote with
resync markers and B-VOPs: its vector prediction at a video packet's
first macroblock overwrites the left neighbour's fourth vector, which
direct mode then reads; and a new packet resets only the first field's
B-VOP predictor. A frame-threaded FFmpeg decode is no reference either:
its threads do not share the field vectors interlaced direct mode
reads, and it gets those macroblocks wrong.

samples/formats/mpeg4: rmpeg4_test decodes five small streams (ASP
tools; interlaced; Xvid GMC; resync markers with data partitioning;
the short header) whole and a start code unit at a time - each unit in
a buffer of its own size, so a read past it is caught - and checks
the frames, the errors, the skipped count and a hash taken when they
matched FFmpeg; then decodes damaged copies, cut short and bit-flipped,
which must not fault. With a stream and a reference it compares every
frame. Shown to fail with the quarter sample filter, the field direct
mode distances, the short header's DC, MPEG mismatch control, the
packet predictor reset or the partitioned AC prediction flag broken,
and under ASan with the reader's end check removed.

CI: rmpeg4_test runs with the other samples, and a step runs it over
thirteen streams FFmpeg's mpeg4, libxvid and h263 encoders write
(three seconds under ASan/UBSan).
2026-11-11 07:42:42 +01:00
LibretroAdmin 66376b18c5 cloud_sync: WebDAV, S3 and Google Drive downloads go straight to disk
A download from these servers was gathered whole in memory, up to the
256 MB body limit, then written to the local file in one piece; the
WebDAV driver carried a TODO asking for exactly this.  They now use
task_push_http_download_file_with_headers: the body goes to the file
in pieces as it arrives, and the callback opens the finished file to
hand on.  The HTTP error text these drivers log still reaches them.

A body with no Content-Length or chunked framing is still refused, and
its file is removed with it.  A 404 (or S3's 400) still means the
server does not have the file; the download removed the file already.
2026-11-11 07:42:20 +01:00
LibretroAdmin 697d251723 task_http: downloads to a file with request headers, and their error text
task_push_http_download_file_with_headers is task_push_http_download_file
with extra request headers, for a server that wants authorization;
task_push_http_download_file is now that with none.  The Emscripten
backend has both.

A download to a file that ends in an HTTP error response used to hand
its callback no body, so a caller could not log the server's message.
It now hands it the start of the body, up to 4 KB, read back from the
file before the file is removed; only on that error path.  A
successful download still hands its callback no body, and the callers
that stream already go by their streamed flag and the error before
they look at it.
2026-11-11 07:42:19 +01:00
sonninnos 2c80e1abd6 Add Bluetooth settings visibility option 2026-11-11 08:26:03 +02:00
LibretroAdmin dedc4dfc42 libretro-common: filestream_copy through the VFS copy
filestream_copy copied through a 256 KB buffer it read into and wrote
out of.  It now runs the VFS copy (filestream_copy_begin/step/close)
to the end, which uses the platform's own primitive where there is
one: copy_file_range on Linux, where no byte enters user space, and
clonefile on a same-volume APFS copy, where no byte moves at all.
Where that copy cannot start -- no VFS v5 behind a core, or a target
that cannot be removed to make way, as on Windows when another
process holds it open -- the old loop still writes it in place.

The archive extractor's copy of a member over a target it cannot
rename onto is now filestream_copy as well, instead of a 64 KB loop
of its own.

The portable copy path, the one Windows, consoles and cross-volume
copies take, writes 128 KB pieces, and on resume opens its target to
read and write in place; both now take the descriptor path where the
platform has it.  UWP gets the same hint, which its backend ignores.

The comments in task_content.c and runahead.c that named Windows copy
primitives (CopyFileFromAppW, CopyFileW) said what the VFS copy does
not do; they now say what it does.
2026-11-11 07:06:02 +01:00
LibretroAdmin 8a9d7eece3 cloud_sync/smb: write a download as it arrives
A download was gathered whole in a buffer of the size the server
reported, then written to the local file in one piece.  The local
file is now opened first and each SMB read goes to it as it arrives,
through one buffer the size of a read: no copy of the whole file in
memory, and no allocation sized by the server.  A failed local write
fails the download; it was not checked before.
2026-11-11 07:04:21 +01:00
LibretroAdmin 7f88ccd7ea task_cloudsync: hash a download where it lies
A download comes back on the stream it was written through, opened to
read and write, which cannot lend its bytes: hashing it read the whole
file back into the 256 KB hash buffer.  A download of a hash chunk or
more is now opened again to read with the mapping hint, after a flush
of the stream that wrote it, and hashed through filestream_view out of
the page cache the write just filled, with no copy.  A smaller one is
a single read, cheaper than another open, and keeps its stream.  Where
the platform has no mapping the new stream reads as the old one did.
2026-11-11 07:04:21 +01:00
LibretroAdmin 9c92dae502 CI: run rmpeg2_test and image_yuv_test with the other samples
The samples job builds every sample directory and fails on a target in
neither RUN_TARGETS nor NORUN_TARGETS ("Target not classified").
samples/formats/mpeg2 and samples/formats/image_yuv came without an
entry, so the formats group failed on both.

Both run with no arguments: rmpeg2_test checks its three fixture
streams, whole and a byte at a time; image_yuv_test the byte-order and
UYVY conversions. They join RUN_TARGETS; the MPEG-2 step further down
still runs rmpeg2_test over FFmpeg's and the conformance streams.
2026-11-11 06:57:41 +01:00
LibretroAdmin 8ba38e325e task_core_backup: bulk reads and writes for backup and restore
Backing up and restoring a core copies it in 100 KB pieces and
checksums it in 256 KB pieces.  Both ends of the uncompressed copy
and the checksum reads of the core now ask for the bulk path: the
descriptor, with no copy through a stdio buffer, on the targets that
have it.  The restore's checksum of the backup keeps its mapping, and
the compressed (rzip) backup streams are unchanged.  Where the bulk
path is not available the hint is ignored.
2026-11-11 06:54:19 +01:00
LibretroAdmin 583785e886 save, task_save: bulk writes for save RAM and save states
Save RAM is written in one piece and flushed; a save state in one
piece (the synchronous path) or in 100 KB to 16 MB pieces (the
task).  These uncompressed writers now ask for descriptor writes:
on the desktops and Android the bytes go to the file with no copy
through a stdio buffer, and the save RAM flush has nothing left to
push.  The compressed (rzip) writers are unchanged.  Where descriptor
writes are not available the hint is ignored.
2026-11-11 06:54:15 +01:00
LibretroAdmin 2f07aac229 net_http: a chunked body reaches the sink in pieces of 32 KB or more too
A chunked body streamed to a sink went to it after every read, as
identity bodies did before the previous commit.  Its decoded bytes
now stay in the receive buffer the same way, until the window left
behind them would fall below the 32 KB floor:

- in the middle of a chunk, the next read appends behind them;
- at the end of a pass, they stay in front of the partial size line,
  as they do for a body kept whole, and are handed over with the
  slide of that line to the front only when due.

The last chunk hands over everything decoded, held bytes included.
Nothing is copied to hold them and the buffer does not grow for them.
2026-11-11 06:43:41 +01:00
LibretroAdmin a51c0facf1 formats/image: image_yuv_blit, pixels by byte order and UYVY
image_yuv_blit converted 4:2:0 to 32-bit words, XRGB8888 or (with
FLAG_RGBA) R in the low byte, alpha always 0xFF. A console's video
decoder and post-processor libraries, emulated, want the frame in the
guest's byte order - R, G, B, A or A, R, G, B in memory, whatever the
host - with the alpha the game asks for, and as packed UYVY 4:2:2.

- image_yuv_420_to_bytes: the same conversion, the same matrices (BT.601
  or BT.709, limited or full range) and the same fixed-point rounding,
  into four-byte pixels in memory order, IMAGE_YUV_BYTES_RGBA or
  IMAGE_YUV_BYTES_ARGB, with a given alpha byte; any 4:2:0 layout, as
  image_yuv_420_to_rgb32 takes it.
- image_yuv_420_to_uyvy: U, Y0, V, Y1 for each pixel pair, each chroma
  row for the two luma rows it covers, an odd last column's luma
  repeated.

Measured against FFmpeg's swscale on a 320x240 frame: the RGBA pixels
within 1 of swscale with accurate rounding (60 samples of 307200 off by
1), within 3 of its default fast path; UYVY identical to swscale's with
point sampling.

samples/formats/image_yuv/image_yuv_test checks, for sizes from 1x1 to
321x241, planar and interleaved chroma, both matrices and both ranges:
the RGBA bytes equal image_yuv_420_to_rgb32's FLAG_RGBA pixels, the ARGB
ones the same pixels moved, the alpha is the one asked for, nothing is
written past a row, and every sample is within 1 of the matrix in
floating point; and the UYVY layout sample by sample. Made to fail by
writing ARGB in RGBA order, by reading past an odd width's last luma
sample, and by swapping the chroma terms of green.
2026-11-11 06:43:07 +01:00
LibretroAdmin 8fa6a2ea24 task_http: bulk writes for downloads streamed to disk
The sink now gets a download in pieces of 32 KB or more (identity
bodies), so the file it writes asks for descriptor writes: no copy
through a stdio buffer on the desktops and Android.  The restart
path's seek and truncate work on the descriptor too.  Where
descriptor writes are not available the hint is ignored.
2026-11-11 06:41:53 +01:00
LibretroAdmin ac3ca0bc44 net_http: a streamed body reaches the sink in pieces of 32 KB or more
A body streamed to a sink went to it after every read, so the sink
was called once per TLS record, or once per few hundred bytes on a
slow link.  A body framed by Content-Length or by the close now stays
in the receive buffer, where it was read, until the window left
behind it would fall below the 32 KB floor; the sink then gets it in
one piece.  What is still held when the body ends goes with it, at
the last byte of a Content-Length body or at the close.

Nothing is copied to hold it and the buffer does not grow for it: the
hand-off comes before the window would shrink enough to trigger a
grow.  Progress already counts the held bytes (flushed + pos).  A
transfer that breaks loses at most what was held, which a resume asks
for again from the length on disk.

Chunked bodies are unchanged for now: they still go to the sink after
every read.
2026-11-11 06:41:49 +01:00
LibretroAdmin a5668b1a9a cloud_sync: bulk writes for downloaded files
A downloaded file is written in one piece (WebDAV, S3, Google Drive,
SMB, iCloud, iCloud Drive) or in 64 KB pieces (NFS), and the sync
task reads it back 256 KB at a time to hash it.  These streams are
opened read-write, which takes the descriptor path on the desktops
and Android now, so they ask for it: the body goes to the file in one
write() with no copy through a stdio buffer, and reads back without
one.  Where descriptor writes are not available the hint is ignored.

The updated manifest keeps its buffer: the JSON writer hands it over
1 KB at a time.
2026-11-11 06:38:01 +01:00
LibretroAdmin fddf92b579 task_content_disc: bulk writes for the dumped disc image
The image is written 32 sectors (64 KB) at a time, so it asks for
descriptor writes: no copy through a stdio buffer on the desktops
and Android.  The small .cue file keeps its buffer.  Where descriptor
writes are not available the hint is ignored.
2026-11-11 06:37:58 +01:00
LibretroAdmin 59ffefa9c7 libretro-common: bulk writes for file copy and archive extraction
Now that descriptor writes work for written streams on the desktops
and Android too, the libretro-common writers that write in big
pieces ask for them:

- filestream_copy: the destination, written in 256 KB pieces (the
  source already had the hint);
- file_archive_pending_copy_in_place: both ends, 64 KB pieces;
- the bz2 extractor's output file, written a whole decoded block
  (up to 900 KB) at a time.

Their bytes go to the file without a copy through a stdio buffer
and without the flush at close.  Where descriptor writes are not
available the hint is ignored and these streams stay buffered as
before.
2026-11-11 06:37:54 +01:00
LibretroAdmin 37713cbc45 formats/mpeg1: MPEG-2 main profile in rmpeg1_video
rmpeg1_video decoded MPEG-2 only in its narrowest form: progressive
frame I pictures with 8-bit DC, linear quantisation, zigzag scan and
Table B.14. Everything else - P and B pictures, interlace, the coding
tools - was skipped, so DVD video, a console's game videos (PS3 PAMF
M2V) and broadcast streams did not decode.

It now decodes H.262 main profile, 4:2:0, up to 4095x2800:

- frame pictures and field pictures; the two fields of a frame are
  decoded into the one frame (a field is its plane from line 0 or 1 at
  twice the stride, so nothing is copied) and come out as it, a field
  without its pair as the half it is
- frame, field and dual-prime prediction in frame pictures; field,
  16x8 and dual-prime prediction in field pictures, the second field of
  a P frame predicting from the first
- frame and field DCT
- MPEG-2 motion vectors: f_code per direction and component, the field
  vectors' halved predictors in frame pictures, concealment vectors
- MPEG-2 block reconstruction: 8 to 11-bit intra DC, the 12-bit escape,
  Table B.15 (intra_vlc_format), alternate scan, the non-linear
  quantiser, saturation and mismatch control
- quant_matrix_extension; picture and sequence extensions with the
  size extension bits and progressive_sequence (an interlaced sequence
  has whole field macroblock rows)
- skipped macroblocks as 7.6.6 has them: in B pictures by the vector
  predictors in the previous macroblock's directions

4:2:2, 4:4:4 and the scalable extensions are refused at the extension
that announces them; their pictures are counted as skipped.

Table B.15 is added to rmpeg1_tables.h, its run/level pairs those of
B.14 and checked prefix-free with them.

Fixes found on the way, MPEG-1 included:
- A slice's first macroblock_address_increment says where in the row
  the slice starts; it was taken as skipped macroblocks (refused in I
  pictures, wrong prediction in P and B).
- The last macroblock of a stream without a sequence_end_code, when it
  took fewer bits than a start code, was dropped as padding.
- A sequence header that does not load a quantiser matrix restores the
  default (11172-2 2.4.3.2, H.262 6.3.3) instead of keeping an earlier
  one.
- The planes are made again only when the size in macroblocks changes,
  not at each sequence header.

samples/formats/mpeg2/rmpeg2_test decodes three small FFmpeg streams
(progressive; interlaced with field DCT and motion; B.15, alternate
scan, the non-linear quantiser, 10-bit DC) whole and a byte at a time,
and in its second form compares a stream with a reference decode. The
workflow runs that against FFmpeg's decode, within 3 per sample and 0.5
on average, for FFmpeg-written streams and for eight ISO/IEC 13818-4
conformance streams (field pictures, dual prime, frame and field
pictures mixed, slices starting mid-row), fetched once into the Actions
cache, their md5s checked. All 42 main profile 4:2:0 conformance
streams were checked here against FFmpeg and libmpeg2: every one within
3 of both (teracom_vlc4, whose extreme coefficients the inverse
transforms treat differently, within 2 of libmpeg2) except the
D-picture stream, which is skipped as FFmpeg skips it. The MPEG-1
stream these tests were made with now matches FFmpeg in its last frame
(within 2, from 210). rdvd_h262_test's gates now flip the chroma format
instead of picture coding bits that are decoded.
2026-11-11 06:36:59 +01:00
LibretroAdmin ade67cf50d libretro-common: descriptor writes on the desktops, for read-write streams too
A stream opened for writing, or for reading and writing (updating an
existing file included), with RETRO_VFS_FILE_ACCESS_HINT_SEQUENTIAL_BULK
now takes the descriptor path on the file mapping targets (Linux, the
BSDs, macOS, Windows, Android) as it did on the consoles: no copy
through a stdio buffer either way, and a write is in the file for every
other handle at once. filestream_write_file is one write(), as the
consoles had it.

Fixed on the way, for descriptor streams:
- a file they create is made with fopen()'s permissions (0666 less the
  umask) where it was 0600, and on Windows writable where _wopen()'s
  pmode 0 made it read-only;
- flush succeeds instead of failing on the missing FILE;
- truncate works on the descriptor instead of failing on the missing
  FILE.

samples/file/vfs/vfs_descriptor_rw_test: create, write in place, read
back, another handle seeing a write unflushed, truncate both ways,
reopen to update, the owner's permissions; in every descriptor
configuration and on Windows (msvcrt under wine). vfs_bulk_read_test
expects the descriptor path for bulk writes where it is listed.
2026-11-11 06:25:56 +01:00
LibretroAdmin a78fbce8fe libretro-common: retro_seqlock, a record one thread updates and any thread reads
The writer never waits; a reader copies the record between two looks at
a sequence word and copies again when a write overlapped. Header only,
on retro_atomic. samples/rthreads/retro_seqlock_test: two million
updates against three readers, no torn copy and none going back; also
on aarch64.
2026-11-11 05:06:34 +01:00
LibretroAdmin 4cbd463b25 formats/jpeg: rjpeg_decode_rows, rows where the caller wants them
rjpeg decodes into an image it allocates, 4 bytes a pixel, which the
caller then copies or converts to where it is needed; and each image
takes the decoder's memory anew (its state, its component planes, the
image). A core emulating a console's JPEG decoder library wants the
rows in the guest's buffer, in the guest's formats, and no allocation
from one image to the next while a game runs.

rjpeg_decode_rows decodes a whole file in one call, read where it
lies, and hands each row over as it is made:

- size(): the image's size once the header is read, before any row,
  so a caller can check its buffer first (false stops the decode)
- row(): where row y goes - memory of the caller's, written in
  place, or NULL for rjpeg's own row
- done(): row y is written, at that place

The rows are those rjpeg_process_image makes, byte for byte, BGRA or
RGBA. Nothing else is allocated for them: the fused baseline and
progressive paths emit into the caller's rows instead of an image,
and the layouts decoded whole first (grayscale, non-interleaved scans)
are emitted afterwards the same way.

The decoder's state and its component arena are kept in the rjpeg_t
for the next image: an arena big enough is reused (zeroed where the
decode needs it zeroed), a bigger one replaces it. Once an image of a
size has been decoded, decoding another no bigger takes no memory.
rjpeg_start, rjpeg_iterate_image and rjpeg_process_image work as
before.

samples/formats/jpeg/rjpeg_test checks, for 4:2:0, 4:2:2, 4:4:4,
grayscale, progressive and CMYK files (made by libjpeg-turbo) and the
test's own: the rows match rjpeg_process_image's image in both byte
orders, written into the caller's rows (each its own allocation, so
AddressSanitizer catches a byte past one), rjpeg's own, or a mix; in
order, each once; a size refused writes no row; broken and cut files
fail and leave the decoder usable; and a second round over all the
files takes no allocation (counted by wrapping malloc and calloc where
the linker can, on Linux). Made to fail by dropping the kept arena and
by ignoring the caller's rows.
2026-11-11 05:05:09 +01:00
LibretroAdmin ccc4165176 tls_retro: count the duplex client's lines without their CR
The failing duplex check was the Windows client's, run under Wine in
the network clients job (EXE=.exe). A Windows binary ends its lines
with CR LF, so '^got: s[1-4]$' matched none of its four 'got:' lines,
the check counted 0 of 4, and it failed on every run although the
client read all four, sent all 60, and the server printed 'done'
(its -msg trace shows the line and both KeyUpdates). The lines are
counted with the CRs taken out.

The Wine run of the duplex check now passes three of three; it failed
four of four before. d9b00b7's longer server tail was not the cause;
it costs nothing and stays.
2026-11-11 04:47:45 +01:00
LibretroAdmin e216b5083e samples/file_path: the target list on one line
The samples workflow reads TARGETS a line at a time, so the list split
with a backslash gave it a target named "\" (failing the job as
unclassified) and never ran path_tree_test on the next line.
2026-11-11 03:01:12 +01:00
LibretroAdmin 3bce3840e6 formats/rvz: one Zstandard decoder per image
Each Zstandard group was decoded with rzstd_decode(), which makes and
frees the decoder's scratch every time. The reader now makes it once,
when the image opens, and decodes every group with
rzstd_decode_reuse(). The sample counts the allocations reads make and
checks that a Zstandard image read through once makes none.
2026-11-11 03:01:07 +01:00
LibretroAdmin 8ae89c9d76 libretro-common/rthreads: retro_shared_eventcount, an eventcount between processes
retro_eventcount's protocol with its state in memory two or more
processes map: a process parks a thread until another process (or a
thread of its own) publishes, with no lock on the publisher's path and
no polling. For a libretro core that runs its emulator in a process of
its own and shares rings with it through a mapping, and until now woke
on timers.

The state (epoch and waiters) lives in the shared memory, zeroed or
through retro_shared_eventcount_init. Each process attaches once under
a name common to all of them and then notifies, prepares, cancels and
commits as with retro_eventcount; commit_wait_timeout says whether a
notify came. A notify with nobody parked is one sequentially-consistent
read-modify-write and one load.

Linux and Android wait on the shared word with FUTEX_WAIT and
FUTEX_WAKE, not the _PRIVATE forms, and need no name. Windows uses a
named semaphore per object, opened by each process; a notify releases a
permit per thread in a wait window, and a permit left by a waiter that
did not sleep makes a later wait return early, which the waiting loop
allows for. Elsewhere, and where the atomics are not lock-free, attach
fails and the caller keeps its own fallback. C89.

samples/rthreads/retro_shared_eventcount_test: a parent and a forked
child hand a counter back and forth 200000 times, each parked between
turns, under a watchdog (on Windows two threads with handles of their
own); a notify inside the window; a timed wait with nothing published.
Passes on Linux under ASan/UBSan and on Windows (MinGW, under wine).
Run by the libretro-common samples workflow.
2026-11-11 02:05:40 +01:00
LibretroAdmin c2762af1b6 crypto: the XTS tweak loaded inside the AES-NI function
aes_xts() loaded the encrypted tweak into an __m128i and passed it to
aes_x86_xts(). aes_xts() is built for the TU's own baseline, which on
i686 has no SSE, so the load's always_inline _mm_loadu_si128 could
not be inlined into it ('target specific option mismatch') and the
i686 Linux and MXE builds stopped at crypto.c; the vector argument
also changed the ABI there (-Wpsabi). aes_x86_xts() now takes the
tweak's bytes and loads them itself, under its own target attribute,
as the other AES-NI functions take their keys.

i686 mingw builds crypto.c without errors or warnings (two errors
before), as do x86_64 mingw and Linux gcc; the crypto suite, XTS
vectors included, passes as 32- and 64-bit Windows binaries under
Wine on a CPU with AES-NI.
2026-11-11 01:58:40 +01:00
Codex 22c3146900 rtmp: validate borrowed AV1 delivery against a live receiver
Exercise AV1/AAC through the shared FLV mux and owned publisher over TCP and TLS 1.2/1.3. Compare packet hashes and timestamps, decoded frames and PCM, and capture the advertised encode capabilities.

Preserve frames and millisecond timing in the independent raw decode oracle instead of letting an inferred frame rate duplicate AV1 frames. Cover both command modes under native Windows, ASan/UBSan and MinGW/Wine. Frontend AV1 activation remains gated on native encoder acceptance.
2026-11-11 01:41:36 +01:00
Codex 60a78b0fb1 flv: mux borrowed AV1 temporal units for Enhanced RTMP
Validate av1C against its sequence and accept native sized configuration OBUs during cold setup. Preserve configuration metadata, enforce coded color/depth agreement, and validate one borrowed media OBU per step without conversion storage or payload copies.

Cover shown/hidden key frames, immutable output lifetime, bounds and malformed configurations. Independently compare AV1 8/10-bit packets, timestamps, decoded pixels and PCM, including source colorInfo and native configuration adaptation.
2026-11-11 01:17:58 +01:00
LibretroAdmin 8a14f39367 libretro-common/file: path_remove_tree and path_copy_tree
path_remove_tree() removes a file, or a directory with everything in it,
hidden entries included; nothing at the path counts as removed. A link
to a directory is removed, not followed: each directory entry is first
tried with rmdir (an empty directory, or a link on Windows) and unlink
(a link elsewhere), and only a real directory with something in it is
walked. One path buffer for the whole walk, grown in place.

path_copy_tree() copies a file, or a directory with everything in it,
making the destination's directories as needed and replacing files
already there. Each file goes through retro_vfs_copy_begin_impl in one
step, so the platform's own copy (a clone where the file system can
make one) moves the bytes with no buffer of ours.

Both go through the built-in VFS, as path_rmdir and
path_is_empty_directory do, and add no VFS backend function.

For parallel-ps4, whose save data, PKG install and app content code
removes and copies directory trees through std::filesystem.

samples/file/file_path/path_tree_test: copies at every depth, a hidden
file, an empty directory, trailing separators, copying over an existing
copy, a single file, removal, and a link to a directory outside the
tree being removed without what it points to; passes on Linux under
ASan/UBSan and on Windows (MinGW, under wine). Run by the
libretro-common samples workflow.
2026-11-11 01:14:54 +01:00
Codex 654744fbce record: stream native HEVC and HDR over Enhanced RTMP
Select HEVC Annex B framing and advertise the actual encoder and optional AAC track during worker setup. Derive colorInfo from coded encoder metadata, preserving absent mastering/light values and the existing core HDR ABI. Keep packet/audio leases and bounded worker scheduling unchanged.

Allow native HEVC streaming in the frontend and use its smaller FLV packet limit. Cover admission/teardown contracts, real SDR/PQ/HLG GPU streams, wire color metadata and threaded/direct frontend streaming. Correct the native fixture to use the codec-specific packet bound.
2026-11-11 01:02:40 +01:00
Codex 1442117a5e rtmp: advertise explicit codec capabilities during connect
Add optional cold constructors that serialize fourCcList and per-media capability maps before asynchronous setup. Keep existing configuration layouts and default command bytes unchanged; reject invalid codes, duplicate entries and unsupported flags.

Write AMF numbers directly into owned command storage. Verify exact wire values, capacity boundaries and capability lifetime through the native client, including standard/legacy RTMP and TLS 1.2/1.3 with independent decoded media.
2026-11-11 00:48:58 +01:00
Codex cb5d4d6597 flv: adapt native HEVC Annex B configuration and packets
Build hvcC from borrowed VPS/SPS/PPS and parse SPS profile, depth and temporal fields without staging an RBSP copy. Reserve packet conversion storage during cold setup and retain the existing one-NAL step contract.

Cover truncated configuration, emulation prevention and sublayer fields. Independently compare real HEVC 8/10-bit Annex B remuxes, including colorInfo, against source packets, decoded video and PCM.
2026-11-11 00:38:44 +01:00
Codex a6b3361aae flv: emit source color metadata before HEVC sequences
Serialize optional Enhanced RTMP colorInfo during cold setup, preserving unknown source fields and validating hvcC depth and mastering units. Keep metadata owned by the mux and media packets borrowed.

Cover exact AMF values and omissions, immutable output ownership, and independent HEVC/AAC packet, pixel and PCM interoperability. Require the native receiver to expose mastering and light metadata in Windows CI.
2026-11-11 00:32:48 +01:00
LibretroAdmin e630ac2e02 net/retrossl: SSL_RETRO_NO_SNI, a ClientHello without server_name
For a client that emulates one which never sent SNI, as the Wii's IOS
does: the ClientHello carries no server_name, and the certificate is
still checked against the host. The local server test reads the
server's trace for the extension, with and without the flag.
2026-11-11 00:31:36 +01:00
Codex 0b7881fcb7 flv: mux borrowed HEVC packets for Enhanced RTMP
Validate hvcC parameter-set records and emit hvc1 sequence and coded-frame tags, preserving signed composition offsets and borrowing length-prefixed access units. Keep AVC and AAC framing unchanged.

Check packet and decoded output against independent 8/10-bit fixtures, with B-frame timing, malformed records and producer lifetime coverage.
2026-11-11 00:00:05 +01:00
Codex 2f6cdda706 record: stream native NVENC video and AAC over RTMP
Connect the I/O-owned publisher to borrowed encoder packets and direct native AAC input. Keep producer input pending until publish setup completes, schedule network readiness alongside GPU completion, and bound setup and final drain.

Cover packet lifetime and cancellation contracts, native socket wakes, and independent GPU/AAC receiver decoding. Preserve file recording and reject unsupported streaming formats explicitly.
2026-11-10 23:46:07 +01:00
LibretroAdmin 9522cd8060 libretro-common/crypto: AES-XTS
XTS-AES (IEEE 1619, SP 800-38E) over whole blocks of one data unit:
aes_xts_encrypt() and aes_xts_decrypt(), with the data and tweak keys
as two aes_ctx and the tweak given as the 16 octets IEEE 1619 numbers
data units by. Eight blocks are in flight on AES-NI and on the ARMv8
crypto extension, the round keys made once per call; elsewhere one
block at a time. In and out may alias.

For disk and package formats that encrypt sectors with it (the PS4's
PKG PFS image, which parallel-ps4 decrypted with OpenSSL).

test_crypto: IEEE 1619 vectors 1 and 2, and random data units against
one block at a time and back; passes on x86-64 with and without the
AES-NI path and on aarch64 under qemu with and without the crypto
extension.
2026-11-10 23:24:03 +01:00
Codex 6ee48d2f87 net/rtmp: own incremental publisher connections
Parse publisher URLs and drive asynchronous resolution, TCP address fallback, TLS and RTMP setup on one I/O owner. Report readiness and local work without an internal wait or clock. Cancel transport resources while retaining producer borrows for retirement; drain plain TCP replies after normal send shutdown.

Exercise URL admission and canceled resolver capacity, plus independently decoded TCP/TLS media through the owned client. Wire normal and Griffin builds and retain completed session status during transport retirement.
2026-11-10 22:57:03 +01:00
LibretroAdmin d9b00b7e61 tls_retro: the cache test's put blocks, the duplex check's server waits
tls_cache_threads_test called tls_cache_put() with two arguments after
58ccb33 gave it a third, nonblock, and crypto-tsan-check stopped
building. The test stores to check every store landed, so it passes
false: a blocking put, as a handshake that is not nonblocking makes.

The duplex check's openssl server closed its input 1.5 s after sending
"end", and s_server shuts the connection down when its input ends. The
client sends its 60 lines 50 ms apart and then "done"; on a slow runner
the last of them were still on their way when the server shut down, so
the client finished cleanly (its 4 lines read, all of its own sent) but
the server never printed "done" and the check failed. Its input now
stays open 10 s after "end"; the server is killed a second after the
client exits, so the longer tail costs nothing. With the client's lines
slowed to 140 ms apart the old script fails the same way and the new
one passes.

crypto-tsan-check passes, and the local server matrix and the duplex
check pass.
2026-11-10 22:49:17 +01:00
Codex 4d06a59a07 formats/flv: adapt native Annex B AVC packets
Build AVC configuration from native SPS/PPS and reserve bounded conversion storage at startup. Adapt one immutable Annex B NAL per worker step while retaining ordinary length-prefixed packet borrows without a copy.

Check mixed start codes, admission and conversion bounds, then compare independent encoded packets, timestamps and decoded A/V with and without B frames.
2026-11-10 22:38:58 +01:00
Codex 56af306e27 net/rtmp: drain deleteStream before transport shutdown
Stop media admission after its final borrow retires and send deleteStream through the existing bounded command path. Preserve control traffic and partial transport grants until the close command is consumed; leave TLS flushing with the transport owner.

Cover exact command bytes, held media, partial writes and cancellation, and finish real TCP/TLS media fixtures before transport close.
2026-11-10 22:30:48 +01:00
Codex 122da7195a record: add borrowed native Windows AAC encoding
Lend native PCM input and retain raw AAC output until its consumer returns the packet. Keep native calls on one recording I/O owner, bound input admission, preserve source timestamps and load optional Media Foundation entry points at runtime.

Use private interface IDs to avoid bulk SDK GUID collisions. Verify real Windows encoding against an independent decoder, including mono/stereo, supported rates and bitrates, partial blocks, large clocks, cancellation and owner checks.
2026-11-10 22:23:07 +01:00
LibretroAdmin a5d7f8b5bd encodings: Shift-JIS (code page 932) and code page 1252 conversions
sjis_to_utf8(), utf8_to_sjis() and cp1252_to_utf8(), table driven and
allocation free, for cores that read Japanese and western text from
game data (GameCube and Wii banners, saves and memory cards) and today
pull in iconv for it. Shift-JIS is code page 932: JIS X 0208 with the
NEC and IBM extensions. Octets that do not form a character are
skipped one at a time.

The tables are generated by gen_sjis_tables.py from Python's cp932
codec. The sample checks known characters, skipping, truncation and a
round trip of every two-octet code.
2026-11-10 22:21:28 +01:00
LibretroAdmin 183ba70c15 net/retrossl: set the socket's blocking mode only when it changes
Every ssl_socket_receive_all_nonblocking / send_all_nonblocking /
flush_nonblocking call, and every blocking one, set the socket's mode
again: socket_set_block(), two fcntl() calls (an ioctlsocket on
Windows), however many times in a row the mode was already right. A
connection keeps the mode it last gave its socket and calls
socket_set_block() only when that changes, or once more after
socket_connect_with_timeout(), which leaves the socket nonblocking
behind its back. The socket is the connection's own: nothing else in
libretro-common or RetroArch sets the mode of a socket handed to it.

A duplex connection is made nonblocking as its handshake completes, so
neither of its two threads changes the mode afterwards (and they share
no write).

tls_duplex, 60 lines sent while 2 are read over TLS 1.3: 170 fcntl
calls before, 6 after. The local server matrix passes, and the duplex
check under ThreadSanitizer.
2026-11-10 21:56:06 +01:00
LibretroAdmin 1a6a5c2e91 formats/yaml: values below their keys, and \x, \u, \U escapes
Read against RPCS3's patch.yml (3 MB, 216803 nodes), ryaml stopped at
two things the file uses and YAML allows:

- A key's value on the line below it, more indented - a quoted or plain
  scalar, a flow collection or an alias - often with the key anchored:
      DeS_FPS_Notes: &DeS_FPS_Notes
        "Patch version 2.0 and above ..."
  A line there that is itself a key (quoted ones included) still makes
  the key a mapping, as before.
- \xNN, \uNNNN and \UNNNNNNNN in double-quoted scalars, as bytes and as
  UTF-8 ("shiv   \x00" in the patch data), and "\ " for a space. A bad
  digit is refused rather than read as something else.

With these, every key, scalar, sequence and mapping of that patch.yml
reads as yaml-cpp reads it, aliases included, and so do 3000 random
documents written by yaml-cpp's emitter (checked with a comparison
built against yaml-cpp, which RetroArch does not carry).

ryaml_writer writes any other control byte as \xNN instead of as it is.

ryaml_test covers values below their keys and the escapes, and refuses
a bad \x; ryaml_writer_test's random scalars now hold control bytes.
2026-11-10 21:43:36 +01:00
Codex 65f332c151 formats/flv: lend codec payloads to RTMP transports
Expose ready codec headers and packet spans without FLV file framing or a payload copy. Streaming consumers keep mux output and encoder data borrowed until the RTMP session returns them.

Exercise the mux-to-session path with independent decoded H.264/AAC output over TCP and TLS, including B-frame composition offsets, peer credit and cancellation of a live packet borrow.
2026-11-10 21:29:27 +01:00
LibretroAdmin 1b87a9af45 formats/yaml: ryaml_writer, the reader's counterpart
libretro-common could read YAML but not write it, so a core that keeps
its settings, patch configuration or caches as YAML (RPCS3) still needed
yaml-cpp for the writing.

ryaml_writer takes the document as calls - begin a mapping, a key, a
scalar, begin a sequence (block, or flow on one line), end - and lays it
out as block YAML, two spaces a level, the way yaml-cpp's emitter does
for such documents: a sequence under a key indented below it, a mapping
in a sequence item starting on the dash's line, empty collections as {}
and [], and blank lines between entries on request. A key or scalar is
quoted, double, with the escapes ryaml reads, only where ryaml would not
read it back as it is. Output goes through a callback a 4 KiB stage at
a time, or into a buffer the writer grows and hands over to read in
place; nothing else is allocated per call.

samples/formats/yaml_writer/ryaml_writer_test: a configuration and a
patch file byte for byte against their expected layout; 36 scalars that
need care (indicators, ": ", " #", blanks at the ends, line breaks,
quotes, backslashes, flow punctuation, UTF-8) as keys, values, items and
flow items, read back the same; 3000 random documents read back by
ryaml to the same tree, through memory and through a callback; and the
calls that make no document refused. With " #" left unquoted it fails.
2026-11-10 21:28:47 +01:00
LibretroAdmin bbbc50f1f6 formats/yaml: anchors and aliases, and where a node is
ryaml read neither "&name" nor "*name", and a document that used them
came out wrong rather than refused. RPCS3's patch files need them: an
entry [ load, *name, modifier ] stands for the sequence of patches
anchored as name under "Anchors:".

- "&name" ahead of a value, or of the lines that make it up, anchors
  the node; on a block mapping value, a sequence item or a flow item.
- "*name" as a value is a node of its own, with its own key, siblings
  and parent, that holds what the anchored node holds: every call that
  reads a value, a type or children reads the anchored node's.
  ryaml_is_alias() tells one apart. Aliases are resolved as they are
  read, so the names are gone with the parse; an alias of an alias
  holds the first node, and a name anchored again names the newer one.
- An alias to a name not anchored before it, an anchor with no name,
  and anything but a comment after an alias are refused.
- ryaml_location(): a node's line and column in the document, for
  messages about it (where its key, its dash or its flow item starts).

Nodes grow by eight bytes (the alias and the offset); a lookup costs
one test more. Nothing changes for a document without anchors.

samples/formats/yaml/ryaml_test, the reader's first test: a
configuration, block scalars, compact mappings, a patch file in
patch.yml's shape with load entries through aliases, anchors on
scalars, mappings and sequences, the documents that must be refused,
and node locations. The old reader fails the anchor, alias and location
checks and passes the rest.
2026-11-10 21:23:50 +01:00
LibretroAdmin fed60b7e05 net/retrossl: SSL_RETRO_DUPLEX, one thread receiving while another sends
A connection configured with SSL_RETRO_DUPLEX may be read on one thread
and written on another at the same time, with no lock - what a client
that keeps a reader and a writer thread on one socket needs (RPCS3's
RPCN client, which had wolfSSL's write_dup for it).

What the two sides shared:

- Receiving pushed out pending output (a sealed record the socket had
  not taken, a KeyUpdate owed) before reading. In duplex mode it does
  not: the sending side's buffers, keys and sequence number are the
  sending thread's alone.
- A KeyUpdate the server asks for was answered from the receive path.
  In duplex mode the receive path only raises key_update, now an atomic,
  and the next send or flush sends ours before its data, as RFC 8446
  4.6.3 asks. The blocking receive path does the same in duplex mode.
- ssl_socket_retro_pending() read the sending side's state for its
  write interest; in duplex mode it reports none.
- last_err could be written by both at once when the connection drops;
  it is an atomic, stored and read relaxed.

A duplex connection takes its record buffer for sending when it is
configured, not on its first send. Without the flag nothing changes but
key_update and last_err being atomics (plain loads and stores on x86 and
ARM).

samples/network/tls_retro/tls_duplex reads the lines openssl s_server
sends while a second thread sends 60 of its own, over TLS 1.3 with two
requested KeyUpdates and an unrequested one between the server's lines,
and over TLS 1.2. The server must print all 60 lines and its -msg trace
must show exactly two KeyUpdates from us. CI runs it in the local server
matrix and again built under ThreadSanitizer; with the receive path
still pushing output, TSan reports the races, and with the request not
raised, the server sees no KeyUpdate.
2026-11-10 21:21:04 +01:00
Codex 49582a9383 net: pump RTMP output through borrowed TLS connections
Add a TLS transport constructor and an explicit nonblocking drain to the existing RTMP socket owner. Retain stalled writes and drain accepted ciphertext before borrowing further session output; cancellation retires the session without closing caller-owned TLS state.

Exercise backpressure and drain failures, then compare decoded H.264/AAC from real TLS 1.2 and 1.3 transfers in standard and legacy command modes. Run the oracle natively under sanitizers and as Windows binaries through the recording CI job.
2026-11-10 21:07:43 +01:00
Codex 5f5f2f47ea test: bundle independent ATRAC9 codebook reference data
Run the complete Huffman reference checks from the sample directory without a private /tmp specification checkout. Pin the MIT-licensed LibAtrac9 source and provide an offline deterministic fixture generator; retain the external fixture argument.
2026-11-10 21:07:35 +01:00
LibretroAdmin 1087a448da formats/dds: BGRA block decoders, and one BC3 alpha ramp on every path
rdds_decode_block_bc1_bgra / bc2_bgra / bc3_bgra: the BC1-BC3 block
decoders with B,G,R,A in memory, the RGBA ones' values with red and
blue swapped (in the four reference colours, once per block). For a
core that decodes blocks for a BGRA8 texture itself, as RPCS3 does
when the GPU has no BCn; that core then needs no copy of bcdec.

The DDS loader uses them too: on a little-endian machine a BGRA video
driver now gets the direct row decoders the RGBA one had (interior
blocks written straight into the surface, no scratch block and no
repack per texel), where it went through the scratch path. Big-endian
keeps the scratch path. The order is a constant in each decoder (the
shared bodies are force-inlined), so the RGBA decoders are built as
before. Decoding a 2048x2048 DDS, best of 5 runs on x86-64:

            RGBA before/after    BGRA before/after
  BC1       4.42 / 4.29 ms        8.83 / 5.64 ms
  BC2       5.74 / 5.45 ms       12.22 / 6.70 ms
  BC3       6.83 / 7.01 ms       14.03 / 7.97 ms

The RGBA BC3 row decoder truncated its interpolated alpha while the
block decoder and the scratch path round it (to the D3D reference), so
an RGBA and a BGRA driver decoded the same DDS with alpha off by one on
some texels. It rounds now, as the rest do; the adds are the 3% on
RGBA BC3.

rdds_block_test checks the BGRA decoders against the RGBA ones over
60000 random blocks, and random BC1/BC2/BC3 DDS images, with edges,
decoded for an RGBA and a BGRA driver, texel for texel against the
block decoders (which fails on the old truncating ramp).
2026-11-10 21:02:27 +01:00
Codex 67c4a68f89 menu: admit MOV movies to the internal media player
Include MOV in the builtin browser filter and classify its extension as an MP4-family thumbnail source. This routes selection and external launches through the existing movie reader, including mixed-case extensions.

Cover browser listing and internal routing, and exercise real QuickTime MOV fixtures through browsing and external launch in all four menu drivers.
2026-11-10 20:51:31 +01:00
LibretroAdmin bf5c84a903 gfx/font: rtt hands out TrueType points as stored and composite components
rtt_glyph_points gives a simple TrueType glyph's points as the glyf
table stores them, in font units, with on-curve flags and contour
ends; rtt_glyph_components gives a composite glyph's components, with
their offsets or matched point numbers and their 2.14 matrices.

A caller that places glyphs in its own fixed-point arithmetic needs
them: a FreeType-compatible engine scales each component's points on
their own, then transforms and moves them, and rounds differently
from scaling the composite as a whole. The core of parallel-ps4 uses
them so its font library keeps FreeType's numbers exactly.

rtt_test checks both on its TrueType font: the square's four stored
corners, counts alone, a composite refused, and the composite's two
components with the second's half scale and offset.
2026-11-10 20:24:21 +01:00
Codex 58ccb33e28 net: advance TLS handshakes without blocking socket I/O
Add a TLS-only step API across the native, Mbed TLS and BearSSL backends. Preserve handshake state and pending ciphertext across calls, report readiness interests, and close stalled connections without waiting.

Snapshot resumption keys with the offered session and skip contended cache updates in nonblocking calls. Exercise fragmented records, backpressure, cancellation, cache replacement and contention, plus real server verification through the existing network CI jobs.
2026-11-10 20:07:06 +01:00
LibretroAdmin 798dcd7f33 formats/rvz: read a Wii partition's data decrypted
A Wii emulator reads a game's partition decrypted, and WIA and RVZ
hold that data decrypted already. Through rvz_read_begin() it came back
as the disc has it: every sector's hashes worked out and the sector
encrypted, for the caller to decrypt again at once.

rvz_read_decrypted_begin() arms a read of a partition's data at an
offset in it, 0x7c00 octets a sector, which rvz_read_step() runs as
it does any read: each sector's data is copied from its group as the
file stores it. rvz_decrypted_supported() says whether a range lies
within one of the partition's data segments. A read past them fails.

rvz_test reads the partition's data of each Wii image decrypted, in
order and anywhere, against the data the image was made from, then
the disc image again, and checks the ranges refused.
2026-11-10 20:04:01 +01:00
LibretroAdmin 024dcba88c formats/rvz: the file's compression method, level and chunk size
rvz_compression() gives the open file's RVZ_COMPRESSION_* method and
the compression level its disc struct records, and rvz_chunk_size()
the octets of the disc a group holds. A frontend or core showing what
an image is, or warning that its groups are large for random access,
needs them; until now only the reader itself could see them.

rvz_test writes a different level for each method, one negative as
Zstandard's may be, and checks all three for every image.
2026-11-10 20:03:57 +01:00
LibretroAdmin a86d86a22c HAVE_RGIF: GIF images through image_texture, image_transfer and the tasks
rgif now loads GIFs wherever RetroArch loads images: thumbnails and
backgrounds through task_image, the image viewer core, the file browser
and the image_texture/image_transfer layer underneath them all.

rgif:
- the image_transfer interface the other decoders have:
  rgif_set_buf_ptr, rgif_set_avail, rgif_header_ready and
  rgif_process_image, giving a 32-bit texture of its own, ARGB32 or
  ABGR32 in native byte order
- RGIF_ABGR, for ABGR32 on big-endian targets
- a file not all read yet gives IMAGE_PROCESS_WAIT and is not touched;
  the image is decoded in one call once it is all in

Wiring, as HAVE_RTGA is wired:
- IMAGE_TYPE_GIF (image.h) and NBIO_TYPE_GIF (task_file_transfer.h),
  both appended so no other value moves
- image_texture_get_type: .gif
- image_transfer: new, free, start, is_valid, set_buffer_ptr, process,
  set_avail; the support matrix gains a GIF column
- task_image and task_file_transfer: GIF is an image task
- FILE_TYPE_GIF ("gif", "GIF"): opened as image content
- the file browser's image extensions, the image viewer core's
  extensions, the System Information list ("GIF (RGIF)") and
  --features ("RGIF"); SUPPORTS_RGIF in config.features.h
- griffin.c, Makefile.common (rgif.o), configure (HAVE_RGIF=yes), and
  next to HAVE_RTGA in every platform Makefile, the Android, Apple,
  MSVC, UWP and QNX projects and the CI compile matrix
- tools/settings_check.py and tools/stack_budget.py check with it

Also: menu_displaylist's image viewer extension list wrote "tga"
without moving its length on, so "webp" and "dds" landed on top of it
and TGA dropped out of the list; it moves on now.

Tests: rgif_test checks the texture in both byte orders against
rgif_decode, the wait before the whole file is in, and a refused file;
image_decode_fuzz sweeps GIF too (every byte of a small GIF set to
edge values, and every truncation), under ASan and UBSan. The griffin
unity build with every codec, the touched objects in a configured
build, the MinGW/C89 syntax check, the overlap check and the stack
budget pass.
2026-11-10 20:00:24 +01:00
LibretroAdmin 4c1412cd0f gfx/font: rtt reads CFF fonts, vertical metrics and kerning; faster
rtt now reads OpenType fonts with CFF outlines as well as TrueType
ones, so the stb font renderer can load .otf files:

- Type 2 charstrings with local and global subroutines, hints and
  hintmasks, flex and the arithmetic operators;
- CID-keyed fonts, each glyph's font dict found by FDSelect (formats
  0 and 3), with the first 32 font dicts' subroutines found at init;
- vmtx/vhea vertical metrics, 'kern' format 0 pair kerning, and
  rtt_table for any table, all read in place;
- rtt_glyph_outline hands a glyph's outline to the caller, and
  rtt_glyph_cbox gives its control box;
- rtt_render_glyph_at takes a fractional origin for sub-pixel
  placement, and rtt_set_flatness a finer tessellation.

Checked against FreeType on Inter (CFF), the three faces of Noto Serif
CJK Bold (CID-keyed CFF, 65535 glyphs each) and DejaVu Sans: the same
glyph for every code point, advances, vertical advances, kerning and
control boxes. The control boxes match exactly for the CFF fonts, and
to within one unit for DejaVu, where FreeType moves a glyph whose
side bearing disagrees with its xMin.

TrueType rendering is byte for byte what it was, and faster: each
point is carried to the device once before the contours are walked,
glyf flags and coordinates are read without a check per byte where the
font holds them all, and curve steps multiply by an exact reciprocal.
Every glyph of DejaVu Sans at 12 to 20 pixels draws 5.5% faster, at 12
to 100 pixels 1.3% faster.

rtt_test, with a TrueType and a CID-keyed CFF font built by
rtt_fonts.py, checks cmaps, metrics, kerning, boxes and outlines, the
exact area a rendered square covers and a circle's to 0.4%, at
sub-pixel offsets, and every truncation and 1500 corruptions of each
font under the sanitizers; it runs on Linux and under Wine.
2026-11-10 19:58:09 +01:00
LibretroAdmin cddfd368da formats/gif: rgif, a GIF's first image straight into the caller's rows
libretro-common had no GIF decoder; cores that show or emulate one
(an emulated console's GIF decoder library, overlay art) carried
stb_image for it. rgif decodes a GIF's first image:

- from the file where it lies, nothing copied
- into the caller's memory at the caller's row pitch, so a core can
  decode straight into the guest's buffer, RGBA, ARGB or BGRA
- with its memory (the LZW table, about 20 KB) taken by rgif_new() and
  kept: one image after another takes nothing more
- strings written out from a stack in the decoder, not by recursion

It gives what stb_image 2.30 gives for the first frame: the logical
screen, the background colour (opaque) where the image does not reach
when the file names one past entry 0, transparent black for transparent
pixels and indices past the colour table, interlaced rows in their
passes, a file cut short decoded as far as it goes, and the same files
refused (no clear code first, codes past 8192, an image past the
screen, no colour table, an unknown block). The one difference is on
purpose: stb_image copies the background from its BGR-ordered table,
so its red and blue come out swapped; here they do not.

Checked against stb_image 2.30 over 2170 files and their truncations:
the same pixels everywhere but that background.

samples/formats/gif/rgif_test writes GIFs of every colour table size,
plain and interlaced, global and local tables, transparency, images
smaller than the screen with and without a background, LZW tables
that fill and are cleared or left full; decodes them in each byte
order into rows with padding (left untouched) and checks every pixel;
cuts them short; and refuses broken ones. Added to the samples
workflow.
2026-11-10 19:32:52 +01:00
LibretroAdmin c1c815b33e CI: retry the MSYS2 setup once when its system upgrade fails
The package installs after setup-msys2 already retry five times across
mirrors, but the action's own system upgrade (update: true) has no
retry: when mirror.msys2.org stalls on the package databases, pacman
gives up after ten seconds below a byte a second, skips the mirror for
the transaction, and the job fails before anything is built.

In the MSYS2 build and the Windows VFS samples the setup step now
continues on error, and a failed attempt is run once more after a
minute; the second run reinstalls over the first. A second failure
still fails the job. The upgrade stays in the action, since an
msys2-runtime update kills every MSYS2 process and a run step's shell
would not survive it. The Vulkan validation job does no upgrade in the
action and is left as it is.
2026-11-10 19:32:04 +01:00
LibretroAdmin 4356847c42 gfx/font: move the clean-room TrueType rasterizer into libretro-common
The TrueType loader and rasterizer the stb font renderer carried
inside gfx/drivers_font_renderer/stb.c moves, unchanged, to
libretro-common/gfx/font/rtt.c with its interface in
<gfx/font/rtt.h>, so cores can use it: sfnt/TTC directories, cmap
formats 0/4/6/12, hmtx/hhea/head/maxp metrics, glyf outlines and the
exact-coverage anti-aliased rasterizer with its SSE2/NEON resolves.

It reads the caller's bytes in place, opens no file, and allocates
only to grow the caller's rtt_scratch_t, as before. The scratch's
cell key is packed locally instead of through video_defines.h.

The stb renderer now calls it. Every glyph of four fonts at 12, 24,
48 and 100 pixels renders byte for byte as before, at the same speed.
2026-11-10 19:30:21 +01:00
Codex afcfce0d67 ozone: retain media previews through settings refreshes
Do not replace the settings preview player in browser selection or thumbnail refresh callbacks while media settings are active. Other thumbnail update paths already preserve this ownership.

Exercise external movie activation, the settings mouse button, stale browser selection and thumbnail refresh notifications, playback progress and Back. Check root and subfolder paths on Windows and rendered preview pixels in the GPU menu jobs.
2026-11-10 19:17:36 +01:00
LibretroAdmin 0580d2d6fc math/float16.h: half floats both ways, exactly
retro_half_to_float and retro_float_to_half convert IEEE 754 binary16
to and from binary32 with integer arithmetic only, so flush-to-zero and
denormals-are-zero modes change nothing. Narrowing rounds to nearest,
ties to even, as F16C and GPUs do; subnormal halves, signed zeros,
infinities and NaNs are kept (a signalling NaN comes back quiet).

The Direct3D and Vulkan HDR screenshot paths each had a private copy
of the widening; both use the header now.

float16_test checks every half against its value and its round trip,
the floats in and near the half range of both signs against F16C's
VCVTPS2PH (a stride elsewhere), a stride against the nearest half by
search, and the same again with FTZ and DAZ set.
2026-11-10 19:17:25 +01:00
LibretroAdmin 378865087c formats/png: rpng_rows keeps the colour chunks
gAMA, cHRM, sRGB, sBIT, cICP and mDCV are now kept as the file states
them, with where each came among them, for a caller that converts colour
as libpng would: an emulated console PNG decoder turning RGB into gray
weights the channels by the file's chromaticities and works in linear
light when the file has a gamma, as the libpng it emulates did.

Each is dropped where libpng 1.6 drops it: the wrong length, out of
place (after PLTE or the image data), a second of a kind, or a value
the specification forbids (a gamma over 31 bits, a cHRM value that is
no 32-bit integer, a rendering intent over 3, an sBIT of none or more
bits than the sample has). Before, gAMA, cHRM, sRGB and sBIT were
counted whatever they held; cICP and mDCV get bits of their own, above
the sixteen console libraries report.

rpng_rows_test covers each kind kept, the order, the broken ones and
the second of a kind dropped, sBIT by colour type, and the chunks after
PLTE.
2026-11-10 18:59:40 +01:00
LibretroAdmin 7600667d1b net_socket: socket_poll past 64 sockets on Windows
socket_poll runs on select() on Windows, and FD_SET drops a socket
once a set holds FD_SETSIZE (64): any socket past the 64th was never
reported ready, so a poll over more sockets waited out its timeout.

A Winsock fd_set is a count and an array that select() reads up to the
count. Up to FD_SETSIZE sockets the sets stay on the stack; past it
they are allocated for the call, sized for the sockets given.

socket_poll_test polls 150 loopback UDP sockets, on Linux and under
Wine: the two given a datagram, the 71st and the last, are the only
readable ones, all are writable, and none ready times out.
2026-11-10 18:59:12 +01:00
LibretroAdmin 86a5c9918b formats/mp3: gapless playback from the Xing/Info tag and LAME's delay
rmp3_parse_vbr_tag reads the tag frame an encoder puts first: Xing or
Info, with LAME's extension (encoder delay and padding) where there is
one, or VBRI. The side-information offset follows the frame's MPEG
version, channel mode and CRC.

The pull API uses it: the tag frame, which decodes to silence, is not
decoded; the encoder delay plus the decoder's own 529 samples
(RMP3_DECODER_DELAY) come off the start; reads stop at the track's
end, before the padding; and rmp3.totalFrames gives the length up
front. Seeks count from the trimmed start. A stream without the tag
decodes as before. The stream API is unchanged; its callers can use
rmp3_parse_vbr_tag.

rmp3_gapless_test checks LAME streams (MPEG-1 CBR and VBR, MPEG-2)
come out exactly as long as they went in and match ffmpeg's trimmed
decode to a rounding step, plus seeks, an untagged stream and tags
built by hand.
2026-11-10 18:49:55 +01:00
LibretroAdmin 0484a6d4f7 formats/png: rpng_rows, a PNG a row at a time as it is stored
A push decoder for callers with conversions of their own to apply, such
as an emulated console PNG library: input is handed over a piece at a
time, and each row comes out with its filter undone and nothing else
done to it - palette indices, gray at 1 to 16 bits, RGB(A) at 8 or 16,
as the file has them. Adam7 images come out pass by pass, each row
naming its image row and the columns its pixels go to. Before the first
row it reports the header, the palette, tRNS and which ancillary chunks
there were; at IEND the text and unknown chunks are counted.

The image data is inflated straight into the row being filled; two rows
and the inflate state are taken at the header, nothing after. Every CRC
is checked: a bad critical chunk is an error, a bad ancillary chunk is
dropped, as are ancillary chunks out of their place, as libpng drops
them.

rpng_rows_test writes every colour type and bit depth, plain and
interlaced, with every filter type and the image data split over
several IDATs, decodes them a byte and 4 KiB at a time, and checks the
pixels, the chunk bookkeeping and that broken files are refused.
2026-11-10 18:38:26 +01:00
LibretroAdmin 095b916b08 libretro-common: encoding_adler32
The Adler-32 rinflate and rdeflate keep for the zlib wrapper, as zlib's
adler32() gives it, under a name of its own: a caller that wants the
checksum alone, a GCZ disc image's block hash say, no longer needs zlib
for it. The kernel is the existing vector one.

encoding_adler32_test checks known values, a checksum continued across
pieces against the whole, and long runs against the textbook loop at
the block edges, the 5552-octet bound and all 0xff from the largest
seed.
2026-11-10 18:38:24 +01:00
LibretroAdmin 97dd672cc6 samples: run rdeflate_flush_test in CI
The samples workflow runs the targets a Makefile names TARGET, TARGETS
or TARGET_TEST<n>; rdeflate_flush_test was TARGET_FLUSH, so it was built
and never run. It is TARGET_TEST2 now.
2026-11-10 18:38:24 +01:00
Codex 8c62b973fd Measure HTTP stall tests with the production monotonic clock
Use the same clock as the timeout being checked so wall-clock adjustments cannot change the elapsed-time assertion. Retain the original bounds and print transfer state when a stall check fails.
2026-11-10 18:29:50 +01:00
Codex dd3a2d0831 Yield on partial native TLS records and control traffic
Retain partial record headers and bodies instead of switching a nonblocking read into a blocking receive. Process one record or buffered post-handshake message per service call, keeping direct application decryption.

Queue KeyUpdate replies in the retired handshake cork behind existing ciphertext and ahead of new application records. Preserve duplex reads under write backpressure and expose buffered work/write interest to HTTP waits.

Test fragmented input, mixed APIs, control ordering, socket failures and authenticated output. Drive OpenSSL HTTP and KeyUpdate acceptance through readiness waits. The handshake remains synchronous.
2026-11-10 18:24:38 +01:00
LibretroAdmin dd8f53271f net_upnp: a gateway from its description URL, and a mapping to another port
net_upnp_gateway_from_url sets a gateway up from the device description
URL discovery found it by, which net_upnp_discover now keeps in
gw->desc_url, so a caller that saved it skips the SSDP search the next
time. net_upnp_add_port_mapping_ext forwards one external port to a
different internal one, for a caller that tries the next external port
when one is taken; net_upnp_add_port_mapping is it with both the same.

The RPCS3 core replaces miniupnpc with these. net_upnp_test covers both
against its fake gateway.
2026-11-10 18:18:45 +01:00
LibretroAdmin 22b987902c net_http: redirects off on request, the caller's own headers; AES-CFB128
For an HTTP client that keeps its own rules (a console's HTTP library
emulated in a core):

- net_http_connection_set_redirects(conn, false) hands a 3xx back as it
  is - status, headers and body - instead of following its Location.
- An Accept-Encoding or User-Agent among the caller's headers replaces
  net_http's own instead of going out next to it.
- A request carrying a body sends its Content-Length whatever the
  method; a DELETE with a body went without one.

crypto: aes_cfb128_encrypt/decrypt, CFB with 128-bit feedback as
OpenSSL's AES_cfb128_encrypt, resumable mid-block. Checked against
SP 800-38A F.3.13 and F.3.17 and against itself split at random points.

samples/net/net_http_options_test checks the three request options
against a loopback server.
2026-11-10 18:17:37 +01:00
Codex 5dd6fbbfc8 Add bounded asynchronous resolver jobs
Dispatch lookups through an existing resolver executor and publish borrowed address chains without main-thread waits. Separate caller and worker references keep cancellation safe during queued and executing lookups; cap live requests instead of accumulating canceled jobs.

Exercise ownership failures, concurrent native DNS and the independent RTMP A/V receiver path. Keep executor thread lifetime outside the SDK request.
2026-11-10 18:03:29 +01:00
Codex d10f7ab438 Add incremental nonblocking socket connection setup
Separate connection admission from zero-time completion probing so streaming setup can yield while TCP connects. Validate SO_ERROR before reporting readiness and retain the platform-specific completion rules.

Exercise poll/select progress and refusal paths, and use the API in the independent RTMP receiver fixture.
2026-11-10 17:48:35 +01:00
LibretroAdmin a27ef55d56 libretro-common: rdeflate_flush, a sync flush
zlib's Z_SYNC_FLUSH: once the input given is consumed, the pending
symbols go out as a block and an empty stored block ends the output on
a byte boundary, so everything compressed so far decodes from the
output up to there. The window is kept. A log compressed as it is
written stays readable to its last line after a crash; the RPCS3 core
writes its .gz log that way.

rdeflate_flush_test compresses a stream a piece at a time with a flush
after each, at every level and with the output a few bytes at a time,
and decodes the output after every flush.
2026-11-10 17:44:37 +01:00
LibretroAdmin c867b2a34a formats/atrac9: ratrac9, a clean-room ATRAC9 decoder
Decodes ATRAC9 (PS4/PS Vita audio) frame by frame from the 4-byte
config: all six channel layouts (mono to 7.1 with LFE), all sixteen
sample rates, superframes, band extension and intensity stereo. Output
is 16-bit, 32-bit or float, interleaved or planar. Also savestates of
the decoder's history and a WAVE 'fmt ' reader for the config.

Written in two steps: a functional specification of the format and its
tables was drawn up from the reference decoder, and the decoder was
then written from that specification alone, without access to any
existing implementation.

Its 16-bit, 32-bit, float and planar output and bytes used are
bit-identical to LibAtrac9's on 384 generated streams covering every
rate, layout and superframe size. It decodes them about 2.8x as fast
(-O2, x86-64): a 64-bit bit reader, one-lookup Huffman tables that
yield scaled coefficients, and a double-precision split-radix DCT-IV
with SSE2 and NEON paths that give the same bits as the C one. One
allocation at open; none while decoding or resetting; no writable
globals. Where the reference reads outside its own tables (extension
with fewer than 13 or more than 20 units, fine precision 16 and 17),
the decoder rejects the frame or extends the table by its formula.

samples/formats/atrac9: ratrac9_vectors_test checks generated streams
against CRCs of the reference decoder's output, a reset and a savestate
mid-stream; ratrac9_test checks the bit reader, every codebook, the
DCT-IV, config and WAVE parsing, savestates and truncated input.
2026-11-10 17:42:23 +01:00
Codex 085bf1bf56 Send borrowed media through RTMP sessions
Admit encoded audio, video and data without copying packet payloads. Retire producer borrows explicitly, schedule controls between chunks, and bound output by acknowledged peer credit.

Announce outgoing chunk-size changes before media admission. Exercise cancellation, short writes, stalled credit and exhausted output, and compare H.264/AAC decoded through an independent RTMP receiver on Linux and MinGW/Wine.
2026-11-10 17:13:45 +01:00
LibretroAdmin 1dbab030d4 rslc: highp on GLSL ES samplers without a default precision
GLSL ES gives only sampler2D and samplerCube a default precision. A
sampler2DArray, sampler2DMSArray, samplerBuffer, an int or uint array
sampler or a storage image, as a uniform or a function parameter,
cannot compile declared without one, and the builder declares its
samplers without one. Such a declaration now prints its own precision,
highp where none is stated; sampler2D prints as it did.
2026-11-10 16:50:48 +01:00
LibretroAdmin ce3ee5174c net_upnp, raac: the stack budget and overlap copy checks
net_upnp_discover() held a 1536-byte SSDP datagram buffer next to a
NET_UPNP_URL_MAX location, a 2384-2512 byte frame against the 2048 the
budget allows for PSP's and GX's 8 KiB thread stacks. The datagram
buffer is allocated once per discovery instead, before the receive
loop, and freed with the socket; a failed allocation fails the
discovery as a failed socket does.

The parametric stereo decoder's six same-array copies are disjoint by
construction and go on the overlap allow list: a parameter row copied
from another row (the copy is guarded by src != num_env), the matrices
of envelope 0 from envelope num_env_old (guarded by num_env_old > 0),
and in_buf[i][32..37] down to [0..5].

tools/stack_budget.py reports no frame over 2048 bytes (Linux gcc and
x86_64 mingw); the overlap copy check is clean; net_upnp_test passes
under ASan and UBSan.
2026-11-10 16:32:31 +01:00
LibretroAdmin 7370c1a268 vfs_prefetch_offthread_test: let the read-ahead thread take its first hint
The reader went through the 16 MiB fixture in 64 KiB reads without
waiting, and under Wine the read-ahead thread that its first read
starts was not scheduled until the reader had reached the end: its
first look found only the last range published, [12.4 MiB, 16 MiB),
which it brought in as its one hint (3735552 bytes, 57 units), and
'a sequential reader is read ahead of' failed on hints > 1. That is
the thread working as meant - a newer range replaces one not yet
started - not a missing hint. After its first read the test now waits,
outside the timed read, until the thread has taken up that read's
hint; the rest of the file is then read while that hint sleeps, and
the thread brings in the latest range after it: two hints, still no
byte asked for twice.

The same job's PS2 build of vfs_implementation.c for
vfs_close_result_test is a mingw build, so retro_vfs_file_rename_impl
takes its Windows branch and rename_aside_path() went unused
(-Wunused-function). The helper is now built exactly where a branch
that calls it is.

Under Wine 10 the test passes five runs of five (2 hints, 7929856
bytes); vfs_readahead_test and vfs_close_result_test pass, and the
three build without warnings; vfs_implementation.c builds clean with
mingw plain, -DPS2, -D_3DS and -DLEGACY_WIN32, and natively.
2026-11-10 16:24:22 +01:00
LibretroAdmin 0803d4be13 rslc: early depth as conservative depth in GLSL
opts.early_z_depth_unchanged prints a stage's early fragment tests as
layout(depth_unchanged) on gl_FragDepth, under
GL_ARB_conservative_depth below GLSL 4.20 and GL_EXT_conservative_depth
in GLSL ES. A GL context without image load / store has no
early_fragment_tests, and conservative depth is how its driver is told
the stage leaves depth alone so the test can run early.

rslc_ext_test prints the dual-source stage that way at three versions;
check runs glslangValidator on the GLSL 4.50 one where installed.
2026-11-10 16:03:33 +01:00
LibretroAdmin 9cbbbcbacf rslc: framebuffer fetch in GLSL
An input attachment, the colour attachment's value on entry, prints in
GLSL where the caller names the extension it has with opts.fb_fetch:
GL_EXT_shader_framebuffer_fetch reads it from the colour output at the
attachment's index, which prints inout, and
GL_ARM_shader_framebuffer_fetch from gl_LastFragColorARM. Without one
it is refused as before. A stage built for Vulkan and Metal with a
subpass input for its blending can then print for a GL ES context that
fetches from the framebuffer.

rslc_ext_test prints the framebuffer fetch stage both ways, and checks
EXT's refusal of an attachment with no colour output at its index.
2026-11-10 16:03:30 +01:00
LibretroAdmin 1f59acacbe rslc: subgroup operations in GLSL
GLSL prints subgroupMin, subgroupMax, subgroupElect and
gl_HelperInvocation as they are, under GL_KHR_shader_subgroup's basic
and arithmetic extensions, where it refused them before. A core that
reduces its bounding box over the subgroup on OpenGL can then build the
stage instead of writing GLSL for the driver. HLSL still refuses them.

rslc_ext_test prints the bounding box stage for GLSL and GLSL ES and
reads the GLSL back; check runs glslangValidator on it where installed.
2026-11-10 16:03:26 +01:00
573 changed files with 81873 additions and 4443 deletions

No files matched your search

+30 -1
View File
@@ -102,6 +102,7 @@ jobs:
./vfs_v5_metadata_test
./vfs_readahead_test
./vfs_close_result_test
./vfs_descriptor_rw_test
}
# Mapping on (what the platform actually ships), mapping off
# with the descriptor path forced (what the consoles have),
@@ -146,7 +147,26 @@ jobs:
# The system upgrade runs in the action: an msys2-runtime update
# kills every MSYS2 process, which a run step's own shell would
# not survive.
- uses: msys2/setup-msys2@v2
- name: Set up MSYS2
id: msys2
continue-on-error: true
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.sys }}
update: true
# setup-msys2 has no retry of its own, and its system upgrade fails
# the job when mirror.msys2.org stalls ("Operation too slow") on the
# package databases. A failed attempt is run once more after a
# minute; the second reinstalls over the first.
- name: Wait before retrying MSYS2
if: steps.msys2.outcome == 'failure'
shell: bash
run: sleep 60
- name: Set up MSYS2 (retry)
if: steps.msys2.outcome == 'failure'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.sys }}
update: true
@@ -206,6 +226,15 @@ jobs:
./vfs_prefetch_offthread_test.exe
# The descriptor path's close() result, with PS2's semantics.
./vfs_close_result_test.exe
# Read-write descriptor streams on the Windows C runtimes,
# and that a file they create is writable afterwards.
./vfs_descriptor_rw_test.exe
# Reads and appends at offsets from several threads through
# the second handle: opened relative to the stream's, the same
# checked as before Vista, and by the path; a -1 fails each.
./vfs_positional_nommap_test.exe
./vfs_positional_ntverify_test.exe
./vfs_positional_pathreopen_test.exe
# The Win64 fastjmp_set stores xmm6-xmm15 with movaps and faults
# on a misaligned buffer. Linux has no such stores, so the Linux
+19
View File
@@ -7,6 +7,10 @@ name: CI Linux EGL hardware core
# bound to the window surface the video thread holds, and with it off.
# Mesa takes the surfaceless path by itself; a preloaded shim hides
# parts of its EGL so the pbuffer paths run too.
#
# Then a core that draws on a thread of its own through
# RETRO_ENVIRONMENT_GET_HW_SHARED_CONTEXT_INTERFACE, on GLX and on EGL,
# threaded and not: the colour on screen must be that thread's drawing.
on:
push:
@@ -21,6 +25,10 @@ on:
- 'gfx/video_thread_hw.c'
- 'gfx/video_thread_wrapper.c'
- 'samples/gfx/egl_hw_core/**'
- 'samples/gfx/gl_shared_context/**'
- 'gfx/drivers_context/x_ctx.c'
- 'runloop.c'
- 'libretro-common/include/libretro.h'
- '.github/workflows/Linux-egl-hw-core.yml'
pull_request:
paths:
@@ -32,6 +40,10 @@ on:
- 'gfx/video_thread_hw.c'
- 'gfx/video_thread_wrapper.c'
- 'samples/gfx/egl_hw_core/**'
- 'samples/gfx/gl_shared_context/**'
- 'gfx/drivers_context/x_ctx.c'
- 'runloop.c'
- 'libretro-common/include/libretro.h'
- '.github/workflows/Linux-egl-hw-core.yml'
workflow_dispatch:
@@ -125,6 +137,13 @@ jobs:
make -C samples/gfx/egl_hw_core
timeout 600 python3 samples/gfx/egl_hw_core/egl_hw_core_test.py ./retroarch
- name: Run gl_shared_context_test
shell: bash
run: |
set -eu
make -C samples/gfx/gl_shared_context
timeout 300 python3 samples/gfx/gl_shared_context/gl_shared_context_test.py ./retroarch
- name: Save ccache
# Only master's pushes keep a cache: pull requests restore
# master's, so one push does not evict the next one's.
@@ -171,6 +171,9 @@ jobs:
make -C ../tls_retro clean
SSL_BACKEND=$be ../tls_retro/verify_hook_test.sh
make -C ../tls_retro clean
SSL_BACKEND=$be CFLAGS='-O1 -g1 -std=gnu89 -Wall -Wextra -Werror=declaration-after-statement -fsanitize=address,undefined -fno-omit-frame-pointer' \
sh ../tls_retro/handshake_test.sh
make -C ../tls_retro clean
- name: Operate the libsmb2 backend (SMB VFS against a local smbd)
if: matrix.lane == 'libsmb2'
@@ -125,7 +125,12 @@ jobs:
rtmp_publish_test
rtmp_control_test
rtmp_session_test
rtmp_client_test
rtmp_socket_test
rtmp_connect_test
rtmp_connect_select_test
rtmp_resolve_test
rtmp_resolve_native_test
rslc_test
rslc_build_test
rslc_ext_test
@@ -134,6 +139,8 @@ jobs:
s16_to_float_test
float_to_s16_test
float_to_s32_test
# G.711 mu-law; a step below compares it with FFmpeg's.
float_to_ulaw_test
compat_fnmatch_test
snprintf
unbase64_test
@@ -146,8 +153,11 @@ jobs:
rzstd_raw_block_test_novec
rzstd_dict_test
rbzip2_test
shiftjis_test
rinflate_window_test
rdeflate_hash_test
rdeflate_flush_test
encoding_adler32_test
emscripten_mmap_test
archive_zip_test
archive_zstd_test
@@ -163,7 +173,19 @@ jobs:
fill_pathname_test
path_mkdir_test
path_parent_dir_test
# path_remove_tree and path_copy_tree on a real tree, links
path_tree_test
rpng
# a PNG a row at a time, as stored, from input in pieces
rpng_rows_test
# a GIF's first image, straight into the caller's rows
rgif_test
# YAML: configurations, block scalars, a patch file whose
# entries name anchored sequences by alias, node locations
ryaml_test
# what the YAML writer writes reads back the same, over
# tricky scalars and 3000 random documents
ryaml_writer_test
rzip_chunk_size_test
rzip_matches_buf_test
rzip_codec_test
@@ -175,6 +197,41 @@ jobs:
# rvorbis' codebook size bound, and a real stream past it.
rvorbis_codebook_test
rjpeg_test
# Baseline JPEG encoding, decoded back with rjpeg; a step
# below has FFmpeg decode what it writes.
rjpeg_encode_test
# MP4 writing, read back with rmp4; a step below has FFmpeg
# decode the files.
rmp4_mux_test
# MPEG-2 video through rmpeg1_video, on its own fixtures; the
# step below runs it again over FFmpeg's and the conformance
# streams.
rmpeg2_test
# MPEG-4 Part 2 video through rmpeg4_video, on its own
# fixtures, whole, a unit at a time and damaged; a step below
# runs it over FFmpeg's and Xvid's streams.
rmpeg4_test
# MPEG-4 Part 2 encoding, decoded back with rmpeg4_video; a
# step below has FFmpeg decode the streams.
rmpeg4_encode_test
# AAC-LC encoding, decoded back with raac; a step below has
# FFmpeg decode the streams.
raac_encode_test
# H.264 encoding, decoded back with rh264; a step below has
# FFmpeg decode the streams.
rh264_encode_test
# Tags in each container and ID3 version, and every prefix
# and corruption of each file read inside it.
rtags_test
# A file's audio_transfer arm from its bytes, and the folds.
audio_transfer_sniff_test
# ATRAC3 and ATRAC3plus in WAVE and OpenMG through audio_transfer.
audio_transfer_atrac_test
# AVI and ASF headers: streams, codecs, sizes, rates, length.
rcontainer_test
# 4:2:0 to bytes in memory order and to UYVY, and bytes to
# 4:2:0; a step below compares the latter with swscale's.
image_yuv_test
rh264_lossless_test
rh264_lossless_test_nothreads
rh264_lossless_test_volatile
@@ -184,6 +241,8 @@ jobs:
rh264_state_test
# rmp3's frame-at-a-time API against its pull API.
rmp3_frame_test
# rmp3's gapless trimming against ffmpeg's, and its tag parser.
rmp3_gapless_test
# raac_last_size over units followed by more.
raac_size_test
# raac's savestates: saved between units, loaded into a new decoder.
@@ -203,6 +262,7 @@ jobs:
r7z_stored_test
r7z_methods_test
r7z_stream_test
r7z_peek_threads_test
rzip_archive_test
# The RAR reader on real archives (LZ and PPMd blocks,
# filters, Unicode names) and on mangled copies of them.
@@ -225,6 +285,25 @@ jobs:
filestream_rbuf_fault_test
vfs_rename_replace_test
vfs_close_result_test
# A read-write stream on the descriptor path, as an emulated
# file system uses it: in-place writes, truncate, reopen.
vfs_descriptor_rw_test
# Positional I/O (VFS v6): reads at offsets and appends from
# several threads on one stream, with no lock.
vfs_positional_test
# intfstream reads at offsets on file, buffered and memory
# streams: position kept, window kept, four threads.
intfstream_read_at_test
# The same without HAVE_MMAP (Android's build): pread/pwrite
# are still there, so a -1 fails.
vfs_positional_nommap_test
# Where off_t is 32 bits: past 4 GiB refused, never read
# 4 GiB lower.
vfs_positional_off32_test
# Windows NT before Vista's second handle (the path, checked);
# the same run as vfs_positional_test here.
vfs_positional_pathreopen_test
vfs_positional_ntverify_test
cdrom_cuesheet_overflow_test
rdvd_h262_test
rdvd_lpcm_test
@@ -253,6 +332,15 @@ jobs:
http_parse_test
# UPnP gateway client: parsers, and SOAP against a fake gateway.
net_upnp_test
# net_http: a 3xx handed back unfollowed, a DELETE body's
# Content-Length, the caller's Accept-Encoding/User-Agent.
net_http_options_test
# socket_poll past FD_SETSIZE sockets.
socket_poll_test
# Half floats both ways, against F16C.
float16_test
# TrueType and CID-keyed CFF glyphs, metrics and kerning.
rtt_test
rjson_test
rm3u_test
rmp4_fragment_test
@@ -364,6 +452,16 @@ jobs:
# forces every tier on Linux, Windows and macOS runners.
retro_procbarrier_test
retro_asym_eventcount_test
# Two processes hand a counter back and forth 200000 times,
# each parked between turns: a lost wake hangs it, and its
# own watchdog reports that as a failure.
retro_shared_eventcount_test
# A writer updates a record two million times while three
# threads copy it: no copy torn, none going back.
retro_seqlock_test
# Twenty threads change one record two million times in all
# while three copy it: no change lost, no copy torn.
retro_cas_record_test
sthread_ex_test
fast_cores_test
memshm_test
@@ -407,6 +505,11 @@ jobs:
# stereo stream (tones, gain points, swap and negate masks)
# and a mono one, then bad frames and garbage.
ratrac3p_test
# ATRAC9 against a reference decoder's output for generated
# streams in every channel layout, rate and superframe size,
# with reset and savestates; then its own unit checks.
ratrac9_vectors_test
ratrac9_test
# Animation playback against a scripted stream: order,
# pacing, loops, catch-up, the worker's two jobs.
video_player_test
@@ -696,6 +799,435 @@ jobs:
ffmpeg -hide_banner -loglevel error -i "$fixture" -pix_fmt yuv420p -f rawvideo -y "$reference"
./rdvd_h262_test "$fixture" "$reference"
# The ISO/IEC 13818-4 streams never change (their md5s are checked):
# one good download is kept in the Actions cache
- name: Restore the MPEG-2 conformance streams
if: matrix.group == 'formats'
id: mpeg2_conformance
uses: actions/cache/restore@v4
with:
path: ~/mpeg2-conformance
key: mpeg2-conformance-main-8-1
# MPEG-2 video against FFmpeg's decode, within 3 per sample (two
# IEEE 1180 inverse transforms): streams FFmpeg writes, progressive,
# interlaced (field DCT, field motion) and with Table B.15, alternate
# scan, the non-linear quantiser and 10-bit intra DC; and conformance
# streams with field pictures, dual prime, frame and field pictures
# mixed, slices starting mid-row
- name: Decode MPEG-2 video against FFmpeg and conformance streams
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats/mpeg2
run: |
set -euo pipefail
make -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
src="-f lavfi -i testsrc2=size=352x288:rate=25,format=yuv420p -frames:v 24"
i=0
for opts in "-bf 2 -g 12 -q:v 4" "-bf 2 -g 12 -q:v 4 -flags +ildct+ilme -top 1" \
"-bf 2 -g 12 -q:v 3 -qmax 28 -intra_vlc 1 -non_linear_quant 1 -alternate_scan 1 -dc 10"; do
i=$((i+1))
ffmpeg -hide_banner -loglevel error $src -c:v mpeg2video $opts -f mpeg2video -y "$work/g$i.m2v"
ffmpeg -hide_banner -loglevel error -i "$work/g$i.m2v" -fps_mode passthrough \
-pix_fmt yuv420p -f rawvideo -y "$work/g$i.yuv"
./rmpeg2_test "$work/g$i.m2v" "$work/g$i.yuv"
done
conf="$HOME/mpeg2-conformance"
mkdir -p "$conf"
base=https://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_IEC_13818-4_2004_Conformance_Testing/Video/bitstreams/main-profile
# A stream the server will not give is reported and left out
# rather than failing the job; the cache is saved only once every
# stream is in it, and kept from then on
missing=0
while read -r path md5; do
f="$conf/$(basename "$path")"
if ! { [ -f "$f" ] && echo "$md5 $f" | md5sum --quiet -c -; }; then
if ! curl --fail --silent --show-error --location --retry 3 --retry-all-errors \
--retry-max-time 60 --connect-timeout 10 --max-time 60 "$base/$path" -o "$f" \
|| ! echo "$md5 $f" | md5sum --quiet -c -; then
echo "::warning title=MPEG-2 conformance::$path could not be fetched; left out"
rm -f "$f"
missing=1
continue
fi
fi
gunzip -c "$f" > "$work/c.m2v"
ffmpeg -nostdin -hide_banner -loglevel error -i "$work/c.m2v" -fps_mode passthrough \
-pix_fmt yuv420p -f rawvideo -y "$work/c.yuv"
./rmpeg2_test "$work/c.m2v" "$work/c.yuv"
done <<'LIST'
tcela/tcela-8-fp-dp/tcela-8.bits.gz f0ef12c0e4f4a6cdb9ba42d000054c8b
hhi/hhi_burst_short/hhi_burst_short.bits.gz a68d3436d89ffc896393b18e8556a33c
nokia/nokia6/nokia6_dual.bit.gz 9890f21e949716f67469240d4e3105c0
toshiba/toshiba_DPall-0/toshiba_DPall-0.mpg.gz d92090842a0c2e37cf1cda7212868cc3
sony/sony-ct3/sony-ct3.bs.gz 31bb0abfaf802c754254b38b500d7685
tcela/tcela-6-slices/tcela-6.bits.gz 1247cf986205e7dfe4413bd223be8ee6
mei/mei.2conftest.4f/mei_2stream.4f.gz 505375e1f68fe527f52eb3604cc8edae
ti/TI_cl_2/TI_c1_2.bits.gz 3b25d76d4b517ec3ab493f2f3fb767c0
LIST
[ "$missing" = 0 ] && touch "$conf/complete" || rm -f "$conf/complete"
- name: Save the MPEG-2 conformance streams
if: matrix.group == 'formats' && steps.mpeg2_conformance.outputs.cache-hit != 'true' && hashFiles('~/mpeg2-conformance/complete') != ''
uses: actions/cache/save@v4
with:
path: ~/mpeg2-conformance
key: mpeg2-conformance-main-8-1
# MPEG-4 Part 2 video against FFmpeg's decode, within 3 per sample:
# streams FFmpeg's mpeg4 encoder writes (B-VOPs, quarter sample, four
# vectors, MPEG quantisation, interlaced field DCT and motion with
# alternate scan, AC prediction, resync markers, data partitioning, a
# size not a multiple of 16), Xvid's
# (GMC S-VOPs), and H.263 for the short video header, with and
# without GOB headers. FFmpeg decodes with one thread: its frame
# threads do not share the field vectors interlaced direct mode reads.
# What rjpeg_encode writes - 4:2:0 and 4:2:2, sizes in and out of
# whole MCUs, qualities 20 to 100 - must decode in FFmpeg with no
# error
- name: Decode rjpeg_encode's JPEGs with FFmpeg
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats/jpeg_encode
run: |
set -euo pipefail
make -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
./rjpeg_encode_test "$work"
n=0
for f in "$work"/*.jpg; do
ffmpeg -nostdin -hide_banner -loglevel error -xerror -i "$f" -f null -
n=$((n+1))
done
[ "$n" = 9 ]
# What rmp4_mux writes - MPEG-4 video with B-VOPs and 16-bit PCM,
# motion JPEG and float PCM - must decode in FFmpeg with no error,
# every frame there and the first shown at time zero
- name: Read rmp4_mux's files with FFmpeg
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats/mp4_mux
run: |
set -euo pipefail
make -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
./rmp4_mux_test "$work"
for f in mpeg4_pcm:8 mjpeg_f32:6; do
file="$work/${f%%:*}.mp4"
ffmpeg -nostdin -hide_banner -loglevel error -xerror -i "$file" -f null -
got=$(ffprobe -v error -count_frames -select_streams v \
-show_entries stream=nb_read_frames,start_time -of csv=p=0 "$file")
echo "$file: $got"
[ "$got" = "0.000000,${f##*:}" ]
done
- name: Decode MPEG-4 video against FFmpeg
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats/mpeg4
run: |
set -euo pipefail
make -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
i=0
while IFS='|' read -r size fmt opts; do
i=$((i+1))
ffmpeg -nostdin -hide_banner -loglevel error \
-f lavfi -i "testsrc2=size=$size:rate=25,format=yuv420p" -frames:v 24 \
$opts -f "$fmt" -y "$work/s$i.bin"
ffmpeg -nostdin -hide_banner -loglevel error -threads 1 -flags +bitexact -idct simple \
-f "$fmt" -i "$work/s$i.bin" -fps_mode passthrough -pix_fmt yuv420p -f rawvideo -y "$work/s$i.yuv"
./rmpeg4_test "$work/s$i.bin" "$work/s$i.yuv"
done <<'LIST'
352x288|m4v|-c:v mpeg4 -g 12 -bf 2 -q:v 4
352x288|m4v|-c:v mpeg4 -g 12 -bf 2 -q:v 4 -flags +qpel+mv4
352x288|m4v|-c:v mpeg4 -g 12 -bf 2 -q:v 4 -mpeg_quant 1
352x288|m4v|-c:v mpeg4 -g 12 -bf 2 -q:v 4 -flags +ildct+ilme
352x288|m4v|-c:v mpeg4 -g 12 -bf 2 -q:v 3 -mpeg_quant 1 -flags +ildct+ilme+qpel -alternate_scan 1
352x288|m4v|-c:v mpeg4 -g 12 -bf 2 -q:v 8 -flags +aic
352x288|m4v|-c:v mpeg4 -g 12 -bf 2 -q:v 4 -ps 400
352x288|m4v|-c:v mpeg4 -g 12 -bf 0 -q:v 6 -ps 300 -data_partitioning 1 -flags +mv4+aic
350x286|m4v|-c:v mpeg4 -g 12 -bf 2 -q:v 4 -flags +mv4+qpel
352x288|m4v|-c:v libxvid -g 8 -bf 0 -q:v 4 -gmc 1 -flags +qpel+mv4
352x288|m4v|-c:v libxvid -g 8 -bf 0 -q:v 4 -flags +ildct
176x144|h263|-c:v h263 -g 12 -q:v 5
352x288|h263|-c:v h263 -g 12 -q:v 5 -ps 300
LIST
# What rmpeg4_encode writes - whole macroblocks and not, fine and
# coarse quantisers, a bit rate - FFmpeg decodes within 3 per sample
# of rmpeg4_video, which decodes it exactly as the encoder
# reconstructed it (the sample checks that)
- name: Decode rmpeg4_encode's streams with FFmpeg
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats
run: |
set -euo pipefail
make -C mpeg4 -j2 SANITIZER=address,undefined
make -C mpeg4_encode -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
./mpeg4_encode/rmpeg4_encode_test "$work"
n=0
for f in "$work"/*.m4v; do
ffmpeg -nostdin -hide_banner -loglevel error -xerror -threads 1 -flags +bitexact -idct simple \
-i "$f" -pix_fmt yuv420p -f rawvideo -y "${f%.m4v}.yuv"
./mpeg4/rmpeg4_test "$f" "${f%.m4v}.yuv"
n=$((n+1))
done
[ "$n" = 5 ]
# What raac_encode writes - tones, noise, an attack, mono and
# stereo, 8 to 96 kHz - FFmpeg decodes within 2 per sample of raac
# (the sample checks raac's decoding against the input)
- name: Decode raac_encode's streams with FFmpeg
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats/aac_encode
run: |
set -euo pipefail
make -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
./raac_encode_test "$work"
n=0
for f in "$work"/*.aac; do
ffmpeg -nostdin -hide_banner -loglevel error -xerror -i "$f" \
-f s16le -y "${f%.aac}.ff.s16"
./raac_encode_test --compare "${f%.aac}.s16" "${f%.aac}.ff.s16"
n=$((n+1))
done
[ "$n" = 9 ]
# What rh264_encode writes - whole macroblocks and cropped, fine
# and coarse quantisers, a bit rate - FFmpeg decodes to exactly the
# encoder's reconstruction, as rh264 does (the sample checks that)
- name: Decode rh264_encode's streams with FFmpeg
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats/h264_encode
run: |
set -euo pipefail
make -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
./rh264_encode_test "$work"
n=0
for f in "$work"/*.264; do
ffmpeg -nostdin -hide_banner -loglevel error -xerror -i "$f" \
-f rawvideo -pix_fmt yuv420p -y "${f%.264}.ff.yuv"
cmp "${f%.264}.yuv" "${f%.264}.ff.yuv"
n=$((n+1))
done
[ "$n" = 5 ]
# What FFmpeg writes as tags - ID3v2.3 and 2.4, ID3v1 alone, FLAC,
# Vorbis and Opus in Ogg, MP4, ASF, WAVE - rtags reads as ffprobe
# does, non-ASCII text, numbered genres and full dates in
- name: Read FFmpeg's tags with rtags
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats/tags
run: |
set -euo pipefail
make -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
tags=(-metadata 'title=Tïtle ☃' -metadata 'artist=Ärtist'
-metadata 'album=Albüm' -metadata genre=Rock -metadata track=3/12
-metadata disc=1/2 -metadata date=2004)
enc() { ffmpeg -nostdin -hide_banner -loglevel error -f lavfi -i sine=d=0.2 "$@"; }
enc "${tags[@]}" -id3v2_version 3 -y "$work/v23.mp3"
enc "${tags[@]}" -id3v2_version 4 -y "$work/v24.mp3"
enc -id3v2_version 3 -metadata genre=17 -metadata date=2004-05-01 \
-metadata 'title=a long title with a snowman, ☃' -y "$work/genre.mp3"
enc "${tags[@]}" -write_id3v1 1 -y "$work/both.mp3"
# ID3v1 alone: the ID3v2 tag in front cut off
python3 -c 'import sys
d = open(sys.argv[1], "rb").read()
s = (d[6] & 127) << 21 | (d[7] & 127) << 14 | (d[8] & 127) << 7 | (d[9] & 127)
open(sys.argv[2], "wb").write(d[10 + s:])' "$work/both.mp3" "$work/v1.mp3"
rm "$work/both.mp3"
enc "${tags[@]}" -y "$work/a.flac"
enc "${tags[@]}" -c:a libvorbis -y "$work/a.ogg"
enc "${tags[@]}" -c:a libopus -y "$work/a.opus"
enc "${tags[@]}" -y "$work/a.m4a"
enc -metadata genre=17 -metadata date=2004-05-01 -y "$work/b.m4a"
enc "${tags[@]}" -y "$work/a.wma"
enc "${tags[@]}" -y "$work/a.wav"
n=0
for f in "$work"/*.*; do
ffprobe -v error -show_entries format_tags:stream_tags \
-of default=nw=1 "$f" > "$f.tags"
./rtags_test --ffprobe "$f" "$f.tags"
n=$((n+1))
done
[ "$n" = 11 ]
# Files FFmpeg writes - MP3 with and without ID3v2, ADTS with and
# without, M4A, FLAC, Ogg Vorbis, Opus, WAVE PCM and float, WMA1/2,
# AC-3 - each sniffed as its arm and decoded where the sniffer
# says; and 5.1 with a tone a channel in each multichannel arm,
# folded with audio_transfer_stereo_matrix: every tone on its side
- name: Sniff and fold FFmpeg's files with audio_transfer
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats/audio_transfer_sniff
run: |
set -euo pipefail
make -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
enc() { ffmpeg -nostdin -hide_banner -loglevel error \
-f lavfi -i sine=f=440:d=1:sample_rate=48000 -ac 2 "$@"; }
enc -metadata title=t -y "$work/id3.mp3"
enc -id3v2_version 0 -write_xing 0 -y "$work/bare.mp3"
enc -c:a aac -f adts -y "$work/bare.aac"
enc -c:a aac -f adts -write_id3v2 1 -metadata title=t -y "$work/id3.aac"
enc -y "$work/a.m4a"
enc -y "$work/a.flac"
enc -c:a libvorbis -y "$work/a.ogg"
enc -c:a libopus -y "$work/a.opus"
enc -y "$work/s16.wav"
enc -c:a pcm_f32le -y "$work/f32.wav"
enc -c:a wmav1 -y "$work/v1.wma"
enc -y "$work/v2.wma"
enc -c:a ac3 -y "$work/a.ac3"
for p in id3.mp3:mp3 bare.mp3:mp3 bare.aac:aac id3.aac:aac a.m4a:aac \
a.flac:flac a.ogg:vorbis a.opus:opus s16.wav:wav f32.wav:wav \
v1.wma:wma v2.wma:wma a.ac3:ac3; do
./audio_transfer_sniff_test --file "$work/${p%%:*}" "${p#*:}"
done
# MP3, gapless (LAME tag) and not: FFmpeg's samples, where they
# start and end
for f in id3.mp3 bare.mp3; do
ffmpeg -nostdin -hide_banner -loglevel error -i "$work/$f" -f f32le -y "$work/$f.f32"
./audio_transfer_sniff_test --file "$work/$f" mp3 --pcm "$work/$f.f32"
done
# WMA: the length the ASF header states, as ffprobe reads it
for f in v1.wma v2.wma; do
frames=$(ffprobe -v error -show_entries format=duration:stream=sample_rate \
-of csv=p=0 "$work/$f" | tr '\n' ' ' | awk '{ printf "%d", $1 * $2 + 0.5 }')
./audio_transfer_sniff_test --file "$work/$f" wma --frames "$frames"
done
# WMA1 and WMA2, gapless at each frame size (2048, 1024 and 512
# samples): the signal FFmpeg's encoder was given, from its
# first sample, at the length the header states
for codec in wmav1 wmav2; do
for rate in 44100 22050 16000 8000; do
src="sine=f=440:d=0.73:sample_rate=$rate"
ffmpeg -nostdin -hide_banner -loglevel error -f lavfi -i "$src" -ac 2 \
-c:a "$codec" -y "$work/g.wma"
ffmpeg -nostdin -hide_banner -loglevel error -f lavfi -i "$src" -ac 2 \
-f f32le -y "$work/g.f32"
./audio_transfer_sniff_test --file "$work/g.wma" wma --source "$work/g.f32"
done
done
six=""
n=0
for hz in 300 500 700 80 1100 1300; do
six="${six}sine=f=$hz:d=2:sample_rate=48000[c$n];"
n=$((n+1))
done
six="${six}[c0][c1][c2][c3][c4][c5]join=inputs=6:channel_layout=5.1"
six="$six:map=0.0-FL|1.0-FR|2.0-FC|3.0-LFE|4.0-BL|5.0-BR"
for p in m4a:aac:aac ogg:libvorbis:vorbis opus:libopus:opus \
flac:flac:flac ac3:ac3:ac3 wav:pcm_s16le:wav; do
IFS=: read -r ext codec type <<< "$p"
ffmpeg -nostdin -hide_banner -loglevel error -filter_complex "$six" \
-c:a "$codec" -y "$work/six.$ext"
./audio_transfer_sniff_test --file "$work/six.$ext" "$type" --tones
done
# ATRAC3 and ATRAC3plus - stereo, joint stereo, mono, 4.0, 5.1,
# 6.1, 7.1 - in WAVE and OpenMG files the sample writes: FFmpeg
# decodes each to the samples audio_transfer gives (in FFmpeg's
# channel layout), and ffprobe reads the OpenMG ea3 tags as rtags
# does
- name: Decode audio_transfer's ATRAC files with FFmpeg
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats
run: |
set -euo pipefail
make -C audio_transfer_atrac -j2 SANITIZER=address,undefined
make -C tags -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
./audio_transfer_atrac/audio_transfer_atrac_test --write "$work"
n=0
for f in "$work"/*.wav "$work"/*.oma; do
ffmpeg -nostdin -hide_banner -loglevel fatal -i "$f" -f f32le -y "$f.f32"
./audio_transfer_atrac/audio_transfer_atrac_test --compare "$f" "$f.f32"
n=$((n+1))
done
[ "$n" = 11 ]
for f in "$work"/*.oma; do
ffprobe -v error -show_entries format_tags:stream_tags \
-of default=nw=1 "$f" > "$f.tags"
./tags/rtags_test --ffprobe "$f" "$f.tags"
done
# AVI (MPEG-4 Part 2 with MP3, AVC with AC-3, MPEG-4 alone,
# MS-MPEG4 with PCM) and WMV (WMV2 with WMA2) from FFmpeg: rcontainer
# reads the streams, tags, sizes, rates, bit rates and length as
# ffprobe does
- name: Read FFmpeg's AVI and ASF headers with rcontainer
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats/container
run: |
set -euo pipefail
make -j2 SANITIZER=address,undefined
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
v=(-f lavfi -i testsrc2=s=96x64:r=25:d=1)
a=(-f lavfi -i sine=f=440:d=1:sample_rate=44100)
ff() { ffmpeg -nostdin -hide_banner -loglevel error "$@"; }
ff "${v[@]}" "${a[@]}" -c:v mpeg4 -vtag XVID -c:a libmp3lame -y "$work/a.avi"
ff "${v[@]}" "${a[@]}" -c:v libx264 -c:a ac3 -y "$work/b.avi"
ff "${v[@]}" -c:v mpeg4 -y "$work/c.avi"
ff "${v[@]}" "${a[@]}" -c:v msmpeg4 -c:a pcm_s16le -ac 1 -y "$work/d.avi"
ff "${v[@]}" "${a[@]}" -c:v wmv2 -c:a wmav2 -y "$work/e.wmv"
n=0
for f in "$work"/*.avi "$work"/*.wmv; do
ffprobe -v error -show_entries \
stream=codec_type,codec_tag,width,height,sample_rate,channels,bit_rate,duration \
-of default=nw=1 "$f" > "$f.probe"
./rcontainer_test --ffprobe "$f" "$f.probe"
n=$((n+1))
done
[ "$n" = 5 ]
# RGBA to 4:2:0 against swscale's with the same 2x2 chroma average
# (area): every sample within 1, in each of FFmpeg's test patterns
- name: Convert RGBA to yuv420p as swscale does
if: matrix.group == 'formats'
shell: bash
working-directory: libretro-common/samples/formats/image_yuv
run: |
set -euo pipefail
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
for src in testsrc2=size=160x120 smptehdbars=size=320x180 \
"gradients=size=256x144:speed=0.05" mandelbrot=size=200x150; do
ffmpeg -nostdin -hide_banner -loglevel error -f lavfi -i "$src:rate=25" \
-frames:v 8 -pix_fmt rgba -f rawvideo -y "$work/f.rgba"
size=$(ffprobe -v error -f lavfi -i "$src:rate=25" -show_entries stream=width,height \
-of csv=p=0 | head -1)
ffmpeg -nostdin -hide_banner -loglevel error -f rawvideo -pix_fmt rgba \
-s "${size/,/x}" -i "$work/f.rgba" -sws_flags area+accurate_rnd \
-pix_fmt yuv420p -f rawvideo -y "$work/f.yuv"
./image_yuv_test --ffmpeg "$work/f.rgba" "$work/f.yuv" ${size/,/ }
done
- name: Frontend H.264 decode interface, v1 to v4, plain and under ASan/UBSan
if: matrix.group == 'codecs'
shell: bash
@@ -841,6 +1373,20 @@ jobs:
TSAN_OPTIONS=halt_on_error=1 ./retro_atomic_test
- name: Run r7z_peek_threads_test under Clang + ThreadSanitizer
if: matrix.group == 'core'
shell: bash
working-directory: libretro-common/samples/formats/r7z
run: |
# A folder peeked at while another thread decodes it, through
# its being held: never torn, never the other folder's.
set -u
set -o pipefail
make clean
CC=clang make -j$(nproc) r7z_peek_threads_test SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 ./r7z_peek_threads_test
- name: Run video_player_test under Clang + ThreadSanitizer, and threadless
if: matrix.group == 'core'
shell: bash
@@ -912,6 +1458,22 @@ jobs:
TSAN_OPTIONS=halt_on_error=1 ./audio_mixer_voice_threads_test
# Every 16-bit sample through convert_s16_to_ulaw against FFmpeg's
# pcm_mulaw: all 65536 codes the same
- name: Encode mu-law as FFmpeg's pcm_mulaw does
if: matrix.group == 'core'
shell: bash
working-directory: libretro-common/samples/audio/float_to_ulaw
run: |
set -euo pipefail
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
make -j2 all
./float_to_ulaw_test --ramp "$work/ramp.s16"
ffmpeg -nostdin -hide_banner -loglevel error -f s16le -ar 8000 -ac 1 \
-i "$work/ramp.s16" -c:a pcm_mulaw -f mulaw -y "$work/ramp.ul"
./float_to_ulaw_test --ffmpeg "$work/ramp.ul"
- name: Run mpsc_stack_test under Clang + ThreadSanitizer
if: matrix.group == 'core'
shell: bash
@@ -1420,6 +1982,34 @@ jobs:
-lwinmm -o thread_cleanup_test.exe
timeout 30 wine ./thread_cleanup_test.exe
- name: rtt under Wine
shell: bash
working-directory: libretro-common/samples/gfx/font
env:
WINEDEBUG: -all
run: |
set -eu
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
make clean
make CC=x86_64-w64-mingw32-gcc all
timeout 60 wine ./rtt_test.exe
make clean
- name: socket_poll past FD_SETSIZE under Wine
shell: bash
working-directory: libretro-common/samples/net/socket_poll
env:
WINEDEBUG: -all
run: |
set -eu
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
# select() takes 64 sockets a set; past that the sets are sized
# for the call.
make clean
make CC=x86_64-w64-mingw32-gcc all
timeout 60 wine ./socket_poll_test.exe
make clean
- name: Build and run the NVIDIA loaders under Wine
shell: bash
working-directory: libretro-common/samples/gfx/nvidia
+16
View File
@@ -64,6 +64,15 @@ jobs:
run: python3 samples/menu/rgui_media/run.py --c89 --cadence-hz 30 --driver materialui samples/menu/rgui_media/fixture.mp4
- name: Real XMB file-browser playback controls
run: python3 samples/menu/rgui_media/run.py --c89 --cadence-hz 30 --driver xmb samples/menu/rgui_media/fixture.mp4
- name: QuickTime MOV browsing and external launch across all menus
run: |
ffmpeg -v error -y -i samples/menu/rgui_media/fixture.mp4 -map 0 -c copy -f mov samples/menu/rgui_media/fixture.mov
python3 -c "import json,subprocess; p='samples/menu/rgui_media/fixture.mov'; d=json.loads(subprocess.check_output(['ffprobe','-v','error','-show_format','-of','json',p])); assert d['format']['tags']['major_brand']=='qt '"
cp samples/menu/rgui_media/fixture.mov samples/menu/rgui_media/fixture-upper.MOV
for driver in rgui materialui xmb ozone; do
python3 samples/menu/rgui_media/run.py --c89 --driver "$driver" samples/menu/rgui_media/fixture.mov
python3 samples/menu/rgui_media/run.py --c89 --external --driver "$driver" samples/menu/rgui_media/fixture-upper.MOV
done
- name: Real Ozone file-browser playback controls
run: python3 samples/menu/rgui_media/run.py --c89 --cadence-hz 30 --driver ozone samples/menu/rgui_media/fixture.mp4
- name: UTF-8 subtitle sidecars across all four menus
@@ -188,6 +197,13 @@ jobs:
xvfb-run -a python3 samples/menu/rgui_media/run.py --c89 --continuous-error --video-driver gl --driver "$driver" $threaded samples/menu/rgui_media/window-toggle.webm
done
done
- name: External movie settings retain the playing preview
run: |
for driver in materialui xmb ozone; do
for threaded in '' --threaded; do
MEDIA_TEST_SETTINGS_ORANGE=1 xvfb-run -a python3 samples/menu/rgui_media/run.py --c89 --external --external-settings --video-driver gl --driver "$driver" $threaded samples/menu/rgui_media/settings-preview.webm
done
done
- name: Generate delayed first-picture fixture
run: ffmpeg -v error -y -f lavfi -i "testsrc2=size=320x240:rate=30,hue=h=90*t" -vf "setpts=PTS+0.2/TB" -t 3 -c:v libvpx -deadline realtime -cpu-used 8 -g 90 -auto-alt-ref 0 samples/menu/rgui_media/delayed.webm
- name: Compare actual visible pictures after delayed-start seeks
@@ -87,10 +87,22 @@ jobs:
# IP address matches only an iPAddress SAN entry, never the
# same address written as a DNS name.
samples/network/tls_retro/local_server_test.sh
# One connection read and written from two threads at once
# (SSL_RETRO_DUPLEX), under ThreadSanitizer
make -C samples/network/tls_retro clean
make -C samples/network/tls_retro tls_duplex CFLAGS='-O1 -g -fsanitize=thread -fno-omit-frame-pointer'
TLS_TEST_DUPLEX_ONLY=1 TSAN_OPTIONS=halt_on_error=1 RUNNER="setarch $(uname -m) -R" \
samples/network/tls_retro/local_server_test.sh
# The same tool cross-linked for Windows: the sample's link
# line has to carry Winsock, which nothing on Linux checks.
make -C samples/network/tls_retro clean
- name: Incremental built-in TLS handshakes
shell: bash
run: |
set -eu
TLS_TEST_HANDSHAKE_STEP=1 samples/network/tls_retro/local_server_test.sh
make -C samples/network/tls_retro clean
- name: Built-in SMB2/3 client against a local smbd
shell: bash
+20
View File
@@ -1265,6 +1265,26 @@ jobs:
make clean
echo "[pass] udev_devices_test (ASan)"
- name: Build and run udev_touch_test (ASan + UBSan)
shell: bash
working-directory: samples/input/udev_touch
run: |
set -eu
# The udev input driver's touchscreen support
# (UDEV_TOUCH_SUPPORT), handed a panel in memory: its
# ioctls are answered by the test and evdev frames are fed
# to its event handler. The pointer is pressed while any
# contact is down and released after the last; the pointer
# device answers X, Y and pressed; a multi-touch panel's
# single-touch emulation leaves the slots alone; a
# single-touch panel's contact is slot 0; positions are
# taken from the axis minimum.
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 60 ./udev_touch_test
make clean
echo "[pass] udev_touch_test (ASan)"
- name: Build and run device_pins_test
shell: bash
working-directory: samples/input/device_pins
+157 -2
View File
@@ -7,6 +7,10 @@ on:
paths:
- 'record/**'
- 'libretro-common/net/net_rtmp*'
- 'libretro-common/net/net_socket.c'
- 'libretro-common/net/net_resolve.c'
- 'libretro-common/include/net/net_resolve.h'
- 'libretro-common/include/net/net_socket.h'
- 'libretro-common/include/net/net_rtmp*'
- 'libretro-common/formats/flv/**'
- 'libretro-common/include/formats/rflv_mux.h'
@@ -24,6 +28,10 @@ on:
paths:
- 'record/**'
- 'libretro-common/net/net_rtmp*'
- 'libretro-common/net/net_socket.c'
- 'libretro-common/net/net_resolve.c'
- 'libretro-common/include/net/net_resolve.h'
- 'libretro-common/include/net/net_socket.h'
- 'libretro-common/include/net/net_rtmp*'
- 'libretro-common/formats/flv/**'
- 'libretro-common/include/formats/rflv_mux.h'
@@ -52,7 +60,7 @@ jobs:
name: Build and run samples/record
runs-on: ubuntu-latest
env:
APT_PKGS: mingw-w64 wine build-essential pkg-config ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev ccache
APT_PKGS: clang mingw-w64 wine build-essential pkg-config ffmpeg openssl python3 libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev ccache
timeout-minutes: 25
steps:
@@ -141,6 +149,8 @@ jobs:
make clean && make check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
make clean
- name: Native MP4 mux (ASan + UBSan, mingw-w64)
shell: bash
working-directory: samples/record/mp4_mux
@@ -161,14 +171,73 @@ jobs:
make clean && make check stream-check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
make clean
- name: RTMP protocol and sockets (ASan + UBSan, mingw-w64)
- name: Media Foundation AAC portable and cross builds
shell: bash
working-directory: samples/record/mf_audio
run: |
set -eu
make clean all SANITIZER=address,undefined
make clean all CC=x86_64-w64-mingw32-gcc EXE=.exe
x86_64-w64-mingw32-objdump -p encode_test.exe > imports.txt
if grep -Ei 'DLL Name:.*(mfplat|mfaacenc|ole32)\.dll' imports.txt; then
echo 'Native AAC must load optional entry points at runtime'
exit 1
fi
make clean
- name: Pollable producer wake (Clang ASan + UBSan, mingw-w64)
shell: bash
working-directory: libretro-common/samples/net/wake
run: |
set -eu
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
make clean && make check CC=clang SANITIZER=address,undefined
make clean && make check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
make clean
- name: RTMP protocol and sockets (ASan + UBSan, TSan, mingw-w64)
shell: bash
working-directory: libretro-common/samples/net/rtmp
run: |
set -eu
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
make clean && make rtmp_handshake_test CC=clang SANITIZER=address,undefined
./rtmp_handshake_test
make clean && make check peer-check SANITIZER=address,undefined
make rtmps_socket_native_test SANITIZER=address,undefined
python3 media_peer.py --mux --tls 1.2 ./rtmps_socket_native_test
python3 media_peer.py --mux --tls 1.3 ./rtmps_socket_native_test
make rtmps_client_native_test SANITIZER=address,undefined
python3 media_peer.py --caps --mux --tls 1.2 ./rtmps_client_native_test
python3 media_peer.py --caps --mux --tls 1.3 ./rtmps_client_native_test
python3 media_peer.py --codec av1 --caps --mux --tls 1.2 ./rtmps_client_native_test
python3 media_peer.py --codec av1 --caps --mux --tls 1.3 ./rtmps_client_native_test
python3 media_peer.py --codec hevc --caps --mux --tls 1.2 ./rtmps_client_native_test
python3 media_peer.py --codec hevc --caps --mux --tls 1.3 ./rtmps_client_native_test
make clean && make rtmp_resolve_native_test SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 30 ./rtmp_resolve_native_test
make clean && make check peer-check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
make rtmps_socket_native_test.exe CC=x86_64-w64-mingw32-gcc EXE=.exe
python3 media_peer.py --runner wine --mux --tls 1.2 ./rtmps_socket_native_test.exe
python3 media_peer.py --runner wine --mux --tls 1.3 ./rtmps_socket_native_test.exe
make rtmps_client_native_test.exe CC=x86_64-w64-mingw32-gcc EXE=.exe
python3 media_peer.py --runner wine --caps --mux --tls 1.2 ./rtmps_client_native_test.exe
python3 media_peer.py --runner wine --caps --mux --tls 1.3 ./rtmps_client_native_test.exe
python3 media_peer.py --runner wine --codec av1 --caps --mux --tls 1.2 ./rtmps_client_native_test.exe
python3 media_peer.py --runner wine --codec av1 --caps --mux --tls 1.3 ./rtmps_client_native_test.exe
python3 media_peer.py --runner wine --codec hevc --caps --mux --tls 1.2 ./rtmps_client_native_test.exe
python3 media_peer.py --runner wine --codec hevc --caps --mux --tls 1.3 ./rtmps_client_native_test.exe
make clean
- name: Native encoder stream ownership contracts
working-directory: samples/record/rtmp_stream
run: |
set -eu
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
make clean check SANITIZER=address,undefined
make clean vt_thread_test SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./vt_thread_test
make clean check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
make clean
- name: GPU PCM output pipeline (ASan + UBSan, TSan, mingw-w64)
@@ -208,6 +277,16 @@ jobs:
make clean && make check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
make clean
- name: AudioToolbox AAC input and packet ownership contracts
working-directory: samples/record/audiotoolbox_audio
run: |
set -eu
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
make clean && make check CC=clang
make clean && make check SANITIZER=address,undefined
make clean && make check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
make clean
# The Apple API stand-in checks ownership/configuration only. Native
# compilation and hardware encode run in MacOS-videotoolbox-encode.yml.
- name: VideoToolbox encode callback and session contracts
@@ -241,3 +320,79 @@ jobs:
with:
path: ~/.ccache
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
mf-audio-native:
name: Native Windows AAC packets and source clock
runs-on: windows-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Ensure the server media component is installed
shell: pwsh
run: |
if ((Get-CimInstance Win32_OperatingSystem).ProductType -ne 1) {
$feature = Get-WindowsFeature Server-Media-Foundation -ErrorAction Stop
if (-not $feature.Installed) {
$result = Install-WindowsFeature Server-Media-Foundation -ErrorAction Stop
if (-not $result.Success) { throw 'Media Foundation installation failed' }
}
}
- name: Native compiler and independent decoder
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: false
install: make mingw-w64-x86_64-gcc mingw-w64-x86_64-python mingw-w64-x86_64-ffmpeg
- name: Encode and independently decode native AAC
shell: msys2 {0}
working-directory: samples/record/mf_audio
run: |
set -eu
make clean native-check CC=gcc EXE=.exe
objdump -p encode_test.exe > imports.txt
if grep -Ei 'DLL Name:.*(mfplat|mfaacenc|ole32)\.dll' imports.txt; then
echo 'Native AAC must load optional entry points at runtime'
exit 1
fi
make clean
- name: Native Metal frontend profiles and source clocks
shell: msys2 {0}
working-directory: samples/record/metal_frontend
run: |
set -eu
make clean
make check CC=gcc EXE=.exe
make clean
- name: Config include ownership and stack budget
shell: msys2 {0}
working-directory: libretro-common/samples/file/config_file
run: |
set -eu
make clean
make check CC=gcc EXE=.exe
make clean CC=gcc EXE=.exe
- name: Native streaming worker readiness and cancellation
shell: msys2 {0}
working-directory: samples/record/gpu_dispatch
run: |
set -eu
make clean
make stream_dispatch_test.exe CC=gcc EXE=.exe
./stream_dispatch_test.exe
make clean
- name: Native FLV source color metadata interoperability
shell: msys2 {0}
working-directory: libretro-common/samples/formats/flv
run: |
set -eu
make clean
make check stream-check CC=gcc EXE=.exe COLOR_METADATA_ORACLE=1
make clean
@@ -958,6 +958,14 @@ jobs:
python3 samples/menu/rgui_media/run.py --driver rgui --video-driver ${{ matrix.driver }} --gpu-surface --refresh-pixels samples/menu/rgui_media/fixture.mp4
python3 samples/menu/rgui_media/run.py --driver rgui --video-driver ${{ matrix.driver }} --gpu-surface --continuous-error samples/menu/rgui_media/fixture.mp4
- name: External Ozone movie settings at drive root and in a subfolder
if: matrix.driver == 'd3d11'
shell: msys2 {0}
run: |
set -eu
ffmpeg -v error -y -f lavfi -i color=orange:size=320x240:rate=30 -t 3 -c:v libx264 -preset ultrafast -pix_fmt yuv420p samples/menu/rgui_media/settings-preview.mp4
MEDIA_TEST_SETTINGS_ORANGE=1 python3 samples/menu/texture_media/external_settings_paths.py samples/menu/rgui_media/settings-preview.mp4 --video-driver ${{ matrix.driver }} --threaded
views-sdl3:
name: Views on SDL3, end to end (Xvfb, llvmpipe)
runs-on: ubuntu-latest
+18
View File
@@ -56,6 +56,24 @@ jobs:
uses: actions/checkout@v5
- name: Set up MSYS2
id: msys2
continue-on-error: true
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.sys }}
update: true
# setup-msys2 has no retry of its own, and its system upgrade fails
# the job when mirror.msys2.org stalls ("Operation too slow") on the
# package databases. A failed attempt is run once more after a
# minute; the second reinstalls over the first.
- name: Wait before retrying MSYS2
if: steps.msys2.outcome == 'failure'
shell: bash
run: sleep 60
- name: Set up MSYS2 (retry)
if: steps.msys2.outcome == 'failure'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.sys }}
@@ -17,7 +17,21 @@ on:
- 'record/record_gpu_session_metal.*'
- 'record/record_gpu_dispatch_metal.*'
- 'record/record_gpu_worker_vt*'
- 'record/record_vt_packet.*'
- 'record/record_stream_vt.*'
- 'record/record_stream_output_vt.*'
- 'record/record_stream_session_vt_native.h'
- 'samples/record/rtmp_stream/**'
- 'libretro-common/net/net_rtmp*'
- 'libretro-common/include/net/net_rtmp*'
- 'libretro-common/formats/flv/**'
- 'libretro-common/include/formats/rflv_mux.h'
- 'libretro-common/include/formats/rav1_sequence.h'
- 'libretro-common/include/formats/video_color.h'
- 'record/record_audio_queue.*'
- 'record/record_at_audio.*'
- 'record/record_at_resample.*'
- 'samples/record/audiotoolbox_audio/**'
- 'record/record_avfoundation_worker*'
- 'record/record_avfoundation_session*'
- 'record/record_mux_avfoundation.*'
@@ -42,7 +56,21 @@ on:
- 'record/record_gpu_session_metal.*'
- 'record/record_gpu_dispatch_metal.*'
- 'record/record_gpu_worker_vt*'
- 'record/record_vt_packet.*'
- 'record/record_stream_vt.*'
- 'record/record_stream_output_vt.*'
- 'record/record_stream_session_vt_native.h'
- 'samples/record/rtmp_stream/**'
- 'libretro-common/net/net_rtmp*'
- 'libretro-common/include/net/net_rtmp*'
- 'libretro-common/formats/flv/**'
- 'libretro-common/include/formats/rflv_mux.h'
- 'libretro-common/include/formats/rav1_sequence.h'
- 'libretro-common/include/formats/video_color.h'
- 'record/record_audio_queue.*'
- 'record/record_at_audio.*'
- 'record/record_at_resample.*'
- 'samples/record/audiotoolbox_audio/**'
- 'record/record_avfoundation_worker*'
- 'record/record_avfoundation_session*'
- 'record/record_mux_avfoundation.*'
@@ -52,10 +80,111 @@ on:
- 'samples/record/metal_frontend/**'
- 'libretro-common/include/retro_atomic.h'
workflow_dispatch:
inputs:
native_aac_only:
description: 'Run native AudioToolbox AAC acceptance without GPU/frontend jobs'
type: boolean
default: false
frontend_stream_only:
description: 'Run frontend streaming acceptance and both native frontend links'
type: boolean
default: false
permissions:
contents: read
jobs:
stream-native:
if: ${{ !inputs.native_aac_only && !inputs.frontend_stream_only }}
runs-on: macos-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- name: Install independent streaming receiver
run: command -v ffmpeg >/dev/null && command -v ffprobe >/dev/null || brew install ffmpeg
- name: Pollable producer wake and socket readiness
run: make -C libretro-common/samples/net/wake check
- name: Native VT and AAC over RTMP and TLS
env:
VT_STREAM_ARTIFACT_DIR: ${{ runner.temp }}/vt-stream-evidence
run: python3 samples/record/rtmp_stream/native_vt_run.py
- name: Save native streaming acceptance evidence
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: vt-stream-evidence
path: ${{ runner.temp }}/vt-stream-evidence
if-no-files-found: ignore
- name: Native Metal factory RTMP and TLS with ARC
if: ${{ !cancelled() }}
env:
VT_STREAM_ARTIFACT_DIR: ${{ runner.temp }}/vt-metal-stream-arc
run: python3 samples/record/rtmp_stream/native_vt_run.py --metal --ownership arc
- name: Native Metal factory RTMP and TLS with manual reference counting
if: ${{ !cancelled() }}
env:
VT_STREAM_ARTIFACT_DIR: ${{ runner.temp }}/vt-metal-stream-mrc
run: python3 samples/record/rtmp_stream/native_vt_run.py --metal --ownership mrc
- name: Save native Metal streaming acceptance evidence
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: vt-metal-stream-evidence
path: |
${{ runner.temp }}/vt-metal-stream-arc
${{ runner.temp }}/vt-metal-stream-mrc
if-no-files-found: ignore
- name: Native URL-owning Metal streaming dispatcher with ARC
if: ${{ !cancelled() }}
env:
VT_STREAM_ARTIFACT_DIR: ${{ runner.temp }}/vt-metal-dispatch-arc
run: python3 samples/record/rtmp_stream/native_vt_run.py --dispatcher --ownership arc
- name: Native URL-owning Metal streaming dispatcher with manual reference counting
if: ${{ !cancelled() }}
env:
VT_STREAM_ARTIFACT_DIR: ${{ runner.temp }}/vt-metal-dispatch-mrc
run: python3 samples/record/rtmp_stream/native_vt_run.py --dispatcher --ownership mrc
- name: Save native Metal dispatcher acceptance evidence
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: vt-metal-dispatch-evidence
path: |
${{ runner.temp }}/vt-metal-dispatch-arc
${{ runner.temp }}/vt-metal-dispatch-mrc
if-no-files-found: ignore
aac-native:
if: ${{ inputs.native_aac_only || !inputs.frontend_stream_only }}
runs-on: macos-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- name: Install independent AAC test decoder
run: command -v ffmpeg >/dev/null || brew install ffmpeg
- name: AudioToolbox AAC callback ownership contracts
run: make -C samples/record/audiotoolbox_audio check
- name: Native AudioToolbox AAC packets and independent timing
env:
AUDIO_TOOLBOX_ARTIFACT_DIR: ${{ runner.temp }}/audiotoolbox-aac-evidence
run: make -C samples/record/audiotoolbox_audio native-check
- name: Save AudioToolbox AAC acceptance evidence
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: audiotoolbox-aac-evidence
path: ${{ runner.temp }}/audiotoolbox-aac-evidence
if-no-files-found: ignore
- name: Native source-rate conversion and independent AAC timing
env:
AUDIO_TOOLBOX_SRC_ARTIFACT_DIR: ${{ runner.temp }}/audiotoolbox-src-evidence
run: make -C samples/record/audiotoolbox_audio native-src-check
- name: Save AudioToolbox SRC acceptance evidence
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: audiotoolbox-src-evidence
path: ${{ runner.temp }}/audiotoolbox-src-evidence
if-no-files-found: ignore
encode:
if: ${{ !inputs.native_aac_only && !inputs.frontend_stream_only }}
runs-on: macos-latest
timeout-minutes: 20
steps:
@@ -68,6 +197,8 @@ jobs:
- name: Portable ownership and failure contracts
working-directory: samples/record/videotoolbox_encode
run: make check thread_test worker_thread_test audio_thread_test av_session_thread_test && ./thread_test && ./worker_thread_test && ./audio_thread_test && ./av_session_thread_test
- name: VideoToolbox AAC streaming ownership and independent wire clock
run: make -C samples/record/rtmp_stream vt-check
- name: Native AAC property ABI and advertised rates
run: make -C samples/record/videotoolbox_encode native-audio-check
- name: Native hardware encoder and independent pixels
@@ -154,6 +285,7 @@ jobs:
if-no-files-found: warn
frontend-build:
if: ${{ !inputs.native_aac_only && !inputs.frontend_stream_only }}
runs-on: macos-latest
timeout-minutes: 40
steps:
@@ -189,7 +321,58 @@ jobs:
path: ${{ runner.temp }}/metal-frontend-recordings
if-no-files-found: warn
frontend-stream-build:
if: ${{ !inputs.native_aac_only }}
runs-on: macos-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
threads: [enable, disable]
steps:
- uses: actions/checkout@v6
- name: Configure network-enabled Metal frontend
run: |
set -eu
stream_options=
if [ "${{ matrix.threads }}" = disable ]; then
stream_options="--enable-rtmp --enable-rflv"
fi
./configure --enable-metal --enable-avf --${{ matrix.threads }}-threads --enable-configfile --enable-webmplayer \
--disable-qt --disable-ffmpeg --disable-discord --disable-cheevos \
--enable-networking --disable-sdl2 --disable-sdl --disable-translate \
--disable-update_cores --disable-update_assets --disable-online_updater \
--disable-cdrom --disable-7zip --enable-ssl --enable-retrossl $stream_options
for feature in HAVE_METAL HAVE_AVF HAVE_NETWORKING HAVE_CRYPTO HAVE_RTMP HAVE_RFLV HAVE_RETROSSL; do
grep -Eq "^#define $feature 1" config.h
done
- name: Link network worker and threadless fallback
run: |
set -eu
make -j"$(sysctl -n hw.ncpu)" \
MINVERFLAGS="-mmacosx-version-min=$(sw_vers -productVersion)"
test -x retroarch
nm retroarch | grep '_record_gpu_dispatch_metal_new_stream'
- name: Native frontend streaming through command start and stop
if: ${{ matrix.threads == 'enable' }}
run: |
set -eu
command -v ffmpeg >/dev/null && command -v ffprobe >/dev/null || brew install ffmpeg
cc -dynamiclib -std=gnu89 -Ilibretro-common/include samples/record/metal_frontend/core.c -o "$RUNNER_TEMP/metal-stream-core.dylib" -lm
python3 samples/record/metal_frontend/stream.py --retroarch ./retroarch --core "$RUNNER_TEMP/metal-stream-core.dylib" --video-buffer-frames 2 --output-dir "$RUNNER_TEMP/metal-frontend-stream/h264"
python3 samples/record/metal_frontend/stream.py --retroarch ./retroarch --core "$RUNNER_TEMP/metal-stream-core.dylib" --codec hevc --source-rate 32040.5 --video-buffer-frames 16 --output-dir "$RUNNER_TEMP/metal-frontend-stream/fractional"
python3 samples/record/metal_frontend/stream.py --retroarch ./retroarch --core "$RUNNER_TEMP/metal-stream-core.dylib" --channels 1 --source-rate 44100 --video-buffer-frames 8 --output-dir "$RUNNER_TEMP/metal-frontend-stream/mono"
python3 samples/record/metal_frontend/stream.py --retroarch ./retroarch --core "$RUNNER_TEMP/metal-stream-core.dylib" --video-only --video-buffer-frames 4 --output-dir "$RUNNER_TEMP/metal-frontend-stream/video-only"
- name: Save real frontend streaming evidence
if: ${{ always() && matrix.threads == 'enable' }}
uses: actions/upload-artifact@v4
with:
name: metal-frontend-stream-evidence
path: ${{ runner.temp }}/metal-frontend-stream
if-no-files-found: ignore
frontend-contracts:
if: ${{ !inputs.native_aac_only }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
+1 -1
View File
@@ -1082,7 +1082,7 @@ check_gates "griffin: HAVE_RETROSMB + cloud sync" "$UIDEFS -DRARCH_INTERNAL -DH
# griffin can carry. A static helper in one translation unit that
# another one also defines (get32 in net_smb2.c and rvorbis.c) only
# collides here, so this lane carries as much of griffin as a host can.
GRIFFIN_FULL="-DRARCH_INTERNAL -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_RGUI -DHAVE_XMB -DHAVE_OZONE -DHAVE_MATERIALUI -DHAVE_GFX_WIDGETS -DHAVE_OVERLAY -DHAVE_CONFIGFILE -DHAVE_CHEEVOS -DRC_DISABLE_LUA -DHAVE_RUNAHEAD -DHAVE_REWIND -DHAVE_PATCH -DHAVE_XDELTA -DHAVE_CHEATS -DHAVE_SCREENSHOTS -DHAVE_TRANSLATE -DHAVE_LIBRETRODB -DHAVE_DYNAMIC -DHAVE_DYLIB -DHAVE_THREADS -DHAVE_AUDIOMIXER -DHAVE_DSP_FILTER -DHAVE_VIDEO_FILTER -DHAVE_RWAV -DHAVE_RFLAC -DHAVE_RVORBIS -DHAVE_RMP3 -DHAVE_RAC3 -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_RWEBP -DHAVE_RDDS -DHAVE_RZSTD -DHAVE_7ZIP -DHAVE_CHD -DHAVE_RCHD -DHAVE_STB_FONT -DHAVE_NETWORKING -DHAVE_IFINFO -DHAVE_NETWORK_CMD -DHAVE_NETPLAYDISCOVERY -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES -DHAVE_UPDATE_CORE_INFO -DHAVE_CORE_INFO_CACHE -DHAVE_CLOUDSYNC -DHAVE_S3 -DHAVE_CRYPTO -DHAVE_KEYCHAIN -DHAVE_SSL -DHAVE_RETROSSL -DHAVE_SMBCLIENT -DHAVE_RETROSMB -DHAVE_NFSCLIENT -DHAVE_RETRONFS -DHAVE_LANGEXTRA -DHAVE_SHADERPIPELINE -DHAVE_GLSL -DHAVE_OPENGL -D_GNU_SOURCE"
GRIFFIN_FULL="-DRARCH_INTERNAL -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_RGUI -DHAVE_XMB -DHAVE_OZONE -DHAVE_MATERIALUI -DHAVE_GFX_WIDGETS -DHAVE_OVERLAY -DHAVE_CONFIGFILE -DHAVE_CHEEVOS -DRC_DISABLE_LUA -DHAVE_RUNAHEAD -DHAVE_REWIND -DHAVE_PATCH -DHAVE_XDELTA -DHAVE_CHEATS -DHAVE_SCREENSHOTS -DHAVE_TRANSLATE -DHAVE_LIBRETRODB -DHAVE_DYNAMIC -DHAVE_DYLIB -DHAVE_THREADS -DHAVE_AUDIOMIXER -DHAVE_DSP_FILTER -DHAVE_VIDEO_FILTER -DHAVE_RWAV -DHAVE_RFLAC -DHAVE_RVORBIS -DHAVE_RMP3 -DHAVE_RAC3 -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_RGIF -DHAVE_RWEBP -DHAVE_RDDS -DHAVE_RZSTD -DHAVE_7ZIP -DHAVE_CHD -DHAVE_RCHD -DHAVE_STB_FONT -DHAVE_NETWORKING -DHAVE_IFINFO -DHAVE_NETWORK_CMD -DHAVE_NETPLAYDISCOVERY -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES -DHAVE_UPDATE_CORE_INFO -DHAVE_CORE_INFO_CACHE -DHAVE_CLOUDSYNC -DHAVE_S3 -DHAVE_CRYPTO -DHAVE_KEYCHAIN -DHAVE_SSL -DHAVE_RETROSSL -DHAVE_SMBCLIENT -DHAVE_RETROSMB -DHAVE_NFSCLIENT -DHAVE_RETRONFS -DHAVE_LANGEXTRA -DHAVE_SHADERPIPELINE -DHAVE_GLSL -DHAVE_OPENGL -D_GNU_SOURCE"
check_gates "griffin: full unity (every codec + network stack)" "$UIDEFS $GRIFFIN_FULL" griffin/griffin.c
# The SMB show-settings row read a HAVE_MENU-only default under
# HAVE_SMBCLIENT alone; with the built-in client on by default that
+1
View File
@@ -403,3 +403,4 @@ samples/record/ffmpeg_pipeline/record_finalize_fragment.c
samples/record/ffmpeg_pipeline/record_start_fragment.c
samples/input/sensor_subdevice/core_sensor_fragment.c
samples/input/bind_capture/driver_code.inc
samples/record/metal_frontend/profile_fragment.c
+39 -5
View File
@@ -1126,7 +1126,8 @@ ifeq ($(HAVE_OVERLAY), 1)
led/drivers/led_overlay.o
endif
OBJ += gfx/drivers_font_renderer/stb.o
OBJ += gfx/drivers_font_renderer/stb.o \
$(LIBRETRO_COMM_DIR)/gfx/font/rtt.o
ifeq ($(HAVE_FREETYPE), 1)
OBJ += gfx/drivers_font_renderer/freetype.o
@@ -1703,6 +1704,17 @@ ifeq ($(HAVE_METAL), 1)
record/record_gpu_session_metal.o \
record/record_gpu_dispatch_metal.o \
$(LIBRETRO_COMM_DIR)/formats/h265/rh265_sps_crop.o
ifeq ($(HAVE_NETWORKING), 1)
ifeq ($(HAVE_RTMP), 1)
ifeq ($(HAVE_RFLV), 1)
OBJ += record/record_stream_vt.o \
record/record_stream_output_vt.o \
record/record_at_resample.o \
record/record_vt_packet.o \
$(LIBRETRO_COMM_DIR)/net/net_wake.o
endif
endif
endif
LIBS += -framework CoreMedia -framework VideoToolbox -framework AudioToolbox
endif
endif
@@ -2038,6 +2050,20 @@ ifeq ($(HAVE_D3D12), 1)
DEFINES += -DHAVE_D3D12
endif
ifeq ($(HAVE_MF_AUDIO), 1)
ifneq ($(findstring Win32,$(OS)),)
DEFINES += -DHAVE_MF_AUDIO
OBJ += record/record_mf_audio.o
ifeq ($(HAVE_RTMP), 1)
ifeq ($(HAVE_RFLV), 1)
ifeq ($(HAVE_D3D12), 1)
OBJ += record/record_stream_d3d12.o
endif
endif
endif
endif
endif
# H.264 previews decoded on the GPU, as rh264's backend; D3D12 imports
# its frames as well
ifneq ($(findstring 1, $(HAVE_D3D11) $(HAVE_D3D12)),)
@@ -2523,6 +2549,11 @@ ifeq ($(HAVE_RTGA), 1)
OBJ += $(LIBRETRO_COMM_DIR)/formats/tga/rtga.o
endif
ifeq ($(HAVE_RGIF), 1)
DEFINES += -DHAVE_RGIF
OBJ += $(LIBRETRO_COMM_DIR)/formats/gif/rgif.o
endif
ifeq ($(HAVE_RDDS), 1)
DEFINES += -DHAVE_RDDS
OBJ += $(LIBRETRO_COMM_DIR)/formats/dds/rdds.o
@@ -2664,10 +2695,11 @@ endif
# when HAVE_AVF is on.
ifeq ($(HAVE_AVF), 1)
DEFINES += -DHAVE_AVF
OBJ += camera/drivers/avfoundation.o \
OBJ += record/record_at_audio.o \
camera/drivers/avfoundation.o \
record/drivers/record_avfoundation.o
LIBS += -framework AVFoundation -framework CoreVideo \
-framework CoreMedia -framework Accelerate
-framework CoreMedia -framework Accelerate -framework AudioToolbox
ifeq ($(HAVE_COCOATOUCH), 1)
LIBS += -framework UIKit
endif
@@ -2760,12 +2792,14 @@ ifeq ($(HAVE_NETWORKING), 1)
$(LIBRETRO_COMM_DIR)/net/net_rtmp_receive.o \
$(LIBRETRO_COMM_DIR)/net/net_rtmp_reply.o \
$(LIBRETRO_COMM_DIR)/net/net_rtmp_session.o \
$(LIBRETRO_COMM_DIR)/net/net_rtmp_socket.o
$(LIBRETRO_COMM_DIR)/net/net_rtmp_socket.o \
$(LIBRETRO_COMM_DIR)/net/net_rtmp_client.o
endif
OBJ += $(LIBRETRO_COMM_DIR)/net/net_compat.o \
$(LIBRETRO_COMM_DIR)/net/net_http.o \
$(LIBRETRO_COMM_DIR)/net/net_http_parse.o \
$(LIBRETRO_COMM_DIR)/net/net_socket.o \
$(LIBRETRO_COMM_DIR)/net/net_resolve.o \
core_updater_list.o \
network/natt.o \
network/natt_desc.o \
@@ -3378,7 +3412,7 @@ ifeq ($(HAVE_RECORD_MEDIACODEC), 1)
DEFINES += -DHAVE_RECORD_MEDIACODEC
MEDIA_RECORD_OBJ := record/record_mediacodec_encoder.o \
record/record_audio_tap.o \
record/record_mux_mediacodec.o record/record_mediacodec_worker.o \
record/record_mux_mediacodec.o record/record_mediacodec_worker.o record/record_aac_stream_timing.o \
record/record_aac_probe.o \
record/record_mp4_audio_edit.o record/record_mediacodec_file_edit.o \
record/record_mediacodec_surface.o record/record_mediacodec_file.o \
+2 -1
View File
@@ -53,7 +53,7 @@ DEFINES :=
ifeq ($(GRIFFIN_BUILD), 1)
OBJ += griffin/griffin.o
DEFINES += -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_CONFIGFILE -DHAVE_RGUI -DHAVE_XMB -DHAVE_LIBRETRODB -DHAVE_CC_RESAMPLER
DEFINES += -DHAVE_7ZIP -DHAVE_RZSTD -DHAVE_RPNG -DHAVE_RWEBP -DHAVE_RDDS -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA
DEFINES += -DHAVE_7ZIP -DHAVE_RZSTD -DHAVE_RPNG -DHAVE_RWEBP -DHAVE_RDDS -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_RGIF
DEFINES += -DHAVE_NETWORKING -DHAVE_IFINFO -DHAVE_CHEEVOS -DRC_DISABLE_LUA -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_CORES
DEFINES += -DHAVE_UPDATE_CORE_INFO
#DEFINES += -DHAVE_UPDATE_ASSETS
@@ -73,6 +73,7 @@ else
HAVE_CC_RESAMPLER = 1
HAVE_MENU_COMMON = 1
HAVE_RTGA = 1
HAVE_RGIF = 1
HAVE_RPNG = 1
HAVE_RWEBP = 1
HAVE_RDDS = 1
+1
View File
@@ -34,6 +34,7 @@ HAVE_AUDIOMIXER = 1
HAVE_CC_RESAMPLER = 1
HAVE_MENU_COMMON = 1
HAVE_RTGA = 1
HAVE_RGIF = 1
HAVE_RPNG = 1
HAVE_RWEBP = 1
HAVE_RDDS = 1
+1
View File
@@ -34,6 +34,7 @@ HAVE_AUDIOMIXER = 1
HAVE_CC_RESAMPLER = 1
HAVE_MENU_COMMON = 1
HAVE_RTGA = 1
HAVE_RGIF = 1
HAVE_RPNG = 1
HAVE_RJPEG = 1
HAVE_RBMP = 1
+21
View File
@@ -196,6 +196,7 @@ else ifeq ($(libogc_platform), 1)
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_RMODTRACKER := 1
HAVE_OVERLAY := 1
HAVE_7ZIP := 1
@@ -272,6 +273,7 @@ else ifeq ($(platform), psp1)
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_KERNEL_PRX := 1
#HAVE_LANGEXTRA := 1
RARCH_CONSOLE = 1
@@ -324,6 +326,7 @@ else ifeq ($(platform), vita)
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_GXM := 1
HAVE_NETWORKING := 1
@@ -346,6 +349,7 @@ else ifeq ($(platform), windows_msvc6_x86)
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_NETWORKING := 0
HAVE_IFINFO := 0
@@ -397,6 +401,7 @@ else ifeq ($(platform), windows_msvc2003_x86)
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_NETWORKING := 1
HAVE_IFINFO := 0
@@ -454,6 +459,7 @@ else ifeq ($(platform), windows_msvc2005_x86)
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_NETWORKING := 1
HAVE_IFINFO := 0
@@ -542,6 +548,7 @@ else ifneq (,$(findstring windows_msvc2010,$(platform)))
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_NETWORKING := 1
HAVE_IFINFO := 1
@@ -638,6 +645,7 @@ else ifneq (,$(findstring windows_msvc2012,$(platform)))
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_NETWORKING := 1
HAVE_IFINFO := 1
@@ -725,6 +733,7 @@ else ifneq (,$(findstring windows_msvc2013,$(platform)))
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_NETWORKING := 1
HAVE_IFINFO := 1
@@ -812,6 +821,7 @@ else ifneq (,$(findstring windows_msvc2015,$(platform)))
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_NETWORKING := 1
HAVE_IFINFO := 1
@@ -914,6 +924,7 @@ else ifeq (qnx,$(platform))
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_NETWORKING := 1
HAVE_IFINFO := 1
@@ -960,6 +971,7 @@ else ifneq (,$(findstring unix,$(platform)))
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_NETWORKING := 1
HAVE_IFINFO := 1
@@ -1077,6 +1089,9 @@ endif
ifeq ($(HAVE_RTGA), 1)
CFLAGS += -DHAVE_RTGA
endif
ifeq ($(HAVE_RGIF), 1)
CFLAGS += -DHAVE_RGIF
endif
ifeq ($(HAVE_BSV_MOVIE), 1)
CFLAGS += -DHAVE_BSV_MOVIE
@@ -1129,6 +1144,12 @@ ifeq ($(HAVE_RFLV), 1)
CFLAGS += -DHAVE_RFLV
endif
ifeq ($(HAVE_MF_AUDIO), 1)
ifeq ($(system_platform), win)
CFLAGS += -DHAVE_MF_AUDIO
endif
endif
ifeq ($(HAVE_IFINFO), 1)
CFLAGS += -DHAVE_IFINFO
endif
+1
View File
@@ -29,6 +29,7 @@ HAVE_AUDIOMIXER = 1
HAVE_CC_RESAMPLER = 1
HAVE_MENU_COMMON = 1
HAVE_RTGA = 1
HAVE_RGIF = 1
HAVE_RPNG = 1
HAVE_RWEBP = 1
HAVE_RDDS = 1
+1
View File
@@ -54,6 +54,7 @@ HAVE_MENU_COMMON := 1
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_NETWORKING := 1
HAVE_IFINFO := 1
+1
View File
@@ -158,6 +158,7 @@ HAVE_RDDS := 0
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_RMODTRACKER := 0
HAVE_OVERLAY := 1
HAVE_7ZIP := 0
+2 -1
View File
@@ -34,7 +34,7 @@ ifeq ($(GRIFFIN_BUILD), 1)
OBJ += griffin/griffin.o
DEFINES += -DHAVE_GRIFFIN=1
DEFINES += -DHAVE_MENU -DHAVE_LIBRETRODB -DHAVE_CONFIGFILE
DEFINES += -DHAVE_RPNG -DHAVE_RWEBP -DHAVE_RDDS -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_CC_RESAMPLER
DEFINES += -DHAVE_RPNG -DHAVE_RWEBP -DHAVE_RDDS -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_RGIF -DHAVE_CC_RESAMPLER
DEFINES += -DHAVE_CORE_INFO_CACHE
ifeq ($(DEBUG), 1)
DEFINES += -DHAVE_NETLOGGER
@@ -50,6 +50,7 @@ else
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_OVERLAY := 1
HAVE_7ZIP := 1
HAVE_RZSTD := 1
+2
View File
@@ -37,6 +37,7 @@ ifeq ($(GRIFFIN_BUILD), 1)
-DHAVE_RJPEG \
-DHAVE_RBMP \
-DHAVE_RTGA \
-DHAVE_RGIF \
-DHAVE_CC_RESAMPLER \
-DHAVE_CORE_INFO_CACHE
ifeq ($(DEBUG), 1)
@@ -58,6 +59,7 @@ else
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_7ZIP := 1
HAVE_RZSTD := 1
ifeq ($(HAVE_VITAGLES), 1)
+1
View File
@@ -148,6 +148,7 @@ HAVE_RDDS := 1
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RGIF := 1
HAVE_RMODTRACKER := 1
HAVE_OVERLAY := 1
HAVE_7ZIP := 1
+2 -1
View File
@@ -122,7 +122,7 @@ endif
DEFINES += -DHAVE_RWAV
DEFINES += -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_GFX_WIDGETS -DHAVE_CONFIGFILE -DHAVE_PATCH -DHAVE_CHEATS -DHAVE_RGUI -DHAVE_LIBRETRODB
DEFINES += -DHAVE_REWIND
DEFINES += -DHAVE_RPNG -DHAVE_RWEBP -DHAVE_RDDS -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_CC_RESAMPLER
DEFINES += -DHAVE_RPNG -DHAVE_RWEBP -DHAVE_RDDS -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_RGIF -DHAVE_CC_RESAMPLER
DEFINES += -DHAVE_RSLC -DHAVE_SLANG
DEFINES += -DHAVE_RVORBIS -DHAVE_LANGEXTRA -DHAVE_LIBRETRODB -DHAVE_NETWORKING -DHAVE_IFINFO -DHAVE_NETPLAYDISCOVERY
DEFINES += -DHAVE_XMB -DHAVE_MATERIALUI
@@ -139,6 +139,7 @@ endif
HAVE_MENU_COMMON = 1
HAVE_GFX_WIDGETS = 1
HAVE_RTGA = 1
HAVE_RGIF = 1
HAVE_RPNG = 1
HAVE_RWEBP = 1
HAVE_RDDS = 1
+4 -1
View File
@@ -1443,7 +1443,10 @@ static int rcheevos_rvz_pump(rcheevos_rvz_t* handle, bool opening)
if (err != RVZ_PENDING)
return err;
if (filestream_seek(handle->file, (int64_t)req.offset,
/* At the offset in one call; seek and read where there is none */
if ((got = filestream_read_at(handle->file, req.dst,
(int64_t)req.length, (int64_t)req.offset)) < 0
&& filestream_seek(handle->file, (int64_t)req.offset,
RETRO_VFS_SEEK_POSITION_START) == 0)
got = filestream_read(handle->file, req.dst, (int64_t)req.length);
rvz_feed(handle->rvz, got > 0 ? (size_t)got : 0);
+76 -216
View File
@@ -3194,22 +3194,15 @@ static uint8_t *command_memory_get_pointer(
/* What RUN_UNTIL, a PRESS that waits after it and a STEPS list wait on:
* the byte at @lo, or with @wide the value @hi * 256 + @lo, from two
* bytes that need not be adjacent. With @diff, that less the value at
* @sub_lo, or @sub_hi:@sub_lo: how far apart two things are, signed. */
* bytes that need not be adjacent. */
struct command_cond
{
unsigned hi;
unsigned lo;
unsigned sub_hi;
unsigned sub_lo;
unsigned mask; /* "& <mask>": the bits compared; 0 for all */
int want;
unsigned want;
uint8_t op; /* enum cmd_until_op */
bool wide;
bool sub_wide;
bool diff;
/* a stop: fires only on a frame it holds after one it did not */
bool edge;
};
/* "<address>[:<address>]" into @c, @end after it: 0, or 1 where the
@@ -3232,34 +3225,20 @@ static int command_cond_address(const char *s, struct command_cond *c,
return 0;
}
/* The byte at @lo, or with @wide the value @hi * 256 + @lo. */
static bool command_cond_value(unsigned hi, unsigned lo, bool wide,
static bool command_cond_read(const struct command_cond *c,
unsigned *out, char *why, size_t why_len)
{
unsigned avail = 0;
const rarch_system_info_t *sys = &runloop_state_get_ptr()->system;
const uint8_t *l = command_memory_get_pointer(sys, lo, &avail, 0,
const uint8_t *lo = command_memory_get_pointer(sys, c->lo, &avail, 0,
why, why_len);
const uint8_t *h = NULL;
if (!l || (wide && !(h = command_memory_get_pointer(sys, hi,
const uint8_t *hi = NULL;
if (!lo || (c->wide && !(hi = command_memory_get_pointer(sys, c->hi,
&avail, 0, why, why_len))))
return false;
*out = h ? ((unsigned)*h << 8) | *l : *l;
return true;
}
static bool command_cond_read(const struct command_cond *c,
int *out, char *why, size_t why_len)
{
unsigned a = 0;
unsigned b = 0;
if ( !command_cond_value(c->hi, c->lo, c->wide, &a, why, why_len)
|| (c->diff && !command_cond_value(c->sub_hi, c->sub_lo,
c->sub_wide, &b, why, why_len)))
return false;
*out = hi ? ((unsigned)*hi << 8) | *lo : *lo;
if (c->mask)
a &= c->mask;
*out = (int)a - (int)b;
*out &= c->mask;
return true;
}
@@ -3312,9 +3291,9 @@ static size_t command_watch_lines(char *s, size_t len)
i++)
{
const struct command_watch_entry *e = &command_watches[i];
int now = 0;
unsigned now = 0;
if (command_cond_read(&e->where, &now, reason, sizeof(reason)))
snprintf(value, sizeof(value), "%d", now);
snprintf(value, sizeof(value), "%u", now);
else
strlcpy(value, "none", sizeof(value));
if (e->where.wide)
@@ -3359,17 +3338,15 @@ static bool command_cond_number(const char *s, char **end,
return errno != ERANGE;
}
/* "<address>[:<address>] [- <address>[:<address>]] <condition>
* <value>", @end after the value; false with what was wrong in @why. */
/* "<address>[:<address>] <condition> <value>", @end after the value;
* false with what was wrong in @why. */
static bool command_cond_parse(const char *s, struct command_cond *c,
char **end, char *why, size_t why_len)
{
unsigned i;
unsigned long num;
unsigned long max;
const char *sp;
int bad;
bool neg = false;
memset(c, 0, sizeof(*c));
if ((bad = command_cond_address(s, c, end)))
@@ -3382,32 +3359,6 @@ static bool command_cond_parse(const char *s, struct command_cond *c,
}
while (**end == ' ')
(*end)++;
/* "485 - 47e": the second's value taken from the first's, signed,
* so how far an enemy is from the player is one condition */
if (**end == '-')
{
struct command_cond sub;
memset(&sub, 0, sizeof(sub));
if (command_cond_address(*end + 1, &sub, end))
{
strlcpy(why, "a hexadecimal address, or two as high:low, after "
"the \"-\"", why_len);
return false;
}
c->sub_hi = sub.hi;
c->sub_lo = sub.lo;
c->sub_wide = sub.wide;
c->diff = true;
while (**end == ' ')
(*end)++;
}
max = (c->wide || c->sub_wide) ? 65535UL : 255UL;
if (**end == '&' && c->diff)
{
strlcpy(why, "a mask compares one address's bits, not a difference",
why_len);
return false;
}
/* "& 0x40" or "& 64": only those bits, as a game keeps its flags */
if (**end == '&')
{
@@ -3446,29 +3397,17 @@ static bool command_cond_parse(const char *s, struct command_cond *c,
}
while (*sp == ' ')
sp++;
/* a difference may be below zero: "<= -16" */
if (c->diff && *sp == '-')
{
neg = true;
sp++;
}
/* "== 112" or "== 0x70", as a mask is given */
if (!command_cond_number(sp, end, &num) || num > max)
if ( !command_cond_number(sp, end, &num)
|| num > (c->wide ? 65535UL : 255UL))
{
if (c->diff)
strlcpy(why, max > 255UL
? "a value from -65535 to 65535, which is what two values "
"of two bytes differ by"
: "a value from -255 to 255, which is what two bytes "
"differ by", why_len);
else
strlcpy(why, c->wide
? "a value from 0 to 65535 (0xFFFF), which is what two bytes hold"
: "a value from 0 to 255 (0xFF), which is what a byte holds",
why_len);
strlcpy(why, c->wide
? "a value from 0 to 65535 (0xFFFF), which is what two bytes hold"
: "a value from 0 to 255 (0xFF), which is what a byte holds",
why_len);
return false;
}
c->want = neg ? -(int)num : (int)num;
c->want = (unsigned)num;
c->op = (uint8_t)i;
return true;
}
@@ -3476,7 +3415,7 @@ static bool command_cond_parse(const char *s, struct command_cond *c,
/* What @c's memory holds now, looked up afresh as WATCH's bytes are,
* since a core may move its memory while the content runs; false where
* nothing maps it, with the reason as line clients read it in @why. */
static bool command_cond_met(const struct command_cond *c, int now)
static bool command_cond_met(const struct command_cond *c, unsigned now)
{
switch (c->op)
{
@@ -3498,7 +3437,7 @@ static bool command_cond_mapped(command_t *cmd, const char *name,
const struct command_cond *c)
{
char reason[128];
int now = 0;
unsigned now = 0;
if (command_cond_read(c, &now, reason, sizeof(reason)))
return true;
command_reply_error(cmd, name, "%s", command_memory_why(reason));
@@ -3566,15 +3505,12 @@ static struct
unsigned left; /* frames left in a RUN or PRESS step */
unsigned budget; /* frames left for the whole job */
unsigned ran; /* frames run so far */
int value; /* what the last condition read held */
unsigned value; /* what the last condition read held */
unsigned port; /* the player whose buttons are held */
uint16_t held; /* the buttons the job let go of, by bit */
uint8_t answer; /* enum cmd_job_answer */
unsigned nstops;
unsigned stopped; /* which stop held, from 1 */
/* whether each stop held on the frame before: one that becomes
* fires only where it did not */
bool stop_was[COMMAND_STOPS_MAX];
/* a frame with the buttons let go between two presses of the same
* one, so the game sees two taps rather than one long press */
bool gap;
@@ -3696,7 +3632,7 @@ static void command_run_frames_end(const char *how)
case CMD_ANSWER_UNTIL:
snprintf(first, sizeof(first), "%s %u\n", how,
command_run_frames_st.ran);
snprintf(last, sizeof(last), "VALUE %d\n",
snprintf(last, sizeof(last), "VALUE %u\n",
command_run_frames_st.value);
command_run_frames_answer(first, last, false);
break;
@@ -3841,14 +3777,8 @@ static void command_run_frames_enter(bool safe)
}
else
{
int now = 0;
command_run_frames_st.stops[s->idx - 1] = s->cond;
command_run_frames_st.nstops = s->idx;
/* what it holds as it takes over, so one already true when
* the list starts waits for it to stop holding first */
command_run_frames_st.stop_was[s->idx - 1] =
command_cond_read(&s->cond, &now, why, sizeof(why))
&& command_cond_met(&s->cond, now);
}
command_run_frames_st.cur++;
continue;
@@ -3997,7 +3927,7 @@ void command_run_frames_between(void)
void command_run_frames_tick(void)
{
char why[128];
int now = 0;
unsigned now = 0;
unsigned i;
struct command_step *s;
@@ -4018,17 +3948,10 @@ void command_run_frames_tick(void)
}
if (command_cond_met(&command_run_frames_st.stops[i], now))
{
bool was = command_run_frames_st.stop_was[i];
command_run_frames_st.stop_was[i] = true;
if (!command_run_frames_st.stops[i].edge || !was)
{
command_run_frames_st.stopped = i + 1;
command_run_frames_end("STOPPED");
return;
}
command_run_frames_st.stopped = i + 1;
command_run_frames_end("STOPPED");
return;
}
else
command_run_frames_st.stop_was[i] = false;
}
s = &command_run_frames_st.steps[command_run_frames_st.cur];
if (command_run_frames_st.gap)
@@ -4361,14 +4284,12 @@ bool command_set_input(command_t *cmd, const char *arg)
* With nothing, what is watched now. Each label and address watches the
* byte there under that label, carried by RUN_FRAMES, PRESS and
* RUN_UNTIL; a label and "off" stops watching it. Every pair is taken
* or none is, so a refusal leaves the list as it was. The limit is on
* the list they leave, so an off later in the request makes room.
* or none is, so a refusal leaves the list as it was.
*
* Answers with the list either way, so a client sees what it has. */
bool command_watch(command_t *cmd, const char *arg)
{
/* room past the limit for those an off further on takes back out */
struct command_watch_entry next[2 * COMMAND_WATCH_MAX];
struct command_watch_entry next[COMMAND_WATCH_MAX];
char reply[64 + COMMAND_WATCH_MAX * COMMAND_WATCH_LINE];
char label[sizeof(next[0].label)];
char reason[128];
@@ -4378,7 +4299,7 @@ bool command_watch(command_t *cmd, const char *arg)
unsigned i;
const char *sp;
memcpy(next, command_watches, sizeof(command_watches));
memcpy(next, command_watches, sizeof(next));
while (arg && *arg)
{
while (*arg == ' ')
@@ -4425,7 +4346,7 @@ bool command_watch(command_t *cmd, const char *arg)
else
{
struct command_cond where;
int now = 0;
unsigned now = 0;
char *end = NULL;
memset(&where, 0, sizeof(where));
/* high:low, two bytes as one value, as conditions take them */
@@ -4437,7 +4358,7 @@ bool command_watch(command_t *cmd, const char *arg)
return false;
}
/* only a label that is not watched yet needs room */
if (i == count && count >= ARRAY_SIZE(next))
if (i == count && count >= COMMAND_WATCH_MAX)
{
command_reply_error(cmd, "WATCH", "at most %u bytes watched "
"at once", (unsigned)COMMAND_WATCH_MAX);
@@ -4457,13 +4378,7 @@ bool command_watch(command_t *cmd, const char *arg)
strlcpy(next[i].label, label, sizeof(next[i].label));
}
}
if (count > COMMAND_WATCH_MAX)
{
command_reply_error(cmd, "WATCH", "at most %u bytes watched "
"at once", (unsigned)COMMAND_WATCH_MAX);
return false;
}
memcpy(command_watches, next, sizeof(command_watches));
memcpy(command_watches, next, sizeof(next));
command_watch_count = count;
_len = command_watch_lines(reply, sizeof(reply));
@@ -4473,12 +4388,11 @@ bool command_watch(command_t *cmd, const char *arg)
return true;
}
/* RUN_UNTIL <address> [- <address>] <condition> <value> [max frames]
/* RUN_UNTIL <address> <condition> <value> [max frames]
*
* Runs the content until the byte at a system address meets what was
* asked, then answers with how many frames that took and the value it
* found. With "- <address>" it is that byte less the other, signed:
* how far apart two things are. Where a client would otherwise run a few frames, read the
* found. Where a client would otherwise run a few frames, read the
* byte, and go round again - which is where a field run spent most of
* its calls - this is one request.
*
@@ -4690,9 +4604,7 @@ static void command_steps_report(command_t *cmd, long idle,
* <player> <controls> <frames>" and "until <condition>", in order, then
* answers with how it ended and what WATCH watches. A "stop
* <condition>" entry ends the list after any frame where it holds, so a
* death does not play out the rest; "stop becomes <condition>" only
* after one where it starts to, so one already true does not end the
* list at its first frame. "checkpoint" keeps the state
* death does not play out the rest, and "checkpoint" keeps the state
* STATE rollback returns to. The whole list runs no longer than a wait
* does; past that it answers WAITING and the step it was on, and the
* next request carries on from there.
@@ -4750,10 +4662,9 @@ bool command_steps(command_t *cmd, const char *arg)
for (entry = list; entry; entry = next)
{
struct command_step *s;
struct command_cond *c = NULL;
char *end = NULL;
struct command_cond *c = NULL;
char *end = NULL;
size_t n;
bool edge = false;
if ((next = strchr(entry, ';')))
*next++ = '\0';
while (*entry == ' ')
@@ -4775,13 +4686,6 @@ bool command_steps(command_t *cmd, const char *arg)
}
c = &stops[nstops++];
entry += 5;
/* "stop becomes <condition>": only once it starts to hold, so
* one true as the list starts does not end it at once */
if (!strncmp(entry, "becomes ", 8))
{
entry += 8;
edge = true;
}
}
else if (count >= COMMAND_STEPS_MAX)
{
@@ -4881,7 +4785,6 @@ bool command_steps(command_t *cmd, const char *arg)
command_reply_error(cmd, "STEPS", "%s", why);
return false;
}
c->edge = edge;
while (*end == ' ')
end++;
if (*end)
@@ -6188,23 +6091,11 @@ bool command_get_status(command_t *cmd, const char* arg)
return true;
}
/* READ_CORE_MEMORY <address> <bytes> [<address> <bytes>]...
*
* Each range a line of its own, in the order asked, so the arrays a
* step needs - positions, health - are one request rather than one
* apiece. Where nothing maps one, that line alone is the answer. */
#define COMMAND_READ_RANGES_MAX 16
bool command_read_memory(command_t *cmd, const char *arg)
{
char* reply = NULL;
char* reply_at = NULL;
const uint8_t* data = NULL;
unsigned int addrs[COMMAND_READ_RANGES_MAX];
unsigned int counts[COMMAND_READ_RANGES_MAX];
unsigned int nranges = 0;
unsigned int total = 0;
unsigned int r;
unsigned int nbytes = 0;
unsigned int alloc_size = 0;
unsigned int address = -1;
@@ -6213,48 +6104,27 @@ bool command_read_memory(command_t *cmd, const char *arg)
runloop_state_t *runloop_st = runloop_state_get_ptr();
const rarch_system_info_t* sys_info= &runloop_st->system;
while (arg && *arg)
{
char *end = NULL;
unsigned long num;
if (nranges >= COMMAND_READ_RANGES_MAX)
{
command_reply_error(cmd, "READ_CORE_MEMORY",
"at most %u ranges", (unsigned)COMMAND_READ_RANGES_MAX);
return false;
}
if (!command_parse_address(arg, &address, &end) || *end != ' ')
{
command_reply_error(cmd, "READ_CORE_MEMORY",
"a hexadecimal address, a space and a byte count");
return false;
}
num = strtoul(end + 1, &end, 10);
num = strtoul(end + 1, NULL, 10);
nbytes = num <= COMMAND_READ_NBYTES_MAX ? (unsigned)num : 0;
if (nbytes == 0 || total + nbytes > COMMAND_READ_NBYTES_MAX)
if (nbytes == 0)
{
command_reply_error(cmd, "READ_CORE_MEMORY", nranges
? "byte counts from 1, %u in all"
: "a byte count from 1 to %u", COMMAND_READ_NBYTES_MAX);
command_reply_error(cmd, "READ_CORE_MEMORY",
"a byte count from 1 to %u", COMMAND_READ_NBYTES_MAX);
return false;
}
addrs[nranges] = address;
counts[nranges] = nbytes;
nranges++;
total += nbytes;
/* another address and count, or the end of the line */
for (arg = end; *arg && (unsigned char)*arg <= ' '; arg++)
;
}
if (!nranges)
{
command_reply_error(cmd, "READ_CORE_MEMORY",
"a hexadecimal address, a space and a byte count");
return false;
}
/* Ensure large enough to return all requested bytes or an error message */
alloc_size = 96 * nranges + total * 3;
alloc_size = 96 + nbytes * 3;
reply = (char*)malloc(alloc_size);
if (!reply)
@@ -6262,53 +6132,43 @@ bool command_read_memory(command_t *cmd, const char *arg)
command_reply_error(cmd, "READ_CORE_MEMORY", "out of memory");
return false;
}
reply_at = reply + snprintf(reply, alloc_size - 1, "READ_CORE_MEMORY %x", address);
for (r = 0; r < nranges; r++)
if ((data = command_memory_get_pointer(
sys_info, address, &max_bytes,
0, reply_at, alloc_size - strlen(reply))))
{
char *line = reply + _len;
address = addrs[r];
nbytes = counts[r];
reply_at = line + snprintf(line, alloc_size - _len - 1,
"READ_CORE_MEMORY %x", address);
if ((data = command_memory_get_pointer(
sys_info, address, &max_bytes,
0, reply_at, alloc_size - (size_t)(reply_at - reply))))
char why[128];
char *end = reply_at;
unsigned int read = 0;
/* on into the region that follows, as a core that splits its RAM
* maps it; where nothing maps the rest, how much was read */
while (data)
{
char why[128];
char *end = reply_at;
unsigned int read = 0;
/* on into the region that follows, as a core that splits its RAM
* maps it; where nothing maps the rest, how much was read */
while (data)
{
unsigned int n = nbytes - read < max_bytes
? nbytes - read : max_bytes;
if (!n)
break;
end = command_hex_bytes(end, data, n);
read += n;
if (read >= nbytes)
break;
data = command_memory_get_pointer(sys_info, address + read,
&max_bytes, 0, why, sizeof(why));
}
*end++ = '\n';
if (read < nbytes)
end += snprintf(end, alloc_size - (size_t)(end - reply),
"TRUNCATED %u\n", read);
_len = (size_t)(end - reply);
}
else
{
/* command_memory_get_pointer() has put the reason in the reply,
* in the form line clients have always read: this range's line
* alone */
_len = strlen(line);
memmove(reply, line, _len + 1);
cmd->error = true;
break;
unsigned int n = nbytes - read < max_bytes
? nbytes - read : max_bytes;
if (!n)
break;
end = command_hex_bytes(end, data, n);
read += n;
if (read >= nbytes)
break;
data = command_memory_get_pointer(sys_info, address + read,
&max_bytes, 0, why, sizeof(why));
}
*end++ = '\n';
if (read < nbytes)
end += snprintf(end, alloc_size - (size_t)(end - reply),
"TRUNCATED %u\n", read);
_len = (size_t)(end - reply);
}
else
{
/* command_memory_get_pointer() has put the reason in the reply,
* in the form line clients have always read */
_len = strlen(reply);
cmd->error = true;
}
cmd->replier(cmd, reply, _len);
+8 -25
View File
@@ -603,11 +603,6 @@ struct cmd_rows
/* The reply may carry a "PREVIOUS ..." line: how a queued STEPS no
* reply has told of ended. */
bool previous;
/* Set, for a record: the field a reply of several such lines - one
* for each range READ_CORE_MEMORY was asked - gives them all in, in
* order, each with the trailers after it. The record's own fields
* are the first. */
const char *more;
};
/* A command with its own handler; @arg_desc names the argument, or
@@ -881,19 +876,18 @@ static const struct cmd_param cmd_params_watch[] = {
{ "watches", "Label and hexadecimal address pairs, as GET_MEMORY_MAP "
"gives them: \"x 86 mode 770\"; \"x 6d:86\" reads two bytes as one "
"value. A label watched again is replaced; off as its address stops "
"it. 16 at most, counted once the offs are taken.",
"it. 16 at most.",
CMD_PARAM_STRING, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_until[] = {
{ "address", "Hexadecimal, as GET_MEMORY_MAP gives it; high:low "
"(6d:86) reads two bytes as one value; & 0x40 after it compares "
"only those bits; 485 - 47e compares the first less the second, "
"signed.", CMD_PARAM_STRING, true, NULL, NULL, NULL },
"only those bits.", CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "condition", "How it compares with the value.",
CMD_PARAM_STRING, true, NULL, "==|!=|<|>|<=|>=", NULL },
{ "value", "0 to 255, or 65535 for high:low; a difference from "
"-255, or -65535.", CMD_PARAM_INT, true, NULL, NULL, NULL },
{ "value", "0 to 255, or 65535 for high:low.",
CMD_PARAM_INT, true, NULL, NULL, NULL },
{ "frames", "Frames to wait at most; a request's nine seconds when "
"left out.", CMD_PARAM_INT, false, NULL, NULL, NULL },
{ NULL }
@@ -916,8 +910,7 @@ static const struct cmd_param cmd_params_steps[] = {
"<frames>, until <condition> as RUN_UNTIL's, checkpoint, and up to "
"four stop <condition> that end it: \"until 86 >= 200; "
"press 1 a 20; until 1d == 0; stop e == 11\". 32 at most. A press "
"after a press lets go for a frame first. stop becomes <condition> "
"ends it only once it starts to hold, not where it already does.",
"after a press lets go for a frame first.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "queue", "on: answer now, run after those queued, pause when all "
"have (idle: ms since). After a queued list ends the next reply says "
@@ -979,16 +972,6 @@ static const struct cmd_param cmd_params_read[] = {
CMD_PARAM_INT, true, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_read_memory[] = {
{ "address", "The address to read from, in hexadecimal.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
{ "bytes", "How many.",
CMD_PARAM_INT, true, NULL, NULL, NULL },
{ "more", "Further ranges in the same request, as address and byte "
"count pairs: \"4a2 18 568 18\". Each answers a line, given as "
"ranges.", CMD_PARAM_STRING, false, NULL, NULL, NULL },
{ NULL }
};
static const struct cmd_param cmd_params_write[] = {
{ "address", "The address to write to, in hexadecimal.",
CMD_PARAM_STRING, true, NULL, NULL, NULL },
@@ -1229,7 +1212,7 @@ static const struct cmd_trailer cmd_trailers_read[] = {
{ "TRUNCATED", "truncated", "read", "Bytes read, short of those asked." },
{ NULL }
};
static const struct cmd_rows cmd_rows_read = { cmd_cols_read, cmd_trailers_read, 0, 0, " ", false, "ranges" };
static const struct cmd_rows cmd_rows_read = { cmd_cols_read, cmd_trailers_read, 0, 0, " " };
/* "MENU Main Menu", the entries, then which is selected of how many */
static const struct cmd_column cmd_cols_menu[] = {
{ "index", NULL },
@@ -1275,7 +1258,7 @@ static const struct cmd_action_map action_map[] = {
{ "RUN_FRAMES", command_run_frames, "<count>", "Pause and advance the content some frames; it finishes paused. One request runs nine seconds of frames at most (540 at 60fps), as PRESS, RUN_UNTIL and STEPS do.", 0, cmd_params_run_frames, &cmd_rows_frames },
{ "SET_INPUT", command_set_input, "<player> <control>[+<control>...] <value>", "Hold RetroPad buttons or stick axes for the core, over the player's own input, until set again or released. The core is given exactly what is held: opposing directions are not cleaned and the D-Pad is not turned with the picture. Under netplay the player is this machine's, and what is held is sent like a press. Answers with the player, the buttons held and the axes held, none for either where nothing is.", CMD_INFO_NO_TOOL, cmd_params_input, &cmd_rows_input },
{ "PRESS", command_press, "<player> <control>[+<control>...] <frames> [until <condition>]", "Press buttons for some frames and let go; with until, run on until a condition holds. What INPUT holds stays held. Answers with what WATCH watches; finishes paused.", 0, cmd_params_press, &cmd_rows_press },
{ "RUN_UNTIL", command_run_until, "<address>[:<address>] [- <address>[:<address>]] <condition> <value> [max frames]", "Run until memory meets a condition. Answers met or waiting (ask again) with the frames run, what WATCH watches and the value.", 0, cmd_params_until, &cmd_rows_until },
{ "RUN_UNTIL", command_run_until, "<address>[:<address>] <condition> <value> [max frames]", "Run until memory meets a condition. Answers met or waiting (ask again) with the frames run, what WATCH watches and the value.", 0, cmd_params_until, &cmd_rows_until },
{ "STEPS", command_steps, "<step>[; <step>...][|on]", "Play a stretch in one request, or queued to play on unpaused. Answers with how it ended and what WATCH watches.", 0, cmd_params_steps, &cmd_rows_steps },
{ "WATCH", command_watch, "[<label> <address|off>]...", "Watch bytes under labels: every step answers with them, so needs no read after. With none, answers each value, in decimal. Cleared when content closes.", 0, cmd_params_watch, &cmd_rows_watch },
{ "RELEASE_INPUT", command_release_input, "[player]", "Let go of everything SET_INPUT holds, for one player, or every player when given none. Answers as GET_INPUT does, for that player or the first.", CMD_INFO_NO_TOOL, cmd_params_input_release, &cmd_rows_input },
@@ -1292,7 +1275,7 @@ static const struct cmd_action_map action_map[] = {
{ "READ_CORE_RAM", command_read_ram, "<address> <number of bytes>", "Read bytes from core memory at an achievement address (needs a game with achievements).", CMD_INFO_READ_ONLY | CMD_INFO_NO_TOOL, cmd_params_read },
{ "WRITE_CORE_RAM", command_write_ram, "<address> <byte1> <byte2> ...", "Write bytes to core memory at an achievement address (needs a game with achievements).", CMD_INFO_DESTRUCTIVE | CMD_INFO_NO_TOOL, cmd_params_write },
#endif
{ "READ_CORE_MEMORY", command_read_memory, "<address> <number of bytes> [<address> <number of bytes>]...", "Read bytes from core memory at a system address. Answers with the address and the bytes, in hexadecimal; a line for each range asked.", CMD_INFO_READ_ONLY, cmd_params_read_memory, &cmd_rows_read },
{ "READ_CORE_MEMORY", command_read_memory, "<address> <number of bytes>", "Read bytes from core memory at a system address. Answers with the address and the bytes, in hexadecimal.", CMD_INFO_READ_ONLY, cmd_params_read, &cmd_rows_read },
{ "GET_MEMORY_MAP", command_get_memory_map, "No argument", "The core's memory map: start, length, select and disconnect in hexadecimal, as READ_CORE_MEMORY takes them. A core with none lists the RAM it hands over, in space core: system RAM from 0, save RAM after.", CMD_INFO_READ_ONLY, NULL, &cmd_rows_memory },
{ "WRITE_CORE_MEMORY",command_write_memory, "<address> <byte1> <byte2> ...", "Write bytes to core memory at a system address. Answers with the address and how many bytes were written.", CMD_INFO_DESTRUCTIVE, cmd_params_write },
+1
View File
@@ -894,6 +894,7 @@
#define DEFAULT_SETTINGS_SHOW_ACCESSIBILITY true
#define DEFAULT_SETTINGS_SHOW_POWER_MANAGEMENT true
#define DEFAULT_SETTINGS_SHOW_ACHIEVEMENTS true
#define DEFAULT_SETTINGS_SHOW_BLUETOOTH true
#define DEFAULT_SETTINGS_SHOW_NETWORK true
#define DEFAULT_SETTINGS_SHOW_PLAYLISTS true
#define DEFAULT_SETTINGS_SHOW_USER true
+6
View File
@@ -418,6 +418,12 @@
#define SUPPORTS_RTGA false
#endif
#ifdef HAVE_RGIF
#define SUPPORTS_RGIF true
#else
#define SUPPORTS_RGIF false
#endif
#ifdef HAVE_RWEBP
#define SUPPORTS_RWEBP true
#else
+1
View File
@@ -1015,6 +1015,7 @@ typedef struct settings
bool settings_show_accessibility;
bool settings_show_power_management;
bool settings_show_achievements;
bool settings_show_bluetooth;
bool settings_show_network;
bool settings_show_playlists;
bool settings_show_user;
+6 -2
View File
@@ -12,7 +12,7 @@
#include <streams/file_stream.h>
#if defined(HAVE_RPNG) || defined(HAVE_RJPEG) || defined(HAVE_RTGA) || defined(HAVE_RBMP) || defined(HAVE_RWEBP) || defined(HAVE_RDDS)
#if defined(HAVE_RPNG) || defined(HAVE_RJPEG) || defined(HAVE_RTGA) || defined(HAVE_RBMP) || defined(HAVE_RWEBP) || defined(HAVE_RDDS) || defined(HAVE_RGIF)
#define PREFER_NON_STB_IMAGE
#endif
@@ -87,6 +87,10 @@ static const char image_formats[] =
"|tga"
#endif
#ifdef HAVE_RGIF
"|gif"
#endif
#ifdef HAVE_RWEBP
"|webp"
#endif
@@ -95,7 +99,7 @@ static const char image_formats[] =
"|dds"
#endif
#if !defined(HAVE_RJPEG) && !defined(HAVE_RPNG) && !defined(HAVE_RBMP) && !defined(HAVE_RTGA) && !defined(HAVE_RWEBP) && !defined(HAVE_RDDS)
#if !defined(HAVE_RJPEG) && !defined(HAVE_RPNG) && !defined(HAVE_RBMP) && !defined(HAVE_RTGA) && !defined(HAVE_RWEBP) && !defined(HAVE_RDDS) && !defined(HAVE_RGIF)
#error "can't build this core with no image formats"
#endif
;
+2 -25
View File
@@ -143,15 +143,6 @@ goes on into the next; where nothing maps the rest it stops there and
says so, `TRUNCATED 2` after the bytes, given as `truncated: true,
read: 2`.
`READ_CORE_MEMORY 47e 18 4a2 18 568 18` reads several ranges in one
request (`more: "4a2 18 568 18"` over MCP), up to sixteen and 16384
bytes in all: a line for each, in the order asked, each with its own
`TRUNCATED` after it where it was cut short. The record's fields are
the first range's, and `ranges` gives every one, the first too, as
`{"address", "bytes"}` with `truncated` and `read` where cut short.
Where nothing maps one of them, its line alone is the answer, as a
single range's is.
## Results that are rows
The replies that are lines of tab-separated fields - `OPTION`,
@@ -455,9 +446,7 @@ its place in the list when its address changes; with nothing watched
it answers `WATCH none`. Values are decimal, 0 to 255 - or to 65535
for a `high:low` watch - as `RUN_UNTIL` takes them; addresses are hexadecimal. A label is up to 15 characters,
with no tab or newline in it. `x 6d:86` watches two bytes as one value,
high:low, as conditions read them. Sixteen at once, counted on the
list the request leaves: `WATCH lives 4d e13h off` with sixteen watched
adds one and drops one. Each is checked when it is
high:low, as conditions read them. Sixteen at once. Each is checked when it is
set - an address nothing maps is refused there -
and read afresh on every reply, so a core that moves its memory is
followed; one that stops mapping a watched address answers `none` for
@@ -484,14 +473,6 @@ x 7e0086 52
VALUE 5
```
`<address> - <address>` compares the first's value less the second's,
signed, each one byte or `high:low`: `485 - 47e <= 32` waits until an
enemy at x `485` is no more than 32 to the right of the player at
`47e`, walking in until it is in range. The value may be below zero, from -255 to 255, or -65535 to
65535 where either side is `high:low`, and `VALUE` answers the
difference, `VALUE -12`. Both addresses must be mapped, and a mask is
refused on one.
Where a client would otherwise run a few frames, read the byte and go
round again, this is one request. The address is the kind
`GET_MEMORY_MAP` gives and `READ_CORE_MEMORY` takes, and one nothing
@@ -512,11 +493,7 @@ told it failed.
step is `run <frames>`, `press <player> <controls> <frames>`, `until
<condition>` or `checkpoint`, in order; up to four `stop <condition>`
entries anywhere in the list end it after any frame where one holds - a
death, the level's end - so neither plays out the rest. `stop becomes
<condition>` ends it only after a frame where the condition starts to
hold: one already true when the list starts - `stop becomes 412 == 4`
while the player is still reeling - waits for it to stop holding and
hold again, rather than ending the list at its first frame. `checkpoint`
death, the level's end - so neither plays out the rest. `checkpoint`
keeps the state `STATE rollback` returns to, where the list has got to,
so a stretch can mark its own retry point:
+7 -45
View File
@@ -3010,45 +3010,7 @@ void dxgi_set_hdr_metadata(
#include <math.h>
#include <stdint.h>
#include <formats/rpng.h>
/* IEEE 754 binary16 -> binary32. Used to decode FP16 scRGB samples. */
static INLINE float dxgi_half_to_float(uint16_t h)
{
uint32_t sign = (uint32_t)(h >> 15) & 0x1u;
uint32_t exp = (uint32_t)(h >> 10) & 0x1Fu;
uint32_t mant = (uint32_t) h & 0x3FFu;
uint32_t f;
union { uint32_t u; float f; } u;
if (exp == 0)
{
if (mant == 0)
f = sign << 31; /* signed zero */
else
{
/* Subnormal: normalize. */
while (!(mant & 0x400u))
{
mant <<= 1;
exp -= 1; /* exp starts at 0, goes negative */
}
exp += 1;
mant &= ~0x400u;
f = (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13);
}
}
else if (exp == 0x1F)
{
/* Inf / NaN: propagate to float32 Inf/NaN with the same mantissa
* low bits (clamped away later, so exact bit-pattern doesn't matter). */
f = (sign << 31) | (0xFFu << 23) | (mant << 13);
}
else
f = (sign << 31) | ((exp + 127 - 15) << 23) | (mant << 13);
u.u = f;
return u.f;
}
#include <math/float16.h>
/* ST.2084 (PQ) EOTF. Input is the non-linear PQ code value in [0,1];
* output is linear light normalized so 1.0 == 10000 nits. */
@@ -3210,9 +3172,9 @@ bool dxgi_hdr_readback_to_bgr24(
const uint16_t* src = (const uint16_t*)src_row + src_x * 4;
for (x = 0; x < width; x++)
{
float r = dxgi_half_to_float(src[4 * x + 0]) * inv_scale;
float g = dxgi_half_to_float(src[4 * x + 1]) * inv_scale;
float b = dxgi_half_to_float(src[4 * x + 2]) * inv_scale;
float r = retro_half_to_float(src[4 * x + 0]) * inv_scale;
float g = retro_half_to_float(src[4 * x + 1]) * inv_scale;
float b = retro_half_to_float(src[4 * x + 2]) * inv_scale;
/* scRGB carries legal negative values for out-of-gamut
* colours; clamping in LinearToSRGB handles them. */
dst[3 * x + 0] = dxgi_float_to_unorm8(dxgi_linear_to_srgb(b));
@@ -3309,9 +3271,9 @@ bool dxgi_hdr_readback_to_rgb16(
* scale to absolute nits, PQ-encode into 16 bit. Extended
* range (negatives, >10000 nits) clamps -- unavoidable for
* a UNORM PNG. */
float r = dxgi_half_to_float(src[x * 4 + 0]);
float g = dxgi_half_to_float(src[x * 4 + 1]);
float b = dxgi_half_to_float(src[x * 4 + 2]);
float r = retro_half_to_float(src[x * 4 + 0]);
float g = retro_half_to_float(src[x * 4 + 1]);
float b = retro_half_to_float(src[x * 4 + 2]);
float lvl;
float nr = r * (80.0f / 10000.0f);
float ng = g * (80.0f / 10000.0f);
+41 -2
View File
@@ -482,13 +482,52 @@ static bool kms_display_server_crtc_vrr(int fd, uint32_t crtc_id)
return vrr;
}
/* Physical size comes from the connector (EDID or the panel's device
* tree node), pixel size from the live mode. The menu's touch drag,
* swipe and scaling need DPI; 0 mm means the sink did not say. */
static bool kms_display_server_geometry_metric(
enum display_metric_types type, float *value)
{
unsigned n;
switch (type)
{
case DISPLAY_METRIC_PIXEL_WIDTH:
n = g_drm_mode ? g_drm_mode->hdisplay : 0;
break;
case DISPLAY_METRIC_PIXEL_HEIGHT:
n = g_drm_mode ? g_drm_mode->vdisplay : 0;
break;
case DISPLAY_METRIC_MM_WIDTH:
n = g_drm_connector ? g_drm_connector->mmWidth : 0;
break;
case DISPLAY_METRIC_MM_HEIGHT:
n = g_drm_connector ? g_drm_connector->mmHeight : 0;
break;
case DISPLAY_METRIC_DPI:
if ( !g_drm_mode || !g_drm_mode->hdisplay
|| !g_drm_connector || !g_drm_connector->mmWidth)
return false;
*value = (float)g_drm_mode->hdisplay * 25.4f
/ (float)g_drm_connector->mmWidth;
return true;
default:
return false;
}
if (!n)
return false;
*value = (float)n;
return true;
}
static bool kms_display_server_get_metrics(void *data,
enum display_metric_types type, float *value)
{
bool vrr;
if ( type != DISPLAY_METRIC_TOTAL_LINES
|| !kms_display_server_mode_timed())
if (type != DISPLAY_METRIC_TOTAL_LINES)
return kms_display_server_geometry_metric(type, value);
if (!kms_display_server_mode_timed())
return false;
/* Read with the line count, outside Scanline Sync's wait */
vrr = g_crtc_id && kms_display_server_crtc_vrr(g_drm_fd, g_crtc_id);
+4 -32
View File
@@ -30,6 +30,7 @@
#include "../common/vulkan_video_import.h"
#include <retro_inline.h>
#include <retro_miscellaneous.h>
#include <math/float16.h>
#include <queues/mpsc_stack.h>
#include <queues/task_queue.h>
#include <retro_math.h>
@@ -17375,35 +17376,6 @@ static bool vulkan_read_viewport_bgrx(void *data, uint8_t *buffer,
}
#ifdef VULKAN_HDR_SWAPCHAIN
/* IEEE-754 half (binary16) -> float, for reading an RGBA16F scRGB backbuffer
* on the CPU. Handles normals, subnormals, sign and inf/nan. */
static float vulkan_half_to_float(uint16_t h)
{
uint32_t sign = (uint32_t)(h & 0x8000) << 16;
uint32_t exp = (h >> 10) & 0x1F;
uint32_t mant = h & 0x3FF;
uint32_t f;
float out;
if (exp == 0)
{
if (mant == 0)
f = sign;
else
{
exp = 127 - 15 + 1;
while (!(mant & 0x400)) { mant <<= 1; exp--; }
mant &= 0x3FF;
f = sign | (exp << 23) | (mant << 13);
}
}
else if (exp == 0x1F)
f = sign | 0x7F800000 | (mant << 13);
else
f = sign | ((exp - 15 + 127) << 23) | (mant << 13);
memcpy(&out, &f, sizeof(out));
return out;
}
/* ST.2084 (PQ) inverse-EOTF: normalised linear (v = nits/10000, [0,1]) ->
* PQ code [0,1]. Same constants as the forward HDR pipeline. */
static float vulkan_pq_encode(float v)
@@ -17589,9 +17561,9 @@ static bool vulkan_read_viewport_hdr(void *data, uint16_t *buffer,
int x;
for (x = 0; x < (int)vp_width; x++)
{
float r = vulkan_half_to_float(row[x * 4 + 0]);
float g = vulkan_half_to_float(row[x * 4 + 1]);
float b = vulkan_half_to_float(row[x * 4 + 2]);
float r = retro_half_to_float(row[x * 4 + 0]);
float g = retro_half_to_float(row[x * 4 + 1]);
float b = retro_half_to_float(row[x * 4 + 2]);
float lvl;
buffer[x * 3 + 0] = vulkan_scrgb_channel_to_pq16(r);
buffer[x * 3 + 1] = vulkan_scrgb_channel_to_pq16(g);
+19
View File
@@ -432,6 +432,19 @@ static bool android_gfx_ctx_destroy_surface(void *data)
#endif
}
#ifdef HAVE_EGL
/* A context in the share group, of the window's client API: OpenGL ES */
static bool android_gfx_ctx_shared_context_new(void *data,
gfx_ctx_shared_context_t *out)
{
android_ctx_data_t *and = (android_ctx_data_t*)data;
memset(out, 0, sizeof(*out));
if (!and || android_api != GFX_CTX_OPENGL_ES_API)
return false;
return egl_shared_context_new(&and->egl, EGL_OPENGL_ES_API, out);
}
#endif
const gfx_ctx_driver_t gfx_ctx_android = {
android_gfx_ctx_init,
android_gfx_ctx_destroy,
@@ -473,9 +486,15 @@ const gfx_ctx_driver_t gfx_ctx_android = {
NULL, /* presentable */
NULL, /* last_present_time */
android_gfx_ctx_release_current,
#ifdef HAVE_EGL
android_gfx_ctx_shared_context_new,
egl_shared_context_bind,
egl_shared_context_free,
#else
NULL, /* shared_context_new */
NULL, /* shared_context_bind */
NULL, /* shared_context_free */
#endif
#ifdef HAVE_EGL
&android_gfx_ctx_windows
#else
+35 -1
View File
@@ -1447,6 +1447,28 @@ static bool gfx_ctx_drm_destroy_surface(void *data)
#endif
}
#ifdef HAVE_EGL
/* A context in the share group, of the window's client API */
static bool gfx_ctx_drm_shared_context_new(void *data,
gfx_ctx_shared_context_t *out)
{
gfx_ctx_drm_data_t *drm = (gfx_ctx_drm_data_t*)data;
memset(out, 0, sizeof(*out));
if (!drm)
return false;
switch (drm_api)
{
case GFX_CTX_OPENGL_API:
return egl_shared_context_new(&drm->egl, EGL_OPENGL_API, out);
case GFX_CTX_OPENGL_ES_API:
return egl_shared_context_new(&drm->egl, EGL_OPENGL_ES_API, out);
default:
break;
}
return false;
}
#endif
const gfx_ctx_driver_t gfx_ctx_drm = {
gfx_ctx_drm_init,
gfx_ctx_drm_destroy,
@@ -1484,5 +1506,17 @@ const gfx_ctx_driver_t gfx_ctx_drm = {
NULL,
NULL,
gfx_ctx_drm_create_surface,
gfx_ctx_drm_destroy_surface
gfx_ctx_drm_destroy_surface,
NULL, /* presentable */
NULL, /* last_present_time */
NULL, /* release_current */
#ifdef HAVE_EGL
gfx_ctx_drm_shared_context_new,
egl_shared_context_bind,
egl_shared_context_free
#else
NULL, /* shared_context_new */
NULL, /* shared_context_bind */
NULL /* shared_context_free */
#endif
};
+83 -1
View File
@@ -152,6 +152,10 @@ static uint8_t wgl_flags;
#ifdef HAVE_EGL
static egl_ctx_data_t win32_egl;
#endif
/* The attributes the window's context was made with, for contexts in its
* share group (gfx_ctx_wgl_shared_context_new); none for a legacy one */
static int wgl_ctx_attribs[16];
static bool wgl_ctx_attribs_used = false;
static unsigned win32_major = 0;
static unsigned win32_minor = 0;
static int win32_interval = 0;
@@ -368,6 +372,10 @@ void create_gl_context(HWND hwnd, bool *quit)
}
}
for (i = 0; i < 16; i++)
wgl_ctx_attribs[i] = attribs[i];
wgl_ctx_attribs_used = true;
/* Found a suitable version that is high enough, we can stop now */
break;
}
@@ -819,6 +827,71 @@ static void gfx_ctx_wgl_release_current(void *data)
}
}
#if (defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)) && !defined(HAVE_OPENGLES)
/* A context in the share group of the one current on this thread - the
* core's, or the window's - with the window's attributes, for a thread
* of its own. It is made current on the window's device context: it
* draws into framebuffers of its own, never into the window. */
static bool gfx_ctx_wgl_shared_context_new(void *data,
gfx_ctx_shared_context_t *out)
{
typedef HGLRC (APIENTRY *wglCreateContextAttribsProc)
(HDC, HGLRC, const int*);
HGLRC share = wglGetCurrentContext();
HGLRC ctx = NULL;
memset(out, 0, sizeof(*out));
if (win32_api != GFX_CTX_OPENGL_API || !win32_hdc || !win32_hrc)
return false;
if (!share)
share = win32_hw_hrc ? win32_hw_hrc : win32_hrc;
if (wgl_ctx_attribs_used)
{
wglCreateContextAttribsProc create = (wglCreateContextAttribsProc)
gfx_ctx_wgl_get_proc_address("wglCreateContextAttribsARB");
if (create)
ctx = create(win32_hdc, share, wgl_ctx_attribs);
}
else if ((ctx = wglCreateContext(win32_hdc)) && !wglShareLists(share, ctx))
{
wglDeleteContext(ctx);
ctx = NULL;
}
if (!ctx)
{
RARCH_ERR("[WGL] A shared context could not be made: 0x%08lx.\n",
(unsigned long)GetLastError());
return false;
}
out->kind = GFX_CTX_SHARED_WGL;
out->context = ctx;
out->get_proc_address = gfx_ctx_wgl_get_proc_address;
return true;
}
static bool gfx_ctx_wgl_shared_context_bind(gfx_ctx_shared_context_t *ctx,
bool bind)
{
if (ctx->kind != GFX_CTX_SHARED_WGL)
return false;
if (!bind)
return wglMakeCurrent(NULL, NULL) ? true : false;
/* The window's, read now: a window made again has another one, of the
* same pixel format */
return wglMakeCurrent(win32_hdc, (HGLRC)ctx->context) ? true : false;
}
static void gfx_ctx_wgl_shared_context_free(gfx_ctx_shared_context_t *ctx)
{
if (ctx->kind != GFX_CTX_SHARED_WGL)
return;
wglDeleteContext((HGLRC)ctx->context);
memset(ctx, 0, sizeof(*ctx));
}
#endif
static void gfx_ctx_wgl_bind_hw_render(void *data, bool enable)
{
switch (win32_api)
@@ -1058,5 +1131,14 @@ const gfx_ctx_driver_t gfx_ctx_wgl = {
gfx_ctx_wgl_destroy_surface,
gfx_ctx_wgl_presentable,
gfx_ctx_wgl_last_present_time,
gfx_ctx_wgl_release_current
gfx_ctx_wgl_release_current,
#if (defined(HAVE_OPENGL) || defined(HAVE_OPENGL1) || defined(HAVE_OPENGL_CORE)) && !defined(HAVE_OPENGLES)
gfx_ctx_wgl_shared_context_new,
gfx_ctx_wgl_shared_context_bind,
gfx_ctx_wgl_shared_context_free
#else
NULL, /* shared_context_new */
NULL, /* shared_context_bind */
NULL /* shared_context_free */
#endif
};
File diff suppressed because it is too large. Load diff
+81
View File
@@ -5102,6 +5102,87 @@ bool video_driver_hw_render_interface_kept(void)
return iface == video_hw_render_iface_held;
}
/* RETRO_ENVIRONMENT_GET_HW_SHARED_CONTEXT_INTERFACE: the context driver's
* contexts in the window context's share group, which the core's context
* is in too, handed to the core for threads of its own. Made at most a few
* times a session (one per thread of the core's), so each its own block. */
struct retro_hw_shared_context
{
gfx_ctx_shared_context_t shared;
};
static struct retro_hw_shared_context *RETRO_CALLCONV
video_hw_shared_context_create(void)
{
video_driver_state_t *video_st = &video_driver_st;
const gfx_ctx_driver_t *ctx = &video_st->current_video_context;
struct retro_hw_shared_context *out;
if ( !ctx->shared_context_new
|| !video_st->context_data
|| !(out = (struct retro_hw_shared_context*)calloc(1, sizeof(*out))))
return NULL;
if (!ctx->shared_context_new((void*)video_st->context_data, &out->shared))
{
free(out);
return NULL;
}
return out;
}
static bool RETRO_CALLCONV video_hw_shared_context_make_current(
struct retro_hw_shared_context *ctx, bool current)
{
const gfx_ctx_driver_t *drv = &video_driver_st.current_video_context;
if (!ctx || !drv->shared_context_bind)
return false;
return drv->shared_context_bind(&ctx->shared, current);
}
static void RETRO_CALLCONV video_hw_shared_context_destroy(
struct retro_hw_shared_context *ctx)
{
const gfx_ctx_driver_t *drv = &video_driver_st.current_video_context;
if (!ctx)
return;
if (drv->shared_context_free)
drv->shared_context_free(&ctx->shared);
free(ctx);
}
static const struct retro_hw_shared_context_interface
video_hw_shared_context_iface = {
RETRO_HW_SHARED_CONTEXT_INTERFACE_VERSION,
video_hw_shared_context_create,
video_hw_shared_context_make_current,
video_hw_shared_context_destroy
};
const struct retro_hw_shared_context_interface
*video_driver_hw_shared_context_interface(void)
{
video_driver_state_t *video_st = &video_driver_st;
const gfx_ctx_driver_t *ctx = &video_st->current_video_context;
switch (video_st->hw_render.context_type)
{
case RETRO_HW_CONTEXT_OPENGL:
case RETRO_HW_CONTEXT_OPENGL_CORE:
case RETRO_HW_CONTEXT_OPENGLES2:
case RETRO_HW_CONTEXT_OPENGLES3:
case RETRO_HW_CONTEXT_OPENGLES_VERSION:
break;
default:
return NULL;
}
if ( !video_st->context_data
|| !ctx->shared_context_new
|| !ctx->shared_context_bind
|| !ctx->shared_context_free)
return NULL;
return &video_hw_shared_context_iface;
}
bool video_driver_hw_context_moved(int before, bool shared)
{
return before != -1 && before != (shared ? 1 : 0);
+15 -6
View File
@@ -829,7 +829,8 @@ enum gfx_ctx_shared_kind
{
GFX_CTX_SHARED_NONE = 0,
GFX_CTX_SHARED_EGL,
GFX_CTX_SHARED_GLX
GFX_CTX_SHARED_GLX,
GFX_CTX_SHARED_WGL
};
/* A second context in the window context's share group, for a thread
@@ -841,7 +842,7 @@ typedef struct gfx_ctx_shared_context
enum gfx_ctx_shared_kind kind;
void *display; /* EGLDisplay, or the X Display */
void *config; /* EGLConfig, or GLXFBConfig */
void *context; /* EGLContext, or GLXContext */
void *context; /* EGLContext, GLXContext or HGLRC */
void *surface; /* EGL: its own pbuffer, NULL surfaceless */
unsigned long drawable; /* GLX: its own pbuffer */
unsigned visual_id; /* GLX: the config's visual */
@@ -1011,10 +1012,12 @@ typedef struct gfx_ctx_driver
void (*release_current)(void *data);
/* Optional, desktop GL: a context in the window context's share
* group (gfx_ctx_shared_context_t). New and free run on the video
* thread with the window's context current, and leave it so; bind
* and unbind run on the thread that uses the context, which never
* gets the window's surface. NULL where the driver has none. Placed
* group (gfx_ctx_shared_context_t). New and free run on a thread
* with a context of the group current - the video thread with the
* window's, or a hardware core's thread with the core's
* (RETRO_ENVIRONMENT_GET_HW_SHARED_CONTEXT_INTERFACE) - and leave it
* so; bind and unbind run on the thread that uses the context, which
* never gets the window's surface. NULL where the driver has none. Placed
* last, as above. */
bool (*shared_context_new)(void *data, gfx_ctx_shared_context_t *out);
bool (*shared_context_bind)(gfx_ctx_shared_context_t *ctx, bool bind);
@@ -2827,6 +2830,12 @@ void video_driver_cache_context_ack_set(void);
* restart - and the core has to be reset after all. True for a core
* that holds none. */
void video_driver_hw_render_interface_note(const struct retro_hw_render_interface *iface);
/* RETRO_ENVIRONMENT_GET_HW_SHARED_CONTEXT_INTERFACE's answer: NULL when the
* core's context is not OpenGL or the context driver cannot make contexts
* in its share group. */
const struct retro_hw_shared_context_interface
*video_driver_hw_shared_context_interface(void);
bool video_driver_hw_render_interface_kept(void);
/* Which of a GL driver's contexts a hardware core renders in: its own,
* shared with the driver's (@shared), or the driver's. Called by the
+20
View File
@@ -582,6 +582,9 @@ VIDEO IMAGE
#ifdef HAVE_RTGA
#include "../libretro-common/formats/tga/rtga.c"
#endif
#ifdef HAVE_RGIF
#include "../libretro-common/formats/gif/rgif.c"
#endif
#ifdef HAVE_IMAGEVIEWER
#include "../cores/libretro-imageviewer/image_core.c"
@@ -776,6 +779,13 @@ VIDEO DRIVER
#include "../gfx/drivers/d3d12.c"
#endif
#ifdef HAVE_MF_AUDIO
#include "../record/record_mf_audio.c"
#if defined(HAVE_D3D12) && defined(HAVE_RTMP) && defined(HAVE_RFLV)
#include "../record/record_stream_d3d12.c"
#endif
#endif
#if defined(HAVE_D3D10)
#include "../gfx/drivers/d3d10.c"
#endif
@@ -905,6 +915,7 @@ FONTS
#include "../gfx/font_driver.c"
#include "../libretro-common/gfx/font/rtt.c"
#include "../gfx/drivers_font_renderer/stb.c"
#if defined(HAVE_FREETYPE)
@@ -1712,6 +1723,7 @@ RECORDING
#include "../record/record_mp4_audio_edit.c"
#include "../record/record_mediacodec_encoder.c"
#include "../record/record_mux_mediacodec.c"
#include "../record/record_aac_stream_timing.c"
#include "../record/record_mediacodec_worker.c"
#include "../record/record_mediacodec_surface.c"
#include "../record/record_mediacodec_file.c"
@@ -1740,6 +1752,12 @@ RECORDING
#include "../record/record_avfoundation_worker.c"
#include "../record/record_avfoundation_session.c"
#include "../libretro-common/formats/h265/rh265_sps_crop.c"
#if defined(HAVE_NETWORKING) && defined(HAVE_RTMP) && defined(HAVE_RFLV)
#include "../record/record_stream_vt.c"
#include "../record/record_stream_output_vt.c"
#include "../record/record_vt_packet.c"
#include "../libretro-common/net/net_wake.c"
#endif
#endif
#ifdef HAVE_FFMPEG
#include "../record/drivers/record_ffmpeg.c"
@@ -1787,6 +1805,7 @@ NETPLAY
#include "../network/netplay/netplay_room_parse.c"
#include "../libretro-common/net/net_compat.c"
#include "../libretro-common/net/net_socket.c"
#include "../libretro-common/net/net_resolve.c"
#include "../libretro-common/net/net_http.c"
#ifdef HAVE_RTMP
#include "../libretro-common/net/net_rtmp_chunk.c"
@@ -1798,6 +1817,7 @@ NETPLAY
#include "../libretro-common/net/net_rtmp_reply.c"
#include "../libretro-common/net/net_rtmp_session.c"
#include "../libretro-common/net/net_rtmp_socket.c"
#include "../libretro-common/net/net_rtmp_client.c"
#endif
#ifdef HAVE_IFINFO
#include "../libretro-common/net/net_ifinfo.c"
+4
View File
@@ -74,6 +74,7 @@
#endif
#ifdef HAVE_AVF
#include "../record/record_at_audio.c"
#include "../camera/drivers/avfoundation.m"
#include "../record/drivers/record_avfoundation.m"
#endif
@@ -84,6 +85,9 @@
#include "../record/record_gpu_capture_metal.m"
#include "../record/record_mux_avfoundation.m"
#include "../record/record_gpu_session_metal.m"
#if defined(HAVE_NETWORKING) && defined(HAVE_RTMP) && defined(HAVE_RFLV)
#include "../record/record_at_resample.c"
#endif
#include "../record/record_gpu_dispatch_metal.m"
#endif
#endif
+65 -34
View File
@@ -514,6 +514,9 @@ typedef struct
bool run_state_update;
/* Touch panel properties */
bool is_touch_device;
/* Contacts come in MT slots; the single-touch emulation
* (ABS_X/ABS_Y/BTN_TOUCH) repeats one of them and is ignored. */
bool mt_slots;
} udev_input_touch_t;
#endif
@@ -715,7 +718,6 @@ static bool udev_vp_ok;
static udev_input_mouse_t *udev_get_mouse(
struct udev_input *udev, unsigned port)
{
unsigned i;
unsigned mouse_index = 0;
int dev_index = -1;
@@ -1131,10 +1133,8 @@ static void udev_touch_ts_print(const udev_touch_ts_t *ts)
static udev_input_device_t *udev_get_pointer_port_dev(
struct udev_input *udev, unsigned port)
{
uint16_t i;
uint16_t pointer_index = 0;
int16_t dev_index = -1;
udev_input_mouse_t *mouse = NULL;
if (port >= MAX_USERS || !video_driver_has_focus())
return NULL;
@@ -1506,13 +1506,19 @@ static void udev_init_touch_dev(udev_input_device_t *dev)
{
RARCH_WARN("[udev] Failed to get touchscreen limits.\n");
/* One slot, fed by the single-touch events */
memset(&abs_info, 0, sizeof(abs_info));
udev_touch_set_limits_from(&abs_info, &touch->info_slots);
touch->info_slots.enabled = false;
}
else
udev_touch_set_limits_from(&abs_info, &touch->info_slots);
touch->mt_slots = touch->info_slots.enabled
&& touch->info_slots.range > 1;
/* Single-touch devices use ABS_X/Y, Multi-touch use ABS_MT_POSITION_X/Y */
if (abs_info.maximum == 0)
if (!touch->mt_slots)
{
/* TODO - Test for single-touch devices. */
RARCH_WARN("[udev] Single-touch devices are currently untested.\n");
@@ -1608,9 +1614,15 @@ static void udev_init_touch_dev(udev_input_device_t *dev)
udev_destroy_touch_dev(dev);
return;
}
for (iii = 0; iii < touch->info_slots.range; ++iii)
{
touch->staging[iii].tracking_id = UDEV_INPUT_TOUCH_TRACKING_ID_NONE;
touch->current[iii].tracking_id = UDEV_INPUT_TOUCH_TRACKING_ID_NONE;
}
/* Initialize touch device */
touch->current_slot = UDEV_INPUT_TOUCH_SLOT_ID_NONE;
/* Initialize touch device; the kernel starts on slot 0 and does
* not announce it */
touch->current_slot = 0;
touch->is_touch_device = true;
touch->run_state_update = false;
udev_touch_ts_now(&touch->last_state_update);
@@ -1711,7 +1723,7 @@ static void udev_sync_touch(udev_input_device_t *dev)
int32_t *mt_req_values;
uint8_t *mt_request_data;
size_t mt_request_data_size;
size_t slot_count;
int slot_count;
udev_touch_ts_t now;
udev_input_touch_t *touch = &dev->touch;
udev_slot_state_t *staging = dev->touch.staging;
@@ -1754,6 +1766,11 @@ static void udev_sync_touch(udev_input_device_t *dev)
staging[iii].tracking_id = mt_req_values[iii];
udev_touch_ts_copy(&now, &staging[iii].td_time);
}
/* Contacts begun or ended unseen are counted as they are now */
touch->staging_active = 0;
for (iii = 0; iii < slot_count; ++iii)
if (staging[iii].tracking_id >= 0)
touch->staging_active++;
/* Request MT position on the x-axis for each slot */
*mt_req_code = ABS_MT_POSITION_X;
@@ -1856,8 +1873,8 @@ static bool udev_translate_touch_pos(
* One way to fix this is to add or remove 0.5, but this needs floating
* point operations which might not be desirable.
*/
int32_t ma_pos_x = (((((pointer_pos_x + src_touch->info_x_limits.min) * 0x7fff) / src_touch->info_x_limits.range) * VIDEO_SCALE_W(target_vp->full_dims)) / 0x7fff);
int32_t ma_pos_y = (((((pointer_pos_y + src_touch->info_y_limits.min) * 0x7fff) / src_touch->info_y_limits.range) * VIDEO_SCALE_H(target_vp->full_dims)) / 0x7fff);
int32_t ma_pos_x = (((((pointer_pos_x - src_touch->info_x_limits.min) * 0x7fff) / src_touch->info_x_limits.range) * VIDEO_SCALE_W(target_vp->full_dims)) / 0x7fff);
int32_t ma_pos_y = (((((pointer_pos_y - src_touch->info_y_limits.min) * 0x7fff) / src_touch->info_y_limits.range) * VIDEO_SCALE_H(target_vp->full_dims)) / 0x7fff);
/* Calculate relative offsets. */
VIDEO_POS_ADD(*pointer_ma_rel, ma_pos_x - VIDEO_POS_X(*pointer_ma_pos),
@@ -2390,7 +2407,7 @@ static void udev_report_touch(udev_input_t *udev, udev_input_device_t *dev)
/* Simulated pointer */
/* Release the pointer only after all contacts are released */
if (touch->pointer_enabled)
touch->pointer_btn_pp = (touch->staging_active == 0);
touch->pointer_btn_pp = (touch->staging_active > 0);
/* Mouse section */
if (touch->mouse_enabled)
@@ -2603,8 +2620,8 @@ static void udev_handle_touch(void *data,
const struct input_event *event, udev_input_device_t *dev)
{
udev_input_t *udev = (udev_input_t*)data;
udev_input_mouse_t *mouse = &dev->mouse;
udev_input_touch_t *touch = &dev->touch;
udev_slot_state_t *st0 = touch->staging;
/* struct input_event */
/* { */
@@ -2657,16 +2674,18 @@ static void udev_handle_touch(void *data,
}
break;
case ABS_X:
/* TODO - Currently using single-touch events as touch with id 0 */
/* Single-touch contact, kept in slot 0 */
if (touch->mt_slots)
break;
RARCH_DDBG("[udev] handle_touch: [0] ST_X to %d\n", event->value);
VIDEO_POS_PUT_X(touch->staging[0].pos, event->value);
/* Low priority event, mark the change. */
if (touch->staging[0].change == UDEV_TOUCH_CHANGE_NONE)
touch->staging[0].change = UDEV_TOUCH_CHANGE_MOVE;
VIDEO_POS_PUT_X(st0->pos, event->value);
/* Starting tracing, remember touchdown position. */
else if (touch->staging[0].change == UDEV_TOUCH_CHANGE_DOWN)
VIDEO_POS_PUT_X(touch->staging[0].td_pos, event->value);
if (st0->change == UDEV_TOUCH_CHANGE_DOWN)
VIDEO_POS_PUT_X(st0->td_pos, event->value);
/* Low priority event, mark the change of a held contact. */
else if (st0->change == UDEV_TOUCH_CHANGE_NONE
&& st0->tracking_id >= 0)
st0->change = UDEV_TOUCH_CHANGE_MOVE;
break;
case ABS_MT_POSITION_X:
RARCH_DDBG("[udev] handle_touch: [%d] MT_X to %d\n", touch->current_slot, event->value);
@@ -2679,15 +2698,15 @@ static void udev_handle_touch(void *data,
VIDEO_POS_PUT_X(touch->staging[touch->current_slot].td_pos, event->value);
break;
case ABS_Y:
/* TODO - Currently using single-touch events as touch with id 0 */
if (touch->mt_slots)
break;
RARCH_DDBG("[udev] handle_touch: [0] ST_Y to %d\n", event->value);
VIDEO_POS_PUT_Y(touch->staging[0].pos, event->value);
/* Low priority event, mark the change. */
if (touch->staging[0].change == UDEV_TOUCH_CHANGE_NONE)
touch->staging[0].change = UDEV_TOUCH_CHANGE_MOVE;
/* Starting tracing, remember touchdown position. */
else if (touch->staging[0].change == UDEV_TOUCH_CHANGE_DOWN)
VIDEO_POS_PUT_Y(touch->staging[0].td_pos, event->value);
VIDEO_POS_PUT_Y(st0->pos, event->value);
if (st0->change == UDEV_TOUCH_CHANGE_DOWN)
VIDEO_POS_PUT_Y(st0->td_pos, event->value);
else if (st0->change == UDEV_TOUCH_CHANGE_NONE
&& st0->tracking_id >= 0)
st0->change = UDEV_TOUCH_CHANGE_MOVE;
break;
case ABS_MT_POSITION_Y:
RARCH_DDBG("[udev] handle_touch: [%d] MT_Y to %d\n", touch->current_slot, event->value);
@@ -2728,11 +2747,25 @@ static void udev_handle_touch(void *data,
case EV_KEY:
if (event->code == BTN_TOUCH)
{
RARCH_DDBG("[udev] handle_touch: [%d] TOUCH %d\n", touch->current_slot, event->value);
/* Single-touch contact, kept in slot 0 */
if (touch->mt_slots)
break;
RARCH_DDBG("[udev] handle_touch: [0] TOUCH %d\n", event->value);
if (event->value > 0)
touch->staging[touch->current_slot].change = UDEV_TOUCH_CHANGE_DOWN;
else
touch->staging[touch->current_slot].change = UDEV_TOUCH_CHANGE_UP;
{
if (st0->tracking_id < 0)
{
st0->tracking_id = 0;
st0->change = UDEV_TOUCH_CHANGE_DOWN;
st0->td_pos = st0->pos;
udev_touch_event_ts_copy(event, &st0->td_time);
}
}
else if (st0->tracking_id >= 0)
{
st0->tracking_id = UDEV_INPUT_TOUCH_TRACKING_ID_NONE;
st0->change = UDEV_TOUCH_CHANGE_UP;
}
break;
}
else
@@ -3036,8 +3069,7 @@ static int16_t udev_input_touch_state(
case RETRO_DEVICE_POINTER:
case RARCH_DEVICE_POINTER_SCREEN:
screen = (device == RARCH_DEVICE_MOUSE_SCREEN);
break;
screen = (device == RARCH_DEVICE_POINTER_SCREEN);
switch (id)
{
case RETRO_DEVICE_ID_POINTER_X:
@@ -3552,7 +3584,6 @@ static void udev_input_handle_hotplug(udev_input_t *udev)
const char *val_touchscreen = NULL;
const char *action = NULL;
const char *devnode = NULL;
int check = 0;
struct udev_device *dev = udev_monitor_receive_device(
udev->monitor);
+1 -5
View File
@@ -2349,7 +2349,6 @@ static const struct
char s_aa08c6d6[124];
#endif
#endif
char s_27aef25c[59];
#ifdef _3DS
char s_2873cef1[111];
char s_b4816da6[77];
@@ -9628,8 +9627,6 @@ static const struct
"\200\321\213\320\273\320\260\320\264\320\260\321\205",
#endif
#endif
"\320\237\320\276\321\210\321\203\320\272 \321\226 \320\277\320\260\320\264\320\273\321\203\321"
"\207\321\215\320\275\320\275\320\265 bluetooth-\320\277\321\200\321\213\320\273\320\260\320\264.",
#ifdef _3DS
"\320\232\320\260\321\202\320\260\320\273\320\276\320\263 \321\200\321\215\321\201\321\203\321"
"\200\321\201\320\260\321\236 \320\264\320\273\321\217 \320\275\321\226\320\266\320\275\321\217"
@@ -16515,7 +16512,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_be_blob_check[
(sizeof(msg_hash_be_blob) == (259250u
(sizeof(msg_hash_be_blob) == (259191u
#ifdef ANDROID
+ 531u
#endif
@@ -19203,7 +19200,6 @@ static const msg_hash_id_t msg_hash_be_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -2832,7 +2832,6 @@ static const struct
char s_aa08c6d6[89];
#endif
#endif
char s_27aef25c[46];
#ifdef _3DS
char s_2873cef1[107];
char s_b4816da6[57];
@@ -7956,7 +7955,6 @@ static const struct
"Desactiva el ERTM del senyal Bluetooth per corregir l'emparellament d'alguns dispositius",
#endif
#endif
"Escaneja dispositius bluetooth i connecta-hi.",
#ifdef _3DS
"Indica la carpeta de recursos de la pantalla inferior. La carpeta ha de tenir un fitxer \"bottom"
"_menu.png\".",
@@ -10912,7 +10910,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_ca_blob_check[
(sizeof(msg_hash_ca_blob) == (243481u
(sizeof(msg_hash_ca_blob) == (243435u
#ifdef ANDROID
+ 281u
#endif
@@ -14285,7 +14283,6 @@ static const msg_hash_id_t msg_hash_ca_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -5
View File
@@ -2900,7 +2900,6 @@ static const struct
char s_aa08c6d6[49];
#endif
#endif
char s_27aef25c[37];
#ifdef _3DS
char s_2873cef1[64];
char s_b4816da6[45];
@@ -9185,8 +9184,6 @@ static const struct
"\237\220\344\272\233\350\256\276\345\244\207\347\232\204\351\205\215\345\257\271",
#endif
#endif
"\346\211\253\346\217\217\350\223\235\347\211\231\350\256\276\345\244\207\345\271\266\350\277\236"
"\346\216\245\345\256\203\344\273\254\343\200\202",
#ifdef _3DS
"\345\272\225\351\203\250\345\261\217\345\271\225\350\265\204\344\272\247\347\233\256\345\275\225"
"\343\200\202\347\233\256\345\275\225\345\277\205\351\241\273\345\214\205\345\220\253 \"bow_menu."
@@ -14974,7 +14971,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_chs_blob_check[
(sizeof(msg_hash_chs_blob) == (182082u
(sizeof(msg_hash_chs_blob) == (182045u
#ifdef ANDROID
+ 220u
#endif
@@ -18441,7 +18438,6 @@ static const msg_hash_id_t msg_hash_chs_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -5
View File
@@ -2310,7 +2310,6 @@ static const struct
#ifdef HAVE_LAKKA
char s_2bab34b2[22];
#endif
char s_27aef25c[31];
#ifdef _3DS
char s_2873cef1[102];
char s_b4816da6[34];
@@ -6968,8 +6967,6 @@ static const struct
#ifdef HAVE_LAKKA
"\351\205\215\345\260\215\350\227\215\347\211\231\350\243\235\347\275\256\343\200\202",
#endif
"\346\216\203\346\217\217\350\227\215\347\211\231\350\243\235\347\275\256\344\270\246\351\200\243"
"\346\216\245\343\200\202",
#ifdef _3DS
"\350\250\255\345\256\232\344\270\213\350\236\242\345\271\225\347\232\204\351\201\270\345\226\256"
"\344\270\273\351\241\214\350\263\207\346\226\231\345\244\276\344\275\215\347\275\256, \350\263"
@@ -10477,7 +10474,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_cht_blob_check[
(sizeof(msg_hash_cht_blob) == (120267u
(sizeof(msg_hash_cht_blob) == (120236u
#ifdef ANDROID
+ 157u
#endif
@@ -13092,7 +13089,6 @@ static const msg_hash_id_t msg_hash_cht_ids[] =
#ifdef HAVE_LAKKA
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ENABLE,
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -2268,7 +2268,6 @@ static const struct
char s_aa08c6d6[78];
#endif
#endif
char s_27aef25c[49];
#ifdef _3DS
char s_2873cef1[84];
char s_b4816da6[50];
@@ -6405,7 +6404,6 @@ static const struct
"\275ch za\305\231\303\255zen\303\255",
#endif
#endif
"Vyhledejte za\305\231\303\255zen\303\255 Bluetooth a p\305\231ipojte je.",
#ifdef _3DS
"Adres\303\241\305\231 aktiv na spodn\303\255 obrazovce. Adres\303\241\305\231 mus\303\255 obsaho"
"vat \"spodn\303\255_menu.png\".",
@@ -8810,7 +8808,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_cs_blob_check[
(sizeof(msg_hash_cs_blob) == (161551u
(sizeof(msg_hash_cs_blob) == (161502u
#ifdef ANDROID
+ 390u
#endif
@@ -11429,7 +11427,6 @@ static const msg_hash_id_t msg_hash_cs_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -2828,7 +2828,6 @@ static const struct
char s_aa08c6d6[74];
#endif
#endif
char s_27aef25c[55];
#ifdef _3DS
char s_2873cef1[84];
char s_b4816da6[52];
@@ -8046,7 +8045,6 @@ static const struct
"Bluetooth ERTM deaktivieren, um das Koppeln einiger Ger\303\244te zu verbessern",
#endif
#endif
"Nach Bluetoothger\303\244ten suchen und mit ihnen verbinden.",
#ifdef _3DS
"Unterer Bildschirm Asset Verzeichnis. Verzeichnis muss \"bottom_menu.png\" enthalten.",
"Blaue Schriftfarbe auf unteren Bildschirm anpassen.",
@@ -11189,7 +11187,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_de_blob_check[
(sizeof(msg_hash_de_blob) == (255124u
(sizeof(msg_hash_de_blob) == (255069u
#ifdef ANDROID
+ 358u
#endif
@@ -14578,7 +14576,6 @@ static const msg_hash_id_t msg_hash_de_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -264,7 +264,6 @@ static const struct
char s_3c3598a9[39];
char s_a8d552a7[43];
char s_6112ec7c[38];
char s_27aef25c[45];
#ifdef _3DS
char s_b4816da6[39];
char s_450ca36f[40];
@@ -812,7 +811,6 @@ static const struct
"Change audio synchronisation settings.",
"Load customised configuration at start-up.",
"Load customised controls at start-up.",
"Scan for Bluetooth devices and connect them.",
#ifdef _3DS
"Adjust bottom screen font blue colour.",
"Adjust bottom screen font green colour.",
@@ -1141,7 +1139,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_en_blob_check[
(sizeof(msg_hash_en_blob) == (26222u
(sizeof(msg_hash_en_blob) == (26177u
#ifdef HAVE_LAKKA
+ 176u
#endif
@@ -1475,7 +1473,6 @@ static const msg_hash_id_t msg_hash_en_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_AUDIO_SYNCHRONIZATION_SETTINGS,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_AUTO_OVERRIDES_ENABLE,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_AUTO_REMAPS_ENABLE,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_GREEN,
+1 -4
View File
@@ -2539,7 +2539,6 @@ static const struct
char s_aa08c6d6[68];
#endif
#endif
char s_27aef25c[49];
#ifdef _3DS
char s_2873cef1[82];
char s_b4816da6[58];
@@ -6986,7 +6985,6 @@ static const struct
"Malebligi ERTM de Bluetooth por ripari kupladon de kelkaj aparatoj.",
#endif
#endif
"Ser\304\211i aparatojn kun bluetooth kaj konekti ilin.",
#ifdef _3DS
"Havaja dosierujo de malsupra ekrano. Dosierujo devas inkluzivi \"bottom_menu.png\".",
"Al\304\235ustigi la bluecon de la tiparo de la malsupra ekrano.",
@@ -9170,7 +9168,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_eo_blob_check[
(sizeof(msg_hash_eo_blob) == (170789u
(sizeof(msg_hash_eo_blob) == (170740u
#ifdef ANDROID
+ 312u
#endif
@@ -12139,7 +12137,6 @@ static const msg_hash_id_t msg_hash_eo_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -2939,7 +2939,6 @@ static const struct
char s_aa08c6d6[98];
#endif
#endif
char s_27aef25c[58];
#ifdef _3DS
char s_2873cef1[116];
char s_b4816da6[59];
@@ -8272,7 +8271,6 @@ static const struct
"itivos.",
#endif
#endif
"Busca dispositivos con Bluetooth para conectarse a ellos.",
#ifdef _3DS
"Indica el directorio de recursos de la pantalla inferior. El directorio debe contener el archivo"
" \"bottom_menu.png\".",
@@ -11520,7 +11518,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_es_blob_check[
(sizeof(msg_hash_es_blob) == (272662u
(sizeof(msg_hash_es_blob) == (272604u
#ifdef ANDROID
+ 329u
#endif
@@ -15013,7 +15011,6 @@ static const msg_hash_id_t msg_hash_es_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -2206,7 +2206,6 @@ static const struct
#ifdef HAVE_LAKKA
char s_2bab34b2[29];
#endif
char s_27aef25c[41];
#ifdef _3DS
char s_2873cef1[85];
char s_b4816da6[51];
@@ -5797,7 +5796,6 @@ static const struct
#ifdef HAVE_LAKKA
"M\303\244\303\244rit\303\244 bluetoothin tila.",
#endif
"Etsi bluetooth laitteita ja yhdist\303\244 ne.",
#ifdef _3DS
"Alemman n\303\244yt\303\266n resurssien kansio. Kansioon on sis\303\244llytett\303\244v\303\244 "
"\"bottom_menu.png\".",
@@ -7580,7 +7578,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_fi_blob_check[
(sizeof(msg_hash_fi_blob) == (123470u
(sizeof(msg_hash_fi_blob) == (123429u
#ifdef HAVE_LAKKA
+ 257u
#endif
@@ -10111,7 +10109,6 @@ static const msg_hash_id_t msg_hash_fi_ids[] =
#ifdef HAVE_LAKKA
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ENABLE,
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -2801,7 +2801,6 @@ static const struct
char s_aa08c6d6[77];
#endif
#endif
char s_27aef25c[59];
#ifdef _3DS
char s_2873cef1[86];
char s_b4816da6[58];
@@ -7941,7 +7940,6 @@ static const struct
"D\303\251sactiver l'ERTM Bluetooth pour corriger l'appairage de certains appareils",
#endif
#endif
"Rechercher des p\303\251riph\303\251riques Bluetooth et les connecter.",
#ifdef _3DS
"Dossier des ressources de l'\303\251cran du bas. Le dossier doit inclure \"bottom_menu.png\".",
"Ajuster la couleur bleue de la police de l'\303\251cran du bas.",
@@ -11027,7 +11025,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_fr_blob_check[
(sizeof(msg_hash_fr_blob) == (257607u
(sizeof(msg_hash_fr_blob) == (257548u
#ifdef ANDROID
+ 373u
#endif
@@ -14366,7 +14364,6 @@ static const msg_hash_id_t msg_hash_fr_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+39 -4
View File
@@ -2546,6 +2546,10 @@ static const struct
char s_bb929824[28];
char s_fe2ec9bd[35];
char s_1e3a9360[40];
char s_5943aeda[30];
char s_60b84450[31];
char s_782f95e3[31];
char s_5081d7d8[37];
char s_5741c1d0[23];
char s_a1a7717d[24];
char s_e6ca3876[27];
@@ -2975,7 +2979,6 @@ static const struct
char s_aa08c6d6[73];
#endif
#endif
char s_27aef25c[61];
#ifdef _3DS
char s_2873cef1[90];
char s_b4816da6[48];
@@ -4221,6 +4224,10 @@ static const struct
char s_72e21512[263];
char s_9f6de46b[305];
char s_26e66ece[276];
char s_5f131848[172];
char s_23227a3e[167];
char s_87e08991[167];
char s_56660806[173];
char s_66f2b57e[54];
char s_a776daeb[87];
char s_88095324[87];
@@ -4283,6 +4290,7 @@ static const struct
char s_dc6f9d69[50];
char s_417085f4[57];
char s_1bcf70a7[185];
char s_fbed99aa[217];
char s_2e2ca41a[312];
char s_f48b1fac[55];
char s_f4ad1b37[100];
@@ -7967,6 +7975,10 @@ static const struct
"Leagan Amach Fo-phicteil\303\255n",
"\303\232s\303\241id an Buaicphointe Taispe\303\241na",
"D\303\255ch\303\263d\303\272 f\303\255se\303\241in tr\303\255 chrua-earra\303\255",
"Imeall an Sc\303\241ile\303\241in Bun (%)",
"Imill Chl\303\251 an Sc\303\241ile\303\241in (%)",
"Imill Sc\303\241ile\303\241in ar Dheis (%)",
"Imill an Sc\303\241ile\303\241in ag an mBarr (%)",
"Leagan Amach F\303\255se\303\241in",
"Uasmh\303\251id Moille Fr\303\241ma",
"Uasta \303\215omh\303\241nna Swapchain",
@@ -8556,7 +8568,6 @@ static const struct
"\303\272",
#endif
#endif
"Scan\303\241il le haghaidh gl\303\251asanna bluetooth agus ceangail iad.",
#ifdef _3DS
"Eolaire s\303\263cmhainn\303\255 bun an sc\303\241ile\303\241in. N\303\255 m\303\263r \"bottom_m"
"enu.png\" a bheith san eolaire.",
@@ -11099,6 +11110,18 @@ static const struct
" Vulkan), rud a fh\303\241gann an CPU saor; m\303\241 roghna\303\255tear 'Off', d\303\251antar i"
"ad a dh\303\255ch\303\263d\303\272 ar an CPU. Baineann s\303\251 seo le r\303\251amhamhairc a os"
"cla\303\255tear feasta.",
"F\303\241g sp\303\241s ag imeall \303\255ochtair an chluiche agus an roghchl\303\241ir, arna shl"
"oinneadh mar ch\303\251atad\303\241n den fhuinneog ar iOS. M\303\241 shocra\303\255tear \303\251"
" ar 0, n\303\255 bheidh aon imeall ann.",
"F\303\241g sp\303\241s ag imeall cl\303\251 an chluiche agus an roghchl\303\241ir, arna shloinne"
"adh mar ch\303\251atad\303\241n den fhuinneog ar iOS. M\303\241 shocra\303\255tear \303\251 ar 0"
", n\303\255 bheidh aon imeall ann.",
"F\303\241g sp\303\241s ag imeall deas an chluiche agus an roghchl\303\241ir, arna shloinneadh ma"
"r ch\303\251atad\303\241n den fhuinneog ar iOS. M\303\241 shocra\303\255tear \303\251 ar 0, n"
"\303\255 bheidh aon imeall ann.",
"F\303\241g sp\303\241s ag imeall uachtarach an chluiche agus an roghchl\303\241ir, arna shloinne"
"adh mar ch\303\251atad\303\241n den fhuinneog ar iOS. M\303\241 shocra\303\255tear \303\251 ar 0"
", n\303\255 bheidh aon imeall ann.",
"St\303\263r\303\241iltear Leagan Amach F\303\255se\303\241in san eolaire seo.",
"Ins\303\255onn s\303\251 don tiom\303\241na\303\255 f\303\255se modh maol\303\241naithe sonraith"
"e a \303\272s\303\241id go sainr\303\241ite.",
@@ -11297,6 +11320,10 @@ static const struct
"Coinn\303\255onn 'Ceangailte' an sc\303\241ile\303\241n seo in \303\251ineacht leis na cinn eile"
". Cuireann 'Ar leithligh' \303\251 ar fhuinneog n\303\263 ar thaispe\303\241int d\303\241 chuid "
"f\303\251in. F\303\241gann 'Faoi cheilt' ar l\303\241r \303\251.",
"C\303\241 dt\303\251ann an sc\303\241ile\303\241n seo nuair a bh\303\255onn a fhuinneog f\303"
"\251in aige? Roghna\303\255tear an dara taispe\303\241int go huathoibr\303\255och. Fanann an sc"
"\303\241ile\303\241n ceangailte leis an taispe\303\241int a bhfuil RetroArch \303\241 thaispe"
"\303\241int uirthi.",
"D\303\272n an roghchl\303\241r agus bog sc\303\241ile\303\241in agus roghchl\303\241r na gcluas"
"\303\241n leis na rialt\303\263ir\303\255: greama\303\255onn an greim, claonann an truicear, ath"
"ra\303\255onn an bata achar agus m\303\251id, casann agus claonann bata na l\303\241imhe saora, "
@@ -12404,7 +12431,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_ga_blob_check[
(sizeof(msg_hash_ga_blob) == (273097u
(sizeof(msg_hash_ga_blob) == (274061u
#ifdef ANDROID
+ 390u
#endif
@@ -15505,6 +15532,10 @@ static const msg_hash_id_t msg_hash_ga_ids[] =
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SUBPIXEL_LAYOUT,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_HDR_USE_DISPLAY_PEAK,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_HW_DECODE,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_IOS_MARGIN_BOTTOM,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_IOS_MARGIN_LEFT,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_IOS_MARGIN_RIGHT,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_IOS_MARGIN_TOP,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_LAYOUT_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_MAX_FRAME_LATENCY,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_MAX_SWAPCHAIN_IMAGES,
@@ -15926,7 +15957,6 @@ static const msg_hash_id_t msg_hash_ga_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
@@ -17162,6 +17192,10 @@ static const msg_hash_id_t msg_hash_ga_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_HDR_SUBPIXEL_LAYOUT,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_HDR_USE_DISPLAY_PEAK,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_HW_DECODE,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_IOS_MARGIN_BOTTOM,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_IOS_MARGIN_LEFT,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_IOS_MARGIN_RIGHT,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_IOS_MARGIN_TOP,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_LAYOUT_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_MAX_FRAME_LATENCY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_MAX_SWAPCHAIN_IMAGES,
@@ -17224,6 +17258,7 @@ static const msg_hash_id_t msg_hash_ga_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_LAYOUT,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_SELECTED,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_WINDOW_MODE,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_WINDOW_MONITOR,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_XR_EDIT,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_XR_PITCH,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_XR_RESET,
+1 -4
View File
@@ -2615,7 +2615,6 @@ static const struct
char s_aa08c6d6[78];
#endif
#endif
char s_27aef25c[43];
#ifdef _3DS
char s_2873cef1[90];
char s_450ca36f[50];
@@ -7315,7 +7314,6 @@ static const struct
"Desactiva o Bluetooth ERTM para corrixir a vinculaci\303\263n dalg\303\272ns dispositivos",
#endif
#endif
"Busca dispositivos bluetooth e con\303\251ctaos.",
#ifdef _3DS
"Directorio de activos da pantalla inferior. O directorio debe inclu\303\255r \"bottom_menu.png\""
".",
@@ -9794,7 +9792,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_gl_blob_check[
(sizeof(msg_hash_gl_blob) == (210917u
(sizeof(msg_hash_gl_blob) == (210874u
#ifdef ANDROID
+ 365u
#endif
@@ -12849,7 +12847,6 @@ static const msg_hash_id_t msg_hash_gl_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_GREEN,
+1 -4
View File
@@ -2455,7 +2455,6 @@ static const struct
char s_aa08c6d6[83];
#endif
#endif
char s_27aef25c[53];
#ifdef _3DS
char s_2873cef1[104];
char s_b4816da6[53];
@@ -7023,7 +7022,6 @@ static const struct
"\241ros\303\255t\303\241s\303\241n\303\241l",
#endif
#endif
"Bluetooth eszk\303\266z\303\266k keres\303\251se \303\251s csatlakoztat\303\241sa.",
#ifdef _3DS
"Az als\303\263 k\303\251perny\305\221 be\303\251p\303\255tett m\303\251diaf\303\241jljainak k"
"\303\266nyvt\303\241ra. A \"bottom_menu.png\" meg kell legyen itt.",
@@ -9816,7 +9814,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_hu_blob_check[
(sizeof(msg_hash_hu_blob) == (187189u
(sizeof(msg_hash_hu_blob) == (187136u
#ifdef ANDROID
+ 316u
#endif
@@ -12699,7 +12697,6 @@ static const msg_hash_id_t msg_hash_hu_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -2429,7 +2429,6 @@ static const struct
char s_aa08c6d6[81];
#endif
#endif
char s_27aef25c[52];
#ifdef _3DS
char s_2873cef1[88];
char s_b4816da6[55];
@@ -6713,7 +6712,6 @@ static const struct
"Disabilita l'ERTM Bluetooth per correggere l'accoppiamento di alcuni dispositivi",
#endif
#endif
"Scansione per i dispositivi bluetooth e collegarli.",
#ifdef _3DS
"Directory asset nello schermo inferiore. La directory deve includere \"bottom_menu.png\".",
"Regola il colore blu del font dello schermo inferiore.",
@@ -8840,7 +8838,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_it_blob_check[
(sizeof(msg_hash_it_blob) == (182312u
(sizeof(msg_hash_it_blob) == (182260u
#ifdef ANDROID
+ 376u
#endif
@@ -11700,7 +11698,6 @@ static const msg_hash_id_t msg_hash_it_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -6
View File
@@ -2250,7 +2250,6 @@ static const struct
char s_aa08c6d6[82];
#endif
#endif
char s_27aef25c[62];
#ifdef _3DS
char s_2873cef1[133];
char s_b4816da6[55];
@@ -8157,9 +8156,6 @@ static const struct
"\263\343\202\260\343\202\222\344\277\256\346\255\243\343\201\227\343\201\276\343\201\231",
#endif
#endif
"Bluetooth \343\203\207\343\203\220\343\202\244\343\202\271\343\202\222\343\202\271\343\202\255"
"\343\203\243\343\203\263\343\201\227\343\201\246\346\216\245\347\266\232\343\201\227\343\201\276"
"\343\201\231\343\200\202",
#ifdef _3DS
"\344\270\213\347\224\273\351\235\242\343\202\242\343\202\273\343\203\203\343\203\210\343\201\256"
"\343\203\207\343\202\243\343\203\254\343\202\257\343\203\210\343\203\252\343\201\247\343\201\231"
@@ -13932,7 +13928,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_ja_blob_check[
(sizeof(msg_hash_ja_blob) == (199282u
(sizeof(msg_hash_ja_blob) == (199220u
#ifdef ANDROID
+ 484u
#endif
@@ -16508,7 +16504,6 @@ static const msg_hash_id_t msg_hash_ja_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -6
View File
@@ -2808,7 +2808,6 @@ static const struct
char s_aa08c6d6[121];
#endif
#endif
char s_27aef25c[53];
#ifdef _3DS
char s_2873cef1[122];
char s_b4816da6[54];
@@ -9660,9 +9659,6 @@ static const struct
"\210\230 \354\236\210\354\212\265\353\213\210\353\213\244",
#endif
#endif
"\353\270\224\353\243\250\355\210\254\354\212\244 \354\236\245\353\271\204\353\245\274 \352\262"
"\200\354\203\211\355\225\230\352\263\240 \354\227\260\352\262\260\355\225\251\353\213\210\353"
"\213\244.",
#ifdef _3DS
"\354\225\204\353\236\230 \355\231\224\353\251\264 \354\225\240\354\205\213 \353\224\224\353\240"
"\211\355\206\240\353\246\254\354\236\205\353\213\210\353\213\244. \353\224\224\353\240\211\355"
@@ -16223,7 +16219,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_ko_blob_check[
(sizeof(msg_hash_ko_blob) == (228977u
(sizeof(msg_hash_ko_blob) == (228924u
#ifdef ANDROID
+ 436u
#endif
@@ -19492,7 +19488,6 @@ static const msg_hash_id_t msg_hash_ko_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -2381,7 +2381,6 @@ static const struct
char s_aa08c6d6[78];
#endif
#endif
char s_27aef25c[45];
#ifdef _3DS
char s_2873cef1[76];
char s_b4816da6[64];
@@ -6581,7 +6580,6 @@ static const struct
"Schakel Bluetooth ERTM uit om de koppeling van sommige apparaten te repareren",
#endif
#endif
"Scan naar bluetooth apparaten en verbind ze.",
#ifdef _3DS
"De map met assets voor het onderste scherm moet \"bottom_menu.png\" bevatten.",
"Pas de blauwe kleur van het lettertype onderaan het scherm aan.",
@@ -8652,7 +8650,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_nl_blob_check[
(sizeof(msg_hash_nl_blob) == (172428u
(sizeof(msg_hash_nl_blob) == (172383u
#ifdef ANDROID
+ 375u
#endif
@@ -11466,7 +11464,6 @@ static const msg_hash_id_t msg_hash_nl_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -924,7 +924,6 @@ static const struct
char s_8a24406f[28];
char s_5977d6d8[44];
char s_f6adf4ee[39];
char s_27aef25c[53];
#ifdef _3DS
char s_f441f728[37];
#endif
@@ -2208,7 +2207,6 @@ static const struct
"Demp all lyd i stillemodus.",
"Endre innstillinger for lyd inngang/utgang.",
"Vis n\303\245v\303\246rende batteriniv\303\245 i menyen.",
"Skann etter bluetooth-enheter og opprett tilkobling.",
#ifdef _3DS
"Juster skriftskalaen for bunnskjerm.",
#endif
@@ -2631,7 +2629,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_no_blob_check[
(sizeof(msg_hash_no_blob) == (29330u
(sizeof(msg_hash_no_blob) == (29277u
#ifdef HAVE_LAKKA
+ 178u
#endif
@@ -3685,7 +3683,6 @@ static const msg_hash_id_t msg_hash_no_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_AUDIO_RESPECT_SILENT_MODE,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_AUDIO_SETTINGS,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BATTERY_LEVEL_ENABLE,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_SCALE,
#endif
+1 -4
View File
@@ -2695,7 +2695,6 @@ static const struct
char s_aa08c6d6[71];
#endif
#endif
char s_27aef25c[59];
#ifdef _3DS
char s_2873cef1[75];
char s_b4816da6[53];
@@ -7522,7 +7521,6 @@ static const struct
"ze\305\204",
#endif
#endif
"Skanuj w poszukiwaniu urz\304\205dze\305\204 bluetooth i pod\305\202\304\205cz je.",
#ifdef _3DS
"Katalog zasob\303\263w dolnego ekranu. Katalog musi zawiera\304\207 \"bottom_menu.png\".",
"Dostosuj niebieski kolor czcionki na dolnym ekranie.",
@@ -10225,7 +10223,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_pl_blob_check[
(sizeof(msg_hash_pl_blob) == (199715u
(sizeof(msg_hash_pl_blob) == (199656u
#ifdef ANDROID
+ 385u
#endif
@@ -13418,7 +13416,6 @@ static const msg_hash_id_t msg_hash_pl_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -2566,7 +2566,6 @@ static const struct
char s_aa08c6d6[79];
#endif
#endif
char s_27aef25c[49];
#ifdef _3DS
char s_2873cef1[89];
char s_b4816da6[45];
@@ -7121,7 +7120,6 @@ static const struct
"Desativa o ERTM do Bluetooth para corrigir o pareamento de alguns dispositivos",
#endif
#endif
"Procura por dispositivos bluetooth e os conecta.",
#ifdef _3DS
"Diret\303\263rio dos recursos da tela inferior. O diret\303\263rio precisa ter um \"bottom_menu."
"png\".",
@@ -9528,7 +9526,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_pt_br_blob_check[
(sizeof(msg_hash_pt_br_blob) == (193642u
(sizeof(msg_hash_pt_br_blob) == (193593u
#ifdef ANDROID
+ 352u
#endif
@@ -12552,7 +12550,6 @@ static const msg_hash_id_t msg_hash_pt_br_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -6
View File
@@ -2576,7 +2576,6 @@ static const struct
char s_aa08c6d6[133];
#endif
#endif
char s_27aef25c[67];
#ifdef _3DS
char s_2873cef1[119];
char s_b4816da6[83];
@@ -10865,9 +10864,6 @@ static const struct
"\203\321\201\321\202\321\200\320\276\320\271\321\201\321\202\320\262\320\260\321\205.",
#endif
#endif
"\320\237\320\276\320\270\321\201\320\272 \320\270 \320\277\320\276\320\264\320\272\320\273\321"
"\216\321\207\320\265\320\275\320\270\320\265 bluetooth-\321\203\321\201\321\202\321\200\320\276"
"\320\271\321\201\321\202\320\262.",
#ifdef _3DS
"\320\232\320\260\321\202\320\260\320\273\320\276\320\263 \321\200\320\265\321\201\321\203\321"
"\200\321\201\320\276\320\262 \320\264\320\273\321\217 \320\275\320\270\320\266\320\275\320\265"
@@ -19291,7 +19287,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_ru_blob_check[
(sizeof(msg_hash_ru_blob) == (308352u
(sizeof(msg_hash_ru_blob) == (308285u
#ifdef ANDROID
+ 547u
#endif
@@ -22293,7 +22289,6 @@ static const msg_hash_id_t msg_hash_ru_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+492 -4
View File
File diff suppressed because it is too large. Load diff
+39 -4
View File
@@ -2423,6 +2423,10 @@ static const struct
char s_bb929824[16];
char s_fe2ec9bd[33];
char s_1e3a9360[29];
char s_5943aeda[32];
char s_60b84450[35];
char s_782f95e3[33];
char s_5081d7d8[32];
char s_5741c1d0[14];
char s_a1a7717d[19];
char s_e6ca3876[32];
@@ -2828,7 +2832,6 @@ static const struct
char s_aa08c6d6[69];
#endif
#endif
char s_27aef25c[45];
#ifdef _3DS
char s_2873cef1[88];
char s_b4816da6[62];
@@ -4067,6 +4070,10 @@ static const struct
char s_72e21512[180];
char s_9f6de46b[288];
char s_26e66ece[211];
char s_5f131848[122];
char s_23227a3e[125];
char s_87e08991[123];
char s_56660806[122];
char s_66f2b57e[38];
char s_a776daeb[88];
char s_88095324[88];
@@ -4129,6 +4136,7 @@ static const struct
char s_dc6f9d69[55];
char s_417085f4[44];
char s_1bcf70a7[142];
char s_fbed99aa[170];
char s_2e2ca41a[296];
char s_f48b1fac[55];
char s_f4ad1b37[84];
@@ -4500,6 +4508,7 @@ static const struct
char s_e9eb2822[45];
char s_ed159cd6[16];
char s_fa458d88[36];
char s_f2f8ca3f[212];
char s_7ccec78e[41];
char s_308c47e4[40];
char s_525bf430[20];
@@ -7562,6 +7571,10 @@ static const struct
"Subpixel-layout",
"Anv\303\244nd bildsk\303\244rmens toppv\303\244rde",
"Maskinvaruavkodning av video",
"Undermarginal f\303\266r sk\303\244rmen (%)",
"V\303\244nstermarginal f\303\266r sk\303\244rmen (%)",
"H\303\266germarginal f\303\266r sk\303\244rmen (%)",
"\303\226vermarginal f\303\266r sk\303\244rmen (%)",
"Videolayouter",
"Max bildrutelatens",
"Maximalt antal swapchain-bilder",
@@ -8094,7 +8107,6 @@ static const struct
"Inaktivera Bluetooth ERTM f\303\266r att fixa parkoppling av vissa enheter",
#endif
#endif
"S\303\266k efter bluetooth-enheter och anslut dem.",
#ifdef _3DS
"Nedre sk\303\244rmens katalog f\303\266r tillg\303\245ngar. Katalogen m\303\245ste inkludera \"b"
"ottom_menu.png\".",
@@ -10223,6 +10235,14 @@ static const struct
"Avkoda videof\303\266rhandsvisningar p\303\245 GPU:n n\303\244r grafikdrivrutinen kan g\303\266r"
"a det (H.264 med Vulkan), s\303\245 att CPU:n avlastas. \342\200\235Av\342\200\235 avkodar dem p"
"\303\245 CPU:n. G\303\244ller f\303\266rhandsvisningar som \303\266ppnas fr\303\245n och med nu.",
"L\303\244mna utrymme vid nederkanten av spelet och menyn, i procent av f\303\266nstret, p\303"
"\245 iOS. 0 l\303\244gger inte till n\303\245gon marginal.",
"L\303\244mna utrymme vid v\303\244nsterkanten av spelet och menyn, i procent av f\303\266nstret,"
" p\303\245 iOS. 0 l\303\244gger inte till n\303\245gon marginal.",
"L\303\244mna utrymme vid h\303\266gerkanten av spelet och menyn, i procent av f\303\266nstret, p"
"\303\245 iOS. 0 l\303\244gger inte till n\303\245gon marginal.",
"L\303\244mna utrymme vid \303\266verkanten av spelet och menyn, i procent av f\303\266nstret, p"
"\303\245 iOS. 0 l\303\244gger inte till n\303\245gon marginal.",
"Videolayouter lagras i denna katalog.",
"S\303\244ger till videodrivrutinen att uttryckligen anv\303\244nda ett specificerat buffringsl"
"\303\244ge.",
@@ -10370,6 +10390,9 @@ static const struct
"V\303\244lj den flytande sk\303\244rm som ska justeras.",
"Kopplad beh\303\245ller sk\303\244rmen tillsammans med de andra. Separat visar den i ett eget f"
"\303\266nster eller p\303\245 en egen bildsk\303\244rm. Dold utel\303\244mnar den.",
"Var den h\303\244r sk\303\244rmen visas n\303\244r den har ett eget f\303\266nster. Auto v\303"
"\244ljer en annan bildsk\303\244rm. P\303\245 den bildsk\303\244rm d\303\244r RetroArch k\303"
"\266rs beh\303\245lls sk\303\244rmen i samma f\303\266nster.",
"St\303\244ng menyn och flytta VR-headsetets sk\303\244rmar och meny med handkontrollerna: greppk"
"nappen tar tag, avtryckaren vinklar, styrspaken \303\244ndrar avst\303\245nd och storlek, den fr"
"ia handens styrspak vrider och vinklar, huvudknappen \303\245terst\303\244ller och V\303\244lj n"
@@ -10910,6 +10933,9 @@ static const struct
"Filen finns redan. Sparar till reservbuffert",
"Filen hittas ej",
"En eller flera firmwarefiler saknas",
"Vissa av RetroArchs nedladdningsbara filer (menyresurser, handkontrollprofiler, k\303\244rninfor"
"mation, databaser) saknas.H\303\244mta dem nu? Det kr\303\244ver en internetanslutning.De kan up"
"pdateras senare med N\303\244tuppdateraren.",
"Mappspecifika k\303\244rnalternativ hittades i",
"Hittade automatiskt sparat tillst\303\245nd i",
"Hittade skivetikett",
@@ -11357,7 +11383,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_sv_blob_check[
(sizeof(msg_hash_sv_blob) == (234595u
(sizeof(msg_hash_sv_blob) == (235556u
#ifdef ANDROID
+ 361u
#endif
@@ -14344,6 +14370,10 @@ static const msg_hash_id_t msg_hash_sv_ids[] =
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_HDR_SUBPIXEL_LAYOUT,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_HDR_USE_DISPLAY_PEAK,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_HW_DECODE,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_IOS_MARGIN_BOTTOM,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_IOS_MARGIN_LEFT,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_IOS_MARGIN_RIGHT,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_IOS_MARGIN_TOP,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_LAYOUT_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_MAX_FRAME_LATENCY,
(msg_hash_id_t)MENU_ENUM_LABEL_VALUE_VIDEO_MAX_SWAPCHAIN_IMAGES,
@@ -14742,7 +14772,6 @@ static const msg_hash_id_t msg_hash_sv_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
@@ -15979,6 +16008,10 @@ static const msg_hash_id_t msg_hash_sv_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_HDR_SUBPIXEL_LAYOUT,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_HDR_USE_DISPLAY_PEAK,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_HW_DECODE,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_IOS_MARGIN_BOTTOM,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_IOS_MARGIN_LEFT,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_IOS_MARGIN_RIGHT,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_IOS_MARGIN_TOP,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_LAYOUT_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_MAX_FRAME_LATENCY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_MAX_SWAPCHAIN_IMAGES,
@@ -16041,6 +16074,7 @@ static const msg_hash_id_t msg_hash_sv_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_LAYOUT,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_SELECTED,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_WINDOW_MODE,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_WINDOW_MONITOR,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_XR_EDIT,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_XR_PITCH,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_VIDEO_SCREEN_XR_RESET,
@@ -16407,6 +16441,7 @@ static const msg_hash_id_t msg_hash_sv_ids[] =
(msg_hash_id_t)MSG_FILE_ALREADY_EXISTS_SAVING_TO_BACKUP_BUFFER,
(msg_hash_id_t)MSG_FILE_NOT_FOUND,
(msg_hash_id_t)MSG_FIRMWARE,
(msg_hash_id_t)MSG_FIRST_RUN_DOWNLOAD_PROMPT,
(msg_hash_id_t)MSG_FOLDER_SPECIFIC_CORE_OPTIONS_FOUND_AT,
(msg_hash_id_t)MSG_FOUND_AUTO_SAVESTATE_IN,
(msg_hash_id_t)MSG_FOUND_DISK_LABEL,
+1 -7
View File
@@ -2605,7 +2605,6 @@ static const struct
char s_aa08c6d6[164];
#endif
#endif
char s_27aef25c[94];
#ifdef _3DS
char s_2873cef1[209];
char s_b4816da6[124];
@@ -13115,10 +13114,6 @@ static const struct
"\243\340\270\223\340\271\214",
#endif
#endif
"\340\270\252\340\271\201\340\270\201\340\270\231\340\270\253\340\270\262\340\270\255\340\270\270"
"\340\270\233\340\270\201\340\270\243\340\270\223\340\271\214\340\270\232\340\270\245\340\270\271"
"\340\270\227\340\270\271\340\270\230\340\271\201\340\270\245\340\270\260\340\271\200\340\270\212"
"\340\270\267\340\271\210\340\270\255\340\270\241\340\270\225\340\271\210\340\270\255",
#ifdef _3DS
"\340\271\204\340\270\224\340\271\200\340\270\243\340\270\201\340\270\227\340\270\255\340\270\243"
"\340\270\265\340\270\227\340\270\243\340\270\261\340\270\236\340\270\242\340\270\262\340\270\201"
@@ -26516,7 +26511,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_th_blob_check[
(sizeof(msg_hash_th_blob) == (453709u
(sizeof(msg_hash_th_blob) == (453615u
#ifdef ANDROID
+ 757u
#endif
@@ -29616,7 +29611,6 @@ static const msg_hash_id_t msg_hash_th_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -4
View File
@@ -2369,7 +2369,6 @@ static const struct
char s_aa08c6d6[76];
#endif
#endif
char s_27aef25c[56];
#ifdef _3DS
char s_2873cef1[64];
char s_b4816da6[46];
@@ -6690,7 +6689,6 @@ static const struct
"\237\304\261 b\304\261rak\304\261n",
#endif
#endif
"Bluetooth cihazlar\304\261n\304\261 tarar ve ba\304\237lant\304\261 olu\305\237turur.",
#ifdef _3DS
"Alt ekran i\303\247erik dizini. Dizin 'bottom_menu.png' i\303\247ermelidir.",
"Alt ekran yaz\304\261 tipi mavi rengini ayarlay\304\261n.",
@@ -9195,7 +9193,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_tr_blob_check[
(sizeof(msg_hash_tr_blob) == (169322u
(sizeof(msg_hash_tr_blob) == (169266u
#ifdef ANDROID
+ 340u
#endif
@@ -11924,7 +11922,6 @@ static const msg_hash_id_t msg_hash_tr_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -7
View File
@@ -2755,7 +2755,6 @@ static const struct
char s_aa08c6d6[103];
#endif
#endif
char s_27aef25c[86];
#ifdef _3DS
char s_2873cef1[127];
char s_b4816da6[79];
@@ -11993,10 +11992,6 @@ static const struct
"\321\200\320\270\321\201\321\202\321\200\320\276\321\227\320\262",
#endif
#endif
"\320\241\320\272\320\260\320\275\321\203\320\262\320\260\321\202\320\270 \320\264\320\273\321"
"\217 Bluetooth \320\277\321\200\320\270\321\201\321\202\321\200\320\276\321\227\320\262 \321\202"
"\320\260 \320\277\321\226\320\264'\321\224\320\264\320\275\320\260\321\202\320\270 \321\227\321"
"\205.",
#ifdef _3DS
"\320\235\320\270\320\266\320\275\321\226\320\271 \320\265\320\272\321\200\320\260\320\275\320"
"\275\320\270\320\271 \320\272\320\260\321\202\320\260\320\273\320\276\320\263 \320\260\320\272"
@@ -21397,7 +21392,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_uk_blob_check[
(sizeof(msg_hash_uk_blob) == (349435u
(sizeof(msg_hash_uk_blob) == (349349u
#ifdef ANDROID
+ 610u
#endif
@@ -24567,7 +24562,6 @@ static const msg_hash_id_t msg_hash_uk_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+1 -5
View File
@@ -3007,7 +3007,6 @@ static const struct
char s_aa08c6d6[90];
#endif
#endif
char s_27aef25c[51];
#ifdef _3DS
char s_2873cef1[91];
char s_b4816da6[73];
@@ -9570,8 +9569,6 @@ static const struct
" b\341\273\213",
#endif
#endif
"Qu\303\251t c\303\241c thi\341\272\277t b\341\273\213 Bluetooth v\303\240 k\341\272\277t n\341"
"\273\221i.",
#ifdef _3DS
"Th\306\260 m\341\273\245c t\303\240i nguy\303\252n m\303\240n h\303\254nh d\306\260\341\273\233i"
". Th\306\260 m\341\273\245c ph\341\272\243i bao g\341\273\223m \"bottom_menu.png\".",
@@ -15672,7 +15669,7 @@ static const struct
* compiler that pads this struct fails here instead of
* misindexing at runtime. */
typedef char msg_hash_vn_blob_check[
(sizeof(msg_hash_vn_blob) == (312852u
(sizeof(msg_hash_vn_blob) == (312801u
#ifdef ANDROID
+ 373u
#endif
@@ -19228,7 +19225,6 @@ static const msg_hash_id_t msg_hash_vn_ids[] =
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_ERTM_DISABLE,
#endif
#endif
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BLUETOOTH_SETTINGS,
#ifdef _3DS
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_ASSETS_DIRECTORY,
(msg_hash_id_t)MENU_ENUM_SUBLABEL_BOTTOM_FONT_COLOR_BLUE,
+2 -2
View File
@@ -127,7 +127,7 @@
#define LANGUAGE_PROGRESS_RUSSIAN_APPROVED 5
/* Slovak */
#define LANGUAGE_PROGRESS_SLOVAK_TRANSLATED 95
#define LANGUAGE_PROGRESS_SLOVAK_TRANSLATED 99
#define LANGUAGE_PROGRESS_SLOVAK_APPROVED 0
/* Serbian (Latin) */
@@ -159,7 +159,7 @@
#define LANGUAGE_PROGRESS_VALENCIAN_APPROVED 0
/* Vietnamese */
#define LANGUAGE_PROGRESS_VIETNAMESE_TRANSLATED 100
#define LANGUAGE_PROGRESS_VIETNAMESE_TRANSLATED 99
#define LANGUAGE_PROGRESS_VIETNAMESE_APPROVED 0
/* Chinese Simplified */
+10 -107
View File
@@ -387,47 +387,11 @@ static INLINE float audio_mixer_bits_float(int i)
#ifdef AUDIO_MIXER_HAS_STREAM
/* ---- folding a multichannel stream to the stereo a voice mixes ------
*
* Vorbis and Opus both decode more than two channels now, and so do
* WAV and FLAC - but they do not agree on what order the channels
* arrive in, so there are two tables and the caller says which.
*
* Vorbis's order (spec 4.3.9), which Opus mapping family 1 adopts:
*
* 3 L C R 4 L R RL RR 5 L C R RL RR
* 6 L C R RL RR LFE 7 L C R RL RR BC LFE
* 8 L C R RL RR SL SR LFE
*
* Microsoft's, which WAV carries and FLAC's spec matches:
*
* 3 FL FR FC 4 FL FR BL BR 5 FL FR FC BL BR
* 6 FL FR FC LFE BL BR 7 FL FR FC LFE BC SL SR
* 8 FL FR FC LFE BL BR SL SR
*
* Folding one with the other's table is not a subtle error: on 5.1 it
* sends front-right to both sides, front-centre to the right alone,
* LFE into the mix and back-right nowhere.
*
* A stream voice mixes stereo, so anything wider is folded here - the
* arms deliberately do no channel conversion, leaving it to the
* mixer. Coefficients are the usual ITU-R BS.775 ones: centre and
* surrounds enter both sides at -3 dB, a lone back centre at -6 dB
* into each, and LFE is dropped, which is what it is for.
*
* Not normalised. Scaling by the coefficient sum would put 5.1
* content 7.7 dB below the same material in a stereo file, which is a
* surprising thing for a file's channel count to do to its volume;
* the sum only reaches full scale when the channels are correlated,
* and the mixer already clamps at its s16 and float boundaries. The
* trade is level for the possibility of clipping on correlated
* material, and it is the trade most players make.
*
* Q15 in int32 - unity is 32768, which does not fit the int16 the
* coefficients would otherwise want, and wrapping it would invert a
* channel rather than pass it. The s16 fold stays clear of float. */
#define AMIX_DM_UNITY 32768
#define AMIX_DM_M3DB 23170
#define AMIX_DM_M6DB 16384
* A voice mixes stereo, so anything wider is folded here - the arms
* deliberately do no channel conversion. The fold for each arm's
* channel order is audio_transfer's (audio_transfer_fold_q15): Vorbis
* and Opus in Vorbis order, the rest in WAVE order, WMA by its speaker
* mask. Q15 in int32; the s16 fold stays clear of float. */
static int16_t audio_mixer_sat_s16_64(int64_t v)
{
if (v > 32767)
@@ -437,59 +401,6 @@ static int16_t audio_mixer_sat_s16_64(int64_t v)
return (int16_t)v;
}
/* [channels][input channel][0 = left, 1 = right] */
static const int32_t audio_mixer_downmix_vorbis_q15[9][8][2] = {
{ {0,0} }, /* unused */
{ {0,0} }, /* mono */
{ {0,0} }, /* stereo */
/* 3: L C R */
{ {AMIX_DM_UNITY,0}, {AMIX_DM_M3DB,AMIX_DM_M3DB}, {0,AMIX_DM_UNITY} },
/* 4: L R RL RR */
{ {AMIX_DM_UNITY,0}, {0,AMIX_DM_UNITY},
{AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB} },
/* 5: L C R RL RR */
{ {AMIX_DM_UNITY,0}, {AMIX_DM_M3DB,AMIX_DM_M3DB}, {0,AMIX_DM_UNITY},
{AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB} },
/* 6: L C R RL RR LFE */
{ {AMIX_DM_UNITY,0}, {AMIX_DM_M3DB,AMIX_DM_M3DB}, {0,AMIX_DM_UNITY},
{AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB}, {0,0} },
/* 7: L C R RL RR BC LFE */
{ {AMIX_DM_UNITY,0}, {AMIX_DM_M3DB,AMIX_DM_M3DB}, {0,AMIX_DM_UNITY},
{AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB},
{AMIX_DM_M6DB,AMIX_DM_M6DB}, {0,0} },
/* 8: L C R RL RR SL SR LFE */
{ {AMIX_DM_UNITY,0}, {AMIX_DM_M3DB,AMIX_DM_M3DB}, {0,AMIX_DM_UNITY},
{AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB},
{AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB}, {0,0} }
};
/* Microsoft/FLAC order. */
static const int32_t audio_mixer_downmix_wav_q15[9][8][2] = {
{ {0,0} }, /* unused */
{ {0,0} }, /* mono */
{ {0,0} }, /* stereo */
/* 3: FL FR FC */
{ {AMIX_DM_UNITY,0}, {0,AMIX_DM_UNITY}, {AMIX_DM_M3DB,AMIX_DM_M3DB} },
/* 4: FL FR BL BR */
{ {AMIX_DM_UNITY,0}, {0,AMIX_DM_UNITY},
{AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB} },
/* 5: FL FR FC BL BR */
{ {AMIX_DM_UNITY,0}, {0,AMIX_DM_UNITY}, {AMIX_DM_M3DB,AMIX_DM_M3DB},
{AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB} },
/* 6: FL FR FC LFE BL BR */
{ {AMIX_DM_UNITY,0}, {0,AMIX_DM_UNITY}, {AMIX_DM_M3DB,AMIX_DM_M3DB},
{0,0}, {AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB} },
/* 7: FL FR FC LFE BC SL SR */
{ {AMIX_DM_UNITY,0}, {0,AMIX_DM_UNITY}, {AMIX_DM_M3DB,AMIX_DM_M3DB},
{0,0}, {AMIX_DM_M6DB,AMIX_DM_M6DB},
{AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB} },
/* 8: FL FR FC LFE BL BR SL SR */
{ {AMIX_DM_UNITY,0}, {0,AMIX_DM_UNITY}, {AMIX_DM_M3DB,AMIX_DM_M3DB},
{0,0}, {AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB},
{AMIX_DM_M3DB,0}, {0,AMIX_DM_M3DB} }
};
/* Which order an arm hands its channels over in. */
static const int32_t (*audio_mixer_downmix_table(
enum audio_type_enum type, unsigned ch, const audio_mixer_voice_t *voice))[2]
@@ -498,15 +409,7 @@ static const int32_t (*audio_mixer_downmix_table(
#ifdef HAVE_RWMA
if (type == AUDIO_TYPE_WMA) return voice->types.stream.wma_matrix;
#endif
switch (type)
{
case AUDIO_TYPE_VORBIS:
case AUDIO_TYPE_OPUS:
return audio_mixer_downmix_vorbis_q15[ch];
default:
break;
}
return audio_mixer_downmix_wav_q15[ch];
return audio_transfer_fold_q15(type, ch);
}
/* Frames a temp buffer of 'samples' holds at 'ch' channels: what a
@@ -596,7 +499,7 @@ static float wav_sample_unit(const rwav_t *wav, const uint8_t *src,
static void wav_fold_to_stereo(const rwav_t *wav, const uint8_t *src,
float *dst, size_t frames)
{
const int32_t (*co)[2] = audio_mixer_downmix_wav_q15[wav->numchannels];
const int32_t (*co)[2] = audio_transfer_fold_q15(AUDIO_TYPE_WAV, wav->numchannels);
size_t i;
for (i = 0; i < frames; i++)
{
@@ -674,7 +577,7 @@ static bool wav_to_float(const rwav_t* wav, const uint8_t* src,
got = rwav_decode_s16(&h, src, 0, wav->numsamples, tmp);
{
const int32_t (*co)[2] =
audio_mixer_downmix_wav_q15[wav->numchannels];
audio_transfer_fold_q15(AUDIO_TYPE_WAV, wav->numchannels);
unsigned c;
for (i = 0; i < got; i++)
{
@@ -1017,7 +920,7 @@ static bool wav_to_s16(const rwav_t* wav, const uint8_t* src,
got = rwav_decode_s16(&h, src, 0, wav->numsamples, tmp);
{
const int32_t (*co)[2] =
audio_mixer_downmix_wav_q15[wav->numchannels];
audio_transfer_fold_q15(AUDIO_TYPE_WAV, wav->numchannels);
unsigned c;
for (i = 0; i < got; i++)
{
@@ -1046,7 +949,7 @@ static bool wav_to_s16(const rwav_t* wav, const uint8_t* src,
size_t i2;
unsigned c;
const int32_t (*co)[2] =
audio_mixer_downmix_wav_q15[wav->numchannels];
audio_transfer_fold_q15(AUDIO_TYPE_WAV, wav->numchannels);
for (i2 = 0; i2 < wav->numsamples; i2++)
{
int64_t l = 0, r = 0;
@@ -0,0 +1,83 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (float_to_ulaw.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* G.711 mu-law: see audio/conversion/float_to_ulaw.h. */
#include <audio/conversion/float_to_ulaw.h>
/* The level code @i (0..127, magnitude) decodes to, 16-bit scale */
static int ulaw_level(int i)
{
return ((((i & 15) << 3) + 0x84) << (i >> 4)) - 0x84;
}
/* The code for magnitude @j (1..8192, 14-bit scale): the first whose
* decision level - halfway to the next code's level, rounded - is above
* it, or 127 past the last; seven steps of a binary search */
static int ulaw_code(int j)
{
int lo = 0, hi = 127;
while (lo < hi)
{
int mid = (lo + hi) >> 1;
if (j < ((ulaw_level(mid) + ulaw_level(mid + 1) + 4) >> 3))
hi = mid;
else
lo = mid + 1;
}
return lo;
}
static uint8_t ulaw_from_s16(int s)
{
int idx = (s + 32768) >> 2; /* 0..16383, 8192 at zero */
if (idx > 8192)
return (uint8_t)(ulaw_code(idx - 8192) ^ 0xFF);
if (idx == 8192)
return 0xFF;
return (uint8_t)(ulaw_code(8192 - idx) ^ 0x7F);
}
void convert_s16_to_ulaw(uint8_t *out, const int16_t *in, size_t samples)
{
size_t i;
for (i = 0; i < samples; i++)
out[i] = ulaw_from_s16(in[i]);
}
void convert_float_to_ulaw(uint8_t *out, const float *in, size_t samples)
{
size_t i;
for (i = 0; i < samples; i++)
{
float f = in[i] * 32768.0f;
int s;
/* clipped, then rounded half away from zero */
if (f >= 32767.0f)
s = 32767;
else if (f <= -32768.0f)
s = -32768;
else
s = (int)(f < 0 ? f - 0.5f : f + 0.5f);
out[i] = ulaw_from_s16(s);
}
}
+278
View File
@@ -574,6 +574,117 @@ static void aes_x86_cbc_decrypt(const struct aes_ctx *ctx, uint8_t *iv,
_mm_storeu_si128((__m128i*)iv, prev);
}
/* XTS (IEEE 1619): the tweak is multiplied by alpha from one block to
* the next. As a little-endian 128-bit number it is a shift left by
* one, the bit out of the low half carried into the high one and the
* one out of the top folded back in as 0x87. */
AES_TARGET_X86
static __m128i aes_x86_xts_next(__m128i t)
{
/* lane 0: the top bit, lane 1: the low half's top bit; times 0x87
* and 1, which the carries being 0 or 1 make a multiply */
const __m128i poly = _mm_set_epi32(0, 1, 0, 0x87);
const __m128i carry = _mm_shuffle_epi32(_mm_srli_epi64(t, 63), 0x4e);
return _mm_xor_si128(_mm_slli_epi64(t, 1), _mm_mul_epu32(carry, poly));
}
/* XTS over whole blocks, eight in flight, from the encrypted tweak
* @tweak; the round keys made once per call (through InvMixColumns to
* decrypt). Each block's input is held in a register before its output
* is stored, so @in and @out may alias. The tweak comes as bytes: a
* vector argument would be loaded by the caller, which is built
* without SSE on i686. */
AES_TARGET_X86
static void aes_x86_xts(const struct aes_ctx *ctx, const uint8_t *tweak,
const uint8_t *in, uint8_t *out, size_t len, int decrypt)
{
__m128i k[15];
__m128i t = _mm_loadu_si128((const __m128i*)tweak);
unsigned r, nr = ctx->rounds;
if (decrypt)
{
k[0] = aes_x86_bswap32(_mm_loadu_si128((const __m128i*)(ctx->rk + 4 * nr)));
k[nr] = aes_x86_bswap32(_mm_loadu_si128((const __m128i*)ctx->rk));
for (r = 1; r < nr; r++)
k[r] = _mm_aesimc_si128(aes_x86_bswap32(
_mm_loadu_si128((const __m128i*)(ctx->rk + 4 * (nr - r)))));
}
else
for (r = 0; r <= nr; r++)
k[r] = aes_x86_bswap32(_mm_loadu_si128((const __m128i*)(ctx->rk + 4 * r)));
#define AES_X86_XTS_ROUNDS(op, oplast) \
for (r = 1; r < nr; r++) \
{ \
const __m128i kr = k[r]; \
b0 = op(b0, kr); b1 = op(b1, kr); b2 = op(b2, kr); b3 = op(b3, kr); \
b4 = op(b4, kr); b5 = op(b5, kr); b6 = op(b6, kr); b7 = op(b7, kr); \
} \
b0 = oplast(b0, k[nr]); b1 = oplast(b1, k[nr]); \
b2 = oplast(b2, k[nr]); b3 = oplast(b3, k[nr]); \
b4 = oplast(b4, k[nr]); b5 = oplast(b5, k[nr]); \
b6 = oplast(b6, k[nr]); b7 = oplast(b7, k[nr]);
while (len >= 128)
{
const __m128i t0 = t;
const __m128i t1 = aes_x86_xts_next(t0);
const __m128i t2 = aes_x86_xts_next(t1);
const __m128i t3 = aes_x86_xts_next(t2);
const __m128i t4 = aes_x86_xts_next(t3);
const __m128i t5 = aes_x86_xts_next(t4);
const __m128i t6 = aes_x86_xts_next(t5);
const __m128i t7 = aes_x86_xts_next(t6);
__m128i b0 = _mm_xor_si128(_mm_xor_si128(_mm_loadu_si128((const __m128i*)(in)), t0), k[0]);
__m128i b1 = _mm_xor_si128(_mm_xor_si128(_mm_loadu_si128((const __m128i*)(in + 16)), t1), k[0]);
__m128i b2 = _mm_xor_si128(_mm_xor_si128(_mm_loadu_si128((const __m128i*)(in + 32)), t2), k[0]);
__m128i b3 = _mm_xor_si128(_mm_xor_si128(_mm_loadu_si128((const __m128i*)(in + 48)), t3), k[0]);
__m128i b4 = _mm_xor_si128(_mm_xor_si128(_mm_loadu_si128((const __m128i*)(in + 64)), t4), k[0]);
__m128i b5 = _mm_xor_si128(_mm_xor_si128(_mm_loadu_si128((const __m128i*)(in + 80)), t5), k[0]);
__m128i b6 = _mm_xor_si128(_mm_xor_si128(_mm_loadu_si128((const __m128i*)(in + 96)), t6), k[0]);
__m128i b7 = _mm_xor_si128(_mm_xor_si128(_mm_loadu_si128((const __m128i*)(in + 112)), t7), k[0]);
if (decrypt)
{
AES_X86_XTS_ROUNDS(_mm_aesdec_si128, _mm_aesdeclast_si128)
}
else
{
AES_X86_XTS_ROUNDS(_mm_aesenc_si128, _mm_aesenclast_si128)
}
_mm_storeu_si128((__m128i*)(out), _mm_xor_si128(b0, t0));
_mm_storeu_si128((__m128i*)(out + 16), _mm_xor_si128(b1, t1));
_mm_storeu_si128((__m128i*)(out + 32), _mm_xor_si128(b2, t2));
_mm_storeu_si128((__m128i*)(out + 48), _mm_xor_si128(b3, t3));
_mm_storeu_si128((__m128i*)(out + 64), _mm_xor_si128(b4, t4));
_mm_storeu_si128((__m128i*)(out + 80), _mm_xor_si128(b5, t5));
_mm_storeu_si128((__m128i*)(out + 96), _mm_xor_si128(b6, t6));
_mm_storeu_si128((__m128i*)(out + 112), _mm_xor_si128(b7, t7));
t = aes_x86_xts_next(t7);
in += 128;
out += 128;
len -= 128;
}
#undef AES_X86_XTS_ROUNDS
for (; len >= 16; in += 16, out += 16, len -= 16)
{
__m128i b = _mm_xor_si128(_mm_xor_si128(_mm_loadu_si128((const __m128i*)in), t), k[0]);
if (decrypt)
{
for (r = 1; r < nr; r++)
b = _mm_aesdec_si128(b, k[r]);
b = _mm_aesdeclast_si128(b, k[nr]);
}
else
{
for (r = 1; r < nr; r++)
b = _mm_aesenc_si128(b, k[r]);
b = _mm_aesenclast_si128(b, k[nr]);
}
_mm_storeu_si128((__m128i*)out, _mm_xor_si128(b, t));
t = aes_x86_xts_next(t);
}
}
/* CTR with eight blocks in flight so the AES unit's latency is hidden,
* the round keys loaded and byte-swapped once per call rather than once
* per round. The counter block is kept byte-reversed, where its 32-bit
@@ -967,6 +1078,73 @@ static void aes_arm_cbc_decrypt(const struct aes_ctx *ctx, uint8_t *iv,
vst1q_u8(iv, prev);
}
/* XTS's tweak times alpha, as the x86 form above: each half shifted
* left, the carries exchanged and the top one folded in as 0x87. */
AES_TARGET_ARM
static uint8x16_t aes_arm_xts_next(uint8x16_t t)
{
static const uint64_t poly_v[2] = { 0x87, 1 };
const uint64x2_t v = vreinterpretq_u64_u8(t);
const uint64x2_t top = vshrq_n_u64(v, 63);
const uint64x2_t carry = vextq_u64(top, top, 1);
const uint64x2_t mask = vsubq_u64(vdupq_n_u64(0), carry);
return vreinterpretq_u8_u64(veorq_u64(vshlq_n_u64(v, 1),
vandq_u64(mask, vld1q_u64(poly_v))));
}
/* XTS over whole blocks, eight in flight, the round keys loaded once.
* Decrypting is the CBC decryption's AESD chain, encrypting the CTR's
* AESE chain; the tweak goes in before the first round key and out
* after the last. */
AES_TARGET_ARM
static void aes_arm_xts(const struct aes_ctx *ctx, uint8x16_t t,
const uint8_t *in, uint8_t *out, size_t len, int decrypt)
{
uint8x16_t k[15];
unsigned r, nr = ctx->rounds;
for (r = 0; r <= nr; r++)
k[r] = aes_arm_rk(ctx, r);
if (decrypt)
for (r = 1; r < nr; r++)
k[r] = vaesimcq_u8(k[r]);
while (len >= 16)
{
uint8x16_t tw[8], b[8];
unsigned i, n = len >= 128 ? 8 : 1;
for (i = 0; i < n; i++)
{
tw[i] = t;
b[i] = veorq_u8(vld1q_u8(in + 16 * i), t);
t = aes_arm_xts_next(t);
}
if (decrypt)
{
for (i = 0; i < n; i++)
b[i] = vaesdq_u8(b[i], k[nr]);
for (r = nr - 1; r > 0; r--)
for (i = 0; i < n; i++)
b[i] = vaesdq_u8(vaesimcq_u8(b[i]), k[r]);
for (i = 0; i < n; i++)
b[i] = veorq_u8(b[i], k[0]);
}
else
{
for (r = 0; r + 1 < nr; r++)
for (i = 0; i < n; i++)
b[i] = vaesmcq_u8(vaeseq_u8(b[i], k[r]));
for (i = 0; i < n; i++)
b[i] = veorq_u8(vaeseq_u8(b[i], k[nr - 1]), k[nr]);
}
for (i = 0; i < n; i++)
vst1q_u8(out + 16 * i, veorq_u8(b[i], tw[i]));
in += 16 * n;
out += 16 * n;
len -= 16 * n;
}
}
/* CTR with eight blocks in flight, the round keys loaded once per
* call. The counter block is kept byte-reversed within each 32-bit
* lane, where its big-endian counter is lane 3 as a native word: one
@@ -1441,6 +1619,42 @@ void aes_ctr_crypt(const struct aes_ctx *ctx, uint8_t *counter,
crypto_memzero(ks, sizeof(ks));
}
/* The register holds ciphertext: encrypting it gives the next
* keystream block, and each ciphertext octet goes back into it. */
void aes_cfb128_encrypt(const struct aes_ctx *ctx, uint8_t *iv,
unsigned *num, const uint8_t *in, uint8_t *out, size_t len)
{
unsigned n = *num & 15;
while (len)
{
if (!n)
aes_encrypt_block(ctx, iv, iv);
iv[n] ^= *in++;
*out++ = iv[n];
n = (n + 1) & 15;
len--;
}
*num = n;
}
void aes_cfb128_decrypt(const struct aes_ctx *ctx, uint8_t *iv,
unsigned *num, const uint8_t *in, uint8_t *out, size_t len)
{
unsigned n = *num & 15;
while (len)
{
uint8_t c;
if (!n)
aes_encrypt_block(ctx, iv, iv);
c = *in++;
*out++ = (uint8_t)(iv[n] ^ c);
iv[n] = c;
n = (n + 1) & 15;
len--;
}
*num = n;
}
void aes_cbc_encrypt(const struct aes_ctx *ctx, uint8_t *iv,
const uint8_t *in, uint8_t *out, size_t len)
{
@@ -1515,6 +1729,70 @@ void aes_cbc_decrypt(const struct aes_ctx *ctx, uint8_t *iv,
memcpy(iv, prev, 16);
}
/* The tweak times alpha in GF(2^128), as a little-endian number: a
* shift left, the bit out of the top folded back in as 0x87. */
static void aes_xts_next(uint8_t *t)
{
unsigned i;
uint8_t carry = 0;
for (i = 0; i < 16; i++)
{
uint8_t next = t[i] >> 7;
t[i] = (uint8_t)((t[i] << 1) | carry);
carry = next;
}
t[0] ^= (uint8_t)(0x87 & (0 - carry));
}
static void aes_xts(const struct aes_ctx *data_key,
const struct aes_ctx *tweak_key, const uint8_t *tweak,
const uint8_t *in, uint8_t *out, size_t len, int decrypt)
{
uint8_t t[16], b[16], o[16];
unsigned i;
aes_encrypt_block(tweak_key, tweak, t);
#if defined(AES_HAVE_X86_PATH)
if (aes_x86_available())
{
aes_x86_xts(data_key, t, in, out, len, decrypt);
return;
}
#elif defined(AES_HAVE_ARM_PATH)
if (aes_arm_available())
{
aes_arm_xts(data_key, vld1q_u8(t), in, out, len, decrypt);
return;
}
#endif
for (; len >= 16; in += 16, out += 16, len -= 16)
{
for (i = 0; i < 16; i++)
b[i] = in[i] ^ t[i];
if (decrypt)
aes_decrypt_block_c(data_key, b, o);
else
aes_encrypt_block_c(data_key, b, o);
for (i = 0; i < 16; i++)
out[i] = o[i] ^ t[i];
aes_xts_next(t);
}
}
void aes_xts_encrypt(const struct aes_ctx *data_key,
const struct aes_ctx *tweak_key, const uint8_t *tweak,
const uint8_t *in, uint8_t *out, size_t len)
{
aes_xts(data_key, tweak_key, tweak, in, out, len, 0);
}
void aes_xts_decrypt(const struct aes_ctx *data_key,
const struct aes_ctx *tweak_key, const uint8_t *tweak,
const uint8_t *in, uint8_t *out, size_t len)
{
aes_xts(data_key, tweak_key, tweak, in, out, len, 1);
}
/* Double in GF(2^128) with the CMAC polynomial. */
static void aes_cmac_dbl(uint8_t *b)
{
+45 -2
View File
@@ -407,6 +407,11 @@ static uint32_t rd_adler32_update(uint32_t adler,
return rd_adler32_on(adler, buf, len, 16);
}
uint32_t encoding_adler32(uint32_t adler, const uint8_t *buf, size_t len)
{
return rd_adler32_update(adler, buf, len);
}
/* Test entry point for tools/encodings/adler32_test.c: the kernel above is
* static, and driving it only through rinflate/rdeflate would exercise
* whatever lengths and alignments a stream happens to produce rather than
@@ -2220,6 +2225,7 @@ struct rdeflate
uint32_t crc; /* gzip: running crc32 of the input */
uint32_t total_in; /* gzip: mod 2^32, for ISIZE */
int final_in; /* caller signalled end of input */
int flush_req; /* sync flush asked for: 1 pending, 2 marker out */
int done;
int error;
@@ -2944,6 +2950,14 @@ void rdeflate_finish(void *data)
s->final_in = 1;
}
/* write out everything taken so far, ending on a byte boundary */
void rdeflate_flush(void *data)
{
struct rdeflate *s = (struct rdeflate*)data;
if (!s->flush_req)
s->flush_req = 1;
}
/* ------- the parser: consume win[block_start..win_len) into symbols ------- */
/* Fills the symbol buffer using greedy/lazy matching. Leaves s->pos at the
* first unprocessed byte. Stops early if the symbol buffer is near full.
@@ -3141,8 +3155,10 @@ static void rd_parse(struct rdeflate *s)
s->prev_lit = prev_lit;
}
/* flush a deferred match/literal at true end of input */
if (s->final_in && s->pos >= end && s->have_prev
/* flush a deferred match/literal at true end of input, or at a sync
* flush once all the input given so far is in */
if ((s->final_in || (s->flush_req && s->in_pos >= s->in_size))
&& s->pos >= end && s->have_prev
&& s->nsyms < RD_BLOCK_SYMS - 2)
{
s->have_prev = 0;
@@ -3886,6 +3902,33 @@ int rdeflate_process(void *data, size_t *read, size_t *wrote)
block_ready = 1;
is_final = 1;
}
/* sync flush: once all the input given is parsed, the pending
* symbols go out as a block, then an empty stored block ends
* the output on a byte boundary (zlib's Z_SYNC_FLUSH). The
* window is kept, so matches still reach across it. */
if (!block_ready && s->flush_req && !s->final_in
&& s->in_pos >= s->in_size && s->pos >= s->win_len
&& !s->have_prev)
{
if (s->nsyms)
block_ready = 1;
else
{
if (s->flush_req == 1)
{
rd_putbits(s, 0, 3); /* BFINAL=0, BTYPE=00 */
if (s->bitcnt & 7)
rd_putbits(s, 0, 8 - (s->bitcnt & 7));
rd_putbits(s, 0x0000, 16);
rd_putbits(s, 0xffff, 16);
s->flush_req = 2;
}
if (!rd_flush_bytes(s))
goto suspend;
s->flush_req = 0;
goto suspend;
}
}
if (!block_ready) /* need more input */
goto suspend;
s->block_final = is_final;
@@ -0,0 +1,242 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (encoding_shiftjis.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdint.h>
#include <stddef.h>
#include <encodings/shiftjis.h>
#include "encoding_shiftjis_tables.h"
/* The row of sjis_decode for lead octet @b, or -1 */
static int sjis_row(unsigned b)
{
if (b >= 0x81 && b <= 0x9F)
return (int)(b - 0x81);
if (b >= 0xE0 && b <= 0xEF)
return (int)(b - 0xE0 + 31);
if (b >= 0xFA && b <= 0xFC)
return (int)(b - 0xFA + 47);
return -1;
}
/* Puts @cp at @out + *pos as UTF-8 if it fits in @cap octets */
static int sjis_put_utf8(char *out, size_t cap, size_t *pos, uint32_t cp)
{
size_t n = cp < 0x80 ? 1 : cp < 0x800 ? 2 : 3;
char *p = out + *pos;
if (cap - *pos < n)
return 0;
if (n == 1)
p[0] = (char)cp;
else if (n == 2)
{
p[0] = (char)(0xC0 | (cp >> 6));
p[1] = (char)(0x80 | (cp & 0x3F));
}
else
{
p[0] = (char)(0xE0 | (cp >> 12));
p[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
p[2] = (char)(0x80 | (cp & 0x3F));
}
*pos += n;
return 1;
}
size_t sjis_to_utf8(char *out, size_t out_size, const char *in, size_t in_len)
{
const unsigned char *s = (const unsigned char*)in;
size_t i = 0, pos = 0;
if (!out_size)
return 0;
while (i < in_len)
{
unsigned b = s[i];
uint32_t cp;
int row;
if (b < 0x80)
{
cp = b;
i++;
}
else if (b >= 0xA1 && b <= 0xDF)
{
/* half-width katakana */
cp = 0xFF61 + (b - 0xA1);
i++;
}
else
{
unsigned t;
if ((row = sjis_row(b)) < 0 || i + 1 >= in_len)
{
i++;
continue;
}
t = s[i + 1];
if (t < 0x40 || t > 0xFC || !(cp = sjis_decode[row][t - 0x40]))
{
i++;
continue;
}
i += 2;
}
if (!sjis_put_utf8(out, out_size - 1, &pos, cp))
break;
}
out[pos] = '\0';
return pos;
}
/* The two-octet code for @cp, or 0 */
static unsigned sjis_lookup(uint32_t cp)
{
size_t lo = 0, hi = SJIS_ENCODE_COUNT;
while (lo < hi)
{
size_t mid = lo + (hi - lo) / 2;
if (sjis_encode[mid][0] < cp)
lo = mid + 1;
else
hi = mid;
}
return lo < SJIS_ENCODE_COUNT && sjis_encode[lo][0] == cp ? sjis_encode[lo][1] : 0;
}
/* The code point UTF-8 starts at @s (@len octets left), its length at
* @n; 0 at @n where the octet starts no valid character */
static uint32_t sjis_utf8_next(const unsigned char *s, size_t len, size_t *n)
{
unsigned b = s[0];
uint32_t cp, min;
size_t k, more;
*n = 0;
if (b < 0x80)
{
*n = 1;
return b;
}
if (b >= 0xC2 && b <= 0xDF)
{
more = 1;
cp = b & 0x1F;
min = 0x80;
}
else if (b >= 0xE0 && b <= 0xEF)
{
more = 2;
cp = b & 0x0F;
min = 0x800;
}
else if (b >= 0xF0 && b <= 0xF4)
{
more = 3;
cp = b & 0x07;
min = 0x10000;
}
else
return 0;
if (len <= more)
return 0;
for (k = 1; k <= more; k++)
{
if ((s[k] & 0xC0) != 0x80)
return 0;
cp = (cp << 6) | (s[k] & 0x3F);
}
if (cp < min || cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF))
return 0;
*n = more + 1;
return cp;
}
size_t utf8_to_sjis(char *out, size_t out_size, const char *in, size_t in_len)
{
const unsigned char *s = (const unsigned char*)in;
size_t i = 0, pos = 0;
if (!out_size)
return 0;
while (i < in_len)
{
size_t n;
uint32_t cp = sjis_utf8_next(s + i, in_len - i, &n);
unsigned code;
if (!n)
{
i++;
continue;
}
i += n;
if (cp < 0x80)
code = cp;
else if (cp >= 0xFF61 && cp <= 0xFF9F)
code = 0xA1 + (cp - 0xFF61);
else if (!(code = sjis_lookup(cp)))
continue;
if (code > 0xFF)
{
if (out_size - 1 - pos < 2)
break;
out[pos++] = (char)(code >> 8);
out[pos++] = (char)(code & 0xFF);
}
else
{
if (out_size - 1 - pos < 1)
break;
out[pos++] = (char)code;
}
}
out[pos] = '\0';
return pos;
}
/* Code page 1252's 0x80-0x9F; 0 where it has nothing */
static const uint16_t cp1252_high[32] = {
0x20AC, 0, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0, 0x017D, 0,
0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0, 0x017E, 0x0178
};
size_t cp1252_to_utf8(char *out, size_t out_size, const char *in, size_t in_len)
{
const unsigned char *s = (const unsigned char*)in;
size_t i, pos = 0;
if (!out_size)
return 0;
for (i = 0; i < in_len; i++)
{
uint32_t cp = s[i];
if (cp >= 0x80 && cp <= 0x9F && !(cp = cp1252_high[cp - 0x80]))
continue;
if (!sjis_put_utf8(out, out_size - 1, &pos, cp))
break;
}
out[pos] = '\0';
return pos;
}
File diff suppressed because it is too large. Load diff
+185
View File
@@ -0,0 +1,185 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (encoding_xxh32.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string.h>
#if defined(_MSC_VER)
#include <stdlib.h>
#endif
#include <retro_inline.h>
#include <retro_endianness.h>
#include <encodings/xxh32.h>
#define XXH32_P1 0x9E3779B1U
#define XXH32_P2 0x85EBCA77U
#define XXH32_P3 0xC2B2AE3DU
#define XXH32_P4 0x27D4EB2FU
#define XXH32_P5 0x165667B1U
#if defined(_MSC_VER)
#define XXH32_ROTL(x, r) _rotl((x), (r))
#else
#define XXH32_ROTL(x, r) (((x) << (r)) | ((x) >> (32 - (r))))
#endif
#define XXH32_ROUND(acc, in) \
((acc) += (in) * XXH32_P2, (acc) = XXH32_ROTL((acc), 13), \
(acc) *= XXH32_P1)
static INLINE uint32_t xxh32_rd32(const uint8_t *p)
{
uint32_t v;
memcpy(&v, p, 4);
return swap_if_big32(v);
}
/* The whole stripes of @p up to @end, at least one, through the lanes
* @v; returns where the stripes stop. */
static INLINE const uint8_t *xxh32_stripes(uint32_t *v, const uint8_t *p,
const uint8_t *end)
{
uint32_t v1 = v[0];
uint32_t v2 = v[1];
uint32_t v3 = v[2];
uint32_t v4 = v[3];
const uint8_t *stop = p + ((size_t)(end - p) & ~(size_t)15);
do
{
XXH32_ROUND(v1, xxh32_rd32(p));
XXH32_ROUND(v2, xxh32_rd32(p + 4));
XXH32_ROUND(v3, xxh32_rd32(p + 8));
XXH32_ROUND(v4, xxh32_rd32(p + 12));
p += 16;
} while (p != stop);
v[0] = v1;
v[1] = v2;
v[2] = v3;
v[3] = v4;
return p;
}
static INLINE uint32_t xxh32_converge(const uint32_t *v)
{
return XXH32_ROTL(v[0], 1) + XXH32_ROTL(v[1], 7)
+ XXH32_ROTL(v[2], 12) + XXH32_ROTL(v[3], 18);
}
/* The last @len (under 16) octets at @p, and the avalanche: fours, then
* single octets */
static INLINE uint32_t xxh32_finish(uint32_t h, const uint8_t *p, size_t len)
{
for (; len >= 4; len -= 4, p += 4)
{
h += xxh32_rd32(p) * XXH32_P3;
h = XXH32_ROTL(h, 17) * XXH32_P4;
}
for (; len; len--)
{
h += (uint32_t)*p++ * XXH32_P5;
h = XXH32_ROTL(h, 11) * XXH32_P1;
}
h ^= h >> 15;
h *= XXH32_P2;
h ^= h >> 13;
h *= XXH32_P3;
h ^= h >> 16;
return h;
}
static void xxh32_lanes(uint32_t *v, uint32_t seed)
{
v[0] = seed + XXH32_P1 + XXH32_P2;
v[1] = seed + XXH32_P2;
v[2] = seed;
v[3] = seed - XXH32_P1;
}
uint32_t xxh32(const void *data, size_t len, uint32_t seed)
{
const uint8_t *p = (const uint8_t*)data;
uint32_t h;
if (len >= 16)
{
uint32_t v[4];
xxh32_lanes(v, seed);
p = xxh32_stripes(v, p, p + len);
h = xxh32_converge(v);
}
else
h = seed + XXH32_P5;
return xxh32_finish(h + (uint32_t)len, p, len & 15);
}
void xxh32_reset(xxh32_state_t *s, uint32_t seed)
{
xxh32_lanes(s->v, seed);
s->total = 0;
s->large = 0;
s->memsize = 0;
}
void xxh32_update(xxh32_state_t *s, const void *data, size_t len)
{
const uint8_t *p = (const uint8_t*)data;
const uint8_t *end = p + len;
if (!len)
return;
s->total += (uint32_t)len;
s->large |= (len >= 16) | (s->total >= 16);
/* not yet a stripe */
if (s->memsize + len < 16)
{
memcpy(s->mem + s->memsize, p, len);
s->memsize += (uint32_t)len;
return;
}
/* the stripe begun before, made whole */
if (s->memsize)
{
size_t fill = 16 - s->memsize;
memcpy(s->mem + s->memsize, p, fill);
xxh32_stripes(s->v, s->mem, s->mem + 16);
p += fill;
s->memsize = 0;
}
if (end - p >= 16)
p = xxh32_stripes(s->v, p, end);
if (p != end)
{
memcpy(s->mem, p, (size_t)(end - p));
s->memsize = (uint32_t)(end - p);
}
}
uint32_t xxh32_digest(const xxh32_state_t *s)
{
uint32_t h = s->large
? xxh32_converge(s->v)
: s->v[2] + XXH32_P5;
return xxh32_finish(h + s->total, s->mem, s->memsize);
}
@@ -0,0 +1,61 @@
# python3 gen_sjis_tables.py > encoding_shiftjis_tables.h
#
# Writes encoding_shiftjis_tables.h: Shift-JIS as Windows code page 932
# has it (JIS X 0208 with the NEC and IBM extensions), from Python's
# cp932 codec. The user-defined area F040-F9FC and the single octets
# 0x80, 0xA0 and 0xFD-0xFF, which code page 932 sends to the Private Use
# Area, are left out.
import sys
def lead_ok(l):
return 0x81 <= l <= 0x9F or 0xE0 <= l <= 0xEF or 0xFA <= l <= 0xFC
leads = [l for l in range(0x81, 0xFD) if lead_ok(l)]
decode = []
pairs = {}
for l in leads:
row = []
for t in range(0x40, 0xFD):
u = 0
try:
s = bytes([l, t]).decode('cp932')
if len(s) == 1 and not (0xE000 <= ord(s) <= 0xF8FF):
u = ord(s)
except UnicodeDecodeError:
pass
row.append(u)
decode.append(row)
for cp in range(0x80, 0x10000):
if 0xD800 <= cp < 0xE000 or 0xE000 <= cp <= 0xF8FF:
continue
try:
e = chr(cp).encode('cp932')
except UnicodeEncodeError:
continue
if len(e) == 2 and lead_ok(e[0]):
pairs[cp] = (e[0] << 8) | e[1]
out = sys.stdout
out.write('/* Generated by gen_sjis_tables.py from code page 932, as Python\'s\n'
' * cp932 codec has it; do not edit. */\n'
'#ifndef LIBRETRO_ENCODING_SHIFTJIS_TABLES_H\n'
'#define LIBRETRO_ENCODING_SHIFTJIS_TABLES_H\n\n'
'#include <stdint.h>\n\n')
out.write('/* Unicode for lead octet L (rows: 0x81-0x9F, 0xE0-0xEF, 0xFA-0xFC)\n'
' * and trail octet 0x40-0xFC; 0 where nothing is mapped. */\n')
out.write('static const uint16_t sjis_decode[%d][189] = {\n' % len(leads))
for l, row in zip(leads, decode):
out.write(' /* 0x%02X */ {\n' % l)
for i in range(0, len(row), 10):
out.write(' ' + ','.join('0x%04X' % v for v in row[i:i+10]) + ',\n')
out.write(' },\n')
out.write('};\n\n')
keys = sorted(pairs)
out.write('/* Unicode code points with their two-octet Shift-JIS codes, sorted\n'
' * by code point for a binary search. */\n')
out.write('#define SJIS_ENCODE_COUNT %d\n' % len(keys))
out.write('static const uint16_t sjis_encode[SJIS_ENCODE_COUNT][2] = {\n')
for i in range(0, len(keys), 6):
out.write(' ' + ','.join('{0x%04X,0x%04X}' % (k, pairs[k]) for k in keys[i:i+6]) + ',\n')
out.write('};\n\n#endif\n')
+5 -43
View File
@@ -599,52 +599,14 @@ struct string_list *file_archive_get_file_list(const char *path,
/* Copies a complete temporary file over a target it could not be
* renamed onto, and removes it. Only for filesystems that refuse the
* rename; an atomic replacement is no longer possible there, which is
* what writing the member in place always meant. */
* renamed onto, with the platform's copy primitive where there is one.
* Only for filesystems that refuse the rename; an atomic replacement
* is no longer possible there, which is what writing the member in
* place always meant. */
static bool file_archive_pending_copy_in_place(const char *temp_path,
const char *path)
{
bool ok = true;
uint8_t *buf;
RFILE *in;
RFILE *out;
if (!(buf = (uint8_t*)malloc(65536)))
return false;
if (!(in = filestream_open(temp_path, RETRO_VFS_FILE_ACCESS_READ,
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
{
free(buf);
return false;
}
if (!(out = filestream_open(path, RETRO_VFS_FILE_ACCESS_WRITE,
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
{
filestream_close(in);
free(buf);
return false;
}
for (;;)
{
int64_t rd = filestream_read(in, buf, 65536);
if (rd < 0)
ok = false;
if (rd <= 0)
break;
if (filestream_write(out, buf, rd) != rd)
{
ok = false;
break;
}
}
filestream_close(in);
if (filestream_close(out) != 0)
ok = false;
free(buf);
return ok;
return filestream_copy(temp_path, path) == 0;
}
/* Puts a completed member in place. Written beside the file and
+2 -1
View File
@@ -390,7 +390,8 @@ static int64_t bz2_file_read(const char *path, const char *needle,
if (optional_outfile)
{
RFILE *out = filestream_open(optional_outfile,
RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
RETRO_VFS_FILE_ACCESS_WRITE,
RETRO_VFS_FILE_ACCESS_HINT_SEQUENTIAL_BULK);
ok = out && bz2_pass(&src, file, filestream_get_size(file),
bz2_write, out);
if (out)
+47 -50
View File
@@ -185,6 +185,18 @@ static INLINE uint64_t read_le64(const uint8_t *data)
return (uint64_t)read_le(data, 4) | ((uint64_t)read_le(data + 4, 4) << 32);
}
/* @len octets at @off: one read at the offset, or a seek and a read
* where the file has none */
static int64_t zip_read_at(RFILE *f, void *buf, int64_t len, int64_t off)
{
int64_t got = filestream_read_at(f, buf, len, off);
if (got >= 0)
return got;
if (filestream_seek(f, off, RETRO_VFS_SEEK_POSITION_START) < 0)
return -1;
return filestream_read(f, buf, len);
}
static void zip_context_free_stream(
zip_context_t *zip_context, bool keep_decompressed)
{
@@ -274,17 +286,16 @@ static bool zip_bzip2_fill(struct file_archive_transfer *state,
uint64_t to = taken + cap;
if (to > zip_context->csize)
to = zip_context->csize;
filestream_seek(state->archive_file,
(int64_t)(zip_context->fdoffset + zip_context->boffset),
RETRO_VFS_SEEK_POSITION_START);
while (zip_context->boffset < to)
{
size_t at = (size_t)(zip_context->boffset % cap);
size_t n = cap - at;
if (n > to - zip_context->boffset)
n = (size_t)(to - zip_context->boffset);
if (filestream_read(state->archive_file, zip_context->tmpbuf + at,
(int64_t)n) != (int64_t)n)
if (zip_read_at(state->archive_file, zip_context->tmpbuf + at,
(int64_t)n,
(int64_t)(zip_context->fdoffset + zip_context->boffset))
!= (int64_t)n)
return false;
zip_context->boffset += n;
}
@@ -352,8 +363,8 @@ static const uint8_t *zip_member_whole(struct file_archive_transfer *state,
*buf = p;
*cap = (size_t)csize;
}
filestream_seek(state->archive_file, off, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, *buf, csize) != (int64_t)csize)
if (zip_read_at(state->archive_file, *buf, (int64_t)csize, off)
!= (int64_t)csize)
return NULL;
return *buf;
}
@@ -444,8 +455,8 @@ static bool zlib_stream_decompress_data_to_file_init(
else
#endif
{
filestream_seek(state->archive_file, (int64_t)local + 26, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, local_header_buf, 4) != 4)
if (zip_read_at(state->archive_file, local_header_buf, 4,
(int64_t)local + 26) != 4)
return false;
local_header = local_header_buf;
}
@@ -518,11 +529,9 @@ static bool zlib_stream_decompress_data_to_file_init(
else
#endif
{
filestream_seek(state->archive_file, offsetData,
RETRO_VFS_SEEK_POSITION_START);
if ( csize < sizeof(hdr)
|| filestream_read(state->archive_file, hdr, sizeof(hdr))
!= (int64_t)sizeof(hdr))
|| zip_read_at(state->archive_file, hdr, sizeof(hdr),
offsetData) != (int64_t)sizeof(hdr))
return false;
}
if ( csize < sizeof(hdr)
@@ -708,10 +717,10 @@ static int zlib_stream_decompress_data_to_file_iterate(
#endif
{
/* Read the entire file to memory */
filestream_seek(state->archive_file, zip_context->fdoffset, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file,
zip_context->decompressed_data,
zip_context->usize) != (int64_t)zip_context->usize)
if (zip_read_at(state->archive_file,
zip_context->decompressed_data,
(int64_t)zip_context->usize, zip_context->fdoffset)
!= (int64_t)zip_context->usize)
return -1;
}
@@ -897,11 +906,9 @@ static int zlib_stream_decompress_data_to_file_iterate(
size_t k = left < room ? (size_t)left : room;
if (k)
{
filestream_seek(state->archive_file,
(int64_t)(zip_context->fdoffset + zip_context->boffset),
RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file,
zip_context->tmpbuf + zip_context->in_have, (int64_t)k)
if (zip_read_at(state->archive_file,
zip_context->tmpbuf + zip_context->in_have, (int64_t)k,
(int64_t)(zip_context->fdoffset + zip_context->boffset))
!= (int64_t)k)
return -1;
zip_context->boffset += k;
@@ -971,11 +978,9 @@ static int zlib_stream_decompress_data_to_file_iterate(
size_t k = left < room ? (size_t)left : room;
if (k)
{
filestream_seek(state->archive_file,
(int64_t)(zip_context->fdoffset + zip_context->boffset),
RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file,
zip_context->tmpbuf + zip_context->in_have, (int64_t)k)
if (zip_read_at(state->archive_file,
zip_context->tmpbuf + zip_context->in_have, (int64_t)k,
(int64_t)(zip_context->fdoffset + zip_context->boffset))
!= (int64_t)k)
return -1;
zip_context->boffset += k;
@@ -1068,12 +1073,11 @@ static int zlib_stream_decompress_data_to_file_iterate(
? RZSTD_PROCESS_END : RZSTD_PROCESS_ERROR;
else
{
filestream_seek(state->archive_file,
(int64_t)(zip_context->fdoffset + zip_context->boffset),
RETRO_VFS_SEEK_POSITION_START);
if ( want > zip_context->csize - zip_context->boffset
|| filestream_read(state->archive_file, zip_context->tmpbuf,
(int64_t)want) != (int64_t)want
|| zip_read_at(state->archive_file, zip_context->tmpbuf,
(int64_t)want,
(int64_t)(zip_context->fdoffset + zip_context->boffset))
!= (int64_t)want
|| rzstd_blocks_give((rzstd_blocks_t*)zip_context->zs,
zip_context->tmpbuf, want, &blk, &len)
!= RZSTD_PROCESS_NEXT)
@@ -1445,9 +1449,8 @@ static int zip_parse_file_init(file_archive_transfer_t *state,
else
read_pos = MAX(read_pos - read_block + 21, 0);
/* Seek to read_pos and read read_block bytes. */
filestream_seek(state->archive_file, read_pos, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, footer_buf, read_block) != read_block)
if (zip_read_at(state->archive_file, footer_buf, read_block,
read_pos) != read_block)
return -1;
footer = footer_buf + read_block - 22;
@@ -1489,9 +1492,7 @@ static int zip_parse_file_init(file_archive_transfer_t *state,
loc = map + (size_t)(at - 20);
else
{
filestream_seek(state->archive_file, at - 20,
RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, loc_buf, 20) != 20)
if (zip_read_at(state->archive_file, loc_buf, 20, at - 20) != 20)
return -1;
}
if (read_le(loc, 4) != ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIGNATURE)
@@ -1503,9 +1504,8 @@ static int zip_parse_file_init(file_archive_transfer_t *state,
rec = map + (size_t)rec_off;
else
{
filestream_seek(state->archive_file, (int64_t)rec_off,
RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, rec_buf, 56) != 56)
if (zip_read_at(state->archive_file, rec_buf, 56, (int64_t)rec_off)
!= 56)
return -1;
}
if (read_le(rec, 4) != ZIP64_END_OF_CENTRAL_DIR_SIGNATURE)
@@ -1576,8 +1576,8 @@ static int zip_parse_file_init(file_archive_transfer_t *state,
if (!map)
{
filestream_seek(state->archive_file, directory_offset, RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, zip_context->directory, directory_size) != directory_size)
if (zip_read_at(state->archive_file, zip_context->directory,
directory_size, directory_offset) != directory_size)
{
free(zip_context);
return -1;
@@ -1837,9 +1837,8 @@ static int file_archive_entry_source_capture(
else
#endif
{
filestream_seek(state->archive_file, (int64_t)local + 26,
RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(state->archive_file, local_header_buf, 4) != 4)
if (zip_read_at(state->archive_file, local_header_buf, 4,
(int64_t)local + 26) != 4)
return -1;
local_header = local_header_buf;
}
@@ -1964,10 +1963,8 @@ int64_t file_archive_entry_source_read(file_archive_entry_source_t *s,
else
#endif
{
filestream_seek(s->state.archive_file,
(int64_t)(s->fdoffset + s->out_off),
RETRO_VFS_SEEK_POSITION_START);
if (filestream_read(s->state.archive_file, dst, n) != n)
if (zip_read_at(s->state.archive_file, dst, n,
(int64_t)(s->fdoffset + s->out_off)) != n)
return -1;
}
s->out_off += (uint64_t)n;
+13 -11
View File
@@ -832,7 +832,7 @@ static void config_file_get_realpath(char *s, size_t len,
}
}
static void config_file_add_sub_conf(config_file_t *conf, char *path,
static bool config_file_add_sub_conf(config_file_t *conf, char *path,
char *s, size_t len, config_file_cb_t *cb)
{
struct config_include_list *node = (struct config_include_list*)
@@ -841,12 +841,7 @@ static void config_file_add_sub_conf(config_file_t *conf, char *path,
if (node)
{
node->next = NULL;
/* Add include list */
if (!(node->path = strdup(path)))
{
free(node);
goto realpath;
}
node->path = path;
/* Order is user-visible - config_file_dump() writes the
* '#include' lines back in list order - so this appends rather
@@ -859,9 +854,9 @@ static void config_file_add_sub_conf(config_file_t *conf, char *path,
conf->includes_tail = node;
}
realpath:
config_file_get_realpath(s, len, path,
conf->path);
return node != NULL;
}
size_t config_file_add_reference(config_file_t *conf, char *path)
@@ -1128,7 +1123,7 @@ static bool config_file_parse_line(config_file_t *conf,
if (include_found)
{
config_file_t sub_conf;
char real_path[PATH_MAX_LENGTH];
char *real_path;
char *include_line = comment + (sizeof("include ")-1);
if (*include_line == '\0')
return false;
@@ -1140,8 +1135,14 @@ static bool config_file_parse_line(config_file_t *conf,
free(path);
return false;
}
config_file_add_sub_conf(conf, path,
real_path, sizeof(real_path), cb);
if (!(real_path = (char*)malloc(PATH_MAX_LENGTH)))
{
free(path);
return false;
}
if (config_file_add_sub_conf(conf, path,
real_path, PATH_MAX_LENGTH, cb))
path = NULL; /* The include list owns the parsed path. */
config_file_initialize(&sub_conf);
/* Includes are fetched the same way their parent was. */
sub_conf.io = conf->io;
@@ -1169,6 +1170,7 @@ static bool config_file_parse_line(config_file_t *conf,
* NULL/transferred, so the call is equally correct
* there. */
config_file_deinitialize(&sub_conf);
free(real_path);
}
/* Starting a line with an 'reference' directive
* sets the reference path */
+168
View File
@@ -254,6 +254,174 @@ bool path_rmdir(const char *dir)
return retro_vfs_rmdir_impl(dir) == 0;
}
static bool path_is_dot_entry(const char *name)
{
return name[0] == '.'
&& (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
}
/* Appends the separator and @name to the @len characters of @path,
* a PATH_MAX_LENGTH buffer. Returns the new length, or 0 if it
* does not fit. */
static size_t path_tree_append(char *path, size_t len, const char *name)
{
size_t name_len = strlen(name);
if (len + 1 + name_len >= PATH_MAX_LENGTH)
return 0;
path[len] = PATH_DEFAULT_SLASH_C();
memcpy(path + len + 1, name, name_len + 1);
return len + 1 + name_len;
}
/* Drops trailing separators, keeping a root's. Returns the length. */
static size_t path_tree_trim(char *path)
{
size_t len = strlen(path);
while (len > 1 && (path[len - 1] == '/' || path[len - 1] == '\\')
&& path[len - 2] != ':')
path[--len] = '\0';
return len;
}
/* Empties and removes the directory in the first @len characters of
* @path, which grows in place with each entry's name. */
static bool path_remove_tree_dir(char *path, size_t len)
{
bool ok = true;
libretro_vfs_implementation_dir *dir =
retro_vfs_opendir_impl(path, true);
if (!dir)
return false;
while (retro_vfs_readdir_impl(dir))
{
size_t entry_len;
const char *name = retro_vfs_dirent_get_name_impl(dir);
if (!name || path_is_dot_entry(name))
continue;
if (!(entry_len = path_tree_append(path, len, name)))
{
ok = false;
continue;
}
if (retro_vfs_dirent_is_dir_impl(dir))
{
/* An empty directory goes at once, and so does a link to
* one (rmdir on Windows, unlink elsewhere): only a real
* directory with something in it is walked. */
if ( retro_vfs_rmdir_impl(path) != 0
&& retro_vfs_file_remove_impl(path) != 0
&& !path_remove_tree_dir(path, entry_len))
ok = false;
}
else if (retro_vfs_file_remove_impl(path) != 0)
ok = false;
}
retro_vfs_closedir_impl(dir);
path[len] = '\0';
return retro_vfs_rmdir_impl(path) == 0 && ok;
}
bool path_remove_tree(const char *path)
{
bool ok;
size_t len;
char *buf;
if (!path || !*path)
return false;
/* A file, a link or an empty directory: no walk */
if ( retro_vfs_rmdir_impl(path) == 0
|| retro_vfs_file_remove_impl(path) == 0
|| !path_is_valid(path))
return true;
if (!path_is_directory(path))
return false;
if (!(buf = (char*)malloc(PATH_MAX_LENGTH)))
return false;
strlcpy(buf, path, PATH_MAX_LENGTH);
len = path_tree_trim(buf);
ok = path_remove_tree_dir(buf, len);
free(buf);
return ok;
}
static bool path_copy_tree_file(const char *src, const char *dst)
{
struct retro_vfs_copy_handle *copy =
retro_vfs_copy_begin_impl(src, dst, RETRO_VFS_COPY_OVERWRITE);
if (!copy)
return false;
/* The whole file in one step: the platform's copy primitive at
* full speed, no buffer of ours */
while (retro_vfs_copy_step_impl(copy, (int64_t)1 << 62, NULL, NULL)
== RETRO_VFS_COPY_RUNNING) { }
return retro_vfs_copy_close_impl(copy) == 0;
}
/* Copies the directory in the first @src_len characters of @src into
* the one in the first @dst_len of @dst; both grow in place. */
static bool path_copy_tree_dir(char *src, size_t src_len,
char *dst, size_t dst_len)
{
bool ok = true;
libretro_vfs_implementation_dir *dir;
if (!path_is_directory(dst) && !path_mkdir(dst))
return false;
if (!(dir = retro_vfs_opendir_impl(src, true)))
return false;
while (retro_vfs_readdir_impl(dir))
{
size_t src_entry, dst_entry;
const char *name = retro_vfs_dirent_get_name_impl(dir);
if (!name || path_is_dot_entry(name))
continue;
if ( !(src_entry = path_tree_append(src, src_len, name))
|| !(dst_entry = path_tree_append(dst, dst_len, name)))
ok = false;
else if (retro_vfs_dirent_is_dir_impl(dir))
{
if (!path_copy_tree_dir(src, src_entry, dst, dst_entry))
ok = false;
}
else if (!path_copy_tree_file(src, dst))
ok = false;
src[src_len] = '\0';
dst[dst_len] = '\0';
}
retro_vfs_closedir_impl(dir);
return ok;
}
bool path_copy_tree(const char *src, const char *dst)
{
bool ok;
char *src_buf;
char *dst_buf;
if (!src || !*src || !dst || !*dst)
return false;
if (!path_is_directory(src))
return path_copy_tree_file(src, dst);
if (!(src_buf = (char*)malloc(2 * PATH_MAX_LENGTH)))
return false;
dst_buf = src_buf + PATH_MAX_LENGTH;
strlcpy(src_buf, src, PATH_MAX_LENGTH);
strlcpy(dst_buf, dst, PATH_MAX_LENGTH);
ok = path_copy_tree_dir(src_buf, path_tree_trim(src_buf),
dst_buf, path_tree_trim(dst_buf));
free(src_buf);
return ok;
}
/**
* path_mkdir:
* @dir : directory
+57 -34
View File
@@ -396,12 +396,13 @@ struct r7z_archive
/* What a reader on another thread may look at while one thread calls
* r7z_archive_decode_step(): the buffer the folder is being decoded
* into in order, and how much of it is final. prog_state is 0 when
* there is nothing to look at, 1 while it is being decoded, 2 when
* the folder is decoded and held; the buffer and the folder are
* written before the state says they are there. */
const uint8_t *prog_buf;
uint32_t prog_folder;
* into in order, and how much of it is final. prog_state's low two
* bits are 0 when there is nothing to look at, 1 while it is being
* decoded, 2 when the folder is decoded and held; the rest count the
* changes, so that a reader that finds it the same after reading the
* others read them all from one (see r7z_progress_state()). */
retro_atomic_size_t prog_buf;
retro_atomic_size_t prog_folder;
retro_atomic_int_t prog_state;
retro_atomic_size_t prog_pos;
@@ -2332,6 +2333,26 @@ static int decode_encoded_header(r7z_archive_t *a, rd_t *r,
return res;
}
/* A new state, the count of changes moved on. Only the decoding thread
* writes it. */
static void r7z_progress_state(r7z_archive_t *a, int state)
{
unsigned n = (unsigned)retro_atomic_load_relaxed_int(&a->prog_state) >> 2;
retro_atomic_store_release_int(&a->prog_state,
(int)((((n + 1u) & 0x1FFFFFFFu) << 2) | (unsigned)state));
}
/* What a reader is to see from now on: nothing while it is written */
static void r7z_progress_set(r7z_archive_t *a, const uint8_t *buf,
uint32_t folder, size_t pos, int state)
{
r7z_progress_state(a, 0);
retro_atomic_store_release_size(&a->prog_buf, (size_t)(uintptr_t)buf);
retro_atomic_store_release_size(&a->prog_folder, folder);
retro_atomic_store_release_size(&a->prog_pos, pos);
r7z_progress_state(a, state);
}
/* The archive in memory (@data), or read through @read_cb. */
static int r7z_open(r7z_archive_t **out, const uint8_t *data, size_t len,
r7z_read_t read_cb, void *ud)
@@ -2386,6 +2407,8 @@ static int r7z_open(r7z_archive_t **out, const uint8_t *data, size_t len,
a->len = len;
a->read_cb = read_cb;
a->read_ud = ud;
retro_atomic_size_init(&a->prog_buf, 0);
retro_atomic_size_init(&a->prog_folder, 0);
retro_atomic_int_init(&a->prog_state, 0);
retro_atomic_size_init(&a->prog_pos, 0);
a->cached_folder = 0xFFFFFFFFu;
@@ -2471,7 +2494,7 @@ void r7z_archive_close(r7z_archive_t *a)
if (!a)
return;
decode_pending_reset(a);
retro_atomic_store_release_int(&a->prog_state, 0);
r7z_progress_state(a, 0);
if (!a->cached_borrowed)
free(a->cached_data);
#ifdef HAVE_RBZIP2
@@ -2555,8 +2578,8 @@ static void decode_pending_reset(r7z_archive_t *a)
if (!a)
return;
/* (a folder decoded and held stays to be looked at: 2) */
if (retro_atomic_load_acquire_int(&a->prog_state) == 1)
retro_atomic_store_release_int(&a->prog_state, 0);
if ((retro_atomic_load_relaxed_int(&a->prog_state) & 3) == 1)
r7z_progress_state(a, 0);
if (!a->pend_in_borrowed)
free(a->pend_in);
if (!a->pend_pass)
@@ -2614,10 +2637,7 @@ static int coder_is_resumable(const coder_t *c)
/* The cached folder, decoded whole and checked, is there to be read. */
static void r7z_progress_held(r7z_archive_t *a)
{
a->prog_buf = a->cached_data;
a->prog_folder = a->cached_folder;
retro_atomic_store_release_size(&a->prog_pos, a->cached_len);
retro_atomic_store_release_int(&a->prog_state, 2);
r7z_progress_set(a, a->cached_data, a->cached_folder, a->cached_len, 2);
}
/* The stage that is being decoded writes its output in order, and is the
@@ -2625,7 +2645,7 @@ static void r7z_progress_held(r7z_archive_t *a)
* to be read (r7z_archive_decode_peek()). */
static void r7z_progress_at(r7z_archive_t *a, size_t pos)
{
if (retro_atomic_load_acquire_int(&a->prog_state) == 1)
if ((retro_atomic_load_relaxed_int(&a->prog_state) & 3) == 1)
retro_atomic_store_release_size(&a->prog_pos, pos);
}
@@ -2829,10 +2849,7 @@ static int decode_stage_begin(r7z_archive_t *a, const folder_t *f)
* folder will be held in: its progress can be watched. */
if (a->pend_coder + 1 == f->num_coders)
{
a->prog_buf = a->pend_out;
a->prog_folder = a->pend_folder;
retro_atomic_store_release_size(&a->prog_pos, 0);
retro_atomic_store_release_int(&a->prog_state, 1);
r7z_progress_set(a, a->pend_out, a->pend_folder, 0, 1);
}
/* Stand up a decoder that survives between slices. */
@@ -3238,7 +3255,7 @@ static int decode_folder_bcj2_slice(r7z_archive_t *a, uint32_t fi,
return R7Z_ERROR_CRC;
}
retro_atomic_store_release_int(&a->prog_state, 0);
r7z_progress_state(a, 0);
r7z_cache_hold(a, a->pend_b2dst, a->pend_b2dstlen, fi, 0);
a->pend_b2dst = NULL;
@@ -3405,7 +3422,7 @@ static int decode_folder_slice(r7z_archive_t *a, uint32_t fi, int *done)
/* a folder no coder touched is held where it lies, when the
* archive is in memory */
retro_atomic_store_release_int(&a->prog_state, 0);
r7z_progress_state(a, 0);
r7z_cache_hold(a, a->pend_in, a->pend_in_len, fi, a->pend_in_borrowed);
r7z_progress_held(a);
@@ -3502,7 +3519,7 @@ int r7z_archive_extract_slice(r7z_archive_t *a, uint32_t index,
if ((res = decode_folder(a, e->folder, &folder_data, &folder_borrowed)) != R7Z_OK)
return res;
retro_atomic_store_release_int(&a->prog_state, 0);
r7z_progress_state(a, 0);
r7z_cache_hold(a, folder_data,
(size_t)a->folders[e->folder].unpack_size, e->folder,
folder_borrowed);
@@ -3563,7 +3580,7 @@ int r7z_archive_extract(r7z_archive_t *a, uint32_t index,
if (res != R7Z_OK)
return res;
retro_atomic_store_release_int(&a->prog_state, 0);
r7z_progress_state(a, 0);
r7z_cache_hold(a, folder_data,
(size_t)a->folders[e->folder].unpack_size, e->folder,
folder_borrowed);
@@ -3634,7 +3651,7 @@ int r7z_archive_extract_detach(r7z_archive_t *a, uint32_t index,
else
{
int borrowed = 0;
retro_atomic_store_release_int(&a->prog_state, 0);
r7z_progress_state(a, 0);
r7z_cache_hold(a, NULL, 0, 0xFFFFFFFFu, 0);
if ((res = decode_folder(a, e->folder, &buf, &borrowed)) != R7Z_OK)
return res;
@@ -3647,7 +3664,7 @@ int r7z_archive_extract_detach(r7z_archive_t *a, uint32_t index,
/* The folder's output is the member: hand the buffer over rather
* than keep it cached and copy it. */
retro_atomic_store_release_int(&a->prog_state, 0);
r7z_progress_state(a, 0);
a->cached_data = NULL;
a->cached_len = 0;
a->cached_folder = 0xFFFFFFFFu;
@@ -3694,7 +3711,7 @@ int r7z_archive_entry_borrow(r7z_archive_t *a, uint32_t index,
{
if ((res = decode_folder(a, e->folder, &folder_data, &folder_borrowed)) != R7Z_OK)
return res;
retro_atomic_store_release_int(&a->prog_state, 0);
r7z_progress_state(a, 0);
r7z_cache_hold(a, folder_data,
(size_t)a->folders[e->folder].unpack_size, e->folder,
folder_borrowed);
@@ -3736,7 +3753,7 @@ int r7z_archive_decode_step(r7z_archive_t *a, uint32_t index)
if (a->cached_folder == e->folder && a->cached_data)
{
if (retro_atomic_load_acquire_int(&a->prog_state) != 2)
if ((retro_atomic_load_relaxed_int(&a->prog_state) & 3) != 2)
r7z_progress_held(a);
return R7Z_OK;
}
@@ -3750,7 +3767,7 @@ int r7z_archive_decode_step(r7z_archive_t *a, uint32_t index)
if ((res = decode_folder(a, e->folder, &folder_data, &folder_borrowed)) != R7Z_OK)
return res;
retro_atomic_store_release_int(&a->prog_state, 0);
r7z_progress_state(a, 0);
r7z_cache_hold(a, folder_data,
(size_t)a->folders[e->folder].unpack_size, e->folder,
folder_borrowed);
@@ -3762,7 +3779,7 @@ int r7z_archive_decode_step(r7z_archive_t *a, uint32_t index)
if (!done)
return R7Z_PENDING;
/* (a folder that went through the BCJ2 slicer says so only now) */
if (retro_atomic_load_acquire_int(&a->prog_state) != 2)
if ((retro_atomic_load_relaxed_int(&a->prog_state) & 3) != 2)
r7z_progress_held(a);
return R7Z_OK;
}
@@ -4805,7 +4822,7 @@ int r7z_archive_decode_peek(const r7z_archive_t *a, uint32_t index,
const uint8_t **data, size_t *avail)
{
const r7z_entry_t *e;
size_t pos;
size_t pos, buf, folder;
int state;
if (!a || !data || !avail || index >= a->num_entries)
@@ -4816,14 +4833,20 @@ int r7z_archive_decode_peek(const r7z_archive_t *a, uint32_t index,
if (e->is_dir)
return R7Z_ERROR_PARAM;
/* the state, and then what it says is there */
state = retro_atomic_load_acquire_int((retro_atomic_int_t*)&a->prog_state);
if (!state || a->prog_folder != e->folder)
/* the state, what it says is there, and the state again: changed,
* and what was read may be from before and after */
state = retro_atomic_load_acquire_int((retro_atomic_int_t*)&a->prog_state);
if (!(state & 3))
return R7Z_OK;
folder = retro_atomic_load_acquire_size((retro_atomic_size_t*)&a->prog_folder);
buf = retro_atomic_load_acquire_size((retro_atomic_size_t*)&a->prog_buf);
pos = retro_atomic_load_acquire_size((retro_atomic_size_t*)&a->prog_pos);
if (retro_atomic_load_acquire_int((retro_atomic_int_t*)&a->prog_state) != state
|| folder != e->folder)
return R7Z_OK;
pos = retro_atomic_load_acquire_size((retro_atomic_size_t*)&a->prog_pos);
if (e->offset_in_folder > (uint64_t)((size_t)-1))
return R7Z_ERROR_DATA;
*data = a->prog_buf + (size_t)e->offset_in_folder;
*data = (const uint8_t*)(uintptr_t)buf + (size_t)e->offset_in_folder;
if (pos > (size_t)e->offset_in_folder)
{
size_t have = pos - (size_t)e->offset_in_folder;
+2 -385
View File
@@ -216,384 +216,8 @@ static INLINE raac_v raac_v_set4(float a, float b, float c, float e)
#define RAAC_CB_INTENSITY 15
#define RAAC_CB_NOISE 13
static const uint16_t raac_hcb1_code[81] = {
0x07f8, 0x01f1, 0x07fd, 0x03f5, 0x0068, 0x03f0, 0x07f7, 0x01ec,
0x07f5, 0x03f1, 0x0072, 0x03f4, 0x0074, 0x0011, 0x0076, 0x01eb,
0x006c, 0x03f6, 0x07fc, 0x01e1, 0x07f1, 0x01f0, 0x0061, 0x01f6,
0x07f2, 0x01ea, 0x07fb, 0x01f2, 0x0069, 0x01ed, 0x0077, 0x0017,
0x006f, 0x01e6, 0x0064, 0x01e5, 0x0067, 0x0015, 0x0062, 0x0012,
0x0000, 0x0014, 0x0065, 0x0016, 0x006d, 0x01e9, 0x0063, 0x01e4,
0x006b, 0x0013, 0x0071, 0x01e3, 0x0070, 0x01f3, 0x07fe, 0x01e7,
0x07f3, 0x01ef, 0x0060, 0x01ee, 0x07f0, 0x01e2, 0x07fa, 0x03f3,
0x006a, 0x01e8, 0x0075, 0x0010, 0x0073, 0x01f4, 0x006e, 0x03f7,
0x07f6, 0x01e0, 0x07f9, 0x03f2, 0x0066, 0x01f5, 0x07ff, 0x01f7,
0x07f4
};
static const uint8_t raac_hcb1_bits[81] = {
11, 9, 11, 10, 7, 10, 11, 9, 11, 10, 7, 10, 7, 5, 7, 9,
7, 10, 11, 9, 11, 9, 7, 9, 11, 9, 11, 9, 7, 9, 7, 5,
7, 9, 7, 9, 7, 5, 7, 5, 1, 5, 7, 5, 7, 9, 7, 9,
7, 5, 7, 9, 7, 9, 11, 9, 11, 9, 7, 9, 11, 9, 11, 10,
7, 9, 7, 5, 7, 9, 7, 10, 11, 9, 11, 10, 7, 9, 11, 9,
11
};
static const uint16_t raac_hcb2_code[81] = {
0x01f3, 0x006f, 0x01fd, 0x00eb, 0x0023, 0x00ea, 0x01f7, 0x00e8,
0x01fa, 0x00f2, 0x002d, 0x0070, 0x0020, 0x0006, 0x002b, 0x006e,
0x0028, 0x00e9, 0x01f9, 0x0066, 0x00f8, 0x00e7, 0x001b, 0x00f1,
0x01f4, 0x006b, 0x01f5, 0x00ec, 0x002a, 0x006c, 0x002c, 0x000a,
0x0027, 0x0067, 0x001a, 0x00f5, 0x0024, 0x0008, 0x001f, 0x0009,
0x0000, 0x0007, 0x001d, 0x000b, 0x0030, 0x00ef, 0x001c, 0x0064,
0x001e, 0x000c, 0x0029, 0x00f3, 0x002f, 0x00f0, 0x01fc, 0x0071,
0x01f2, 0x00f4, 0x0021, 0x00e6, 0x00f7, 0x0068, 0x01f8, 0x00ee,
0x0022, 0x0065, 0x0031, 0x0002, 0x0026, 0x00ed, 0x0025, 0x006a,
0x01fb, 0x0072, 0x01fe, 0x0069, 0x002e, 0x00f6, 0x01ff, 0x006d,
0x01f6
};
static const uint8_t raac_hcb2_bits[81] = {
9, 7, 9, 8, 6, 8, 9, 8, 9, 8, 6, 7, 6, 5, 6, 7,
6, 8, 9, 7, 8, 8, 6, 8, 9, 7, 9, 8, 6, 7, 6, 5,
6, 7, 6, 8, 6, 5, 6, 5, 3, 5, 6, 5, 6, 8, 6, 7,
6, 5, 6, 8, 6, 8, 9, 7, 9, 8, 6, 8, 8, 7, 9, 8,
6, 7, 6, 4, 6, 8, 6, 7, 9, 7, 9, 7, 6, 8, 9, 7,
9
};
static const uint16_t raac_hcb3_code[81] = {
0x0000, 0x0009, 0x00ef, 0x000b, 0x0019, 0x00f0, 0x01eb, 0x01e6,
0x03f2, 0x000a, 0x0035, 0x01ef, 0x0034, 0x0037, 0x01e9, 0x01ed,
0x01e7, 0x03f3, 0x01ee, 0x03ed, 0x1ffa, 0x01ec, 0x01f2, 0x07f9,
0x07f8, 0x03f8, 0x0ff8, 0x0008, 0x0038, 0x03f6, 0x0036, 0x0075,
0x03f1, 0x03eb, 0x03ec, 0x0ff4, 0x0018, 0x0076, 0x07f4, 0x0039,
0x0074, 0x03ef, 0x01f3, 0x01f4, 0x07f6, 0x01e8, 0x03ea, 0x1ffc,
0x00f2, 0x01f1, 0x0ffb, 0x03f5, 0x07f3, 0x0ffc, 0x00ee, 0x03f7,
0x7ffe, 0x01f0, 0x07f5, 0x7ffd, 0x1ffb, 0x3ffa, 0xffff, 0x00f1,
0x03f0, 0x3ffc, 0x01ea, 0x03ee, 0x3ffb, 0x0ff6, 0x0ffa, 0x7ffc,
0x07f2, 0x0ff5, 0xfffe, 0x03f4, 0x07f7, 0x7ffb, 0x0ff7, 0x0ff9,
0x7ffa
};
static const uint8_t raac_hcb3_bits[81] = {
1, 4, 8, 4, 5, 8, 9, 9, 10, 4, 6, 9, 6, 6, 9, 9,
9, 10, 9, 10, 13, 9, 9, 11, 11, 10, 12, 4, 6, 10, 6, 7,
10, 10, 10, 12, 5, 7, 11, 6, 7, 10, 9, 9, 11, 9, 10, 13,
8, 9, 12, 10, 11, 12, 8, 10, 15, 9, 11, 15, 13, 14, 16, 8,
10, 14, 9, 10, 14, 12, 12, 15, 11, 12, 16, 10, 11, 15, 12, 12,
15
};
static const uint16_t raac_hcb4_code[81] = {
0x0007, 0x0016, 0x00f6, 0x0018, 0x0008, 0x00ef, 0x01ef, 0x00f3,
0x07f8, 0x0019, 0x0017, 0x00ed, 0x0015, 0x0001, 0x00e2, 0x00f0,
0x0070, 0x03f0, 0x01ee, 0x00f1, 0x07fa, 0x00ee, 0x00e4, 0x03f2,
0x07f6, 0x03ef, 0x07fd, 0x0005, 0x0014, 0x00f2, 0x0009, 0x0004,
0x00e5, 0x00f4, 0x00e8, 0x03f4, 0x0006, 0x0002, 0x00e7, 0x0003,
0x0000, 0x006b, 0x00e3, 0x0069, 0x01f3, 0x00eb, 0x00e6, 0x03f6,
0x006e, 0x006a, 0x01f4, 0x03ec, 0x01f0, 0x03f9, 0x00f5, 0x00ec,
0x07fb, 0x00ea, 0x006f, 0x03f7, 0x07f9, 0x03f3, 0x0fff, 0x00e9,
0x006d, 0x03f8, 0x006c, 0x0068, 0x01f5, 0x03ee, 0x01f2, 0x07f4,
0x07f7, 0x03f1, 0x0ffe, 0x03ed, 0x01f1, 0x07f5, 0x07fe, 0x03f5,
0x07fc
};
static const uint8_t raac_hcb4_bits[81] = {
4, 5, 8, 5, 4, 8, 9, 8, 11, 5, 5, 8, 5, 4, 8, 8,
7, 10, 9, 8, 11, 8, 8, 10, 11, 10, 11, 4, 5, 8, 4, 4,
8, 8, 8, 10, 4, 4, 8, 4, 4, 7, 8, 7, 9, 8, 8, 10,
7, 7, 9, 10, 9, 10, 8, 8, 11, 8, 7, 10, 11, 10, 12, 8,
7, 10, 7, 7, 9, 10, 9, 11, 11, 10, 12, 10, 9, 11, 11, 10,
11
};
static const uint16_t raac_hcb5_code[81] = {
0x1fff, 0x0ff7, 0x07f4, 0x07e8, 0x03f1, 0x07ee, 0x07f9, 0x0ff8,
0x1ffd, 0x0ffd, 0x07f1, 0x03e8, 0x01e8, 0x00f0, 0x01ec, 0x03ee,
0x07f2, 0x0ffa, 0x0ff4, 0x03ef, 0x01f2, 0x00e8, 0x0070, 0x00ec,
0x01f0, 0x03ea, 0x07f3, 0x07eb, 0x01eb, 0x00ea, 0x001a, 0x0008,
0x0019, 0x00ee, 0x01ef, 0x07ed, 0x03f0, 0x00f2, 0x0073, 0x000b,
0x0000, 0x000a, 0x0071, 0x00f3, 0x07e9, 0x07ef, 0x01ee, 0x00ef,
0x0018, 0x0009, 0x001b, 0x00eb, 0x01e9, 0x07ec, 0x07f6, 0x03eb,
0x01f3, 0x00ed, 0x0072, 0x00e9, 0x01f1, 0x03ed, 0x07f7, 0x0ff6,
0x07f0, 0x03e9, 0x01ed, 0x00f1, 0x01ea, 0x03ec, 0x07f8, 0x0ff9,
0x1ffc, 0x0ffc, 0x0ff5, 0x07ea, 0x03f3, 0x03f2, 0x07f5, 0x0ffb,
0x1ffe
};
static const uint8_t raac_hcb5_bits[81] = {
13, 12, 11, 11, 10, 11, 11, 12, 13, 12, 11, 10, 9, 8, 9, 10,
11, 12, 12, 10, 9, 8, 7, 8, 9, 10, 11, 11, 9, 8, 5, 4,
5, 8, 9, 11, 10, 8, 7, 4, 1, 4, 7, 8, 11, 11, 9, 8,
5, 4, 5, 8, 9, 11, 11, 10, 9, 8, 7, 8, 9, 10, 11, 12,
11, 10, 9, 8, 9, 10, 11, 12, 13, 12, 12, 11, 10, 10, 11, 12,
13
};
static const uint16_t raac_hcb6_code[81] = {
0x07fe, 0x03fd, 0x01f1, 0x01eb, 0x01f4, 0x01ea, 0x01f0, 0x03fc,
0x07fd, 0x03f6, 0x01e5, 0x00ea, 0x006c, 0x0071, 0x0068, 0x00f0,
0x01e6, 0x03f7, 0x01f3, 0x00ef, 0x0032, 0x0027, 0x0028, 0x0026,
0x0031, 0x00eb, 0x01f7, 0x01e8, 0x006f, 0x002e, 0x0008, 0x0004,
0x0006, 0x0029, 0x006b, 0x01ee, 0x01ef, 0x0072, 0x002d, 0x0002,
0x0000, 0x0003, 0x002f, 0x0073, 0x01fa, 0x01e7, 0x006e, 0x002b,
0x0007, 0x0001, 0x0005, 0x002c, 0x006d, 0x01ec, 0x01f9, 0x00ee,
0x0030, 0x0024, 0x002a, 0x0025, 0x0033, 0x00ec, 0x01f2, 0x03f8,
0x01e4, 0x00ed, 0x006a, 0x0070, 0x0069, 0x0074, 0x00f1, 0x03fa,
0x07ff, 0x03f9, 0x01f6, 0x01ed, 0x01f8, 0x01e9, 0x01f5, 0x03fb,
0x07fc
};
static const uint8_t raac_hcb6_bits[81] = {
11, 10, 9, 9, 9, 9, 9, 10, 11, 10, 9, 8, 7, 7, 7, 8,
9, 10, 9, 8, 6, 6, 6, 6, 6, 8, 9, 9, 7, 6, 4, 4,
4, 6, 7, 9, 9, 7, 6, 4, 4, 4, 6, 7, 9, 9, 7, 6,
4, 4, 4, 6, 7, 9, 9, 8, 6, 6, 6, 6, 6, 8, 9, 10,
9, 8, 7, 7, 7, 7, 8, 10, 11, 10, 9, 9, 9, 9, 9, 10,
11
};
static const uint16_t raac_hcb7_code[64] = {
0x0000, 0x0005, 0x0037, 0x0074, 0x00f2, 0x01eb, 0x03ed, 0x07f7,
0x0004, 0x000c, 0x0035, 0x0071, 0x00ec, 0x00ee, 0x01ee, 0x01f5,
0x0036, 0x0034, 0x0072, 0x00ea, 0x00f1, 0x01e9, 0x01f3, 0x03f5,
0x0073, 0x0070, 0x00eb, 0x00f0, 0x01f1, 0x01f0, 0x03ec, 0x03fa,
0x00f3, 0x00ed, 0x01e8, 0x01ef, 0x03ef, 0x03f1, 0x03f9, 0x07fb,
0x01ed, 0x00ef, 0x01ea, 0x01f2, 0x03f3, 0x03f8, 0x07f9, 0x07fc,
0x03ee, 0x01ec, 0x01f4, 0x03f4, 0x03f7, 0x07f8, 0x0ffd, 0x0ffe,
0x07f6, 0x03f0, 0x03f2, 0x03f6, 0x07fa, 0x07fd, 0x0ffc, 0x0fff
};
static const uint8_t raac_hcb7_bits[64] = {
1, 3, 6, 7, 8, 9, 10, 11, 3, 4, 6, 7, 8, 8, 9, 9,
6, 6, 7, 8, 8, 9, 9, 10, 7, 7, 8, 8, 9, 9, 10, 10,
8, 8, 9, 9, 10, 10, 10, 11, 9, 8, 9, 9, 10, 10, 11, 11,
10, 9, 9, 10, 10, 11, 12, 12, 11, 10, 10, 10, 11, 11, 12, 12
};
static const uint16_t raac_hcb8_code[64] = {
0x000e, 0x0005, 0x0010, 0x0030, 0x006f, 0x00f1, 0x01fa, 0x03fe,
0x0003, 0x0000, 0x0004, 0x0012, 0x002c, 0x006a, 0x0075, 0x00f8,
0x000f, 0x0002, 0x0006, 0x0014, 0x002e, 0x0069, 0x0072, 0x00f5,
0x002f, 0x0011, 0x0013, 0x002a, 0x0032, 0x006c, 0x00ec, 0x00fa,
0x0071, 0x002b, 0x002d, 0x0031, 0x006d, 0x0070, 0x00f2, 0x01f9,
0x00ef, 0x0068, 0x0033, 0x006b, 0x006e, 0x00ee, 0x00f9, 0x03fc,
0x01f8, 0x0074, 0x0073, 0x00ed, 0x00f0, 0x00f6, 0x01f6, 0x01fd,
0x03fd, 0x00f3, 0x00f4, 0x00f7, 0x01f7, 0x01fb, 0x01fc, 0x03ff
};
static const uint8_t raac_hcb8_bits[64] = {
5, 4, 5, 6, 7, 8, 9, 10, 4, 3, 4, 5, 6, 7, 7, 8,
5, 4, 4, 5, 6, 7, 7, 8, 6, 5, 5, 6, 6, 7, 8, 8,
7, 6, 6, 6, 7, 7, 8, 9, 8, 7, 6, 7, 7, 8, 8, 10,
9, 7, 7, 8, 8, 8, 9, 9, 10, 8, 8, 8, 9, 9, 9, 10
};
static const uint16_t raac_hcb9_code[169] = {
0x0000, 0x0005, 0x0037, 0x00e7, 0x01de, 0x03ce, 0x03d9, 0x07c8,
0x07cd, 0x0fc8, 0x0fdd, 0x1fe4, 0x1fec, 0x0004, 0x000c, 0x0035,
0x0072, 0x00ea, 0x00ed, 0x01e2, 0x03d1, 0x03d3, 0x03e0, 0x07d8,
0x0fcf, 0x0fd5, 0x0036, 0x0034, 0x0071, 0x00e8, 0x00ec, 0x01e1,
0x03cf, 0x03dd, 0x03db, 0x07d0, 0x0fc7, 0x0fd4, 0x0fe4, 0x00e6,
0x0070, 0x00e9, 0x01dd, 0x01e3, 0x03d2, 0x03dc, 0x07cc, 0x07ca,
0x07de, 0x0fd8, 0x0fea, 0x1fdb, 0x01df, 0x00eb, 0x01dc, 0x01e6,
0x03d5, 0x03de, 0x07cb, 0x07dd, 0x07dc, 0x0fcd, 0x0fe2, 0x0fe7,
0x1fe1, 0x03d0, 0x01e0, 0x01e4, 0x03d6, 0x07c5, 0x07d1, 0x07db,
0x0fd2, 0x07e0, 0x0fd9, 0x0feb, 0x1fe3, 0x1fe9, 0x07c4, 0x01e5,
0x03d7, 0x07c6, 0x07cf, 0x07da, 0x0fcb, 0x0fda, 0x0fe3, 0x0fe9,
0x1fe6, 0x1ff3, 0x1ff7, 0x07d3, 0x03d8, 0x03e1, 0x07d4, 0x07d9,
0x0fd3, 0x0fde, 0x1fdd, 0x1fd9, 0x1fe2, 0x1fea, 0x1ff1, 0x1ff6,
0x07d2, 0x03d4, 0x03da, 0x07c7, 0x07d7, 0x07e2, 0x0fce, 0x0fdb,
0x1fd8, 0x1fee, 0x3ff0, 0x1ff4, 0x3ff2, 0x07e1, 0x03df, 0x07c9,
0x07d6, 0x0fca, 0x0fd0, 0x0fe5, 0x0fe6, 0x1feb, 0x1fef, 0x3ff3,
0x3ff4, 0x3ff5, 0x0fe0, 0x07ce, 0x07d5, 0x0fc6, 0x0fd1, 0x0fe1,
0x1fe0, 0x1fe8, 0x1ff0, 0x3ff1, 0x3ff8, 0x3ff6, 0x7ffc, 0x0fe8,
0x07df, 0x0fc9, 0x0fd7, 0x0fdc, 0x1fdc, 0x1fdf, 0x1fed, 0x1ff5,
0x3ff9, 0x3ffb, 0x7ffd, 0x7ffe, 0x1fe7, 0x0fcc, 0x0fd6, 0x0fdf,
0x1fde, 0x1fda, 0x1fe5, 0x1ff2, 0x3ffa, 0x3ff7, 0x3ffc, 0x3ffd,
0x7fff
};
static const uint8_t raac_hcb9_bits[169] = {
1, 3, 6, 8, 9, 10, 10, 11, 11, 12, 12, 13, 13, 3, 4, 6,
7, 8, 8, 9, 10, 10, 10, 11, 12, 12, 6, 6, 7, 8, 8, 9,
10, 10, 10, 11, 12, 12, 12, 8, 7, 8, 9, 9, 10, 10, 11, 11,
11, 12, 12, 13, 9, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 12,
13, 10, 9, 9, 10, 11, 11, 11, 12, 11, 12, 12, 13, 13, 11, 9,
10, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 11, 10, 10, 11, 11,
12, 12, 13, 13, 13, 13, 13, 13, 11, 10, 10, 11, 11, 11, 12, 12,
13, 13, 14, 13, 14, 11, 10, 11, 11, 12, 12, 12, 12, 13, 13, 14,
14, 14, 12, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 12,
11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 15, 15, 13, 12, 12, 12,
13, 13, 13, 13, 14, 14, 14, 14, 15
};
static const uint16_t raac_hcb10_code[169] = {
0x0022, 0x0008, 0x001d, 0x0026, 0x005f, 0x00d3, 0x01cf, 0x03d0,
0x03d7, 0x03ed, 0x07f0, 0x07f6, 0x0ffd, 0x0007, 0x0000, 0x0001,
0x0009, 0x0020, 0x0054, 0x0060, 0x00d5, 0x00dc, 0x01d4, 0x03cd,
0x03de, 0x07e7, 0x001c, 0x0002, 0x0006, 0x000c, 0x001e, 0x0028,
0x005b, 0x00cd, 0x00d9, 0x01ce, 0x01dc, 0x03d9, 0x03f1, 0x0025,
0x000b, 0x000a, 0x000d, 0x0024, 0x0057, 0x0061, 0x00cc, 0x00dd,
0x01cc, 0x01de, 0x03d3, 0x03e7, 0x005d, 0x0021, 0x001f, 0x0023,
0x0027, 0x0059, 0x0064, 0x00d8, 0x00df, 0x01d2, 0x01e2, 0x03dd,
0x03ee, 0x00d1, 0x0055, 0x0029, 0x0056, 0x0058, 0x0062, 0x00ce,
0x00e0, 0x00e2, 0x01da, 0x03d4, 0x03e3, 0x07eb, 0x01c9, 0x005e,
0x005a, 0x005c, 0x0063, 0x00ca, 0x00da, 0x01c7, 0x01ca, 0x01e0,
0x03db, 0x03e8, 0x07ec, 0x01e3, 0x00d2, 0x00cb, 0x00d0, 0x00d7,
0x00db, 0x01c6, 0x01d5, 0x01d8, 0x03ca, 0x03da, 0x07ea, 0x07f1,
0x01e1, 0x00d4, 0x00cf, 0x00d6, 0x00de, 0x00e1, 0x01d0, 0x01d6,
0x03d1, 0x03d5, 0x03f2, 0x07ee, 0x07fb, 0x03e9, 0x01cd, 0x01c8,
0x01cb, 0x01d1, 0x01d7, 0x01df, 0x03cf, 0x03e0, 0x03ef, 0x07e6,
0x07f8, 0x0ffa, 0x03eb, 0x01dd, 0x01d3, 0x01d9, 0x01db, 0x03d2,
0x03cc, 0x03dc, 0x03ea, 0x07ed, 0x07f3, 0x07f9, 0x0ff9, 0x07f2,
0x03ce, 0x01e4, 0x03cb, 0x03d8, 0x03d6, 0x03e2, 0x03e5, 0x07e8,
0x07f4, 0x07f5, 0x07f7, 0x0ffb, 0x07fa, 0x03ec, 0x03df, 0x03e1,
0x03e4, 0x03e6, 0x03f0, 0x07e9, 0x07ef, 0x0ff8, 0x0ffe, 0x0ffc,
0x0fff
};
static const uint8_t raac_hcb10_bits[169] = {
6, 5, 6, 6, 7, 8, 9, 10, 10, 10, 11, 11, 12, 5, 4, 4,
5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 6, 4, 5, 5, 6, 6,
7, 8, 8, 9, 9, 10, 10, 6, 5, 5, 5, 6, 7, 7, 8, 8,
9, 9, 10, 10, 7, 6, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10,
10, 8, 7, 6, 7, 7, 7, 8, 8, 8, 9, 10, 10, 11, 9, 7,
7, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 9, 8, 8, 8, 8,
8, 9, 9, 9, 10, 10, 11, 11, 9, 8, 8, 8, 8, 8, 9, 9,
10, 10, 10, 11, 11, 10, 9, 9, 9, 9, 9, 9, 10, 10, 10, 11,
11, 12, 10, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 11,
10, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 12, 11, 10, 10, 10,
10, 10, 10, 11, 11, 12, 12, 12, 12
};
static const uint16_t raac_hcb11_code[289] = {
0x0000, 0x0006, 0x0019, 0x003d, 0x009c, 0x00c6, 0x01a7, 0x0390,
0x03c2, 0x03df, 0x07e6, 0x07f3, 0x0ffb, 0x07ec, 0x0ffa, 0x0ffe,
0x038e, 0x0005, 0x0001, 0x0008, 0x0014, 0x0037, 0x0042, 0x0092,
0x00af, 0x0191, 0x01a5, 0x01b5, 0x039e, 0x03c0, 0x03a2, 0x03cd,
0x07d6, 0x00ae, 0x0017, 0x0007, 0x0009, 0x0018, 0x0039, 0x0040,
0x008e, 0x00a3, 0x00b8, 0x0199, 0x01ac, 0x01c1, 0x03b1, 0x0396,
0x03be, 0x03ca, 0x009d, 0x003c, 0x0015, 0x0016, 0x001a, 0x003b,
0x0044, 0x0091, 0x00a5, 0x00be, 0x0196, 0x01ae, 0x01b9, 0x03a1,
0x0391, 0x03a5, 0x03d5, 0x0094, 0x009a, 0x0036, 0x0038, 0x003a,
0x0041, 0x008c, 0x009b, 0x00b0, 0x00c3, 0x019e, 0x01ab, 0x01bc,
0x039f, 0x038f, 0x03a9, 0x03cf, 0x0093, 0x00bf, 0x003e, 0x003f,
0x0043, 0x0045, 0x009e, 0x00a7, 0x00b9, 0x0194, 0x01a2, 0x01ba,
0x01c3, 0x03a6, 0x03a7, 0x03bb, 0x03d4, 0x009f, 0x01a0, 0x008f,
0x008d, 0x0090, 0x0098, 0x00a6, 0x00b6, 0x00c4, 0x019f, 0x01af,
0x01bf, 0x0399, 0x03bf, 0x03b4, 0x03c9, 0x03e7, 0x00a8, 0x01b6,
0x00ab, 0x00a4, 0x00aa, 0x00b2, 0x00c2, 0x00c5, 0x0198, 0x01a4,
0x01b8, 0x038c, 0x03a4, 0x03c4, 0x03c6, 0x03dd, 0x03e8, 0x00ad,
0x03af, 0x0192, 0x00bd, 0x00bc, 0x018e, 0x0197, 0x019a, 0x01a3,
0x01b1, 0x038d, 0x0398, 0x03b7, 0x03d3, 0x03d1, 0x03db, 0x07dd,
0x00b4, 0x03de, 0x01a9, 0x019b, 0x019c, 0x01a1, 0x01aa, 0x01ad,
0x01b3, 0x038b, 0x03b2, 0x03b8, 0x03ce, 0x03e1, 0x03e0, 0x07d2,
0x07e5, 0x00b7, 0x07e3, 0x01bb, 0x01a8, 0x01a6, 0x01b0, 0x01b2,
0x01b7, 0x039b, 0x039a, 0x03ba, 0x03b5, 0x03d6, 0x07d7, 0x03e4,
0x07d8, 0x07ea, 0x00ba, 0x07e8, 0x03a0, 0x01bd, 0x01b4, 0x038a,
0x01c4, 0x0392, 0x03aa, 0x03b0, 0x03bc, 0x03d7, 0x07d4, 0x07dc,
0x07db, 0x07d5, 0x07f0, 0x00c1, 0x07fb, 0x03c8, 0x03a3, 0x0395,
0x039d, 0x03ac, 0x03ae, 0x03c5, 0x03d8, 0x03e2, 0x03e6, 0x07e4,
0x07e7, 0x07e0, 0x07e9, 0x07f7, 0x0190, 0x07f2, 0x0393, 0x01be,
0x01c0, 0x0394, 0x0397, 0x03ad, 0x03c3, 0x03c1, 0x03d2, 0x07da,
0x07d9, 0x07df, 0x07eb, 0x07f4, 0x07fa, 0x0195, 0x07f8, 0x03bd,
0x039c, 0x03ab, 0x03a8, 0x03b3, 0x03b9, 0x03d0, 0x03e3, 0x03e5,
0x07e2, 0x07de, 0x07ed, 0x07f1, 0x07f9, 0x07fc, 0x0193, 0x0ffd,
0x03dc, 0x03b6, 0x03c7, 0x03cc, 0x03cb, 0x03d9, 0x03da, 0x07d3,
0x07e1, 0x07ee, 0x07ef, 0x07f5, 0x07f6, 0x0ffc, 0x0fff, 0x019d,
0x01c2, 0x00b5, 0x00a1, 0x0096, 0x0097, 0x0095, 0x0099, 0x00a0,
0x00a2, 0x00ac, 0x00a9, 0x00b1, 0x00b3, 0x00bb, 0x00c0, 0x018f,
0x0004
};
static const uint8_t raac_hcb11_bits[289] = {
4, 5, 6, 7, 8, 8, 9, 10, 10, 10, 11, 11, 12, 11, 12, 12,
10, 5, 4, 5, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 10, 10,
11, 8, 6, 5, 5, 6, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10,
10, 10, 8, 7, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 10, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9,
10, 10, 10, 10, 8, 8, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9,
9, 10, 10, 10, 10, 8, 9, 8, 8, 8, 8, 8, 8, 8, 9, 9,
9, 10, 10, 10, 10, 10, 8, 9, 8, 8, 8, 8, 8, 8, 9, 9,
9, 10, 10, 10, 10, 10, 10, 8, 10, 9, 8, 8, 9, 9, 9, 9,
9, 10, 10, 10, 10, 10, 10, 11, 8, 10, 9, 9, 9, 9, 9, 9,
9, 10, 10, 10, 10, 10, 10, 11, 11, 8, 11, 9, 9, 9, 9, 9,
9, 10, 10, 10, 10, 10, 11, 10, 11, 11, 8, 11, 10, 9, 9, 10,
9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 8, 11, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 9, 11, 10, 9,
9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 9, 11, 10,
10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 9, 12,
10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 9,
9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9,
5
};
static const uint16_t raac_hcb_size[11] = {81,81,81,81,81,81,64,64,169,169,289};
static const uint16_t *const raac_hcb_code[11] = {
raac_hcb1_code, raac_hcb2_code, raac_hcb3_code, raac_hcb4_code, raac_hcb5_code, raac_hcb6_code, raac_hcb7_code, raac_hcb8_code, raac_hcb9_code, raac_hcb10_code, raac_hcb11_code
};
static const uint8_t *const raac_hcb_bits[11] = {
raac_hcb1_bits, raac_hcb2_bits, raac_hcb3_bits, raac_hcb4_bits, raac_hcb5_bits, raac_hcb6_bits, raac_hcb7_bits, raac_hcb8_bits, raac_hcb9_bits, raac_hcb10_bits, raac_hcb11_bits
};
static const uint32_t raac_sf_code[121] = {
0x3ffe8, 0x3ffe6, 0x3ffe7, 0x3ffe5, 0x7fff5, 0x7fff1, 0x7ffed, 0x7fff6,
0x7ffee, 0x7ffef, 0x7fff0, 0x7fffc, 0x7fffd, 0x7ffff, 0x7fffe, 0x7fff7,
0x7fff8, 0x7fffb, 0x7fff9, 0x3ffe4, 0x7fffa, 0x3ffe3, 0x1ffef, 0x1fff0,
0x0fff5, 0x1ffee, 0x0fff2, 0x0fff3, 0x0fff4, 0x0fff1, 0x07ff6, 0x07ff7,
0x03ff9, 0x03ff5, 0x03ff7, 0x03ff3, 0x03ff6, 0x03ff2, 0x01ff7, 0x01ff5,
0x00ff9, 0x00ff7, 0x00ff6, 0x007f9, 0x00ff4, 0x007f8, 0x003f9, 0x003f7,
0x003f5, 0x001f8, 0x001f7, 0x000fa, 0x000f8, 0x000f6, 0x00079, 0x0003a,
0x00038, 0x0001a, 0x0000b, 0x00004, 0x00000, 0x0000a, 0x0000c, 0x0001b,
0x00039, 0x0003b, 0x00078, 0x0007a, 0x000f7, 0x000f9, 0x001f6, 0x001f9,
0x003f4, 0x003f6, 0x003f8, 0x007f5, 0x007f4, 0x007f6, 0x007f7, 0x00ff5,
0x00ff8, 0x01ff4, 0x01ff6, 0x01ff8, 0x03ff8, 0x03ff4, 0x0fff0, 0x07ff4,
0x0fff6, 0x07ff5, 0x3ffe2, 0x7ffd9, 0x7ffda, 0x7ffdb, 0x7ffdc, 0x7ffdd,
0x7ffde, 0x7ffd8, 0x7ffd2, 0x7ffd3, 0x7ffd4, 0x7ffd5, 0x7ffd6, 0x7fff2,
0x7ffdf, 0x7ffe7, 0x7ffe8, 0x7ffe9, 0x7ffea, 0x7ffeb, 0x7ffe6, 0x7ffe0,
0x7ffe1, 0x7ffe2, 0x7ffe3, 0x7ffe4, 0x7ffe5, 0x7ffd7, 0x7ffec, 0x7fff4,
0x7fff3
};
static const uint8_t raac_sf_bits[121] = {
18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 18, 19, 18, 17, 17, 16, 17, 16, 16, 16, 16, 15, 15,
14, 14, 14, 14, 14, 14, 13, 13, 12, 12, 12, 11, 12, 11, 10, 10,
10, 9, 9, 8, 8, 8, 7, 6, 6, 5, 4, 3, 1, 4, 4, 5,
6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12,
12, 13, 13, 13, 14, 14, 16, 15, 16, 15, 18, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19
};
static const uint16_t raac_swb_offset_1024_16[44] = {
0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88,
100, 112, 124, 136, 148, 160, 172, 184, 196, 212, 228, 244,
260, 280, 300, 320, 344, 368, 396, 424, 456, 492, 532, 572,
616, 664, 716, 772, 832, 896, 960, 1024
};
static const uint16_t raac_swb_offset_1024_24[48] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44,
52, 60, 68, 76, 84, 92, 100, 108, 116, 124, 136, 148,
160, 172, 188, 204, 220, 240, 260, 284, 308, 336, 364, 396,
432, 468, 508, 552, 600, 652, 704, 768, 832, 896, 960, 1024
};
static const uint16_t raac_swb_offset_1024_32[52] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48,
56, 64, 72, 80, 88, 96, 108, 120, 132, 144, 160, 176,
196, 216, 240, 264, 292, 320, 352, 384, 416, 448, 480, 512,
544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896,
928, 960, 992, 1024
};
static const uint16_t raac_swb_offset_1024_48[50] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48,
56, 64, 72, 80, 88, 96, 108, 120, 132, 144, 160, 176,
196, 216, 240, 264, 292, 320, 352, 384, 416, 448, 480, 512,
544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896,
928, 1024
};
static const uint16_t raac_swb_offset_1024_64[48] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44,
48, 52, 56, 64, 72, 80, 88, 100, 112, 124, 140, 156,
172, 192, 216, 240, 268, 304, 344, 384, 424, 464, 504, 544,
584, 624, 664, 704, 744, 784, 824, 864, 904, 944, 984, 1024
};
static const uint16_t raac_swb_offset_1024_8[41] = {
0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132,
144, 156, 172, 188, 204, 220, 236, 252, 268, 288, 308, 328,
348, 372, 396, 420, 448, 476, 508, 544, 580, 620, 664, 712,
764, 820, 880, 944, 1024
};
static const uint16_t raac_swb_offset_1024_96[42] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44,
48, 52, 56, 64, 72, 80, 88, 96, 108, 120, 132, 144,
156, 172, 188, 212, 240, 276, 320, 384, 448, 512, 576, 640,
704, 768, 832, 896, 960, 1024
};
static const uint16_t *const raac_swb_offset_1024[13] = {
raac_swb_offset_1024_96, raac_swb_offset_1024_96, raac_swb_offset_1024_64,
raac_swb_offset_1024_48, raac_swb_offset_1024_48, raac_swb_offset_1024_32,
raac_swb_offset_1024_24, raac_swb_offset_1024_24, raac_swb_offset_1024_16,
raac_swb_offset_1024_16, raac_swb_offset_1024_16, raac_swb_offset_1024_8,
raac_swb_offset_1024_8
};
#include "raac_tables.h"
static const uint16_t raac_swb_offset_128_16[16] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 40, 48, 60,
72, 88, 108, 128
@@ -693,9 +317,6 @@ static const uint16_t *const raac_swb_offset_128[13] = {
raac_swb_offset_128_16, raac_swb_offset_128_16, raac_swb_offset_128_8,
raac_swb_offset_128_8
};
static const uint8_t raac_num_swb_1024[13] = {
41, 41, 47, 49, 49, 51, 47, 47, 43, 43, 43, 40, 40
};
static const uint8_t raac_num_swb_128[13] = {
12, 12, 12, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15
};
@@ -709,10 +330,6 @@ static const uint8_t raac_tns_max_bands_128[13] = {
9, 9, 10, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14
};
static const unsigned raac_sample_rates[13] = {
96000, 88200, 64000, 48000, 44100, 32000,
24000, 22050, 16000, 12000, 11025, 8000, 7350
};
/* ===== bit reader (MSB first) ===== */
+684
View File
@@ -0,0 +1,684 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (raac_encode.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* AAC-LC encoder. See raac_encode.h. Written from ISO/IEC 14496-3,
* subpart 4, its tables shared with raac (raac_tables.h).
*
* The MDCT is a DCT-IV of the folded window, by a complex FFT of a
* quarter its length. A band's scalefactor is sf = B + 2 log2(rms),
* its step growing with the square root of the band's level: halfway
* between the same noise in every band (the most signal-to-noise ratio
* overall) and the same ratio in every band (quiet bands kept as well
* as loud). B, one number a frame, is searched for the frame's bits;
* no step is finer than 60 dB under the frame's loudest band. */
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <formats/raac_encode.h>
#include "raac_tables.h"
#define N 2048
#define M 1024
#define MAX_SFB 51
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
typedef struct
{
float re, im;
} cplx_t;
typedef struct
{
float prev[M]; /* the last frame's samples */
float spec[M]; /* this frame's MDCT */
int q[M]; /* its quantised values */
int sf[MAX_SFB];
uint8_t cb[MAX_SFB];
int sf_floor; /* the frame's finest scalefactor */
} enc_channel_t;
struct raac_encoder
{
raac_encoder_config_t cfg;
unsigned sfi;
unsigned num_swb, max_sfb;
const uint16_t *swb;
int debt; /* bits over the rate so far */
int last_b; /* the last frame's B, where the search starts */
bool flushed;
float window[N];
cplx_t twiddle[M / 4]; /* the FFT's */
cplx_t pre[M / 2], post[M / 2];
uint16_t bitrev[M / 2];
enc_channel_t ch[2];
/* scratch */
float x[N];
cplx_t z[M / 2];
/* output */
uint8_t *out;
size_t cap, pos;
uint32_t acc;
unsigned nbits;
bool overflow;
};
/* --------------------------------------------------------------------- */
/* Transform */
/* --------------------------------------------------------------------- */
/* In place, radix 2, M/2 points */
static void fft(raac_encoder_t *e, cplx_t *z)
{
unsigned n = M / 2, i, len;
for (i = 0; i < n; i++)
{
unsigned j = e->bitrev[i];
if (j > i)
{
cplx_t t = z[i];
z[i] = z[j];
z[j] = t;
}
}
for (len = 2; len <= n; len <<= 1)
{
unsigned half = len / 2, step = n / len, k;
for (i = 0; i < n; i += len)
for (k = 0; k < half; k++)
{
cplx_t w = e->twiddle[k * step];
cplx_t a = z[i + k], b = z[i + k + half], t;
t.re = b.re * w.re - b.im * w.im;
t.im = b.re * w.im + b.im * w.re;
z[i + k].re = a.re + t.re;
z[i + k].im = a.im + t.im;
z[i + k + half].re = a.re - t.re;
z[i + k + half].im = a.im - t.im;
}
}
}
/* The MDCT of N windowed samples into M coefficients:
* X[k] = 2 sum x[n] cos(2 pi / N (n + 1/2 + N/4)(k + 1/2)) */
/* sample @k of the fold, (-c_r - d, a - b_r) of the window's quarters */
static float fold(const float *x, unsigned k)
{
return k < M / 2 ? -x[3 * M / 2 - 1 - k] - x[3 * M / 2 + k]
: x[k - M / 2] - x[3 * M / 2 - 1 - k];
}
static void mdct(raac_encoder_t *e, const float *x, float *out)
{
unsigned i;
/* the DCT-IV of the fold by an M/2 point FFT, folded straight into
* its input */
for (i = 0; i < M / 2; i++)
{
float a = fold(x, 2 * i), b = fold(x, M - 1 - 2 * i);
e->z[i].re = a * e->pre[i].re - b * e->pre[i].im;
e->z[i].im = a * e->pre[i].im + b * e->pre[i].re;
}
fft(e, e->z);
for (i = 0; i < M / 2; i++)
{
float re = e->z[i].re * e->post[i].re - e->z[i].im * e->post[i].im;
float im = e->z[i].re * e->post[i].im + e->z[i].im * e->post[i].re;
out[2 * i] = 2.0f * re;
out[M - 1 - 2 * i] = -2.0f * im;
}
}
/* --------------------------------------------------------------------- */
/* Setup */
/* --------------------------------------------------------------------- */
raac_encoder_t *raac_encoder_new(const raac_encoder_config_t *c)
{
raac_encoder_t *e;
unsigned i, bits, cutoff, per_ch;
if (!c || (c->channels != 1 && c->channels != 2) || !c->bitrate)
return NULL;
for (i = 0; i < 13; i++)
if (raac_sample_rates[i] == c->sample_rate)
break;
if (i == 13)
return NULL;
if (!(e = (raac_encoder_t*)calloc(1, sizeof(*e))))
return NULL;
e->cfg = *c;
e->sfi = i;
e->num_swb = raac_num_swb_1024[i];
e->swb = raac_swb_offset_1024[i];
/* the bandwidth the rate allows a channel */
per_ch = c->bitrate / c->channels;
cutoff = per_ch >= 96000 ? 20000 : per_ch >= 64000 ? 16000
: per_ch >= 48000 ? 13000 : per_ch >= 32000 ? 10000 : 7000;
if (cutoff > c->sample_rate / 2)
cutoff = c->sample_rate / 2;
for (e->max_sfb = 0; e->max_sfb < e->num_swb; e->max_sfb++)
if ((unsigned)e->swb[e->max_sfb] * c->sample_rate / (2 * M) >= cutoff)
break;
if (!e->max_sfb)
e->max_sfb = 1;
e->last_b = 60;
for (i = 0; i < N; i++)
e->window[i] = (float)sin(M_PI * (i + 0.5) / N);
for (i = 0; i < M / 4; i++)
{
e->twiddle[i].re = (float)cos(-2.0 * M_PI * i / (M / 2));
e->twiddle[i].im = (float)sin(-2.0 * M_PI * i / (M / 2));
}
for (i = 0; i < M / 2; i++)
{
unsigned r = 0, k;
for (k = 0, bits = i; k < 9; k++, bits >>= 1)
r = (r << 1) | (bits & 1);
e->bitrev[i] = (uint16_t)r;
e->pre[i].re = (float)cos(-M_PI * (i + 0.25) / M);
e->pre[i].im = (float)sin(-M_PI * (i + 0.25) / M);
e->post[i].re = (float)cos(-M_PI * i / M);
e->post[i].im = (float)sin(-M_PI * i / M);
}
return e;
}
void raac_encoder_free(raac_encoder_t *e)
{
free(e);
}
size_t raac_encoder_asc(const raac_encoder_t *e, uint8_t *dst, size_t cap)
{
if (!e || !dst || cap < 2)
return 0;
/* AAC LC (2), the rate's index, the channels; 1024-sample frames,
* no core coder, no extension */
dst[0] = (uint8_t)((2 << 3) | (e->sfi >> 1));
dst[1] = (uint8_t)(((e->sfi & 1) << 7) | (e->cfg.channels << 3));
return 2;
}
/* --------------------------------------------------------------------- */
/* Bits */
/* --------------------------------------------------------------------- */
static void put_bits(raac_encoder_t *e, uint32_t v, unsigned n)
{
while (n)
{
unsigned k = n > 24 ? 24 : n;
uint32_t part = (v >> (n - k)) & ((1u << k) - 1);
e->acc = (e->acc << k) | part;
e->nbits += k;
n -= k;
while (e->nbits >= 8)
{
e->nbits -= 8;
if (e->pos < e->cap)
e->out[e->pos++] = (uint8_t)(e->acc >> e->nbits);
else
e->overflow = true;
}
}
}
/* --------------------------------------------------------------------- */
/* Quantisation and codebooks */
/* --------------------------------------------------------------------- */
#define BIG 0x3FFFFFFF
static const uint8_t book_lav[12] = { 0, 1, 1, 2, 2, 4, 4, 7, 7, 12, 12, 16 };
/* A value's escape, for codebook 11: n ones, a zero, n + 4 bits */
static unsigned esc_bits(int v)
{
unsigned n = 0;
if (v < 16)
return 0;
while ((unsigned)v >= (32u << n))
n++;
return 2 * n + 5;
}
/* Bits (or BIG) for a band's values in a codebook; with @put, codes them */
static unsigned book_bits(raac_encoder_t *e, const int *q, unsigned w,
unsigned cb, bool put)
{
const uint16_t *code = raac_hcb_code[cb - 1];
const uint8_t *len = raac_hcb_bits[cb - 1];
unsigned bits = 0, i;
bool sign = cb == 3 || cb == 4 || cb >= 7;
if (cb <= 4)
{
for (i = 0; i < w; i += 4)
{
int a[4], k, idx = 0;
for (k = 0; k < 4; k++)
{
a[k] = q[i + k];
if (abs(a[k]) > book_lav[cb])
return BIG;
idx = idx * 3 + (sign ? abs(a[k]) : a[k] + 1);
}
bits += len[idx];
if (put)
put_bits(e, code[idx], len[idx]);
if (sign)
for (k = 0; k < 4; k++)
if (a[k])
{
bits++;
if (put)
put_bits(e, a[k] < 0, 1);
}
}
return bits;
}
for (i = 0; i < w; i += 2)
{
int y = q[i], z = q[i + 1], idx, k, ay = abs(y), az = abs(z);
if (cb <= 6)
{
if (ay > 4 || az > 4)
return BIG;
idx = 9 * (y + 4) + (z + 4);
}
else if (cb <= 10)
{
unsigned lav = cb <= 8 ? 8 : 13;
if ((unsigned)ay >= lav || (unsigned)az >= lav)
return BIG;
idx = (int)lav * ay + az;
}
else
idx = 17 * (ay > 16 ? 16 : ay) + (az > 16 ? 16 : az);
bits += len[idx];
if (put)
put_bits(e, code[idx], len[idx]);
if (sign)
for (k = 0; k < 2; k++)
{
int v = k ? z : y;
if (v)
{
bits++;
if (put)
put_bits(e, v < 0, 1);
}
}
if (cb == 11)
for (k = 0; k < 2; k++)
{
int v = k ? az : ay;
unsigned eb = esc_bits(v);
bits += eb;
if (put && eb)
{
unsigned n = (eb - 5) / 2;
put_bits(e, (1u << (n + 1)) - 2, n + 1); /* n ones, a zero */
put_bits(e, (uint32_t)v - (16u << n), n + 4);
}
}
}
return bits;
}
/* The codebook that codes a band in the fewest bits; 0 if it is zero */
static unsigned pick_book(raac_encoder_t *e, const int *q, unsigned w, unsigned *bits)
{
int m = 0;
unsigned i, first, best = 0, best_bits = BIG, cb;
for (i = 0; i < w; i++)
if (abs(q[i]) > m)
m = abs(q[i]);
if (!m)
{
*bits = 0;
return 0;
}
first = m <= 1 ? 1 : m <= 2 ? 3 : m <= 4 ? 5 : m <= 7 ? 7 : m <= 12 ? 9 : 11;
for (cb = first; cb <= 11 && cb < first + 4; cb++)
{
unsigned b = book_bits(e, q, w, cb, false);
if (b < best_bits)
{
best_bits = b;
best = cb;
}
}
*bits = best_bits;
return best;
}
/* The channel quantised at B: scalefactors, values, codebooks; returns
* the bits its section, scalefactor and spectral data take */
static unsigned quantise(raac_encoder_t *e, enc_channel_t *c, int B)
{
unsigned b, bits = 0;
int prev = -1;
for (b = 0; b < e->max_sfb; b++)
{
unsigned s0 = e->swb[b], w = e->swb[b + 1] - s0, i, bb;
double en = 0, mx = 0, gain;
int sf;
for (i = 0; i < w; i++)
{
double v = fabs(c->spec[s0 + i]);
en += v * v;
if (v > mx)
mx = v;
}
if (mx < 1.0)
{
for (i = 0; i < w; i++)
c->q[s0 + i] = 0;
c->cb[b] = 0;
continue;
}
sf = (int)floor(B + 2.0 * log(sqrt(en / w)) / log(2.0) + 0.5);
/* no finer than the frame's floor: what lies that far under its
* loudest band, or under the quietest 16-bit signal, is not heard */
if (sf < c->sf_floor)
sf = c->sf_floor;
/* no value past 8191 */
{
int lo = (int)ceil(100 + 16.0 / 3.0 * (0.75 * log(mx) / log(2.0) - log(8191.0) / log(2.0)));
if (sf < lo)
sf = lo;
}
if (prev >= 0)
{
if (sf < prev - 60)
sf = prev - 60;
if (sf > prev + 60)
sf = prev + 60;
}
if (sf < 0)
sf = 0;
if (sf > 255)
sf = 255;
gain = pow(2.0, -0.1875 * (sf - 100));
for (i = 0; i < w; i++)
{
float v = c->spec[s0 + i];
int l = (int)(pow(fabs(v), 0.75) * gain + 0.4054);
if (l > 8191)
l = 8191;
c->q[s0 + i] = v < 0 ? -l : l;
}
c->cb[b] = (uint8_t)pick_book(e, c->q + s0, w, &bb);
c->sf[b] = sf;
if (!c->cb[b])
continue;
bits += bb;
/* the scalefactor's delta */
bits += raac_sf_bits[(prev < 0 ? 0 : sf - prev) + 60];
prev = sf;
}
/* sections: a codebook, then its run in 5-bit pieces */
for (b = 0; b < e->max_sfb; b++)
{
unsigned run = 1;
while (b + 1 < e->max_sfb && c->cb[b + 1] == c->cb[b])
{
b++;
run++;
}
bits += 4 + 5 * (run / 31 + 1);
}
return bits;
}
static void write_ics_info(raac_encoder_t *e)
{
put_bits(e, 0, 1); /* ics_reserved_bit */
put_bits(e, 0, 2); /* ONLY_LONG_SEQUENCE */
put_bits(e, 0, 1); /* sine window */
put_bits(e, e->max_sfb, 6);
put_bits(e, 0, 1); /* no prediction */
}
/* individual_channel_stream(): global_gain, ics_info unless a channel
* pair's common window gave it, sections, scalefactors, no pulse, TNS
* or gain control, the spectrum */
static void write_ics(raac_encoder_t *e, enc_channel_t *c, bool info)
{
unsigned b, global = 100;
int prev = -1;
for (b = 0; b < e->max_sfb; b++)
if (c->cb[b])
{
global = (unsigned)c->sf[b];
break;
}
put_bits(e, global, 8);
if (info)
write_ics_info(e);
for (b = 0; b < e->max_sfb; b++)
{
unsigned run = 1, cb = c->cb[b];
while (b + 1 < e->max_sfb && c->cb[b + 1] == cb)
{
b++;
run++;
}
put_bits(e, cb, 4);
while (run >= 31)
{
put_bits(e, 31, 5);
run -= 31;
}
put_bits(e, run, 5);
}
for (b = 0; b < e->max_sfb; b++)
{
int d;
if (!c->cb[b])
continue;
d = prev < 0 ? 0 : c->sf[b] - prev;
put_bits(e, raac_sf_code[d + 60], raac_sf_bits[d + 60]);
prev = c->sf[b];
}
put_bits(e, 0, 3); /* pulse, tns, gain control */
for (b = 0; b < e->max_sfb; b++)
if (c->cb[b])
{
unsigned s0 = e->swb[b];
book_bits(e, c->q + s0, e->swb[b + 1] - s0, c->cb[b], true);
}
}
/* --------------------------------------------------------------------- */
static size_t encode(raac_encoder_t *e, const int16_t *s16, const float *f32,
uint8_t *dst, size_t cap, bool adts)
{
unsigned ch, i, n = e->cfg.channels;
unsigned header = adts ? 7 : 0;
int target, share, lo, hi, best_b, limit;
unsigned bits_fixed = n == 1 ? 3 + 4 + 11 + 8 + 3 + 3
: 3 + 4 + 1 + 11 + 2 + 2 * (8 + 3) + 3;
if (!e || !dst)
return 0;
if (!s16 && !f32)
e->flushed = true;
for (ch = 0; ch < n; ch++)
{
enc_channel_t *c = &e->ch[ch];
for (i = 0; i < M; i++)
{
float cur = s16 ? (float)s16[i * n + ch]
: f32 ? f32[i * n + ch] * 32768.0f : 0.0f;
e->x[i] = c->prev[i] * e->window[i];
e->x[M + i] = cur * e->window[M + i];
c->prev[i] = cur;
}
mdct(e, e->x, c->spec);
/* the floor: a step 60 dB under the loudest band's level, and at
* least what a one-LSB sine comes to */
{
unsigned bb;
double peak = 0;
for (bb = 0; bb < e->max_sfb; bb++)
{
unsigned s0 = e->swb[bb], w = e->swb[bb + 1] - s0, k;
double en = 0;
for (k = 0; k < w; k++)
en += (double)c->spec[s0 + k] * c->spec[s0 + k];
if (en / w > peak)
peak = en / w;
}
peak = sqrt(peak) * 0.001; /* -60 dB */
if (peak < 64.0)
peak = 64.0;
c->sf_floor = (int)floor(100 + 4.0 * log(peak) / log(2.0) + 0.5);
}
}
/* the frame's bits: its share of the rate, the debt paid over eight
* frames, within AAC's 6144 bits a channel */
share = (int)((uint64_t)e->cfg.bitrate * M / e->cfg.sample_rate);
target = share - e->debt / 8;
limit = 6144 * (int)n - 8;
if (target < share / 4)
target = share / 4;
if (target > limit)
target = limit;
/* the largest B (coarsest) is never wanted; search for the smallest
* B whose bits fit */
lo = -120;
hi = 255;
best_b = hi;
{
int b0 = e->last_b;
/* bisection over B, starting where the last frame ended */
while (lo <= hi)
{
int mid = (lo + hi) / 2, total = (int)bits_fixed;
if (b0 >= lo && b0 <= hi)
{
mid = b0;
b0 = -1000;
}
for (ch = 0; ch < n; ch++)
total += (int)quantise(e, &e->ch[ch], mid);
if (total <= target)
{
best_b = mid;
hi = mid - 1;
}
else
lo = mid + 1;
}
}
e->last_b = best_b;
for (ch = 0; ch < n; ch++)
quantise(e, &e->ch[ch], best_b);
e->out = dst;
e->cap = cap;
e->pos = 0;
e->acc = 0;
e->nbits = 0;
e->overflow = false;
if (adts)
for (i = 0; i < 7; i++)
put_bits(e, 0, 8); /* filled in below */
if (n == 1)
{
put_bits(e, 0, 3); /* ID_SCE */
put_bits(e, 0, 4);
write_ics(e, &e->ch[0], true);
}
else
{
put_bits(e, 1, 3); /* ID_CPE */
put_bits(e, 0, 4);
put_bits(e, 1, 1); /* common_window */
write_ics_info(e);
put_bits(e, 0, 2); /* no M/S */
write_ics(e, &e->ch[0], false);
write_ics(e, &e->ch[1], false);
}
put_bits(e, 7, 3); /* ID_END */
if (e->nbits)
put_bits(e, 0, 8 - e->nbits);
if (e->overflow)
return 0;
if (adts)
{
size_t len = e->pos;
uint8_t *h = dst;
h[0] = 0xFF;
h[1] = 0xF1; /* MPEG-4, no CRC */
h[2] = (uint8_t)((1 << 6) | (e->sfi << 2) | (n >> 2));
h[3] = (uint8_t)(((n & 3) << 6) | ((len >> 11) & 3));
h[4] = (uint8_t)(len >> 3);
h[5] = (uint8_t)(((len & 7) << 5) | 0x1F);
h[6] = 0xFC;
}
e->debt += (int)((e->pos - header) * 8) - share;
if (e->debt > 64 * share)
e->debt = 64 * share;
if (e->debt < -64 * share)
e->debt = -64 * share;
return e->pos;
}
size_t raac_encoder_encode_s16(raac_encoder_t *e, const int16_t *pcm,
uint8_t *dst, size_t cap, bool adts)
{
return encode(e, pcm, NULL, dst, cap, adts);
}
size_t raac_encoder_encode_f32(raac_encoder_t *e, const float *pcm,
uint8_t *dst, size_t cap, bool adts)
{
return encode(e, NULL, pcm, dst, cap, adts);
}
+420
View File
@@ -0,0 +1,420 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (raac_tables.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* The tables of ISO/IEC 14496-3 that raac decodes with and raac_encode
* codes with: the spectral and scalefactor Huffman codebooks (Tables
* 4.A.2 to 4.A.13), the long window's scalefactor bands (Tables 4.129
* to 4.138) and the sampling frequency indices. Static data, for the two
* files that include it. */
#ifndef __LIBRETRO_SDK_FORMAT_RAAC_TABLES_H
#define __LIBRETRO_SDK_FORMAT_RAAC_TABLES_H
#include <stdint.h>
static const uint16_t raac_hcb1_code[81] = {
0x07f8, 0x01f1, 0x07fd, 0x03f5, 0x0068, 0x03f0, 0x07f7, 0x01ec,
0x07f5, 0x03f1, 0x0072, 0x03f4, 0x0074, 0x0011, 0x0076, 0x01eb,
0x006c, 0x03f6, 0x07fc, 0x01e1, 0x07f1, 0x01f0, 0x0061, 0x01f6,
0x07f2, 0x01ea, 0x07fb, 0x01f2, 0x0069, 0x01ed, 0x0077, 0x0017,
0x006f, 0x01e6, 0x0064, 0x01e5, 0x0067, 0x0015, 0x0062, 0x0012,
0x0000, 0x0014, 0x0065, 0x0016, 0x006d, 0x01e9, 0x0063, 0x01e4,
0x006b, 0x0013, 0x0071, 0x01e3, 0x0070, 0x01f3, 0x07fe, 0x01e7,
0x07f3, 0x01ef, 0x0060, 0x01ee, 0x07f0, 0x01e2, 0x07fa, 0x03f3,
0x006a, 0x01e8, 0x0075, 0x0010, 0x0073, 0x01f4, 0x006e, 0x03f7,
0x07f6, 0x01e0, 0x07f9, 0x03f2, 0x0066, 0x01f5, 0x07ff, 0x01f7,
0x07f4
};
static const uint8_t raac_hcb1_bits[81] = {
11, 9, 11, 10, 7, 10, 11, 9, 11, 10, 7, 10, 7, 5, 7, 9,
7, 10, 11, 9, 11, 9, 7, 9, 11, 9, 11, 9, 7, 9, 7, 5,
7, 9, 7, 9, 7, 5, 7, 5, 1, 5, 7, 5, 7, 9, 7, 9,
7, 5, 7, 9, 7, 9, 11, 9, 11, 9, 7, 9, 11, 9, 11, 10,
7, 9, 7, 5, 7, 9, 7, 10, 11, 9, 11, 10, 7, 9, 11, 9,
11
};
static const uint16_t raac_hcb2_code[81] = {
0x01f3, 0x006f, 0x01fd, 0x00eb, 0x0023, 0x00ea, 0x01f7, 0x00e8,
0x01fa, 0x00f2, 0x002d, 0x0070, 0x0020, 0x0006, 0x002b, 0x006e,
0x0028, 0x00e9, 0x01f9, 0x0066, 0x00f8, 0x00e7, 0x001b, 0x00f1,
0x01f4, 0x006b, 0x01f5, 0x00ec, 0x002a, 0x006c, 0x002c, 0x000a,
0x0027, 0x0067, 0x001a, 0x00f5, 0x0024, 0x0008, 0x001f, 0x0009,
0x0000, 0x0007, 0x001d, 0x000b, 0x0030, 0x00ef, 0x001c, 0x0064,
0x001e, 0x000c, 0x0029, 0x00f3, 0x002f, 0x00f0, 0x01fc, 0x0071,
0x01f2, 0x00f4, 0x0021, 0x00e6, 0x00f7, 0x0068, 0x01f8, 0x00ee,
0x0022, 0x0065, 0x0031, 0x0002, 0x0026, 0x00ed, 0x0025, 0x006a,
0x01fb, 0x0072, 0x01fe, 0x0069, 0x002e, 0x00f6, 0x01ff, 0x006d,
0x01f6
};
static const uint8_t raac_hcb2_bits[81] = {
9, 7, 9, 8, 6, 8, 9, 8, 9, 8, 6, 7, 6, 5, 6, 7,
6, 8, 9, 7, 8, 8, 6, 8, 9, 7, 9, 8, 6, 7, 6, 5,
6, 7, 6, 8, 6, 5, 6, 5, 3, 5, 6, 5, 6, 8, 6, 7,
6, 5, 6, 8, 6, 8, 9, 7, 9, 8, 6, 8, 8, 7, 9, 8,
6, 7, 6, 4, 6, 8, 6, 7, 9, 7, 9, 7, 6, 8, 9, 7,
9
};
static const uint16_t raac_hcb3_code[81] = {
0x0000, 0x0009, 0x00ef, 0x000b, 0x0019, 0x00f0, 0x01eb, 0x01e6,
0x03f2, 0x000a, 0x0035, 0x01ef, 0x0034, 0x0037, 0x01e9, 0x01ed,
0x01e7, 0x03f3, 0x01ee, 0x03ed, 0x1ffa, 0x01ec, 0x01f2, 0x07f9,
0x07f8, 0x03f8, 0x0ff8, 0x0008, 0x0038, 0x03f6, 0x0036, 0x0075,
0x03f1, 0x03eb, 0x03ec, 0x0ff4, 0x0018, 0x0076, 0x07f4, 0x0039,
0x0074, 0x03ef, 0x01f3, 0x01f4, 0x07f6, 0x01e8, 0x03ea, 0x1ffc,
0x00f2, 0x01f1, 0x0ffb, 0x03f5, 0x07f3, 0x0ffc, 0x00ee, 0x03f7,
0x7ffe, 0x01f0, 0x07f5, 0x7ffd, 0x1ffb, 0x3ffa, 0xffff, 0x00f1,
0x03f0, 0x3ffc, 0x01ea, 0x03ee, 0x3ffb, 0x0ff6, 0x0ffa, 0x7ffc,
0x07f2, 0x0ff5, 0xfffe, 0x03f4, 0x07f7, 0x7ffb, 0x0ff7, 0x0ff9,
0x7ffa
};
static const uint8_t raac_hcb3_bits[81] = {
1, 4, 8, 4, 5, 8, 9, 9, 10, 4, 6, 9, 6, 6, 9, 9,
9, 10, 9, 10, 13, 9, 9, 11, 11, 10, 12, 4, 6, 10, 6, 7,
10, 10, 10, 12, 5, 7, 11, 6, 7, 10, 9, 9, 11, 9, 10, 13,
8, 9, 12, 10, 11, 12, 8, 10, 15, 9, 11, 15, 13, 14, 16, 8,
10, 14, 9, 10, 14, 12, 12, 15, 11, 12, 16, 10, 11, 15, 12, 12,
15
};
static const uint16_t raac_hcb4_code[81] = {
0x0007, 0x0016, 0x00f6, 0x0018, 0x0008, 0x00ef, 0x01ef, 0x00f3,
0x07f8, 0x0019, 0x0017, 0x00ed, 0x0015, 0x0001, 0x00e2, 0x00f0,
0x0070, 0x03f0, 0x01ee, 0x00f1, 0x07fa, 0x00ee, 0x00e4, 0x03f2,
0x07f6, 0x03ef, 0x07fd, 0x0005, 0x0014, 0x00f2, 0x0009, 0x0004,
0x00e5, 0x00f4, 0x00e8, 0x03f4, 0x0006, 0x0002, 0x00e7, 0x0003,
0x0000, 0x006b, 0x00e3, 0x0069, 0x01f3, 0x00eb, 0x00e6, 0x03f6,
0x006e, 0x006a, 0x01f4, 0x03ec, 0x01f0, 0x03f9, 0x00f5, 0x00ec,
0x07fb, 0x00ea, 0x006f, 0x03f7, 0x07f9, 0x03f3, 0x0fff, 0x00e9,
0x006d, 0x03f8, 0x006c, 0x0068, 0x01f5, 0x03ee, 0x01f2, 0x07f4,
0x07f7, 0x03f1, 0x0ffe, 0x03ed, 0x01f1, 0x07f5, 0x07fe, 0x03f5,
0x07fc
};
static const uint8_t raac_hcb4_bits[81] = {
4, 5, 8, 5, 4, 8, 9, 8, 11, 5, 5, 8, 5, 4, 8, 8,
7, 10, 9, 8, 11, 8, 8, 10, 11, 10, 11, 4, 5, 8, 4, 4,
8, 8, 8, 10, 4, 4, 8, 4, 4, 7, 8, 7, 9, 8, 8, 10,
7, 7, 9, 10, 9, 10, 8, 8, 11, 8, 7, 10, 11, 10, 12, 8,
7, 10, 7, 7, 9, 10, 9, 11, 11, 10, 12, 10, 9, 11, 11, 10,
11
};
static const uint16_t raac_hcb5_code[81] = {
0x1fff, 0x0ff7, 0x07f4, 0x07e8, 0x03f1, 0x07ee, 0x07f9, 0x0ff8,
0x1ffd, 0x0ffd, 0x07f1, 0x03e8, 0x01e8, 0x00f0, 0x01ec, 0x03ee,
0x07f2, 0x0ffa, 0x0ff4, 0x03ef, 0x01f2, 0x00e8, 0x0070, 0x00ec,
0x01f0, 0x03ea, 0x07f3, 0x07eb, 0x01eb, 0x00ea, 0x001a, 0x0008,
0x0019, 0x00ee, 0x01ef, 0x07ed, 0x03f0, 0x00f2, 0x0073, 0x000b,
0x0000, 0x000a, 0x0071, 0x00f3, 0x07e9, 0x07ef, 0x01ee, 0x00ef,
0x0018, 0x0009, 0x001b, 0x00eb, 0x01e9, 0x07ec, 0x07f6, 0x03eb,
0x01f3, 0x00ed, 0x0072, 0x00e9, 0x01f1, 0x03ed, 0x07f7, 0x0ff6,
0x07f0, 0x03e9, 0x01ed, 0x00f1, 0x01ea, 0x03ec, 0x07f8, 0x0ff9,
0x1ffc, 0x0ffc, 0x0ff5, 0x07ea, 0x03f3, 0x03f2, 0x07f5, 0x0ffb,
0x1ffe
};
static const uint8_t raac_hcb5_bits[81] = {
13, 12, 11, 11, 10, 11, 11, 12, 13, 12, 11, 10, 9, 8, 9, 10,
11, 12, 12, 10, 9, 8, 7, 8, 9, 10, 11, 11, 9, 8, 5, 4,
5, 8, 9, 11, 10, 8, 7, 4, 1, 4, 7, 8, 11, 11, 9, 8,
5, 4, 5, 8, 9, 11, 11, 10, 9, 8, 7, 8, 9, 10, 11, 12,
11, 10, 9, 8, 9, 10, 11, 12, 13, 12, 12, 11, 10, 10, 11, 12,
13
};
static const uint16_t raac_hcb6_code[81] = {
0x07fe, 0x03fd, 0x01f1, 0x01eb, 0x01f4, 0x01ea, 0x01f0, 0x03fc,
0x07fd, 0x03f6, 0x01e5, 0x00ea, 0x006c, 0x0071, 0x0068, 0x00f0,
0x01e6, 0x03f7, 0x01f3, 0x00ef, 0x0032, 0x0027, 0x0028, 0x0026,
0x0031, 0x00eb, 0x01f7, 0x01e8, 0x006f, 0x002e, 0x0008, 0x0004,
0x0006, 0x0029, 0x006b, 0x01ee, 0x01ef, 0x0072, 0x002d, 0x0002,
0x0000, 0x0003, 0x002f, 0x0073, 0x01fa, 0x01e7, 0x006e, 0x002b,
0x0007, 0x0001, 0x0005, 0x002c, 0x006d, 0x01ec, 0x01f9, 0x00ee,
0x0030, 0x0024, 0x002a, 0x0025, 0x0033, 0x00ec, 0x01f2, 0x03f8,
0x01e4, 0x00ed, 0x006a, 0x0070, 0x0069, 0x0074, 0x00f1, 0x03fa,
0x07ff, 0x03f9, 0x01f6, 0x01ed, 0x01f8, 0x01e9, 0x01f5, 0x03fb,
0x07fc
};
static const uint8_t raac_hcb6_bits[81] = {
11, 10, 9, 9, 9, 9, 9, 10, 11, 10, 9, 8, 7, 7, 7, 8,
9, 10, 9, 8, 6, 6, 6, 6, 6, 8, 9, 9, 7, 6, 4, 4,
4, 6, 7, 9, 9, 7, 6, 4, 4, 4, 6, 7, 9, 9, 7, 6,
4, 4, 4, 6, 7, 9, 9, 8, 6, 6, 6, 6, 6, 8, 9, 10,
9, 8, 7, 7, 7, 7, 8, 10, 11, 10, 9, 9, 9, 9, 9, 10,
11
};
static const uint16_t raac_hcb7_code[64] = {
0x0000, 0x0005, 0x0037, 0x0074, 0x00f2, 0x01eb, 0x03ed, 0x07f7,
0x0004, 0x000c, 0x0035, 0x0071, 0x00ec, 0x00ee, 0x01ee, 0x01f5,
0x0036, 0x0034, 0x0072, 0x00ea, 0x00f1, 0x01e9, 0x01f3, 0x03f5,
0x0073, 0x0070, 0x00eb, 0x00f0, 0x01f1, 0x01f0, 0x03ec, 0x03fa,
0x00f3, 0x00ed, 0x01e8, 0x01ef, 0x03ef, 0x03f1, 0x03f9, 0x07fb,
0x01ed, 0x00ef, 0x01ea, 0x01f2, 0x03f3, 0x03f8, 0x07f9, 0x07fc,
0x03ee, 0x01ec, 0x01f4, 0x03f4, 0x03f7, 0x07f8, 0x0ffd, 0x0ffe,
0x07f6, 0x03f0, 0x03f2, 0x03f6, 0x07fa, 0x07fd, 0x0ffc, 0x0fff
};
static const uint8_t raac_hcb7_bits[64] = {
1, 3, 6, 7, 8, 9, 10, 11, 3, 4, 6, 7, 8, 8, 9, 9,
6, 6, 7, 8, 8, 9, 9, 10, 7, 7, 8, 8, 9, 9, 10, 10,
8, 8, 9, 9, 10, 10, 10, 11, 9, 8, 9, 9, 10, 10, 11, 11,
10, 9, 9, 10, 10, 11, 12, 12, 11, 10, 10, 10, 11, 11, 12, 12
};
static const uint16_t raac_hcb8_code[64] = {
0x000e, 0x0005, 0x0010, 0x0030, 0x006f, 0x00f1, 0x01fa, 0x03fe,
0x0003, 0x0000, 0x0004, 0x0012, 0x002c, 0x006a, 0x0075, 0x00f8,
0x000f, 0x0002, 0x0006, 0x0014, 0x002e, 0x0069, 0x0072, 0x00f5,
0x002f, 0x0011, 0x0013, 0x002a, 0x0032, 0x006c, 0x00ec, 0x00fa,
0x0071, 0x002b, 0x002d, 0x0031, 0x006d, 0x0070, 0x00f2, 0x01f9,
0x00ef, 0x0068, 0x0033, 0x006b, 0x006e, 0x00ee, 0x00f9, 0x03fc,
0x01f8, 0x0074, 0x0073, 0x00ed, 0x00f0, 0x00f6, 0x01f6, 0x01fd,
0x03fd, 0x00f3, 0x00f4, 0x00f7, 0x01f7, 0x01fb, 0x01fc, 0x03ff
};
static const uint8_t raac_hcb8_bits[64] = {
5, 4, 5, 6, 7, 8, 9, 10, 4, 3, 4, 5, 6, 7, 7, 8,
5, 4, 4, 5, 6, 7, 7, 8, 6, 5, 5, 6, 6, 7, 8, 8,
7, 6, 6, 6, 7, 7, 8, 9, 8, 7, 6, 7, 7, 8, 8, 10,
9, 7, 7, 8, 8, 8, 9, 9, 10, 8, 8, 8, 9, 9, 9, 10
};
static const uint16_t raac_hcb9_code[169] = {
0x0000, 0x0005, 0x0037, 0x00e7, 0x01de, 0x03ce, 0x03d9, 0x07c8,
0x07cd, 0x0fc8, 0x0fdd, 0x1fe4, 0x1fec, 0x0004, 0x000c, 0x0035,
0x0072, 0x00ea, 0x00ed, 0x01e2, 0x03d1, 0x03d3, 0x03e0, 0x07d8,
0x0fcf, 0x0fd5, 0x0036, 0x0034, 0x0071, 0x00e8, 0x00ec, 0x01e1,
0x03cf, 0x03dd, 0x03db, 0x07d0, 0x0fc7, 0x0fd4, 0x0fe4, 0x00e6,
0x0070, 0x00e9, 0x01dd, 0x01e3, 0x03d2, 0x03dc, 0x07cc, 0x07ca,
0x07de, 0x0fd8, 0x0fea, 0x1fdb, 0x01df, 0x00eb, 0x01dc, 0x01e6,
0x03d5, 0x03de, 0x07cb, 0x07dd, 0x07dc, 0x0fcd, 0x0fe2, 0x0fe7,
0x1fe1, 0x03d0, 0x01e0, 0x01e4, 0x03d6, 0x07c5, 0x07d1, 0x07db,
0x0fd2, 0x07e0, 0x0fd9, 0x0feb, 0x1fe3, 0x1fe9, 0x07c4, 0x01e5,
0x03d7, 0x07c6, 0x07cf, 0x07da, 0x0fcb, 0x0fda, 0x0fe3, 0x0fe9,
0x1fe6, 0x1ff3, 0x1ff7, 0x07d3, 0x03d8, 0x03e1, 0x07d4, 0x07d9,
0x0fd3, 0x0fde, 0x1fdd, 0x1fd9, 0x1fe2, 0x1fea, 0x1ff1, 0x1ff6,
0x07d2, 0x03d4, 0x03da, 0x07c7, 0x07d7, 0x07e2, 0x0fce, 0x0fdb,
0x1fd8, 0x1fee, 0x3ff0, 0x1ff4, 0x3ff2, 0x07e1, 0x03df, 0x07c9,
0x07d6, 0x0fca, 0x0fd0, 0x0fe5, 0x0fe6, 0x1feb, 0x1fef, 0x3ff3,
0x3ff4, 0x3ff5, 0x0fe0, 0x07ce, 0x07d5, 0x0fc6, 0x0fd1, 0x0fe1,
0x1fe0, 0x1fe8, 0x1ff0, 0x3ff1, 0x3ff8, 0x3ff6, 0x7ffc, 0x0fe8,
0x07df, 0x0fc9, 0x0fd7, 0x0fdc, 0x1fdc, 0x1fdf, 0x1fed, 0x1ff5,
0x3ff9, 0x3ffb, 0x7ffd, 0x7ffe, 0x1fe7, 0x0fcc, 0x0fd6, 0x0fdf,
0x1fde, 0x1fda, 0x1fe5, 0x1ff2, 0x3ffa, 0x3ff7, 0x3ffc, 0x3ffd,
0x7fff
};
static const uint8_t raac_hcb9_bits[169] = {
1, 3, 6, 8, 9, 10, 10, 11, 11, 12, 12, 13, 13, 3, 4, 6,
7, 8, 8, 9, 10, 10, 10, 11, 12, 12, 6, 6, 7, 8, 8, 9,
10, 10, 10, 11, 12, 12, 12, 8, 7, 8, 9, 9, 10, 10, 11, 11,
11, 12, 12, 13, 9, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 12,
13, 10, 9, 9, 10, 11, 11, 11, 12, 11, 12, 12, 13, 13, 11, 9,
10, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 11, 10, 10, 11, 11,
12, 12, 13, 13, 13, 13, 13, 13, 11, 10, 10, 11, 11, 11, 12, 12,
13, 13, 14, 13, 14, 11, 10, 11, 11, 12, 12, 12, 12, 13, 13, 14,
14, 14, 12, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 12,
11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 15, 15, 13, 12, 12, 12,
13, 13, 13, 13, 14, 14, 14, 14, 15
};
static const uint16_t raac_hcb10_code[169] = {
0x0022, 0x0008, 0x001d, 0x0026, 0x005f, 0x00d3, 0x01cf, 0x03d0,
0x03d7, 0x03ed, 0x07f0, 0x07f6, 0x0ffd, 0x0007, 0x0000, 0x0001,
0x0009, 0x0020, 0x0054, 0x0060, 0x00d5, 0x00dc, 0x01d4, 0x03cd,
0x03de, 0x07e7, 0x001c, 0x0002, 0x0006, 0x000c, 0x001e, 0x0028,
0x005b, 0x00cd, 0x00d9, 0x01ce, 0x01dc, 0x03d9, 0x03f1, 0x0025,
0x000b, 0x000a, 0x000d, 0x0024, 0x0057, 0x0061, 0x00cc, 0x00dd,
0x01cc, 0x01de, 0x03d3, 0x03e7, 0x005d, 0x0021, 0x001f, 0x0023,
0x0027, 0x0059, 0x0064, 0x00d8, 0x00df, 0x01d2, 0x01e2, 0x03dd,
0x03ee, 0x00d1, 0x0055, 0x0029, 0x0056, 0x0058, 0x0062, 0x00ce,
0x00e0, 0x00e2, 0x01da, 0x03d4, 0x03e3, 0x07eb, 0x01c9, 0x005e,
0x005a, 0x005c, 0x0063, 0x00ca, 0x00da, 0x01c7, 0x01ca, 0x01e0,
0x03db, 0x03e8, 0x07ec, 0x01e3, 0x00d2, 0x00cb, 0x00d0, 0x00d7,
0x00db, 0x01c6, 0x01d5, 0x01d8, 0x03ca, 0x03da, 0x07ea, 0x07f1,
0x01e1, 0x00d4, 0x00cf, 0x00d6, 0x00de, 0x00e1, 0x01d0, 0x01d6,
0x03d1, 0x03d5, 0x03f2, 0x07ee, 0x07fb, 0x03e9, 0x01cd, 0x01c8,
0x01cb, 0x01d1, 0x01d7, 0x01df, 0x03cf, 0x03e0, 0x03ef, 0x07e6,
0x07f8, 0x0ffa, 0x03eb, 0x01dd, 0x01d3, 0x01d9, 0x01db, 0x03d2,
0x03cc, 0x03dc, 0x03ea, 0x07ed, 0x07f3, 0x07f9, 0x0ff9, 0x07f2,
0x03ce, 0x01e4, 0x03cb, 0x03d8, 0x03d6, 0x03e2, 0x03e5, 0x07e8,
0x07f4, 0x07f5, 0x07f7, 0x0ffb, 0x07fa, 0x03ec, 0x03df, 0x03e1,
0x03e4, 0x03e6, 0x03f0, 0x07e9, 0x07ef, 0x0ff8, 0x0ffe, 0x0ffc,
0x0fff
};
static const uint8_t raac_hcb10_bits[169] = {
6, 5, 6, 6, 7, 8, 9, 10, 10, 10, 11, 11, 12, 5, 4, 4,
5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 6, 4, 5, 5, 6, 6,
7, 8, 8, 9, 9, 10, 10, 6, 5, 5, 5, 6, 7, 7, 8, 8,
9, 9, 10, 10, 7, 6, 6, 6, 6, 7, 7, 8, 8, 9, 9, 10,
10, 8, 7, 6, 7, 7, 7, 8, 8, 8, 9, 10, 10, 11, 9, 7,
7, 7, 7, 8, 8, 9, 9, 9, 10, 10, 11, 9, 8, 8, 8, 8,
8, 9, 9, 9, 10, 10, 11, 11, 9, 8, 8, 8, 8, 8, 9, 9,
10, 10, 10, 11, 11, 10, 9, 9, 9, 9, 9, 9, 10, 10, 10, 11,
11, 12, 10, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 11,
10, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 12, 11, 10, 10, 10,
10, 10, 10, 11, 11, 12, 12, 12, 12
};
static const uint16_t raac_hcb11_code[289] = {
0x0000, 0x0006, 0x0019, 0x003d, 0x009c, 0x00c6, 0x01a7, 0x0390,
0x03c2, 0x03df, 0x07e6, 0x07f3, 0x0ffb, 0x07ec, 0x0ffa, 0x0ffe,
0x038e, 0x0005, 0x0001, 0x0008, 0x0014, 0x0037, 0x0042, 0x0092,
0x00af, 0x0191, 0x01a5, 0x01b5, 0x039e, 0x03c0, 0x03a2, 0x03cd,
0x07d6, 0x00ae, 0x0017, 0x0007, 0x0009, 0x0018, 0x0039, 0x0040,
0x008e, 0x00a3, 0x00b8, 0x0199, 0x01ac, 0x01c1, 0x03b1, 0x0396,
0x03be, 0x03ca, 0x009d, 0x003c, 0x0015, 0x0016, 0x001a, 0x003b,
0x0044, 0x0091, 0x00a5, 0x00be, 0x0196, 0x01ae, 0x01b9, 0x03a1,
0x0391, 0x03a5, 0x03d5, 0x0094, 0x009a, 0x0036, 0x0038, 0x003a,
0x0041, 0x008c, 0x009b, 0x00b0, 0x00c3, 0x019e, 0x01ab, 0x01bc,
0x039f, 0x038f, 0x03a9, 0x03cf, 0x0093, 0x00bf, 0x003e, 0x003f,
0x0043, 0x0045, 0x009e, 0x00a7, 0x00b9, 0x0194, 0x01a2, 0x01ba,
0x01c3, 0x03a6, 0x03a7, 0x03bb, 0x03d4, 0x009f, 0x01a0, 0x008f,
0x008d, 0x0090, 0x0098, 0x00a6, 0x00b6, 0x00c4, 0x019f, 0x01af,
0x01bf, 0x0399, 0x03bf, 0x03b4, 0x03c9, 0x03e7, 0x00a8, 0x01b6,
0x00ab, 0x00a4, 0x00aa, 0x00b2, 0x00c2, 0x00c5, 0x0198, 0x01a4,
0x01b8, 0x038c, 0x03a4, 0x03c4, 0x03c6, 0x03dd, 0x03e8, 0x00ad,
0x03af, 0x0192, 0x00bd, 0x00bc, 0x018e, 0x0197, 0x019a, 0x01a3,
0x01b1, 0x038d, 0x0398, 0x03b7, 0x03d3, 0x03d1, 0x03db, 0x07dd,
0x00b4, 0x03de, 0x01a9, 0x019b, 0x019c, 0x01a1, 0x01aa, 0x01ad,
0x01b3, 0x038b, 0x03b2, 0x03b8, 0x03ce, 0x03e1, 0x03e0, 0x07d2,
0x07e5, 0x00b7, 0x07e3, 0x01bb, 0x01a8, 0x01a6, 0x01b0, 0x01b2,
0x01b7, 0x039b, 0x039a, 0x03ba, 0x03b5, 0x03d6, 0x07d7, 0x03e4,
0x07d8, 0x07ea, 0x00ba, 0x07e8, 0x03a0, 0x01bd, 0x01b4, 0x038a,
0x01c4, 0x0392, 0x03aa, 0x03b0, 0x03bc, 0x03d7, 0x07d4, 0x07dc,
0x07db, 0x07d5, 0x07f0, 0x00c1, 0x07fb, 0x03c8, 0x03a3, 0x0395,
0x039d, 0x03ac, 0x03ae, 0x03c5, 0x03d8, 0x03e2, 0x03e6, 0x07e4,
0x07e7, 0x07e0, 0x07e9, 0x07f7, 0x0190, 0x07f2, 0x0393, 0x01be,
0x01c0, 0x0394, 0x0397, 0x03ad, 0x03c3, 0x03c1, 0x03d2, 0x07da,
0x07d9, 0x07df, 0x07eb, 0x07f4, 0x07fa, 0x0195, 0x07f8, 0x03bd,
0x039c, 0x03ab, 0x03a8, 0x03b3, 0x03b9, 0x03d0, 0x03e3, 0x03e5,
0x07e2, 0x07de, 0x07ed, 0x07f1, 0x07f9, 0x07fc, 0x0193, 0x0ffd,
0x03dc, 0x03b6, 0x03c7, 0x03cc, 0x03cb, 0x03d9, 0x03da, 0x07d3,
0x07e1, 0x07ee, 0x07ef, 0x07f5, 0x07f6, 0x0ffc, 0x0fff, 0x019d,
0x01c2, 0x00b5, 0x00a1, 0x0096, 0x0097, 0x0095, 0x0099, 0x00a0,
0x00a2, 0x00ac, 0x00a9, 0x00b1, 0x00b3, 0x00bb, 0x00c0, 0x018f,
0x0004
};
static const uint8_t raac_hcb11_bits[289] = {
4, 5, 6, 7, 8, 8, 9, 10, 10, 10, 11, 11, 12, 11, 12, 12,
10, 5, 4, 5, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 10, 10,
11, 8, 6, 5, 5, 6, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10,
10, 10, 8, 7, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 10, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9,
10, 10, 10, 10, 8, 8, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9,
9, 10, 10, 10, 10, 8, 9, 8, 8, 8, 8, 8, 8, 8, 9, 9,
9, 10, 10, 10, 10, 10, 8, 9, 8, 8, 8, 8, 8, 8, 9, 9,
9, 10, 10, 10, 10, 10, 10, 8, 10, 9, 8, 8, 9, 9, 9, 9,
9, 10, 10, 10, 10, 10, 10, 11, 8, 10, 9, 9, 9, 9, 9, 9,
9, 10, 10, 10, 10, 10, 10, 11, 11, 8, 11, 9, 9, 9, 9, 9,
9, 10, 10, 10, 10, 10, 11, 10, 11, 11, 8, 11, 10, 9, 9, 10,
9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 8, 11, 10, 10, 10,
10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 9, 11, 10, 9,
9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 9, 11, 10,
10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 9, 12,
10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 9,
9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9,
5
};
static const uint16_t raac_hcb_size[11] = {81,81,81,81,81,81,64,64,169,169,289};
static const uint16_t *const raac_hcb_code[11] = {
raac_hcb1_code, raac_hcb2_code, raac_hcb3_code, raac_hcb4_code, raac_hcb5_code, raac_hcb6_code, raac_hcb7_code, raac_hcb8_code, raac_hcb9_code, raac_hcb10_code, raac_hcb11_code
};
static const uint8_t *const raac_hcb_bits[11] = {
raac_hcb1_bits, raac_hcb2_bits, raac_hcb3_bits, raac_hcb4_bits, raac_hcb5_bits, raac_hcb6_bits, raac_hcb7_bits, raac_hcb8_bits, raac_hcb9_bits, raac_hcb10_bits, raac_hcb11_bits
};
static const uint32_t raac_sf_code[121] = {
0x3ffe8, 0x3ffe6, 0x3ffe7, 0x3ffe5, 0x7fff5, 0x7fff1, 0x7ffed, 0x7fff6,
0x7ffee, 0x7ffef, 0x7fff0, 0x7fffc, 0x7fffd, 0x7ffff, 0x7fffe, 0x7fff7,
0x7fff8, 0x7fffb, 0x7fff9, 0x3ffe4, 0x7fffa, 0x3ffe3, 0x1ffef, 0x1fff0,
0x0fff5, 0x1ffee, 0x0fff2, 0x0fff3, 0x0fff4, 0x0fff1, 0x07ff6, 0x07ff7,
0x03ff9, 0x03ff5, 0x03ff7, 0x03ff3, 0x03ff6, 0x03ff2, 0x01ff7, 0x01ff5,
0x00ff9, 0x00ff7, 0x00ff6, 0x007f9, 0x00ff4, 0x007f8, 0x003f9, 0x003f7,
0x003f5, 0x001f8, 0x001f7, 0x000fa, 0x000f8, 0x000f6, 0x00079, 0x0003a,
0x00038, 0x0001a, 0x0000b, 0x00004, 0x00000, 0x0000a, 0x0000c, 0x0001b,
0x00039, 0x0003b, 0x00078, 0x0007a, 0x000f7, 0x000f9, 0x001f6, 0x001f9,
0x003f4, 0x003f6, 0x003f8, 0x007f5, 0x007f4, 0x007f6, 0x007f7, 0x00ff5,
0x00ff8, 0x01ff4, 0x01ff6, 0x01ff8, 0x03ff8, 0x03ff4, 0x0fff0, 0x07ff4,
0x0fff6, 0x07ff5, 0x3ffe2, 0x7ffd9, 0x7ffda, 0x7ffdb, 0x7ffdc, 0x7ffdd,
0x7ffde, 0x7ffd8, 0x7ffd2, 0x7ffd3, 0x7ffd4, 0x7ffd5, 0x7ffd6, 0x7fff2,
0x7ffdf, 0x7ffe7, 0x7ffe8, 0x7ffe9, 0x7ffea, 0x7ffeb, 0x7ffe6, 0x7ffe0,
0x7ffe1, 0x7ffe2, 0x7ffe3, 0x7ffe4, 0x7ffe5, 0x7ffd7, 0x7ffec, 0x7fff4,
0x7fff3
};
static const uint8_t raac_sf_bits[121] = {
18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 18, 19, 18, 17, 17, 16, 17, 16, 16, 16, 16, 15, 15,
14, 14, 14, 14, 14, 14, 13, 13, 12, 12, 12, 11, 12, 11, 10, 10,
10, 9, 9, 8, 8, 8, 7, 6, 6, 5, 4, 3, 1, 4, 4, 5,
6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12,
12, 13, 13, 13, 14, 14, 16, 15, 16, 15, 18, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19
};
static const uint16_t raac_swb_offset_1024_16[44] = {
0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88,
100, 112, 124, 136, 148, 160, 172, 184, 196, 212, 228, 244,
260, 280, 300, 320, 344, 368, 396, 424, 456, 492, 532, 572,
616, 664, 716, 772, 832, 896, 960, 1024
};
static const uint16_t raac_swb_offset_1024_24[48] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44,
52, 60, 68, 76, 84, 92, 100, 108, 116, 124, 136, 148,
160, 172, 188, 204, 220, 240, 260, 284, 308, 336, 364, 396,
432, 468, 508, 552, 600, 652, 704, 768, 832, 896, 960, 1024
};
static const uint16_t raac_swb_offset_1024_32[52] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48,
56, 64, 72, 80, 88, 96, 108, 120, 132, 144, 160, 176,
196, 216, 240, 264, 292, 320, 352, 384, 416, 448, 480, 512,
544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896,
928, 960, 992, 1024
};
static const uint16_t raac_swb_offset_1024_48[50] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 48,
56, 64, 72, 80, 88, 96, 108, 120, 132, 144, 160, 176,
196, 216, 240, 264, 292, 320, 352, 384, 416, 448, 480, 512,
544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896,
928, 1024
};
static const uint16_t raac_swb_offset_1024_64[48] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44,
48, 52, 56, 64, 72, 80, 88, 100, 112, 124, 140, 156,
172, 192, 216, 240, 268, 304, 344, 384, 424, 464, 504, 544,
584, 624, 664, 704, 744, 784, 824, 864, 904, 944, 984, 1024
};
static const uint16_t raac_swb_offset_1024_8[41] = {
0, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132,
144, 156, 172, 188, 204, 220, 236, 252, 268, 288, 308, 328,
348, 372, 396, 420, 448, 476, 508, 544, 580, 620, 664, 712,
764, 820, 880, 944, 1024
};
static const uint16_t raac_swb_offset_1024_96[42] = {
0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44,
48, 52, 56, 64, 72, 80, 88, 96, 108, 120, 132, 144,
156, 172, 188, 212, 240, 276, 320, 384, 448, 512, 576, 640,
704, 768, 832, 896, 960, 1024
};
static const uint16_t *const raac_swb_offset_1024[13] = {
raac_swb_offset_1024_96, raac_swb_offset_1024_96, raac_swb_offset_1024_64,
raac_swb_offset_1024_48, raac_swb_offset_1024_48, raac_swb_offset_1024_32,
raac_swb_offset_1024_24, raac_swb_offset_1024_24, raac_swb_offset_1024_16,
raac_swb_offset_1024_16, raac_swb_offset_1024_16, raac_swb_offset_1024_8,
raac_swb_offset_1024_8
};
static const uint8_t raac_num_swb_1024[13] = {
41, 41, 47, 49, 49, 51, 47, 47, 43, 43, 43, 40, 40
};
static const unsigned raac_sample_rates[13] = {
96000, 88200, 64000, 48000, 44100, 32000,
24000, 22050, 16000, 12000, 11025, 8000, 7350
};
#endif
File diff suppressed because it is too large. Load diff
+806 -8
View File
@@ -127,6 +127,12 @@ static size_t audio_transfer_ogg_page(const uint8_t *buf, size_t size,
#ifdef HAVE_RLPCM
#include <formats/rlpcm.h>
#endif
#ifdef HAVE_RATRAC3
#include <formats/ratrac3.h>
#endif
#ifdef HAVE_RATRAC3P
#include <formats/ratrac3p.h>
#endif
#ifdef HAVE_RAAC
#include <formats/raac.h>
#ifdef HAVE_RMP4
@@ -493,7 +499,8 @@ static size_t audio_transfer_ogg_page(const uint8_t *buf, size_t size,
* audio_transfer_webm_audio_type), .ogg being ambiguous between Vorbis
* and Opus in any case. */
#if defined(HAVE_RWAV) || defined(HAVE_RAC3)
#if defined(HAVE_RWAV) || defined(HAVE_RAC3) || defined(HAVE_RATRAC3) \
|| defined(HAVE_RATRAC3P)
/* Unit-scale float (full scale +-1.0) to s16, the conversion raac
* applies at its own edge, for the arms whose float arrives at unit
* scale already (float WAV, AC-3). A float source can carry anything, including
@@ -513,6 +520,12 @@ static int16_t audio_transfer_unit_to_s16(float unit)
}
#endif
static uint32_t audio_transfer_le32(const uint8_t *p)
{
return (uint32_t)p[0] | ((uint32_t)p[1] << 8)
| ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
/* One transfer context per codec. Each backend keeps only what it needs;
* the enum 'type' handed to every entry point selects which arm runs, the
* same switch-dispatch pattern formats/image_transfer.c uses. */
@@ -1407,6 +1420,337 @@ static int audio_transfer_ac3_read(struct audio_transfer_ac3 *a,
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
/* ATRAC3 and ATRAC3plus: OpenMG files (.oma, .aa3: an optional "ea3"
* tag, then the 96-byte EA3 header naming the codec and its frame
* size) and WAVE files (.at3: format tag 270h, or FFFEh with the
* ATRAC3plus sub-format). Frames are block_align bytes, 1024 samples a
* channel for ATRAC3 and 2048 for ATRAC3plus, decoded one at a time
* into a pending block the reads drain. Encrypted OpenMG is refused.
*
* ATRAC3plus decodes in Sony's order - FL FR FC, then the back pair,
* the back centre or the side pair, and LFE last - which is put into
* the WAV shape for the count, as the AC-3 arm does: 5.1, 6.1 and 7.1
* reordered, 6.1's back pair in WAV 7's side slots (the fold treats
* them alike), and 4.0's lone back centre split into a phantom pair at
* -3 dB each, five channels out. A frame that does not parse comes out
* as silence, the decoders keeping their history. */
struct audio_transfer_atrac
{
const uint8_t *buf;
size_t buf_size;
size_t avail; /* resident prefix, 0 = all */
size_t data, data_end;
size_t pos; /* the next frame's offset */
unsigned block_align;
unsigned spf; /* samples a channel a frame */
unsigned dec_channels; /* the stream's */
unsigned channels; /* handed over, in the WAV shape */
unsigned rate;
bool plus;
bool joint;
bool identity;
bool ended;
signed char map[8]; /* output channel: the decoded one */
float gain[8];
#ifdef HAVE_RATRAC3
ratrac3_t *d3;
#endif
#ifdef HAVE_RATRAC3P
ratrac3p_t *d3p;
#endif
float *raw; /* spf * dec_channels */
float *pend; /* spf * channels */
size_t pend_n, pend_off;
uint64_t total_frames;
uint64_t frame_pos;
};
static unsigned audio_transfer_be16(const uint8_t *p)
{
return ((unsigned)p[0] << 8) | p[1];
}
/* The container: codec, frame size, rate and where the frames are */
static bool audio_transfer_atrac_parse(struct audio_transfer_atrac *a)
{
static const unsigned rates[8] = { 32000, 44100, 48000, 88200, 96000, 0, 0, 0 };
static const unsigned plus_ch[8] = { 0, 1, 2, 3, 4, 6, 7, 8 };
const uint8_t *b = a->buf;
size_t n = a->buf_size, off = 0;
if (n >= 12 && !memcmp(b, "RIFF", 4) && !memcmp(b + 8, "WAVE", 4))
{
size_t c = 12;
bool fmt = false;
while (c + 8 <= n)
{
size_t len = audio_transfer_le32(b + c + 4);
if (len > n - c - 8)
len = n - c - 8;
if (!memcmp(b + c, "fmt ", 4))
{
#ifdef HAVE_RATRAC3P
ratrac3p_info_t pi;
if (ratrac3p_parse_wave_fmt(b + c + 8, len, &pi))
{
a->plus = true; a->dec_channels = pi.channels;
a->rate = pi.sample_rate; a->block_align = pi.block_align;
fmt = true;
}
#endif
#ifdef HAVE_RATRAC3
if (!fmt)
{
ratrac3_info_t ti;
if (ratrac3_parse_wave_fmt(b + c + 8, len, &ti))
{
a->plus = false; a->dec_channels = ti.channels;
a->rate = ti.sample_rate; a->block_align = ti.block_align;
a->joint = ti.joint_stereo;
fmt = true;
}
}
#endif
if (!fmt)
return false;
}
else if (!memcmp(b + c, "data", 4) && fmt)
{
a->data = c + 8;
a->data_end = c + 8 + len;
return true;
}
c += 8 + len + (len & 1);
}
return false;
}
/* OpenMG: "ea3" tag (an ID3v2.3 tag by another name), then "EA3" */
if (n >= 10 && !memcmp(b, "ea3", 3))
off = 10 + (((size_t)(b[6] & 0x7f) << 21) | ((size_t)(b[7] & 0x7f) << 14)
| ((size_t)(b[8] & 0x7f) << 7) | (b[9] & 0x7f));
if (off > n || n - off < 96 || memcmp(b + off, "EA3", 3))
return false;
{
const uint8_t *h = b + off;
unsigned hsize = audio_transfer_be16(h + 4);
unsigned eid = audio_transfer_be16(h + 6);
unsigned codec = h[32];
uint32_t params = ((uint32_t)h[33] << 16) | ((uint32_t)h[34] << 8) | h[35];
if (eid != 0xFFFF && eid != 0xFF80)
return false; /* encrypted */
if (hsize < 96 || hsize > n - off)
return false;
a->rate = rates[(params >> 13) & 7];
switch (codec)
{
#ifdef HAVE_RATRAC3
case 0:
a->plus = false;
a->dec_channels = 2;
a->joint = ((params >> 17) & 1) != 0;
a->block_align = (unsigned)(params & 0x3FF) * 8;
break;
#endif
#ifdef HAVE_RATRAC3P
case 1:
a->plus = true;
a->dec_channels = plus_ch[(params >> 10) & 7];
a->block_align = (unsigned)(params & 0x3FF) * 8 + 8;
break;
#endif
default:
return false;
}
a->data = off + hsize;
a->data_end = n;
}
(void)plus_ch;
return a->rate && a->dec_channels && a->block_align;
}
/* the WAV shape for ATRAC3plus's channel count (see above) */
static void audio_transfer_atrac_map(struct audio_transfer_atrac *a)
{
static const signed char maps[9][8] = {
{ 0 }, { 0 }, { 0, 1 }, { 0, 1, 2 },
{ 0, 1, 2, 3, 3 }, /* FL FR FC BC: BC split */
{ 0 },
{ 0, 1, 2, 5, 3, 4 }, /* FL FR FC BL BR LFE */
{ 0, 1, 2, 6, 5, 3, 4 }, /* FL FR FC BL BR BC LFE */
{ 0, 1, 2, 7, 3, 4, 5, 6 } /* FL FR FC BL BR SL SR LFE */
};
unsigned i, n = a->dec_channels;
a->channels = n == 4 ? 5 : n;
for (i = 0; i < a->channels; i++)
{
a->map[i] = a->plus ? maps[n][i] : (signed char)i;
a->gain[i] = (a->plus && n == 4 && i >= 3) ? 0.70710678f : 1.0f;
}
a->identity = true;
for (i = 0; i < a->channels; i++)
if (a->map[i] != (signed char)i || a->gain[i] != 1.0f)
a->identity = false;
if (a->channels != n)
a->identity = false;
}
static size_t audio_transfer_atrac_end(const struct audio_transfer_atrac *a)
{
size_t end = a->data_end;
if (a->avail && a->avail < end)
end = a->avail;
return end;
}
/* Decodes the frame at pos into @fdst (floats) or @sdst (s16) in the
* WAV shape and advances pos: 1 with @frames, 0 at the end of the
* frames or the frontier */
static int audio_transfer_atrac_next_into(struct audio_transfer_atrac *a,
float *fdst, int16_t *sdst, size_t *frames)
{
size_t end = audio_transfer_atrac_end(a), i;
float *dec;
unsigned c;
int r = -1;
if (a->pos >= end || a->block_align > end - a->pos)
return 0;
dec = (a->identity && fdst) ? fdst : a->raw;
#ifdef HAVE_RATRAC3P
if (a->plus)
r = ratrac3p_decode_float(a->d3p, a->buf + a->pos, a->block_align, dec);
#endif
#ifdef HAVE_RATRAC3
if (!a->plus)
r = ratrac3_decode_float(a->d3, a->buf + a->pos, a->block_align, dec);
#endif
(void)r; /* a frame that does not parse is silence */
if (dec != fdst)
{
if (a->identity)
for (i = 0; i < (size_t)a->spf * a->channels; i++)
sdst[i] = audio_transfer_unit_to_s16(a->raw[i]);
else
for (i = 0; i < a->spf; i++)
{
const float *in = a->raw + i * a->dec_channels;
for (c = 0; c < a->channels; c++)
{
float v = in[a->map[c]] * a->gain[c];
if (fdst)
fdst[i * a->channels + c] = v;
else
sdst[i * a->channels + c] = audio_transfer_unit_to_s16(v);
}
}
}
a->pos += a->block_align;
*frames = a->spf;
return 1;
}
static int audio_transfer_atrac_read(struct audio_transfer_atrac *a,
float *out_f32, int16_t *out_s16, size_t frames, size_t *frames_out)
{
size_t produced = 0;
if (!a || !a->raw)
return AUDIO_PROCESS_ERROR;
while (produced < frames)
{
size_t take, i, n;
const float *src;
if (a->pend_off >= a->pend_n)
{
size_t got = 0;
int r;
if (a->ended)
break;
/* room for a whole frame: straight into the caller's buffer */
if (frames - produced >= a->spf)
{
r = audio_transfer_atrac_next_into(a,
out_f32 ? out_f32 + produced * a->channels : NULL,
out_f32 ? NULL : out_s16 + produced * a->channels, &got);
if (r == 1)
{
a->frame_pos += got;
produced += got;
continue;
}
}
else
{
r = audio_transfer_atrac_next_into(a, a->pend, NULL, &got);
if (r == 1)
{
a->pend_n = got;
a->pend_off = 0;
}
}
if (r == 0)
{
if (audio_transfer_atrac_end(a) >= a->data_end)
a->ended = true;
break;
}
}
take = a->pend_n - a->pend_off;
if (take > frames - produced)
take = frames - produced;
src = a->pend + a->pend_off * a->channels;
n = take * a->channels;
if (out_f32)
memcpy(out_f32 + produced * a->channels, src, n * sizeof(float));
else
for (i = 0; i < n; i++)
out_s16[produced * a->channels + i] = audio_transfer_unit_to_s16(src[i]);
a->pend_off += take;
a->frame_pos += take;
produced += take;
}
if (frames_out)
*frames_out = produced;
if (produced == 0 && a->ended)
return AUDIO_PROCESS_END;
return AUDIO_PROCESS_NEXT;
}
/* Fresh history: the frame before the target decoded and dropped, so
* the overlap is what a playthrough would have left */
static bool audio_transfer_atrac_seek_to(struct audio_transfer_atrac *a, uint64_t frame)
{
uint64_t index = frame / a->spf;
size_t got;
if (frame >= a->total_frames)
return false;
#ifdef HAVE_RATRAC3P
if (a->plus)
ratrac3p_reset(a->d3p);
#endif
#ifdef HAVE_RATRAC3
if (!a->plus)
ratrac3_reset(a->d3);
#endif
a->ended = false;
a->pend_n = a->pend_off = 0;
if (index)
{
a->pos = a->data + (size_t)(index - 1) * a->block_align;
audio_transfer_atrac_next_into(a, a->pend, NULL, &got);
}
a->pos = a->data + (size_t)index * a->block_align;
a->frame_pos = index * a->spf;
if (audio_transfer_atrac_next_into(a, a->pend, NULL, &got) == 1)
{
a->pend_n = got;
a->pend_off = (size_t)(frame - a->frame_pos);
a->frame_pos = frame;
}
return true;
}
#endif
#ifdef HAVE_RAAC
struct audio_transfer_aac
{
@@ -1577,6 +1921,10 @@ void *audio_transfer_new(enum audio_type_enum type)
case AUDIO_TYPE_AAC:
return calloc(1, sizeof(struct audio_transfer_aac));
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
return calloc(1, sizeof(struct audio_transfer_atrac));
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
return calloc(1, sizeof(struct audio_transfer_ac3));
@@ -1682,6 +2030,18 @@ void audio_transfer_set_buffer_ptr(void *data, enum audio_type_enum type,
break;
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
{
struct audio_transfer_atrac *a = (struct audio_transfer_atrac*)data;
if (a)
{
a->buf = (const uint8_t*)ptr;
a->buf_size = len;
}
break;
}
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
{
@@ -2387,6 +2747,15 @@ void audio_transfer_set_avail(void *data, enum audio_type_enum type,
return;
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
{
struct audio_transfer_atrac *a = (struct audio_transfer_atrac*)data;
if (a)
a->avail = avail;
break;
}
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
{
@@ -2569,6 +2938,228 @@ enum audio_type_enum audio_transfer_webm_audio_type(const void *buf,
#endif
}
/* ---- a file's arm from its bytes ------------------------------------ */
#ifdef HAVE_RMP3
/* An MPEG audio frame header's frame length (layers I to III, MPEG-1,
* 2 and 2.5), 0 when @h is not one; free format is not taken */
static size_t audio_transfer_mpa_len(const uint8_t *h)
{
static const uint16_t kbps[2][3][15] = {
{ { 0,32,64,96,128,160,192,224,256,288,320,352,384,416,448 },
{ 0,32,48,56,64,80,96,112,128,160,192,224,256,320,384 },
{ 0,32,40,48,56,64,80,96,112,128,160,192,224,256,320 } },
{ { 0,32,48,56,64,80,96,112,128,144,160,176,192,224,256 },
{ 0,8,16,24,32,40,48,56,64,80,96,112,128,144,160 },
{ 0,8,16,24,32,40,48,56,64,80,96,112,128,144,160 } }
};
static const uint16_t rates[3] = { 44100, 48000, 32000 };
unsigned ver, layer, bi, si, pad, rate, lsf;
uint32_t br;
if (h[0] != 0xFF || (h[1] & 0xE0) != 0xE0)
return 0;
ver = (h[1] >> 3) & 3; /* 0 MPEG-2.5, 1 reserved, 2 MPEG-2, 3 MPEG-1 */
layer = 4 - ((h[1] >> 1) & 3);
bi = h[2] >> 4;
si = (h[2] >> 2) & 3;
pad = (h[2] >> 1) & 1;
if (ver == 1 || layer == 4 || bi == 0 || bi == 15 || si == 3)
return 0;
lsf = ver != 3;
rate = rates[si] >> (ver == 3 ? 0 : ver == 2 ? 1 : 2);
br = (uint32_t)kbps[lsf][layer - 1][bi] * 1000;
if (layer == 1)
return (size_t)((12 * br / rate + pad) * 4);
if (layer == 3 && lsf)
return (size_t)(72 * br / rate + pad);
return (size_t)(144 * br / rate + pad);
}
#endif
/* The length of the raw-stream frame at @p of @type's syntax, 0 when
* there is none there */
static size_t audio_transfer_raw_frame(const uint8_t *p, size_t n,
enum audio_type_enum type)
{
switch (type)
{
#ifdef HAVE_RMP3
case AUDIO_TYPE_MP3:
return n >= 4 ? audio_transfer_mpa_len(p) : 0;
#endif
#ifdef HAVE_RAAC
case AUDIO_TYPE_AAC:
{
raac_adts_t h;
return raac_adts_parse(p, n, &h) == 0 ? h.frame_size : 0;
}
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
{
rac3_frame_info_t info;
return rac3_parse_frame_info(p, n, &info) == RAC3_OK
? info.frame_bytes : 0;
}
#endif
default:
break;
}
(void)p; (void)n;
return 0;
}
enum audio_type_enum audio_transfer_sniff_type(const void *buf, size_t len,
size_t *skip)
{
const uint8_t *b = (const uint8_t*)buf;
size_t at = 0, i;
if (skip)
*skip = 0;
if (!b)
return AUDIO_TYPE_NONE;
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
/* OpenMG: its "ea3" tag, or the EA3 header itself; the arm reads
* both */
if (len >= 10 && (!memcmp(b, "ea3", 3) || !memcmp(b, "EA3", 3)))
return AUDIO_TYPE_ATRAC;
#endif
/* ID3v2 tags in front of a stream, each 10 bytes, a syncsafe size
* and 10 more for a footer */
while (len - at >= 10 && !memcmp(b + at, "ID3", 3) && b[at + 3] != 0xFF
&& !((b[at + 6] | b[at + 7] | b[at + 8] | b[at + 9]) & 0x80))
{
size_t tag = 10 + (((size_t)b[at + 6] << 21) | ((size_t)b[at + 7] << 14)
| ((size_t)b[at + 8] << 7) | b[at + 9]) + ((b[at + 5] & 0x10) ? 10 : 0);
if (tag > len - at)
return AUDIO_TYPE_NONE;
at += tag;
}
b += at;
len -= at;
if (skip)
*skip = at;
#ifdef HAVE_RFLAC
if (len >= 4 && !memcmp(b, "fLaC", 4))
return AUDIO_TYPE_FLAC;
#endif
if (len >= 4 && !memcmp(b, "OggS", 4))
return audio_transfer_ogg_audio_type(b, len);
if (len >= 4 && b[0] == 0x1A && b[1] == 0x45 && b[2] == 0xDF && b[3] == 0xA3)
return audio_transfer_webm_audio_type(b, len);
if (len >= 12 && !memcmp(b, "RIFF", 4))
{
size_t c = 12;
#ifdef HAVE_RWMA
if (!memcmp(b + 8, "XWMA", 4))
return AUDIO_TYPE_WMA;
#endif
if (memcmp(b + 8, "WAVE", 4))
return AUDIO_TYPE_NONE;
/* the format tag says which arm */
while (c + 8 <= len)
{
uint32_t clen = audio_transfer_le32(b + c + 4);
if (!memcmp(b + c, "fmt ", 4) && clen >= 2 && len - c - 8 >= 2)
{
unsigned tag = b[c + 8] | ((unsigned)b[c + 9] << 8);
if (tag == 0xFFFE && clen >= 26 && len - c - 8 >= 26)
tag = b[c + 32] | ((unsigned)b[c + 33] << 8);
#ifdef HAVE_RWAV
if (tag == 1 || tag == 3)
return AUDIO_TYPE_WAV;
#endif
#ifdef HAVE_RWMA
if (tag == 0x0161 || tag == 0x0162 || tag == 0x0163)
return AUDIO_TYPE_WMA;
#endif
#ifdef HAVE_RATRAC3
if (tag == 0x0270)
return AUDIO_TYPE_ATRAC;
#endif
#ifdef HAVE_RATRAC3P
{
/* the ATRAC3plus sub-format GUID */
static const uint8_t g[16] = { 0xBF,0xAA,0x23,0xE9,0x58,0xCB,0x71,0x44,
0xA1,0x19,0xFF,0xFA,0x01,0xE4,0xCE,0x62 };
if ( b[c + 8] == 0xFE && b[c + 9] == 0xFF && clen >= 40
&& len - c - 8 >= 40 && !memcmp(b + c + 32, g, 16))
return AUDIO_TYPE_ATRAC;
}
#endif
return AUDIO_TYPE_NONE;
}
if (clen > len - c - 8)
break;
c += 8 + clen + (clen & 1);
}
return AUDIO_TYPE_NONE;
}
#ifdef HAVE_RWMA
{
static const uint8_t asf[16] = { 0x30,0x26,0xB2,0x75,0x8E,0x66,0xCF,0x11,
0xA6,0xD9,0x00,0xAA,0x00,0x62,0xCE,0x6C };
if (len >= 16 && !memcmp(b, asf, 16))
return AUDIO_TYPE_WMA;
}
#endif
#if defined(HAVE_RAAC) && defined(HAVE_RMP4)
if (len >= 8 && (!memcmp(b + 4, "ftyp", 4) || !memcmp(b + 4, "moov", 4)))
return AUDIO_TYPE_AAC;
#endif
#ifdef HAVE_RMODTRACKER
if ( (len >= 17 && !memcmp(b, "Extended Module: ", 17))
|| (len >= 4 && !memcmp(b, "IMPM", 4))
|| (len >= 48 && !memcmp(b + 44, "SCRM", 4)))
return AUDIO_TYPE_MOD;
if (len >= 1084)
{
static const char tags[][5] = { "M.K.", "M!K!", "FLT4", "FLT8", "EXO4",
"EXO8", "OKTA", "OCTA", "CD81", "FA08" };
const uint8_t *t = b + 1080;
for (i = 0; i < sizeof(tags) / sizeof(tags[0]); i++)
if (!memcmp(t, tags[i], 4))
return AUDIO_TYPE_MOD;
if ( (t[0] >= '1' && t[0] <= '9' && !memcmp(t + 1, "CHN", 3))
|| (t[0] >= '1' && t[0] <= '9' && t[1] >= '0' && t[1] <= '9'
&& !memcmp(t + 2, "CH", 2)))
return AUDIO_TYPE_MOD;
}
#endif
/* Raw streams: a frame header, and another where it says the next
* one is (or the end of the file), at the start or after a little
* junk. */
{
static const enum audio_type_enum raw[3] = {
AUDIO_TYPE_AC3, AUDIO_TYPE_AAC, AUDIO_TYPE_MP3 };
size_t limit = len < 65536 ? len : 65536;
for (i = 0; i + 4 <= limit; i++)
{
unsigned k;
for (k = 0; k < 3; k++)
{
size_t f = audio_transfer_raw_frame(b + i, len - i, raw[k]);
if ( f && f <= len - i
&& (f == len - i
|| audio_transfer_raw_frame(b + i + f, len - i - f, raw[k])))
{
if (skip)
*skip = at + i;
return raw[k];
}
}
}
}
(void)i;
return AUDIO_TYPE_NONE;
}
bool audio_transfer_set_start_trim(void *data, enum audio_type_enum type,
uint64_t frames)
{
@@ -2687,16 +3278,22 @@ static int audio_transfer_mp3_gapless(const uint8_t *b, size_t len,
if (!frames)
return 0;
/* The LAME tag starts here; its delay and padding are twelve
* bits each, packed into three bytes at offset 21. */
* bits each, packed into three bytes at offset 21. Its delay is
* the encoder's; the decoder's own, 529 samples, comes on top,
* as rmp3's pull API (RMP3_DECODER_DELAY) and FFmpeg take it -
* without it the audio started 529 samples late and its last
* 529 were cut. The padding is net of it already. */
if ((size_t)(p - b) + 24 <= len)
{
delay = ((uint64_t)p[21] << 4) | (p[22] >> 4);
pad = ((uint64_t)(p[22] & 0xf) << 8) | p[23];
*out_delay = (uint64_t)spf + delay + RMP3_DECODER_DELAY;
}
else
*out_delay = (uint64_t)spf;
/* The filler frame is decoded like any other, so it is priming
* too. What is left to play is the stated audio less both
* ends. */
*out_delay = (uint64_t)spf + delay;
*out_frames = (uint64_t)frames * spf;
*out_frames = (*out_frames > delay + pad)
? *out_frames - delay - pad : 0;
@@ -4033,6 +4630,39 @@ bool audio_transfer_start(void *data, enum audio_type_enum type)
return true;
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
{
struct audio_transfer_atrac *a = (struct audio_transfer_atrac*)data;
if (!a || !a->buf || !audio_transfer_atrac_parse(a))
return false;
a->spf = a->plus ? 2048 : 1024;
audio_transfer_atrac_map(a);
#ifdef HAVE_RATRAC3P
if (a->plus && !(a->d3p = ratrac3p_new(a->dec_channels, a->block_align)))
return false;
#endif
#ifdef HAVE_RATRAC3
if (!a->plus && !(a->d3 = ratrac3_new(a->dec_channels, a->block_align, a->joint)))
return false;
#endif
a->raw = (float*)malloc((size_t)a->spf * a->dec_channels * sizeof(float));
a->pend = (float*)malloc((size_t)a->spf * a->channels * sizeof(float));
if (!a->raw || !a->pend)
{
free(a->raw);
free(a->pend);
a->raw = a->pend = NULL;
return false;
}
a->total_frames = (uint64_t)((a->data_end - a->data) / a->block_align) * a->spf;
a->pos = a->data;
a->pend_n = a->pend_off = 0;
a->frame_pos = 0;
a->ended = false;
return true;
}
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
{
@@ -4294,6 +4924,10 @@ bool audio_transfer_start(void *data, enum audio_type_enum type)
return false;
}
ac->channels = raac_channels(ac->handle);
/* the WAVE order every arm but Vorbis and Opus hands over, and
* audio_transfer_fold_q15 folds by; a layout raac has no WAVE
* map for stays in stream order */
raac_set_channel_order(ac->handle, RAAC_ORDER_WAVE);
ac->trim_left = ac->start_trim;
ac->emitted = 0;
ac->pkt_index = 0;
@@ -4360,6 +4994,13 @@ bool audio_transfer_is_valid(void *data, enum audio_type_enum type)
return op && op->handle;
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
{
struct audio_transfer_atrac *a = (struct audio_transfer_atrac*)data;
return a && a->raw;
}
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
{
@@ -4388,11 +5029,106 @@ bool audio_transfer_is_valid(void *data, enum audio_type_enum type)
return false;
}
/* ---- folding to stereo ----------------------------------------------
*
* The arms do no channel conversion: wider than stereo comes out as the
* codec orders it, and the consumer folds. Vorbis and Opus (mapping
* family 1) order the channels one way (Vorbis I 4.3.9), everything
* else - WAV, FLAC, AAC (put into this order at start), AC-3 (shaped
* into it), MP3 - the WAVEFORMATEXTENSIBLE way:
*
* Vorbis 3 L C R 4 L R RL RR 5 L C R RL RR
* 6 L C R RL RR LFE 7 L C R RL RR BC LFE
* 8 L C R RL RR SL SR LFE
* WAV 3 FL FR FC 4 FL FR BL BR 5 FL FR FC BL BR
* 6 FL FR FC LFE BL BR 7 FL FR FC LFE BC SL SR
* 8 FL FR FC LFE BL BR SL SR
*
* Folding one with the other's table is not a subtle error: on 5.1 it
* sends front-right to both sides, front-centre to the right alone,
* LFE into the mix and back-right nowhere.
*
* Coefficients are the usual ITU-R BS.775 ones: centre and surrounds
* enter both sides at -3 dB, a lone back centre at -6 dB into each, LFE
* is dropped. Not normalised: scaling by the coefficient sum would put
* 5.1 content 7.7 dB below the same material in a stereo file, which is
* a surprising thing for a file's channel count to do to its volume;
* the sum only reaches full scale when the channels are correlated.
* Q15 in int32: unity is 32768, which an int16 would wrap. */
#define AT_FOLD_UNITY 32768
#define AT_FOLD_M3DB 23170
#define AT_FOLD_M6DB 16384
static const int32_t audio_transfer_fold_vorbis[9][8][2] = {
{ {0,0} },
{ {AT_FOLD_UNITY,AT_FOLD_UNITY} },
{ {AT_FOLD_UNITY,0}, {0,AT_FOLD_UNITY} },
/* 3: L C R */
{ {AT_FOLD_UNITY,0}, {AT_FOLD_M3DB,AT_FOLD_M3DB}, {0,AT_FOLD_UNITY} },
/* 4: L R RL RR */
{ {AT_FOLD_UNITY,0}, {0,AT_FOLD_UNITY},
{AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB} },
/* 5: L C R RL RR */
{ {AT_FOLD_UNITY,0}, {AT_FOLD_M3DB,AT_FOLD_M3DB}, {0,AT_FOLD_UNITY},
{AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB} },
/* 6: L C R RL RR LFE */
{ {AT_FOLD_UNITY,0}, {AT_FOLD_M3DB,AT_FOLD_M3DB}, {0,AT_FOLD_UNITY},
{AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB}, {0,0} },
/* 7: L C R RL RR BC LFE */
{ {AT_FOLD_UNITY,0}, {AT_FOLD_M3DB,AT_FOLD_M3DB}, {0,AT_FOLD_UNITY},
{AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB},
{AT_FOLD_M6DB,AT_FOLD_M6DB}, {0,0} },
/* 8: L C R RL RR SL SR LFE */
{ {AT_FOLD_UNITY,0}, {AT_FOLD_M3DB,AT_FOLD_M3DB}, {0,AT_FOLD_UNITY},
{AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB},
{AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB}, {0,0} }
};
static const int32_t audio_transfer_fold_wav[9][8][2] = {
{ {0,0} },
{ {AT_FOLD_UNITY,AT_FOLD_UNITY} },
{ {AT_FOLD_UNITY,0}, {0,AT_FOLD_UNITY} },
/* 3: FL FR FC */
{ {AT_FOLD_UNITY,0}, {0,AT_FOLD_UNITY}, {AT_FOLD_M3DB,AT_FOLD_M3DB} },
/* 4: FL FR BL BR */
{ {AT_FOLD_UNITY,0}, {0,AT_FOLD_UNITY},
{AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB} },
/* 5: FL FR FC BL BR */
{ {AT_FOLD_UNITY,0}, {0,AT_FOLD_UNITY}, {AT_FOLD_M3DB,AT_FOLD_M3DB},
{AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB} },
/* 6: FL FR FC LFE BL BR */
{ {AT_FOLD_UNITY,0}, {0,AT_FOLD_UNITY}, {AT_FOLD_M3DB,AT_FOLD_M3DB},
{0,0}, {AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB} },
/* 7: FL FR FC LFE BC SL SR */
{ {AT_FOLD_UNITY,0}, {0,AT_FOLD_UNITY}, {AT_FOLD_M3DB,AT_FOLD_M3DB},
{0,0}, {AT_FOLD_M6DB,AT_FOLD_M6DB},
{AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB} },
/* 8: FL FR FC LFE BL BR SL SR */
{ {AT_FOLD_UNITY,0}, {0,AT_FOLD_UNITY}, {AT_FOLD_M3DB,AT_FOLD_M3DB},
{0,0}, {AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB},
{AT_FOLD_M3DB,0}, {0,AT_FOLD_M3DB} }
};
const int32_t (*audio_transfer_fold_q15(enum audio_type_enum type,
unsigned channels))[2]
{
if (channels < 1 || channels > 8)
return NULL;
if (type == AUDIO_TYPE_VORBIS || type == AUDIO_TYPE_OPUS)
return audio_transfer_fold_vorbis[channels];
return audio_transfer_fold_wav[channels];
}
bool audio_transfer_stereo_matrix(void *data, enum audio_type_enum type,
int32_t matrix[8][2])
{
const int32_t (*fold)[2];
unsigned ch = 0;
if (!data || !matrix)
return false;
#ifdef HAVE_RWMA
if (data && matrix && type == AUDIO_TYPE_WMA)
/* by the stream's speaker mask */
if (type == AUDIO_TYPE_WMA)
{
static const uint32_t defaults[9] = {0,4,3,7,51,55,63,1807,1599};
struct audio_transfer_wma *a = (struct audio_transfer_wma*)data;
@@ -4425,10 +5161,14 @@ bool audio_transfer_stereo_matrix(void *data, enum audio_type_enum type,
}
return lane == a->channels;
}
#else
(void)data; (void)type; (void)matrix;
#endif
return false;
/* by the channel count, in the order the arm hands them over */
if ( !audio_transfer_info(data, type, &ch, NULL, NULL)
|| !(fold = audio_transfer_fold_q15(type, ch)))
return false;
memset(matrix, 0, 8 * 2 * sizeof(int32_t));
memcpy(matrix, fold, ch * 2 * sizeof(int32_t));
return true;
}
bool audio_transfer_stereo_matrix_f32(void *data, enum audio_type_enum type,
@@ -4455,7 +5195,9 @@ bool audio_transfer_info(void *data, enum audio_type_enum type,
if (!a || !a->stream) return false;
if (channels) *channels = a->channels;
if (rate) *rate = a->rate;
if (total_frames) *total_frames = 0;
/* what the container states: the decode runs on past it by
* the codec's priming */
if (total_frames) *total_frames = rwma_stream_frames(a->stream);
return true;
}
#endif
@@ -4602,6 +5344,18 @@ bool audio_transfer_info(void *data, enum audio_type_enum type,
return true;
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
{
struct audio_transfer_atrac *a = (struct audio_transfer_atrac*)data;
if (!a || !a->raw)
return false;
if (channels) *channels = a->channels;
if (rate) *rate = a->rate;
if (total_frames) *total_frames = a->total_frames;
return true;
}
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
{
@@ -6096,6 +6850,11 @@ int audio_transfer_read_s16(void *data, enum audio_type_enum type,
break;
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
return audio_transfer_atrac_read((struct audio_transfer_atrac*)data,
NULL, out, frames, frames_out);
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
return audio_transfer_ac3_read((struct audio_transfer_ac3*)data,
@@ -6420,6 +7179,11 @@ int audio_transfer_read_f32(void *data, enum audio_type_enum type,
break;
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
return audio_transfer_atrac_read((struct audio_transfer_atrac*)data,
out, NULL, frames, frames_out);
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
return audio_transfer_ac3_read((struct audio_transfer_ac3*)data,
@@ -6635,6 +7399,13 @@ size_t audio_transfer_buffer_tell(void *data, enum audio_type_enum type)
return 0;
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
{
struct audio_transfer_atrac *a = (struct audio_transfer_atrac*)data;
return a ? a->pos : 0;
}
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
{
@@ -6991,6 +7762,15 @@ bool audio_transfer_seek(void *data, enum audio_type_enum type,
return true;
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
{
struct audio_transfer_atrac *a = (struct audio_transfer_atrac*)data;
if (!a || !a->raw)
return false;
return audio_transfer_atrac_seek_to(a, frame);
}
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
{
@@ -7157,6 +7937,24 @@ void audio_transfer_free(void *data, enum audio_type_enum type)
break;
}
#endif
#if defined(HAVE_RATRAC3) || defined(HAVE_RATRAC3P)
case AUDIO_TYPE_ATRAC:
{
struct audio_transfer_atrac *a = (struct audio_transfer_atrac*)data;
if (a)
{
#ifdef HAVE_RATRAC3P
ratrac3p_free(a->d3p);
#endif
#ifdef HAVE_RATRAC3
ratrac3_free(a->d3);
#endif
free(a->raw);
free(a->pend);
}
break;
}
#endif
#ifdef HAVE_RAC3
case AUDIO_TYPE_AC3:
{
@@ -0,0 +1,248 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rcontainer.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* AVI and ASF headers: see formats/rcontainer.h. */
#include <string.h>
#include <formats/rcontainer.h>
static uint32_t rc_le16(const uint8_t *p) { return (uint32_t)p[0] | ((uint32_t)p[1] << 8); }
static uint32_t rc_le32(const uint8_t *p)
{
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
static uint64_t rc_le64(const uint8_t *p)
{
return (uint64_t)rc_le32(p) | ((uint64_t)rc_le32(p + 4) << 32);
}
/* a WAVEFORMATEX into an audio stream */
static void rc_wave(rcontainer_stream *s, const uint8_t *p, size_t n)
{
s->type = RCONTAINER_STREAM_AUDIO;
if (n < 16)
return;
s->format_tag = rc_le16(p);
s->channels = rc_le16(p + 2);
s->sample_rate = rc_le32(p + 4);
s->bitrate = rc_le32(p + 8) * 8;
/* WAVE_FORMAT_EXTENSIBLE: the sub-format's first two bytes */
if (s->format_tag == 0xFFFE && n >= 26 && rc_le16(p + 16) >= 22)
s->format_tag = rc_le16(p + 24);
}
/* a BITMAPINFOHEADER into a video stream */
static void rc_bitmap(rcontainer_stream *s, const uint8_t *p, size_t n)
{
int32_t h;
s->type = RCONTAINER_STREAM_VIDEO;
if (n < 20)
return;
s->width = rc_le32(p + 4);
h = (int32_t)rc_le32(p + 8);
s->height = (unsigned)(h < 0 ? -h : h);
s->fourcc = rc_le32(p + 16);
}
/* ---------------------------------------------------------------- */
/* AVI */
/* ---------------------------------------------------------------- */
/* the chunk of @id among those in [@at, @end): its body */
static bool rc_chunk(const uint8_t *p, size_t at, size_t end, const char *id,
const char *list, size_t *body, size_t *body_end)
{
while (at + 8 <= end)
{
size_t len = rc_le32(p + at + 4);
if (len > end - at - 8)
len = end - at - 8;
if (!memcmp(p + at, id, 4) && (!list || (len >= 4 && !memcmp(p + at + 8, list, 4))))
{
*body = at + 8 + (list ? 4 : 0);
*body_end = at + 8 + len;
return true;
}
at += 8 + len + (len & 1);
}
return false;
}
static bool rc_avi(const uint8_t *p, size_t n, rcontainer_info *info)
{
size_t hdrl, hdrl_end, b, b_end, at;
uint64_t frames = 0, period = 0, bytes[RCONTAINER_MAX_STREAMS];
double seconds = 0;
unsigned i;
if (!rc_chunk(p, 12, n, "LIST", "hdrl", &hdrl, &hdrl_end))
return false;
info->kind = RCONTAINER_AVI;
if (rc_chunk(p, hdrl, hdrl_end, "avih", NULL, &b, &b_end) && b_end - b >= 20)
{
period = rc_le32(p + b); /* microseconds a frame */
frames = rc_le32(p + b + 16);
}
/* each stream list in turn */
for (at = hdrl; at + 8 <= hdrl_end; )
{
size_t len = rc_le32(p + at + 4), sl, sl_end, h, h_end, f, f_end;
if (len > hdrl_end - at - 8)
len = hdrl_end - at - 8;
if (!memcmp(p + at, "LIST", 4) && len >= 4 && !memcmp(p + at + 8, "strl", 4)
&& info->num_streams < RCONTAINER_MAX_STREAMS)
{
rcontainer_stream *s = &info->streams[info->num_streams++];
sl = at + 12;
sl_end = at + 8 + len;
if (rc_chunk(p, sl, sl_end, "strh", NULL, &h, &h_end) && h_end - h >= 36)
{
uint32_t scale = rc_le32(p + h + 20), rate = rc_le32(p + h + 24);
uint32_t length = rc_le32(p + h + 32);
if (rc_chunk(p, sl, sl_end, "strf", NULL, &f, &f_end))
{
if (!memcmp(p + h, "vids", 4))
rc_bitmap(s, p + f, f_end - f);
else if (!memcmp(p + h, "auds", 4))
rc_wave(s, p + f, f_end - f);
}
/* the longest stream's length is the file's, as FFmpeg
* gives it */
if (scale && rate && (double)length * scale / rate > seconds)
seconds = (double)length * scale / rate;
}
}
at += 8 + len + (len & 1);
}
if (!seconds && frames && period)
seconds = (double)frames * period / 1e6;
info->duration_us = (int64_t)(seconds * 1e6 + 0.5);
/* each stream's bytes, from idx1: "NNxx" ids, NN the stream */
memset(bytes, 0, sizeof(bytes));
if (seconds > 0 && rc_chunk(p, 12, n, "idx1", NULL, &b, &b_end))
{
for (at = b; at + 16 <= b_end; at += 16)
{
unsigned d0 = p[at], d1 = p[at + 1], k;
if (d0 < '0' || d0 > '9' || d1 < '0' || d1 > '9')
continue;
k = (d0 - '0') * 10 + (d1 - '0');
if (k < info->num_streams)
bytes[k] += rc_le32(p + at + 12);
}
for (i = 0; i < info->num_streams; i++)
if (bytes[i] && info->streams[i].type == RCONTAINER_STREAM_VIDEO)
info->streams[i].bitrate = (uint32_t)(bytes[i] * 8 / seconds);
}
return info->num_streams > 0;
}
/* ---------------------------------------------------------------- */
/* ASF */
/* ---------------------------------------------------------------- */
static const uint8_t rc_asf_header[16] = { 0x30,0x26,0xB2,0x75,0x8E,0x66,0xCF,0x11,0xA6,0xD9,0x00,0xAA,0x00,0x62,0xCE,0x6C };
static const uint8_t rc_asf_file[16] = { 0xA1,0xDC,0xAB,0x8C,0x47,0xA9,0xCF,0x11,0x8E,0xE4,0x00,0xC0,0x0C,0x20,0x53,0x65 };
static const uint8_t rc_asf_stream[16] = { 0x91,0x07,0xDC,0xB7,0xB7,0xA9,0xCF,0x11,0x8E,0xE6,0x00,0xC0,0x0C,0x20,0x53,0x65 };
static const uint8_t rc_asf_audio[16] = { 0x40,0x9E,0x69,0xF8,0x4D,0x5B,0xCF,0x11,0xA8,0xFD,0x00,0x80,0x5F,0x5C,0x44,0x2B };
static const uint8_t rc_asf_video[16] = { 0xC0,0xEF,0x19,0xBC,0x4D,0x5B,0xCF,0x11,0xA8,0xFD,0x00,0x80,0x5F,0x5C,0x44,0x2B };
static const uint8_t rc_asf_bitrate[16] = { 0xCE,0x75,0xF8,0x7B,0x8D,0x46,0xD1,0x11,0x8D,0x82,0x00,0x60,0x97,0xC9,0xA2,0xB2 };
static bool rc_asf(const uint8_t *p, size_t n, rcontainer_info *info)
{
uint64_t size = rc_le64(p + 16);
size_t end = size < n ? (size_t)size : n, at = 30;
unsigned number[RCONTAINER_MAX_STREAMS], i;
uint32_t rates[128];
info->kind = RCONTAINER_ASF;
memset(rates, 0, sizeof(rates));
while (at + 24 <= end)
{
uint64_t osize = rc_le64(p + at + 16);
size_t body = at + 24, oend;
if (osize < 24 || osize > end - at)
break;
oend = at + (size_t)osize;
if (!memcmp(p + at, rc_asf_file, 16) && oend - body >= 68)
{
uint64_t play = rc_le64(p + body + 40), preroll = rc_le64(p + body + 56);
if (!(rc_le32(p + body + 64) & 1) && preroll < play / 10000)
info->duration_us = (int64_t)((play - preroll * 10000) / 10);
}
else if (!memcmp(p + at, rc_asf_stream, 16) && oend - body >= 54
&& info->num_streams < RCONTAINER_MAX_STREAMS)
{
rcontainer_stream *s = &info->streams[info->num_streams];
uint32_t tlen = rc_le32(p + body + 40);
size_t t = body + 54;
number[info->num_streams++] = rc_le16(p + body + 48) & 0x7f;
if (tlen > oend - t)
tlen = (uint32_t)(oend - t);
if (!memcmp(p + body, rc_asf_audio, 16))
rc_wave(s, p + t, tlen);
else if (!memcmp(p + body, rc_asf_video, 16))
{
/* width, height, a flag byte, the format data's size, then
* a BITMAPINFOHEADER */
s->type = RCONTAINER_STREAM_VIDEO;
if (tlen >= 11)
rc_bitmap(s, p + t + 11, tlen - 11);
if (tlen >= 8)
{
s->width = rc_le32(p + t);
s->height = rc_le32(p + t + 4);
}
}
}
else if (!memcmp(p + at, rc_asf_bitrate, 16) && oend - body >= 2)
{
unsigned count = rc_le16(p + body), k;
for (k = 0; k < count && body + 2 + k * 6 + 6 <= oend; k++)
{
const uint8_t *r = p + body + 2 + k * 6;
rates[rc_le16(r) & 0x7f] = rc_le32(r + 2);
}
}
at = oend;
}
for (i = 0; i < info->num_streams; i++)
if (rates[number[i]])
info->streams[i].bitrate = rates[number[i]];
return info->num_streams > 0;
}
bool rcontainer_probe(const uint8_t *data, size_t len, rcontainer_info *info)
{
if (!info)
return false;
memset(info, 0, sizeof(*info));
if (!data)
return false;
if (len >= 12 && !memcmp(data, "RIFF", 4) && !memcmp(data + 8, "AVI ", 4))
return rc_avi(data, len, info);
if (len >= 30 && !memcmp(data, rc_asf_header, 16))
return rc_asf(data, len, info);
return false;
}
+4
View File
@@ -1259,6 +1259,10 @@ static int data_transfer_read_at(data_transfer_t *dt, size_t off,
int64_t save, r;
if (dt->read_at)
return dt->read_at(dt->read_ud,off,dst,n) == (int64_t)n;
/* At the offset, the cursor and read-ahead untouched; seek and read
* where the file has no positional reads */
if ((r = filestream_read_at(dt->f, dst, (int64_t)n, (int64_t)off)) >= 0)
return r == (int64_t)n;
save = filestream_tell(dt->f);
if (save < 0
|| (save != (int64_t)off
+112 -20
View File
@@ -66,6 +66,7 @@
#include <limits.h>
#include <retro_inline.h>
#include <retro_endianness.h>
#include <formats/image.h>
#include <formats/rdds.h>
@@ -90,7 +91,20 @@
( (unsigned long long)RDDS_RL32((const unsigned char*)(p)) \
| ((unsigned long long)RDDS_RL32((const unsigned char*)(p) + 4) << 32))
static void rdds_bcdec__color_block(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int onlyOpaqueMode) {
/* Inlined into each of its callers whatever the compiler would choose, so
* that a decoder taking a constant order (RGBA or BGRA) is built for it
* alone, with no test of the order per block. */
#if defined(_MSC_VER)
#define RDDS_FORCE_INLINE static __forceinline
#elif defined(__GNUC__)
#define RDDS_FORCE_INLINE static INLINE __attribute__((always_inline))
#else
#define RDDS_FORCE_INLINE static INLINE
#endif
/* bgra: B,G,R,A in memory instead of R,G,B,A (red and blue swapped in
* the reference colours) */
RDDS_FORCE_INLINE void rdds_bcdec__color_block_order(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int onlyOpaqueMode, int bgra) {
unsigned short c0, c1;
unsigned int refColors[4]; /* 0xAABBGGRR */
unsigned char* dstColors;
@@ -146,6 +160,12 @@ static void rdds_bcdec__color_block(const void* compressedBlock, void* decompres
colorIndices = RDDS_RL32((const unsigned char*)compressedBlock + 4);
if (bgra) {
for (i = 0; i < 4; ++i)
refColors[i] = (refColors[i] & 0xFF00FF00u) | ((refColors[i] >> 16) & 0xFFu)
| ((refColors[i] & 0xFFu) << 16);
}
/* Fill out the decompressed color block */
dstColors = (unsigned char*)decompressedBlock;
for (i = 0; i < 4; ++i) {
@@ -162,6 +182,14 @@ static void rdds_bcdec__color_block(const void* compressedBlock, void* decompres
}
}
static void rdds_bcdec__color_block(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int onlyOpaqueMode) {
rdds_bcdec__color_block_order(compressedBlock, decompressedBlock, destinationPitch, onlyOpaqueMode, 0);
}
static void rdds_bcdec__color_block_bgra(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int onlyOpaqueMode) {
rdds_bcdec__color_block_order(compressedBlock, decompressedBlock, destinationPitch, onlyOpaqueMode, 1);
}
static void rdds_bcdec__sharp_alpha_block(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
const unsigned char* alpha;
unsigned char* decompressed;
@@ -390,6 +418,21 @@ static void rdds_bcdec_bc3(const void* compressedBlock, void* decompressedBlock,
rdds_bcdec__smooth_alpha_block(compressedBlock, ((char*)decompressedBlock) + 3, destinationPitch, 4);
}
/* The same, B,G,R,A in memory: alpha stays the fourth byte */
static void rdds_bcdec_bc1_bgra(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
rdds_bcdec__color_block_bgra(compressedBlock, decompressedBlock, destinationPitch, 0);
}
static void rdds_bcdec_bc2_bgra(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
rdds_bcdec__color_block_bgra(((char*)compressedBlock) + 8, decompressedBlock, destinationPitch, 1);
rdds_bcdec__sharp_alpha_block(compressedBlock, ((char*)decompressedBlock) + 3, destinationPitch);
}
static void rdds_bcdec_bc3_bgra(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
rdds_bcdec__color_block_bgra(((char*)compressedBlock) + 8, decompressedBlock, destinationPitch, 1);
rdds_bcdec__smooth_alpha_block(compressedBlock, ((char*)decompressedBlock) + 3, destinationPitch, 4);
}
#ifndef BCDEC_BC4BC5_PRECISE
static void rdds_bcdec_bc4(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
rdds_bcdec__smooth_alpha_block(compressedBlock, decompressedBlock, destinationPitch, 1);
@@ -1891,7 +1934,7 @@ static const unsigned char rdds_d6[190] = {
* stores the compiler already coalesces, so it needs no fused variant
* and no punch-through handling here.) */
static INLINE void rdds_bc_refcolors(const unsigned char *cp,
unsigned int ref[4])
unsigned int ref[4], int bgra)
{
unsigned c0 = RDDS_RL16(cp), c1 = RDDS_RL16(cp + 2);
unsigned r0 = (c0 >> 11) & 0x1F, g0 = (c0 >> 5) & 0x3F, b0 = c0 & 0x1F;
@@ -1905,11 +1948,19 @@ static INLINE void rdds_bc_refcolors(const unsigned char *cp,
| ((unsigned)rdds_d6[2 * g0 + g1] << 8) | rdds_d5[2 * r0 + r1];
ref[3] = 0xFF000000u | ((unsigned)rdds_d5[b0 + 2 * b1] << 16)
| ((unsigned)rdds_d6[g0 + 2 * g1] << 8) | rdds_d5[r0 + 2 * r1];
if (bgra) /* A<<24|R<<16|G<<8|B, for a BGRA surface */
{
int k;
for (k = 0; k < 4; k++)
ref[k] = (ref[k] & 0xFF00FF00u) | ((ref[k] >> 16) & 0xFFu)
| ((ref[k] & 0xFFu) << 16);
}
}
/* BC2: opaque colour + explicit 4-bit alpha, fused into one store. */
static void rdds_bcdec_bc2_direct(const void *compressedBlock,
void *decompressedBlock, int destinationPitch)
/* BC2: opaque colour + explicit 4-bit alpha, fused into one store.
* bgra: the uint32 is A<<24|R<<16|G<<8|B instead of A<<24|B<<16|G<<8|R. */
RDDS_FORCE_INLINE void rdds_bcdec_bc2_fused(const void *compressedBlock,
void *decompressedBlock, int destinationPitch, int bgra)
{
const unsigned char *p = (const unsigned char*)compressedBlock;
unsigned char *dst = (unsigned char*)decompressedBlock;
@@ -1917,7 +1968,7 @@ static void rdds_bcdec_bc2_direct(const void *compressedBlock,
unsigned int ci = RDDS_RL32(p + 12);
int i, j;
rdds_bc_refcolors(p + 8, ref);
rdds_bc_refcolors(p + 8, ref, bgra);
for (i = 0; i < 4; ++i)
{
unsigned int arow = RDDS_RL16(p + 2 * i); /* four 4-bit alphas */
@@ -1934,9 +1985,11 @@ static void rdds_bcdec_bc2_direct(const void *compressedBlock,
}
}
/* BC3: opaque colour + smooth (interpolated) 8-value alpha, one store. */
static void rdds_bcdec_bc3_direct(const void *compressedBlock,
void *decompressedBlock, int destinationPitch)
/* BC3: opaque colour + smooth (interpolated) 8-value alpha, one store.
* The alpha ramp rounds as rdds_bcdec__smooth_alpha_block does, so this
* path and the scratch one agree for every texel. */
RDDS_FORCE_INLINE void rdds_bcdec_bc3_fused(const void *compressedBlock,
void *decompressedBlock, int destinationPitch, int bgra)
{
const unsigned char *p = (const unsigned char*)compressedBlock;
unsigned char *dst = (unsigned char*)decompressedBlock;
@@ -1951,24 +2004,24 @@ static void rdds_bcdec_bc3_direct(const void *compressedBlock,
al[1] = (unsigned char)((blk >> 8) & 0xFF);
if (al[0] > al[1])
{
al[2] = (unsigned char)((6 * al[0] + al[1]) / 7);
al[3] = (unsigned char)((5 * al[0] + 2 * al[1]) / 7);
al[4] = (unsigned char)((4 * al[0] + 3 * al[1]) / 7);
al[5] = (unsigned char)((3 * al[0] + 4 * al[1]) / 7);
al[6] = (unsigned char)((2 * al[0] + 5 * al[1]) / 7);
al[7] = (unsigned char)(( al[0] + 6 * al[1]) / 7);
al[2] = (unsigned char)((6 * al[0] + al[1] + 3) / 7);
al[3] = (unsigned char)((5 * al[0] + 2 * al[1] + 3) / 7);
al[4] = (unsigned char)((4 * al[0] + 3 * al[1] + 3) / 7);
al[5] = (unsigned char)((3 * al[0] + 4 * al[1] + 3) / 7);
al[6] = (unsigned char)((2 * al[0] + 5 * al[1] + 3) / 7);
al[7] = (unsigned char)(( al[0] + 6 * al[1] + 3) / 7);
}
else
{
al[2] = (unsigned char)((4 * al[0] + al[1]) / 5);
al[3] = (unsigned char)((3 * al[0] + 2 * al[1]) / 5);
al[4] = (unsigned char)((2 * al[0] + 3 * al[1]) / 5);
al[5] = (unsigned char)(( al[0] + 4 * al[1]) / 5);
al[2] = (unsigned char)((4 * al[0] + al[1] + 2) / 5);
al[3] = (unsigned char)((3 * al[0] + 2 * al[1] + 2) / 5);
al[4] = (unsigned char)((2 * al[0] + 3 * al[1] + 2) / 5);
al[5] = (unsigned char)(( al[0] + 4 * al[1] + 2) / 5);
al[6] = 0x00;
al[7] = 0xFF;
}
rdds_bc_refcolors(p + 8, ref);
rdds_bc_refcolors(p + 8, ref, bgra);
ci = RDDS_RL32(p + 12);
idx = blk >> 16; /* 16 * 3-bit indices */
for (i = 0; i < 4; ++i)
@@ -1985,6 +2038,15 @@ static void rdds_bcdec_bc3_direct(const void *compressedBlock,
}
}
static void rdds_bcdec_bc2_direct(const void *b, void *o, int pitch)
{ rdds_bcdec_bc2_fused(b, o, pitch, 0); }
static void rdds_bcdec_bc3_direct(const void *b, void *o, int pitch)
{ rdds_bcdec_bc3_fused(b, o, pitch, 0); }
static void rdds_bcdec_bc2_direct_bgra(const void *b, void *o, int pitch)
{ rdds_bcdec_bc2_fused(b, o, pitch, 1); }
static void rdds_bcdec_bc3_direct_bgra(const void *b, void *o, int pitch)
{ rdds_bcdec_bc3_fused(b, o, pitch, 1); }
/* Emit an edge (partially out-of-range) block from a decoded RGBA8
* scratch into the surface with per-texel bounds tests. Shared by every
* path so the hot interior loops stay branch-free. */
@@ -2060,6 +2122,17 @@ RDDS_GEN_ROWS_DIRECT(rdds_rows_bc1_rgba, rdds_bcdec_bc1, 8u)
RDDS_GEN_ROWS_DIRECT(rdds_rows_bc2_rgba, rdds_bcdec_bc2_direct, 16u)
RDDS_GEN_ROWS_DIRECT(rdds_rows_bc3_rgba, rdds_bcdec_bc3_direct, 16u)
#ifndef MSB_FIRST
/* The same for a BGRA video driver, little-endian only: there the
* A<<24|R<<16|G<<8|B pixel is the bytes B,G,R,A, which the BGRA block
* decoders write, so the edge scratch passes through rdds_emit_edge's
* byte order unchanged (as with RGBA). Big-endian keeps the scratch
* path below. */
RDDS_GEN_ROWS_DIRECT(rdds_rows_bc1_bgra, rdds_bcdec_bc1_bgra, 8u)
RDDS_GEN_ROWS_DIRECT(rdds_rows_bc2_bgra, rdds_bcdec_bc2_direct_bgra, 16u)
RDDS_GEN_ROWS_DIRECT(rdds_rows_bc3_bgra, rdds_bcdec_bc3_direct_bgra, 16u)
#endif
/* Decode block rows [by0, by1) of a BCn surface into 'out'. The caller
* has already validated that the payload holds every block. */
static void rdds_rows_compressed(const rdds_info_t *info,
@@ -2093,6 +2166,22 @@ static void rdds_rows_compressed(const rdds_info_t *info,
break; /* BC4/BC5/BC7 fall through to the scratch loop */
}
}
#ifndef MSB_FIRST
else if (!premul)
{
switch (info->fmt)
{
case RDDS_FMT_BC1:
rdds_rows_bc1_bgra(info, data, out, by0, by1); return;
case RDDS_FMT_BC2:
rdds_rows_bc2_bgra(info, data, out, by0, by1); return;
case RDDS_FMT_BC3:
rdds_rows_bc3_bgra(info, data, out, by0, by1); return;
default:
break;
}
}
#endif
/* Generic scratch path: BGRA output order, premultiplied alpha,
* BC4/BC5 channel expansion, and BC7 (whose decode dominates, so the
@@ -2722,3 +2811,6 @@ void rdds_decode_block_bc1(const void *block, void *out, int pitch) { rdds_bcdec
void rdds_decode_block_bc2(const void *block, void *out, int pitch) { rdds_bcdec_bc2(block, out, pitch); }
void rdds_decode_block_bc3(const void *block, void *out, int pitch) { rdds_bcdec_bc3(block, out, pitch); }
void rdds_decode_block_bc7(const void *block, void *out, int pitch) { rdds_bcdec_bc7(block, out, pitch); }
void rdds_decode_block_bc1_bgra(const void *block, void *out, int pitch) { rdds_bcdec_bc1_bgra(block, out, pitch); }
void rdds_decode_block_bc2_bgra(const void *block, void *out, int pitch) { rdds_bcdec_bc2_bgra(block, out, pitch); }
void rdds_decode_block_bc3_bgra(const void *block, void *out, int pitch) { rdds_bcdec_bc3_bgra(block, out, pitch); }
+7 -1
View File
@@ -131,7 +131,13 @@ bool rdvd_vfs_service(rdvd_vfs_t *vfs)
memset(&result, 0, sizeof(result));
result.ticket = request.ticket; result.bytes = -1;
bytes = request.bytes < 65536 ? request.bytes : 65536;
if (filestream_seek(request.file, (int64_t)request.offset,
/* At the offset in one call; seek and read where there is none */
if ((result.bytes = filestream_read_at(request.file, request.destination,
(int64_t)bytes, (int64_t)request.offset)) >= 0)
{
if (result.bytes > (int64_t)bytes) result.bytes = -1;
}
else if (filestream_seek(request.file, (int64_t)request.offset,
RETRO_VFS_SEEK_POSITION_START) == 0
&& filestream_tell(request.file) == (int64_t)request.offset)
{
+680 -17
View File
@@ -20,27 +20,37 @@
* THE SOFTWARE.
*/
#include <formats/rflv_mux.h>
#include <formats/rav1_sequence.h>
#include <stdlib.h>
#include <string.h>
enum flv_output
{
FLV_HEADER, FLV_VIDEO_CONFIG, FLV_AUDIO_CONFIG, FLV_MEDIA, FLV_IDLE
FLV_HEADER, FLV_COLOR, FLV_VIDEO_CONFIG, FLV_AUDIO_CONFIG, FLV_MEDIA, FLV_IDLE
};
enum flv_codec { FLV_AVC, FLV_HEVC, FLV_AV1 };
struct rflv_mux
{
uint64_t last[2], input_dts;
rflv_span_t spans[3];
uint8_t *config;
uint8_t *converted;
uint8_t *metadata;
rav1_sequence_t *av1_sequence;
rav1_config_t av1_config;
const uint8_t *input;
size_t avcc_size, asc_size, limit, size, offset, span_count;
size_t converted_size;
size_t metadata_size;
uint32_t timestamp;
int32_t composition;
unsigned timescale[2], track;
enum flv_output output;
uint8_t prefix[16], suffix[4];
uint8_t prefix[19], suffix[4];
bool pending, scanning, failed, finished, seen[2], keyframe, vcl, idr;
bool annexb, hevc, av1;
};
static void flv_uint(uint8_t *out, uint32_t value, unsigned count)
@@ -90,6 +100,42 @@ static bool flv_avcc(const uint8_t *data, size_t size)
return flv_sets(data, size, &pos, count, 13) && pos == size;
}
static bool flv_hvcc(const uint8_t *data, size_t size)
{
size_t at = 23;
unsigned arrays, count, type, length, present = 0;
if (!data || size < 23 || size > 65536 || data[0] != 1
|| (data[13] & 0xf0) != 0xf0 || (data[15] & 0xfc) != 0xfc
|| (data[16] & 0xfc) != 0xfc || (data[17] & 0xf8) != 0xf8
|| (data[18] & 0xf8) != 0xf8
|| (data[21] & 3) != 3 || !data[22])
return false;
for (arrays = data[22]; arrays; arrays--)
{
if (size - at < 3 || (data[at] & 0x40))
return false;
type = data[at++] & 63;
count = ((unsigned)data[at] << 8) | data[at + 1];
at += 2;
if (!count)
return false;
while (count--)
{
if (size - at < 2)
return false;
length = ((unsigned)data[at] << 8) | data[at + 1];
at += 2;
if (length < 2 || length > size - at || (data[at] & 0x80)
|| ((data[at] >> 1) & 63) != type || !(data[at + 1] & 7))
return false;
at += length;
}
if (type >= 32 && type <= 34)
present |= 1u << (type - 32);
}
return at == size && present == 7;
}
static unsigned flv_bits(const uint8_t *data, size_t size, unsigned *pos, unsigned count)
{
unsigned i, value = 0;
@@ -123,7 +169,7 @@ static bool flv_asc(const uint8_t *data, size_t size)
static void flv_tag(rflv_mux_t *m, unsigned track, const void *data,
size_t size, uint32_t timestamp, int32_t composition, bool keyframe, bool config)
{
unsigned bytes = track == 1 ? 5 : 2;
unsigned bytes = track == 1 ? (m->hevc && !config && composition ? 8 : 5) : 2;
memset(m->prefix, 0, sizeof(m->prefix));
m->prefix[0] = track == 1 ? 9 : 8;
flv_uint(m->prefix + 1, (uint32_t)size + bytes, 3);
@@ -131,7 +177,15 @@ static void flv_tag(rflv_mux_t *m, unsigned track, const void *data,
m->prefix[7] = (uint8_t)(timestamp >> 24);
m->prefix[11] = track == 1 ? (keyframe ? 0x17 : 0x27) : 0xaf;
m->prefix[12] = config ? 0 : 1;
if (track == 1)
if (track == 1 && (m->hevc || m->av1))
{
m->prefix[11] = (uint8_t)(0x80 | (keyframe ? 0x10 : 0x20)
| (config ? 0 : m->av1 || composition ? 1 : 3));
memcpy(m->prefix + 12, m->av1 ? "av01" : "hvc1", 4);
if (bytes == 8)
flv_uint(m->prefix + 16, (uint32_t)composition, 3);
}
else if (track == 1)
flv_uint(m->prefix + 13, (uint32_t)composition, 3);
flv_uint(m->suffix, (uint32_t)size + bytes + 11, 4);
m->spans[0].data = m->prefix;
@@ -144,11 +198,16 @@ static void flv_tag(rflv_mux_t *m, unsigned track, const void *data,
m->pending = true;
}
rflv_mux_t *rflv_mux_new(const rflv_config_t *c)
static rflv_mux_t *flv_new(const rflv_config_t *c, enum flv_codec codec)
{
rflv_mux_t *m;
if (!c || (!c->avcc && !c->asc) || !c->max_packet_size || c->max_packet_size > 0xfffffa
|| (c->avcc ? !c->video_timescale || !flv_avcc((const uint8_t*)c->avcc, c->avcc_size)
bool hevc = codec == FLV_HEVC, av1 = codec == FLV_AV1;
if (!c || (!c->avcc && !c->asc) || ((hevc || av1) && !c->avcc)
|| !c->max_packet_size || c->max_packet_size > (hevc ? 0xfffff7u : 0xfffffau)
|| (c->avcc ? !c->video_timescale
|| !(av1 ? c->avcc_size >= 4 && c->avcc_size <= 65536
: hevc ? flv_hvcc((const uint8_t*)c->avcc, c->avcc_size)
: flv_avcc((const uint8_t*)c->avcc, c->avcc_size))
: c->avcc_size || c->video_timescale)
|| (c->asc ? !c->audio_timescale || !flv_asc((const uint8_t*)c->asc, c->asc_size)
: c->asc_size || c->audio_timescale))
@@ -171,6 +230,18 @@ rflv_mux_t *rflv_mux_new(const rflv_config_t *c)
m->timescale[0] = c->video_timescale;
m->timescale[1] = c->audio_timescale;
m->limit = c->max_packet_size;
m->hevc = hevc;
m->av1 = av1;
if (av1)
{
m->av1_sequence = (rav1_sequence_t*)malloc(sizeof(*m->av1_sequence));
if (!m->av1_sequence || !rav1_config_parse(&m->av1_config, m->config, m->avcc_size)
|| !rav1_config_sequence(m->av1_sequence, &m->av1_config))
{
rflv_mux_free(m);
return NULL;
}
}
memcpy(m->prefix, "FLV\1\0\0\0\0\11\0\0\0\0", 13);
m->prefix[4] = (c->avcc ? 1 : 0) | (c->asc ? 4 : 0);
m->spans[0].data = m->prefix;
@@ -180,6 +251,511 @@ rflv_mux_t *rflv_mux_new(const rflv_config_t *c)
return m;
}
rflv_mux_t *rflv_mux_new(const rflv_config_t *c)
{
return flv_new(c, FLV_AVC);
}
rflv_mux_t *rflv_mux_new_hevc(const rflv_config_t *c)
{
return flv_new(c, FLV_HEVC);
}
rflv_mux_t *rflv_mux_new_av1(const rflv_config_t *c)
{
return flv_new(c, FLV_AV1);
}
rflv_mux_t *rflv_mux_new_av1_obu(const rflv_config_t *c)
{
rav1_obu_t obu, sequence;
rav1_sequence_t *info;
rflv_config_t config;
rflv_mux_t *m = NULL;
uint8_t *data;
size_t at = 0, used;
int result;
if (!c || !c->avcc || !c->avcc_size || c->avcc_size > 65532)
return NULL;
memset(&sequence, 0, sizeof(sequence));
while ((result = rav1_obu_next(&obu, (const uint8_t*)c->avcc, c->avcc_size, &at, false)) > 0)
{
if (obu.type == 1)
{
if (sequence.data)
return NULL;
sequence = obu;
}
else if (obu.type != 2 && obu.type != 5 && obu.type != 15)
return NULL;
}
if (result < 0 || !sequence.data)
return NULL;
info = (rav1_sequence_t*)malloc(sizeof(*info));
if (!info)
return NULL;
if (!rav1_sequence_parse(info, sequence.payload, sequence.payload_size))
{
free(info);
return NULL;
}
data = (uint8_t*)malloc(c->avcc_size + 4);
if (data)
{
data[0] = 0x81;
data[1] = (uint8_t)((info->profile << 5) | info->operating_points[0].level);
data[2] = (uint8_t)((info->operating_points[0].tier << 7)
| ((info->bit_depth > 8) << 6) | ((info->bit_depth == 12) << 5)
| (info->monochrome << 4) | (info->subsampling_x << 3)
| (info->subsampling_y << 2) | info->chroma_sample_position);
data[3] = 0;
memcpy(data + 4, sequence.data, sequence.size);
used = sequence.size + 4;
at = 0;
while (rav1_obu_next(&obu, (const uint8_t*)c->avcc, c->avcc_size, &at, false) > 0)
if (obu.type == 5)
{
memcpy(data + used, obu.data, obu.size);
used += obu.size;
}
config = *c;
config.avcc = data;
config.avcc_size = used;
m = rflv_mux_new_av1(&config);
free(data);
}
free(info);
return m;
}
struct flv_metadata_writer
{
uint8_t *data;
size_t used;
bool failed;
};
static void flv_metadata_bytes(struct flv_metadata_writer *w,
const void *data, size_t size)
{
if (w->failed || size > 512 - w->used)
{
w->failed = true;
return;
}
memcpy(w->data + w->used, data, size);
w->used += size;
}
static void flv_metadata_name(struct flv_metadata_writer *w,
const char *name, bool property)
{
uint8_t header[3];
size_t size = strlen(name);
header[0] = 2;
header[1] = (uint8_t)(size >> 8);
header[2] = (uint8_t)size;
flv_metadata_bytes(w, header + (property ? 1 : 0), property ? 2 : 3);
flv_metadata_bytes(w, name, size);
}
static void flv_metadata_object(struct flv_metadata_writer *w, const char *name)
{
static const uint8_t marker = 3;
flv_metadata_name(w, name, true);
flv_metadata_bytes(w, &marker, 1);
}
static void flv_metadata_end(struct flv_metadata_writer *w)
{
static const uint8_t end[] = {0, 0, 9};
flv_metadata_bytes(w, end, sizeof(end));
}
/* Encode the nonnegative rational as binary64 without host float assumptions. */
static void flv_metadata_number(struct flv_metadata_writer *w, const char *name,
uint32_t numerator, unsigned denominator)
{
uint64_t n = numerator, d = denominator, fraction = 0, bits = 0, remainder;
int exponent = 0;
unsigned i;
if (n)
{
while (n < d)
{
n <<= 1;
exponent--;
}
while (n >= d * 2)
{
d <<= 1;
exponent++;
}
remainder = n - d;
for (i = 0; i < 52; i++)
{
remainder <<= 1;
fraction <<= 1;
if (remainder >= d)
{
remainder -= d;
fraction++;
}
}
if (remainder * 2 > d || (remainder * 2 == d && (fraction & 1)))
fraction++;
if (fraction == ((uint64_t)1 << 52))
{
fraction = 0;
exponent++;
}
bits = ((uint64_t)(exponent + 1023) << 52) | fraction;
}
flv_metadata_name(w, name, true);
if (w->failed || 512 - w->used < 9)
{
w->failed = true;
return;
}
w->data[w->used++] = 0;
for (i = 0; i < 8; i++)
w->data[w->used++] = (uint8_t)(bits >> (56 - i * 8));
}
bool rflv_mux_set_color(rflv_mux_t *m, const video_color_metadata_t *c,
unsigned depth)
{
static const char *const xy[] =
{ "redX", "redY", "greenX", "greenY", "blueX", "blueY", "whitePointX", "whitePointY" };
struct flv_metadata_writer w;
static const uint8_t object = 3;
unsigned i;
if (!m || !c || (!m->hevc && !m->av1) || m->failed || m->metadata
|| m->output != FLV_HEADER || !m->pending
|| (depth != 8 && depth != 10 && depth != 12)
|| (m->hevc && (depth != 8u + (m->config[17] & 7)
|| ((m->config[16] & 3) && depth != 8u + (m->config[18] & 7))))
|| (m->av1 && (depth != m->av1_sequence->bit_depth
|| (c->primaries && c->primaries != m->av1_sequence->color_primaries)
|| (c->transfer && c->transfer != m->av1_sequence->transfer_characteristics)
|| (c->matrix && c->matrix != m->av1_sequence->matrix_coefficients)
|| (c->range && c->range != m->av1_sequence->color_range + 1)))
|| c->primaries > 255 || c->transfer > 255 || c->matrix > 255
|| c->range > 2 || c->max_cll > 10000 || c->max_fall > 10000
|| (c->max_cll && c->max_fall > c->max_cll))
return false;
if (c->mastering.present)
{
if (c->mastering.present != VIDEO_MASTERING_DISPLAY_COMPLETE
|| c->mastering.luminance_max < 50000 || c->mastering.luminance_max > 100000000
|| !c->mastering.luminance_min || c->mastering.luminance_min > 50000)
return false;
for (i = 0; i < 8; i++)
if (c->mastering.chromaticity[i] < 5
|| c->mastering.chromaticity[i] > (i & 1 ? 42000 : 37000))
return false;
}
w.data = (uint8_t*)malloc(512);
if (!w.data)
return false;
w.used = 0;
w.failed = false;
flv_metadata_name(&w, "colorInfo", false);
flv_metadata_bytes(&w, &object, 1);
flv_metadata_object(&w, "colorConfig");
flv_metadata_number(&w, "bitDepth", depth, 1);
if (c->primaries)
flv_metadata_number(&w, "colorPrimaries", c->primaries, 1);
if (c->transfer)
flv_metadata_number(&w, "transferCharacteristics", c->transfer, 1);
if (c->matrix)
flv_metadata_number(&w, "matrixCoefficients", c->matrix, 1);
flv_metadata_end(&w);
if (c->max_cll || c->max_fall)
{
flv_metadata_object(&w, "hdrCll");
if (c->max_cll)
flv_metadata_number(&w, "maxCLL", c->max_cll, 1);
if (c->max_fall)
flv_metadata_number(&w, "maxFall", c->max_fall, 1);
flv_metadata_end(&w);
}
if (c->mastering.present)
{
flv_metadata_object(&w, "hdrMdcv");
for (i = 0; i < 8; i++)
flv_metadata_number(&w, xy[i], (c->mastering.chromaticity[i] + 2u) / 5, 10000);
flv_metadata_number(&w, "maxLuminance", c->mastering.luminance_max, 10000);
flv_metadata_number(&w, "minLuminance", c->mastering.luminance_min, 10000);
flv_metadata_end(&w);
}
flv_metadata_end(&w);
if (w.failed)
{
free(w.data);
return false;
}
m->metadata = w.data;
m->metadata_size = w.used;
return true;
}
static bool flv_annexb(const uint8_t *data, size_t size, size_t *offset,
const uint8_t **nal, size_t *length)
{
size_t start = *offset, at, zeros = 0, end;
while (start < size && !data[start])
start++;
if (start - *offset < 2 || start == size || data[start] != 1)
return false;
start++;
end = size;
for (at = start; at < size; at++)
{
if (!data[at])
zeros++;
else
{
if (data[at] == 1 && zeros >= 2)
{
end = at - zeros;
break;
}
zeros = 0;
}
}
*offset = at < size ? end : size;
if (at == size)
end -= zeros;
if (end <= start)
return false;
*nal = data + start;
*length = end - start;
return true;
}
struct flv_rbsp
{
const uint8_t *data;
size_t size, at;
unsigned byte, left, zeros;
bool failed;
};
static unsigned flv_rbsp_bits(struct flv_rbsp *b, unsigned count)
{
unsigned value = 0, next;
while (count--)
{
if (!b->left)
{
if (b->at == b->size)
goto failed;
next = b->data[b->at++];
if (b->zeros == 2 && next == 3)
{
if (b->at == b->size || b->data[b->at] > 3)
goto failed;
b->zeros = 0;
next = b->data[b->at++];
}
b->zeros = next ? 0 : b->zeros < 2 ? b->zeros + 1 : 2;
b->byte = next;
b->left = 8;
}
value = (value << 1) | ((b->byte >> --b->left) & 1);
}
return value;
failed:
b->failed = true;
return 0;
}
static unsigned flv_rbsp_ue(struct flv_rbsp *b)
{
unsigned zeros = 0;
while (!b->failed && !flv_rbsp_bits(b, 1))
if (++zeros == 32)
b->failed = true;
if (b->failed)
return ~0u;
return ((1u << zeros) - 1) + flv_rbsp_bits(b, zeros);
}
static bool flv_hevc_info(const uint8_t *sps, size_t size, uint8_t *header)
{
struct flv_rbsp b;
unsigned layers, nested, flags = 0, i, j, chroma, luma, chroma_depth;
memset(&b, 0, sizeof(b));
b.data = sps + 2;
b.size = size - 2;
flv_rbsp_bits(&b, 4);
layers = flv_rbsp_bits(&b, 3);
nested = flv_rbsp_bits(&b, 1);
if (layers > 6)
return false;
for (i = 1; i <= 12; i++)
header[i] = (uint8_t)flv_rbsp_bits(&b, 8);
for (i = 0; i < layers; i++)
flags |= flv_rbsp_bits(&b, 2) << (i * 2);
if (layers)
for (i = layers; i < 8; i++)
if (flv_rbsp_bits(&b, 2))
return false;
for (i = 0; i < layers; i++)
{
if (flags & (2u << (i * 2)))
for (j = 0; j < 11; j++)
flv_rbsp_bits(&b, 8);
if (flags & (1u << (i * 2)))
flv_rbsp_bits(&b, 8);
}
if (flv_rbsp_ue(&b) > 15)
return false;
chroma = flv_rbsp_ue(&b);
if (chroma > 3)
return false;
if (chroma == 3)
flv_rbsp_bits(&b, 1);
if (!flv_rbsp_ue(&b) || !flv_rbsp_ue(&b))
return false;
if (flv_rbsp_bits(&b, 1))
for (i = 0; i < 4; i++)
flv_rbsp_ue(&b);
luma = flv_rbsp_ue(&b);
chroma_depth = flv_rbsp_ue(&b);
if (b.failed || luma > 6 || chroma_depth > 6)
return false;
header[0] = 1;
header[13] = 0xf0;
header[15] = 0xfc;
header[16] = (uint8_t)(0xfc | chroma);
header[17] = (uint8_t)(0xf8 | luma);
header[18] = (uint8_t)(0xf8 | chroma_depth);
header[21] = (uint8_t)(((layers + 1) << 3) | (nested << 2) | 3);
header[22] = 3;
return true;
}
rflv_mux_t *rflv_mux_new_hevc_annexb(const rflv_config_t *c)
{
const uint8_t *sets[3] = {NULL, NULL, NULL}, *nal;
size_t lengths[3] = {0, 0, 0}, at = 0, size, total = 38;
unsigned type, i;
uint8_t *hvcc;
rflv_config_t config;
rflv_mux_t *m;
if (!c || !c->avcc || !c->avcc_size || c->avcc_size > 65536
|| !c->max_packet_size || c->max_packet_size > 0xfffff7)
return NULL;
while (at < c->avcc_size)
{
if (!flv_annexb((const uint8_t*)c->avcc, c->avcc_size, &at, &nal, &size)
|| size < 3 || size > 65535 || (nal[0] & 0x81) || nal[1] != 1)
return NULL;
type = (nal[0] >> 1) & 63;
if (type < 32 || type > 34 || sets[type - 32] || size > 65536 - total)
return NULL;
sets[type - 32] = nal;
lengths[type - 32] = size;
total += size;
}
if (!sets[0] || !sets[1] || !sets[2])
return NULL;
hvcc = (uint8_t*)calloc(1, total);
if (!hvcc)
return NULL;
if (!flv_hevc_info(sets[1], lengths[1], hvcc))
{
free(hvcc);
return NULL;
}
at = 23;
for (i = 0; i < 3; i++)
{
hvcc[at++] = (uint8_t)(0xa0 + i);
flv_uint(hvcc + at, 1, 2);
flv_uint(hvcc + at + 2, (uint32_t)lengths[i], 2);
memcpy(hvcc + at + 4, sets[i], lengths[i]);
at += lengths[i] + 4;
}
config = *c;
config.avcc = hvcc;
config.avcc_size = total;
m = rflv_mux_new_hevc(&config);
free(hvcc);
if (!m)
return NULL;
m->converted = (uint8_t*)malloc(m->limit);
if (!m->converted)
{
rflv_mux_free(m);
return NULL;
}
return m;
}
rflv_mux_t *rflv_mux_new_annexb(const rflv_config_t *c)
{
rflv_config_t config;
rflv_mux_t *m;
const uint8_t *nal, *sps = NULL, *pps = NULL;
uint8_t *avcc;
size_t offset = 0, size, sps_size = 0, pps_size = 0;
if (!c || !c->avcc || !c->avcc_size || c->avcc_size > 65536
|| !c->max_packet_size || c->max_packet_size > 0xfffffa)
return NULL;
while (offset < c->avcc_size)
{
unsigned type;
if (!flv_annexb((const uint8_t*)c->avcc, c->avcc_size, &offset, &nal, &size)
|| size > 65535 || (nal[0] & 0x80))
return NULL;
type = nal[0] & 31;
if (type == 7 && !sps && size >= 4)
{
sps = nal;
sps_size = size;
}
else if (type == 8 && !pps)
{
pps = nal;
pps_size = size;
}
else
return NULL;
}
if (!sps || !pps || sps_size + pps_size > 65536 - 11)
return NULL;
size = sps_size + pps_size + 11;
avcc = (uint8_t*)malloc(size);
if (!avcc)
return NULL;
avcc[0] = 1;
memcpy(avcc + 1, sps + 1, 3);
avcc[4] = 255;
avcc[5] = 225;
flv_uint(avcc + 6, (uint32_t)sps_size, 2);
memcpy(avcc + 8, sps, sps_size);
avcc[8 + sps_size] = 1;
flv_uint(avcc + 9 + sps_size, (uint32_t)pps_size, 2);
memcpy(avcc + 11 + sps_size, pps, pps_size);
config = *c;
config.avcc = avcc;
config.avcc_size = size;
m = rflv_mux_new(&config);
free(avcc);
if (!m)
return NULL;
m->converted = (uint8_t*)malloc(m->limit);
if (!m->converted)
{
rflv_mux_free(m);
return NULL;
}
return m;
}
static bool flv_milliseconds(uint64_t ticks, unsigned scale, uint32_t *result)
{
uint64_t seconds = ticks / scale;
@@ -209,6 +785,7 @@ int rflv_mux_push(rflv_mux_t *m, unsigned track, const void *data,
return RFLV_ERROR;
composition = (int64_t)pts_ms - dts_ms;
if ((track == 1 && (composition < -0x800000 || composition > 0x7fffff
|| (m->av1 && pts != dts)
|| (!m->seen[0] && !keyframe))) || (track == 2 && pts != dts))
return RFLV_ERROR;
m->track = track;
@@ -220,6 +797,8 @@ int rflv_mux_push(rflv_mux_t *m, unsigned track, const void *data,
m->composition = (int32_t)composition;
m->keyframe = keyframe;
m->vcl = m->idr = false;
m->annexb = false;
m->converted_size = 0;
m->output = FLV_MEDIA;
m->scanning = track == 1;
if (track == 2)
@@ -227,8 +806,22 @@ int rflv_mux_push(rflv_mux_t *m, unsigned track, const void *data,
return RFLV_PROGRESS;
}
int rflv_mux_push_annexb(rflv_mux_t *m, const void *data, size_t size,
uint64_t dts, uint64_t pts, bool keyframe)
{
int result;
if (!m || !m->converted)
return RFLV_ERROR;
result = rflv_mux_push(m, 1, data, size, dts, pts, keyframe);
if (result == RFLV_PROGRESS)
m->annexb = true;
return result;
}
int rflv_mux_step(rflv_mux_t *m)
{
const uint8_t *nal;
size_t size;
uint32_t length;
unsigned type;
if (!m || m->failed)
@@ -244,22 +837,70 @@ int rflv_mux_step(rflv_mux_t *m)
if (!m->vcl || (!m->seen[0] && !m->idr))
goto failed;
m->scanning = false;
flv_tag(m, 1, m->input, m->size, m->timestamp, m->composition, m->keyframe, false);
flv_tag(m, 1, m->annexb ? m->converted : m->input,
m->annexb ? m->converted_size : m->size,
m->timestamp, m->composition, m->keyframe, false);
return RFLV_PACKET;
}
if (m->size - m->offset < 4)
if (m->av1)
{
rav1_obu_t obu;
if (rav1_obu_next(&obu, m->input, m->size, &m->offset, false) != 1
|| !obu.type || (obu.type >= 8 && obu.type != 15)
|| obu.spatial_id)
goto failed;
if (obu.type == 1 && (obu.payload_size != m->av1_config.sequence.payload_size
|| memcmp(obu.payload, m->av1_config.sequence.payload, obu.payload_size)))
goto failed;
if (obu.type == 3 || obu.type == 6)
{
if (!obu.payload_size)
goto failed;
m->vcl = true;
m->idr = m->idr || m->av1_sequence->reduced_still_picture_header
|| (obu.payload[0] & 0xe0) == 0;
}
return RFLV_PROGRESS;
}
if (m->annexb)
{
if (!flv_annexb(m->input, m->size, &m->offset, &nal, &size)
|| m->limit - m->converted_size < 4
|| size > m->limit - m->converted_size - 4)
goto failed;
flv_uint(m->converted + m->converted_size, (uint32_t)size, 4);
memcpy(m->converted + m->converted_size + 4, nal, size);
m->converted_size += size + 4;
}
else
{
if (m->size - m->offset < 4)
goto failed;
length = ((uint32_t)m->input[m->offset] << 24) | ((uint32_t)m->input[m->offset + 1] << 16)
| ((uint32_t)m->input[m->offset + 2] << 8) | m->input[m->offset + 3];
m->offset += 4;
if (!length || length > m->size - m->offset)
goto failed;
nal = m->input + m->offset;
size = length;
m->offset += length;
}
if (nal[0] & 0x80)
goto failed;
length = ((uint32_t)m->input[m->offset] << 24) | ((uint32_t)m->input[m->offset + 1] << 16)
| ((uint32_t)m->input[m->offset + 2] << 8) | m->input[m->offset + 3];
m->offset += 4;
if (!length || length > m->size - m->offset || (m->input[m->offset] & 0x80))
goto failed;
type = m->input[m->offset] & 31;
if (m->hevc)
{
if (size < 2 || !(nal[1] & 7))
goto failed;
type = (nal[0] >> 1) & 63;
m->vcl = m->vcl || type <= 31;
m->idr = m->idr || (type >= 16 && type <= 23);
return RFLV_PROGRESS;
}
type = nal[0] & 31;
if (!type || type >= 24)
goto failed;
m->vcl = m->vcl || type <= 5;
m->idr = m->idr || type == 5;
m->offset += length;
return RFLV_PROGRESS;
failed:
m->failed = true;
@@ -277,12 +918,31 @@ bool rflv_mux_get(rflv_mux_t *m, const rflv_span_t **spans, size_t *count)
return true;
}
bool rflv_mux_get_tag(rflv_mux_t *m, rflv_tag_t *tag)
{
if (!m || !tag || !m->pending || m->output == FLV_HEADER)
return false;
tag->spans[0].data = m->prefix + 11;
tag->spans[0].size = m->spans[0].size - 11;
tag->spans[1] = m->spans[1];
tag->timestamp = m->timestamp;
tag->type = m->prefix[0];
return true;
}
bool rflv_mux_release(rflv_mux_t *m)
{
if (!m || !m->pending)
return false;
m->pending = false;
if (m->output == FLV_HEADER && m->avcc_size)
if (m->output == FLV_HEADER && m->metadata)
{
m->output = FLV_COLOR;
flv_tag(m, 1, m->metadata, m->metadata_size, 0, 0, true, true);
/* Information-frame flags preserve existing demuxer interoperability. */
m->prefix[11] = 0xd4;
}
else if ((m->output == FLV_HEADER || m->output == FLV_COLOR) && m->avcc_size)
{
m->output = FLV_VIDEO_CONFIG;
flv_tag(m, 1, m->config, m->avcc_size, 0, 0, true, true);
@@ -319,5 +979,8 @@ void rflv_mux_free(rflv_mux_t *m)
if (!m)
return;
free(m->config);
free(m->converted);
free(m->metadata);
free(m->av1_sequence);
free(m);
}
+463
View File
@@ -0,0 +1,463 @@
/* Copyright (C) 2010-2026 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (rgif.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdlib.h>
#include <string.h>
#include <retro_endianness.h>
#include <formats/image.h>
#include <formats/rgif.h>
#define RG_CODES 4096 /* 12-bit LZW codes */
struct rgif
{
/* the LZW table: each code is its prefix code and one last index */
int16_t prefix[RG_CODES];
uint8_t first[RG_CODES]; /* the first index of the code's string */
uint8_t suffix[RG_CODES];
uint8_t stack[RG_CODES]; /* a string, last index first */
uint8_t pal[256][4]; /* the global table: R, G, B, alpha */
uint8_t lpal[256][4]; /* the image's own */
uint8_t px[256][4]; /* each index as written: in @order, or
0 where it is transparent */
/* the image_transfer interface's file */
const uint8_t *buf;
size_t len;
size_t avail;
};
/* The file, read as stb_image reads it: past the end, every byte is 0 */
struct rg_in
{
const uint8_t *p;
size_t at, len;
};
static unsigned rg_get8(struct rg_in *in)
{
return in->at < in->len ? in->p[in->at++] : 0;
}
static unsigned rg_get16(struct rg_in *in)
{
unsigned lo = rg_get8(in);
return lo | (rg_get8(in) << 8);
}
static void rg_skip(struct rg_in *in, size_t n)
{
in->at = n < in->len - in->at ? in->at + n : in->len;
}
static void rg_put(uint8_t *d, unsigned order, unsigned r, unsigned g,
unsigned b, unsigned a)
{
switch (order)
{
case RGIF_ARGB:
d[0] = (uint8_t)a; d[1] = (uint8_t)r; d[2] = (uint8_t)g; d[3] = (uint8_t)b;
break;
case RGIF_BGRA:
d[0] = (uint8_t)b; d[1] = (uint8_t)g; d[2] = (uint8_t)r; d[3] = (uint8_t)a;
break;
case RGIF_ABGR:
d[0] = (uint8_t)a; d[1] = (uint8_t)b; d[2] = (uint8_t)g; d[3] = (uint8_t)r;
break;
default:
d[0] = (uint8_t)r; d[1] = (uint8_t)g; d[2] = (uint8_t)b; d[3] = (uint8_t)a;
break;
}
}
static void rg_colours(struct rg_in *in, uint8_t pal[256][4], unsigned n,
int transparent)
{
unsigned i;
for (i = 0; i < n; i++)
{
pal[i][0] = (uint8_t)rg_get8(in);
pal[i][1] = (uint8_t)rg_get8(in);
pal[i][2] = (uint8_t)rg_get8(in);
pal[i][3] = (int)i == transparent ? 0 : 255;
}
}
static int rg_header(struct rg_in *in, unsigned *w, unsigned *h)
{
unsigned v;
if (rg_get8(in) != 'G' || rg_get8(in) != 'I' || rg_get8(in) != 'F'
|| rg_get8(in) != '8')
return 0;
v = rg_get8(in);
if ((v != '7' && v != '9') || rg_get8(in) != 'a')
return 0;
*w = rg_get16(in);
*h = rg_get16(in);
return 1;
}
bool rgif_size(const uint8_t *data, size_t len,
unsigned *width, unsigned *height)
{
struct rg_in in;
in.p = data; in.at = 0; in.len = data ? len : 0;
return rg_header(&in, width, height) != 0;
}
rgif_t *rgif_new(void)
{
return (rgif_t*)calloc(1, sizeof(rgif_t));
}
void rgif_free(rgif_t *g)
{
free(g);
}
/* Where the image's pixels go, in the order its LZW stream gives them */
struct rg_raster
{
uint8_t *out;
size_t stride;
unsigned x0, x1; /* the image's columns, x0 to x1 - 1 */
unsigned y0, y1; /* and rows */
unsigned x, y; /* the next pixel */
unsigned step; /* rows from one to the next in this pass */
unsigned parse; /* interlace passes still to come */
uint8_t *row; /* out + y * stride */
};
/* A code's string, out to the image; the rows of an interlaced image in
* GIF's four passes */
static void rg_emit(rgif_t *g, struct rg_raster *r, unsigned code)
{
unsigned n = 0;
do
{
g->stack[n++] = g->suffix[code];
code = (unsigned)g->prefix[code];
} while (code < RG_CODES);
while (n)
{
if (r->y >= r->y1)
return; /* past the image: the rest of the stream is dropped */
memcpy(r->row + (size_t)r->x * 4, g->px[g->stack[--n]], 4);
if (++r->x >= r->x1)
{
r->x = r->x0;
r->y += r->step;
while (r->y >= r->y1 && r->parse)
{
r->step = 1u << r->parse; /* 8, 4, 2: from row 4, 2, 1 */
r->y = r->y0 + r->step / 2;
r->parse--;
}
if (r->y < r->y1)
r->row = r->out + (size_t)r->y * r->stride;
}
}
}
/* The image's LZW stream, as stb_image reads it; 0 where it is broken */
static int rg_raster(rgif_t *g, struct rg_in *in, struct rg_raster *r)
{
unsigned lzw_cs = rg_get8(in);
unsigned clear, codesize, codemask, avail, bits = 0, valid = 0, len = 0;
int oldcode = -1, first = 1;
unsigned i;
if (lzw_cs > 12)
return 0;
clear = 1u << lzw_cs;
codesize = lzw_cs + 1;
codemask = (1u << codesize) - 1;
for (i = 0; i < clear; i++)
{
g->prefix[i] = -1;
g->first[i] = (uint8_t)i;
g->suffix[i] = (uint8_t)i;
}
avail = clear + 2;
for (;;)
{
unsigned code;
if (valid < codesize)
{
if (!len)
{
len = rg_get8(in); /* the next sub-block */
if (!len)
return 1;
}
len--;
bits |= rg_get8(in) << valid;
valid += 8;
continue;
}
code = bits & codemask;
bits >>= codesize;
valid -= codesize;
if (code == clear)
{
codesize = lzw_cs + 1;
codemask = (1u << codesize) - 1;
avail = clear + 2;
oldcode = -1;
first = 0;
}
else if (code == clear + 1)
{
/* the end: what is left of the sub-blocks is skipped */
rg_skip(in, len);
while ((len = rg_get8(in)) > 0)
rg_skip(in, len);
return 1;
}
else if (code <= avail)
{
if (first)
return 0; /* stb_image wants a clear code first */
if (oldcode >= 0)
{
/* past 4096 a code can never be named; stb_image still
* counts them, up to 8192 */
if (avail < RG_CODES)
{
g->prefix[avail] = (int16_t)oldcode;
g->first[avail] = g->first[oldcode];
g->suffix[avail] = code == avail ? g->first[oldcode]
: g->first[code];
}
if (++avail > 8192)
return 0;
}
else if (code == avail)
return 0;
rg_emit(g, r, code);
if ((avail & codemask) == 0 && avail <= 0x0fff)
{
codesize++;
codemask = (1u << codesize) - 1;
}
oldcode = (int)code;
}
else
return 0;
}
}
bool rgif_decode(rgif_t *g, const uint8_t *data, size_t len,
uint8_t *out, size_t stride, unsigned order)
{
struct rg_in in;
unsigned w, h, flags, bgindex, eflags = 0, i;
int transparent = -1;
uint8_t bg[4];
in.p = data; in.at = 0; in.len = data ? len : 0;
if (!g || !rg_header(&in, &w, &h))
return false;
flags = rg_get8(&in);
bgindex = rg_get8(&in);
rg_get8(&in); /* aspect ratio */
memset(g->pal, 0, sizeof(g->pal));
memset(g->lpal, 0, sizeof(g->lpal));
if (flags & 0x80)
rg_colours(&in, g->pal, 2u << (flags & 7), -1);
/* Where the image does not reach: the background, opaque, unless the
* file names entry 0 (none of it, in stb_image's reading) */
if (bgindex > 0)
rg_put(bg, order, g->pal[bgindex][0], g->pal[bgindex][1],
g->pal[bgindex][2], 255);
else
memset(bg, 0, sizeof(bg));
{
unsigned y, x;
for (y = 0; y < h; y++)
{
uint8_t *row = out + (size_t)y * stride;
if (!bgindex)
memset(row, 0, (size_t)w * 4);
else
for (x = 0; x < w; x++)
memcpy(row + (size_t)x * 4, bg, 4);
}
}
for (;;)
{
unsigned tag = rg_get8(&in);
if (tag == 0x2c) /* an image */
{
struct rg_raster r;
unsigned x = rg_get16(&in), y = rg_get16(&in);
unsigned iw = rg_get16(&in), ih = rg_get16(&in);
unsigned lflags;
uint8_t (*table)[4];
if (x + iw > w || y + ih > h)
return false;
lflags = rg_get8(&in);
if (lflags & 0x80)
{
rg_colours(&in, g->lpal, 2u << (lflags & 7),
(eflags & 1) ? transparent : -1);
table = g->lpal;
}
else if (flags & 0x80)
table = g->pal;
else
return false;
/* a transparent index, or one past the table, leaves the pixel
* as stb_image's first frame has it: transparent black */
for (i = 0; i < 256; i++)
{
if (table[i][3] > 128)
rg_put(g->px[i], order, table[i][0], table[i][1], table[i][2],
table[i][3]);
else
memset(g->px[i], 0, 4);
}
r.out = out;
r.stride = stride;
r.x0 = x;
r.x1 = x + iw;
r.y0 = y;
r.y1 = y + ih;
r.x = x;
r.y = iw ? y : r.y1; /* no columns: nothing to draw */
if (lflags & 0x40)
{
r.step = 8;
r.parse = 3;
}
else
{
r.step = 1;
r.parse = 0;
}
r.row = out + (size_t)y * stride;
return rg_raster(g, &in, &r) != 0;
}
else if (tag == 0x21) /* an extension */
{
unsigned n;
if (rg_get8(&in) == 0xf9) /* graphic control */
{
n = rg_get8(&in);
if (n != 4)
{
/* stb_image skips it and reads on from there */
rg_skip(&in, n);
continue;
}
eflags = rg_get8(&in);
rg_get16(&in); /* delay */
if (transparent >= 0)
g->pal[transparent][3] = 255;
if (eflags & 1)
{
transparent = (int)rg_get8(&in);
g->pal[transparent][3] = 0;
}
else
{
rg_skip(&in, 1);
transparent = -1;
}
}
while ((n = rg_get8(&in)) != 0)
rg_skip(&in, n);
}
else
return false; /* the trailer before any image, or no GIF block */
}
}
bool rgif_header_ready(const uint8_t *data, size_t len)
{
unsigned w, h;
return data && len >= 13 && rgif_size(data, len, &w, &h);
}
bool rgif_set_buf_ptr(rgif_t *g, void *data, size_t len)
{
if (!g)
return false;
g->buf = (const uint8_t*)data;
g->len = len;
g->avail = len;
return true;
}
void rgif_set_avail(rgif_t *g, size_t avail)
{
if (g)
g->avail = avail < g->len ? avail : g->len;
}
int rgif_process_image(rgif_t *g, void **buf, size_t size,
unsigned *width, unsigned *height, bool supports_rgba)
{
unsigned w, h, order;
size_t len;
uint8_t *px;
if (!g || !g->buf || !buf)
return IMAGE_PROCESS_ERROR;
len = g->len ? g->len : size;
if (g->avail < len)
return IMAGE_PROCESS_WAIT; /* nothing taken: the whole file first */
if (!rgif_size(g->buf, len, &w, &h) || !w || !h)
return IMAGE_PROCESS_ERROR;
if (!(px = (uint8_t*)malloc((size_t)w * h * 4)))
return IMAGE_PROCESS_ERROR;
/* the 32-bit pixel, ARGB or ABGR from the high byte, as it lies in
* memory */
if (is_little_endian())
order = supports_rgba ? RGIF_RGBA : RGIF_BGRA;
else
order = supports_rgba ? RGIF_ABGR : RGIF_ARGB;
if (!rgif_decode(g, g->buf, len, px, (size_t)w * 4, order))
{
free(px);
return IMAGE_PROCESS_ERROR;
}
*buf = px;
*width = w;
*height = h;
return IMAGE_PROCESS_END;
}
+2 -1
View File
@@ -9,7 +9,8 @@ was explicitly authorized by the maintainer on 2026-10-13.
OpenH264 data
------------
The CAVLC tables in rh264.c and the generated CABAC initialization data in
The CAVLC tables in rh264_cavlc_tables.h (shared by rh264.c and
rh264_encode.c) and the generated CABAC initialization data in
rh264_cabac_tables.h contain data adapted from Cisco OpenH264. These portions
remain BSD-2-Clause, with the Cisco copyright, conditions and disclaimer
retained in the source and COPYING.OpenH264. The MIT permission applies to
Loaded 100 of 573 files, more files were not shown because too many files have changed in this diff. Show more