Skip to content

Commit d2af699

Browse files
authored
Merge pull request WolvenKit#2666 from WolvenKit/feature/generate-item-codes
feature: generate item codes
2 parents 521e908 + 5c93222 commit d2af699

File tree

18 files changed

+280
-61
lines changed

18 files changed

+280
-61
lines changed

WolvenKit.App/Enums.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,12 @@ public enum PhotomodeBodyGender
6363

6464
[Display(Description = "Cat (Nibbles)")]
6565
Cat
66-
}
66+
}
67+
68+
public enum ProjectFolder
69+
{
70+
All,
71+
Archive,
72+
Raw,
73+
Resources
74+
}

WolvenKit.App/Helpers/ProjectResourceTools.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,25 @@ public string GetAbsolutePath(string fileName, string rootRelativeFolder = "", b
110110
return "";
111111
}
112112

113-
if (appendModderNameFromSettings)
113+
if (rootRelativeFolder == "" &&
114+
activeProject.Files.FirstOrDefault(f => f.EndsWith(fileName)) is string filePath)
115+
{
116+
return Path.Join(activeProject.FileDirectory, filePath);
117+
}
118+
119+
// Deal with file paths
120+
if (fileName.Contains(Path.DirectorySeparatorChar))
121+
{
122+
var absoluteFilePath = Path.Join(activeProject.FileDirectory, fileName);
123+
if (File.Exists(absoluteFilePath))
124+
{
125+
return absoluteFilePath;
126+
}
127+
128+
fileName = fileName[(fileName.LastIndexOf(Path.DirectorySeparatorChar) + 1)..];
129+
}
130+
131+
if (appendModderNameFromSettings && rootRelativeFolder == "")
114132
{
115133
rootRelativeFolder = AppendPersonalDirectory(rootRelativeFolder);
116134
}
@@ -1053,4 +1071,27 @@ public void InitializeArchiveManager()
10531071
_loggerService.Info("Scanning your mods... this can take a moment. Wolvenkit will be unresponsive.");
10541072
_archiveManager.Initialize(new FileInfo(_settingsService.CP77ExecutablePath));
10551073
}
1074+
1075+
public List<string> GetProjectFiles(string fileExtension, ProjectFolder folder)
1076+
{
1077+
if (_projectManager.ActiveProject is not { } project)
1078+
{
1079+
return [];
1080+
}
1081+
1082+
var projectFiles = folder switch
1083+
{
1084+
ProjectFolder.All => project.Files,
1085+
ProjectFolder.Archive => project.ModFiles,
1086+
ProjectFolder.Raw => project.RawFiles,
1087+
ProjectFolder.Resources => project.ResourceFiles,
1088+
_ => [],
1089+
};
1090+
1091+
return projectFiles
1092+
.Where(f => f.HasFileExtension(fileExtension))
1093+
.ToList();
1094+
}
1095+
1096+
10561097
}

WolvenKit.App/Helpers/YamlHelper.cs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Dynamic;
24
using System.IO;
5+
using System.Linq;
6+
using System.Text.RegularExpressions;
7+
using Newtonsoft.Json;
8+
using Newtonsoft.Json.Converters;
39
using YamlDotNet.RepresentationModel;
410
using YamlDotNet.Serialization;
511

@@ -25,7 +31,7 @@ public static YamlMappingNode CreateScopeNode(string scopeName, List<string> sco
2531
{ "scope", new YamlMappingNode { { scopeName, scopeNode } } }
2632
};
2733
}
28-
34+
2935

