Skip to content

Commit

Permalink
Specify encoding when reading/writing
Browse files Browse the repository at this point in the history
  • Loading branch information
matigramirez committed Sep 25, 2023
1 parent e8fa45d commit b402825
Show file tree
Hide file tree
Showing 23 changed files with 306 additions and 198 deletions.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,15 @@ might not work with them.
#### From a standalone file

```cs
// Read file
// Read file with default episode (EP5) and encoding (ASCII)
var svmap = Reader.ReadFromFile<Svmap>("0.svmap");

// Read file with a specific episode and default encoding
var svmap = Reader.ReadFromFile<Svmap>("0.svmap", Episode.Ep6);

// Read file with a specific episode and a specific encoding
var windows1252Encoding = CodePagesEncodingProvider.Instance.GetEncoding(1252);
var svmap = Reader.ReadFromFile<Svmap>("0.svmap", Episode.Ep6, windows1252Encoding);
```

#### From data.saf
Expand Down Expand Up @@ -122,14 +129,28 @@ All of the Episode 8 `BinarySData` formats have `CSV` support.
var item = Item.ReadFromCsv("Item.csv");
```

### Encoding

When reading files, the default encoding is `ASCII`. If you want to read a file with a different encoding, you can
specify it as a parameter when calling the ReadFromFile/Json/Csv methods.

### Writing

#### Writing a standalone file

After modifying the file, you can save it in its original format by calling the `Write` method
After modifying the file, you can save it in its original format by calling the `Write` method. If you specified
the episode and encoding when reading the file, you don't need to specify them again when writing it.

```cs
// Write file with previously defined episode and encoding (defined when reading the file)
svmap.Write("0_modified.svmap");

// Write file with a specific episode and default encoding
svmap.Write("0_modified.svmap", Episode.Ep6);

// Write file with a specific episode and a specific encoding
var windows1252Encoding = CodePagesEncodingProvider.Instance.GetEncoding(1252);
svmap.Write("0_modified.svmap", Episode.Ep6, windows1252Encoding);
```

#### Writing as JSON
Expand All @@ -150,6 +171,11 @@ All of the Episode 8 `BinarySData` formats have `CSV` support.
item.WriteCsv("Item.csv")
```

### Encoding

When writing files, the default encoding is `ASCII`. If you want to write a file with a different encoding, you can
specify it as a parameter when calling the `Write`, `WriteJson` and `WriteCsv` methods.

