Skip to content

Commit f36c60b

Browse files
committed
Harden the parser; add an error API; add web builds and a sample viewer; bump version to 1.1.0
That was quite a lot I've been dragging my feet on. Security and memory safety: * Fix discovered memory-safety bugs: signed-overflow in the warp grid expansion, an out-of-bounds read on negative keyframe indices, NULL+0 pointer arithmetic when the blend-key table is empty, NULL derefs on empty art-mesh bindings and missing key_count reads at the blend-window boundary, and an index underflow in psm__valid_range. * Improved fuzzing verification: PSM_DEBUG_MALLOC build mode that uses `malloc` for every model field. Public API: * Add per-model csmGetLastError / csmGetErrorString; record error codes in the MOC3 header's padding, and expose csmGetMocError so applications can receive detailed failure status. * Add csmGetExtendedVersionString() with embedded git hash to query the exact library build. * Misc. correctness fixes: propagate color to opacity-0 meshes and stop the v5.3 extended-modes bit masking constant-mode bits. Style / housekeeping: * .clang-format for the library (BSD-KNF) and samples/viewer (raylib layout), a clang-format pre-commit hook, make format/format-check targets, and a CI check. * Line wrapping, standardizing on `bool` for all booleans, remove VLAs, rename many symbols for consistency, and more! Sample viewer: * Implement a sample MOC3 viewer (both v5 and v6 ABI support) using Raylib. Supports all blend modes from v6. WASM port: * Web/WASM drop-in Live2DCubismCore.js for both v5 and v6. Build: * Makefile rewrite with per-OS shared rules and a proper platform matrix. WASM support also added. * Bundle now generated from a template (src/bundle.c.in) instead of manually by a script. * CMake build system added for cross-compilation. This should be easier to build on plain Windows. CI: * Misc improvements: cross-build is skipped on v* tag pushes (the release job rebuilds the matrix itself), and the release job verifies the tag matches PSM_TRUE_VERSION in the header before building.
1 parent 97dd2a5 commit f36c60b

83 files changed

Lines changed: 14323 additions & 2049 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.clang-format

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Purism Core house style: BSD KNF / OpenBSD style(9)-ish.
2+
# - return type on its own line for function definitions
3+
# - column-aligned declarations and #define values
4+
# - 2-space indent, control-statement braces attached, function braces on own line
5+
# - preprocessor directives indented after the '#'
6+
# Function-like "statement" macros (control-flow bodies) are hand-aligned and
7+
# fenced with `// clang-format off/on` where this config would reflow them.
8+
Language: Cpp
9+
BasedOnStyle: LLVM
10+
IndentWidth: 2
11+
UseTab: Never
12+
ContinuationIndentWidth: 4
13+
ColumnLimit: 0
14+
AlignAfterOpenBracket: DontAlign
15+
BreakBeforeBraces: Linux
16+
AlwaysBreakAfterReturnType: AllDefinitions
17+
PointerAlignment: Right
18+
AllowShortFunctionsOnASingleLine: None
19+
AllowShortIfStatementsOnASingleLine: WithoutElse
20+
AllowShortLoopsOnASingleLine: false
21+
SpaceBeforeParens: ControlStatements
22+
Cpp11BracedListStyle: false
23+
IndentCaseLabels: false
24+
KeepEmptyLinesAtTheStartOfBlocks: false
25+
SortIncludes: Never
26+
AlignEscapedNewlines: DontAlign
27+
AlignTrailingComments:
28+
Kind: Leave
29+
IndentPPDirectives: AfterHash
30+
AlignConsecutiveDeclarations: true
31+
AlignConsecutiveMacros: true

.githooks/pre-commit

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/sh
2+
# Pre-commit: reject staged C sources that aren't clang-format-clean.
3+
#
4+
# Enable once per clone: git config core.hooksPath .githooks (or: make hooks)
5+
# Override the binary: CLANG_FORMAT=clang-format-19 git commit ...
6+
#
7+
# Each file's nearest .clang-format decides the style; DisableFormat trees
8+
# (tests, samples, vendored) are no-ops. Checks the working-tree copy of the
9+
# staged files (the usual, fast approximation) -- if a file has both staged and
10+
# unstaged edits, format the whole file before committing.
11+
12+
CLANG_FORMAT="${CLANG_FORMAT:-clang-format}"
13+
14+
files=$(git diff --cached --name-only --diff-filter=ACM -- '*.c' '*.h')
15+
[ -z "$files" ] && exit 0
16+
17+
if ! command -v "$CLANG_FORMAT" >/dev/null 2>&1; then
18+
echo "pre-commit: $CLANG_FORMAT not found; skipping format check" >&2
19+
exit 0
20+
fi
21+
22+
if ! "$CLANG_FORMAT" --dry-run --Werror $files; then
23+
echo >&2
24+
echo "pre-commit: staged C sources need formatting." >&2
25+
echo " fix with: make format (then re-stage)" >&2
26+
exit 1
27+
fi
28+
exit 0

