package topdata import ( "os" "path/filepath" "strings" ) func importLegacyVFXPersistent(referenceBuilderDir, dataDir string, legacyTLK *legacyTLKData) (int, error) { _ = legacyTLK if strings.TrimSpace(referenceBuilderDir) == "" { return 0, nil } legacyDir := filepath.Join(referenceBuilderDir, "data", "vfx_persistent") if _, err := os.Stat(legacyDir); err != nil { if os.IsNotExist(err) { return 0, nil } return 0, err } targetDir := filepath.Join(dataDir, "vfx_persistent") targetBasePath := filepath.Join(targetDir, "base.json") targetLockPath := filepath.Join(targetDir, "lock.json") targetModulesDir := filepath.Join(targetDir, "modules") targetModulePath := filepath.Join(targetModulesDir, "freeformaoes.json") if fileExists(targetBasePath) && fileExists(targetLockPath) && fileExists(targetModulePath) { baseObj, baseErr := loadJSONObject(targetBasePath) lockObj, lockErr := loadJSONObject(targetLockPath) if baseErr == nil && lockErr == nil && vfxPersistentImportComplete(baseObj, lockObj) { legacyModulePaths, err := collectJSONRelativePaths(filepath.Join(legacyDir, "modules")) if err == nil { targetModulePaths, err := collectJSONRelativePaths(targetModulesDir) if err == nil && equalStringSlices(legacyModulePaths, targetModulePaths) { return 0, nil } } } } baseObj, err := loadJSONObject(filepath.Join(legacyDir, "base.json")) if err != nil { return 0, err } lockObj, err := synthesizeVFXPersistentKeys(baseObj) if err != nil { return 0, err } legacyLockObj, err := loadJSONObject(filepath.Join(legacyDir, "lock.json")) if err != nil { return 0, err } for key, value := range legacyLockObj { lockObj[key] = value } if err := os.MkdirAll(targetModulesDir, 0o755); err != nil { return 0, err } updated := 0 for _, write := range []struct { path string obj map[string]any }{ {path: targetBasePath, obj: baseObj}, {path: targetLockPath, obj: lockObj}, } { changed, err := writeJSONObjectIfChanged(write.path, write.obj) if err != nil { return 0, err } if changed { updated++ } } moduleRelPaths, err := collectJSONRelativePaths(filepath.Join(legacyDir, "modules")) if err != nil { return 0, err } for _, relPath := range moduleRelPaths { moduleObj, err := loadJSONObject(filepath.Join(legacyDir, "modules", filepath.FromSlash(relPath))) if err != nil { return 0, err } targetPath := filepath.Join(targetModulesDir, filepath.FromSlash(relPath)) if err := os.MkdirAll(filepath.Dir(targetPath), 0o755); err != nil { return 0, err } changed, err := writeJSONObjectIfChanged(targetPath, moduleObj) if err != nil { return 0, err } if changed { updated++ } } targetModulePaths, err := collectJSONRelativePaths(targetModulesDir) if err != nil { return 0, err } legacyModules := make(map[string]struct{}, len(moduleRelPaths)) for _, relPath := range moduleRelPaths { legacyModules[relPath] = struct{}{} } for _, relPath := range targetModulePaths { if _, ok := legacyModules[relPath]; ok { continue } if err := os.Remove(filepath.Join(targetModulesDir, filepath.FromSlash(relPath))); err != nil && !os.IsNotExist(err) { return 0, err } updated++ } return updated, nil } func vfxPersistentImportComplete(baseObj, lockObj map[string]any) bool { if !vfxPersistentRowsHaveKeys(baseObj) { return false } expected := map[string]int{ "vfx_persistent:blankradius5": 47, "vfx_persistent:blankradius10": 48, "vfx_persistent:blankradius15": 49, "vfx_persistent:blankradius20": 50, "vfx_persistent:blankradius25": 51, "vfx_persistent:blankradius30": 52, } for key, want := range expected { raw, ok := lockObj[key] if !ok { return false } got, err := asInt(raw) if err != nil || got != want { return false } } return true } func synthesizeVFXPersistentKeys(baseObj map[string]any) (map[string]any, error) { rawRows, ok := baseObj["rows"].([]any) if !ok { return map[string]any{}, nil } lockObj := map[string]any{} for _, raw := range rawRows { row, ok := raw.(map[string]any) if !ok { continue } label, _ := row["LABEL"].(string) if label == "" { continue } key := "vfx_persistent:" + strings.ToLower(label) row["key"] = key if rawID, ok := row["id"]; ok { lockObj[key] = rawID } } return lockObj, nil } func vfxPersistentRowsHaveKeys(baseObj map[string]any) bool { rawRows, ok := baseObj["rows"].([]any) if !ok { return false } for _, raw := range rawRows { row, ok := raw.(map[string]any) if !ok { return false } key, _ := row["key"].(string) if key == "" { return false } } return true } func equalStringSlices(left, right []string) bool { if len(left) != len(right) { return false } for i := range left { if left[i] != right[i] { return false } } return true }