3036
public static void WriteYaml(string absolutePath, YamlMappingNode rootNode)
3137
{
@@ -35,4 +41,59 @@ public static void WriteYaml(string absolutePath, YamlMappingNode rootNode)
3541
using var writer = new StreamWriter(absolutePath);
3642
writer.Write(yaml);
3743
}
38-
}
44+
45+
public static ExpandoObject? ReadYamlAsObject(string absolutePath)
46+
{
47+
if (!File.Exists(absolutePath))
48+
{
49+
return null;
50+
}
51+
52+
var yamlText = File.ReadAllText(absolutePath);
53+
var jsonText = Modkit.Resources.YamlHelper.YamlToJson(yamlText);
54+
return JsonConvert.DeserializeObject<ExpandoObject>(jsonText, new ExpandoObjectConverter());
55+
}
56+
57+
public static Dictionary<string, List<string>> GetItemsFromYaml(string absolutePath)
58+
{
59+
Dictionary<string, List<string>> ret = [];
60+
if (ReadYamlAsObject(absolutePath) is not ExpandoObject yaml || yaml.AsReadOnly() is not { } yamlDict ||
61+
yamlDict.Count == 0)
62+
{
63+
return ret;
64+
}
65+
66+
67+
foreach (var itemKey in yamlDict.Keys.Where(key => key.StartsWith("Items.")))
68+
{
69+
if (!yamlDict.TryGetValue(itemKey, out var itemValue) || itemValue is not ExpandoObject value ||
70+
value.AsReadOnly() is not { } itemDict || itemDict.Count == 0)
71+
{
72+
continue;
73+
}
74+
75+
ret.TryAdd(itemKey, []);
76+
77+
if (!itemDict.TryGetValue("$instances", out var inst) || inst is not List<Object> instances)
78+
{
79+
ret[itemKey].Add("");
80+
continue;
81+
}
82+
83+
foreach (var i in instances.OfType<ExpandoObject>().Select(i => i.AsReadOnly()))
84+
{
85+
var itemName = itemKey.Replace('{', '(').Replace('}', ')');
86+
foreach (var iKey in i.Keys)
87+
{
88+
itemName = itemName.Replace($"$({iKey})", $"{i[iKey]}");
89+
}
90+
91+
ret[itemKey].Add(itemName);
92+
}
93+
}
94+
95+
return ret;
96+
}
97+
98+
99+
}

WolvenKit.App/Interaction/Interactions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ public static Func<
192192
/// <summary>
193193
/// Display dictionary of {relativePath => brokenReferences[]} in a dialogue.
194194
/// </summary>
195-
public static Func<(string, IDictionary<string, List<string>>), bool> ShowBrokenReferencesList { get; set; } =
195+
public static Func<(string title, string text, IDictionary<string, List<string>> list, bool isExperimental), bool>
196+
ShowDictionaryAsCopyableList { get; set; } =
196197
_ => throw new NotImplementedException();
197198

198199
/// <summary>

WolvenKit.App/ViewModels/Dialogs/ShowBrokenReferences.cs renamed to WolvenKit.App/ViewModels/Dialogs/ShowDictionaryForCopyDialogViewModel.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Windows;
34
using CommunityToolkit.Mvvm.ComponentModel;
45
using WolvenKit.App.Helpers;
56

67
namespace WolvenKit.App.ViewModels.Dialogs;
78

8-
public partial class ShowBrokenReferencesDialogViewModel : DialogViewModel
9+
public partial class ShowDictionaryForCopyDialogViewModel : DialogViewModel
910
{
1011
[ObservableProperty] private IDictionary<string, List<string>> _references;
1112
[ObservableProperty] private string _title;
13+
[ObservableProperty] private string _text;
14+
[ObservableProperty] private bool _isExperimental;
15+
1216
public int ReferencesCount => References.Count;
13-
public double MaxHeight = UIHelper.GetScreenHeight() * 0.9;
1417

15-
public ShowBrokenReferencesDialogViewModel(string title, IDictionary<string, List<string>> references)
18+
19+
public ShowDictionaryForCopyDialogViewModel(string title, string text, IDictionary<string, List<string>> references,
20+
bool isExperimental)
1621
{
1722
References = references;
1823
Title = title;
24+
Text = text;
25+
IsExperimental = isExperimental;
1926
}
20-
}
27+
}

WolvenKit.App/ViewModels/Shell/AppViewModel.ComplexFiles.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ private void NewPhotoModeFiles()
9090
{
9191
var yamlTargetDir =
9292
Path.Join(_projectManager.ActiveProject.ResourcesDirectory,
93-
_projectResourceTools.AppendPersonalDirectory("r6", "tweaks"), "photomode");
93+
ProjectResourceTools.AppendPersonalDirectory("r6", "tweaks"), "photomode");
9494