.github/workflows/ci.yml

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,23 @@ on:
88
branches: [master]
99

1010
jobs:
11+
format:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- uses: actions/setup-python@v5
17+
with:
18+
python-version: '3.x'
19+
20+
# Pinned: clang-format output drifts between major versions, so the gate
21+
# must use the same version the tree was formatted with.
22+
- name: Install clang-format
23+
run: pip install clang-format==19.1.7
24+
25+
- name: Check formatting
26+
run: make format-check
27+
1128
build-and-test:
1229
runs-on: ubuntu-latest
1330
steps:
@@ -28,41 +45,97 @@ jobs:
2845
cross-build:
2946
runs-on: ubuntu-latest
3047
needs: build-and-test
48+
# The release job rebuilds the whole matrix itself (and it is the only job
49+
# that can lipo a universal Viewer.app / dylib), so on a tag push the 7
50+
# cross-build targets + their artifact uploads are pure duplication -- skip
51+
# them and let release be the single build. (PR and branch pushes keep the
52+
# matrix, which uploads per-target artifacts for inspection.)
53+
if: github.event_name != 'push' || !startsWith(github.ref, 'refs/tags/v')
3154
strategy:
3255
matrix:
3356
target:
34-
- linux-x64
35-
- linux-arm64
36-
- macos-x64
37-
- macos-arm64
38-
- win-x64
39-
- win-x86
40-
- win-arm64
57+
- zig-cross-linux-x86_64
58+
- zig-cross-linux-arm64
59+
- zig-cross-macos-x86_64
60+
- zig-cross-macos-arm64
61+
- zig-cross-windows-x86_64
62+
- zig-cross-windows-x86
63+
- zig-cross-windows-arm64
4164
steps:
4265
- uses: actions/checkout@v4
4366

4467
- uses: mlugg/setup-zig@v2
4568
with:
4669
version: 0.16.0
4770

71+
# raylib v6.0 release archive for this target (viewer-in-dist is opt-in
72+
# via RAYLIB_DIR_<PRESET> env vars; if a target has no archive -- e.g.
73+
# zig-cross-windows-arm64 only ships MSVC-built raylib -- the viewer is skipped and
74+
# the library still builds). Pinned to v6.0; bump here + in the release
75+
# job together when upgrading.
76+
- name: Fetch raylib v6.0 for ${{ matrix.target }}
77+
shell: bash
78+
run: |
79+
case "${{ matrix.target }}" in
80+
zig-cross-linux-x86_64) url=raylib-6.0_linux_amd64.tar.gz; var=RAYLIB_DIR_LINUX_AMD64; ext=tar.gz ;;
81+
zig-cross-linux-arm64) url=raylib-6.0_linux_arm64.tar.gz; var=RAYLIB_DIR_LINUX_ARM64; ext=tar.gz ;;
82+
zig-cross-macos-x86_64) url=raylib-6.0_macos.tar.gz; var=RAYLIB_DIR_MACOS; ext=tar.gz ;;
83+
zig-cross-macos-arm64) url=raylib-6.0_macos.tar.gz; var=RAYLIB_DIR_MACOS; ext=tar.gz ;;
84+
zig-cross-windows-x86_64) url=raylib-6.0_win64_mingw-w64.zip; var=RAYLIB_DIR_WINDOWS_AMD64; ext=zip ;;
85+
zig-cross-windows-x86) url=raylib-6.0_win32_mingw-w64.zip; var=RAYLIB_DIR_WINDOWS_X86; ext=zip ;;
86+
zig-cross-windows-arm64) echo "no raylib archive for windows-arm64"; exit 0 ;;
87+
esac
88+
mkdir -p build/raylib
89+
base="${url%.$ext}"
90+
if [ "$ext" = tar.gz ]; then
91+
curl -sL "https://github.com/raysan5/raylib/releases/download/6.0/$url" \
92+
-o "build/raylib/$url"
93+
tar -C build/raylib -xzf "build/raylib/$url"
94+
else
95+
curl -sL "https://github.com/raysan5/raylib/releases/download/6.0/$url" \
96+
-o "build/raylib/$url"
97+
( cd build/raylib && unzip -q "$url" )
98+
fi
99+
# Expose the extracted tree path to subsequent steps via GITHUB_ENV.
100+
echo "$var=$PWD/build/raylib/$base" >> "$GITHUB_ENV"
101+
48102
- name: Cross-build (${{ matrix.target }})
49-
run: ./scripts/zig-build.sh ${{ matrix.target }} --both
103+
run: ./scripts/build-dist.sh ${{ matrix.target }}
50104

