check_missing_media.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import os
  2. import re
  3. from collections import defaultdict
  4. # made by Awesome
  5. # Get the main directory path where the tools folder is located
  6. main_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
  7. qsrc_dir = os.path.join(main_dir, "locations")
  8. text_path = main_dir
  9. image_directory = input('Enter the game directory: ')
  10. custom_keywords = input('Enter custom keywords to exclude (separated by comma), or press Enter to skip: ')
  11. # Ensure image_directory ends with a '/'
  12. if not image_directory.endswith('/'):
  13. image_directory += '/'
  14. # Check if the user entered the images folder path instead of the game directory
  15. if image_directory.lower().endswith('images/'):
  16. image_directory = os.path.dirname(os.path.dirname(image_directory))
  17. # Remove the 'images' subfolder from image_directory only if it's there
  18. if os.path.basename(image_directory.rstrip('/')).lower() == 'images':
  19. image_directory = os.path.dirname(image_directory.rstrip('/'))
  20. # Define keywords to exclude from media paths
  21. keywords_to_exclude = ['$pcs_haircol', 'FUNC(', '<<pirs', '$ARGS', '+iif']
  22. if custom_keywords:
  23. custom_keywords = custom_keywords.split(',')
  24. keywords_to_exclude += [keyword.strip() for keyword in custom_keywords]
  25. ignore_file = os.path.join(main_dir, 'ignoremedialinks.txt')
  26. ignore_paths = []
  27. # Check if the ignore file exists
  28. if os.path.exists(ignore_file):
  29. with open(ignore_file, 'r') as f:
  30. ignore_paths = [line.strip() for line in f]
  31. # Create a list to store missing media (images and videos)
  32. missing_media = []
  33. # Function to evaluate and replace random expressions while preserving existing single quotes or '+' operators
  34. def evaluate_and_replace_random(expression):
  35. media_paths = []
  36. # Define a regular expression pattern to find expressions like ' + rand(...) + ' or "'+rand(...)+'" or '<<rand(...)>>'
  37. rand_pattern = r'(["\']?)\s*([\+\-]?)\s*(<<)?rand\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)(>>)?\s*([\+\-]?)\s*(["\']?)'
  38. # Find all matching expressions in the input string
  39. matches = re.finditer(rand_pattern, expression)
  40. # Initialize the modified expression
  41. modified_expression = expression
  42. # Iterate through the matches and replace them
  43. for match in matches:
  44. start_quote, start_operator, _, min_value, max_value, _, end_operator, end_quote = match.groups()
  45. min_value = int(min_value)
  46. max_value = int(max_value)
  47. if min_value <= max_value:
  48. # Generate media paths for each number in the range
  49. for num in range(min_value, max_value + 1):
  50. # Create a copy of the modified expression
  51. temp_expression = modified_expression
  52. # Replace the 'rand' expression with the current number
  53. replacement = (start_operator if start_operator else '') + str(num) + (end_operator if end_operator else '')
  54. if start_quote and start_quote == end_quote:
  55. replacement = replacement.strip("'")
  56. replacement = replacement.strip("+")
  57. temp_expression = temp_expression[:match.start()] + replacement + temp_expression[match.end():]
  58. media_paths.append(temp_expression)
  59. # If no random evaluations were found, return the original expression
  60. if not media_paths:
  61. media_paths = [modified_expression]
  62. return media_paths
  63. #
  64. def evaluate_and_replace_variables(expression, variables):
  65. variable_matches = re.findall(r'\'\s*\+\s*(\$?[a-zA-Z_][a-zA-Z0-9_]*)\s*\+\s*\'|<<(\$?[a-zA-Z_][a-zA-Z0-9_]*)>>', expression)
  66. for variable_match in variable_matches:
  67. for variable_name in variable_match:
  68. variable_value = None # Initialize variable_value to None
  69. if variable_name: # Check if the group actually matched something
  70. if variable_name.startswith("$"):
  71. variable_value = variables.get(variable_name[1:], None)
  72. else:
  73. variable_value = variables.get(variable_name, None)
  74. if isinstance(variable_value, str) and 'rand(' in variable_value:
  75. # Evaluate the 'rand' function
  76. variable_value = evaluate_and_replace_random(variable_value)[0]
  77. if variable_value is None:
  78. return None
  79. expression = expression.replace(f"<<{variable_name}>>", str(variable_value))
  80. return [expression]
  81. # Initialize an empty dictionary to store variable values
  82. variables = {}
  83. # Iterate through .qsrc files in the specified directory and its subdirectories
  84. for file in os.listdir(qsrc_dir):
  85. if file.endswith(".qsrc"):
  86. qsrc_file = os.path.join(qsrc_dir, file)
  87. # Read the .qsrc file
  88. with open(qsrc_file, "r", encoding="utf-8", errors="ignore") as script_file:
  89. script_content = script_file.readlines()
  90. # Initialize a dictionary to store the 'rand' variables and their expressions
  91. rand_variables = {}
  92. # Define a regular expression pattern to find 'rand' variable assignments
  93. rand_assignment_pattern = r'(\$?[a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(rand\(\d+,\s*\d+\))'
  94. # Iterate through the script content to find and store 'rand' variable assignments
  95. for line_number, line in enumerate(script_content, start=1):
  96. matches = re.findall(rand_assignment_pattern, line)
  97. for match in matches:
  98. variable_name, rand_expression = match
  99. rand_variables[variable_name.strip()] = rand_expression.strip()
  100. # Iterate through each line in the .qsrc file
  101. for line_number, line in enumerate(script_content, start=1):
  102. # Check if the line contains a variable assignment
  103. if "=" in line and "<<" in line and ">>" in line:
  104. # Find all matches of the regular expression in the line
  105. matches = re.findall(r'<<(\$?[a-zA-Z_][a-zAZ0-9_]*)>>\s*=\s*([\'"]?[^\'"]+[\'"]?)', line)
  106. # Check if there are any matches
  107. if matches:
  108. # Split the line into a variable name and value
  109. variable_name, variable_value = matches[0]
  110. # Check if the variable value contains a 'rand' function
  111. if "rand(" in variable_value:
  112. # Evaluate the 'rand' function
  113. variable_value = evaluate_and_replace_random(variable_value)[0]
  114. # Store the variable value in the dictionary with the filename and line number as the key
  115. variables[(qsrc_file, line_number)] = {variable_name.strip(): variable_value.strip("'\"")}
  116. # Use regular expressions to find media references in the script
  117. if line.strip().startswith('!') or '<<FUNC' in line:
  118. continue
  119. media_references = re.findall(r'<(?:img.*?|video.*?)src="([^"]+)"', line)
  120. for media_reference in media_references:
  121. if any(keyword in media_reference for keyword in keywords_to_exclude):
  122. continue
  123. # Special rules
  124. media_reference = media_reference.replace("lover_picrand", 'rand(1, 30)')
  125. media_reference = media_reference.replace("modelfoto[''debut_image'']+1", 'rand(1, 9)')
  126. media_reference = media_reference.replace("modelfoto[''debut_image'']+2", 'rand(1, 9)')
  127. media_reference = media_reference.replace("modelfoto[''debut_image'']", 'rand(1, 9)')
  128. media_reference = media_reference.replace("VKWoods", 'rand(1, 8)')
  129. media_reference = media_reference.replace("metrorand['rand']", 'rand(1, 2)')
  130. media_reference = media_reference.replace("picpRand", 'rand(1, 20)')
  131. media_reference = media_reference.replace("Doublerand", 'rand(14, 15)')
  132. # Find the variable assignment that is closest to (but above) the media reference
  133. relevant_variables = max(((k, v) for k, v in variables.items() if k[0] == qsrc_file and k[1] < line_number), key=lambda item: item[0][1], default=({}, {}))[1]
  134. # Evaluate variables first
  135. media_reference = evaluate_and_replace_variables(media_reference, relevant_variables)
  136. if media_reference is None:
  137. continue
  138. media_reference = media_reference[0]
  139. # Use regular expression to identify and replace variables set with rand in the image path
  140. variable_references = re.findall(r'<<(\$?[a-zA-Z_][a-zAZ0-9_]*)>>', media_reference)
  141. for variable_reference in variable_references:
  142. if variable_reference in rand_variables:
  143. rand_expression = rand_variables[variable_reference]
  144. media_reference = media_reference.replace(f"<<{variable_reference}>>", str(eval(rand_expression)))
  145. media_paths = evaluate_and_replace_random(media_reference)
  146. for path in media_paths:
  147. if any(keyword in path for keyword in keywords_to_exclude) or path in ignore_paths:
  148. continue
  149. absolute_path = os.path.join(image_directory, path)
  150. # Check if the path is a directory
  151. if os.path.isdir(absolute_path) and not os.listdir(absolute_path):
  152. missing_media.append((os.path.basename(qsrc_file), path, line_number))
  153. elif not os.path.exists(absolute_path):
  154. missing_media.append((os.path.basename(qsrc_file), path, line_number))
  155. # Specify the path to the "missing_media.txt" output file
  156. missing_media_file = os.path.join(text_path, "missing_media.txt")
  157. # Convert the list to a set to remove duplicates, then convert it back to a list
  158. missing_media = [i for n, i in enumerate(missing_media) if i not in missing_media[n + 1:]]
  159. # Create a dictionary to store the file name, media path and line numbers
  160. missing_media_dict = defaultdict(list)
  161. for qsrc_file, media_path, line_number in missing_media:
  162. # Use the file name and media path as the key, and append the line number to the list of values
  163. missing_media_dict[(qsrc_file, media_path)].append(line_number)
  164. # Write the dictionary to the "missing_media.txt" file
  165. with open(missing_media_file, "w", encoding="utf-8") as file:
  166. for (qsrc_file, media_path), line_numbers in missing_media_dict.items():
  167. # Join the line numbers with commas
  168. line_numbers_str = ", ".join(map(str, line_numbers))
  169. file.write(f"From file: {qsrc_file}, lines: {line_numbers_str}\n") # Write the .qsrc filename and line numbers
  170. file.write(f"{media_path}\n\n") # Write the full media path and start a new line
  171. file.write(f"\nTotal missing media files: {len(missing_media_dict)}\n") # Write the count of missing media files at the end of the file
  172. file.write(f"this amazing script was made by Awesome (with help from chatGPT)\n") # Bragging
  173. print(f"{len(missing_media_dict)} Missing media files have been saved to 'missing_media.txt'.")
  174. pause = input("Press Enter to exit.")