@@ -12,4 +12,387 @@ log d "QT plugin path is: $QT_PLUGIN_PATH"
1212log d " QT QPA PLATFORM plugin path is: $QT_QPA_PLATFORM_PLUGIN_PATH "
1313
1414# Launch
15- exec " $component_path /bin/ecwolf" --fullscreen --nowait --config /var/config/ecwolf/ecwolf_rd.cfg --savesdir /var/data/ecwolf/saves " $@ "
15+ log i " RetroDECK ECWolf Runner"
16+ path=" ${@: -1} " # getting the last argument as game path
17+ args=" ${@: 1: $# -1} " # getting all the other passed args
18+
19+ append_wolf_data_files () {
20+ local folder=" $1 "
21+ local ext
22+ local file
23+
24+ [[ -d " $folder " ]] || return 1
25+
26+ for ext in wl6 wl1 sdm sod n3d; do
27+ shopt -s nullglob
28+ for file in " $folder " /* ." $ext " ; do
29+ [[ -f " $file " ]] || continue
30+ args=" $args --file \" $file \" "
31+ done
32+ shopt -u nullglob
33+ done
34+ }
35+
36+ # Identify Wolf3D version by core files in folder
37+ # Requests: gamemaps, maphead, vswap with an extension.
38+ # Preference order: wl6, wl1, sdm, sod, sd1, sd2, sd3, n3d.
39+ # Returns version name (wl6/wl1/sdm/sod/sd1/sd2/sd3/n3d) or failure.
40+ detect_wolf3d_version () {
41+ local folder=" $1 "
42+ [[ -d " $folder " ]] || return 1
43+
44+ local versions=(wl6 wl1 sdm sod sd1 sd2 sd3 n3d)
45+
46+ for version in " ${versions[@]} " ; do
47+ local gamemaps_file
48+ local maphead_file
49+ local vswap_file
50+
51+ gamemaps_file=$( find " $folder " -maxdepth 1 -type f -iname " gamemaps.$version " -print -quit 2> /dev/null)
52+ maphead_file=$( find " $folder " -maxdepth 1 -type f -iname " maphead.$version " -print -quit 2> /dev/null)
53+ vswap_file=$( find " $folder " -maxdepth 1 -type f -iname " vswap.$version " -print -quit 2> /dev/null)
54+
55+ if [[ -n " $gamemaps_file " && -n " $maphead_file " && -n " $vswap_file " ]]; then
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
62+ fi
63+ done
64+
65+ return 1
66+ }
67+
68+ pretty_wolf3d_version () {
69+ local version=" ${1,,} "
70+ case " $version " in
71+ wl6) echo " Wolfenstein 3D (Full)" ;;
72+ wl1) echo " Wolfenstein 3D (Shareware)" ;;
73+ sdm) echo " Spear of Destiny (Demo)" ;;
74+ sod) echo " Spear of Destiny (Full)" ;;
75+ sd1) echo " Spear of Destiny - Mission Pack 1 - Return to Danger" ;;
76+ sd2) echo " Spear of Destiny - Mission Pack 2 - Return to Danger" ;;
77+ sd3) echo " Spear of Destiny - Mission Pack 3 - Ultimate Challenge" ;;
78+ n3d) echo " Super 3D Noah’s Ark" ;;
79+ * ) echo " Unknown Wolf3D version: $version " ;;
80+ esac
81+ }
82+
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+
176+ # Normalize user-provided Wolf3D version keys from .wolf data= values.
177+ normalize_wolf3d_version_key () {
178+ local input=" ${1,,} "
179+ input=" ${input// \" / } "
180+ input=" ${input// \' / } "
181+ input=" ${input// [[:space:]]/ } "
182+ input=" ${input// [^a-z0-9]/ } "
183+
184+ case " $input " in
185+ wl6|wolfenstein3dfull|wolfensteinfull|full) echo wl6 ;;
186+ wl1|shareware|wolfenstein3dshareware|shareware) echo wl1 ;;
187+ sdm|spearofdestinydemo|speardestinydemo) echo sdm ;;
188+ sod|spearofdestinyfull|speardestinyfull) echo sod ;;
189+ sd1|missionpack1|returntodanger) echo sd1 ;;
190+ sd2|missionpack2|returntodanger2) echo sd2 ;;
191+ sd3|missionpack3|ultimatechallenge) echo sd3 ;;
192+ n3d|super3dnoahsark|noahsark) echo n3d ;;
193+ * ) return 1 ;;
194+ esac
195+ }
196+
197+ # Find a wolf3d data folder under $root, optionally enforcing a preferred version.
198+ # Mode "default" uses legacy priority: wl6 -> wl1 -> sdm -> sod -> sd1 -> sd2 -> sd3 -> n3d.
199+ # Mode "mod" uses mod-friendly order (full/packs first, shareware last): wl6 -> sod -> sd1 -> sd2 -> sd3 -> n3d -> sdm -> wl1.
200+ find_wolf3d_wolf_folder () {
201+ local root=" ${1:- ${roms_path} / wolf} "
202+ local preferred_version=" ${2:- } "
203+ local mode=" ${3:- default} "
204+
205+ [[ -d " $root " ]] || return 1
206+
207+ local -A version_rank
208+ if [[ " $mode " == " mod" ]]; then
209+ version_rank=( [wl6]=1 [sod]=2 [sd1]=3 [sd2]=4 [sd3]=5 [n3d]=6 [sdm]=7 [wl1]=8 )
210+ else
211+ version_rank=( [wl6]=1 [wl1]=2 [sdm]=3 [sod]=4 [sd1]=5 [sd2]=6 [sd3]=7 [n3d]=8 )
212+ fi
213+
214+ local best_path=" " best_rank=999
215+
216+ for candidate in " $root " /* .wolf; do
217+ [[ -d " $candidate " ]] || continue
218+ if [[ -n " $preferred_version " ]]; then
219+ version=" $( detect_wolf3d_version " $candidate " ) " || continue
220+ if [[ " $version " != " $preferred_version " ]]; then
221+ continue
222+ fi
223+ wolf3d_data_version=" $version "
224+ printf ' %s' " $candidate "
225+ return 0
226+ fi
227+
228+ version=" $( detect_wolf3d_version " $candidate " ) " || continue
229+ local rank=${version_rank[$version]:- 999}
230+ if (( rank < best_rank )) ; then
231+ best_rank=$rank
232+ best_path=" $candidate "
233+ wolf3d_data_version=" $version "
234+ fi
235+ done
236+
237+ if [[ -n " $best_path " ]]; then
238+ printf ' %s' " $best_path "
239+ return 0
240+ fi
241+
242+ # last chance: maybe root itself is an IWAD folder
243+ if version=" $( detect_wolf3d_version " $root " ) " ; then
244+ if [[ -z " $preferred_version " || " $version " == " $preferred_version " ]]; then
245+ wolf3d_data_version=" $version "
246+ printf ' %s' " $root "
247+ return 0
248+ fi
249+ fi
250+
251+ return 1
252+ }
253+
254+ # Start launcher mode selection
255+ raw_args=( " ${@: 1: $# -1} " )
256+ input_path=" ${@: -1} "
257+
258+ if [[ -z " $input_path " ]]; then
259+ log e " No game path argument provided"
260+ exit 1
261+ fi
262+
263+ if [[ -f " $input_path " && " ${input_path##* .} " == " wolf" ]]; then
264+ # mod descriptor path
265+ mod_wolf_file=" $input_path "
266+ mod_folder=" $( dirname " $mod_wolf_file " ) "
267+ log i " Mod descriptor provided: $mod_wolf_file "
268+
269+ mod_files=()
270+ mod_data_override=" "
271+ while IFS= read -r line || [[ -n " $line " ]]; do
272+ line=" ${line%%#* } "
273+ line=" ${line# ${line%% [![:space:]]* } } "
274+ line=" ${line% ${line##* [![:space:]]} } "
275+ [[ -z " $line " ]] && continue
276+
277+ if [[ " ${line,,} " =~ ^data[[:space:]]* = [[:space:]]* (.+)$ ]]; then
278+ raw_data=" ${BASH_REMATCH[1]} "
279+ if ! mod_data_override=" $( normalize_wolf3d_version_key " $raw_data " ) " ; then
280+ log e " Invalid data= value '$raw_data ' in '$mod_wolf_file '"
281+ exit 1
282+ fi
283+ log i " Mod descriptor requests base IWAD '$mod_data_override '"
284+ continue
285+ fi
286+
287+ mod_files+=(" $line " )
288+ done < " $mod_wolf_file "
289+
290+ if [[ -n " $mod_data_override " ]]; then
291+ iwad_folder=" $( find_wolf3d_wolf_folder " $roms_path /wolf" " $mod_data_override " " mod" ) " || {
292+ log e " Base IWAD '$mod_data_override ' specified in '$mod_wolf_file ' not found in $roms_path /wolf"
293+ exit 1
294+ }
295+ elif [[ ${# mod_files[@]} -gt 0 ]]; then
296+ iwad_folder=" $( find_wolf3d_wolf_folder " $roms_path /wolf" " wl6" " mod" ) " || {
297+ log e " No default WL6 IWAD found for mod; mod requires at least one valid base IWAD"
298+ exit 1
299+ }
300+ else
301+ iwad_folder=" $( find_wolf3d_wolf_folder " $roms_path /wolf" " " " mod" ) " || {
302+ log e " No IWAD found in $roms_path /wolf"
303+ exit 1
304+ }
305+ fi
306+
307+ version=" $( detect_wolf3d_version " $iwad_folder " ) "
308+ wolf3d_data_version_pretty=" $( pretty_wolf3d_version " $version " ) "
309+ log i " Mod mode: base IWAD set to '$iwad_folder ' (found $wolf3d_data_version_pretty )"
310+
311+ requested_mod_files=()
312+ if [[ ${# mod_files[@]} -gt 0 ]]; then
313+ for mod_entry in " ${mod_files[@]} " ; do
314+ if [[ " $mod_entry " == /* ]]; then
315+ requested_mod_files+=( --file " $mod_entry " )
316+ else
317+ requested_mod_files+=( --file " $mod_folder /$mod_entry " )
318+ fi
319+ done
320+ launch_folder=" $iwad_folder "
321+ else
322+ launch_folder=" $mod_folder "
323+ fi
324+
325+ elif [[ -d " $input_path " ]]; then
326+ version=" $( detect_wolf3d_version " $input_path " ) " || true
327+ if [[ -n " $version " ]]; then
328+ iwad_folder=" $input_path "
329+ wolf3d_data_version_pretty=" $( pretty_wolf3d_version " $version " ) "
330+ log i " IWAD mode: launching '$iwad_folder ' (detected $wolf3d_data_version_pretty )"
331+ launch_folder=" $iwad_folder "
332+ else
333+ # directory could be a mod container with same-name .wolf file
334+ candidate_mod_file=" $input_path /$( basename " $input_path " ) .wolf"
335+ if [[ -f " $candidate_mod_file " ]]; then
336+ input_path=" $candidate_mod_file "
337+ # Re-run by calling this script recursively avoids duplication; but to keep it simple, we follow same logic inline.
338+ mod_wolf_file=" $candidate_mod_file "
339+ mod_folder=" $input_path "
340+ # fallback to using this .wolf logic path
341+ # no recursive here to avoid complexity; if we reach this path we can proceed as mod with input_path set
342+ # (this scenario should be rare in Data layout)
343+ # We don't re-enter the if block, continue with auto-detect using mod_wolf_file below.
344+ else
345+ log i " No IWAD detected in '$input_path ', auto-detecting under $roms_path /wolf"
346+ iwad_folder=" $( find_wolf3d_wolf_folder " $roms_path /wolf" ) " || {
347+ log e " No valid Wolf3D data folder found under $roms_path /wolf"
348+ exit 1
349+ }
350+ version=" $( detect_wolf3d_version " $iwad_folder " ) "
351+ wolf3d_data_version_pretty=" $( pretty_wolf3d_version " $version " ) "
352+ log i " Auto-detected IWAD '$iwad_folder ' ($wolf3d_data_version_pretty )"
353+ launch_folder=" $iwad_folder "
354+ fi
355+ fi
356+ else
357+ log i " Input path '$input_path ' not a file or directory; auto-detecting IWAD in $roms_path /wolf"
358+ iwad_folder=" $( find_wolf3d_wolf_folder " $roms_path /wolf" ) " || {
359+ log e " No valid Wolf3D data folder found under $roms_path /wolf"
360+ exit 1
361+ }
362+ version=" $( detect_wolf3d_version " $iwad_folder " ) "
363+ wolf3d_data_version_pretty=" $( pretty_wolf3d_version " $version " ) "
364+ log i " Auto-detected IWAD '$iwad_folder ' ($wolf3d_data_version_pretty )"
365+ launch_folder=" $iwad_folder "
366+ fi
367+
368+ # If we didn't set launch_folder yet (e.g. mod path without explicit file entries), use iwad_folder
369+ launch_folder=" ${launch_folder:- $iwad_folder } "
370+
371+ if [[ -z " $launch_folder " || ! -d " $launch_folder " ]]; then
372+ log e " Nessuna cartella di avvio valida trovata"
373+ exit 1
374+ fi
375+
376+ # Always use requested base options and preserved args.
377+ args=( " ${raw_args[@]} " )
378+
379+ if [[ ${# requested_mod_files[@]} -gt 0 ]]; then
380+ args+=( " ${requested_mod_files[@]} " )
381+ fi
382+
383+ if [[ -n " $main_game " ]]; then
384+ args+=( " $main_game " )
385+ fi
386+
387+ # Log command line internal representation
388+ if [[ ${# args[@]} -gt 0 ]]; then
389+ log i " With args: ${args[*]} "
390+ fi
391+
392+ # Final command
393+ log d " Executing: \" $component_path /bin/ecwolf\" --fullscreen --nowait --config /var/config/ecwolf/ecwolf_rd.cfg --savedir /var/data/ecwolf/saves ${args[*]} "
394+
395+ cd " $launch_folder " || exit 1
396+ log i " Running from $launch_folder "
397+ exec " $component_path /bin/ecwolf" --fullscreen --nowait --config /var/config/ecwolf/ecwolf_rd.cfg --savedir /var/data/ecwolf/saves " ${args[@]} "
398+ cd -
0 commit comments