/*
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
*/
using CsvHelper;
using System.Collections.Generic;
#nullable enable
namespace Yarn.Unity
{
public struct StringTableEntry
{
///
/// The language that the line is written in.
///
public string Language;
///
/// The line ID for this line. This value will be the same across all
/// localizations.
///
public string ID;
///
/// The text of this line, in the language specified by .
///
public string? Text;
///
/// The name of the Yarn script in which this line was originally found.
///
public string File;
///
/// The name of the node in which this line was originally found.
///
///
/// This node can be found in the file indicated by .
///
public string Node;
///
/// The line number in the file indicated by at which
/// the original version of this line can be found.
///
public string LineNumber;
///
/// A string used as part of a mechanism for checking if translated
/// versions of this string are out of date.
///
///
///
/// This field contains the first 8 characters of the SHA-256 hash of
/// the line's text as it appeared in the base localization CSV file.
///
///
/// When a new StringTableEntry is created in a localized CSV file for a
/// .yarn file, the Lock value is copied over from the base CSV file,
/// and used for the translated entry.
///
///
/// Because the base localization CSV is regenerated every time the
/// .yarn file is imported, the base localization Lock value will change
/// if a line's text changes. This means that if the base lock and
/// translated lock differ, the translated line is out of date, and
/// needs to be updated.
///
///
public string Lock;
///
/// A comment used to describe this line to translators.
///
public string Comment;
///
/// Initializes a new instance of the
/// struct, copying values from an existing instance.
///
/// The instance to copy values from.
public StringTableEntry(StringTableEntry s)
{
ID = s.ID;
Text = s.Text;
File = s.File;
Node = s.Node;
LineNumber = s.LineNumber;
Lock = s.Lock;
Comment = s.Comment;
Language = s.Language;
}
private static CsvHelper.Configuration.Configuration? CsvConfiguration;
private static CsvHelper.Configuration.Configuration GetConfiguration()
{
if (CsvConfiguration == null)
{
CsvConfiguration = new CsvHelper.Configuration.Configuration(System.Globalization.CultureInfo.InvariantCulture)
{
MemberTypes = CsvHelper.Configuration.MemberTypes.Fields,
};
}
return CsvConfiguration;
}
///
/// Reads comma-separated value data from ,
/// and produces a collection of structs.
///
/// A string containing CSV-formatted
/// data.
/// The parsed collection of
/// structs.
/// Thrown when an error occurs when
/// parsing the string.
public static IEnumerable ParseFromCSV(string sourceText)
{
try
{
using (var stringReader = new System.IO.StringReader(sourceText))
using (var csv = new CsvReader(stringReader, GetConfiguration()))
{
/*
Do the below instead of GetRecords due to incompatibility
with IL2CPP See more:
https://github.com/YarnSpinnerTool/YarnSpinner-Unity/issues/36#issuecomment-691489913
*/
var records = new List();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
// Fetch values; if they can't be found, they'll be
// defaults.
csv.TryGetField("language", out var language);
csv.TryGetField("lock", out var lockString);
csv.TryGetField("comment", out var comment);
csv.TryGetField("id", out var id);
csv.TryGetField("text", out var text);
csv.TryGetField("file", out var file);
csv.TryGetField("node", out var node);
csv.TryGetField("lineNumber", out var lineNumber);
var record = new StringTableEntry
{
Language = language ?? string.Empty,
ID = id ?? string.Empty,
Text = text ?? string.Empty,
File = file ?? string.Empty,
Node = node ?? string.Empty,
LineNumber = lineNumber ?? string.Empty,
Lock = lockString ?? string.Empty,
Comment = comment ?? string.Empty,
};
records.Add(record);
}
return records;
}
}
catch (CsvHelperException e)
{
throw new System.ArgumentException($"Error reading CSV file: {e}");
}
}
///
/// Creates a CSV-formatted string containing data from .
///
/// The values to
/// generate the spreadsheet from.
/// A string containing CSV-formatted data.
public static string CreateCSV(IEnumerable entries)
{
using (var textWriter = new System.IO.StringWriter())
{
// Generate the localised .csv file
// Use the invariant culture when writing the CSV
var csv = new CsvHelper.CsvWriter(
textWriter, // write into this stream
GetConfiguration() // use this configuration
);
var fieldNames = new[] {
"language",
"id",
"text",
"file",
"node",
"lineNumber",
"lock",
"comment",
};
foreach (var field in fieldNames)
{
csv.WriteField(field);
}
csv.NextRecord();
foreach (var entry in entries)
{
var values = new[] {
entry.Language,
entry.ID,
entry.Text,
entry.File,
entry.Node,
entry.LineNumber,
entry.Lock,
entry.Comment,
};
foreach (var value in values)
{
csv.WriteField(value);
}
csv.NextRecord();
}
return textWriter.ToString();
}
}
///
public override string ToString()
{
return $"StringTableEntry: lang={Language} id={ID} text=\"{Text}\" file={File} node={Node} line={LineNumber} lock={Lock} comment={Comment}";
}
///
public override bool Equals(object obj)
{
return obj is StringTableEntry entry &&
Language == entry.Language &&
ID == entry.ID &&
Text == entry.Text &&
File == entry.File &&
Node == entry.Node &&
LineNumber == entry.LineNumber &&
Lock == entry.Lock &&
Comment == entry.Comment;
}
///
public override int GetHashCode()
{
return
Language.GetHashCode() ^
ID.GetHashCode() ^
(Text?.GetHashCode() ?? 1) ^
File.GetHashCode() ^
Node.GetHashCode() ^
LineNumber.GetHashCode() ^
Lock.GetHashCode() ^
Comment.GetHashCode();
}
}
}