Compare commits
5 Commits
ae8f8f1216
...
2b18d39744
Author | SHA1 | Date |
---|---|---|
|
2b18d39744 | 5 months ago |
|
c38b6097c7 | 5 months ago |
|
eccf607522 | 5 months ago |
|
60d3cc40d1 | 5 months ago |
|
ad48ba0f22 | 5 months ago |
21 changed files with 462 additions and 93 deletions
@ -0,0 +1,9 @@ |
|||||||
|
using Unity.Profiling; |
||||||
|
|
||||||
|
namespace Tools |
||||||
|
{ |
||||||
|
public static class ProfilerUtil |
||||||
|
{ |
||||||
|
public static readonly ProfilerMarker MyMarker = new ProfilerMarker("MyCustomCode"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: ab28fc99e92b409aadd67ec27519c3cc |
||||||
|
timeCreated: 1737917311 |
@ -0,0 +1,118 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Reflection; |
||||||
|
using OfficeOpenXml; |
||||||
|
using Sirenix.OdinInspector; |
||||||
|
using UnityEditor; |
||||||
|
using UnityEngine; |
||||||
|
|
||||||
|
namespace Tools.ExcelResolver.Editor |
||||||
|
{ |
||||||
|
public sealed partial class ExcelResolverEditorWindow |
||||||
|
{ |
||||||
|
[FoldoutGroup("Hide Setting")] |
||||||
|
[ShowInInspector] private Dictionary<ExcelWorksheet, ClassCodeData> classCodeDataDict = new(); |
||||||
|
|
||||||
|
private void WriteDataSO() |
||||||
|
{ |
||||||
|
foreach (var classCodeDataDictPair in classCodeDataDict) |
||||||
|
{ |
||||||
|
var worksheet = classCodeDataDictPair.Key; |
||||||
|
var classCodeData = classCodeDataDictPair.Value; |
||||||
|
|
||||||
|
var dataName = $"{classCodeData.className}"; |
||||||
|
var path = $"{excelResolverConfig.SOPathRoot}/{classCodeData.className}/"; |
||||||
|
|
||||||
|
string[] assetGuids = AssetDatabase.FindAssets($"t:{dataName}", new[] { path }); |
||||||
|
Dictionary<string, ScriptableObject> originAssets = new(); |
||||||
|
for (int i = 0; i < assetGuids.Length; i++) |
||||||
|
{ |
||||||
|
string assetPath = AssetDatabase.GUIDToAssetPath(assetGuids[i]); |
||||||
|
ScriptableObject scriptableObject = AssetDatabase.LoadAssetAtPath<ScriptableObject>(assetPath); |
||||||
|
originAssets.Add(scriptableObject.name, scriptableObject); |
||||||
|
} |
||||||
|
|
||||||
|
var result = new List<ScriptableObject>(); |
||||||
|
|
||||||
|
for (int row = 1; row <= worksheet.Dimension.End.Row; row++) |
||||||
|
{ |
||||||
|
// 跳过注释行 |
||||||
|
if (worksheet.Cells[row, 1].Text.StartsWith("##")) continue; |
||||||
|
|
||||||
|
var assetName = getAssetName(worksheet, row, classCodeData); |
||||||
|
var fullPath = $"{path}{assetName}.asset"; |
||||||
|
|
||||||
|
if (originAssets.ContainsKey(assetName)) |
||||||
|
{ |
||||||
|
set(originAssets[assetName], worksheet, row, classCodeData); |
||||||
|
EditorUtility.SetDirty(originAssets[assetName]); |
||||||
|
Debug.Log($"刷新:{assetName}", originAssets[assetName]); |
||||||
|
result.Add(originAssets[assetName]); |
||||||
|
originAssets.Remove(assetName); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
ScriptableObject instance = ScriptableObject.CreateInstance(dataName); |
||||||
|
set(instance, worksheet, row, classCodeData); |
||||||
|
AssetDatabase.CreateAsset(instance, fullPath); |
||||||
|
Debug.Log($"创建:{assetName}", instance); |
||||||
|
result.Add(instance); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
foreach (var value in originAssets.Values) |
||||||
|
{ |
||||||
|
Debug.LogError($"删除:{value.name}", value); |
||||||
|
AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(value)); |
||||||
|
} |
||||||
|
|
||||||
|
WriteUtilSO(path, result, classCodeData); |
||||||
|
} |
||||||
|
|
||||||
|
AssetDatabase.SaveAssets(); |
||||||
|
GC.Collect(); |
||||||
|
GC.WaitForPendingFinalizers(); |
||||||
|
|
||||||
|
void set(ScriptableObject instance, ExcelWorksheet worksheet, int row, ClassCodeData classCodeData) |
||||||
|
{ |
||||||
|
foreach (var pair in classCodeData.fields) |
||||||
|
{ |
||||||
|
var col = pair.Key; |
||||||
|
var fieldData = pair.Value; |
||||||
|
|
||||||
|
var cell = worksheet.Cells[row, col]; |
||||||
|
object convertedValue = ExcelResolverUtil.ConvertCellValue(cell, fieldData.type, classCodeData.className); |
||||||
|
FieldInfo fieldInfo = instance.GetType().GetField(fieldData.varName); |
||||||
|
if (fieldInfo == null) throw new Exception($"{classCodeData.className}中不存在字段:{fieldData.varName}"); |
||||||
|
fieldInfo.SetValue(instance, convertedValue); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
string getAssetName(ExcelWorksheet worksheet, int row, ClassCodeData classCodeData) |
||||||
|
{ |
||||||
|
switch (classCodeData.tableType) |
||||||
|
{ |
||||||
|
case TableType.SingleKeyTable: |
||||||
|
var keyColIndex = classCodeData.keyField[0].colIndex; |
||||||
|
var key = worksheet.Cells[row, keyColIndex].Text; |
||||||
|
if (!string.IsNullOrEmpty(key)) |
||||||
|
return $"{classCodeData.className}_{key}"; |
||||||
|
break; |
||||||
|
// case TableType.UnionMultiKeyTable: |
||||||
|
// return $"{classCodeData.className}_{classCodeData.keyField[0].varName}"; |
||||||
|
// case TableType.MultiKeyTable: |
||||||
|
// return $"{classCodeData.className}_{classCodeData.keyField[0].varName}"; |
||||||
|
// case TableType.NotKetTable: |
||||||
|
// return classCodeData.className; |
||||||
|
// case TableType.ColumnTable: |
||||||
|
// return $"{classCodeData.className}_{classCodeData.fields[2].Value.varName}"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
throw new Exception($"无法定义资产名称 " + |
||||||
|
$"className: '<color=cyan>{classCodeData.className}</color>' " + |
||||||
|
$"FullAddress: <color=cyan>{row}</color> "); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,80 +0,0 @@ |
|||||||
using System; |
|
||||||
using System.Collections.Generic; |
|
||||||
using System.IO; |
|
||||||
using System.Reflection; |
|
||||||
using OfficeOpenXml; |
|
||||||
using Sirenix.OdinInspector; |
|
||||||
using UnityEditor; |
|
||||||
using UnityEngine; |
|
||||||
|
|
||||||
namespace Tools.ExcelResolver.Editor |
|
||||||
{ |
|
||||||
public sealed partial class ExcelResolverEditorWindow |
|
||||||
{ |
|
||||||
[FoldoutGroup("Hide Setting")] |
|
||||||
[ShowInInspector] private Dictionary<ExcelWorksheet, ClassCodeData> classCodeDataDict = new(); |
|
||||||
|
|
||||||
private void WriteSOData() |
|
||||||
{ |
|
||||||
foreach (var classCodeDataDictPair in classCodeDataDict) |
|
||||||
{ |
|
||||||
var worksheet = classCodeDataDictPair.Key; |
|
||||||
var classCodeData = classCodeDataDictPair.Value; |
|
||||||
|
|
||||||
Type soType = ExcelResolverUtil.GetOrCacheTypeByName(classCodeData.className); |
|
||||||
|
|
||||||
if (soType == null) |
|
||||||
{ |
|
||||||
Debug.LogError($"Class '{classCodeData.className}SO' not found. Please generate classes first (or check namespace)."); |
|
||||||
return; |
|
||||||
} |
|
||||||
var path = $"{excelResolverConfig.SOPathRoot}/{classCodeData.className}/"; |
|
||||||
|
|
||||||
for (int row = 1; row <= worksheet.Dimension.End.Row; row++) |
|
||||||
{ |
|
||||||
var assetName = $"{classCodeData.className}_{row}"; |
|
||||||
var fullPath = $"{path}{assetName}.asset"; |
|
||||||
// 跳过注释行 |
|
||||||
if (worksheet.Cells[row, 1].Text.StartsWith("##")) |
|
||||||
{ |
|
||||||
if (File.Exists(fullPath)) |
|
||||||
{ |
|
||||||
AssetDatabase.DeleteAsset(fullPath); |
|
||||||
Debug.LogError($"删除:{assetName}"); |
|
||||||
} |
|
||||||
continue; |
|
||||||
} |
|
||||||
ScriptableObject instance; |
|
||||||
if (File.Exists(fullPath)) |
|
||||||
{ |
|
||||||
instance = AssetDatabase.LoadAssetAtPath<ScriptableObject>(fullPath); |
|
||||||
Debug.Log($"刷新:{assetName}", instance); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
instance = ScriptableObject.CreateInstance(soType); |
|
||||||
AssetDatabase.CreateAsset(instance, fullPath); |
|
||||||
AssetDatabase.SaveAssets(); |
|
||||||
Debug.Log($"创建:{assetName}", instance); |
|
||||||
} |
|
||||||
|
|
||||||
foreach (var pair in classCodeData.fields) |
|
||||||
{ |
|
||||||
var col = pair.Key; |
|
||||||
var fieldData = pair.Value; |
|
||||||
|
|
||||||
var cell = worksheet.Cells[row, col]; |
|
||||||
object convertedValue = ExcelResolverUtil.ConvertCellValue(cell, fieldData.type, classCodeData.className); |
|
||||||
FieldInfo fieldInfo = soType.GetField(fieldData.varName); |
|
||||||
if (fieldInfo == null) throw new Exception($"目标类中不存在字段:{fieldData.varName}"); |
|
||||||
fieldInfo.SetValue(instance, convertedValue); |
|
||||||
} |
|
||||||
EditorUtility.SetDirty(instance); |
|
||||||
} |
|
||||||
AssetDatabase.SaveAssets(); |
|
||||||
} |
|
||||||
GC.Collect(); |
|
||||||
GC.WaitForPendingFinalizers(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,48 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections; |
||||||
|
using System.Collections.Generic; |
||||||
|
using UnityEditor; |
||||||
|
using UnityEngine; |
||||||
|
|
||||||
|
namespace Tools.ExcelResolver.Editor |
||||||
|
{ |
||||||
|
public sealed partial class ExcelResolverEditorWindow |
||||||
|
{ |
||||||
|
private void WriteUtilSO(string path, List<ScriptableObject> assets, ClassCodeData classCodeData) |
||||||
|
{ |
||||||
|
var utilName = $"{classCodeData.className}Util"; |
||||||
|
var utilPath = $"{path}/_{utilName}.asset"; |
||||||
|
|
||||||
|
var instance = AssetDatabase.LoadAssetAtPath<ScriptableObject>(utilPath); |
||||||
|
if (instance == null) |
||||||
|
{ |
||||||
|
instance = ScriptableObject.CreateInstance(utilName); |
||||||
|
AssetDatabase.CreateAsset(instance, utilPath); |
||||||
|
} |
||||||
|
|
||||||
|
switch (classCodeData.tableType) |
||||||
|
{ |
||||||
|
case TableType.SingleKeyTable: |
||||||
|
var keyField = classCodeData.keyField[0]; |
||||||
|
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(keyField.type.RealType, assets[0].GetType())); |
||||||
|
|
||||||
|
foreach (var asset in assets) |
||||||
|
{ |
||||||
|
var key = asset.GetType().GetField(keyField.varName).GetValue(asset); |
||||||
|
dict[key] = asset; |
||||||
|
} |
||||||
|
instance.GetType().GetField("Data").SetValue(instance, dict); |
||||||
|
break; |
||||||
|
case TableType.UnionMultiKeyTable: |
||||||
|
break; |
||||||
|
case TableType.MultiKeyTable: |
||||||
|
break; |
||||||
|
case TableType.NotKetTable: |
||||||
|
break; |
||||||
|
case TableType.ColumnTable: |
||||||
|
break; |
||||||
|
} |
||||||
|
EditorUtility.SetDirty(instance); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,3 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 3b78262c18a44a04af02c750d7628d02 |
||||||
|
timeCreated: 1737914896 |
Binary file not shown.
@ -1,5 +1,5 @@ |
|||||||
fileFormatVersion: 2 |
fileFormatVersion: 2 |
||||||
guid: f68d13699cf81a14a8e10092094b0f8d |
guid: 3b98756cd197195498b14b5e67935cbe |
||||||
NativeFormatImporter: |
NativeFormatImporter: |
||||||
externalObjects: {} |
externalObjects: {} |
||||||
mainObjectFileID: 11400000 |
mainObjectFileID: 11400000 |
@ -1,5 +1,5 @@ |
|||||||
fileFormatVersion: 2 |
fileFormatVersion: 2 |
||||||
guid: acae57c5032fca54685e664fb60872d6 |
guid: cdde19489f16dce429041c1d6e207864 |
||||||
NativeFormatImporter: |
NativeFormatImporter: |
||||||
externalObjects: {} |
externalObjects: {} |
||||||
mainObjectFileID: 11400000 |
mainObjectFileID: 11400000 |
@ -1,5 +1,5 @@ |
|||||||
fileFormatVersion: 2 |
fileFormatVersion: 2 |
||||||
guid: 031f6bc6b7884a54da49033ea61073a7 |
guid: 934bc10c06f0592419baf474987cc0a7 |
||||||
NativeFormatImporter: |
NativeFormatImporter: |
||||||
externalObjects: {} |
externalObjects: {} |
||||||
mainObjectFileID: 11400000 |
mainObjectFileID: 11400000 |
@ -0,0 +1,132 @@ |
|||||||
|
%YAML 1.1 |
||||||
|
%TAG !u! tag:unity3d.com,2011: |
||||||
|
--- !u!114 &11400000 |
||||||
|
MonoBehaviour: |
||||||
|
m_ObjectHideFlags: 0 |
||||||
|
m_CorrespondingSourceObject: {fileID: 0} |
||||||
|
m_PrefabInstance: {fileID: 0} |
||||||
|
m_PrefabAsset: {fileID: 0} |
||||||
|
m_GameObject: {fileID: 0} |
||||||
|
m_Enabled: 1 |
||||||
|
m_EditorHideFlags: 0 |
||||||
|
m_Script: {fileID: 11500000, guid: e8aa3c1560079b84cafbabe4bc0d2c8d, type: 3} |
||||||
|
m_Name: Hero_4 |
||||||
|
m_EditorClassIdentifier: |
||||||
|
serializationData: |
||||||
|
SerializedFormat: 2 |
||||||
|
SerializedBytes: |
||||||
|
ReferencedUnityObjects: [] |
||||||
|
SerializedBytesString: |
||||||
|
Prefab: {fileID: 0} |
||||||
|
PrefabModificationsReferencedUnityObjects: [] |
||||||
|
PrefabModifications: [] |
||||||
|
SerializationNodes: |
||||||
|
- Name: nihao |
||||||
|
Entry: 7 |
||||||
|
Data: 0|System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.Single, |
||||||
|
mscorlib]], mscorlib |
||||||
|
- Name: comparer |
||||||
|
Entry: 7 |
||||||
|
Data: 1|System.Collections.Generic.GenericEqualityComparer`1[[System.Int32, |
||||||
|
mscorlib]], mscorlib |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 12 |
||||||
|
Data: 2 |
||||||
|
- Name: |
||||||
|
Entry: 7 |
||||||
|
Data: |
||||||
|
- Name: $k |
||||||
|
Entry: 3 |
||||||
|
Data: 2 |
||||||
|
- Name: $v |
||||||
|
Entry: 4 |
||||||
|
Data: 2.3 |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 7 |
||||||
|
Data: |
||||||
|
- Name: $k |
||||||
|
Entry: 3 |
||||||
|
Data: 5 |
||||||
|
- Name: $v |
||||||
|
Entry: 4 |
||||||
|
Data: 4.6 |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 13 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: attribute |
||||||
|
Entry: 7 |
||||||
|
Data: 2|System.Collections.Generic.Dictionary`2[[Tools.ExcelResolver.Attribute, |
||||||
|
Assembly-CSharp],[System.Int32, mscorlib]], mscorlib |
||||||
|
- Name: comparer |
||||||
|
Entry: 7 |
||||||
|
Data: 3|System.Collections.Generic.EnumEqualityComparer`1[[Tools.ExcelResolver.Attribute, |
||||||
|
Assembly-CSharp]], mscorlib |
||||||
|
- Name: |
||||||
|
Entry: 12 |
||||||
|
Data: 0 |
||||||
|
- Name: |
||||||
|
Entry: 13 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 12 |
||||||
|
Data: 2 |
||||||
|
- Name: |
||||||
|
Entry: 7 |
||||||
|
Data: |
||||||
|
- Name: $k |
||||||
|
Entry: 3 |
||||||
|
Data: 0 |
||||||
|
- Name: $v |
||||||
|
Entry: 3 |
||||||
|
Data: 500 |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 7 |
||||||
|
Data: |
||||||
|
- Name: $k |
||||||
|
Entry: 3 |
||||||
|
Data: 1 |
||||||
|
- Name: $v |
||||||
|
Entry: 3 |
||||||
|
Data: 300 |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 13 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
id: 4 |
||||||
|
name: "\u9053\u58EB" |
||||||
|
icon: hero_1 |
||||||
|
has: 1 |
||||||
|
hp: 100 |
||||||
|
atk: 20 |
||||||
|
speed: 1.2 |
||||||
|
pos: {x: 0, y: 1, z: 9} |
||||||
|
ches: {x: 0, y: 1} |
||||||
|
attack_target: |
||||||
|
- {x: 1, y: 2} |
||||||
|
- {x: 1, y: 2} |
||||||
|
- {x: 1, y: 2} |
||||||
|
- {x: 1, y: 2} |
||||||
|
color: 1 |
@ -0,0 +1,8 @@ |
|||||||
|
fileFormatVersion: 2 |
||||||
|
guid: 4f9da86093404c6449ad6f21e1207cb0 |
||||||
|
NativeFormatImporter: |
||||||
|
externalObjects: {} |
||||||
|
mainObjectFileID: 11400000 |
||||||
|
userData: |
||||||
|
assetBundleName: |
||||||
|
assetBundleVariant: |
@ -0,0 +1,95 @@ |
|||||||
|
%YAML 1.1 |
||||||
|
%TAG !u! tag:unity3d.com,2011: |
||||||
|
--- !u!114 &11400000 |
||||||
|
MonoBehaviour: |
||||||
|
m_ObjectHideFlags: 0 |
||||||
|
m_CorrespondingSourceObject: {fileID: 0} |
||||||
|
m_PrefabInstance: {fileID: 0} |
||||||
|
m_PrefabAsset: {fileID: 0} |
||||||
|
m_GameObject: {fileID: 0} |
||||||
|
m_Enabled: 1 |
||||||
|
m_EditorHideFlags: 0 |
||||||
|
m_Script: {fileID: 11500000, guid: 0b8ec7437f31db141b138472f0c2a3aa, type: 3} |
||||||
|
m_Name: _HeroUtil |
||||||
|
m_EditorClassIdentifier: |
||||||
|
serializationData: |
||||||
|
SerializedFormat: 2 |
||||||
|
SerializedBytes: |
||||||
|
ReferencedUnityObjects: |
||||||
|
- {fileID: 11400000, guid: 3b98756cd197195498b14b5e67935cbe, type: 2} |
||||||
|
- {fileID: 11400000, guid: cdde19489f16dce429041c1d6e207864, type: 2} |
||||||
|
- {fileID: 11400000, guid: 934bc10c06f0592419baf474987cc0a7, type: 2} |
||||||
|
- {fileID: 11400000, guid: 4f9da86093404c6449ad6f21e1207cb0, type: 2} |
||||||
|
SerializedBytesString: |
||||||
|
Prefab: {fileID: 0} |
||||||
|
PrefabModificationsReferencedUnityObjects: [] |
||||||
|
PrefabModifications: [] |
||||||
|
SerializationNodes: |
||||||
|
- Name: Data |
||||||
|
Entry: 7 |
||||||
|
Data: 0|System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[Tools.ExcelResolver.Hero, |
||||||
|
Assembly-CSharp]], mscorlib |
||||||
|
- Name: comparer |
||||||
|
Entry: 7 |
||||||
|
Data: 1|System.Collections.Generic.GenericEqualityComparer`1[[System.Int32, |
||||||
|
mscorlib]], mscorlib |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 12 |
||||||
|
Data: 4 |
||||||
|
- Name: |
||||||
|
Entry: 7 |
||||||
|
Data: |
||||||
|
- Name: $k |
||||||
|
Entry: 3 |
||||||
|
Data: 1 |
||||||
|
- Name: $v |
||||||
|
Entry: 10 |
||||||
|
Data: 0 |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 7 |
||||||
|
Data: |
||||||
|
- Name: $k |
||||||
|
Entry: 3 |
||||||
|
Data: 2 |
||||||
|
- Name: $v |
||||||
|
Entry: 10 |
||||||
|
Data: 1 |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 7 |
||||||
|
Data: |
||||||
|
- Name: $k |
||||||
|
Entry: 3 |
||||||
|
Data: 3 |
||||||
|
- Name: $v |
||||||
|
Entry: 10 |
||||||
|
Data: 2 |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 7 |
||||||
|
Data: |
||||||
|
- Name: $k |
||||||
|
Entry: 3 |
||||||
|
Data: 4 |
||||||
|
- Name: $v |
||||||
|
Entry: 10 |
||||||
|
Data: 3 |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 13 |
||||||
|
Data: |
||||||
|
- Name: |
||||||
|
Entry: 8 |
||||||
|
Data: |
Loading…
Reference in new issue