Skip to content

Commit ef261d7

Browse files
committed
nzp-team/nzportable#1194 - Fix broken Ballistic Knife assets crashing Vril on load
1 parent 0034eb0 commit ef261d7

11 files changed

Lines changed: 422 additions & 0 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Verify MDL Models Conform to Standard
2+
on: [pull_request]
3+
jobs:
4+
PCX-Conformity:
5+
name: Verify MDL Models Conform to Standard
6+
runs-on: ubuntu-latest
7+
container:
8+
image: ubuntu:24.10
9+
steps:
10+
- name: Install xxd
11+
run: apt update && apt install xxd -y
12+
shell: bash
13+
- name: Checkout
14+
uses: actions/checkout@v2
15+
- name: Wait for GitHub to keep up..
16+
run: sleep 2s
17+
shell: bash
18+
- name: Run Script
19+
run: |
20+
bash testing/mdl_validator.sh
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Verify PCX Textures Conform to Standard
2+
on: [pull_request]
3+
jobs:
4+
PCX-Conformity:
5+
name: Verify PCX Textures Conform to Standard
6+
runs-on: ubuntu-latest
7+
container:
8+
image: ubuntu:24.10
9+
steps:
10+
- name: Install file
11+
run: apt update && apt install file -y
12+
shell: bash
13+
- name: Checkout
14+
uses: actions/checkout@v2
15+
- name: Wait for GitHub to keep up..
16+
run: sleep 2s
17+
shell: bash
18+
- name: Run Script
19+
run: |
20+
bash testing/pcx_validator.sh
0 Bytes
Binary file not shown.

common/models/props/mailbox.mdl

-657 KB
Binary file not shown.
-13.4 KB
Binary file not shown.
1.66 KB
Binary file not shown.
-7.13 KB
Binary file not shown.
7.65 KB
Binary file not shown.

