整合SLSUtilities

This commit is contained in:
SoulliesOfficial
2026-01-17 11:35:49 -05:00
parent d94241f36c
commit 7ee2894a63
1338 changed files with 3051541 additions and 507034 deletions

View File

@@ -0,0 +1,29 @@
using System.Collections.Generic;
using System.IO;
namespace LunaWolfStudiosEditor.ScriptableSheets.Tables
{
public static class FlatFileUtility
{
public static readonly Dictionary<string, string> FlatFileExtensions = new Dictionary<string, string>()
{
{ ",", "csv" },
{ "|", "psv" },
{ ";", "ssv" },
{ "\t", "tsv" },
};
public static readonly Dictionary<string, string> FlatFileDelimiters = new Dictionary<string, string>()
{
{ "csv", "," },
{ "psv", "|" },
{ "ssv", ";" },
{ "tsv", "\t" },
};
public static string GetExtensionFromPath(string path)
{
return Path.GetExtension(path).Replace(".", string.Empty);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0af394b0956d2b24b937b33a8afb3b77
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 284559
packageName: Scriptable Sheets
packageVersion: 1.8.0
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Tables/Utilities/FlatFileUtility.cs
uploadId: 823456

View File

@@ -0,0 +1,17 @@
using System.Text;
namespace LunaWolfStudiosEditor.ScriptableSheets.Tables
{
public static class StringBuilderUtility
{
public static StringBuilder Wrap(this StringBuilder sb, string value, char open, char close)
{
return sb.Append(open).Append(value).Append(close);
}
public static StringBuilder Wrap(this StringBuilder sb, string value, Wrapper wrapper)
{
return sb.Wrap(value, wrapper.Open, wrapper.Close);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0880701e7a3efd6408f1b8351cd0aac1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 284559
packageName: Scriptable Sheets
packageVersion: 1.8.0
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Tables/Utilities/StringBuilderUtility.cs
uploadId: 823456

View File

@@ -0,0 +1,378 @@
using LunaWolfStudiosEditor.ScriptableSheets.Shared;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
namespace LunaWolfStudiosEditor.ScriptableSheets.Tables
{
public static class TableUtility
{
public static void FromFlatFileFormat(this Table<ITableProperty> propertyTable, string flatFileContent, FlatFileFormatSettings formatSettings)
{
var wrapper = formatSettings.GetWrapper();
if (formatSettings.HasHeaders)
{
var firstRowEndIndex = flatFileContent.IndexOf(formatSettings.RowDelimiter);
var joinedColumnHeaders = formatSettings.GetJoinedColumnHeaders(wrapper);
var joinedColumnHeadersSanitized = SanitizeHeader(joinedColumnHeaders);
if (firstRowEndIndex >= 0)
{
var headerRow = flatFileContent.Substring(0, firstRowEndIndex).Trim();
var headerRowSanitized = SanitizeHeader(headerRow);
// Validate column headers match the header row.
if (joinedColumnHeadersSanitized.Contains(headerRowSanitized) || headerRowSanitized.Contains(joinedColumnHeadersSanitized))
{
flatFileContent = flatFileContent.Substring(firstRowEndIndex + formatSettings.RowDelimiter.Length);
}
}
else
{
// Handle case where there's only a single row.
var flatFileContentSanitized = SanitizeHeader(flatFileContent);
if (joinedColumnHeadersSanitized.Contains(flatFileContentSanitized))
{
flatFileContent = string.Empty;
}
}
}
var stringSplitOptions = formatSettings.RemoveEmptyRows ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None;
string[] rowData;
if (formatSettings.HasWrapping)
{
var inverseWrap = wrapper.WrapInverse(formatSettings.RowDelimiter);
// Replace any trailing white space at end of line.
flatFileContent = Regex.Replace(flatFileContent, $@"{Regex.Escape(wrapper.Close.ToString())}\s*{Regex.Escape(formatSettings.RowDelimiter)}\s*{Regex.Escape(wrapper.Open.ToString())}", inverseWrap);
// Use new string array parameter to support older versions of C#.
rowData = flatFileContent.Split(new string[] { inverseWrap }, stringSplitOptions);
for (var i = 0; i < rowData.Length - 1; i++)
{
rowData[i] += wrapper.Close;
var nextRow = i + 1;
if (nextRow < rowData.Length)
{
rowData[nextRow] = wrapper.Open + rowData[nextRow];
}
}
var totalWrapChars = flatFileContent.Count(c => c == wrapper.Open || c == wrapper.Close);
var expectedWrapChars = rowData.Length * 2;
if (totalWrapChars < expectedWrapChars)
{
Debug.LogWarning($"Invalid {nameof(WrapOption)} {formatSettings.WrapOption}. Found {totalWrapChars} matching char(s) but expected at least {expectedWrapChars}. Defaulting to {nameof(WrapOption)} {WrapOption.None}.");
formatSettings.WrapOption = WrapOption.None;
rowData = flatFileContent.Split(new string[] { formatSettings.RowDelimiter }, stringSplitOptions);
}
}
else
{
rowData = flatFileContent.Split(new string[] { formatSettings.RowDelimiter }, stringSplitOptions);
}
var rowIndex = 0;
for (var row = formatSettings.FirstRowIndex; rowIndex < rowData.Length && row < propertyTable.Rows; row++)
{
string[] columnData;
if (formatSettings.HasWrapping)
{
var line = rowData[rowIndex].Trim();
columnData = line.Split(new string[] { wrapper.WrapInverse(formatSettings.ColumnDelimiter) }, StringSplitOptions.None);
if (columnData.Length > 0 && columnData[0].StartsWith(wrapper.Open.ToString()))
{
columnData[0] = columnData[0].Remove(0, 1);
var lastIndex = columnData.Length - 1;
if (columnData[lastIndex].EndsWith(wrapper.Close.ToString()))
{
columnData[lastIndex] = columnData[lastIndex].Remove(columnData[lastIndex].Length - 1, 1);
}
}
}
else
{
columnData = rowData[rowIndex].Split(new string[] { formatSettings.ColumnDelimiter }, StringSplitOptions.None);
}
var columnIndex = 0;
if (formatSettings.HasWrapping && formatSettings.EscapeOption != EscapeOption.None)
{
var escapedOpen = formatSettings.EscapeOption.GetEscapedWrapper(wrapper.Open, formatSettings.CustomEscapeSequence);
var escapedClose = formatSettings.EscapeOption.GetEscapedWrapper(wrapper.Close, formatSettings.CustomEscapeSequence);
for (var y = formatSettings.FirstColumnIndex; columnIndex < columnData.Length && y < propertyTable.Columns; y++)
{
var unescapedValue = wrapper.UnescapeContent(columnData[columnIndex], escapedOpen, escapedClose);
propertyTable.UpdateProperty(row, y, unescapedValue, formatSettings);
columnIndex++;
}
}
else
{
for (var y = formatSettings.FirstColumnIndex; columnIndex < columnData.Length && y < propertyTable.Columns; y++)
{
propertyTable.UpdateProperty(row, y, columnData[columnIndex], formatSettings);
columnIndex++;
}
}
rowIndex++;
}
}
private static ITableProperty UpdateProperty(this Table<ITableProperty> propertyTable, int row, int column, string value, FlatFileFormatSettings formatSettings)
{
var property = propertyTable.Get(row, column);
// Property could be null when mixing array sizes.
if (property != null)
{
property.SetProperty(value, formatSettings);
}
return property;
}
public static void FromJsonFormat(this Table<ITableProperty> propertyTable, string json, JsonSerializationFormat format, FlatFileFormatSettings formatSettings)
{
switch (format)
{
case JsonSerializationFormat.Flat:
propertyTable.FromJsonFlatFormat(json, formatSettings);
break;
case JsonSerializationFormat.Hierarchy:
propertyTable.FromJsonHierarchyFormat(json, formatSettings);
break;
default:
Debug.LogError($"Unsupported {nameof(JsonSerializationFormat)} {format}.");
break;
}
}
private static void FromJsonFlatFormat(this Table<ITableProperty> propertyTable, string json, FlatFileFormatSettings formatSettings)
{
try
{
var rowData = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(json);
var row = 0;
foreach (var rowEntry in rowData)
{
if (row >= propertyTable.Rows)
{
LogJSONRowWarning(rowData.Count, propertyTable.Rows);
break;
}
var column = formatSettings.FirstColumnIndex;
var rowValues = rowEntry.Value.ToArray();
for (var i = 0; i < rowValues.Length; i++)
{
if (column >= propertyTable.Columns)
{
// Add 1 for Actions column.
Debug.LogWarning($"Mismatched columns. JSON data has ({rowEntry.Value.Count + 1}) column(s) but the property table only has ({propertyTable.Columns}) column(s).");
break;
}
var property = propertyTable.UpdateProperty(row, column, rowValues[i].Value, formatSettings);
// Only go to the next element if the previous was found successfully. This handles edge cases with mismatched array sizes.
if (property == null)
{
i--;
}
else if (property.PropertyPath != rowValues[i].Key)
{
Debug.LogWarning($"Mismatched property path. Expected property path '{property.PropertyPath}' but used property path from JSON '{rowValues[i].Key}' for '{rowEntry.Key}'.");
}
column++;
}
row++;
}
}
catch (Exception ex)
{
Debug.LogError($"Error parsing JSON {ex.Message}");
}
}
private static void FromJsonHierarchyFormat(this Table<ITableProperty> propertyTable, string json, FlatFileFormatSettings formatSettings)
{
try
{
var rowData = JsonConvert.DeserializeObject<Dictionary<string, JObject>>(json);
var row = 0;
foreach (var rowEntry in rowData)
{
if (row >= propertyTable.Rows)
{
LogJSONRowWarning(rowData.Count, propertyTable.Rows);
break;
}
var rootObject = propertyTable.Get(row, formatSettings.FirstColumnIndex).RootObject;
var assetName = rowEntry.Key;
if (assetName != rootObject.name)
{
var assetPath = AssetDatabase.GetAssetPath(rootObject);
var assetRenameResponse = AssetDatabase.RenameAsset(assetPath, assetName);
if (!string.IsNullOrEmpty(assetRenameResponse))
{
Debug.LogWarning($"Cannot rename {rootObject.name} at asset path {assetPath} to new name '{assetName.GetEscapedText()}'.\n{assetRenameResponse}");
assetName = rootObject.name;
}
}
var serializedObjectJson = JsonConvert.SerializeObject(rowEntry.Value);
if (rootObject is ScriptableObject)
{
JsonUtility.FromJsonOverwrite(serializedObjectJson, rootObject);
}
else
{
EditorJsonUtility.FromJsonOverwrite(serializedObjectJson, rootObject);
// Ensure m_Name property matches filename after deserializing.
rootObject.name = assetName;
}
EditorUtility.SetDirty(rootObject);
row++;
}
}
catch (Exception ex)
{
Debug.LogError($"Error parsing JSON {ex.Message}");
}
}
private static void LogJSONRowWarning(int expectedRows, int actualRows)
{
Debug.LogWarning($"Mismatched rows. JSON data has ({expectedRows}) row(s) but the property table only has ({actualRows}) row(s). Create ({expectedRows - actualRows}) more row(s).");
}
public static string ToFlatFileFormat(this Table<ITableProperty> propertyTable, FlatFileFormatSettings formatSettings)
{
var lastColumn = propertyTable.Columns - 1;
var lastRow = propertyTable.Rows - 1;
var flatFile = new StringBuilder();
var wrapper = formatSettings.GetWrapper();
if (formatSettings.HasHeaders)
{
for (var column = formatSettings.FirstColumnIndex; column < formatSettings.ColumnHeaders.Length; column++)
{
var header = formatSettings.ColumnHeaders[column];
if (formatSettings.HasWrapping)
{
flatFile.Wrap(header, wrapper);
}
else
{
flatFile.Append(header);
}
if (formatSettings.FirstColumnOnly)
{
break;
}
if (column < lastColumn)
{
flatFile.Append(formatSettings.ColumnDelimiter);
}
}
if (propertyTable.Rows > formatSettings.FirstRowIndex)
{
flatFile.Append(formatSettings.RowDelimiter);
}
}
for (var row = formatSettings.FirstRowIndex; row < propertyTable.Rows; row++)
{
for (var column = formatSettings.FirstColumnIndex; column < propertyTable.Columns; column++)
{
var property = propertyTable.Get(row, column)?.GetProperty(formatSettings);
if (formatSettings.HasWrapping)
{
if (formatSettings.EscapeOption != EscapeOption.None)
{
var escapedOpen = formatSettings.EscapeOption.GetEscapedWrapper(wrapper.Open, formatSettings.CustomEscapeSequence);
var escapedClose = formatSettings.EscapeOption.GetEscapedWrapper(wrapper.Close, formatSettings.CustomEscapeSequence);
property = wrapper.EscapeContent(property, escapedOpen, escapedClose);
}
flatFile.Wrap(property, wrapper);
}
else
{
flatFile.Append(property);
}
if (formatSettings.FirstColumnOnly)
{
break;
}
if (column < lastColumn)
{
flatFile.Append(formatSettings.ColumnDelimiter);
}
}
if (formatSettings.FirstRowOnly)
{
break;
}
if (row < lastRow)
{
flatFile.Append(formatSettings.RowDelimiter);
}
}
return flatFile.ToString();
}
public static string ToJsonFormat(this Table<ITableProperty> propertyTable, JsonSerializationFormat format, FlatFileFormatSettings formatSettings)
{
switch (format)
{
case JsonSerializationFormat.Flat:
return propertyTable.ToJsonFlatFormat(formatSettings);
case JsonSerializationFormat.Hierarchy:
return propertyTable.ToJsonHierarchyFormat();
default:
Debug.LogError($"Unsupported {nameof(JsonSerializationFormat)} {format}.");
break;
}
return string.Empty;
}
private static string ToJsonFlatFormat(this Table<ITableProperty> propertyTable, FlatFileFormatSettings formatSettings)
{
var rowObjects = new Dictionary<string, Dictionary<string, string>>();
for (var row = 0; row < propertyTable.Rows; row++)
{
if (propertyTable.Columns > formatSettings.FirstColumnIndex)
{
var rowRootObjectName = propertyTable.Get(row, formatSettings.FirstColumnIndex).RootObject.name;
var rowData = new Dictionary<string, string>();
for (var column = formatSettings.FirstColumnIndex; column < propertyTable.Columns; column++)
{
var value = propertyTable.Get(row, column);
if (value != null)
{
rowData[value.PropertyPath] = value.GetProperty(formatSettings);
}
}
rowObjects[rowRootObjectName] = rowData;
}
}
return JsonConvert.SerializeObject(rowObjects, Formatting.Indented);
}
private static string ToJsonHierarchyFormat(this Table<ITableProperty> propertyTable)
{
var rowObjects = new Dictionary<string, JObject>();
for (var row = 0; row < propertyTable.Rows; row++)
{
if (propertyTable.Columns > 1)
{
var rowRootObject = propertyTable.Get(row, 1).RootObject;
var unityJson = rowRootObject is ScriptableObject ? JsonUtility.ToJson(rowRootObject) : EditorJsonUtility.ToJson(rowRootObject);
rowObjects[rowRootObject.name] = (JObject) JsonConvert.DeserializeObject(unityJson);
}
}
return JsonConvert.SerializeObject(rowObjects, Formatting.Indented);
}
private static string SanitizeHeader(string header)
{
return new string(header.Where(c => !char.IsWhiteSpace(c)).ToArray()).ToLower().Replace("m_", string.Empty);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b1ce0b5d5c88a604baad759781ecc701
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 284559
packageName: Scriptable Sheets
packageVersion: 1.8.0
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Tables/Utilities/TableUtility.cs
uploadId: 823456

View File

@@ -0,0 +1,24 @@
using Newtonsoft.Json;
namespace LunaWolfStudiosEditor.ScriptableSheets.Tables
{
public class UnityObjectWrapper
{
[JsonProperty("guid")]
public string Guid;
[JsonProperty("localId")]
public long LocalId;
[JsonProperty("type")]
public int Type;
[JsonProperty("instanceID")]
public int InstanceId;
public override string ToString()
{
return $"guid: '{Guid}' localId: '{LocalId}' type: '{Type}' instanceID: '{InstanceId}'";
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 72e72fd55d4fcd8409158e2a9beff9fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 284559
packageName: Scriptable Sheets
packageVersion: 1.8.0
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Tables/Utilities/UnityObjectWrapper.cs
uploadId: 823456

View File

@@ -0,0 +1,66 @@
using UnityEngine;
namespace LunaWolfStudiosEditor.ScriptableSheets.Tables
{
[System.Serializable]
public class Wrapper
{
[SerializeField]
private char m_Open;
public char Open { get => m_Open; set => m_Open = value; }
[SerializeField]
private char m_Close;
public char Close { get => m_Close; set => m_Close = value; }
public Wrapper(char openAndClose)
{
m_Open = openAndClose;
m_Close = openAndClose;
}
public Wrapper(char open, char close)
{
m_Open = open;
m_Close = close;
}
public string Wrap(string value)
{
return $"{m_Open}{value}{m_Close}";
}
public string WrapInverse(string value)
{
return $"{m_Close}{value}{m_Open}";
}
public string EscapeContent(string content, string escapedOpen, string escapedClose)
{
if (string.IsNullOrEmpty(content))
{
return content;
}
var escapedContent = content.Replace(m_Open.ToString(), escapedOpen);
if (m_Open != m_Close)
{
escapedContent = escapedContent.Replace(m_Close.ToString(), escapedClose);
}
return escapedContent;
}
public string UnescapeContent(string content, string escapedOpen, string escapedClose)
{
if (string.IsNullOrEmpty(content))
{
return content;
}
var unescapedContent = content.Replace(escapedOpen, m_Open.ToString());
if (escapedOpen != escapedClose)
{
unescapedContent = unescapedContent.Replace(escapedClose, m_Close.ToString());
}
return unescapedContent;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 8cb18fbca1e06dd4281497ed67795cbd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 284559
packageName: Scriptable Sheets
packageVersion: 1.8.0
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Tables/Utilities/Wrapper.cs
uploadId: 823456