9595
_templateFileTools.CreatePhotomodeYaml(new PhotomodeYamlOptions()
9696
{
@@ -193,9 +193,9 @@ string GetRelativePath(string pathOrFileName)
193193
return pathOrFileName;
194194
}
195195

196-
if (pathOrFileName.EndsWith(".yaml"))
196+
if (pathOrFileName.HasFileExtension(".yaml"))
197197
{
198-
return Path.Join(_projectResourceTools.AppendPersonalDirectory("r6", "tweaks"), pathOrFileName);
198+
return Path.Join(ProjectResourceTools.AppendPersonalDirectory("r6", "tweaks"), pathOrFileName);
199199
}
200200

201201
return Path.Join(relativePhotoModeFolder, pathOrFileName);

WolvenKit.App/ViewModels/Shell/AppViewModel.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,10 @@ public partial class AppViewModel : ObservableObject/*, IAppViewModel*/
7979
private readonly DocumentTools _documentTools;
8080
private readonly Cr2WTools _cr2WTools;
8181
private readonly TemplateFileTools _templateFileTools;
82-
private readonly ProjectResourceTools _projectResourceTools;
8382
private readonly IUpdateService _updateService;
8483
// expose to view
8584
public ISettingsManager SettingsManager { get; init; }
85+
public ProjectResourceTools ProjectResourceTools { get; init; }
8686

8787
/// <summary>
8888
/// Class constructor
@@ -132,7 +132,7 @@ IUpdateService updateService
132132
_documentTools = documentTools;
133133
_cr2WTools = cr2WTools;
134134
_templateFileTools = templateFileTools;
135-
_projectResourceTools = projectResourceTools;
135+
ProjectResourceTools = projectResourceTools;
136136
_updateService = updateService;
137137

138138
_fileValidationScript = _scriptService.GetScripts().ToList()
@@ -946,7 +946,7 @@ private async Task ScanForBrokenReferencePaths()
946946
case null:
947947
return;
948948
case true when !_archiveManager.IsInitialized:
949-
_projectResourceTools.InitializeArchiveManager();
949+
ProjectResourceTools.InitializeArchiveManager();
950950
break;
951951
}
952952

@@ -965,7 +965,8 @@ private async Task ScanForBrokenReferencePaths()
965965
}
966966

967967
_loggerService.Info("Done scanning");
968-
Interactions.ShowBrokenReferencesList(("Broken references", brokenReferences));
968+
Interactions.ShowDictionaryAsCopyableList(("Broken references",
969+
$"The following {brokenReferences.Count} files seem to hold broken references", brokenReferences, true));
969970
}
970971

971972
[RelayCommand(CanExecute = nameof(CanShowProjectActions))]
@@ -999,7 +1000,8 @@ private void ScanForBrokenFiles()
9991000

10001001
Dictionary<string, List<string>> files = [];
10011002
files.Add(ActiveProject.ModName, brokenFiles);
1002-
Interactions.ShowBrokenReferencesList(("Broken references", files));
1003+
Interactions.ShowDictionaryAsCopyableList(("Broken references",
1004+
$"The following {files.Count} files seem to hold broken references", files, true));
10031005
_progressService.IsIndeterminate = false;
10041006
return;
10051007

