Two defects in the SCC/CCD writer.
1. Special characters gained a spurious leading space.
#2301 emits a fallback base character before every internal code >= 0x80, but
only EXTENDED characters (0x90-0xcf, hi 0x12/0x13) backspace-replace the cell
before them -- handle_extended() decrements cursor_column. SPECIAL characters
(0x80-0x8f, hi 0x11) are stand-alone: handle_double() writes them without
moving the cursor back, so the base character stays on screen and every one of
them came back one column to the right on re-decode.
Only emit the base character for extended codes. check_padding() still runs for
both so the two-byte code starts on an even offset and lands inside a single
SCC word.
Verified with an SCC exercising all 80 codes in 0x80-0xcf, re-encoded and
decoded again: 79/80 wrong before #2301, 16/80 after it, 0/80 now. On real
samples, 725a49f871 (15 music-note rows) and c032183ef0 (3) round-trip with no
altered text; the apostrophe from #2098 is unchanged at "a7 80 92 29".
2. Out-of-bounds control code index when a style change starts at column 0.
get_preamble_code() and get_tab_offset_code() take unsigned char, so the
column - 1 used to place the preamble one cell left wrapped to 255 at column 0,
yielding 255 / 4 = 63 and an index far past the end of control_codes[]. Row 12
produced code 186 against CONTROL_CODE_MAX 147.
In --out=ccd that garbage entry is passed to strlen() and segfaults; in
--out=scc it silently emits whatever ints follow the array. The read has been
there since before #2301 -- 2 of 20 local samples hit it -- but the layout
change from #2301 moved the garbage pointer into unmapped memory, so it now
crashes rather than misbehaving quietly.
Clamp the preamble column to 0, matching what the adjacent space branch already
does.
Non-SCC output (txt, sami, srt, ttxt, webvtt, g608) is byte-identical to master
across 20 samples. Of those 20, SCC output changes on 12 with special
characters and on 1 that hit the out-of-bounds index; the other 7 are
unchanged. Valgrind reports no invalid reads, only the pre-existing 32-byte
init_encoder leak that master has too.
* fix: move C0 bounds check before match, improve C1 warn message
- C0 handler: bounds check was after the match, meaning process_p16(&block[1..])
could panic with an index out of bounds before the guard ran. Moved the check
before the match to prevent this.
- C1 handler: improved the warn message to include command code, name, and
lengths for easier debugging.
Fixes#1407
* docs: update CHANGES.TXT for #1407 fix
* style: cargo fmt
---------
Co-authored-by: Dhanush Varma <your@email.com>
Replace all 6 hardcoded 1000/29.97 frame delay calculations in
dtvcc_write_scc() with 1000/current_fps so that CEA-708 SCC output
uses the actual stream framerate instead of assuming NTSC 29.97.
Fixes#2172
* feat(ssa): add guarded ASS \pos positioning for CEA-608 captions
* fix(ssa): correct ASS positioning anchor, validate row adjacency, and clean up variable placement
* fix(ssa): adjust top margin to prevent clipping of top-positioned CEA-608 captions
* ssa: map CEA-608 row+col to ASS coords using FFmpeg safe-area formula and fix \an2→\an7 anchor
* fix: memory leaks and invalid CSS in WebVTT encoder
- Remove 6 unnecessary strdup() calls on string literals in
write_cc_buffer_as_webvtt() — literals are passed directly to
write_wrapped() which takes void*, no heap allocation needed.
This runs in a per-character inner loop and leaked on every
styled subtitle in a broadcast.
- Fix invalid CSS: rgba(0, 256, 0, 0.5) -> rgba(0, 255, 0, 0.5)
CSS color channels are 0-255; 256 is out of range.
- Fix missing free(unescaped) on write-error path in
write_stringz_as_webvtt() — matched the existing pattern on
the adjacent error path which correctly freed both el and unescaped.
Fixes#2154
* fix: move WebVTT changelog entry to unreleased 0.96.7 section
- Add case 4 (23.976f) to both get_scc_fps() and get_scc_fps_internal()
in ccx_encoders_scc.c so --scc-framerate 23.98 produces correct output
- Add "23.98" | "23.976" match arm in parser.rs mapping to value 4
- Add test_scc_framerate_23_98() unit test in parser.rs
- Update --scc-framerate help text to clarify it affects both input
parsing AND output encoding (not input only)
- Add 23.98 to the listed valid values in the help text
Follows up on discussion in #2145 and #2146.
Fix multiple bugs in parse_file(): heap buffer overflow on long lines
(no bounds check on 128-byte buffer), broken EOF detection (fgetc()
return stored in char instead of int), missing NULL check after malloc,
missing null-terminator on accumulated string, and last line silently
dropped if file lacks trailing newline.
Also fix typos in configuration_map[]: FIX_PADDINDG → FIX_PADDING,
INVASTIGATE_PACKET → INVESTIGATE_PACKET.
save_sub_track() was missing two things:
1. No check on the return value of open(). If open() failed,
desc would be -1, and the subsequent write_wrapped() calls
would trigger a fatal error with a confusing message.
2. The file descriptor was never closed at the end of the function,
leaking it on every call. The neighboring save_vobsub_track()
already handles both correctly — this brings save_sub_track()
in line with it.
In save_sub_track(), the length argument for writing timestamp_end
was incorrectly using strlen(timestamp_start) instead of
strlen(timestamp_end). This affected WebVTT, SRT, and ASS/SSA
output paths for Matroska subtitle extraction.
The --mkvlang option previously only supported single ISO 639-2 codes
due to using a Language enum with a fixed list of variants. Extended
codes (like "fre-ca") and multiple codes (like "eng,chi") would panic.
This change introduces MkvLangFilter, a proper type for language
filtering that:
- Validates language codes per BCP 47 specification
- Supports ISO 639-2 (3-letter codes like "eng")
- Supports BCP 47 tags (like "en-US", "zh-Hans-CN")
- Supports comma-separated multiple codes
- Provides clean error messages for invalid input
- Includes comprehensive unit tests
The C code continues to receive the raw string for strstr() matching,
maintaining backward compatibility.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update version number across all packaging and build files for the
0.96.5 release.
Files updated:
- docs/CHANGES.TXT - Added changelog entry
- src/lib_ccx/lib_ccx.h - VERSION define
- linux/configure.ac - AC_INIT version
- mac/configure.ac - AC_INIT version
- OpenBSD/Makefile - V variable
- package_creators/PKGBUILD - pkgver
- package_creators/ccextractor.spec - Version
- package_creators/debian.sh - VERSION
- packaging/chocolatey/ccextractor.nuspec - version
- packaging/chocolatey/tools/chocolateyInstall.ps1 - URL
- packaging/winget/*.yaml - PackageVersion and URLs
Note: SHA256 checksums in chocolatey and winget files will need to be
updated after the MSI is built.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>