```cs

## Samples
Expand Down
8 changes: 2 additions & 6 deletions src/Parsec/Attributes/FixedLengthStringAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
using System.Text;

namespace Parsec.Attributes;
namespace Parsec.Attributes;

[AttributeUsage(AttributeTargets.Property)]
public class FixedLengthStringAttribute : Attribute
{
public int Length { get; set; }
public Encoding Encoding { get; set; } = Encoding.ASCII;
public bool IncludeStringTerminator { get; set; }
public bool IsString256 { get; set; }
public string Suffix { get; set; }
Expand All @@ -21,9 +18,8 @@ public FixedLengthStringAttribute(bool isString256)
IsString256 = isString256;
}

public FixedLengthStringAttribute(Encoding encoding, int length, bool removeStringTerminator)
public FixedLengthStringAttribute(int length, bool removeStringTerminator)
{
Encoding = encoding;
Length = length;
IncludeStringTerminator = removeStringTerminator;
}
Expand Down
16 changes: 1 addition & 15 deletions src/Parsec/Attributes/LengthPrefixedStringAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using System.Text;

namespace Parsec.Attributes;
namespace Parsec.Attributes;

[AttributeUsage(AttributeTargets.Property)]
public class LengthPrefixedStringAttribute : Attribute
{
public Encoding Encoding { get; set; } = Encoding.ASCII;
public bool IncludeStringTerminator { get; set; }

public string Suffix { get; set; }
Expand All @@ -14,19 +11,8 @@ public LengthPrefixedStringAttribute()
{
}

public LengthPrefixedStringAttribute(Encoding encoding)
{
Encoding = encoding;
}

public LengthPrefixedStringAttribute(bool includeStringTerminator)
{
IncludeStringTerminator = includeStringTerminator;
}

public LengthPrefixedStringAttribute(Encoding encoding, bool includeStringTerminator)
{
Encoding = encoding;
IncludeStringTerminator = includeStringTerminator;
}
}
7 changes: 5 additions & 2 deletions src/Parsec/Common/ICsv.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
namespace Parsec.Common;
using System.Text;

namespace Parsec.Common;

public interface ICsv
{
/// <summary>
/// Exports the file in csv format
/// </summary>
/// <param name="outputPath">Export file path</param>
void WriteCsv(string outputPath);
/// <param name="encoding">File encoding</param>
void WriteCsv(string outputPath, Encoding encoding = null);
}
2 changes: 0 additions & 2 deletions src/Parsec/Extensions/BinaryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,8 @@ public static IEnumerable<byte> GetLengthPrefixedBytes(this string str, Encoding
var buffer = new List<byte>();

string finalStr = includeStringTerminator ? str + '\0' : str;

buffer.AddRange(finalStr.Length.GetBytes());
buffer.AddRange(finalStr.GetBytes(encoding));

return buffer;
}

Expand Down
11 changes: 6 additions & 5 deletions src/Parsec/Helpers/FileHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.IO;
using System.Linq;
using Parsec.Readers;
using System.Text;

namespace Parsec.Helpers;

Expand All @@ -17,9 +15,12 @@ public static class FileHelper
/// </summary>
/// <param name="path">File path</param>
/// <param name="content">The file content</param>
/// <param name="encoding">File encoding</param>
/// <param name="backupIfExists">Makes a backup of the file if it already existed</param>
public static bool WriteFile(string path, string content, bool backupIfExists = false)
public static bool WriteFile(string path, string content, Encoding encoding = null, bool backupIfExists = false)
{
encoding ??= Encoding.ASCII;

if (backupIfExists && FileExists(path))
{
DeleteFile($"{path}.bak");
Expand All @@ -36,7 +37,7 @@ public static bool WriteFile(string path, string content, bool backupIfExists =

try
{
File.WriteAllText(path, content);
File.WriteAllText(path, content, encoding);
return true;
}
catch
Expand Down
6 changes: 3 additions & 3 deletions src/Parsec/Parsec.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CsvHelper" Version="29.0.0" />
<PackageReference Include="CsvHelper" Version="30.0.1" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
</ItemGroup>

</Project>
72 changes: 43 additions & 29 deletions src/Parsec/Readers/Reader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,56 @@ public static class Reader
/// </summary>
/// <param name="path">File path</param>
/// <param name="episode">File episode</param>
/// <param name="encoding">File encoding</param>
/// <typeparam name="T">Shaiya File Format Type</typeparam>
/// <returns>T instance</returns>
public static T ReadFromFile<T>(string path, Episode episode = Episode.EP5) where T : FileBase, new() =>
FileBase.ReadFromFile<T>(path, episode);
public static T ReadFromFile<T>(string path, Episode episode = Episode.EP5, Encoding encoding = null) where T : FileBase, new() =>
FileBase.ReadFromFile<T>(path, episode, encoding);

/// <summary>
/// Reads a shaiya file format from a file
/// </summary>
/// <param name="path">File path</param>
/// <param name="episode">File episode</param>
/// <param name="encoding">File encoding</param>
/// <typeparam name="T">Shaiya File Format Type</typeparam>
public static Task<T> ReadFromFileAsync<T>(string path, Episode episode = Episode.EP5) where T : FileBase, new() =>
Task.FromResult(ReadFromFile<T>(path, episode));
public static Task<T> ReadFromFileAsync<T>(string path, Episode episode = Episode.EP5, Encoding encoding = null)
where T : FileBase, new() =>
Task.FromResult(ReadFromFile<T>(path, episode, encoding));

/// <summary>
/// Reads the shaiya file format from a file
/// </summary>
/// <param name="path">File path</param>
/// <param name="type">FileBase child type to be read</param>
/// <param name="episode">File episode</param>
/// <param name="encoding">File encoding</param>
/// <returns>FileBase instance</returns>
public static FileBase ReadFromFile(string path, Type type, Episode episode = Episode.EP5) =>
FileBase.ReadFromFile(path, type, episode);
public static FileBase ReadFromFile(string path, Type type, Episode episode = Episode.EP5, Encoding encoding = null) =>
FileBase.ReadFromFile(path, type, episode, encoding);

/// <summary>
/// Reads the shaiya file format from a file
/// </summary>
/// <param name="path">File path</param>
/// <param name="type">FileBase child type to be read</param>
/// <param name="episode">File episode</param>
public static Task<FileBase> ReadFromFileAsync(string path, Type type, Episode episode = Episode.EP5) =>
Task.FromResult(ReadFromFile(path, type, episode));
/// <param name="encoding">File encoding</param>
public static Task<FileBase> ReadFromFileAsync(string path, Type type, Episode episode = Episode.EP5, Encoding encoding = null) =>
Task.FromResult(ReadFromFile(path, type, episode, encoding));

/// <summary>
/// Reads a shaiya file format from a buffer (byte array)
/// </summary>
/// <param name="name">File name</param>
/// <param name="buffer">File buffer</param>
/// <param name="episode">File episode</param>
/// <param name="encoding">File encoding</param>
/// <typeparam name="T">Shaiya File Format Type</typeparam>
/// <returns>T instance</returns>
public static T ReadFromBuffer<T>(string name, byte[] buffer, Episode episode = Episode.EP5) where T : FileBase, new() =>
FileBase.ReadFromBuffer<T>(name, buffer, episode);
public static T ReadFromBuffer<T>(string name, byte[] buffer, Episode episode = Episode.EP5, Encoding encoding = null)
where T : FileBase, new() =>
FileBase.ReadFromBuffer<T>(name, buffer, episode, encoding);


/// <summary>
Expand All @@ -66,8 +73,10 @@ public static Task<FileBase> ReadFromFileAsync(string path, Type type, Episode e
/// <param name="name">File name</param>
/// <param name="buffer">File buffer</param>
/// <param name="episode">File episode</param>
/// <param name="encoding">File encoding</param>
/// <typeparam name="T">Shaiya File Format Type</typeparam>
public static Task<T> ReadFromBufferAsync<T>(string name, byte[] buffer, Episode episode = Episode.EP5) where T : FileBase, new() =>
public static Task<T> ReadFromBufferAsync<T>(string name, byte[] buffer, Episode episode = Episode.EP5, Encoding encoding = null)
where T : FileBase, new() =>
Task.FromResult(ReadFromBuffer<T>(name, buffer, episode));

/// <summary>
Expand All @@ -77,9 +86,10 @@ public static Task<FileBase> ReadFromFileAsync(string path, Type type, Episode e
/// <param name="buffer">File buffer</param>
/// <param name="type">FileBase child type to be read</param>
/// <param name="episode">File episode</param>
/// <param name="encoding">File encoding</param>
/// <returns>FileBase instance</returns>
public static FileBase ReadFromBuffer(string name, byte[] buffer, Type type, Episode episode = Episode.EP5) =>
FileBase.ReadFromBuffer(name, buffer, type);
public static FileBase ReadFromBuffer(string name, byte[] buffer, Type type, Episode episode = Episode.EP5, Encoding encoding = null) =>
FileBase.ReadFromBuffer(name, buffer, type, episode, encoding);

/// <summary>
/// Reads the shaiya file format from a buffer (byte array)
Expand All @@ -88,8 +98,10 @@ public static FileBase ReadFromBuffer(string name, byte[] buffer, Type type, Epi
/// <param name="buffer">File buffer</param>
/// <param name="type">FileBase child type to be read</param>
/// <param name="episode">File episode</param>
public static Task<FileBase> ReadFromBufferAsync(string name, byte[] buffer, Type type, Episode episode = Episode.EP5) =>
Task.FromResult(ReadFromBuffer(name, buffer, type));
/// <param name="encoding">File encoding</param>
public static Task<FileBase> ReadFromBufferAsync(string name, byte[] buffer, Type type, Episode episode = Episode.EP5,
Encoding encoding = null) =>
Task.FromResult(ReadFromBuffer(name, buffer, type, episode, encoding));

/// <summary>
/// Reads a shaiya file format from a json file
Expand Down Expand Up @@ -128,14 +140,11 @@ public static FileBase ReadFromJsonFile(string path, Type type, Encoding encodin
if (Path.GetExtension(path) != ".json")
throw new FileLoadException("The provided file to deserialize must be a valid json file");

// Set default encoding
encoding ??= Encoding.ASCII;

// Read json file content
string jsonContent = File.ReadAllText(path, encoding);
// Deserialize into FileBase
var deserializedObject = (FileBase)JsonConvert.DeserializeObject(jsonContent, type);
// Get file name without the ".json" extension

string fileNameWithoutJsonExtension = Path.GetFileNameWithoutExtension(path);

if (deserializedObject == null)
Expand Down Expand Up @@ -192,10 +201,8 @@ public static FileBase ReadFromJson(string name, string jsonText, Type type, Enc
if (!type.GetBaseClassesAndInterfaces().Contains(typeof(FileBase)))
throw new ArgumentException("Type must be a child of FileBase");

// Set default encoding
encoding ??= Encoding.ASCII;

// Deserialize into FileBase
var deserializedObject = (FileBase)JsonConvert.DeserializeObject(jsonText, type);
if (deserializedObject == null)
return null;
Expand All @@ -221,18 +228,22 @@ public static Task<FileBase> ReadFromJsonAsync(string name, string jsonText, Typ
/// <param name="data"><see cref="Data"/> instance</param>
/// <param name="file"><see cref="SFile"/> instance</param>
/// <param name="episode">File episode</param>
/// <param name="encoding">File encoding</param>
/// <returns>FileBase instance</returns>
public static T ReadFromData<T>(Data data, SFile file, Episode episode = Episode.EP5) where T : FileBase, new() =>
FileBase.ReadFromData<T>(data, file, episode);
public static T ReadFromData<T>(Data data, SFile file, Episode episode = Episode.EP5, Encoding encoding = null)
where T : FileBase, new() =>
FileBase.ReadFromData<T>(data, file, episode, encoding);

/// <summary>
/// Reads the shaiya file format from a buffer (byte array) within a <see cref="Data"/> instance
/// </summary>
/// <param name="data"><see cref="Data"/> instance</param>
/// <param name="file"><see cref="SFile"/> instance</param>
/// <param name="episode">File episode</param>
public static Task<T> ReadFromDataAsync<T>(Data data, SFile file, Episode episode = Episode.EP5) where T : FileBase, new() =>
Task.FromResult(ReadFromData<T>(data, file, episode));
/// <param name="encoding">File encoding</param>
public static Task<T> ReadFromDataAsync<T>(Data data, SFile file, Episode episode = Episode.EP5, Encoding encoding = null)
where T : FileBase, new() =>
Task.FromResult(ReadFromData<T>(data, file, episode, encoding));

/// <summary>
/// Reads the shaiya file format from a buffer (byte array) within a <see cref="Data"/> instance
Expand All @@ -241,9 +252,10 @@ public static Task<FileBase> ReadFromJsonAsync(string name, string jsonText, Typ
/// <param name="file"><see cref="SFile"/> instance</param>
/// <param name="type">FileBase child type to be read</param>
/// <param name="episode">File episode</param>
/// <param name="encoding">File encoding</param>
/// <returns>FileBase instance</returns>
public static FileBase ReadFromData(Data data, SFile file, Type type, Episode episode = Episode.EP5) =>
FileBase.ReadFromData(data, file, type, episode);
public static FileBase ReadFromData(Data data, SFile file, Type type, Episode episode = Episode.EP5, Encoding encoding = null) =>
FileBase.ReadFromData(data, file, type, episode, encoding);

/// <summary>
/// Reads the shaiya file format from a buffer (byte array) within a <see cref="Data"/> instance
Expand All @@ -252,6 +264,8 @@ public static FileBase ReadFromData(Data data, SFile file, Type type, Episode ep
/// <param name="file"><see cref="SFile"/> instance</param>
/// <param name="type">FileBase child type to be read</param>
/// <param name="episode">File episode</param>
public static Task<FileBase> ReadFromDataAsync(Data data, SFile file, Type type, Episode episode = Episode.EP5) =>
Task.FromResult(ReadFromData(data, file, type, episode));
/// <param name="encoding">File encoding</param>
public static Task<FileBase> ReadFromDataAsync(Data data, SFile file, Type type, Episode episode = Episode.EP5,
Encoding encoding = null) =>
Task.FromResult(ReadFromData(data, file, type, episode, encoding));
}
Loading

0 comments on commit b402825

Please sign in to comment.