param( [string[]]$ScanPaths = @( "Unity/Assets/BundleResources/DataAssets", "Unity/Assets/BundleResources/Export" ) ) $ErrorActionPreference = "Stop" $repoRoot = git rev-parse --show-toplevel 2>$null if (-not $repoRoot) { throw "Not inside a git repository." } $repoRoot = [System.IO.Path]::GetFullPath($repoRoot.Trim()) function Resolve-RepoPath([string]$Path) { if ([System.IO.Path]::IsPathRooted($Path)) { return [System.IO.Path]::GetFullPath($Path) } return [System.IO.Path]::GetFullPath((Join-Path $repoRoot $Path)) } $metaGuids = @{} $assetsPath = Resolve-RepoPath "Unity/Assets" Get-ChildItem -LiteralPath $assetsPath -Filter "*.meta" -Recurse -File | ForEach-Object { $metaText = [System.IO.File]::ReadAllText($_.FullName, [System.Text.Encoding]::UTF8) if ($metaText -match '(?m)^guid: ([0-9a-f]{32})') { $metaGuids[$Matches[1]] = $_.FullName } } $issues = New-Object System.Collections.Generic.List[string] $assetRegex = [regex]'\{fileID: ([^,}]+), guid: ([0-9a-f]{32}), type: ([0-9]+)\}' foreach ($scanPath in $ScanPaths) { $fullScanPath = Resolve-RepoPath $scanPath if (-not (Test-Path -LiteralPath $fullScanPath)) { throw "Scan path not found: $fullScanPath" } Get-ChildItem -LiteralPath $fullScanPath -Filter "*.asset" -Recurse -File | ForEach-Object { $relativePath = $_.FullName if ($relativePath.StartsWith($repoRoot, [System.StringComparison]::OrdinalIgnoreCase)) { $relativePath = $relativePath.Substring($repoRoot.Length).TrimStart('\', '/') } $lines = [System.IO.File]::ReadAllLines($_.FullName, [System.Text.Encoding]::UTF8) for ($i = 0; $i -lt $lines.Count; $i++) { foreach ($match in $assetRegex.Matches($lines[$i])) { $fileId = $match.Groups[1].Value $guid = $match.Groups[2].Value if ($fileId -eq "0") { continue } if (-not $metaGuids.ContainsKey($guid)) { $issues.Add("${relativePath}:$($i + 1) references missing Unity guid $guid") } } } } } if ($issues.Count -gt 0) { throw "Missing Unity asset references:`n$($issues -join "`n")" } Write-Host "Unity asset references OK: scanned $($ScanPaths -join ', ')."