Skip to content

Commit 1d7acb2

Browse files
committed
add: valid filename check
1 parent 037a9f4 commit 1d7acb2

File tree

2 files changed

+76
-16
lines changed

2 files changed

+76
-16
lines changed

Scripts/Runtime/Core/ScriptableObjectCollection.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,11 @@ public ScriptableObject AddNew(Type itemType, string assetName = "")
133133
string parentFolderPath = Path.Combine(assetPath, "Items" );
134134
AssetDatabaseUtils.CreatePathIfDoesntExist(parentFolderPath);
135135

136-
string itemName = assetName;
136+
string itemName = assetName.ToValidFilename();
137137

138138
if (string.IsNullOrEmpty(itemName))
139139
{
140-
itemName = $"{itemType.Name}";
140+
itemName = $"{itemType.Name}".ToValidFilename();
141141
}
142142

143143
string uniqueAssetPath = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(parentFolderPath, itemName + ".asset"));

Scripts/Runtime/Extensions/StringExtensions.cs

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,33 @@ public static class StringExtensions
3636
"group", "into", "join", "let", "nameof", "notnull", "on", "orderby", "partial", "partial", "remove",
3737
"select", "set", "unmanaged", "value", "var", "when", "where", "where", "with", "yield","values"
3838
};
39+
40+
private static readonly char[] EXTRA_INVALID_FILE_CHARS = { '`' };
41+
42+
private static readonly string[] WINDOWS_RESERVED_NAMES =
43+
{
44+
"CON","PRN","AUX","NUL","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9",
45+
"LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"
46+
};
3947

4048
public static string Sanitize(this string input)
4149
{
42-
input = input.StartingNumbersToWords();
43-
// replace white spaces with undescore, then replace all invalid chars with empty string
44-
IEnumerable<string> pascalCase =
45-
INVALID_CHARS_RGX.Replace(WHITE_SPACE.Replace(input, "_"), string.Empty)
46-
// split by underscores
47-
.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries)
48-
// set first letter to uppercase
49-
.Select(w => STARTS_WITH_LOWER_CASE_CHAR.Replace(w, m => m.Value.ToUpper()))
50-
// replace second and all following upper case letters to lower if there is no next lower (ABC -> Abc)
51-
.Select(w => FIRST_CHAR_FOLLOWED_BY_UPPER_CASES_ONLY.Replace(w, m => m.Value.ToLower()))
52-
// set upper case the first lower case following a number (Ab9cd -> Ab9Cd)
53-
.Select(w => LOWER_CASE_NEXT_TO_NUMBER.Replace(w, m => m.Value.ToUpper()))
54-
// lower second and next upper case letters except the last if it follows by any lower (ABcDEf -> AbcDef)
55-
.Select(w => UPPER_CASE_INSIDE.Replace(w, m => m.Value.ToLower()));
50+
// Treat any non-alphanumeric sequence as a word separator
51+
input = Regex.Replace(input, "[^a-zA-Z0-9]+", "_");
52+
53+
IEnumerable<string> pascalCase =
54+
input
55+
.Trim('_')
56+
// split by underscores
57+
.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries)
58+
// set first letter to uppercase
59+
.Select(w => STARTS_WITH_LOWER_CASE_CHAR.Replace(w, m => m.Value.ToUpper()))
60+
// replace second and all following upper case letters to lower if there is no next lower (ABC -> Abc)
61+
.Select(w => FIRST_CHAR_FOLLOWED_BY_UPPER_CASES_ONLY.Replace(w, m => m.Value.ToLower()))
62+
// set upper case the first lower case following a number (Ab9cd -> Ab9Cd)
63+
.Select(w => LOWER_CASE_NEXT_TO_NUMBER.Replace(w, m => m.Value.ToUpper()))
64+
// lower second and next upper case letters except the last if it follows by any lower (ABcDEf -> AbcDef)
65+
.Select(w => UPPER_CASE_INSIDE.Replace(w, m => m.Value.ToLower()));
5666

5767
return string.Concat(pascalCase);
5868
}
@@ -333,5 +343,55 @@ public static string GetProjectPath(this string absolutePath)
333343
string relativePath = ToPathWithConsistentSeparators(Path.GetRelativePath(projectPath, absolutePath));
334344
return relativePath;
335345
}
346+
347+
public static bool IsValidFilename(this string value)
348+
{
349+
if (string.IsNullOrWhiteSpace(value))
350+
return false;
351+
352+
char[] invalid = Path.GetInvalidFileNameChars();
353+
if (value.IndexOfAny(invalid) >= 0)
354+
return false;
355+
356+
if (value.IndexOfAny(EXTRA_INVALID_FILE_CHARS) >= 0)
357+
return false;
358+
359+
if (value.EndsWith(" ") || value.EndsWith("."))
360+
return false;
361+
362+
string stem = Path.GetFileNameWithoutExtension(value);
363+
if (WINDOWS_RESERVED_NAMES.Any(r => r.Equals(stem, StringComparison.OrdinalIgnoreCase)))
364+
return false;
365+
366+
return true;
367+
}
368+
369+
public static string ToValidFilename(this string value, char replacement = '_')
370+
{
371+
if (string.IsNullOrEmpty(value))
372+
return value;
373+
374+
if (value.IsValidFilename())
375+
return value;
376+
377+
string safe = value;
378+
379+
foreach (char c in Path.GetInvalidFileNameChars())
380+
safe = safe.Replace(c, replacement);
381+
382+
foreach (char c in EXTRA_INVALID_FILE_CHARS)
383+
safe = safe.Replace(c, replacement);
384+
385+
safe = safe.TrimEnd(' ', '.');
386+
387+
string stem = Path.GetFileNameWithoutExtension(safe);
388+
if (WINDOWS_RESERVED_NAMES.Any(r => r.Equals(stem, StringComparison.OrdinalIgnoreCase)))
389+
safe = "_" + safe;
390+
391+
if (string.IsNullOrWhiteSpace(safe))
392+
safe = "_";
393+
394+
return safe;
395+
}
336396
}
337397
}

0 commit comments

Comments
 (0)