1#! /usr/bin/env python
2
3import fnmatch
4import os
5import sys
6import re
7
8
9###############
10## functions ##
11###############
12
13
14# converts a sound path to a name string
15def sound_path_to_name(sound_path):
16	sound_name = os.path.splitext(os.path.basename(sound_path))[0]
17	dir_name = os.path.dirname(sound_path)
18	# sound is inside an object definition: return DEF::NAME
19	if fnmatch.fnmatch(dir_name, '*.ocd'):
20		return get_namespace_ocd(dir_name) + sound_name
21	# sound is inside a .ocg folder: return FOLDER::NAME
22	if fnmatch.fnmatch(dir_name, '*.ocg'):
23		return get_namespace_ocg(dir_name) + sound_name
24	# sound is inside a .ocs or .ocs folder: return NAME
25	if fnmatch.fnmatch(dir_name, '*.ocs') or fnmatch.fnmatch(dir_name, '*.ocf'):
26		return sound_name
27	# return a warning, this should not happen
28	return "Warning: sound found in illegal location (i.e. not in scenario, scenario folder, Sound.ocg folder or subfolerd, object definition."
29
30# gets the sound namespace of object definition
31def get_namespace_ocd(ocd_path):
32	if not os.path.isfile(os.path.join(ocd_path, "DefCore.txt")):
33		return "NOTFOUND::"
34	with open(os.path.join(ocd_path, "DefCore.txt"), "r") as read_defcore:
35		for line in read_defcore:
36			if "id=" in line:
37				return str(line).split('=')[-1].replace('\n', '') + "::"
38	return "NOTFOUND::"
39
40# gets the sound namespace of an ocg folder
41def get_namespace_ocg(ocg_path):
42	dirname = ocg_path
43	namespace = ""
44	while not fnmatch.fnmatch(dirname, '*Sound.ocg') and not fnmatch.fnmatch(dirname, '*Music.ocg'):
45		basename = os.path.basename(dirname)
46		dirname = os.path.dirname(dirname)
47		namespace = os.path.splitext(basename)[0] + "::" + namespace
48	return namespace
49
50# finds all sound calls in a script file
51def find_sound_calls_script(script_file):
52	with open(script_file, "r") as read_script:
53		script = read_script.read()
54	# regular call: Sound("FooBar")
55	calls = re.findall('Sound\(\s*.*\)', script)
56	calls = (str(re.sub('Sound\(\s*"', '', call).split('"')[0]) for call in calls)
57	# regular call: SoundAt("FooBar")
58	callsat = re.findall('SoundAt\(\s*.*\)', script)
59	callsat = (str(re.sub('SoundAt\(\s*"', '', call).split('"')[0]) for call in callsat)
60	# ActMap property: Sound = "FooBar"
61	actprops = re.findall('Sound\s*=\s*.*\n', script)
62	actprops = (str(re.sub('Sound\s*=\s*"', '', prop).split('"')[0]) for prop in actprops)
63	all_calls = []
64	for call in calls:
65		all_calls.append(call)
66	for call in callsat:
67		all_calls.append(call)
68	for prop in actprops:
69		all_calls.append(prop)
70	return all_calls
71
72# finds all sound calls in a source file
73def find_sound_calls_source(source_file):
74	with open(source_file, "r") as read_source:
75		source = read_source.read()
76	calls = re.findall('GUISound\(.*\)|StartSoundEffectAt\(.*\)|StartSoundEffect\(.*\)', source)
77	calls = (call.replace('GUISound("', '').replace('StartSoundEffectAt("', '').replace('StartSoundEffect("', '').split('"')[0] for call in calls)
78	return calls
79
80
81#############
82## program ##
83#############
84
85
86# program arguments: planet directory and source directory
87planet_dir = "../planet"
88source_dir = "../src"
89if len(sys.argv) >= 2:
90	planet_dir = sys.argv[1]
91if len(sys.argv) >= 3:
92	source_dir = sys.argv[2]
93
94# find all sound and script files in planet
95sound_files = []
96script_files = []
97for root, directories, filenames in os.walk(planet_dir):
98	for filename in filenames:
99		full_filename = os.path.join(root, filename)
100		# get the sound files
101		if fnmatch.fnmatch(full_filename, '*.ogg') or fnmatch.fnmatch(full_filename, '*.wav'):
102			sound_files.append(full_filename)
103		# get the script files
104		if fnmatch.fnmatch(full_filename, '*.c'):
105			script_files.append(full_filename)
106
107# remove music files from sound files
108sound_files[:] = (sound_path for sound_path in sound_files if not fnmatch.fnmatch(sound_path, '*Music.ocg*'))
109
110# find all engine files in source
111source_files = []
112for root, directories, filenames in os.walk(source_dir):
113	for filename in filenames:
114		full_filename = os.path.join(root, filename)
115		# get the source files
116		if fnmatch.fnmatch(full_filename, '*.cpp') or fnmatch.fnmatch(full_filename, '*.h'):
117			source_files.append(full_filename)
118
119
120# get all sound calls from the script and the source files
121sound_calls_script = []
122for script in script_files:
123	for call in find_sound_calls_script(script):
124		sound_calls_script.append(call)
125sound_calls_source = []
126for source in source_files:
127	for call in find_sound_calls_source(source):
128		sound_calls_source.append(call)
129
130# check for all found sound files if there is an occurence in the script
131print "Looking for unused sounds in the planet directory: \n\t", planet_dir
132print "WARNING: always double check if the sound really is not played by searching for the sound name in all engine and script files."
133print "\nChecking", len(sound_files), "sound files ..."
134cnt_unused_sounds = 0
135for sound_path in sound_files:
136	sound = sound_path_to_name(sound_path)
137	found = False
138	for call in sound_calls_script + sound_calls_source:
139		if fnmatch.fnmatch(sound, call):
140			found = True
141			break
142	if not found:
143		cnt_unused_sounds += 1
144		print sound_path + " (" + sound + ")" + " has not been found"
145print "Out of the", len(sound_files), "found sound files, the above", cnt_unused_sounds, "may be unused."
146
147
148
149# script for finding non-normal calls to sound only turn on if needed
150if False:
151	for script_file in script_files:
152		with open(script_file, "r") as read_script:
153			script = read_script.read()
154		calls = re.findall('Sound\(\s*.*\)', script)
155		for call in calls:
156			if re.search('Sound\(\s*"', call) is None:
157				print call
158		calls = re.findall('SoundAt\(\s*.*\)', script)
159		for call in calls:
160			if re.search('SoundAt\(\s*"', call) is None:
161				print call
162		calls = re.findall('Sound\s*=\s*.*', script)
163		for call in calls:
164			if re.search('Sound\s*=\s*".*".*', call) is None:
165				print call
166
167