51105
- name: Upload artifacts
52106
uses: actions/upload-artifact@v4
53107
with:
54108
name: ${{ matrix.target }}
55-
path: build/${{ matrix.target }}/
109+
path: dist/sdk/
56110

57111
release:
58112
if: startsWith(github.ref, 'refs/tags/v')
59113
runs-on: ubuntu-latest
60-
needs: cross-build
114+
# Cross-build is skipped on tag pushes, so depend on build-and-test (which
115+
# always runs) rather than the matrix; release rebuilds the full matrix
116+
# itself anyway.
117+
needs: build-and-test
61118
permissions:
62119
contents: write
63120
steps:
64121
- uses: actions/checkout@v4
65122

123+
- name: Check tag matches PSM_TRUE_VERSION
124+
shell: bash
125+
run: |
126+
tag="${GITHUB_REF#refs/tags/}"; tag="${tag#v}"
127+
ver_hex=$(sed -n 's/^#define PSM_TRUE_VERSION[[:space:]]*\(0x[0-9A-Fa-f]*\).*/\1/p' include/PurismCore.h)
128+
[ -n "$ver_hex" ] || { echo "error: PSM_TRUE_VERSION not found in include/PurismCore.h" >&2; exit 1; }
129+
v_major=$(( ($ver_hex >> 24) & 0xFF ))
130+
v_minor=$(( ($ver_hex >> 16) & 0xFF ))
131+
v_patch=$(( $ver_hex & 0xFFFF ))
132+
version="$v_major.$v_minor.$v_patch"
133+
if [ "$tag" != "$version" ]; then
134+
echo "error: tag v$tag does not match PSM_TRUE_VERSION ($version)" >&2
135+
exit 1
136+
fi
137+
echo "ok: tag v$tag matches PSM_TRUE_VERSION ($version)"
138+
66139
- uses: mlugg/setup-zig@v2
67140
with:
68141
version: 0.16.0
@@ -73,6 +146,30 @@ jobs:
73146
-o /usr/local/bin/lipo
74147
chmod +x /usr/local/bin/lipo
75148
149+
# Download every raylib v6.0 archive the dist matrix needs (viewer-in-dist
150+
# is opt-in via RAYLIB_DIR_<PRESET>; without these env vars, build-dist.sh
151+
# silently skips the viewer and ships the libraries only).
152+
- name: Fetch raylib v6.0 (all targets)
153+
shell: bash
154+
run: |
155+
mkdir -p build/raylib
156+
fetch() { # <url> <top-dir-name> <ext> <env-var>
157+
url="$1"; top="$2"; ext="$3"; var="$4"
158+
curl -sL "https://github.com/raysan5/raylib/releases/download/6.0/$url" \
159+
-o "build/raylib/$url"
160+
if [ "$ext" = tar.gz ]; then
161+
tar -C build/raylib -xzf "build/raylib/$url"
162+
else
163+
( cd build/raylib && unzip -q "$url" )
164+
fi
165+
echo "$var=$PWD/build/raylib/$top" >> "$GITHUB_ENV"
166+
}
167+
fetch raylib-6.0_linux_amd64.tar.gz raylib-6.0_linux_amd64 tar.gz RAYLIB_DIR_LINUX_AMD64
168+
fetch raylib-6.0_linux_arm64.tar.gz raylib-6.0_linux_arm64 tar.gz RAYLIB_DIR_LINUX_ARM64
169+
fetch raylib-6.0_macos.tar.gz raylib-6.0_macos tar.gz RAYLIB_DIR_MACOS
170+
fetch raylib-6.0_win64_mingw-w64.zip raylib-6.0_win64_mingw-w64 zip RAYLIB_DIR_WINDOWS_AMD64
171+
fetch raylib-6.0_win32_mingw-w64.zip raylib-6.0_win32_mingw-w64 zip RAYLIB_DIR_WINDOWS_X86
172+
76173
- name: Build distribution
77174
run: ./scripts/build-dist.sh
78175

@@ -92,4 +189,4 @@ jobs:
92189
files: |
93190
dist/PurismCore-*.zip
94191
dist/PurismCoreBundle-*.h
95-
generate_release_notes: true
192+
generate_release_notes: true

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ testdata/*
3232
# OS
3333
.DS_Store
3434
Thumbs.db
35+
/shot.png

0 commit comments

Comments
 (0)