@@ -53,8 +53,12 @@ detect_wolf3d_version() {
5353 vswap_file=$( find " $folder " -maxdepth 1 -type f -iname " vswap.$version " -print -quit 2> /dev/null)
5454
5555 if [[ -n " $gamemaps_file " && -n " $maphead_file " && -n " $vswap_file " ]]; then
56- printf ' %s' " $version "
57- return 0
56+ if validate_wolf3d_version_hash " $folder " " $version " ; then
57+ printf ' %s' " $version "
58+ return 0
59+ else
60+ log d " Version '$version ' rejected for '$folder ' because hash validation failed"
61+ fi
5862 fi
5963 done
6064
@@ -76,6 +80,99 @@ pretty_wolf3d_version() {
7680 esac
7781}
7882
83+ # Known IWAD hash fixtures for core file trio to avoid false positives in mod folders.
84+ # (Source: ECWolf docs, checked on Wolfenstein 3D / Spear of Destiny / Noah's Ark)
85+ declare -A wolf3d_data_hashes
86+ wolf3d_data_hashes=(
87+ [wl6.gamemaps]=" a4e73706e100dc0cadfb02d23de46481"
88+ [wl6.maphead]=" b8d2a78bc7c50da7ec9ab1d94f7975e1"
89+ # Support both canonical and alternate WL6 vswap variants observed in field
90+ [wl6.vswap]=" b8ff4997461bafa5ef2a94c11f9de001 a6d901dfb455dfac96db5e4705837cdb"
91+
92+ [wl1.gamemaps]=" 30fecd7cce6bc70402651ec922d2da3d"
93+ [wl1.maphead]=" 7b6dd4e55c33c33a41d1600be5df3228"
94+ [wl1.vswap]=" 6efa079414b817c97db779cecfb081c9"
95+
96+ [sdm.gamemaps]=" 4eb2f538aab6e4061dadbc3b73837762"
97+ [sdm.maphead]=" 40fa03caf7a1a4dbd22da4321c6e10d4"
98+ [sdm.vswap]=" 35afda760bea840b547d686a930322dc"
99+
100+ [sod.gamemaps]=" 04f16534235b4b57fc379d5709f88f4a"
101+ [sod.maphead]=" 276c79a4a6419db6b23e7699e41cb9fa"
102+ [sod.vswap]=" b1dac0a8786c7cdbb09331a4eba00652"
103+
104+ [sd2.gamemaps]=" d55508cd58e2e61076ac81b98aeb9269"
105+ [sd2.maphead]=" 25d92ac0ba012a1e9335c747eb4ab177"
106+ [sd2.vswap]=" fa5752c5b1e25ee5c4a9ec0e9d4013a9"
107+
108+ [sd3.gamemaps]=" 4219d83568d770b1c6ac9c2d4d1dfb9e"
109+ [sd3.maphead]=" 52fd50245a77e61dc1df91110c186195"
110+ [sd3.vswap]=" e3e87518f51414872c454b7d72a45af6"
111+
112+ [sd3-alt.gamemaps]=" 29860b87c31348e163e10f8aa6f19295"
113+ [sd3-alt.maphead]=" a8b24dd3d3271e0b7fc6f2f995915f27"
114+ [sd3-alt.vswap]=" 94aeef7980ef640c448087f92be16d83"
115+
116+ [n3d.gamemaps]=" d35ce2257a4fb56f61529df5f7f77adb"
117+ [n3d.maphead]=" 2eaab4dd50856abeaebe75a8bcbbab42"
118+ [n3d.vswap]=" 8c61a9b3bb38a598990ccb743d2679fa"
119+ )
120+
121+ expected_wolf3d_hash () {
122+ local version=" $1 " file=" $2 " key
123+ key=" $version .$file "
124+ # support alternate sd3 variant too
125+ [[ -n " ${wolf3d_data_hashes[$key]:- } " ]] && printf ' %s' " ${wolf3d_data_hashes[$key]} " && return 0
126+ if [[ " $version " == " sd3" ]]; then
127+ key=" sd3-alt.$file "
128+ [[ -n " ${wolf3d_data_hashes[$key]:- } " ]] && printf ' %s' " ${wolf3d_data_hashes[$key]} " && return 0
129+ fi
130+ return 1
131+ }
132+
133+ validate_wolf3d_version_hash () {
134+ local folder=" $1 " version=" $2 "
135+ local md5cmd
136+ md5cmd=$( command -v md5sum || true)
137+ if [[ -z " $md5cmd " ]]; then
138+ log w " md5sum not found, version detection will proceed by filenames only"
139+ return 0
140+ fi
141+
142+ for file in gamemaps maphead vswap; do
143+ # case-insensitive path resolution, because actual files may use uppercase extensions/names
144+ local path
145+ path=$( find " $folder " -maxdepth 1 -type f -iname " ${file} .${version} " -print -quit 2> /dev/null)
146+ [[ -n " $path " ]] || return 1
147+
148+ local expected
149+ expected=$( expected_wolf3d_hash " $version " " $file " )
150+ if [[ -z " $expected " ]]; then
151+ log d " Hash not available for $file .$version , rejecting wildcard match to avoid mod false positive"
152+ return 1
153+ fi
154+
155+ local actual
156+ actual=$( $md5cmd " $path " | awk ' {print tolower($1)}' )
157+
158+ local match=0
159+ for allowed in $expected ; do
160+ if [[ " $actual " == " $allowed " ]]; then
161+ match=1
162+ break
163+ fi
164+ done
165+
166+ if (( match == 0 )) ; then
167+ log d " Hash mismatch for $path ($version ): expected one of [$expected ], got $actual "
168+ return 1
169+ fi
170+ done
171+
172+ # If we reached here, all three core files matched expected hashes
173+ return 0
174+ }
175+
79176# Normalize user-provided Wolf3D version keys from .wolf data= values.
80177normalize_wolf3d_version_key () {
81178 local input=" ${1,,} "
@@ -211,12 +308,13 @@ if [[ -f "$input_path" && "${input_path##*.}" == "wolf" ]]; then
211308 wolf3d_data_version_pretty=" $( pretty_wolf3d_version " $version " ) "
212309 log i " Mod mode: base IWAD set to '$iwad_folder ' (found $wolf3d_data_version_pretty )"
213310
311+ requested_mod_files=()
214312 if [[ ${# mod_files[@]} -gt 0 ]]; then
215313 for mod_entry in " ${mod_files[@]} " ; do
216314 if [[ " $mod_entry " == /* ]]; then
217- args +=( --file " $mod_entry " )
315+ requested_mod_files +=( --file " $mod_entry " )
218316 else
219- args +=( --file " $mod_folder /$mod_entry " )
317+ requested_mod_files +=( --file " $mod_folder /$mod_entry " )
220318 fi
221319 done
222320 launch_folder=" $iwad_folder "
278376# Always use requested base options and preserved args.
279377args=( " ${raw_args[@]} " )
280378
379+ if [[ ${# requested_mod_files[@]} -gt 0 ]]; then
380+ args+=( " ${requested_mod_files[@]} " )
381+ fi
382+
281383if [[ -n " $main_game " ]]; then
282384 args+=( " $main_game " )
283385fi
0 commit comments