Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move StringTable CSV parser to a dedicated class
- Loading branch information
1 parent
61be174
commit d921931
Showing
4 changed files
with
129 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
// Project: Daggerfall Unity | ||
// Copyright: Copyright (C) 2009-2022 Daggerfall Workshop | ||
// Web Site: http://www.dfworkshop.net | ||
// License: MIT License (http://www.opensource.org/licenses/mit-license.php) | ||
// Source Code: https://github.com/Interkarma/daggerfall-unity | ||
// Original Author: Gavin Clayton (interkarma@dfworkshop.net) | ||
// Contributors: | ||
// | ||
// Notes: | ||
// | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text.RegularExpressions; | ||
using UnityEngine; | ||
|
||
/// <summary> | ||
/// Basic CSV parser for Windows-styled CSV files containing StringTable Key and Value columns only. | ||
/// This parser is intended only for importing string tables for localization. It is not a general purpose CSV parser. | ||
/// </summary> | ||
public class StringTableCSVParser | ||
{ | ||
const string csvExt = ".csv"; | ||
const string textString = "Text"; | ||
const string keyString = "Key"; | ||
const string valueString = "Value"; | ||
|
||
/// <summary> | ||
/// Loads a CSV patch file for in-game text. | ||
/// At this time, string patch files must be in the StreamingAssets/Text folder to load. | ||
/// TODO: Support loading CSV file from .dfmod. | ||
/// </summary> | ||
/// <param name="filename">Filename of StringTable CSV file.</param> | ||
/// <returns>KeyValuePair for each row if successful, otherwise null.</returns> | ||
public static KeyValuePair<string, string>[] Load(string filename) | ||
{ | ||
string csvText = null; | ||
|
||
if (!filename.EndsWith(csvExt)) | ||
filename += csvExt; | ||
|
||
// Load patch file if present | ||
string path = Path.Combine(Application.streamingAssetsPath, textString, filename); | ||
if (File.Exists(path)) | ||
{ | ||
csvText = ReadAllText(path); | ||
if (string.IsNullOrEmpty(csvText)) | ||
return null; | ||
} | ||
else | ||
{ | ||
return null; | ||
} | ||
|
||
// Parse into CSV rows | ||
KeyValuePair<string, string>[] rows = null; | ||
try | ||
{ | ||
rows = ParseCSVRows(csvText); | ||
} | ||
catch (Exception ex) | ||
{ | ||
Debug.LogErrorFormat("Could not parse CSV from file '{0}'. Exception message: {1}", filename, ex.Message); | ||
return null; | ||
} | ||
|
||
return rows; | ||
} | ||
|
||
/// <summary> | ||
/// Parse source CSV data into key/value pairs separated by comma character. | ||
/// Source CSV file must have only two columns for Key and Value. | ||
/// </summary> | ||
/// <param name="csvText">Source CSV data.</param> | ||
/// <returns>KeyValuePair for each row.</returns> | ||
static KeyValuePair<string, string>[] ParseCSVRows(string csvText) | ||
{ | ||
// Regex pattern from https://gist.github.com/awwsmm/886ac0ce0cef517ad7092915f708175f | ||
const string linePattern = "(?:,|\\n|^)(\"(?:(?:\"\")*[^\"]*)*\"|[^\",\\n]*|(?:\\n|$))"; | ||
|
||
// Split source CSV based on regex matches | ||
char[] trimChars = { '\r', '\n', '\"', ',' }; | ||
List<KeyValuePair<string, string>> rows = new List<KeyValuePair<string, string>>(); | ||
string[] matches = (from Match m in Regex.Matches(csvText, linePattern, RegexOptions.ExplicitCapture) select m.Groups[0].Value).ToArray(); | ||
int pos = 0; | ||
while (pos < matches.Length) | ||
{ | ||
if (pos + 1 == matches.Length) | ||
{ | ||
// Exit if no valid pair at end of csv (likely an empty line at end of source data) | ||
break; | ||
} | ||
string key = matches[pos++].Trim(trimChars); | ||
string value = matches[pos++].Trim(trimChars); | ||
KeyValuePair<string, string> kvp = new KeyValuePair<string, string>(key, value); | ||
rows.Add(kvp); | ||
} | ||
|
||
return rows.ToArray(); | ||
} | ||
|
||
/// <summary> | ||
/// Read text from a file in read-only access mode. | ||
/// Allows modder to keep CSV open in Excel without throwing exception in game. | ||
/// </summary> | ||
/// <param name="file">File to open.</param> | ||
/// <returns>All text read from file.</returns> | ||
static string ReadAllText(string file) | ||
{ | ||
using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) | ||
using (var textReader = new StreamReader(fileStream)) | ||
return textReader.ReadToEnd(); | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters