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;
0 commit comments