check_missing_media.py 11 KB

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