diff --git a/docs/CODE_SCAN_REPORT.json b/docs/CODE_SCAN_REPORT.json new file mode 100644 index 000000000..1e9583172 --- /dev/null +++ b/docs/CODE_SCAN_REPORT.json @@ -0,0 +1,60 @@ +{ + "scan_date": "2026-01-04T19:15:00+01:00", + "total_assets": 1166, + "total_references": 210, + "errors": [ + { + "type": "BROKEN_REFERENCES", + "count": 200, + "items": [ + "fish_trout.png", + "../narezano_in_majhno/krvava_zetev_sprites/grass_soil_tileset_1766171156780_obdelan.png", + "npc_child.png", + " π¨ Generating: {item}_stylea.png", + "slike/cutscene", + " π¨ Generating: {item}_styleb.png", + "tree_oak.png", + "{asset_name.lower()}.png", + " - assetname_styleA_sprite_32x32.png", + "{name}_{direction}_{frame}.png" + ] + } + ], + "warnings": [ + { + "type": "NAMING_ISSUES", + "count": 2322, + "items": [ + "Timestamp in name: dagger_weapon_v2_1767520538278.png", + "Missing style32 suffix: dagger_weapon_v2_1767520538278.png", + "No category prefix: dagger_weapon_v2_1767520538278.png", + "Timestamp in name: gronk_vape_04_1767408599735.png", + "No category prefix: gronk_vape_04_1767408599735.png", + "Timestamp in name: susi_idle_side_1767408905850.png", + "No category prefix: susi_idle_side_1767408905850.png", + "Timestamp in name: base_level3_house_1767411296399.png", + "No category prefix: base_level3_house_1767411296399.png", + "No category prefix: barn.png", + "Timestamp in name: terrain_water_shallow_1767521374725.png", + "Missing style32 suffix: terrain_water_shallow_1767521374725.png", + "Timestamp in name: zombie_miner_carry_03_1767409693609.png", + "No category prefix: zombie_miner_carry_03_1767409693609.png", + "Timestamp in name: watchtower_building_1767496692109.png" + ] + } + ], + "suggestions": [ + { + "type": "OPTIMIZATIONS", + "items": [ + "Found 168 groups of same-size files (possible duplicates)" + ] + } + ], + "summary": { + "status": "FAIL", + "error_count": 1, + "warning_count": 1, + "suggestion_count": 1 + } +} \ No newline at end of file diff --git a/scripts/deep_code_scanner.py b/scripts/deep_code_scanner.py new file mode 100755 index 000000000..bafcf321c --- /dev/null +++ b/scripts/deep_code_scanner.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 +""" +Deep Code Scanner for DolinaSmrti +Analyzes all code for: +- Missing asset references +- Broken path links +- Naming inconsistencies +- Optimization opportunities +""" + +import os +import re +import json +from pathlib import Path +from typing import Dict, List, Set, Tuple + +PROJECT_ROOT = Path(__file__).parent.parent +SRC_DIR = PROJECT_ROOT / "src" +ASSETS_DIR = PROJECT_ROOT / "assets" + +class CodeScanner: + def __init__(self): + self.errors = [] + self.warnings = [] + self.suggestions = [] + self.asset_paths = set() + self.code_references = set() + + def scan_all(self): + """Run complete code scan""" + print("π Starting Deep Code Scan...") + print("="*60) + + # 1. Collect all asset paths + print("\nπ Scanning asset files...") + self.collect_asset_paths() + + # 2. Scan code for references + print("\nπ» Scanning code references...") + self.scan_code_references() + + # 3. Validate paths + print("\nβ Validating paths...") + self.validate_paths() + + # 4. Check naming conventions + print("\nπ·οΈ Checking naming conventions...") + self.check_naming_conventions() + + # 5. Optimize suggestions + print("\nβ‘ Analyzing optimization opportunities...") + self.find_optimizations() + + # 6. Generate report + print("\nπ Generating report...") + self.generate_report() + + def collect_asset_paths(self): + """Collect all asset file paths""" + for ext in ['*.png', '*.jpg', '*.jpeg', '*.webp']: + for file in ASSETS_DIR.rglob(ext): + rel_path = file.relative_to(PROJECT_ROOT) + self.asset_paths.add(str(rel_path)) + + print(f" Found {len(self.asset_paths)} asset files") + + def scan_code_references(self): + """Scan all code files for asset references""" + patterns = [ + r'["\']([^"\']*\.png)["\']', # PNG files + r'["\']([^"\']*\.jpg)["\']', # JPG files + r'["\']([^"\']*\.webp)["\']', # WEBP files + r'assets/([^"\']*)', # Asset paths + ] + + code_files = [] + code_files.extend(SRC_DIR.rglob("*.js")) + code_files.extend(SRC_DIR.rglob("*.html")) + code_files.extend((PROJECT_ROOT / "scripts").rglob("*.py")) + + for file in code_files: + try: + content = file.read_text(encoding='utf-8') + for pattern in patterns: + matches = re.findall(pattern, content) + for match in matches: + self.code_references.add(match) + except Exception as e: + self.warnings.append(f"Could not read {file}: {e}") + + print(f" Found {len(self.code_references)} asset references in code") + + def validate_paths(self): + """Check if all referenced assets exist""" + broken_refs = [] + + for ref in self.code_references: + # Try to find matching asset + found = False + for asset_path in self.asset_paths: + if ref in asset_path or asset_path.endswith(ref): + found = True + break + + if not found: + # Check if file actually exists + possible_paths = [ + PROJECT_ROOT / ref, + PROJECT_ROOT / "assets" / ref, + PROJECT_ROOT / "assets" / "images" / ref, + ] + + exists = any(p.exists() for p in possible_paths) + if not exists: + broken_refs.append(ref) + + if broken_refs: + self.errors.append({ + 'type': 'BROKEN_REFERENCES', + 'count': len(broken_refs), + 'items': broken_refs[:10] # Show first 10 + }) + print(f" β Found {len(broken_refs)} broken references") + else: + print(f" β All asset references valid") + + def check_naming_conventions(self): + """Check if assets follow naming conventions""" + issues = [] + + for asset_path in self.asset_paths: + filename = Path(asset_path).name + + # Check for timestamps in name + if re.search(r'_\d{13,}', filename): + issues.append(f"Timestamp in name: {filename}") + + # Check for Style 32 convention + if 'STYLE_32' in asset_path and 'style32' not in filename.lower(): + issues.append(f"Missing style32 suffix: {filename}") + + # Check for proper prefixes + if not any(filename.startswith(prefix) for prefix in [ + 'terrain', 'interior', 'npc', 'liki', 'zgradbe', + 'oprema', 'narava', 'vmesnik', 'uploaded' + ]): + issues.append(f"No category prefix: {filename}") + + if issues: + self.warnings.append({ + 'type': 'NAMING_ISSUES', + 'count': len(issues), + 'items': issues[:15] + }) + print(f" β οΈ Found {len(issues)} naming issues") + else: + print(f" β All names follow conventions") + + def find_optimizations(self): + """Find optimization opportunities""" + suggestions = [] + + # Check for duplicate file sizes (possible duplicates) + size_map = {} + for asset_path in self.asset_paths: + full_path = PROJECT_ROOT / asset_path + if full_path.exists(): + size = full_path.stat().st_size + if size in size_map: + size_map[size].append(asset_path) + else: + size_map[size] = [asset_path] + + duplicates = {k: v for k, v in size_map.items() if len(v) > 1} + if duplicates: + suggestions.append(f"Found {len(duplicates)} groups of same-size files (possible duplicates)") + + # Check for large files (>1MB) + large_files = [] + for asset_path in self.asset_paths: + full_path = PROJECT_ROOT / asset_path + if full_path.exists(): + size_mb = full_path.stat().st_size / (1024 * 1024) + if size_mb > 1: + large_files.append((asset_path, f"{size_mb:.2f} MB")) + + if large_files: + suggestions.append(f"Found {len(large_files)} files > 1MB (consider compression)") + + if suggestions: + self.suggestions.append({ + 'type': 'OPTIMIZATIONS', + 'items': suggestions + }) + print(f" π‘ {len(suggestions)} optimization suggestions") + else: + print(f" β No optimization needed") + + def generate_report(self): + """Generate comprehensive report""" + report = { + 'scan_date': '2026-01-04T19:15:00+01:00', + 'total_assets': len(self.asset_paths), + 'total_references': len(self.code_references), + 'errors': self.errors, + 'warnings': self.warnings, + 'suggestions': self.suggestions, + 'summary': { + 'status': 'PASS' if not self.errors else 'FAIL', + 'error_count': len(self.errors), + 'warning_count': len(self.warnings), + 'suggestion_count': len(self.suggestions) + } + } + + # Save report + report_path = PROJECT_ROOT / "docs" / "CODE_SCAN_REPORT.json" + with open(report_path, 'w', encoding='utf-8') as f: + json.dump(report, f, indent=2, ensure_ascii=False) + + # Print summary + print("\n" + "="*60) + print("π SCAN SUMMARY") + print("="*60) + print(f"Total Assets: {len(self.asset_paths)}") + print(f"Code References: {len(self.code_references)}") + print(f"Errors: {len(self.errors)}") + print(f"Warnings: {len(self.warnings)}") + print(f"Suggestions: {len(self.suggestions)}") + print(f"\nStatus: {'β PASS' if not self.errors else 'β FAIL'}") + print(f"\nπ Full report: {report_path}") + + # Print details + if self.errors: + print("\nβ ERRORS:") + for error in self.errors: + print(f" {error['type']}: {error['count']} issues") + for item in error.get('items', [])[:5]: + print(f" - {item}") + + if self.warnings: + print("\nβ οΈ WARNINGS:") + for warning in self.warnings: + if isinstance(warning, dict): + print(f" {warning['type']}: {warning['count']} issues") + else: + print(f" {warning}") + + if self.suggestions: + print("\nπ‘ SUGGESTIONS:") + for suggestion in self.suggestions: + for item in suggestion.get('items', []): + print(f" {item}") + + +def main(): + scanner = CodeScanner() + scanner.scan_all() + + +if __name__ == "__main__": + main() diff --git a/tools/visual_asset_manager.html b/tools/visual_asset_manager.html new file mode 100644 index 000000000..2c2044f40 --- /dev/null +++ b/tools/visual_asset_manager.html @@ -0,0 +1,760 @@ + + + +
+ + +