testing/mdl_validator.sh

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
#!/bin/bash
2+
#
3+
# Nazi Zombies: Portable
4+
# Very involved .MDL header and vertex+uv validation
5+
# ----
6+
# This is intended to be used via a Docker
7+
# container running ubuntu:24.10.
8+
#
9+
set -o errexit
10+
11+
ASSETS_ROOT=$(dirname "${BASH_SOURCE[0]}")/../
12+
cd "${ASSETS_ROOT}"
13+
14+
source "${ASSETS_ROOT}/testing/utils.sh"
15+
16+
#
17+
# Constants for validation
18+
#
19+
MDL_HEADER_LEN="84"
20+
21+
MDL_HEADER_MAGIC="IDPO"
22+
MDL_HEADER_MAGIC_LEN="4"
23+
MDL_HEADER_MAGIC_OFS="0"
24+
25+
MDL_HEADER_VERSION="6"
26+
MDL_HEADER_VERSION_OFS="4"
27+
28+
MDL_SKINWIDTH_MAX="512"
29+
MDL_SKINWIDTH_OFS="52"
30+
31+
MDL_SKINHEIGHT_MAX="512"
32+
MDL_SKINHEIGHT_OFS="56"
33+
34+
MDL_NUMVERTS_MAX="2048"
35+
MDL_NUMVERTS_OFS="60"
36+
37+
MDL_NUMTRIS_MAX="1024"
38+
MDL_NUMTRIS_OFS="64"
39+
40+
MDL_NUMFRAMES_MAX="256"
41+
MDL_NUMFRAMES_OFS="68"
42+
43+
#
44+
# validate_mdl_header()
45+
# ----
46+
# Validates entries in header for MDL file
47+
#
48+
function validate_mdl_header()
49+
{
50+
local mdl_file="${1}"
51+
local should_fail="0"
52+
53+
# Magic
54+
local magic=$(read_string_in_file_at_ofs "${mdl_file}" "${MDL_HEADER_MAGIC_LEN}" "${MDL_HEADER_MAGIC_OFS}")
55+
echo " + MAGIC: [${magic}]"
56+
if [[ "${magic}" != "${MDL_HEADER_MAGIC}" ]]; then
57+
echo " - ERROR: Bad magic! Expected [${MDL_HEADER_MAGIC}] but got [${magic}]!"
58+
should_fail="1"
59+
fi
60+
61+
# Version
62+
local version=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_HEADER_VERSION_OFS}")
63+
echo " + VERSION: [${version}]"
64+
if [[ "${version}" != "${MDL_HEADER_VERSION}" ]]; then
65+
echo " - ERROR: Bad version! Expected [${MDL_HEADER_VERSION}] but got [${version}]!"
66+
should_fail="1"
67+
fi
68+
69+
# Skin Width
70+
local skin_width=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_SKINWIDTH_OFS}")
71+
echo " + SKIN WIDTH: [${skin_width}]"
72+
if [[ "${skin_width}" -gt "${MDL_SKINWIDTH_MAX}" ]]; then
73+
echo " - ERROR: Skin width is too big! Max is [${MDL_SKINWIDTH_MAX}] but got [${skin_width}]!"
74+
should_fail="1"
75+
fi
76+
if [[ "${skin_width}" -le "0" ]]; then
77+
echo " - ERROR: Skin width is zero!"
78+
should_fail="1"
79+
fi
80+
81+
# Skin Height
82+
local skin_height=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_SKINHEIGHT_OFS}")
83+
echo " + SKIN HEIGHT: [${skin_height}]"
84+
if [[ "${skin_height}" -gt "${MDL_SKINHEIGHT_MAX}" ]]; then
85+
echo " - ERROR: Skin height is too big! Max is [${MDL_SKINHEIGHT_MAX}] but got [${skin_height}]!"
86+
should_fail="1"
87+
fi
88+
if [[ "${skin_height}" -le "0" ]]; then
89+
echo " - ERROR: Skin height is zero!"
90+
should_fail="1"
91+
fi
92+
93+
# Num Verts
94+
local num_verts=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_NUMVERTS_OFS}")
95+
echo " + VERTICES: [${num_verts}]"
96+
if [[ "${num_verts}" -gt "${MDL_NUMVERTS_MAX}" ]]; then
97+
echo " - ERROR: Verts is too big! Max is [${MDL_NUMVERTS_MAX}] but got [${num_verts}]!"
98+
should_fail="1"
99+
fi
100+
if [[ "${num_verts}" -le "0" ]]; then
101+
echo " - ERROR: Verts is zero!"
102+
should_fail="1"
103+
fi
104+
105+
# Num Tris
106+
local num_tris=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_NUMTRIS_OFS}")
107+
echo " + TRIANGLES: [${num_tris}]"
108+
if [[ "${num_tris}" -gt "${MDL_NUMTRIS_MAX}" ]]; then
109+
echo " - ERROR: Tris is too big! Max is [${MDL_NUMTRIS_MAX}] but got [${num_tris}]!"
110+
should_fail="1"
111+
fi
112+
if [[ "${num_tris}" -le "0" ]]; then
113+
echo " - ERROR: Tris is zero!"
114+
should_fail="1"
115+
fi
116+
117+
# Num Frames
118+
local num_frames=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_NUMFRAMES_OFS}")
119+
echo " + FRAMES: [${num_frames}]"
120+
if [[ "${num_frames}" -gt "${MDL_NUMFRAMES_MAX}" ]]; then
121+
echo " - ERROR: Frames is too big! Max is [${MDL_NUMFRAMES_MAX}] but got [${num_frames}]!"
122+
should_fail="1"
123+
fi
124+
if [[ "${num_frames}" -le "0" ]]; then
125+
echo " - ERROR: Frames is zero!"
126+
should_fail="1"
127+
fi
128+
129+
return "${should_fail}"
130+
}
131+
132+
#
133+
# validate_mdl_data()
134+
# ----
135+
# Validates vert, uv, mesh data in MDL file
136+
#
137+
function validate_mdl_data()
138+
{
139+
local mdl_file="${1}"
140+
local should_fail="0"
141+
142+
local file_size=$(stat -c %s "${mdl_file}")
143+
144+
local num_frames=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_NUMFRAMES_OFS}")
145+
local num_verts=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_NUMVERTS_OFS}")
146+
local num_tris=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_NUMTRIS_OFS}")
147+
local num_skins=$(read_int_in_file_at_ofs "${mdl_file}" 48)
148+
local skin_width=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_SKINWIDTH_OFS}")
149+
local skin_height=$(read_int_in_file_at_ofs "${mdl_file}" "${MDL_SKINHEIGHT_OFS}")
150+
151+
local tex_coord_size=$((num_verts * 4))
152+
local triangle_size=$((num_tris * 8))
153+
154+
local skin_data_bytes=$((num_skins * skin_width * skin_height))
155+
156+
local frame_header_size=$((4 + 12 + 12 + 16))
157+
local frame_size=$((frame_header_size + num_verts * 4))
158+
local minimum_file_size=$((MDL_HEADER_LEN + skin_data_bytes + tex_coord_size + triangle_size + frame_size * num_frames))
159+
160+
local offset="0"
161+
local i="0"
162+
local idx="0"
163+
local s="0"
164+
local t="0"
165+
166+
# We have a lot of broken UVs that don't necessarily indiciate
167+
# a problem, so disabling this..
168+
169+
# UVs
170+
# for ((i=0; i<num_verts; i++)); do
171+
# offset=$((MDL_HEADER_LEN + i * 4))
172+
# s=$(read_byte_in_file_at_ofs "${mdl_file}" $((offset + 1)))
173+
# t=$(read_byte_in_file_at_ofs "${mdl_file}" $((offset + 2)))
174+
175+
# if [[ "$s" -ge "$skin_width" || "$t" -ge "$skin_height" ]]; then
176+
# echo " - ERROR: UV $i out of bounds: (s=$s t=$t)"
177+
# should_fail="1"
178+
# fi
179+
# done
180+
181+
# Vertices
182+
for ((i=0; i<num_tris; i++)); do
183+
offset=$((MDL_HEADER_LEN + tex_coord_size + i * 8 + 2))
184+
for j in 0 2 4; do
185+
idx=$(read_short_in_file_at_ofs $((offset + j)))
186+
if [[ "${idx}" -ge "${num_verts}" ]]; then
187+
echo " - ERROR: Triangle [${i}] has invalid vertex index [${idx}]!"
188+
should_fail="1"
189+
fi
190+
done
191+
done
192+
193+
# Don't do size check for AI, since the names cause some issues
194+
if [[ "${mdl_file}" == *"/ai/"* ]]; then
195+
return "${should_fail}"
196+
fi
197+
198+
# Size check
199+
if [[ "${file_size}" -lt "${minimum_file_size}" ]]; then
200+
echo " - ERROR: Not enough vertex data (corrupt .MDL?) [${actual_data_bytes}] < [${expected_data_bytes}]!"
201+
should_fail="1"
202+
fi
203+
204+
return "${should_fail}"
205+
}
206+
207+
#
208+
# main()
209+
# ----
210+
# Test entry point.
211+
#
212+
function main()
213+
{
214+
local total_failures=0
215+
216+
# Iterate through every .mdl in our assets..
217+
while read -r mdl_file; do
218+
echo "[INFO]: Verifying MDL model [${mdl_file}].."
219+
220+
if ! validate_mdl_header "${mdl_file}"; then
221+
echo " - ERROR: Invalid header for MDL [${mdl_file}]!"
222+
total_failures=$((total_failures + 1))
223+
continue
224+
fi
225+
226+
if ! validate_mdl_data "${mdl_file}"; then
227+
echo " - ERROR: Invalid mesh data for MDL [${mdl_file}]!"
228+
total_failures=$((total_failures + 1))
229+
continue
230+
fi
231+
done < <(find . -type f -name "*.mdl")
232+
233+
if [[ "${total_failures}" -ne 0 ]]; then
234+
echo "[ERROR]: FAILED to validate [${total_failures}] MDL models!"
235+
exit 1
236+
else
237+
echo "[PASS]: No issues found :)"
238+
exit 0
239+
fi
240+
}
241+
242+
main;