WolvenKit.App/ViewModels/Shell/MenuBarViewModel.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
using System.Collections.Generic;
12
using System.ComponentModel;
23
using CommunityToolkit.Mvvm.ComponentModel;
34
using CommunityToolkit.Mvvm.Input;
5+
using WolvenKit.App.Extensions;
46
using WolvenKit.App.Helpers;
57
using WolvenKit.App.Services;
68
using WolvenKit.App.ViewModels.Tools;
@@ -65,6 +67,20 @@ private void MainViewModel_OnDockedViewVisibleChanged(object? sender, AppViewMod
6567
[RelayCommand]
6668
private void OpenGameFolder() => Commonfunctions.ShowFolderInExplorer(SettingsManager.GetRED4GameRootDir());
6769

70+
public Dictionary<string, List<string>> GenerateItemCodesFromYaml()
71+
{
72+
var files = MainViewModel.ProjectResourceTools.GetProjectFiles(".yaml", ProjectFolder.Resources);
73+
74+
Dictionary<string, List<string>> allItems = [];
75+
76+
foreach (var file in files)
77+
{
78+
allItems.AddRange(YamlHelper.GetItemsFromYaml(MainViewModel.ProjectResourceTools.GetAbsolutePath(file)));
79+
}
80+
81+
return allItems;
82+
}
83+
6884
[ObservableProperty]
6985
private bool _projectExplorerCheckbox;
7086

WolvenKit.Core/Extensions/StringPathExtensions.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,6 @@ namespace WolvenKit.Interfaces.Extensions
88
{
99
public static partial class StringPathExtensions
1010
{
11-
// https://stackoverflow.com/a/3695190
12-
public static void EnsureFolderExists(this string path)
13-
{
14-
var directoryName = Path.GetDirectoryName(path);
15-
// If path is a file name only, directory name will be an empty string
16-
if (!string.IsNullOrEmpty(directoryName))
17-
{
18-
// Create all directories on the path that don't already exist
19-
Directory.CreateDirectory(directoryName);
20-
}
21-
}
22-
23-
2411
public static (string, bool, EProjectFolders) GetModRelativePath(this string fullpath,
2512
string activeModFileDirectory)
2613
{
@@ -103,6 +90,32 @@ public static string SanitizeFilePath(this string target, bool useForwardSlashes
10390
/// </summary>
10491
public static bool HasTwoExtensions(this string filePath) => Path.GetFileName(filePath).Split('.').Length > 2;
10592

93+
/// <summary>
94+
/// Checks if the file path has a given extension. Fuzzy matching (see examples below)
95+
/// </summary>
96+
/// <param name="filePath">The file path</param>
97+
/// <param name="fileExtension">fileExtension (e.g. '.yaml' or '.yml')</param>
98+
/// <example>
99+
/// <code>
100+
/// file.mesh.json => true for .json, .mesh, and .mesh.json
101+
/// file.yaml => true for .yaml and .yml
102+
/// </code>
103+
/// </example>
104+
public static bool HasFileExtension(this string filePath, string fileExtension)
105+
{
106+
var targetExtension = fileExtension.StartsWith('.') ? fileExtension : "." + fileExtension;
107+
var targetFileName = filePath.EndsWith(".json") && !fileExtension.Contains(".json")
108+
? filePath.Replace(".json", "")
109+
: filePath;
110+
111+
return targetExtension switch
112+
{
113+
".yaml" or ".yml" => targetFileName.Contains(".yaml", StringComparison.OrdinalIgnoreCase) ||
114+
targetFileName.Contains(".yml", StringComparison.OrdinalIgnoreCase),
115+
_ => targetFileName.Contains(targetExtension, StringComparison.OrdinalIgnoreCase)
116+
};
117+
}
118+
106119
/// <summary>
107120
/// Regular expression for file path separators, forward or backward slashes
108121
/// </summary>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Text.RegularExpressions;
2+
using Newtonsoft.Json;
3+
using YamlDotNet.Serialization;
4+
5+
namespace WolvenKit.Modkit.Resources;
6+
7+
public class YamlHelper
8+
{
9+
/// <summary>
10+
/// remove yaml tags like !include, !append etc., which make Deserializer fail
11+
/// </summary>
12+
private static readonly Regex s_yamlTagRegex = new(@"(?<=-)\s?\![a-z-]*(?=\s)");
13+
14+
public static string YamlToJson(string yamlText)
15+
{
16+
// remove yaml tags like !include, !append etc.
17+
yamlText = s_yamlTagRegex.Replace(yamlText, "");
18+
19+
// deserialize it
20+
var yamlObject = new Deserializer().Deserialize(yamlText);
21+
22+
return JsonConvert.SerializeObject(yamlObject);
23+
}
24+
}

0 commit comments

Comments
 (0)