testing/pcx_validator.sh

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/bin/bash
2+
#
3+
# Nazi Zombies: Portable
4+
# Validate .PCX files are of an indexed 8-bit color
5+
# and the dimensions are power-of-two.
6+
# ----
7+
# This is intended to be used via a Docker
8+
# container running ubuntu:24.10.
9+
#
10+
set -o errexit
11+
12+
ASSETS_ROOT=$(dirname "${BASH_SOURCE[0]}")/../
13+
cd "${ASSETS_ROOT}"
14+
15+
source "${ASSETS_ROOT}/testing/utils.sh"
16+
17+
#
18+
# main()
19+
# ----
20+
# Test entry point.
21+
#
22+
function main()
23+
{
24+
local total_failures=0
25+
26+
# Iterate through every .pcx in our assets..
27+
while read -r pcx_file; do
28+
echo "[INFO]: Verifying PCX texture file [${pcx_file}].."
29+
local file_output=$(file "${pcx_file}")
30+
31+
# Check for indexed keyword
32+
if [[ "${file_output}" != *", 8-bit "* ]]; then
33+
echo " - ERROR: PCX [${pcx_file}] is not of 8-bit indexed color!"
34+
total_failures=$((total_failures + 1))
35+
fi
36+
37+
# Evil regex to get the second bounding box (zero-offset width and height)
38+
if [[ "${file_output}" =~ \[([0-9]+),[[:space:]]*([0-9]+)\][[:space:]]*-[[:space:]]*\[([0-9]+),[[:space:]]*([0-9]+)\] ]]; then
39+
x1="${BASH_REMATCH[3]}"
40+
y1="${BASH_REMATCH[4]}"
41+
42+
width=$((x1 + 1))
43+
height=$((y1 + 1))
44+
45+
if ! is_pow2 "$width" || ! is_pow2 "$height"; then
46+
echo " - ERROR: Dimensions of .PCX [${pcx_file}] are not a power-of-two: [${width}x${height}]!"
47+
total_failures=$((total_failures + 1))
48+
fi
49+
else
50+
echo " - ERROR: Could not parse bounding box info, is [${pcx_file}] a real .PCX?"
51+
total_failures=$((total_failures + 1))
52+
fi
53+
done < <(find . -type f -name "*.pcx")
54+
55+
if [[ "${total_failures}" -ne 0 ]]; then
56+
echo "[ERROR]: FAILED to validate [${total_failures}] PCX textures!"
57+
exit 1
58+
else
59+
echo "[PASS]: No issues found :)"
60+
exit 0
61+
fi
62+
}
63+
64+
main;

0 commit comments

Comments
 (0)