diff --git a/Directory.Build.props b/Directory.Build.props index 4e5f50ca..412fff75 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,47 +3,37 @@ $([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)')) - $([MSBuild]::NormalizeDirectory('$(ShimakazeSdk_RootFolder)', 'app')) - $([MSBuild]::NormalizeDirectory('$(ShimakazeSdk_RootFolder)', 'lib')) + $([MSBuild]::NormalizeDirectory('$(ShimakazeSdk_RootFolder)', 'src')) $([MSBuild]::NormalizeDirectory('$(ShimakazeSdk_RootFolder)', 'test')) - Application - Library - UnitTest + False + False - 0.0.1-preview.12-patch.1 + True + True - - - net8.0 + + + netstandard2.0;netstandard2.1;net6.0;net8.0 + latest enable enable True - False + True True snupkg True True true - - - - - all - runtime; build; native; contentfiles; analyzers - - + $(MSBuildProjectName.Replace('.Abstractions', '')) - - - + True + latest-recommended - - $(MSBuildProjectDirectory)\Assets\logo.png - $(ShimakazeSdk_RootFolder)assets\logo.png - $(MSBuildProjectDirectory)\ReadMe.md - $(ShimakazeSdk_RootFolder)ReadMe.md + + 1701;1702;8601;8604;8767 + annotations Shimakaze Project Copyright © 2020 - $([System.DateTime]::Now.ToString("yyyy")) $(Authors) @@ -55,67 +45,65 @@ MIT True $(ShimakazeSdk_RootFolder)nupkg + + $(MSBuildProjectDirectory)\Assets\logo.png + $(ShimakazeSdk_RootFolder)assets\logo.png + $(MSBuildProjectDirectory)\ReadMe.md + $(ShimakazeSdk_RootFolder)ReadMe.md - - - + + + - - - Exe - true - + + + all + runtime; build; native; contentfiles; analyzers + + - - + + + - - - $(MSBuildProjectName.Replace('.Tests', '')) + + + net8.0 + false + true + False False - False False True lcov Obsolete,GeneratedCodeAttribute,ExcludeFromCodeCoverageAttribute - - - - - - all - runtime; build; native; contentfiles; analyzers - - + + + + + + all runtime; build; native; contentfiles; analyzers - + - - + + - + - + diff --git a/Directory.Build.targets b/Directory.Build.targets index dc803871..f347dfba 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,10 +1,11 @@ - - - + @@ -23,15 +24,22 @@ - + + + + + - - - - - - - diff --git a/app/Shimakaze.Sdk.Vpl.Editor/Shimakaze.Sdk.Vpl.Editor.csproj b/app/Shimakaze.Sdk.Vpl.Editor/Shimakaze.Sdk.Vpl.Editor.csproj deleted file mode 100644 index 31f5ae1a..00000000 --- a/app/Shimakaze.Sdk.Vpl.Editor/Shimakaze.Sdk.Vpl.Editor.csproj +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/lib/ReadMe.md b/lib/ReadMe.md deleted file mode 100644 index 4533aa79..00000000 --- a/lib/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -# Sources Code - -这里包含项目的源代码。 -The source code of the project is included here. diff --git a/lib/Shimakaze.Sdk.Build/Sdk/tasks.props b/lib/Shimakaze.Sdk.Build/Sdk/tasks.props deleted file mode 100644 index 8cd85284..00000000 --- a/lib/Shimakaze.Sdk.Build/Sdk/tasks.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/lib/Shimakaze.Sdk.Build/Shimakaze.Sdk.Build.csproj b/lib/Shimakaze.Sdk.Build/Shimakaze.Sdk.Build.csproj deleted file mode 100644 index 719b6a7d..00000000 --- a/lib/Shimakaze.Sdk.Build/Shimakaze.Sdk.Build.csproj +++ /dev/null @@ -1,39 +0,0 @@ - - - - Shimakaze.Sdk.RA2 - Build your RA2 Module Project by MSBuild - MSBuild MSBuildSdk RA2Mod ShimakazeProject - MSBuildSdk - $(NoWarn);NU5128; - True - PackAllDependencies - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib/Shimakaze.Sdk.JsonRPC.Server/JsonRPCHostedService.cs b/lib/Shimakaze.Sdk.JsonRPC.Server/JsonRPCHostedService.cs deleted file mode 100644 index e092a660..00000000 --- a/lib/Shimakaze.Sdk.JsonRPC.Server/JsonRPCHostedService.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Immutable; - -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -using StreamJsonRpc; - -namespace Shimakaze.Sdk.JsonRPC.Server; - -/// -/// JsonRPCHostedService -/// -public sealed class JsonRPCHostedService : IHostedService -{ - private readonly JsonRpc _jsonRpc; - private readonly ImmutableArray _methods; - private readonly IServiceProvider _provider; - private readonly ILogger? _logger; - - /// - /// - /// - /// - /// - /// - public JsonRPCHostedService(JsonRPCHostedServiceOptions options, IServiceProvider serviceProvider, ILogger? logger = null) - { - _jsonRpc = options.JsonRpc; - _methods = options.Targets; - _provider = serviceProvider; - _logger = logger; - } - - /// - public Task StartAsync(CancellationToken cancellationToken) - { - foreach (var method in _methods) - { - var isEvent = method.Method.ReturnType == typeof(void); - _logger?.LogDebug("Find {type}: {path}", isEvent ? "Event" : "Method", method.Route); - _jsonRpc.AddLocalRpcMethod(method.Route, method.Method, _provider.GetService(method.Type)); - } - - return Task.Run(_jsonRpc.StartListening, cancellationToken); - } - - /// - public Task StopAsync(CancellationToken cancellationToken) - { - _logger?.LogInformation("See you next time!"); - return Task.CompletedTask; - } -} diff --git a/lib/Shimakaze.Sdk.Preprocessor/Commands/ConditionCommand.cs b/lib/Shimakaze.Sdk.Preprocessor/Commands/ConditionCommand.cs deleted file mode 100644 index fccc0e7c..00000000 --- a/lib/Shimakaze.Sdk.Preprocessor/Commands/ConditionCommand.cs +++ /dev/null @@ -1,159 +0,0 @@ -using Microsoft.Extensions.Logging; - -using Shimakaze.Sdk.Preprocessor.Kernel; - -namespace Shimakaze.Sdk.Preprocessor.Commands; - -/// -/// Condition Commands if elif else endif -/// -public sealed class ConditionalCommand -{ - private readonly Engine _engine; - private readonly Logger? _logger; - - /// - /// - /// - /// - /// - public ConditionalCommand(Engine engine, Logger? logger = null) - { - _engine = engine; - _logger = logger; - } - - /// - /// #if condition - /// - /// - [Command] - public void If([Param(@".*")] string condition) - { - if (string.IsNullOrWhiteSpace(condition)) - throw new ArgumentNullException(nameof(condition)); - - var conditionStack = _engine.GetOrNew("ConditionStack", () => new Stack()); - var conditionParser = _engine.GetOrNew("ConditionParser", () => new ConditionParser(_engine)); - - _engine.CanWritable = conditionParser.Parse(condition); - _logger?.LogDebug("If Condition: \"{condition}\" and computed value is: \"{value}\" ", condition, _engine.CanWritable); - - conditionStack.Push(new ConditionStatus(_engine.CanWritable, condition, "if")); - _logger?.LogDebug("Push Stack: If"); - } - - /// - /// #elif condition - /// - /// - [Command] - public void Elif([Param(@".*")] string condition) - { - if (string.IsNullOrWhiteSpace(condition)) - throw new ArgumentNullException(nameof(condition)); - - var conditionStack = _engine.GetOrNew("ConditionStack", () => new Stack()); - var conditionParser = _engine.GetOrNew("ConditionParser", () => new ConditionParser(_engine)); - - var lastStatus = conditionStack.Pop(); - _logger?.LogDebug("Pop Stack: {tag}", lastStatus.Tag); - - _engine.CanWritable = false; - if (lastStatus.IsMatched) - { - _logger?.LogDebug("ElIf is NOT Actived."); - } - else - { - _engine.CanWritable = conditionParser.Parse(condition); - _logger?.LogDebug("ElIf Condition: \"{condition}\" and computed value is: \"{value}\" ", condition, _engine.CanWritable); - } - - conditionStack.Push(new(_engine.CanWritable, condition, "elif")); - _logger?.LogDebug("Push Stack: Elif"); - } - - /// - /// #else - /// - [Command] - public void Else() - { - var conditionStack = _engine.GetOrNew("ConditionStack", () => new Stack()); - - var lastStatus = conditionStack.Pop(); - _logger?.LogDebug("Pop Stack: {tag}", lastStatus.Tag); - - _engine.CanWritable = false; - - if (lastStatus.IsMatched) - { - _logger?.LogDebug("Else is NOT Actived."); - } - else - { - _engine.CanWritable = true; - _logger?.LogDebug("Else computed value is: \"{value}\" ", _engine.CanWritable); - } - - conditionStack.Push(new(_engine.CanWritable, string.Empty, "else")); - _logger?.LogDebug("Push Stack: Else"); - } - - /// - /// #endif - /// - [Command] - public void Endif() - { - var conditionStack = _engine.GetOrNew("ConditionStack", () => new Stack()); - - var lastStatus = conditionStack.Pop(); - _logger?.LogDebug("Pop Stack: {tag}", lastStatus.Tag); - - _engine.CanWritable = true; - _logger?.LogDebug("EndIf Actived!"); - } -} - -file sealed class ConditionParser -{ - private readonly Engine _engine; - - public ConditionParser(Engine engine) => _engine = engine; - - public bool Parse(string condition) - { - condition = condition.Trim(); - switch (condition.ToLowerInvariant()) - { - case "true": return true; - case "false": return false; - default: break; - } - - var defines = _engine.GetOrNew("Defines", () => new HashSet()); - - return defines.Any(i => i.Equals(condition, StringComparison.OrdinalIgnoreCase)) - || (condition.Contains("||") - ? OR(condition) - : condition.Contains("&&") - ? AND(condition) - : condition.TrimStart().StartsWith('!') && NOT(condition)); - } - - private bool AND(string condition) => condition.Trim().Split("&&").All(Parse); - - private bool NOT(string condition) - { - condition = condition.Trim(); - return condition.StartsWith('!') - ? !Parse(condition[1..]) - : Parse(condition); - } - - private bool OR(string condition) => condition.Trim().Split("||").Any(Parse); -} - -file sealed record ConditionStatus(bool IsMatched, string Condition, string Tag); \ No newline at end of file diff --git a/lib/Shimakaze.Sdk.Preprocessor/Commands/DefineCommand.cs b/lib/Shimakaze.Sdk.Preprocessor/Commands/DefineCommand.cs deleted file mode 100644 index 30b972ee..00000000 --- a/lib/Shimakaze.Sdk.Preprocessor/Commands/DefineCommand.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Microsoft.Extensions.Logging; - -using Shimakaze.Sdk.Preprocessor.Kernel; - -namespace Shimakaze.Sdk.Preprocessor.Commands; -/// -/// Define Commands: define undef -/// -public sealed class DefineCommand -{ - private readonly Engine _engine; - private readonly Logger? _logger; - - /// - /// - /// - /// - /// - public DefineCommand(Engine engine, Logger? logger = null) - { - _engine = engine; - _logger = logger; - } - - /// - /// #define identifier - /// - /// - [Command] - public void Define(string identifier) - { - var defines = _engine.GetOrNew("Defines", () => new HashSet()); - defines.Add(identifier); - _logger?.LogDebug("Define {identifier}", identifier); - } - - /// - /// #undef identifier - /// - /// - [Command] - public void Undef(string identifier) - { - var defines = _engine.GetOrNew("Defines", () => new HashSet()); - defines.Remove(identifier); - _logger?.LogDebug("Undefine {identifier}", identifier); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk.Preprocessor/Commands/RegionCommand.cs b/lib/Shimakaze.Sdk.Preprocessor/Commands/RegionCommand.cs deleted file mode 100644 index 0a9aa7b8..00000000 --- a/lib/Shimakaze.Sdk.Preprocessor/Commands/RegionCommand.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -using Microsoft.Extensions.Logging; - -using Shimakaze.Sdk.Preprocessor.Kernel; - -namespace Shimakaze.Sdk.Preprocessor.Commands; - -/// -/// Region Commands: region endregion -/// -[ExcludeFromCodeCoverage] -public sealed class RegionCommand -{ - private readonly Logger? _logger; - - /// - /// - /// - /// - public RegionCommand(Logger? logger = null) => _logger = logger; - - /// - /// #region - /// - [Command] - public void Region() => _logger?.LogDebug("Region"); - - /// - /// #endregion - /// - [Command] - public void Endregion() => _logger?.LogDebug("Endregion"); -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk.Preprocessor/Commands/TypeCommand.cs b/lib/Shimakaze.Sdk.Preprocessor/Commands/TypeCommand.cs deleted file mode 100644 index 8bc8ee4f..00000000 --- a/lib/Shimakaze.Sdk.Preprocessor/Commands/TypeCommand.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -using Microsoft.Extensions.Logging; - -using Shimakaze.Sdk.Preprocessor.Kernel; - -namespace Shimakaze.Sdk.Preprocessor.Commands; - -/// -/// Region Commands: region endregion -/// -public sealed class TypeCommand -{ - private readonly Engine _engine; - private readonly Logger? _logger; - - /// - /// - /// - /// - /// - public TypeCommand(Engine engine, Logger? logger = null) - { - _engine = engine; - _logger = logger; - } - - /// - /// #type - /// - /// 类型 - /// 键 - /// 单位名 - [Command] - public void Type(string type, string key, string unit) - { - _logger?.LogDebug("Adding {unit} as {key} in {type}", unit, key, type); - string reg = $""" - [{type}] - {key}={unit} - """; - _engine.Writer.WriteLine(reg); - } - - /// - [Command] - public void Type(string type, string key) - { - if (string.IsNullOrEmpty(_engine.FilePath)) - { - _logger?.LogError("Cannot found current file's name"); - throw new NotSupportedException("Cannot found current file's name"); - } - - string unit = Path.GetFileName(_engine.FilePath).Split('.').First(); - Type(type, key, unit); - } - - /// - [Command] - public void Type(string type) - { - var typeCounter = _engine.GetOrNew("TypeCounter", () => new TypeCounter()); - var key = typeCounter.Counter.ToString(); - Type(type, key); - } - -} - -file sealed class TypeCounter -{ - private int _counter = 0; - public int Counter => _counter++; -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk.Preprocessor/Shimakaze.Sdk.Preprocessor.csproj b/lib/Shimakaze.Sdk.Preprocessor/Shimakaze.Sdk.Preprocessor.csproj deleted file mode 100644 index 0dad13cd..00000000 --- a/lib/Shimakaze.Sdk.Preprocessor/Shimakaze.Sdk.Preprocessor.csproj +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Csf/Xml/CsfXmlV1Reader.cs b/lib/Shimakaze.Sdk/IO/Csf/Xml/CsfXmlV1Reader.cs deleted file mode 100644 index 0ec5e0c1..00000000 --- a/lib/Shimakaze.Sdk/IO/Csf/Xml/CsfXmlV1Reader.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Xml; - -using Shimakaze.Sdk.Csf; -using Shimakaze.Sdk.Csf.Xml.Converter.V1; - -namespace Shimakaze.Sdk.IO.Csf.Xml; - -/// -/// CsfXmlV1Reader. -/// -public sealed class CsfXmlV1Reader : AsyncReader, IDisposable, IAsyncDisposable -{ - /// - /// 构造器 - /// - /// 基础流 - /// 退出时是否保持流打开 - public CsfXmlV1Reader(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) - { - } - - /// - public override async Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default) - { - await Task.Yield(); - - CsfDocumentXmlSerializer serializer = new(); - using XmlReader xmlReader = XmlReader.Create(BaseStream); - return serializer.Deserialize(xmlReader); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Csf/Xml/CsfXmlV1Writer.cs b/lib/Shimakaze.Sdk/IO/Csf/Xml/CsfXmlV1Writer.cs deleted file mode 100644 index bc7dbd94..00000000 --- a/lib/Shimakaze.Sdk/IO/Csf/Xml/CsfXmlV1Writer.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Xml; - -using Shimakaze.Sdk.Csf; -using Shimakaze.Sdk.Csf.Xml.Converter.V1; - -namespace Shimakaze.Sdk.IO.Csf.Xml; - -/// -/// CsfXmlV1Writer. -/// -public sealed class CsfXmlV1Writer : AsyncWriter, IDisposable, IAsyncDisposable -{ - private readonly XmlWriterSettings? _settings; - /// - /// - /// - /// - /// - /// ˳ʱǷ񱣳 - public CsfXmlV1Writer(Stream stream, XmlWriterSettings? settings = null, bool leaveOpen = false) : base(stream, leaveOpen) - { - _settings = settings; - } - - /// - public override async Task WriteAsync(CsfDocument value, IProgress? progress = default, CancellationToken cancellationToken = default) - { - await Task.Yield(); - - CsfDocumentXmlSerializer serializer = new(); - using XmlWriter xmlWriter = XmlWriter.Create(BaseStream, _settings); - serializer.Serialize(xmlWriter, value); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Csf/Yaml/CsfYamlV1Reader.cs b/lib/Shimakaze.Sdk/IO/Csf/Yaml/CsfYamlV1Reader.cs deleted file mode 100644 index f197e26f..00000000 --- a/lib/Shimakaze.Sdk/IO/Csf/Yaml/CsfYamlV1Reader.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Shimakaze.Sdk.Csf; -using Shimakaze.Sdk.Csf.Yaml.Converter.V1; - -using YamlDotNet.Serialization; - -namespace Shimakaze.Sdk.IO.Csf.Yaml; - -/// -/// CSF YAML Deserializer. -/// -public sealed class CsfYamlV1Reader : AsyncReader, IDisposable, IAsyncDisposable -{ - /// - /// 构造器 - /// - /// 基础流 - /// 退出时是否保持流打开 - public CsfYamlV1Reader(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) - { - } - - /// - public override async Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default) - { - await Task.Yield(); - - using StreamReader reader = new(BaseStream, leaveOpen: true); - return new DeserializerBuilder() - .WithTypeConverter(CsfValueConverter.Instance) - .WithTypeConverter(CsfDataConverter.Instance) - .WithTypeConverter(CsfDocumentConverter.Instance) - .Build() - .Deserialize(reader); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Ini/IniMerger.cs b/lib/Shimakaze.Sdk/IO/Ini/IniMerger.cs deleted file mode 100644 index f1490637..00000000 --- a/lib/Shimakaze.Sdk/IO/Ini/IniMerger.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System.Collections; -using System.Diagnostics.CodeAnalysis; - -using Shimakaze.Sdk.Ini; - -namespace Shimakaze.Sdk.IO.Ini; - -/// -/// Ini 合并器 -/// -public class IniMerger : ISet -{ - /// - /// 内部的词典 - /// - protected readonly Dictionary _cache = []; - - /// - public virtual int Count => _cache.Count; - - /// - public virtual bool IsReadOnly { get; } = false; - - /// - public virtual bool Add(IniSection item) - { - if (!_cache.TryGetValue(item.Name, out var section)) - _cache.Add(item.Name, section = new() { Name = item.Name }); - - foreach (var kvp in item) - section[kvp.Key] = kvp.Value; - - return true; - } - - /// - [ExcludeFromCodeCoverage] - void ICollection.Add(IniSection item) => Add(item); - - /// - /// 生成Ini - /// - /// Ini文档对象 - public virtual IniDocument Build() - { - return new(_cache.Values); - } - - /// - /// 直接写入Ini文件到流 - /// - /// 流 - /// - /// - public virtual async Task BuildAndWriteToAsync(Stream stream, IProgress? progress = default, CancellationToken cancellationToken = default) - { - await using IniWriter serializer = new(stream, true); - await serializer.WriteAsync(Build(), progress, cancellationToken); - } - - /// - public virtual void Clear() => _cache.Clear(); - - /// - public virtual bool Contains(IniSection item) => _cache.TryGetValue(item.Name, out _); - - /// - public virtual void CopyTo(IniSection[] array, int arrayIndex) => _cache.Values.CopyTo(array, arrayIndex); - - /// - public virtual void ExceptWith(IEnumerable other) - { - foreach (var item in other) - _cache.Remove(item.Name); - } - - /// - public virtual IEnumerator GetEnumerator() => _cache.Values.GetEnumerator(); - - /// - [ExcludeFromCodeCoverage] - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// - public virtual void IntersectWith(IEnumerable other) - { - Clear(); - foreach (var item in other) - Add(item); - } - - /// - public virtual bool IsProperSubsetOf(IEnumerable other) - { - if (other.Count() <= Count) - return false; - - var tmp = other.Select(item => item.Name); - return !_cache.Keys.Any(i => !tmp.Contains(i)); - } - - /// - public virtual bool IsProperSupersetOf(IEnumerable other) => other.Count() < Count && !other.Any(i => !_cache.TryGetValue(i.Name, out _)); - - /// - public virtual bool IsSubsetOf(IEnumerable other) - { - if (other.Count() < Count) - return false; - - var tmp = other.Select(item => item.Name); - return !_cache.Keys.Any(i => !tmp.Contains(i)); - } - - /// - public virtual bool IsSupersetOf(IEnumerable other) => other.Count() <= Count && !other.Any(i => !_cache.TryGetValue(i.Name, out _)); - - /// - public virtual bool Overlaps(IEnumerable other) - { - var tmp = other.Select(item => item.Name); - return _cache.Keys.Any(i => tmp.Contains(i)); - } - - /// - public virtual bool Remove(IniSection item) => _cache.Remove(item.Name); - - /// - public virtual bool SetEquals(IEnumerable other) - { - if (other.Count() != Count) - return false; - - var tmp = other.Select(item => item.Name); - return !_cache.Keys.Any(i => !tmp.Contains(i)); - } - - /// - public virtual void SymmetricExceptWith(IEnumerable other) - { - foreach (var item in other) - { - if (_cache.TryGetValue(item.Name, out var value)) - Remove(value); - else - Add(item); - } - } - - /// - public virtual void UnionWith(IEnumerable other) - { - foreach (var item in other) - Add(item); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Ini/IniReader.cs b/lib/Shimakaze.Sdk/IO/Ini/IniReader.cs deleted file mode 100644 index 14bfbd13..00000000 --- a/lib/Shimakaze.Sdk/IO/Ini/IniReader.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Shimakaze.Sdk.Ini; -using Shimakaze.Sdk.Ini.Binder; -using Shimakaze.Sdk.Ini.Parser; - -namespace Shimakaze.Sdk.IO.Ini; - -/// -/// Ini反序列化器 -/// -public sealed class IniReader : AsyncReader -{ - /// - /// 基础读取器 - /// - private TextReader BaseReader { get; } - private readonly IniTokenReader _tokenReader; - private readonly IniDocumentBinder _binder; - - /// - /// 构造 Ini反序列化器 - /// - /// 基础流 - /// 退出时是否保持流打开 - public IniReader(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) - { - BaseReader = new StreamReader(stream, leaveOpen: leaveOpen); - _tokenReader = new(BaseReader, leaveOpen: leaveOpen); - _binder = new(_tokenReader, leaveOpen: leaveOpen); - } - - /// - /// 读取一个 INI 文档 - /// - /// - public IniDocument Read() => _binder.Bind(); - - /// - public override Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default) - => Task.Run(Read, cancellationToken); - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - if (!_leaveOpen) - _binder.Dispose(); - if (!_leaveOpen) - _tokenReader.Dispose(); - if (!_leaveOpen) - BaseReader.Dispose(); - } - - base.Dispose(disposing); - } - - /// - protected override ValueTask DisposeAsyncCore() - { - if (!_leaveOpen) - _binder.Dispose(); - if (!_leaveOpen) - _tokenReader.Dispose(); - if (!_leaveOpen) - BaseReader.Dispose(); - - return base.DisposeAsyncCore(); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Ini/IniWriter.cs b/lib/Shimakaze.Sdk/IO/Ini/IniWriter.cs deleted file mode 100644 index a3f53f3d..00000000 --- a/lib/Shimakaze.Sdk/IO/Ini/IniWriter.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Shimakaze.Sdk.Ini; -using Shimakaze.Sdk.Ini.Parser; - -namespace Shimakaze.Sdk.IO.Ini; - -/// -/// Ini序列化器 -/// -public sealed class IniWriter : AsyncWriter -{ - /// - /// 基础写入器 - /// - public TextWriter BaseWriter { get; } - private readonly IniTokenWriter _iniTokenWriter; - - /// - /// 构造 INI 序列化器 - /// - /// 基础流 - /// 退出时是否保持流打开 - public IniWriter(Stream baseStream, bool leaveOpen = false) : base(baseStream, leaveOpen) - { - BaseWriter = new StreamWriter(baseStream, leaveOpen: leaveOpen); - _iniTokenWriter = new(BaseWriter, leaveOpen: leaveOpen); - } - - /// - /// 写入 - /// - /// - public void Write(in IniDocument ini) - { - WriteSectionBody(ini.Default); - foreach (var section in ini) - { - _iniTokenWriter.Write(new(IniTokenType.Section, section.Name)); - _iniTokenWriter.WriteLine(); - WriteSectionBody(section); - } - } - - /// - public override Task WriteAsync(IniDocument ini, IProgress? progress = default, CancellationToken cancellationToken = default) - => Task.Run(() => Write(ini), cancellationToken); - - /// - /// 释放资源 - /// - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - if (!_leaveOpen) - _iniTokenWriter.Dispose(); - if (!_leaveOpen) - BaseWriter.Dispose(); - } - - base.Dispose(disposing); - } - - /// - protected override ValueTask DisposeAsyncCore() - { - if (!_leaveOpen) - _iniTokenWriter.Dispose(); - if (!_leaveOpen) - BaseWriter.Dispose(); - - return base.DisposeAsyncCore(); - } - - /// - /// 写入Section块 不包含Section头 - /// - /// Section - private void WriteSectionBody(in IniSection section) - { - foreach (var item in section) - { - _iniTokenWriter.Write(new(IniTokenType.Key, item.Key)); - _iniTokenWriter.Write(new(IniTokenType.Value, item.Value)); - _iniTokenWriter.WriteLine(); - } - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Mix/IdCalculaters.cs b/lib/Shimakaze.Sdk/IO/Mix/IdCalculaters.cs deleted file mode 100644 index 4995ab19..00000000 --- a/lib/Shimakaze.Sdk/IO/Mix/IdCalculaters.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.IO.Hashing; -using System.Text; - -namespace Shimakaze.Sdk.IO.Mix; - -/// -/// File Id Calculater -/// -public delegate uint IdCalculater(string name); - -/// -/// File Id Calculaters -/// -public static class IdCalculaters -{ - /// - /// Tiberian Sun Id Calc - /// - /// File Name - /// Id - public static uint TSIdCalculater(string name) - { - name = name.ToUpper(); - int l = name.Length; - int a = l >> 2; - if ((l & 3) is not 0) - { - name += (char)(byte)(l - (a << 2)); - int i = 3 - (l & 3); - while (i-- is not 0) - name += name[a << 2]; - } - return BitConverter.ToUInt32(Crc32.Hash(Encoding.ASCII.GetBytes(name)), 0); - } - - // /// /// Id Calc for RA/TD /// /// /// This method are used by - // RedAlert and Tiberian Down. /// /// File Name /// - // Id [Obsolete("It's NOT pass the test!")] public static uint - // OldIdCalculater(string name) { name = name.ToUpper(); int i = 0; uint id = 0; int l = - // name.Length; while (i < l) { uint a = 0; for (int j = 0; j < 4; j++) { a >>= 8; if (i < l) a - // += ((uint)name[i]) << 24; i++; } id = (id << 1 | id >> 31) + a; } return id; } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Ares/AresIniDocument.cs b/lib/Shimakaze.Sdk/Ini/Ares/AresIniDocument.cs deleted file mode 100644 index ff1e1590..00000000 --- a/lib/Shimakaze.Sdk/Ini/Ares/AresIniDocument.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Shimakaze.Sdk.Ini.Ares; - -/// -public sealed class AresIniDocument : IniDocument -{ - /// - public AresIniDocument() - { - Default = new(";Default;", default, new(_defaultSectionKeyComparer)); - } - - /// - public AresIniDocument(IEnumerable sections) : base(sections) - { - Default = new(";Default;", default, new(_defaultSectionKeyComparer)); - } - - /// - public AresIniDocument(IEqualityComparer sectionNameComparer, IEqualityComparer? keyComparer = default) : base(sectionNameComparer, keyComparer) - { - Default = new(";Default;", default, new(_defaultSectionKeyComparer)); - } - - /// - public override AresIniSection Default { get; } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Binder/Ares/AresIniDocumentBinder.cs b/lib/Shimakaze.Sdk/Ini/Binder/Ares/AresIniDocumentBinder.cs deleted file mode 100644 index 45f00d74..00000000 --- a/lib/Shimakaze.Sdk/Ini/Binder/Ares/AresIniDocumentBinder.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Shimakaze.Sdk.Ini.Ares; -using Shimakaze.Sdk.Ini.Parser; -using Shimakaze.Sdk.Ini.Parser.Ares; - -namespace Shimakaze.Sdk.Ini.Binder.Ares; - -/// -/// AresIniDocument 绑定器 -/// -/// -/// -/// -public sealed class AresIniDocumentBinder(AresIniTokenReader tokenReader, bool leaveOpen = false, EqualityComparer? keyComparer = default) : IDisposable -{ - private readonly bool _leaveOpen = leaveOpen; - private readonly AresIniTokenReader _tokenReader = tokenReader; - - /// - /// 绑定 - /// - /// - public AresIniDocument Bind(AresIniDocument? ini = default) - { - ini ??= []; - AresIniSection current = ini.Default; - string? key = default; - bool isBase = false; - foreach (var token in _tokenReader) - { - switch (token.Type) - { - // 继承节符号 - case AresIniTokenType.BASE_SECTION: - isBase = true; - break; - // 继承节 - case IniTokenType.Section when isBase: - current.BaseName = token.Value; - isBase = false; - break; - // 节 - case IniTokenType.Section when !isBase && !string.IsNullOrEmpty(token.Value): - if (!ini.TryGetSection(token.Value, out var section)) - section = ini[token.Value] = keyComparer is not null - ? new(token.Value, default, new(keyComparer)) - : new(token.Value); - - current = section; - break; - // 随机键名 - case AresIniTokenType.PLUS: - if (key is not null) - current[key] = string.Empty; - key = Guid.NewGuid().ToString(); - break; - // 键 - case IniTokenType.Key: - if (key is not null) - current[key] = string.Empty; - key = token.Value; - break; - // 值 - case IniTokenType.Value when !string.IsNullOrEmpty(token.Value): - if (key is null) - { - current[token.Value] = string.Empty; - } - else - { - current[key] = token.Value; - key = default; - } - break; - } - } - - // 组织继承节 - foreach (var section in ini) - { - if (!string.IsNullOrEmpty(section.BaseName) && ini.TryGetSection(section.BaseName, out var baseSection)) - section.Base = baseSection; - } - return ini; - } - - /// - public void Dispose() - { - if (!_leaveOpen) - _tokenReader.Dispose(); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Binder/IniDocumentBinder.cs b/lib/Shimakaze.Sdk/Ini/Binder/IniDocumentBinder.cs deleted file mode 100644 index 8c5f9209..00000000 --- a/lib/Shimakaze.Sdk/Ini/Binder/IniDocumentBinder.cs +++ /dev/null @@ -1,67 +0,0 @@ -using Shimakaze.Sdk.Ini.Parser; - -namespace Shimakaze.Sdk.Ini.Binder; - -/// -/// IniDocument 绑定器 -/// -/// -/// -/// -public sealed class IniDocumentBinder(IniTokenReader tokenReader, bool leaveOpen = false, EqualityComparer? keyComparer = default) : IDisposable -{ - private readonly bool _leaveOpen = leaveOpen; - private readonly IniTokenReader _tokenReader = tokenReader; - - /// - /// 绑定 - /// - /// - public IniDocument Bind(IniDocument? ini = default) - { - ini ??= []; - IniSection current = ini.Default; - string? key = default; - foreach (var token in _tokenReader) - { - if (token.Value is null) - continue; - - switch (token.Type) - { - case IniTokenType.Section: - if (!ini.TryGetSection(token.Value, out var section)) - section = ini[token.Value] = keyComparer is not null - ? new(token.Value, new(keyComparer)) - : new(token.Value); - - current = section; - break; - case IniTokenType.Key: - if (key is not null) - current[key] = string.Empty; - key = token.Value; - break; - case IniTokenType.Value: - if (key is null) - { - current[token.Value] = string.Empty; - } - else - { - current[key] = token.Value; - key = default; - } - break; - } - } - return ini; - } - - /// - public void Dispose() - { - if (!_leaveOpen) - _tokenReader.Dispose(); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/IniDocument.cs b/lib/Shimakaze.Sdk/Ini/IniDocument.cs deleted file mode 100644 index dbd31481..00000000 --- a/lib/Shimakaze.Sdk/Ini/IniDocument.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Shimakaze.Sdk.Ini; - -/// -public sealed class IniDocument : IniDocument -{ - /// - public IniDocument() - { - Default = new(";Default;", new(_defaultSectionKeyComparer)); - } - - /// - public IniDocument(IEnumerable sections) : base(sections) - { - Default = new(";Default;", new(_defaultSectionKeyComparer)); - } - - /// - public IniDocument(IEqualityComparer sectionNameComparer, IEqualityComparer? keyComparer = default) : base(sectionNameComparer, keyComparer) - { - Default = new(";Default;", new(_defaultSectionKeyComparer)); - } - - /// - public override IniSection Default { get; } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Parser/Ares/AresIniTokenReader.cs b/lib/Shimakaze.Sdk/Ini/Parser/Ares/AresIniTokenReader.cs deleted file mode 100644 index 28c41fe3..00000000 --- a/lib/Shimakaze.Sdk/Ini/Parser/Ares/AresIniTokenReader.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Shimakaze.Sdk.Ini.Parser.Ares; - -/// -/// Ares INI 方言语法分析器 -/// -/// -/// -/// -public class AresIniTokenReader(TextReader textReader, IniTokenIgnoreLevel ignore = IniTokenIgnoreLevel.NonValue, bool leaveOpen = false) : IniTokenReader(textReader, ignore, leaveOpen) -{ - /// - protected override bool FlushBuffer([NotNullWhen(true)] out IniToken? result, int type = IniTokenType.Unknown) - { - result = default; - if (Buffer.Length is not 0) - { - switch ((type, Buffer[0])) - { - case (IniTokenType.Unknown, ':'): - // 继承节 - result = new(AresIniTokenType.BASE_SECTION); - Buffer.Clear(); - break; - case (IniTokenType.Key, '+'): - // += 注册表 - result = new(AresIniTokenType.PLUS); - Buffer.Clear(); - break; - default: - break; - } - } - - return result is not null || base.FlushBuffer(out result, type); - } -} diff --git a/lib/Shimakaze.Sdk/Ini/Parser/Ares/AresIniTokenType.cs b/lib/Shimakaze.Sdk/Ini/Parser/Ares/AresIniTokenType.cs deleted file mode 100644 index b38d5407..00000000 --- a/lib/Shimakaze.Sdk/Ini/Parser/Ares/AresIniTokenType.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Shimakaze.Sdk.Ini.Parser.Ares; - -/// -/// Ares INI Token Type -/// -public static class AresIniTokenType -{ - /// - /// Ares INI 特有的 继承 Section - /// - /// - /// [Section] : [Base] - /// - public const int BASE_SECTION = ':'; - /// - /// PLUS - /// - /// - /// += Value - /// - public const int PLUS = '+'; -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Parser/Ares/AresIniTokenWriter.cs b/lib/Shimakaze.Sdk/Ini/Parser/Ares/AresIniTokenWriter.cs deleted file mode 100644 index 292ffd0c..00000000 --- a/lib/Shimakaze.Sdk/Ini/Parser/Ares/AresIniTokenWriter.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Shimakaze.Sdk.Ini.Parser.Ares; - -/// -/// Ares INI 方言语法流写入器 -/// -/// -/// -public class AresIniTokenWriter(TextWriter textWriter, bool leaveOpen = false) : IniTokenWriter(textWriter, leaveOpen) -{ - /// - public override void Write(in IniToken token) - { - switch (token.Type) - { - case AresIniTokenType.BASE_SECTION: - case AresIniTokenType.PLUS: - { - BaseWriter.Write((char)token.Type); - break; - } - default: - base.Write(token); - break; - } - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Parser/BucketStack.cs b/lib/Shimakaze.Sdk/Ini/Parser/BucketStack.cs deleted file mode 100644 index 365d223d..00000000 --- a/lib/Shimakaze.Sdk/Ini/Parser/BucketStack.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text; - -namespace Shimakaze.Sdk.Ini.Parser; - -/// -/// 成对符号栈 -/// -public sealed class BucketStack -{ - private readonly Stack<(char Start, StringBuilder Buffer)> _depths = new(); - private readonly Stack _bufferCaches = new(); - - /// - /// 暂存区 - /// - public StringBuilder Buffer { get; private set; } = new(); - - /// - /// 当前符号 - /// - public char Current => _depths.TryPeek(out var result) ? result.Start : char.MinValue; - - /// - /// 是否是空的 - /// - public bool Empty => _depths.Count is 0; - - /// - /// 压栈 - /// - /// - public void Push(char start) - { - _depths.Push((start, Buffer)); - if (!_bufferCaches.TryPop(out var buffer)) - buffer = new(); - Buffer = buffer.Clear(); - } - - /// - /// 弹栈 - /// - /// - /// - public bool Pop([NotNullWhen(true)] out string? result) - { - result = default; - (_, StringBuilder lastBuffer) = _depths.Pop(); - if (Buffer is not { Length: 0 }) - result = Buffer.ToString(); - _bufferCaches.Push(Buffer); - Buffer = lastBuffer; - return result is not null; - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Parser/IniReaderIgnoreLevel.cs b/lib/Shimakaze.Sdk/Ini/Parser/IniReaderIgnoreLevel.cs deleted file mode 100644 index 19a922d0..00000000 --- a/lib/Shimakaze.Sdk/Ini/Parser/IniReaderIgnoreLevel.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Shimakaze.Sdk.Ini.Parser; - -/// -/// Token忽略等级 -/// -public enum IniTokenIgnoreLevel -{ - /// - /// 不忽略 - /// - None, - /// - /// 忽略空白 - /// - White, - /// - /// 忽略不包含值的Token - /// - NonValue, -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Parser/IniToken.cs b/lib/Shimakaze.Sdk/Ini/Parser/IniToken.cs deleted file mode 100644 index b9fe30be..00000000 --- a/lib/Shimakaze.Sdk/Ini/Parser/IniToken.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Shimakaze.Sdk.Ini.Parser; - -/// -/// Ini Token -/// -/// -/// -public record struct IniToken(int Type, string? Value = default) -{ - /// - /// - /// - /// - public IniToken(int type, string? value, bool trim) - : this(type, value) - { - if (trim) - Value = value?.Trim(); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Parser/IniTokenReader.cs b/lib/Shimakaze.Sdk/Ini/Parser/IniTokenReader.cs deleted file mode 100644 index 866f4d98..00000000 --- a/lib/Shimakaze.Sdk/Ini/Parser/IniTokenReader.cs +++ /dev/null @@ -1,207 +0,0 @@ -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Text; - -namespace Shimakaze.Sdk.Ini.Parser; - -/// -/// 语法树抽象 -/// -/// -/// -/// -public class IniTokenReader(TextReader textReader, IniTokenIgnoreLevel ignore = IniTokenIgnoreLevel.NonValue, bool leaveOpen = false) : IEnumerable, IDisposable -{ - /// - /// leave Open - /// - protected bool _leaveOpen = leaveOpen; - - /// - /// 基础流 - /// - public TextReader BaseReader { get; } = textReader; - /// - /// 忽略等级 - /// - public IniTokenIgnoreLevel IgnoreLevel { get; } = ignore; - - /// - /// 括号栈 - /// - protected readonly BucketStack _depths = new(); - - /// - /// 字符暂存区 - /// - protected StringBuilder Buffer => _depths.Buffer; - private bool _disposedValue; - - /// - /// 将 内容输出为 - /// - /// - /// - /// - protected virtual bool FlushBuffer([NotNullWhen(true)] out IniToken? result, int type = IniTokenType.Unknown) - { - result = default; - - switch (_depths.Current) - { - // Flush Value - case '[' when _depths.Pop(out var value): - result = new IniToken(IniTokenType.Section, value, IgnoreLevel is >= IniTokenIgnoreLevel.White); - break; - case '=' when _depths.Pop(out var value): - result = new IniToken(IniTokenType.Value, value, IgnoreLevel is >= IniTokenIgnoreLevel.White); - break; - default: - if (Buffer is not { Length: 0 }) - { - // 不知道是什么东西 - result = new IniToken(type, Buffer.ToString(), IgnoreLevel is >= IniTokenIgnoreLevel.White); - Buffer.Clear(); - } - break; - } - - return result is not null; - } - - private IEnumerable ReadAllInternal() - { - IniToken? token; - while (BaseReader.Peek() is not -1) - { - char ch = (char)BaseReader.Read(); - - // 字符匹配 - switch (ch) - { - // 行尾匹配 - case '\r': - { - // Flush - if (FlushBuffer(out token)) - yield return token.Value; - if (IgnoreLevel < IniTokenIgnoreLevel.White) - yield return new(IniTokenType.CR); - break; - } - case '\n': - { - // Flush - if (FlushBuffer(out token)) - yield return token.Value; - if (IgnoreLevel < IniTokenIgnoreLevel.White) - yield return new(IniTokenType.LF); - break; - } - // 空格 - case ' ': - { - if (_depths.Empty && IgnoreLevel < IniTokenIgnoreLevel.White) - yield return new(IniTokenType.SPACE); - break; - } - // 横向制表符 - case '\t': - { - if (_depths.Empty && IgnoreLevel < IniTokenIgnoreLevel.White) - yield return new(IniTokenType.TAB); - break; - } - // 行内注释 - case ';': - { - // Flush - if (FlushBuffer(out token)) - yield return token.Value; - if (IgnoreLevel < IniTokenIgnoreLevel.NonValue) - yield return new(IniTokenType.SEMI); - - yield return new IniToken(IniTokenType.Comment, BaseReader.ReadLine() ?? string.Empty, IgnoreLevel is >= IniTokenIgnoreLevel.White); - break; - } - // 匹配尾中括号 - case ']': - { - // Flush - if (FlushBuffer(out token)) - yield return token.Value; - if (IgnoreLevel < IniTokenIgnoreLevel.NonValue) - yield return new(IniTokenType.END_BRACKET); - break; - } - // 匹配首中括号 - case '[': - { - // Flush - if (FlushBuffer(out token)) - yield return token.Value; - if (IgnoreLevel < IniTokenIgnoreLevel.NonValue) - yield return new(IniTokenType.START_BRACKET); - - _depths.Push(ch); - break; - } - case '=': - { - // Flush - if (FlushBuffer(out token, IniTokenType.Key)) - yield return token.Value; - if (IgnoreLevel < IniTokenIgnoreLevel.NonValue) - yield return new(IniTokenType.EQ); - - _depths.Push(ch); - break; - } - default: - { - Buffer.Append(ch); - break; - } - } - } - if (FlushBuffer(out token)) - yield return token.Value; - if (IgnoreLevel < IniTokenIgnoreLevel.White) - yield return new(IniTokenType.EOF); - } - - /// - public IEnumerator GetEnumerator() => ReadAllInternal().GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// - /// - /// - /// - protected virtual void Dispose(bool disposing) - { - if (_disposedValue) - return; - - _disposedValue = true; - if (disposing) - { - if (!_leaveOpen) - BaseReader.Dispose(); - } - - } - - // ~IniTokenReader() - // { - // Dispose(disposing: false); - // } - - /// - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Parser/IniTokenType.cs b/lib/Shimakaze.Sdk/Ini/Parser/IniTokenType.cs deleted file mode 100644 index 0d59b4e9..00000000 --- a/lib/Shimakaze.Sdk/Ini/Parser/IniTokenType.cs +++ /dev/null @@ -1,65 +0,0 @@ -namespace Shimakaze.Sdk.Ini.Parser; - -/// -/// INI Token Type -/// -public static class IniTokenType -{ - /// - /// Unknown - /// - public const int Unknown = 0; - /// - /// End of File - /// - public const int EOF = -1; - /// - /// CR - /// - public const int CR = '\r'; - /// - /// LF - /// - public const int LF = '\n'; - /// - /// Space - /// - public const int SPACE = ' '; - /// - /// Tab - /// - public const int TAB = '\t'; - /// - /// ; - /// - public const int SEMI = ';'; - /// - /// = - /// - public const int EQ = '='; - /// - /// [ - /// - public const int START_BRACKET = '['; - /// - /// ] - /// - public const int END_BRACKET = ']'; - // ===================================== - /// - /// ; Comment - /// - public const int Comment = 1; - /// - /// [Section] - /// - public const int Section = 2; - /// - /// Key= - /// - public const int Key = 3; - /// - /// =Value - /// - public const int Value = 4; -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/Parser/IniTokenWriter.cs b/lib/Shimakaze.Sdk/Ini/Parser/IniTokenWriter.cs deleted file mode 100644 index bf1182f3..00000000 --- a/lib/Shimakaze.Sdk/Ini/Parser/IniTokenWriter.cs +++ /dev/null @@ -1,110 +0,0 @@ -namespace Shimakaze.Sdk.Ini.Parser; - -/// -/// INI Token 流写入器 -/// -/// -/// -public class IniTokenWriter(TextWriter textWriter, bool leaveOpen = false) : IDisposable -{ - private bool _disposedValue; - /// - /// leave Open - /// - protected bool _leaveOpen = leaveOpen; - - /// - /// 基础流 - /// - public TextWriter BaseWriter { get; } = textWriter; - - /// - /// 写入Token - /// - /// - public virtual void Write(in IniToken token) - { - switch (token.Type) - { - case IniTokenType.Comment: - { - BaseWriter.Write(';'); - BaseWriter.Write(token.Value); - break; - } - case IniTokenType.Section: - { - BaseWriter.Write('['); - BaseWriter.Write(token.Value); - BaseWriter.Write(']'); - break; - } - case IniTokenType.Key: - { - BaseWriter.Write(token.Value); - break; - } - case IniTokenType.Value: - { - BaseWriter.Write('='); - BaseWriter.Write(token.Value); - break; - } - case IniTokenType.CR: - case IniTokenType.LF: - case IniTokenType.SPACE: - case IniTokenType.TAB: - { - BaseWriter.Write((char)token.Type); - break; - } - default: - throw new NotSupportedException(); - } - } - - /// - /// 写入Token 后写入换行符 - /// - /// - public void WriteLine(in IniToken token) - { - Write(token); - WriteLine(); - } - - /// - /// 写入换行符 - /// - public void WriteLine() => BaseWriter.WriteLine(); - - /// - /// - /// - /// - protected virtual void Dispose(bool disposing) - { - if (_disposedValue) - return; - - _disposedValue = true; - if (disposing) - { - if (!_leaveOpen) - BaseWriter.Dispose(); - } - - } - - // ~IniTokenReader() - // { - // Dispose(disposing: false); - // } - - /// - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Shimakaze.Sdk.csproj b/lib/Shimakaze.Sdk/Shimakaze.Sdk.csproj deleted file mode 100644 index a222528d..00000000 --- a/lib/Shimakaze.Sdk/Shimakaze.Sdk.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Vxl/VXLFile.cs b/lib/Shimakaze.Sdk/Vxl/VXLFile.cs deleted file mode 100644 index ebc8dfa5..00000000 --- a/lib/Shimakaze.Sdk/Vxl/VXLFile.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Shimakaze.Sdk.Pal; - -namespace Shimakaze.Sdk.Vxl; - -/// -/// VXL 文件 -/// -public record struct VXLFile -{ - /// - /// 文件头 - /// - public VXLHeader Header; - /// - /// 文件色板 - /// - public Palette Palette; - /// - /// - public SectionHeader[] SectionHeaders; - /// - /// - public SectionData[] SectionData; - /// - /// - public SectionTailer[] SectionTailers; -} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk.Build/CommonUtil.cs b/src/Shimakaze.Sdk.Build/CommonUtil.cs similarity index 97% rename from lib/Shimakaze.Sdk.Build/CommonUtil.cs rename to src/Shimakaze.Sdk.Build/CommonUtil.cs index 130b9d64..e5f47c68 100644 --- a/lib/Shimakaze.Sdk.Build/CommonUtil.cs +++ b/src/Shimakaze.Sdk.Build/CommonUtil.cs @@ -4,7 +4,6 @@ namespace Shimakaze.Sdk.Build; -[ExcludeFromCodeCoverage] internal static class CommonUtil { /// diff --git a/lib/Shimakaze.Sdk.Build/ReadMe.md b/src/Shimakaze.Sdk.Build/ReadMe.md similarity index 100% rename from lib/Shimakaze.Sdk.Build/ReadMe.md rename to src/Shimakaze.Sdk.Build/ReadMe.md diff --git a/lib/Shimakaze.Sdk.Build/Sdk/Sdk.props b/src/Shimakaze.Sdk.Build/Sdk/Sdk.props similarity index 100% rename from lib/Shimakaze.Sdk.Build/Sdk/Sdk.props rename to src/Shimakaze.Sdk.Build/Sdk/Sdk.props diff --git a/lib/Shimakaze.Sdk.Build/Sdk/Sdk.targets b/src/Shimakaze.Sdk.Build/Sdk/Sdk.targets similarity index 100% rename from lib/Shimakaze.Sdk.Build/Sdk/Sdk.targets rename to src/Shimakaze.Sdk.Build/Sdk/Sdk.targets diff --git a/src/Shimakaze.Sdk.Build/Sdk/tasks.props b/src/Shimakaze.Sdk.Build/Sdk/tasks.props new file mode 100644 index 00000000..942505c1 --- /dev/null +++ b/src/Shimakaze.Sdk.Build/Sdk/tasks.props @@ -0,0 +1,15 @@ + + + + + netstandard2.0 + net8.0 + + + + + + + + + \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Build/Shimakaze.Sdk.Build.csproj b/src/Shimakaze.Sdk.Build/Shimakaze.Sdk.Build.csproj new file mode 100644 index 00000000..d896f2a1 --- /dev/null +++ b/src/Shimakaze.Sdk.Build/Shimakaze.Sdk.Build.csproj @@ -0,0 +1,43 @@ + + + + Shimakaze.Sdk.RA2 + Build your RA2 Module Project by MSBuild + MSBuild MSBuildSdk RA2Mod ShimakazeProject + MSBuildSdk + $(NoWarn);NU5128; + True + PackAllDependencies + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/Shimakaze.Sdk.Build/TaskCsfGenerator.cs b/src/Shimakaze.Sdk.Build/TaskCsfGenerator.cs similarity index 76% rename from lib/Shimakaze.Sdk.Build/TaskCsfGenerator.cs rename to src/Shimakaze.Sdk.Build/TaskCsfGenerator.cs index 5cfa04f9..3abf7ad1 100644 --- a/lib/Shimakaze.Sdk.Build/TaskCsfGenerator.cs +++ b/src/Shimakaze.Sdk.Build/TaskCsfGenerator.cs @@ -3,10 +3,9 @@ using Microsoft.Extensions.DependencyInjection; using Shimakaze.Sdk.Csf; -using Shimakaze.Sdk.IO.Csf; -using Shimakaze.Sdk.IO.Csf.Json; -using Shimakaze.Sdk.IO.Csf.Xml; -using Shimakaze.Sdk.IO.Csf.Yaml; +using Shimakaze.Sdk.Csf.Json; +using Shimakaze.Sdk.Csf.Xml; +using Shimakaze.Sdk.Csf.Yaml; using MSTask = Microsoft.Build.Utilities.Task; @@ -20,18 +19,18 @@ public sealed class TaskCsfGenerator : MSTask /// /// 生成的中间文件的路径 /// - public const string Metadata_Intermediate = "Intermediate"; + public const string MetadataIntermediate = "Intermediate"; /// /// Type /// - public const string Metadata_Type = "Type"; + public const string MetadataType = "Type"; /// /// 生成的目标文件 /// [Output] - public ITaskItem[] OutputFiles { get; set; } = Array.Empty(); + public ITaskItem[] OutputFiles { get; set; } = []; /// /// 将要被处理的文件 @@ -48,15 +47,15 @@ public override bool Execute() List items = new(SourceFiles.Length); foreach (var file in SourceFiles) { - var dest = file.GetMetadata(Metadata_Intermediate); - var tag = file.GetMetadata(Metadata_Type); + var dest = file.GetMetadata(MetadataIntermediate); + var tag = file.GetMetadata(MetadataType); if (!dest.CreateParentDirectory(Log)) return false; services.Clear(); using Stream stream = File.OpenRead(file.ItemSpec); using Stream output = File.Create(dest); - services.AddSingleton>(new CsfWriter(output)); + services.AddSingleton(new CsfWriter(output)); switch (tag.ToLowerInvariant()) { @@ -71,24 +70,24 @@ public override bool Execute() 0, 0, "You shouldn't use the \"CSF Json version 1\". Please port your file to \"version 2\" or use \"Csf Yaml version 1\" to replace that."); - services.AddSingleton>(new CsfJsonV1Reader(stream)); + services.AddSingleton(new CsfJsonV1Reader(stream)); break; case "json": case "jsonv2": - services.AddSingleton>(new CsfJsonV2Reader(stream)); + services.AddSingleton(new CsfJsonV2Reader(stream)); break; case "xml": case "xmlv1": - services.AddSingleton>(new CsfXmlV1Reader(stream)); + services.AddSingleton(new CsfXmlV1Reader(new StreamReader(stream))); break; case "yml": case "yaml": case "ymlv1": case "yamlv1": - services.AddSingleton>(new CsfYamlV1Reader(stream)); + services.AddSingleton(new CsfYamlV1Reader(new StreamReader(stream))); break; case "csf": @@ -122,7 +121,7 @@ public override bool Execute() CsfDocument csf; try { - csf = provider.GetRequiredService>().ReadAsync().Result; + csf = provider.GetRequiredService().ReadAsync().Result; } catch (Exception e) { @@ -144,14 +143,14 @@ public override bool Execute() } return false; } - provider.GetRequiredService>().WriteAsync(csf).Wait(); + provider.GetRequiredService().WriteAsync(csf).Wait(); TaskItem item = new(dest); file.CopyMetadataTo(item); - item.RemoveMetadata(Metadata_Intermediate); - item.SetMetadata(Metadata_Type, "Csf"); + item.RemoveMetadata(MetadataIntermediate); + item.SetMetadata(MetadataType, "Csf"); items.Add(item); } - OutputFiles = items.ToArray(); + OutputFiles = [.. items]; return !Log.HasLoggedErrors; } diff --git a/lib/Shimakaze.Sdk.Build/TaskCsfMerger.cs b/src/Shimakaze.Sdk.Build/TaskCsfMerger.cs similarity index 88% rename from lib/Shimakaze.Sdk.Build/TaskCsfMerger.cs rename to src/Shimakaze.Sdk.Build/TaskCsfMerger.cs index 845c558f..3455b161 100644 --- a/lib/Shimakaze.Sdk.Build/TaskCsfMerger.cs +++ b/src/Shimakaze.Sdk.Build/TaskCsfMerger.cs @@ -1,7 +1,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -using Shimakaze.Sdk.IO.Csf; +using Shimakaze.Sdk.Csf; using MSTask = Microsoft.Build.Utilities.Task; @@ -15,7 +15,7 @@ public sealed class TaskCsfMerger : MSTask /// /// Pack /// - public const string Metadata_Pack = "Pack"; + public const string MetadataPack = "Pack"; /// /// 生成的文件 @@ -42,7 +42,7 @@ public override bool Execute() if (!DestinationFile.CreateParentDirectory(Log)) return false; - CsfMerger merger = new(); + CsfSet merger = []; OutputFile = new TaskItem(DestinationFile); foreach (var file in SourceFiles) { @@ -52,7 +52,7 @@ public override bool Execute() file.CopyMetadataTo(OutputFile); } - OutputFile.SetMetadata(Metadata_Pack, true.ToString()); + OutputFile.SetMetadata(MetadataPack, true.ToString()); using Stream output = File.Create(DestinationFile); merger.BuildAndWriteToAsync(output).Wait(); output.Flush(); diff --git a/lib/Shimakaze.Sdk.Build/TaskIniMerger.cs b/src/Shimakaze.Sdk.Build/TaskIniMerger.cs similarity index 78% rename from lib/Shimakaze.Sdk.Build/TaskIniMerger.cs rename to src/Shimakaze.Sdk.Build/TaskIniMerger.cs index 70c9a43e..f2104078 100644 --- a/lib/Shimakaze.Sdk.Build/TaskIniMerger.cs +++ b/src/Shimakaze.Sdk.Build/TaskIniMerger.cs @@ -2,9 +2,6 @@ using Microsoft.Build.Utilities; using Shimakaze.Sdk.Ini; -using Shimakaze.Sdk.Ini.Binder; -using Shimakaze.Sdk.Ini.Parser; -using Shimakaze.Sdk.IO.Ini; using MSTask = Microsoft.Build.Utilities.Task; @@ -18,7 +15,13 @@ public sealed class TaskIniMerger : MSTask /// /// Pack /// - public const string Metadata_Pack = "Pack"; + public const string MetadataPack = "Pack"; + + // TODO: Support Ares INI + ///// + ///// SpecialType + ///// + //public const string MetadataSpecialType = "SpecialType"; /// /// 生成的文件 @@ -56,9 +59,9 @@ public override bool Execute() file.CopyMetadataTo(OutputFile); } - OutputFile.SetMetadata(Metadata_Pack, true.ToString()); - using Stream output = File.Create(DestinationFile); - using IniWriter writer = new(output); + OutputFile.SetMetadata(MetadataPack, true.ToString()); + using var output = File.CreateText(DestinationFile); + using IniTokenWriter writer = new(output); writer.Write(ini); output.Flush(); diff --git a/lib/Shimakaze.Sdk.Build/TaskIniPreprocessor.cs b/src/Shimakaze.Sdk.Build/TaskIniPreprocessor.cs similarity index 88% rename from lib/Shimakaze.Sdk.Build/TaskIniPreprocessor.cs rename to src/Shimakaze.Sdk.Build/TaskIniPreprocessor.cs index a02f38be..6618ddc2 100644 --- a/lib/Shimakaze.Sdk.Build/TaskIniPreprocessor.cs +++ b/src/Shimakaze.Sdk.Build/TaskIniPreprocessor.cs @@ -20,7 +20,7 @@ public sealed class TaskIniPreprocessor : MSTask /// /// 生成的中间文件的路径 /// - public const string Metadata_Intermediate = "Intermediate"; + public const string MetadataIntermediate = "Intermediate"; /// /// 符号 @@ -32,7 +32,7 @@ public sealed class TaskIniPreprocessor : MSTask /// 输出的文件 /// [Output] - public ITaskItem[] OutputFiles { get; set; } = Array.Empty(); + public ITaskItem[] OutputFiles { get; set; } = []; /// /// 将要被预处理的文件列表 @@ -64,7 +64,7 @@ public override bool Execute() { try { - var dest = file.GetMetadata(Metadata_Intermediate); + var dest = file.GetMetadata(MetadataIntermediate); if (!dest.CreateParentDirectory(Log)) return false; @@ -75,7 +75,7 @@ public override bool Execute() TaskItem item = new(dest); file.CopyMetadataTo(item); - item.RemoveMetadata(Metadata_Intermediate); + item.RemoveMetadata(MetadataIntermediate); outputs.Add(item); } catch (Exception ex) @@ -84,7 +84,7 @@ public override bool Execute() } } - OutputFiles = outputs.ToArray(); + OutputFiles = [.. outputs]; return !Log.HasLoggedErrors; } diff --git a/lib/Shimakaze.Sdk.Build/TaskMixGenerator.cs b/src/Shimakaze.Sdk.Build/TaskMixGenerator.cs similarity index 98% rename from lib/Shimakaze.Sdk.Build/TaskMixGenerator.cs rename to src/Shimakaze.Sdk.Build/TaskMixGenerator.cs index 5ad89707..17ec85df 100644 --- a/lib/Shimakaze.Sdk.Build/TaskMixGenerator.cs +++ b/src/Shimakaze.Sdk.Build/TaskMixGenerator.cs @@ -1,7 +1,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -using Shimakaze.Sdk.IO.Mix; +using Shimakaze.Sdk.Mix; using MSTask = Microsoft.Build.Utilities.Task; diff --git a/lib/Shimakaze.Sdk.Build/targets/Csf.targets b/src/Shimakaze.Sdk.Build/targets/Csf.targets similarity index 100% rename from lib/Shimakaze.Sdk.Build/targets/Csf.targets rename to src/Shimakaze.Sdk.Build/targets/Csf.targets diff --git a/lib/Shimakaze.Sdk.Build/targets/Csf/CsfGenerator.targets b/src/Shimakaze.Sdk.Build/targets/Csf/CsfGenerator.targets similarity index 100% rename from lib/Shimakaze.Sdk.Build/targets/Csf/CsfGenerator.targets rename to src/Shimakaze.Sdk.Build/targets/Csf/CsfGenerator.targets diff --git a/lib/Shimakaze.Sdk.Build/targets/Csf/CsfMerger.targets b/src/Shimakaze.Sdk.Build/targets/Csf/CsfMerger.targets similarity index 100% rename from lib/Shimakaze.Sdk.Build/targets/Csf/CsfMerger.targets rename to src/Shimakaze.Sdk.Build/targets/Csf/CsfMerger.targets diff --git a/lib/Shimakaze.Sdk.Build/targets/Ini.targets b/src/Shimakaze.Sdk.Build/targets/Ini.targets similarity index 100% rename from lib/Shimakaze.Sdk.Build/targets/Ini.targets rename to src/Shimakaze.Sdk.Build/targets/Ini.targets diff --git a/lib/Shimakaze.Sdk.Build/targets/Ini/IniMerger.targets b/src/Shimakaze.Sdk.Build/targets/Ini/IniMerger.targets similarity index 100% rename from lib/Shimakaze.Sdk.Build/targets/Ini/IniMerger.targets rename to src/Shimakaze.Sdk.Build/targets/Ini/IniMerger.targets diff --git a/lib/Shimakaze.Sdk.Build/targets/Ini/IniPreprocessor.targets b/src/Shimakaze.Sdk.Build/targets/Ini/IniPreprocessor.targets similarity index 100% rename from lib/Shimakaze.Sdk.Build/targets/Ini/IniPreprocessor.targets rename to src/Shimakaze.Sdk.Build/targets/Ini/IniPreprocessor.targets diff --git a/lib/Shimakaze.Sdk.Build/targets/Mix.targets b/src/Shimakaze.Sdk.Build/targets/Mix.targets similarity index 100% rename from lib/Shimakaze.Sdk.Build/targets/Mix.targets rename to src/Shimakaze.Sdk.Build/targets/Mix.targets diff --git a/lib/Shimakaze.Sdk.Build/targets/Mix/MixGenerator.targets b/src/Shimakaze.Sdk.Build/targets/Mix/MixGenerator.targets similarity index 100% rename from lib/Shimakaze.Sdk.Build/targets/Mix/MixGenerator.targets rename to src/Shimakaze.Sdk.Build/targets/Mix/MixGenerator.targets diff --git a/lib/Shimakaze.Sdk/Common/AsyncReader.cs b/src/Shimakaze.Sdk.Common/AsyncReader.cs similarity index 94% rename from lib/Shimakaze.Sdk/Common/AsyncReader.cs rename to src/Shimakaze.Sdk.Common/AsyncReader.cs index 3a3b6802..4e53c85d 100644 --- a/lib/Shimakaze.Sdk/Common/AsyncReader.cs +++ b/src/Shimakaze.Sdk.Common/AsyncReader.cs @@ -1,18 +1,15 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Shimakaze.Sdk.Common; +namespace Shimakaze.Sdk; /// /// 异步读取器 /// /// -[ExcludeFromCodeCoverage] public abstract class AsyncReader : IDisposable, IAsyncDisposable { /// /// Leave Open /// - protected readonly bool _leaveOpen; + private readonly bool _leaveOpen; private bool _disposedValue; @@ -99,4 +96,4 @@ protected virtual async ValueTask DisposeAsyncCore() { Dispose(disposing: false); } -} \ No newline at end of file +} diff --git a/src/Shimakaze.Sdk.Common/AsyncTextReader.cs b/src/Shimakaze.Sdk.Common/AsyncTextReader.cs new file mode 100644 index 00000000..c6eb950f --- /dev/null +++ b/src/Shimakaze.Sdk.Common/AsyncTextReader.cs @@ -0,0 +1,102 @@ +namespace Shimakaze.Sdk; + +/// +/// 异步读取器 +/// +/// +public abstract class AsyncTextReader : IDisposable, IAsyncDisposable +{ + /// + /// Leave Open + /// + private readonly bool _leaveOpen; + + private bool _disposedValue; + + /// + /// 基础流 + /// + public TextReader BaseReader { get; } + + /// + /// 构造 Csf 读取器 + /// + /// 基础流 + /// 退出时是否保持流打开 + protected AsyncTextReader(TextReader baseReader, bool leaveOpen = false) + { + BaseReader = baseReader; + _leaveOpen = leaveOpen; + } + + /// + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + public async ValueTask DisposeAsync() + { + await DisposeAsyncCore().ConfigureAwait(false); + + Dispose(disposing: false); + GC.SuppressFinalize(this); + } + + /// + public virtual Task ReadAsync() => ReadAsync(default, default); + + /// + public virtual Task ReadAsync(CancellationToken cancellationToken) => ReadAsync(default, default); + + /// + public virtual Task ReadAsync(IProgress? progress) => ReadAsync(default, default); + + /// + /// 读取 + /// + /// 进度 + /// 取消 + /// + public abstract Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default); + + /// + /// 释放模式 + /// + /// + protected virtual void Dispose(bool disposing) + { + if (_disposedValue) + return; + + if (disposing) + { + if (!_leaveOpen) + BaseReader.Dispose(); + } + + _disposedValue = true; + } + + /// + /// 释放模式 + /// + protected virtual async ValueTask DisposeAsyncCore() + { + await Task.Run(() => + { + if (!_leaveOpen) + BaseReader.Dispose(); + }); + } + + /// + /// 终结器 + /// + ~AsyncTextReader() + { + Dispose(disposing: false); + } +} diff --git a/src/Shimakaze.Sdk.Common/AsyncTextWriter.cs b/src/Shimakaze.Sdk.Common/AsyncTextWriter.cs new file mode 100644 index 00000000..79014583 --- /dev/null +++ b/src/Shimakaze.Sdk.Common/AsyncTextWriter.cs @@ -0,0 +1,100 @@ +namespace Shimakaze.Sdk; + +/// +/// 异步写入器 +/// +/// +public abstract class AsyncTextWriter : IDisposable, IAsyncDisposable +{ + /// + /// Leave Open + /// + private readonly bool _leaveOpen; + + private bool _disposedValue; + + /// + /// 基础流 + /// + public TextWriter BaseWriter { get; } + + /// + /// 构造 Csf 读取器 + /// + /// 基础流 + /// 退出时是否保持流打开 + protected AsyncTextWriter(TextWriter baseWriter, bool leaveOpen = false) + { + BaseWriter = baseWriter; + _leaveOpen = leaveOpen; + } + + /// + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + public async ValueTask DisposeAsync() + { + await DisposeAsyncCore().ConfigureAwait(false); + + Dispose(disposing: false); + GC.SuppressFinalize(this); + } + + /// + public virtual Task WriteAsync(T value) => WriteAsync(value, default, default); + + /// + public virtual Task WriteAsync(T value, CancellationToken cancellationToken) => WriteAsync(value, default, default); + + /// + public virtual Task WriteAsync(T value, IProgress? progress) => WriteAsync(value, default, default); + + /// + /// 写入 + /// + /// 值 + /// 进度 + /// 取消 + /// + public abstract Task WriteAsync(T value, IProgress? progress = default, CancellationToken cancellationToken = default); + + /// + /// 释放模式 + /// + /// + protected virtual void Dispose(bool disposing) + { + if (_disposedValue) + return; + + if (disposing) + { + if (!_leaveOpen) + BaseWriter.Dispose(); + } + + _disposedValue = true; + } + + /// + /// 释放模式 + /// + protected virtual async ValueTask DisposeAsyncCore() + { + if (!_leaveOpen) + await BaseWriter.DisposeAsync().ConfigureAwait(false); + } + + /// + /// 终结器 + /// + ~AsyncTextWriter() + { + Dispose(disposing: false); + } +} diff --git a/lib/Shimakaze.Sdk/Common/AsyncWriter.cs b/src/Shimakaze.Sdk.Common/AsyncWriter.cs similarity index 94% rename from lib/Shimakaze.Sdk/Common/AsyncWriter.cs rename to src/Shimakaze.Sdk.Common/AsyncWriter.cs index 7238ab14..76c96474 100644 --- a/lib/Shimakaze.Sdk/Common/AsyncWriter.cs +++ b/src/Shimakaze.Sdk.Common/AsyncWriter.cs @@ -1,18 +1,15 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Shimakaze.Sdk.Common; +namespace Shimakaze.Sdk; /// /// 异步写入器 /// /// -[ExcludeFromCodeCoverage] public abstract class AsyncWriter : IDisposable, IAsyncDisposable { /// /// Leave Open /// - protected readonly bool _leaveOpen; + private readonly bool _leaveOpen; private bool _disposedValue; diff --git a/src/Shimakaze.Sdk.Common/GroupCollectionShim.cs b/src/Shimakaze.Sdk.Common/GroupCollectionShim.cs new file mode 100644 index 00000000..f1a29f67 --- /dev/null +++ b/src/Shimakaze.Sdk.Common/GroupCollectionShim.cs @@ -0,0 +1,23 @@ +using System.Text.RegularExpressions; + +namespace Shimakaze.Sdk; + +/// +/// 垫片 +/// +public static class GroupCollectionShim +{ + /// + /// + /// + /// + /// + public static IEnumerable GetValues(this GroupCollection group) + { +#if NETSTANDARD + return group.Cast(); +#else + return group.Values; +#endif + } +} diff --git a/src/Shimakaze.Sdk.Common/Shimakaze.Sdk.Common.csproj b/src/Shimakaze.Sdk.Common/Shimakaze.Sdk.Common.csproj new file mode 100644 index 00000000..ca63f0e6 --- /dev/null +++ b/src/Shimakaze.Sdk.Common/Shimakaze.Sdk.Common.csproj @@ -0,0 +1,11 @@ + + + + Shimakaze.Sdk + + + + + + + diff --git a/lib/Shimakaze.Sdk/Common/StreamExtensions.cs b/src/Shimakaze.Sdk.Common/StreamExtensions.cs similarity index 97% rename from lib/Shimakaze.Sdk/Common/StreamExtensions.cs rename to src/Shimakaze.Sdk.Common/StreamExtensions.cs index fb324472..d9447af4 100644 --- a/lib/Shimakaze.Sdk/Common/StreamExtensions.cs +++ b/src/Shimakaze.Sdk.Common/StreamExtensions.cs @@ -1,13 +1,11 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Shimakaze.Sdk.Common; +namespace Shimakaze.Sdk; /// /// 流实用工具 /// -[ExcludeFromCodeCoverage] public static unsafe class StreamExtensions { + /// /// 读取结构体 /// diff --git a/src/Shimakaze.Sdk.Common/StringShim.cs b/src/Shimakaze.Sdk.Common/StringShim.cs new file mode 100644 index 00000000..70180c1b --- /dev/null +++ b/src/Shimakaze.Sdk.Common/StringShim.cs @@ -0,0 +1,22 @@ +namespace Shimakaze.Sdk; + +/// +/// 垫片 +/// +public static class StringShim +{ + /// + /// + /// + /// + /// + /// + public static string Join(this IEnumerable strings, char ch) + { +#if NETSTANDARD2_0 + return string.Join(new(ch, 1), strings); +#else + return string.Join(ch, strings); +#endif + } +} \ No newline at end of file diff --git a/app/Shimakaze.Sdk.Csf.Converter/Program.cs b/src/Shimakaze.Sdk.Csf.Converter/Program.cs similarity index 78% rename from app/Shimakaze.Sdk.Csf.Converter/Program.cs rename to src/Shimakaze.Sdk.Csf.Converter/Program.cs index c3b76d34..7de2aa99 100644 --- a/app/Shimakaze.Sdk.Csf.Converter/Program.cs +++ b/src/Shimakaze.Sdk.Csf.Converter/Program.cs @@ -6,32 +6,30 @@ using Sharprompt; -using Shimakaze.Sdk.Common; using Shimakaze.Sdk.Csf; -using Shimakaze.Sdk.IO.Csf; -using Shimakaze.Sdk.IO.Csf.Json; -using Shimakaze.Sdk.IO.Csf.Xml; -using Shimakaze.Sdk.IO.Csf.Yaml; +using Shimakaze.Sdk.Csf.Json; +using Shimakaze.Sdk.Csf.Xml; +using Shimakaze.Sdk.Csf.Yaml; if (args is { Length: < 1 }) throw new ArgumentException("参数太少"); string input = args[0]; -await using var ifs = File.OpenRead(input); +using Stream ifs = File.OpenRead(input); ServiceCollection services = new(); string current; string defaultValue; -string[] supportFormats = new[] { +string[] supportFormats = [ "Yaml", "JsonV2", "JsonV1", "Xml", "Csf" -}; +]; Func supportFormatNames = value => value switch { "Yaml" => "Shimakaze.Sdk 定义的 Yaml 格式", @@ -45,13 +43,13 @@ switch (Path.GetExtension(input).ToLowerInvariant()) { case ".csf": - services.AddSingleton>(new CsfReader(ifs)); + services.AddSingleton(new CsfReader(ifs)); current = "Csf"; defaultValue = "Yaml"; break; case ".yml": case ".yaml": - services.AddSingleton>(new CsfYamlV1Reader(ifs)); + services.AddSingleton(new CsfYamlV1Reader(new StreamReader(ifs))); current = "Yaml"; defaultValue = "Csf"; break; @@ -78,11 +76,11 @@ switch (protocol) { case 2: - services.AddSingleton>(new CsfJsonV2Reader(ifs, options)); + services.AddSingleton(new CsfJsonV2Reader(ifs, options)); current = "JsonV2"; break; case 1: - services.AddSingleton>(new CsfJsonV1Reader(ifs, options)); + services.AddSingleton(new CsfJsonV1Reader(ifs, options)); current = "JsonV1"; break; default: @@ -93,7 +91,7 @@ break; case ".xml": case ".xaml": - services.AddSingleton>(new CsfXmlV1Reader(ifs)); + services.AddSingleton(new CsfXmlV1Reader(new StreamReader(ifs))); current = "Xml"; defaultValue = "Csf"; break; @@ -133,16 +131,16 @@ _ => throw new NotSupportedException() }); -await using var ofs = File.Create(output); -services.AddSingleton>(selected switch +using Stream ofs = File.Create(output); +services.AddSingleton(selected switch { - "Yaml" => new CsfYamlV1Writer(ofs), + "Yaml" => new CsfYamlV1Writer(new StreamWriter(ofs)), "JsonV2" => new CsfJsonV2Writer(ofs, new() { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }), "JsonV1" => new CsfJsonV1Writer(ofs, new() { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }), - "Xml" => new CsfXmlV1Writer(ofs, new() { Indent = true }), + "Xml" => new CsfXmlV1Writer(new StreamWriter(ofs), new() { Indent = true }), "Csf" => new CsfWriter(ofs), _ => throw new NotSupportedException() }); await using var provider = services.BuildServiceProvider(); -var csf = await provider.GetRequiredService>().ReadAsync(); -await provider.GetRequiredService>().WriteAsync(csf); \ No newline at end of file +var csf = await provider.GetRequiredService().ReadAsync(); +await provider.GetRequiredService().WriteAsync(csf); \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Csf.Converter/Shimakaze.Sdk.Csf.Converter.csproj b/src/Shimakaze.Sdk.Csf.Converter/Shimakaze.Sdk.Csf.Converter.csproj new file mode 100644 index 00000000..6c5c3213 --- /dev/null +++ b/src/Shimakaze.Sdk.Csf.Converter/Shimakaze.Sdk.Csf.Converter.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + true + + + + + + + + + + + + + + + diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfAdvancedValueJsonConverter.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfAdvancedValueJsonConverter.cs similarity index 100% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfAdvancedValueJsonConverter.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfAdvancedValueJsonConverter.cs diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfDataJsonConverter.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfDataJsonConverter.cs similarity index 98% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfDataJsonConverter.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfDataJsonConverter.cs index 5e33f444..f395a217 100644 --- a/lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfDataJsonConverter.cs +++ b/src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfDataJsonConverter.cs @@ -52,7 +52,7 @@ public override CsfData Read(ref Utf8JsonReader reader, Type typeToConvert, Json { return new(label) { - Values = values.ToArray() + Values = [.. values] }; } else @@ -117,7 +117,7 @@ internal static List ReadValues(ref Utf8JsonReader reader, JsonSeriali { reader.Read().ThrowWhenFalse(); reader.TokenType.ThrowWhenNotToken(JsonTokenType.StartArray); - List values = new(); + List values = []; while (reader.Read().ThrowWhenNull()) { if (reader.TokenType is JsonTokenType.EndArray) diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfFileJsonConverter.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfFileJsonConverter.cs similarity index 98% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfFileJsonConverter.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfFileJsonConverter.cs index 7f246a4d..d1e92fae 100644 --- a/lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfFileJsonConverter.cs +++ b/src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfFileJsonConverter.cs @@ -65,7 +65,7 @@ private static List ReadDataList(ref Utf8JsonReader reader, JsonSeriali { reader.Read().ThrowWhenNull(); reader.TokenType.ThrowWhenNotToken(JsonTokenType.StartArray); - List data = new(); + List data = []; while (reader.Read().ThrowWhenNull()) { if (reader.TokenType is JsonTokenType.EndArray) diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfJsonSerializerOptions.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfJsonSerializerOptions.cs similarity index 100% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfJsonSerializerOptions.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfJsonSerializerOptions.cs diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfLanguageJsonConverter.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfLanguageJsonConverter.cs similarity index 100% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfLanguageJsonConverter.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfLanguageJsonConverter.cs diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfMetadataJsonConverter.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfMetadataJsonConverter.cs similarity index 100% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfMetadataJsonConverter.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfMetadataJsonConverter.cs diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfSimpleValueJsonConverter.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfSimpleValueJsonConverter.cs similarity index 100% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfSimpleValueJsonConverter.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfSimpleValueJsonConverter.cs diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfValueJsonConverter.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfValueJsonConverter.cs similarity index 100% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V1/CsfValueJsonConverter.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V1/CsfValueJsonConverter.cs diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V2/CsfDataValueJsonConverter.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V2/CsfDataValueJsonConverter.cs similarity index 97% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V2/CsfDataValueJsonConverter.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V2/CsfDataValueJsonConverter.cs index 54bc9c40..916efc11 100644 --- a/lib/Shimakaze.Sdk/Csf/Json/Converter/V2/CsfDataValueJsonConverter.cs +++ b/src/Shimakaze.Sdk.Csf.Json/Converter/V2/CsfDataValueJsonConverter.cs @@ -15,15 +15,15 @@ public sealed class CsfDataValueJsonConverter : JsonConverter> { return reader.TokenType switch { - JsonTokenType.String or JsonTokenType.StartArray => new(){ + JsonTokenType.String or JsonTokenType.StartArray => [ new CsfValue( reader .Get(options) .ThrowWhenNull() ) - }, + ], JsonTokenType.StartObject => ReadAdvancedValue(ref reader, options), - JsonTokenType.Null => new(), + JsonTokenType.Null => [], _ => reader.TokenType.ThrowNotSupportToken>(), }; } @@ -97,13 +97,13 @@ private static List ReadAdvancedValue(ref Utf8JsonReader reader, JsonS value.ThrowWhenNull(); - return new() - { + return + [ string.IsNullOrEmpty(extra) switch { true => value.Value, false => new CsfValue(value.Value.Value, extra) } - }; + ]; } } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V2/CsfFileJsonConverter.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V2/CsfFileJsonConverter.cs similarity index 94% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V2/CsfFileJsonConverter.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V2/CsfFileJsonConverter.cs index 04736212..63801026 100644 --- a/lib/Shimakaze.Sdk/Csf/Json/Converter/V2/CsfFileJsonConverter.cs +++ b/src/Shimakaze.Sdk.Csf.Json/Converter/V2/CsfFileJsonConverter.cs @@ -69,7 +69,7 @@ private static List ReadDataList(ref Utf8JsonReader reader, JsonSeriali { reader.Read().ThrowWhenNull(); reader.TokenType.ThrowWhenNotToken(JsonTokenType.StartObject); - List list = new(); + List list = []; while (reader.Read().ThrowWhenNull()) { @@ -77,7 +77,7 @@ private static List ReadDataList(ref Utf8JsonReader reader, JsonSeriali break; list.Add(new(reader.GetString().ThrowWhenNull()) { - Values = reader.Read>(options).ThrowWhenNull().ToArray() + Values = [.. reader.Read>(options).ThrowWhenNull()] }); } diff --git a/lib/Shimakaze.Sdk/Csf/Json/Converter/V2/CsfJsonSerializerOptions.cs b/src/Shimakaze.Sdk.Csf.Json/Converter/V2/CsfJsonSerializerOptions.cs similarity index 100% rename from lib/Shimakaze.Sdk/Csf/Json/Converter/V2/CsfJsonSerializerOptions.cs rename to src/Shimakaze.Sdk.Csf.Json/Converter/V2/CsfJsonSerializerOptions.cs diff --git a/lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV1Reader.cs b/src/Shimakaze.Sdk.Csf.Json/CsfJsonV1Reader.cs similarity index 84% rename from lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV1Reader.cs rename to src/Shimakaze.Sdk.Csf.Json/CsfJsonV1Reader.cs index 22552a51..c9d610bc 100644 --- a/lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV1Reader.cs +++ b/src/Shimakaze.Sdk.Csf.Json/CsfJsonV1Reader.cs @@ -1,14 +1,13 @@ using System.Text.Json; -using Shimakaze.Sdk.Csf; using Shimakaze.Sdk.Csf.Json.Converter.V1; -namespace Shimakaze.Sdk.IO.Csf.Json; +namespace Shimakaze.Sdk.Csf.Json; /// /// CsfJsonV1Reader. /// -public sealed class CsfJsonV1Reader : AsyncReader, IDisposable, IAsyncDisposable +public sealed class CsfJsonV1Reader : AsyncReader, ICsfReader { private readonly JsonSerializerOptions _options; @@ -30,6 +29,8 @@ public CsfJsonV1Reader(Stream stream, JsonSerializerOptions? options = null, boo /// public override async Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default) { - return await JsonSerializer.DeserializeAsync(BaseStream, _options, cancellationToken); + return await JsonSerializer.DeserializeAsync(BaseStream, _options, cancellationToken) is not CsfDocument csf + ? throw ThrowHelper.CastCsfDocumentException + : csf; } } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV1Writer.cs b/src/Shimakaze.Sdk.Csf.Json/CsfJsonV1Writer.cs similarity index 91% rename from lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV1Writer.cs rename to src/Shimakaze.Sdk.Csf.Json/CsfJsonV1Writer.cs index d9102bed..1da0dfbb 100644 --- a/lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV1Writer.cs +++ b/src/Shimakaze.Sdk.Csf.Json/CsfJsonV1Writer.cs @@ -1,14 +1,13 @@ using System.Text.Json; -using Shimakaze.Sdk.Csf; using Shimakaze.Sdk.Csf.Json.Converter.V1; -namespace Shimakaze.Sdk.IO.Csf.Json; +namespace Shimakaze.Sdk.Csf.Json; /// /// CsfJsonV1Writer. /// -public sealed class CsfJsonV1Writer : AsyncWriter, IDisposable, IAsyncDisposable +public sealed class CsfJsonV1Writer : AsyncWriter, ICsfWriter { private readonly JsonSerializerOptions _options; diff --git a/lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV2Reader.cs b/src/Shimakaze.Sdk.Csf.Json/CsfJsonV2Reader.cs similarity index 79% rename from lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV2Reader.cs rename to src/Shimakaze.Sdk.Csf.Json/CsfJsonV2Reader.cs index f08b3269..ff621498 100644 --- a/lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV2Reader.cs +++ b/src/Shimakaze.Sdk.Csf.Json/CsfJsonV2Reader.cs @@ -1,14 +1,13 @@ using System.Text.Json; -using Shimakaze.Sdk.Csf; using Shimakaze.Sdk.Csf.Json.Converter.V2; -namespace Shimakaze.Sdk.IO.Csf.Json; +namespace Shimakaze.Sdk.Csf.Json; /// /// CsfJsonV2Reader. /// -public sealed class CsfJsonV2Reader : AsyncReader, IDisposable, IAsyncDisposable +public sealed class CsfJsonV2Reader : AsyncReader, ICsfReader { private readonly JsonSerializerOptions _options; @@ -30,12 +29,16 @@ public CsfJsonV2Reader(Stream stream, JsonSerializerOptions? options = null, boo /// public CsfDocument Deserialize() { - return JsonSerializer.Deserialize(BaseStream, _options); + return JsonSerializer.Deserialize(BaseStream, _options) is not CsfDocument csf + ? throw ThrowHelper.CastCsfDocumentException + : csf; } /// public override async Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default) { - return await JsonSerializer.DeserializeAsync(BaseStream, _options, cancellationToken); + return await JsonSerializer.DeserializeAsync(BaseStream, _options, cancellationToken) is not CsfDocument csf + ? throw ThrowHelper.CastCsfDocumentException + : csf; } } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV2Writer.cs b/src/Shimakaze.Sdk.Csf.Json/CsfJsonV2Writer.cs similarity index 91% rename from lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV2Writer.cs rename to src/Shimakaze.Sdk.Csf.Json/CsfJsonV2Writer.cs index 6a2201da..b521e9e3 100644 --- a/lib/Shimakaze.Sdk/IO/Csf/Json/CsfJsonV2Writer.cs +++ b/src/Shimakaze.Sdk.Csf.Json/CsfJsonV2Writer.cs @@ -1,14 +1,13 @@ using System.Text.Json; -using Shimakaze.Sdk.Csf; using Shimakaze.Sdk.Csf.Json.Converter.V2; -namespace Shimakaze.Sdk.IO.Csf.Json; +namespace Shimakaze.Sdk.Csf.Json; /// /// CsfJsonV2Writer. /// -public sealed class CsfJsonV2Writer : AsyncWriter, IDisposable, IAsyncDisposable +public sealed class CsfJsonV2Writer : AsyncWriter, ICsfWriter { private readonly JsonSerializerOptions _options; diff --git a/lib/Shimakaze.Sdk/Csf/Json/JsonConstants.cs b/src/Shimakaze.Sdk.Csf.Json/JsonConstants.cs similarity index 90% rename from lib/Shimakaze.Sdk/Csf/Json/JsonConstants.cs rename to src/Shimakaze.Sdk.Csf.Json/JsonConstants.cs index 4f0cdebd..d25b923c 100644 --- a/lib/Shimakaze.Sdk/Csf/Json/JsonConstants.cs +++ b/src/Shimakaze.Sdk.Csf.Json/JsonConstants.cs @@ -1,11 +1,8 @@ -using System.Diagnostics.CodeAnalysis; - namespace Shimakaze.Sdk.Csf.Json; /// /// JsonConstants. /// -[ExcludeFromCodeCoverage] internal class JsonConstants { /// diff --git a/lib/Shimakaze.Sdk/Csf/Json/JsonConverterExtensions.cs b/src/Shimakaze.Sdk.Csf.Json/JsonConverterExtensions.cs similarity index 87% rename from lib/Shimakaze.Sdk/Csf/Json/JsonConverterExtensions.cs rename to src/Shimakaze.Sdk.Csf.Json/JsonConverterExtensions.cs index 40dc1cbc..5d675602 100644 --- a/lib/Shimakaze.Sdk/Csf/Json/JsonConverterExtensions.cs +++ b/src/Shimakaze.Sdk.Csf.Json/JsonConverterExtensions.cs @@ -1,10 +1,8 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Serialization; namespace Shimakaze.Sdk.Csf.Json; -[ExcludeFromCodeCoverage] internal static class JsonConverterExtensions { public static TJsonConverter Get(this JsonSerializerOptions options) diff --git a/src/Shimakaze.Sdk.Csf.Json/Shimakaze.Sdk.Csf.Json.csproj b/src/Shimakaze.Sdk.Csf.Json/Shimakaze.Sdk.Csf.Json.csproj new file mode 100644 index 00000000..5d8b9a6a --- /dev/null +++ b/src/Shimakaze.Sdk.Csf.Json/Shimakaze.Sdk.Csf.Json.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/lib/Shimakaze.Sdk/Csf/Json/ThrowHelper.cs b/src/Shimakaze.Sdk.Csf.Json/ThrowHelper.cs similarity index 71% rename from lib/Shimakaze.Sdk/Csf/Json/ThrowHelper.cs rename to src/Shimakaze.Sdk.Csf.Json/ThrowHelper.cs index 639938c4..731d4f61 100644 --- a/lib/Shimakaze.Sdk/Csf/Json/ThrowHelper.cs +++ b/src/Shimakaze.Sdk.Csf.Json/ThrowHelper.cs @@ -1,23 +1,26 @@ +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Text.Json; namespace Shimakaze.Sdk.Csf.Json; -[ExcludeFromCodeCoverage] +[StackTraceHidden] internal static class ThrowHelper { [DoesNotReturn] public static T ThrowNotSupportToken(this JsonTokenType value) { - throw new JsonException($"Not Support Token \"{value}\""); + throw new NotSupportedException("Not Support", new JsonException($"Unsupported Token \"{value}\"")); } [DoesNotReturn] public static TResult ThrowNotSupportValue(this TValue value) { - throw new JsonException($"Not Support Value \"{value}\""); + throw new NotSupportedException("Not Support", new JsonException($"Unsupported Value \"{value}\"")); } + public static InvalidCastException CastCsfDocumentException => new($"Cannot Convert as CsfDocument"); + public static void ThrowWhenFalse(this bool value, string message) { if (!value) diff --git a/lib/Shimakaze.Sdk/Csf/Json/Utf8JsonReaderExtensions.cs b/src/Shimakaze.Sdk.Csf.Json/Utf8JsonReaderExtensions.cs similarity index 92% rename from lib/Shimakaze.Sdk/Csf/Json/Utf8JsonReaderExtensions.cs rename to src/Shimakaze.Sdk.Csf.Json/Utf8JsonReaderExtensions.cs index 48355319..240edc24 100644 --- a/lib/Shimakaze.Sdk/Csf/Json/Utf8JsonReaderExtensions.cs +++ b/src/Shimakaze.Sdk.Csf.Json/Utf8JsonReaderExtensions.cs @@ -1,10 +1,8 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Serialization; namespace Shimakaze.Sdk.Csf.Json; -[ExcludeFromCodeCoverage] internal static class Utf8JsonReaderExtensions { public static T? Get(this ref Utf8JsonReader reader, JsonSerializerOptions options) diff --git a/lib/Shimakaze.Sdk/Csf/Json/Utf8JsonWriterExtensions.cs b/src/Shimakaze.Sdk.Csf.Json/Utf8JsonWriterExtensions.cs similarity index 91% rename from lib/Shimakaze.Sdk/Csf/Json/Utf8JsonWriterExtensions.cs rename to src/Shimakaze.Sdk.Csf.Json/Utf8JsonWriterExtensions.cs index dd1da4a4..e5ad47bf 100644 --- a/lib/Shimakaze.Sdk/Csf/Json/Utf8JsonWriterExtensions.cs +++ b/src/Shimakaze.Sdk.Csf.Json/Utf8JsonWriterExtensions.cs @@ -1,10 +1,8 @@ -using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; namespace Shimakaze.Sdk.Csf.Json; -[ExcludeFromCodeCoverage] internal static class Utf8JsonWriterExtensions { public static void WriteProperty(this Utf8JsonWriter writer, string propertyName, T value, JsonSerializerOptions options) diff --git a/lib/Shimakaze.Sdk/Csf/Xml/Converter/IXmlSerializer.cs b/src/Shimakaze.Sdk.Csf.Xml/Converter/IXmlSerializer.cs similarity index 100% rename from lib/Shimakaze.Sdk/Csf/Xml/Converter/IXmlSerializer.cs rename to src/Shimakaze.Sdk.Csf.Xml/Converter/IXmlSerializer.cs diff --git a/lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfDataListXmlSerializer.cs b/src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfDataListXmlSerializer.cs similarity index 96% rename from lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfDataListXmlSerializer.cs rename to src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfDataListXmlSerializer.cs index dd74229c..fd3cc4f1 100644 --- a/lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfDataListXmlSerializer.cs +++ b/src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfDataListXmlSerializer.cs @@ -12,7 +12,7 @@ public class CsfDataListXmlSerializer : IXmlSerializer> /// public IList Deserialize(XmlReader reader) { - List data = new(); + List data = []; while (reader.Read()) { switch (reader.NodeType) diff --git a/lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfDataXmlSerializer.cs b/src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfDataXmlSerializer.cs similarity index 94% rename from lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfDataXmlSerializer.cs rename to src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfDataXmlSerializer.cs index e635cf11..06d2efee 100644 --- a/lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfDataXmlSerializer.cs +++ b/src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfDataXmlSerializer.cs @@ -23,7 +23,7 @@ public CsfData Deserialize(XmlReader reader) label.Values = reader.GetAttribute("extra") switch { not null => new[] { _csfValueXmlSerializer.Deserialize(reader) }, - _ => _csfValueListXmlSerializer.Deserialize(reader).ToArray(), + _ => [.. _csfValueListXmlSerializer.Deserialize(reader)], }; } return label; diff --git a/lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfDocumentXmlSerializer.cs b/src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfDocumentXmlSerializer.cs similarity index 89% rename from lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfDocumentXmlSerializer.cs rename to src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfDocumentXmlSerializer.cs index 1e938825..c377bb77 100644 --- a/lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfDocumentXmlSerializer.cs +++ b/src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfDocumentXmlSerializer.cs @@ -1,3 +1,4 @@ +using System.Globalization; using System.Xml; namespace Shimakaze.Sdk.Csf.Xml.Converter.V1; @@ -31,7 +32,7 @@ public CsfDocument Deserialize(XmlReader reader) return new() { Metadata = head, - Data = _csfDataListXmlSerializer.Deserialize(reader).ToArray() + Data = [.. _csfDataListXmlSerializer.Deserialize(reader)] }; } @@ -44,8 +45,8 @@ public void Serialize(XmlWriter writer, CsfDocument value) // writer.WriteStartElement("Resources"); writer.WriteAttributeString("protocol", "1"); - writer.WriteAttributeString("version", value.Metadata.Version.ToString()); - writer.WriteAttributeString("language", value.Metadata.Language.ToString()); + writer.WriteAttributeString("version", value.Metadata.Version.ToString(CultureInfo.InvariantCulture)); + writer.WriteAttributeString("language", value.Metadata.Language.ToString(CultureInfo.InvariantCulture)); _csfDataListXmlSerializer.Serialize(writer, value.Data); diff --git a/lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfValueListXmlSerializer.cs b/src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfValueListXmlSerializer.cs similarity index 98% rename from lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfValueListXmlSerializer.cs rename to src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfValueListXmlSerializer.cs index 9809cacd..b5c0823b 100644 --- a/lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfValueListXmlSerializer.cs +++ b/src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfValueListXmlSerializer.cs @@ -12,7 +12,7 @@ public class CsfValueListXmlSerializer : IXmlSerializer> /// public IList Deserialize(XmlReader reader) { - List values = new(); + List values = []; while (reader.Read()) { switch (reader.NodeType) diff --git a/lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfValueXmlSerializer.cs b/src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfValueXmlSerializer.cs similarity index 100% rename from lib/Shimakaze.Sdk/Csf/Xml/Converter/V1/CsfValueXmlSerializer.cs rename to src/Shimakaze.Sdk.Csf.Xml/Converter/V1/CsfValueXmlSerializer.cs diff --git a/src/Shimakaze.Sdk.Csf.Xml/CsfXmlV1Reader.cs b/src/Shimakaze.Sdk.Csf.Xml/CsfXmlV1Reader.cs new file mode 100644 index 00000000..09d281a2 --- /dev/null +++ b/src/Shimakaze.Sdk.Csf.Xml/CsfXmlV1Reader.cs @@ -0,0 +1,27 @@ +using System.Xml; + +using Shimakaze.Sdk.Csf.Xml.Converter.V1; + +namespace Shimakaze.Sdk.Csf.Xml; + +/// +/// CsfXmlV1Reader. +/// +/// +/// 构造器 +/// +/// 基础流 +/// 退出时是否保持流打开 +public sealed class CsfXmlV1Reader(TextReader stream, bool leaveOpen = false) : AsyncTextReader(stream, leaveOpen), ICsfReader +{ + + /// + public override async Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default) + { + await Task.Yield(); + + CsfDocumentXmlSerializer serializer = new(); + using XmlReader xmlReader = XmlReader.Create(BaseReader); + return serializer.Deserialize(xmlReader); + } +} \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Csf.Xml/CsfXmlV1Writer.cs b/src/Shimakaze.Sdk.Csf.Xml/CsfXmlV1Writer.cs new file mode 100644 index 00000000..ff598cfc --- /dev/null +++ b/src/Shimakaze.Sdk.Csf.Xml/CsfXmlV1Writer.cs @@ -0,0 +1,28 @@ +using System.Xml; + +using Shimakaze.Sdk.Csf.Xml.Converter.V1; + +namespace Shimakaze.Sdk.Csf.Xml; + +/// +/// CsfXmlV1Writer. +/// +/// +/// +/// +/// +/// +/// ˳ʱǷ񱣳 +public sealed class CsfXmlV1Writer(TextWriter stream, XmlWriterSettings? settings = null, bool leaveOpen = false) : AsyncTextWriter(stream, leaveOpen), ICsfWriter +{ + + /// + public override async Task WriteAsync(CsfDocument value, IProgress? progress = default, CancellationToken cancellationToken = default) + { + await Task.Yield(); + + CsfDocumentXmlSerializer serializer = new(); + using XmlWriter xmlWriter = XmlWriter.Create(BaseWriter, settings); + serializer.Serialize(xmlWriter, value); + } +} \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Csf.Xml/Shimakaze.Sdk.Csf.Xml.csproj b/src/Shimakaze.Sdk.Csf.Xml/Shimakaze.Sdk.Csf.Xml.csproj new file mode 100644 index 00000000..e51e71ce --- /dev/null +++ b/src/Shimakaze.Sdk.Csf.Xml/Shimakaze.Sdk.Csf.Xml.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/Shimakaze.Sdk/Csf/Xml/XmlConstants.cs b/src/Shimakaze.Sdk.Csf.Xml/XmlConstants.cs similarity index 79% rename from lib/Shimakaze.Sdk/Csf/Xml/XmlConstants.cs rename to src/Shimakaze.Sdk.Csf.Xml/XmlConstants.cs index 844a7308..84cd0825 100644 --- a/lib/Shimakaze.Sdk/Csf/Xml/XmlConstants.cs +++ b/src/Shimakaze.Sdk.Csf.Xml/XmlConstants.cs @@ -1,8 +1,5 @@ -using System.Diagnostics.CodeAnalysis; - namespace Shimakaze.Sdk.Csf.Xml; -[ExcludeFromCodeCoverage] internal class XmlConstants { public static class SchemaUrls diff --git a/lib/Shimakaze.Sdk/Csf/Yaml/Converter/V1/CsfDataConverter.cs b/src/Shimakaze.Sdk.Csf.Yaml/Converter/V1/CsfDataConverter.cs similarity index 94% rename from lib/Shimakaze.Sdk/Csf/Yaml/Converter/V1/CsfDataConverter.cs rename to src/Shimakaze.Sdk.Csf.Yaml/Converter/V1/CsfDataConverter.cs index a8793042..c306dedb 100644 --- a/lib/Shimakaze.Sdk/Csf/Yaml/Converter/V1/CsfDataConverter.cs +++ b/src/Shimakaze.Sdk.Csf.Yaml/Converter/V1/CsfDataConverter.cs @@ -36,7 +36,7 @@ public static CsfDataConverter Instance if (parser.Accept(out var label)) { CsfData data = new(label.Value); - List values = new(); + List values = []; if (parser.TryConsume(out _)) { ParseValue(parser, values); @@ -49,7 +49,7 @@ public static CsfDataConverter Instance } } - data.Values = values.ToArray(); + data.Values = [.. values]; data.ReCount(); return data; } @@ -91,7 +91,7 @@ public void WriteYaml(IEmitter emitter, object? value, Type type) } } - private static void ParseValue(IParser parser, IList data) + private static void ParseValue(IParser parser, List data) { if (CsfValueConverter.Instance.ReadYaml(parser, typeof(CsfValue)) is CsfValue value) { diff --git a/lib/Shimakaze.Sdk/Csf/Yaml/Converter/V1/CsfDocumentConverter.cs b/src/Shimakaze.Sdk.Csf.Yaml/Converter/V1/CsfDocumentConverter.cs similarity index 93% rename from lib/Shimakaze.Sdk/Csf/Yaml/Converter/V1/CsfDocumentConverter.cs rename to src/Shimakaze.Sdk.Csf.Yaml/Converter/V1/CsfDocumentConverter.cs index 9da9eec7..391d0c0e 100644 --- a/lib/Shimakaze.Sdk/Csf/Yaml/Converter/V1/CsfDocumentConverter.cs +++ b/src/Shimakaze.Sdk.Csf.Yaml/Converter/V1/CsfDocumentConverter.cs @@ -1,4 +1,6 @@ -using YamlDotNet.Core; +using System.Globalization; + +using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization; @@ -47,7 +49,7 @@ public static CsfDocumentConverter Instance CsfDocument doc = new(); CsfMetadata metadata = doc.Metadata; - List datas = new(); + List datas = []; parser.Consume(); while (!parser.TryConsume(out _)) @@ -72,7 +74,7 @@ public static CsfDocumentConverter Instance case "version": if (parser.TryConsume(out var scalar2)) { - metadata.Version = int.Parse(scalar2.Value); + metadata.Version = int.Parse(scalar2.Value, CultureInfo.InvariantCulture); } break; @@ -96,7 +98,7 @@ public static CsfDocumentConverter Instance metadata.LabelCount = doc.Data.Length; metadata.StringCount = doc.Data.Sum(i => i.StringCount); doc.Metadata = metadata; - doc.Data = datas.ToArray(); + doc.Data = [.. datas]; doc.ReCount(); return doc; } @@ -118,11 +120,11 @@ public void WriteYaml(IEmitter emitter, object? value, Type type) } else { - emitter.Emit(new Scalar(doc.Metadata.Language.ToString())); + emitter.Emit(new Scalar(doc.Metadata.Language.ToString(CultureInfo.InvariantCulture))); } emitter.Emit(new Scalar("version")); - emitter.Emit(new Scalar(doc.Metadata.Version.ToString())); + emitter.Emit(new Scalar(doc.Metadata.Version.ToString(CultureInfo.InvariantCulture))); emitter.Emit(new MappingEnd()); emitter.Emit(new DocumentEnd(true)); emitter.Emit(new DocumentStart()); diff --git a/lib/Shimakaze.Sdk/Csf/Yaml/Converter/V1/CsfValueConverter.cs b/src/Shimakaze.Sdk.Csf.Yaml/Converter/V1/CsfValueConverter.cs similarity index 98% rename from lib/Shimakaze.Sdk/Csf/Yaml/Converter/V1/CsfValueConverter.cs rename to src/Shimakaze.Sdk.Csf.Yaml/Converter/V1/CsfValueConverter.cs index e6917a65..4fd38f06 100644 --- a/lib/Shimakaze.Sdk/Csf/Yaml/Converter/V1/CsfValueConverter.cs +++ b/src/Shimakaze.Sdk.Csf.Yaml/Converter/V1/CsfValueConverter.cs @@ -39,7 +39,7 @@ public static CsfValueConverter Instance } else if (parser.TryConsume(out _)) { - Dictionary map = new(); + Dictionary map = []; string? key = null; while (!parser.TryConsume(out _)) { diff --git a/src/Shimakaze.Sdk.Csf.Yaml/CsfYamlV1Reader.cs b/src/Shimakaze.Sdk.Csf.Yaml/CsfYamlV1Reader.cs new file mode 100644 index 00000000..2084eecc --- /dev/null +++ b/src/Shimakaze.Sdk.Csf.Yaml/CsfYamlV1Reader.cs @@ -0,0 +1,29 @@ +using Shimakaze.Sdk.Csf.Yaml.Converter.V1; + +using YamlDotNet.Serialization; + +namespace Shimakaze.Sdk.Csf.Yaml; + +/// +/// CSF YAML Deserializer. +/// +/// +/// 构造器 +/// +/// 基础流 +/// 退出时是否保持流打开 +public sealed class CsfYamlV1Reader(TextReader reader, bool leaveOpen = false) : AsyncTextReader(reader, leaveOpen), ICsfReader +{ + /// + public override async Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default) + { + await Task.Yield(); + + return new DeserializerBuilder() + .WithTypeConverter(CsfValueConverter.Instance) + .WithTypeConverter(CsfDataConverter.Instance) + .WithTypeConverter(CsfDocumentConverter.Instance) + .Build() + .Deserialize(BaseReader); + } +} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Csf/Yaml/CsfYamlV1Writer.cs b/src/Shimakaze.Sdk.Csf.Yaml/CsfYamlV1Writer.cs similarity index 52% rename from lib/Shimakaze.Sdk/IO/Csf/Yaml/CsfYamlV1Writer.cs rename to src/Shimakaze.Sdk.Csf.Yaml/CsfYamlV1Writer.cs index 6061ce09..512ab34c 100644 --- a/lib/Shimakaze.Sdk/IO/Csf/Yaml/CsfYamlV1Writer.cs +++ b/src/Shimakaze.Sdk.Csf.Yaml/CsfYamlV1Writer.cs @@ -1,35 +1,30 @@ -using Shimakaze.Sdk.Csf; using Shimakaze.Sdk.Csf.Yaml.Converter.V1; using YamlDotNet.Serialization; -namespace Shimakaze.Sdk.IO.Csf.Yaml; +namespace Shimakaze.Sdk.Csf.Yaml; /// /// CSF YAML Serializer. /// -public sealed class CsfYamlV1Writer : AsyncWriter, IDisposable, IAsyncDisposable +/// +/// +/// +/// +/// ˳ʱǷ񱣳 +public sealed class CsfYamlV1Writer(TextWriter stream, bool leaveOpen = false) : AsyncTextWriter(stream, leaveOpen), ICsfWriter { - /// - /// - /// - /// - /// ˳ʱǷ񱣳 - public CsfYamlV1Writer(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) - { - } /// public override async Task WriteAsync(CsfDocument value, IProgress? progress = default, CancellationToken cancellationToken = default) { await Task.Yield(); - using StreamWriter writer = new(BaseStream, leaveOpen: true); new SerializerBuilder() .WithTypeConverter(CsfValueConverter.Instance) .WithTypeConverter(CsfDataConverter.Instance) .WithTypeConverter(CsfDocumentConverter.Instance) .Build() - .Serialize(writer, value); + .Serialize(BaseWriter, value); } } \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Csf.Yaml/Shimakaze.Sdk.Csf.Yaml.csproj b/src/Shimakaze.Sdk.Csf.Yaml/Shimakaze.Sdk.Csf.Yaml.csproj new file mode 100644 index 00000000..2df29bdc --- /dev/null +++ b/src/Shimakaze.Sdk.Csf.Yaml/Shimakaze.Sdk.Csf.Yaml.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/lib/Shimakaze.Sdk/Csf/Yaml/YamlConstants.cs b/src/Shimakaze.Sdk.Csf.Yaml/YamlConstants.cs similarity index 85% rename from lib/Shimakaze.Sdk/Csf/Yaml/YamlConstants.cs rename to src/Shimakaze.Sdk.Csf.Yaml/YamlConstants.cs index 400255f2..0e0f911b 100644 --- a/lib/Shimakaze.Sdk/Csf/Yaml/YamlConstants.cs +++ b/src/Shimakaze.Sdk.Csf.Yaml/YamlConstants.cs @@ -1,18 +1,15 @@ -using System.Diagnostics.CodeAnalysis; - namespace Shimakaze.Sdk.Csf.Yaml; /// /// YamlConstants. /// -[ExcludeFromCodeCoverage] internal class YamlConstants { /// /// LanguageList. /// - public static readonly List LanguageList = new() - { + public static readonly List LanguageList = + [ "en_US", "en_UK", "de", @@ -23,7 +20,7 @@ internal class YamlConstants "Jabberwockie", "kr", "zh", - }; + ]; /// /// SchemaUrls. diff --git a/lib/Shimakaze.Sdk/Csf/CsfConstants.cs b/src/Shimakaze.Sdk.Csf/CsfConstants.cs similarity index 94% rename from lib/Shimakaze.Sdk/Csf/CsfConstants.cs rename to src/Shimakaze.Sdk.Csf/CsfConstants.cs index f0177481..a86c5af6 100644 --- a/lib/Shimakaze.Sdk/Csf/CsfConstants.cs +++ b/src/Shimakaze.Sdk.Csf/CsfConstants.cs @@ -1,11 +1,8 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Shimakaze.Sdk.Csf; +namespace Shimakaze.Sdk.Csf; /// /// CsfConstants. /// -[ExcludeFromCodeCoverage] public static class CsfConstants { /// diff --git a/lib/Shimakaze.Sdk/Csf/CsfData.cs b/src/Shimakaze.Sdk.Csf/CsfData.cs similarity index 68% rename from lib/Shimakaze.Sdk/Csf/CsfData.cs rename to src/Shimakaze.Sdk.Csf/CsfData.cs index b8594408..dda27073 100644 --- a/lib/Shimakaze.Sdk/Csf/CsfData.cs +++ b/src/Shimakaze.Sdk.Csf/CsfData.cs @@ -3,8 +3,13 @@ /// /// Csf Data. /// -public record struct CsfData +public record class CsfData { + internal int InternalIdentifier = CsfConstants.LblFlagRaw; + internal int InternalStringCount; + internal int InternalLabelNameLength; + internal string InternalLabelName; + /// /// Initializes a new instance of the class. /// @@ -43,37 +48,52 @@ public CsfData(string labelName, IEnumerable values) /// values. public CsfData(int identifier, int stringCount, int labelNameLength, string labelName, IEnumerable values) { - Identifier = identifier; - StringCount = stringCount; - LabelNameLength = labelNameLength; - LabelName = labelName; + InternalIdentifier = identifier; + InternalStringCount = stringCount; + InternalLabelNameLength = labelNameLength; + InternalLabelName = labelName; Values = values.ToArray(); } /// /// Gets or sets identifier. /// - public int Identifier = CsfConstants.LblFlagRaw; - + public int Identifier + { + get => InternalIdentifier; + set => InternalIdentifier = value; + } /// /// Gets or sets stringCount. /// - public int StringCount; + public int StringCount + { + get => InternalStringCount; + set => InternalStringCount = value; + } /// /// Gets or sets labelNameLength. /// - public int LabelNameLength; + public int LabelNameLength + { + get => InternalLabelNameLength; + set => InternalLabelNameLength = value; + } /// /// Gets or sets labelName. /// - public string LabelName; + public string LabelName + { + get => InternalLabelName; + set => InternalLabelName = value; + } /// /// Gets or sets values. /// - public CsfValue[] Values; + public CsfValue[] Values { get; set; } /// /// Re Count. diff --git a/lib/Shimakaze.Sdk/Csf/CsfDocument.cs b/src/Shimakaze.Sdk.Csf/CsfDocument.cs similarity index 76% rename from lib/Shimakaze.Sdk/Csf/CsfDocument.cs rename to src/Shimakaze.Sdk.Csf/CsfDocument.cs index f4f42a76..0bf921f8 100644 --- a/lib/Shimakaze.Sdk/Csf/CsfDocument.cs +++ b/src/Shimakaze.Sdk.Csf/CsfDocument.cs @@ -3,8 +3,10 @@ /// /// Csf Document. /// -public record struct CsfDocument +public record class CsfDocument { + internal CsfMetadata InternalMetadata; + /// /// Initializes a new instance of the class. /// @@ -36,12 +38,16 @@ public CsfDocument(CsfMetadata metadata, IEnumerable data) /// /// Gets or sets metadata. /// - public CsfMetadata Metadata; + public CsfMetadata Metadata + { + get => InternalMetadata; + set => InternalMetadata = value; + } /// /// Gets or sets datas. /// - public CsfData[] Data = Array.Empty(); + public CsfData[] Data { get; set; } = []; /// /// ReCount. @@ -51,7 +57,7 @@ public void ReCount() foreach (CsfData item in Data) item.ReCount(); - Metadata.LabelCount = Data.Length; - Metadata.StringCount = Data.Select(x => x.StringCount).Sum(); + InternalMetadata.LabelCount = Data.Length; + InternalMetadata.StringCount = Data.Select(x => x.StringCount).Sum(); } } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Csf/CsfMetadata.cs b/src/Shimakaze.Sdk.Csf/CsfMetadata.cs similarity index 82% rename from lib/Shimakaze.Sdk/Csf/CsfMetadata.cs rename to src/Shimakaze.Sdk.Csf/CsfMetadata.cs index 8a2b7fc4..82eefbf8 100644 --- a/lib/Shimakaze.Sdk/Csf/CsfMetadata.cs +++ b/src/Shimakaze.Sdk.Csf/CsfMetadata.cs @@ -4,7 +4,7 @@ namespace Shimakaze.Sdk.Csf; /// /// CsfMetadata. /// -[StructLayout(LayoutKind.Explicit)] +[StructLayout(LayoutKind.Sequential)] public record struct CsfMetadata { /// @@ -32,36 +32,30 @@ public CsfMetadata(int version, int language) /// /// Identifier. /// - [FieldOffset(sizeof(int) * 0)] public int Identifier; /// /// Version. /// - [FieldOffset(sizeof(int) * 1)] public int Version; /// /// LabelCount. /// - [FieldOffset(sizeof(int) * 2)] public int LabelCount; /// /// StringCount. /// - [FieldOffset(sizeof(int) * 3)] public int StringCount; /// /// Unknown. /// - [FieldOffset(sizeof(int) * 4)] public int Unknown; /// /// Language. /// - [FieldOffset(sizeof(int) * 5)] public int Language; }; \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Csf/CsfReader.cs b/src/Shimakaze.Sdk.Csf/CsfReader.cs similarity index 60% rename from lib/Shimakaze.Sdk/IO/Csf/CsfReader.cs rename to src/Shimakaze.Sdk.Csf/CsfReader.cs index c0908112..b24b4459 100644 --- a/lib/Shimakaze.Sdk/IO/Csf/CsfReader.cs +++ b/src/Shimakaze.Sdk.Csf/CsfReader.cs @@ -1,26 +1,20 @@ -using Shimakaze.Sdk.Csf; - -namespace Shimakaze.Sdk.IO.Csf; +namespace Shimakaze.Sdk.Csf; /// /// Csf ȡ /// -public sealed class CsfReader : AsyncReader, IDisposable, IAsyncDisposable +/// +/// Csf ȡ +/// +/// +/// ˳ʱǷ񱣳 +public sealed class CsfReader(Stream stream, bool leaveOpen = false) : AsyncReader(stream, leaveOpen), ICsfReader { - /// - /// Csf ȡ - /// - /// - /// ˳ʱǷ񱣳 - public CsfReader(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) - { - } - /// public override async Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default) { CsfDocument csf = new(); - BaseStream.Read(out csf.Metadata); + BaseStream.Read(out csf.InternalMetadata); CsfThrowHelper.IsCsfFile(csf.Metadata.Identifier); csf.Data = new CsfData[csf.Metadata.LabelCount]; @@ -29,24 +23,25 @@ public override async Task ReadAsync(IProgress? progress = d for (int i = 0; i < csf.Metadata.LabelCount; i++) { cancellationToken.ThrowIfCancellationRequested(); + csf.Data[i] ??= new(); progress?.Report((float)i / csf.Data.Length); - BaseStream.Read(out csf.Data[i].Identifier); + BaseStream.Read(out csf.Data[i].InternalIdentifier); CsfThrowHelper.IsLabel(csf.Data[i].Identifier, () => new object[] { i, BaseStream.Position }); - BaseStream.Read(out csf.Data[i].StringCount); - BaseStream.Read(out csf.Data[i].LabelNameLength); - BaseStream.Read(out csf.Data[i].LabelName, csf.Data[i].LabelNameLength); + BaseStream.Read(out csf.Data[i].InternalStringCount); + BaseStream.Read(out csf.Data[i].InternalLabelNameLength); + BaseStream.Read(out csf.Data[i].InternalLabelName, csf.Data[i].LabelNameLength); csf.Data[i].Values = new CsfValue[csf.Data[i].StringCount]; for (int j = 0; j < csf.Data[i].StringCount; j++) { cancellationToken.ThrowIfCancellationRequested(); - BaseStream.Read(out csf.Data[i].Values[j].Identifier); + BaseStream.Read(out csf.Data[i].Values[j].InternalIdentifier); CsfThrowHelper.IsStringOrExtraString(csf.Data[i].Values[j].Identifier, () => new object[] { i, j, BaseStream.Position }); - BaseStream.Read(out csf.Data[i].Values[j].ValueLength); - BaseStream.Read(out csf.Data[i].Values[j].Value, csf.Data[i].Values[j].ValueLength, true); + BaseStream.Read(out csf.Data[i].Values[j].InternalValueLength); + BaseStream.Read(out csf.Data[i].Values[j].InternalValue, csf.Data[i].Values[j].ValueLength, true); unsafe { fixed (char* ptr = csf.Data[i].Values[j].Value) @@ -57,7 +52,7 @@ public override async Task ReadAsync(IProgress? progress = d { BaseStream.Read(out int length); csf.Data[i].Values[j].ExtraValueLength = length; - BaseStream.Read(out csf.Data[i].Values[j].ExtraValue, length); + BaseStream.Read(out csf.Data[i].Values[j].InternalExtraValue, length); } } } diff --git a/lib/Shimakaze.Sdk/IO/Csf/CsfMerger.cs b/src/Shimakaze.Sdk.Csf/CsfSet.cs similarity index 67% rename from lib/Shimakaze.Sdk/IO/Csf/CsfMerger.cs rename to src/Shimakaze.Sdk.Csf/CsfSet.cs index 154bd1c6..695e3490 100644 --- a/lib/Shimakaze.Sdk/IO/Csf/CsfMerger.cs +++ b/src/Shimakaze.Sdk.Csf/CsfSet.cs @@ -1,31 +1,28 @@ using System.Collections; -using System.Diagnostics.CodeAnalysis; -using Shimakaze.Sdk.Csf; - -namespace Shimakaze.Sdk.IO.Csf; +namespace Shimakaze.Sdk.Csf; /// /// Csf合并器 /// -public class CsfMerger : ISet +public class CsfSet + : ISet { /// /// 内部的词典 /// - protected readonly Dictionary _cache = new(); + protected Dictionary Cache { get; } = []; /// - public virtual int Count => _cache.Count; + public virtual int Count => Cache.Count; /// - public virtual bool IsReadOnly { get; } = false; + public virtual bool IsReadOnly { get; } /// - public virtual bool Add(CsfData item) => _cache.TryAdd(item.LabelName, item); + public virtual bool Add(CsfData item) => Cache.TryAdd(item.LabelName, item); /// - [ExcludeFromCodeCoverage] void ICollection.Add(CsfData item) => Add(item); /// @@ -40,7 +37,7 @@ public virtual CsfDocument Build(int language = 0, int version = 3, int unknown return new(new(version, language) { Unknown = unknown - }, _cache.Values); + }, Cache.Values); } /// @@ -59,27 +56,25 @@ public virtual async Task BuildAndWriteToAsync(Stream stream, int language = 0, } /// - public virtual void Clear() => _cache.Clear(); + public virtual void Clear() => Cache.Clear(); /// - public virtual bool Contains(CsfData item) => _cache.TryGetValue(item.LabelName, out _); + public virtual bool Contains(CsfData item) => Cache.TryGetValue(item.LabelName, out _); /// - public virtual void CopyTo(CsfData[] array, int arrayIndex) => _cache.Values.CopyTo(array, arrayIndex); + public virtual void CopyTo(CsfData[] array, int arrayIndex) => Cache.Values.CopyTo(array, arrayIndex); /// public virtual void ExceptWith(IEnumerable other) { foreach (var item in other) - _cache.Remove(item.LabelName); + Cache.Remove(item.LabelName); } /// - [ExcludeFromCodeCoverage] - public virtual IEnumerator GetEnumerator() => _cache.Values.GetEnumerator(); + public virtual IEnumerator GetEnumerator() => Cache.Values.GetEnumerator(); /// - [ExcludeFromCodeCoverage] IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// @@ -91,32 +86,32 @@ public virtual void IntersectWith(IEnumerable other) } /// - public virtual bool IsProperSubsetOf(IEnumerable other) => other.Count() > Count && !_cache.Values.Any(i => !other.Contains(i)); + public virtual bool IsProperSubsetOf(IEnumerable other) => other.Count() > Count && !Cache.Values.Any(i => !other.Contains(i)); /// - public virtual bool IsProperSupersetOf(IEnumerable other) => other.Count() < Count && !other.Any(i => !_cache.TryGetValue(i.LabelName, out _)); + public virtual bool IsProperSupersetOf(IEnumerable other) => other.Count() < Count && !other.Any(i => !Cache.TryGetValue(i.LabelName, out _)); /// - public virtual bool IsSubsetOf(IEnumerable other) => other.Count() >= Count && !_cache.Values.Any(i => !other.Contains(i)); + public virtual bool IsSubsetOf(IEnumerable other) => other.Count() >= Count && !Cache.Values.Any(i => !other.Contains(i)); /// - public virtual bool IsSupersetOf(IEnumerable other) => other.Count() <= Count && !other.Any(i => !_cache.TryGetValue(i.LabelName, out _)); + public virtual bool IsSupersetOf(IEnumerable other) => other.Count() <= Count && !other.Any(i => !Cache.TryGetValue(i.LabelName, out _)); /// - public virtual bool Overlaps(IEnumerable other) => _cache.Values.Any(i => other.Contains(i)); + public virtual bool Overlaps(IEnumerable other) => Cache.Values.Any(i => other.Contains(i)); /// - public virtual bool Remove(CsfData item) => _cache.Remove(item.LabelName); + public virtual bool Remove(CsfData item) => Cache.Remove(item.LabelName); /// - public virtual bool SetEquals(IEnumerable other) => other.Count() == Count && !_cache.Values.Any(i => !other.Contains(i)); + public virtual bool SetEquals(IEnumerable other) => other.Count() == Count && !Cache.Values.Any(i => !other.Contains(i)); /// public virtual void SymmetricExceptWith(IEnumerable other) { foreach (var item in other) { - if (_cache.TryGetValue(item.LabelName, out var value)) + if (Cache.TryGetValue(item.LabelName, out var value)) Remove(value); else Add(item); @@ -127,6 +122,6 @@ public virtual void SymmetricExceptWith(IEnumerable other) public virtual void UnionWith(IEnumerable other) { foreach (var item in other) - _cache[item.LabelName] = item; + Cache[item.LabelName] = item; } } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Csf/CsfThrowHelper.cs b/src/Shimakaze.Sdk.Csf/CsfThrowHelper.cs similarity index 78% rename from lib/Shimakaze.Sdk/Csf/CsfThrowHelper.cs rename to src/Shimakaze.Sdk.Csf/CsfThrowHelper.cs index 4ed67bee..8833c91a 100644 --- a/lib/Shimakaze.Sdk/Csf/CsfThrowHelper.cs +++ b/src/Shimakaze.Sdk.Csf/CsfThrowHelper.cs @@ -1,4 +1,6 @@ -namespace Shimakaze.Sdk.Csf; +using System.Globalization; + +namespace Shimakaze.Sdk.Csf; /// /// CsfThrowHelper. @@ -26,7 +28,7 @@ public static int IsCsfFile(int flag) public static int IsLabel(int flag, Func args) => flag is CsfConstants.LblFlagRaw ? flag - : throw new FormatException(string.Format("It's not CSF Label Flag. #{0} at 0x{1:X8}.", args())); + : throw new FormatException(string.Format(CultureInfo.InvariantCulture, "It's not CSF Label Flag. #{0} at 0x{1:X8}.", args())); /// /// IsStringOrExtraString. @@ -38,5 +40,5 @@ public static int IsLabel(int flag, Func args) public static int IsStringOrExtraString(int flag, Func args) => flag is CsfConstants.StrFlagRaw or CsfConstants.StrwFlgRaw ? flag - : throw new FormatException(string.Format("It's not CSF String Flag #{0}:{1} at 0x{2:X8}.", args())); + : throw new FormatException(string.Format(CultureInfo.InvariantCulture, "It's not CSF String Flag #{0}:{1} at 0x{2:X8}.", args())); } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Csf/CsfValue.cs b/src/Shimakaze.Sdk.Csf/CsfValue.cs similarity index 62% rename from lib/Shimakaze.Sdk/Csf/CsfValue.cs rename to src/Shimakaze.Sdk.Csf/CsfValue.cs index 9786194d..a7f5c903 100644 --- a/lib/Shimakaze.Sdk/Csf/CsfValue.cs +++ b/src/Shimakaze.Sdk.Csf/CsfValue.cs @@ -7,6 +7,12 @@ namespace Shimakaze.Sdk.Csf; /// public record struct CsfValue { + internal int InternalIdentifier; + internal int InternalValueLength; + internal string InternalValue; + internal int? InternalExtraValueLength; + internal string? InternalExtraValue; + /// /// Initializes a new instance of the class. /// @@ -21,9 +27,9 @@ public CsfValue() /// value. public CsfValue(string value) { - Identifier = CsfConstants.StrFlagRaw; - ValueLength = value.Length; - Value = value; + InternalIdentifier = CsfConstants.StrFlagRaw; + InternalValueLength = value.Length; + InternalValue = value; } /// @@ -53,32 +59,52 @@ public CsfValue(string value, string? extraValue) [MemberNotNullWhen(true, nameof(ExtraValueLength), nameof(ExtraValue))] public bool HasExtra { - readonly get => Identifier is CsfConstants.StrwFlgRaw; + readonly get => InternalIdentifier is CsfConstants.StrwFlgRaw; set => Identifier = value ? CsfConstants.StrwFlgRaw : CsfConstants.StrFlagRaw; } /// /// Gets or sets identifier. /// - public int Identifier; + public int Identifier + { + readonly get => InternalIdentifier; + set => InternalIdentifier = value; + } /// /// Gets or sets value length. /// - public int ValueLength; + public int ValueLength + { + readonly get => InternalValueLength; + set => InternalValueLength = value; + } /// /// Gets or sets value. /// - public string Value; + public string Value + { + readonly get => InternalValue; + set => InternalValue = value; + } /// /// Gets or sets extra value length. /// - public int? ExtraValueLength; + public int? ExtraValueLength + { + readonly get => InternalExtraValueLength; + set => InternalExtraValueLength = value; + } /// /// Gets or sets extra value. /// - public string? ExtraValue; + public string? ExtraValue + { + readonly get => InternalExtraValue; + set => InternalExtraValue = value; + } } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Csf/CsfWriter.cs b/src/Shimakaze.Sdk.Csf/CsfWriter.cs similarity index 93% rename from lib/Shimakaze.Sdk/IO/Csf/CsfWriter.cs rename to src/Shimakaze.Sdk.Csf/CsfWriter.cs index 91a00d6d..66dc6a7a 100644 --- a/lib/Shimakaze.Sdk/IO/Csf/CsfWriter.cs +++ b/src/Shimakaze.Sdk.Csf/CsfWriter.cs @@ -1,11 +1,9 @@ -using Shimakaze.Sdk.Csf; - -namespace Shimakaze.Sdk.IO.Csf; +namespace Shimakaze.Sdk.Csf; /// /// Csf 写入器 /// -public sealed class CsfWriter : AsyncWriter, IDisposable, IAsyncDisposable +public sealed class CsfWriter : AsyncWriter, ICsfWriter { /// /// 构造 Csf 写入器 diff --git a/src/Shimakaze.Sdk.Csf/ICsfReader.cs b/src/Shimakaze.Sdk.Csf/ICsfReader.cs new file mode 100644 index 00000000..6e24b60c --- /dev/null +++ b/src/Shimakaze.Sdk.Csf/ICsfReader.cs @@ -0,0 +1,15 @@ +namespace Shimakaze.Sdk.Csf; + +/// +/// Csf 读取器 +/// +public interface ICsfReader +{ + /// + /// 从流中读取CSF + /// + /// + /// + /// + Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default); +} diff --git a/src/Shimakaze.Sdk.Csf/ICsfWriter.cs b/src/Shimakaze.Sdk.Csf/ICsfWriter.cs new file mode 100644 index 00000000..bfaa4dbd --- /dev/null +++ b/src/Shimakaze.Sdk.Csf/ICsfWriter.cs @@ -0,0 +1,16 @@ +namespace Shimakaze.Sdk.Csf; + +/// +/// Csf 写入器 +/// +public interface ICsfWriter +{ + /// + /// 写入到流中 + /// + /// + /// + /// + /// + Task WriteAsync(CsfDocument value, IProgress? progress = default, CancellationToken cancellationToken = default); +} diff --git a/src/Shimakaze.Sdk.Csf/Shimakaze.Sdk.Csf.csproj b/src/Shimakaze.Sdk.Csf/Shimakaze.Sdk.Csf.csproj new file mode 100644 index 00000000..71dc9cde --- /dev/null +++ b/src/Shimakaze.Sdk.Csf/Shimakaze.Sdk.Csf.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/Shimakaze.Sdk/Hva/HvaFile.cs b/src/Shimakaze.Sdk.Hva/HvaFile.cs similarity index 79% rename from lib/Shimakaze.Sdk/Hva/HvaFile.cs rename to src/Shimakaze.Sdk.Hva/HvaFile.cs index ba58ba37..4fc519cc 100644 --- a/lib/Shimakaze.Sdk/Hva/HvaFile.cs +++ b/src/Shimakaze.Sdk.Hva/HvaFile.cs @@ -15,16 +15,22 @@ namespace Shimakaze.Sdk.Hva; /// /// The HVA format is very simple, just note that the matrices are stored in section-fastest order. /// -public record struct HvaFile +public record class HvaFile { + internal HvaHeader InternalHeader; + /// /// - public HvaHeader Header; + public HvaHeader Header + { + get => InternalHeader; + set => InternalHeader = value; + } /// /// The names of all the sections (null-terminated) /// - public Int128[] SectionNames; + public Int128[] SectionNames { get; set; } = []; /// /// - public HvaFrame[] Frames; + public HvaFrame[] Frames { get; set; } = []; } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Hva/HvaFrame.cs b/src/Shimakaze.Sdk.Hva/HvaFrame.cs similarity index 64% rename from lib/Shimakaze.Sdk/Hva/HvaFrame.cs rename to src/Shimakaze.Sdk.Hva/HvaFrame.cs index dd475033..2d054d64 100644 --- a/lib/Shimakaze.Sdk/Hva/HvaFrame.cs +++ b/src/Shimakaze.Sdk.Hva/HvaFrame.cs @@ -2,10 +2,10 @@ namespace Shimakaze.Sdk.Hva; /// /// -public record struct HvaFrame +public record class HvaFrame { /// /// Transformation matrix for each section /// - public HvaMatrix[] Matrices; + public HvaMatrix[] Matrices { get; set; } = []; } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Hva/HvaHeader.cs b/src/Shimakaze.Sdk.Hva/HvaHeader.cs similarity index 100% rename from lib/Shimakaze.Sdk/Hva/HvaHeader.cs rename to src/Shimakaze.Sdk.Hva/HvaHeader.cs diff --git a/lib/Shimakaze.Sdk/Hva/HvaMatrix.cs b/src/Shimakaze.Sdk.Hva/HvaMatrix.cs similarity index 100% rename from lib/Shimakaze.Sdk/Hva/HvaMatrix.cs rename to src/Shimakaze.Sdk.Hva/HvaMatrix.cs diff --git a/lib/Shimakaze.Sdk/IO/Hva/HvaReader.cs b/src/Shimakaze.Sdk.Hva/HvaReader.cs similarity index 66% rename from lib/Shimakaze.Sdk/IO/Hva/HvaReader.cs rename to src/Shimakaze.Sdk.Hva/HvaReader.cs index 07152cc2..024217ca 100644 --- a/lib/Shimakaze.Sdk/IO/Hva/HvaReader.cs +++ b/src/Shimakaze.Sdk.Hva/HvaReader.cs @@ -1,26 +1,17 @@ -using Shimakaze.Sdk.Hva; - -namespace Shimakaze.Sdk.IO.Hva; +namespace Shimakaze.Sdk.Hva; /// /// HvaReader /// -public sealed class HvaReader : AsyncReader, IDisposable, IAsyncDisposable +public sealed class HvaReader(Stream stream, bool leaveOpen = false) : AsyncReader(stream, leaveOpen), IDisposable, IAsyncDisposable { - /// - /// HvaReader - /// - - public HvaReader(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) - { - } /// public override async Task ReadAsync(IProgress? progress = default, CancellationToken cancellationToken = default) { HvaFile hva = new(); - BaseStream.Read(out hva.Header); + BaseStream.Read(out hva.InternalHeader); hva.SectionNames = new Int128[hva.Header.NumSections]; BaseStream.Read(hva.SectionNames); @@ -34,7 +25,8 @@ public override async Task ReadAsync(IProgress? progress = defau for (int i = 0; i < hva.Frames.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); - progress?.Report(1f / 3 + (2f / 3) * ((float)i / hva.Frames.Length)); + hva.Frames[i] ??= new(); + progress?.Report(1f / 3 + 2f / 3 * ((float)i / hva.Frames.Length)); hva.Frames[i].Matrices = new HvaMatrix[hva.Header.NumSections]; BaseStream.Read(hva.Frames[i].Matrices); diff --git a/lib/Shimakaze.Sdk/IO/Hva/HvaWriter.cs b/src/Shimakaze.Sdk.Hva/HvaWriter.cs similarity index 86% rename from lib/Shimakaze.Sdk/IO/Hva/HvaWriter.cs rename to src/Shimakaze.Sdk.Hva/HvaWriter.cs index a7a4db20..49bc9786 100644 --- a/lib/Shimakaze.Sdk/IO/Hva/HvaWriter.cs +++ b/src/Shimakaze.Sdk.Hva/HvaWriter.cs @@ -1,6 +1,4 @@ -using Shimakaze.Sdk.Hva; - -namespace Shimakaze.Sdk.IO.Hva; +namespace Shimakaze.Sdk.Hva; /// /// HvaWriter @@ -28,7 +26,7 @@ public override async Task WriteAsync(HvaFile value, IProgress? progress for (int i = 0; i < value.Frames.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); - progress?.Report(1f / 3 + (2f / 3) * ((float)i / value.Frames.Length)); + progress?.Report(1f / 3 + 2f / 3 * ((float)i / value.Frames.Length)); HvaFrame item = value.Frames[i]; BaseStream.Write(item.Matrices); diff --git a/src/Shimakaze.Sdk.Hva/Shimakaze.Sdk.Hva.csproj b/src/Shimakaze.Sdk.Hva/Shimakaze.Sdk.Hva.csproj new file mode 100644 index 00000000..71dc9cde --- /dev/null +++ b/src/Shimakaze.Sdk.Hva/Shimakaze.Sdk.Hva.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Shimakaze.Sdk.Ini.Abstractions/BaseIniTokenWriter.cs b/src/Shimakaze.Sdk.Ini.Abstractions/BaseIniTokenWriter.cs new file mode 100644 index 00000000..78a84da1 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Abstractions/BaseIniTokenWriter.cs @@ -0,0 +1,78 @@ + +namespace Shimakaze.Sdk.Ini; + +/// +/// INI Token 流写入器 +/// +public abstract class BaseIniTokenWriter(TextWriter textWriter, bool leaveOpen = false) : IIniTokenWriter + where TIniDocument : IIniDocument + where TIniSection : IIniSection +{ + private bool _disposedValue; + + /// + /// 基础流 + /// + protected TextWriter BaseWriter => textWriter; + + /// + public abstract void Write(in IIniToken token); + + /// + public void WriteLine(in IIniToken token) + { + Write(token); + WriteLine(); + } + + /// + public void WriteLine() => BaseWriter.WriteLine(); + + /// + public void Flush() => BaseWriter.Flush(); + + /// + /// + /// + /// + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + Flush(); + if (!leaveOpen) + BaseWriter.Dispose(); + } + + _disposedValue = true; + } + } + + /// + /// + /// + ~BaseIniTokenWriter() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: false); + } + + /// + public void Dispose() + { + // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + public abstract void Write(in TIniDocument document); + + /// + public abstract void Write(in TIniSection section); + + /// + public abstract void Write(in KeyValuePair keyValuePair); +} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/IIniDocument.cs b/src/Shimakaze.Sdk.Ini.Abstractions/IIniDocument.cs similarity index 97% rename from lib/Shimakaze.Sdk/Ini/IIniDocument.cs rename to src/Shimakaze.Sdk.Ini.Abstractions/IIniDocument.cs index 2f77f41e..80f4312d 100644 --- a/lib/Shimakaze.Sdk/Ini/IIniDocument.cs +++ b/src/Shimakaze.Sdk.Ini.Abstractions/IIniDocument.cs @@ -26,7 +26,7 @@ public interface IIniDocument : IEnumerable /// /// 在文档头部的没有包含在Section中的孤立的键值对 /// - TIniSection Default { get; } + TIniSection DefaultSection { get; } /// /// 获取所有的节的名字 diff --git a/src/Shimakaze.Sdk.Ini.Abstractions/IIniDocumentBinder.cs b/src/Shimakaze.Sdk.Ini.Abstractions/IIniDocumentBinder.cs new file mode 100644 index 00000000..f24c75a5 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Abstractions/IIniDocumentBinder.cs @@ -0,0 +1,16 @@ +namespace Shimakaze.Sdk.Ini; + +/// +/// IniDocument 绑定器 +/// +public interface IIniDocumentBinder + where TIniDocument : IIniDocument + where TIniSection : IIniSection +{ + /// + /// 绑定到 IniDocument + /// + /// + /// + TIniDocument Bind(TIniDocument? ini = default); +} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Ini/IIniSection.cs b/src/Shimakaze.Sdk.Ini.Abstractions/IIniSection.cs similarity index 100% rename from lib/Shimakaze.Sdk/Ini/IIniSection.cs rename to src/Shimakaze.Sdk.Ini.Abstractions/IIniSection.cs diff --git a/src/Shimakaze.Sdk.Ini.Abstractions/IIniToken.cs b/src/Shimakaze.Sdk.Ini.Abstractions/IIniToken.cs new file mode 100644 index 00000000..e16a2b64 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Abstractions/IIniToken.cs @@ -0,0 +1,17 @@ +namespace Shimakaze.Sdk.Ini; + +/// +/// Ini Token +/// +public interface IIniToken +{ + /// + /// Token + /// + public int Token { get; } + /// + /// 值 + /// + public string? Value { get; } + +} \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Ini.Abstractions/IIniTokenReader.cs b/src/Shimakaze.Sdk.Ini.Abstractions/IIniTokenReader.cs new file mode 100644 index 00000000..94511268 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Abstractions/IIniTokenReader.cs @@ -0,0 +1,8 @@ +namespace Shimakaze.Sdk.Ini; + +/// +/// Ini Token 读取器 +/// +public interface IIniTokenReader : IEnumerable, IDisposable +{ +} diff --git a/src/Shimakaze.Sdk.Ini.Abstractions/IIniTokenWriter.cs b/src/Shimakaze.Sdk.Ini.Abstractions/IIniTokenWriter.cs new file mode 100644 index 00000000..5e01c16c --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Abstractions/IIniTokenWriter.cs @@ -0,0 +1,57 @@ +namespace Shimakaze.Sdk.Ini; + +/// +/// INI Token 流写入器 +/// +public interface IIniTokenWriter : IDisposable +{ + /// + /// 写入Token + /// + /// + void Write(in IIniToken token); + + /// + /// 写入换行符 + /// + void WriteLine(); + + /// + /// 写入Token 后写入换行符 + /// + /// + void WriteLine(in IIniToken token); + + /// + /// 冲写流 + /// + void Flush(); +} + +/// +/// INI Token 流写入器 +/// +/// +/// +public interface IIniTokenWriter : IIniTokenWriter + where TIniDocument : IIniDocument + where TIniSection : IIniSection +{ + /// + /// 写INI文件 + /// + /// + void Write(in TIniDocument document); + + /// + /// 写入节 + /// + /// + void Write(in TIniSection section); + + /// + /// 写入键值对 + /// + /// + void Write(in KeyValuePair keyValuePair); +} diff --git a/lib/Shimakaze.Sdk/Ini/IniDocument{TIniSection}.cs b/src/Shimakaze.Sdk.Ini.Abstractions/IniDocument{TIniSection}.cs similarity index 74% rename from lib/Shimakaze.Sdk/Ini/IniDocument{TIniSection}.cs rename to src/Shimakaze.Sdk.Ini.Abstractions/IniDocument{TIniSection}.cs index cbf29751..db7f6e82 100644 --- a/lib/Shimakaze.Sdk/Ini/IniDocument{TIniSection}.cs +++ b/src/Shimakaze.Sdk.Ini.Abstractions/IniDocument{TIniSection}.cs @@ -6,13 +6,15 @@ namespace Shimakaze.Sdk.Ini; /// /// 表示一个INI文档 /// +#pragma warning disable CA1710 // 标识符应具有正确的后缀 public abstract class IniDocument : IIniDocument, ICollection, IDictionary +#pragma warning restore CA1710 // 标识符应具有正确的后缀 where TIniSection : IIniSection { /// /// 在文档头部的没有包含在Section中的孤立的键值对 /// - public abstract TIniSection Default { get; } + public abstract TIniSection DefaultSection { get; } /// /// 内部的用于快速查询的Section字典 @@ -22,8 +24,6 @@ public abstract class IniDocument : IIniDocument, ICol /// public int Count => _data.Count; - bool ICollection>.IsReadOnly => ((ICollection>)_data).IsReadOnly; - bool ICollection.IsReadOnly => ((ICollection>)_data).IsReadOnly; /// [Obsolete("Use SectionNames instead of.")] @@ -39,39 +39,37 @@ public abstract class IniDocument : IIniDocument, ICol /// public ICollection Sections => _data.Values; - /// - /// 键比较器 - /// - protected readonly IEqualityComparer? _defaultSectionKeyComparer; - /// - public TIniSection this[string key] + public TIniSection this[string section] { - get => _data[key]; - set => _data[key] = value; + get => _data[section]; + set => _data[section] = value; } /// - /// 构造一个INI文档 + /// 获取值 /// - /// 节列表 - protected IniDocument(IEnumerable sections) : this() => _data = sections.ToDictionary(i => i.Name); - - /// - protected IniDocument() + /// 节名 + /// 键 + /// + public string this[string section, string key] { + get => _data[section][key]; + set => _data[section][key] = value; } /// /// 构造一个INI文档 /// - /// 节名比较器 - /// 默认节的键比较器 - protected IniDocument(IEqualityComparer sectionNameComparer, IEqualityComparer? defaultSectionKeyComparer = default) - { - _data = new(sectionNameComparer); - _defaultSectionKeyComparer = defaultSectionKeyComparer; - } + /// 节列表 + /// + protected IniDocument(IEnumerable sections, IEqualityComparer? sectionNameComparer = default) + : this(sectionNameComparer) + => _data = sections.ToDictionary(i => i.Name, sectionNameComparer); + + /// + protected IniDocument(IEqualityComparer? sectionNameComparer = default) + => _data = new(sectionNameComparer); /// public void Add(TIniSection item) => _data.Add(item.Name, item); @@ -91,8 +89,6 @@ protected IniDocument(IEqualityComparer sectionNameComparer, IEqualityCo /// [Obsolete("Use ContainsSection instead of.")] public bool ContainsKey(string key) => ContainsSection(key); - bool ICollection.Contains(TIniSection item) => ContainsSection(item.Name); - bool ICollection>.Contains(KeyValuePair item) => ContainsSection(item.Key); /// public bool Remove(string key) => _data.Remove(key); @@ -108,7 +104,8 @@ protected IniDocument(IEqualityComparer sectionNameComparer, IEqualityCo public bool TryGetSection(string key, [MaybeNullWhen(false)] out TIniSection section) => _data.TryGetValue(key, out section); /// [Obsolete("Use TryGetSection instead of.")] - public bool TryGetValue(string key, [MaybeNullWhen(false)] out TIniSection value) => _data.TryGetValue(key, out value); + public virtual bool TryGetValue(string key, [MaybeNullWhen(false)] out TIniSection value) + => _data.TryGetValue(key, out value); /// public IEnumerator GetEnumerator() => _data.Values.GetEnumerator(); @@ -117,7 +114,12 @@ protected IniDocument(IEqualityComparer sectionNameComparer, IEqualityCo IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +#pragma warning disable CA1033 // 接口方法应可由子类型调用 + bool ICollection>.IsReadOnly => ((ICollection>)_data).IsReadOnly; + bool ICollection.IsReadOnly => ((ICollection>)_data).IsReadOnly; + bool ICollection.Contains(TIniSection item) => ContainsSection(item.Name); + bool ICollection>.Contains(KeyValuePair item) => ContainsSection(item.Key); void ICollection.CopyTo(TIniSection[] array, int arrayIndex) => Sections.CopyTo(array, arrayIndex); void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) => ((ICollection>)_data).CopyTo(array, arrayIndex); - +#pragma warning restore CA1033 // 接口方法应可由子类型调用 } \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Ini.Abstractions/Shimakaze.Sdk.Ini.Abstractions.csproj b/src/Shimakaze.Sdk.Ini.Abstractions/Shimakaze.Sdk.Ini.Abstractions.csproj new file mode 100644 index 00000000..c5cf6293 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Abstractions/Shimakaze.Sdk.Ini.Abstractions.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Shimakaze.Sdk.Ini.Ares/AresIniDocument.cs b/src/Shimakaze.Sdk.Ini.Ares/AresIniDocument.cs new file mode 100644 index 00000000..ca94ac53 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Ares/AresIniDocument.cs @@ -0,0 +1,28 @@ +namespace Shimakaze.Sdk.Ini.Ares; + +/// +public sealed class AresIniDocument : IniDocument +{ + /// + public AresIniDocument( + IEnumerable sections, + IEqualityComparer? sectionNameComparer = default, + IEqualityComparer? defaultSectionKeyComparer = default) + : base(sections, sectionNameComparer) + { + DefaultSection = new(";Default;", default, new(defaultSectionKeyComparer)); + } + + /// + public AresIniDocument( + IEqualityComparer? sectionNameComparer = default, + IEqualityComparer? defaultSectionKeyComparer = default) + : base(sectionNameComparer) + { + DefaultSection = new(";Default;", default, new(defaultSectionKeyComparer)); + } + + + /// + public override AresIniSection DefaultSection { get; } +} \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Ini.Ares/AresIniDocumentBinder.cs b/src/Shimakaze.Sdk.Ini.Ares/AresIniDocumentBinder.cs new file mode 100644 index 00000000..75f0cfd8 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Ares/AresIniDocumentBinder.cs @@ -0,0 +1,172 @@ +using System.Text; + +namespace Shimakaze.Sdk.Ini.Ares; + +/// +/// IniDocument 绑定器 +/// +/// +/// +/// +public sealed class AresIniDocumentBinder( + AresIniTokenReader tokenReader, + AresIniDocumentBinderOptions? options = default, + bool leaveOpen = false + ) + : IDisposable, IIniDocumentBinder +{ + private readonly bool _leaveOpen = leaveOpen; + private readonly AresIniTokenReader _tokenReader = tokenReader; + + /// + public AresIniDocument Bind(AresIniDocument? ini = default) + { + options ??= AresIniDocumentBinderOptions.Default; + ini ??= new(options.SectionComparer, options.KeyComparer); + AresIniSection current = ini.DefaultSection; + // 暂存区 + StringBuilder sb = new(); + + // 状态 + bool isSection = false; + bool isBaseSection = false; + bool isComment = false; + string? key = default; + + foreach (var token in _tokenReader) + { + switch (token.Token) + { + case ' ' when !isComment: + sb.Append(' '); + break; + case '\t' when !isComment: + sb.Append('\t'); + break; + case 1 when !isComment && !string.IsNullOrEmpty(token.Value): + sb.Append(token.Value); + break; + + case '=' when !isSection: + key = GetString(options.Trim); + if (key.Trim() == "+") + { + do + { + key = Guid.NewGuid().ToString(); + } while (current.ContainsKey(key)); + } + break; + + case '+' when !isSection: + sb.Append('+'); + break; + + case ':' when !isSection: + Flush(options.Trim); + isBaseSection = true; + break; + + case ';': + Flush(options.Trim); + + // 重置状态 + isComment = true; + isSection = false; + isBaseSection = false; + key = null; + + break; + case '\r': + case '\n': + Flush(options.Trim); + + // 重置状态 + isComment = false; + isSection = false; + isBaseSection = false; + key = null; + + break; + + // 读Section + case '[' when !isComment: + Flush(options.Trim); + isSection = true; + break; + case ']' when !isComment: + isSection = false; + string sectionName = GetString(false); + if (!isBaseSection) + { + if (!ini.TryGetSection(sectionName, out var section)) + section = ini[sectionName] = new(sectionName, default, new(options.KeyComparer)); + current = section; + } + else + { + current.BaseName = sectionName; + } + break; + } + } + Flush(options.Trim); + + // 组织继承结构 + + return BindDependencyTree(ini); + + string GetString(bool trim) + { + string result = sb.ToString(); + sb.Clear(); + return trim ? result.Trim() : result; + } + void Flush(bool trim) + { + string value = GetString(trim); + if (!string.IsNullOrEmpty(value)) + { + if (isSection) + value = '[' + value; + if (!string.IsNullOrEmpty(key)) + current[key] = value; + else + current[value] = string.Empty; + } + else if (!string.IsNullOrEmpty(key)) + { + current[key] = string.Empty; + } + } + } + + /// + /// 绑定或重新绑定继承树 + /// + /// + /// + public AresIniDocument BindDependencyTree(AresIniDocument? ini = default) + { + options ??= AresIniDocumentBinderOptions.Default; + ini ??= new(options.SectionComparer, options.KeyComparer); + // 组织继承结构 + foreach (var item in ini) + { + if (string.IsNullOrEmpty(item.BaseName)) + continue; + + if (ini.TryGetSection(item.BaseName, out var section)) + item.Base = section; + } + + return ini; + } + + /// + public void Dispose() + { + if (!_leaveOpen) + _tokenReader.Dispose(); + } +} diff --git a/src/Shimakaze.Sdk.Ini.Ares/AresIniDocumentBinderOptions.cs b/src/Shimakaze.Sdk.Ini.Ares/AresIniDocumentBinderOptions.cs new file mode 100644 index 00000000..b484ae66 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Ares/AresIniDocumentBinderOptions.cs @@ -0,0 +1,15 @@ +namespace Shimakaze.Sdk.Ini.Ares; + +/// +/// IniDocument 绑定器配置 +/// +public record class AresIniDocumentBinderOptions : IniDocumentBinderOptions +{ + /// + /// 默认配置 + /// + public static new readonly AresIniDocumentBinderOptions Default = new() + { + Trim = true, + }; +} diff --git a/lib/Shimakaze.Sdk/Ini/Ares/AresIniSection.cs b/src/Shimakaze.Sdk.Ini.Ares/AresIniSection.cs similarity index 87% rename from lib/Shimakaze.Sdk/Ini/Ares/AresIniSection.cs rename to src/Shimakaze.Sdk.Ini.Ares/AresIniSection.cs index 09a54f1f..d66874f6 100644 --- a/lib/Shimakaze.Sdk/Ini/Ares/AresIniSection.cs +++ b/src/Shimakaze.Sdk.Ini.Ares/AresIniSection.cs @@ -19,6 +19,11 @@ public sealed class AresIniSection(string name, string? @base, Dictionary + /// 仅获取当前节而不获取基节的数据 + /// + public IDictionary Raw => Data; + /// /// 继承自 /// @@ -27,7 +32,7 @@ public sealed class AresIniSection(string name, string? @base, Dictionary public override ICollection Keys => Base is not null - ? _data + ? Data .Concat(Base) .DistinctBy(i => i.Key) .Select(i => i.Key) @@ -37,7 +42,7 @@ public override ICollection Keys /// public override ICollection Values => Base is not null - ? _data + ? Data .Concat(Base) .DistinctBy(i => i.Key) .Select(i => i.Value) @@ -47,7 +52,7 @@ public override ICollection Values /// public override int Count => Base is not null - ? _data + ? Data .Concat(Base) .DistinctBy(i => i.Key) .Count() @@ -67,11 +72,6 @@ public override string this[string key] set => base[key] = value; } - /// - public AresIniSection() - : this(string.Empty, default, []) - { } - /// public AresIniSection(string name) : this(name, default, []) @@ -81,11 +81,6 @@ public AresIniSection(string name, string? @base) : this(name, @base, []) { } - /// - public AresIniSection(Dictionary map) - : this(string.Empty, default, map) - { } - /// public AresIniSection(string name, Dictionary map) : this(name, default, map) @@ -110,7 +105,7 @@ public override bool TryGetValue(string key, [MaybeNullWhen(false)] out string v /// public override IEnumerator> GetEnumerator() => Base is not null - ? _data + ? Data .Concat(Base) .DistinctBy(i => i.Key) .GetEnumerator() diff --git a/src/Shimakaze.Sdk.Ini.Ares/AresIniTokenReader.cs b/src/Shimakaze.Sdk.Ini.Ares/AresIniTokenReader.cs new file mode 100644 index 00000000..9add6774 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Ares/AresIniTokenReader.cs @@ -0,0 +1,76 @@ +using System.Collections; +using System.Text; + +namespace Shimakaze.Sdk.Ini.Ares; + +/// +/// Ini Token 读取器 +/// +/// +/// +public sealed class AresIniTokenReader(TextReader textReader, bool leaveOpen = false) : IIniTokenReader +{ + private readonly StringBuilder _buffer = new(); + + private IEnumerable ReadAll() + { + while (textReader.Read() is int rawChar and not -1) + { + char ch = (char)rawChar; + + IIniToken? token = ch switch + { + // 空白 + '\r' => AresIniTokenTools.CR, + '\n' => AresIniTokenTools.LF, + ' ' => AresIniTokenTools.SPACE, + '\t' => AresIniTokenTools.TAB, + // 符号 + ';' => AresIniTokenTools.SEMI, + '=' => AresIniTokenTools.EQ, + ':' => AresIniTokenTools.COLON, + '+' => AresIniTokenTools.PLUS, + '[' => AresIniTokenTools.BeginBracket, + ']' => AresIniTokenTools.EndBracket, + // 其他 + _ => default + }; + if (token is not null) + { + if (Flush() is IIniToken iniToken) + yield return iniToken; + yield return token; + } + else + { + _buffer.Append(ch); + } + } + if (Flush() is IIniToken iniToken1) + yield return iniToken1; + + yield return AresIniTokenTools.EOF; + + IIniToken? Flush() + { + if (_buffer.Length is 0) + return null; + + string value = _buffer.ToString(); + _buffer.Clear(); + return AresIniTokenTools.Value(value); + } + } + + /// + public IEnumerator GetEnumerator() => ReadAll().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + public void Dispose() + { + if (!leaveOpen) + textReader.Dispose(); + } +} diff --git a/src/Shimakaze.Sdk.Ini.Ares/AresIniTokenTools.cs b/src/Shimakaze.Sdk.Ini.Ares/AresIniTokenTools.cs new file mode 100644 index 00000000..18d4e0ad --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Ares/AresIniTokenTools.cs @@ -0,0 +1,86 @@ +namespace Shimakaze.Sdk.Ini.Ares; + +/// +/// Token生成工具 +/// +public static class AresIniTokenTools +{ + /// + /// 空白字符 + /// + /// + /// + public static IIniToken White(int token) => new IniToken(token, default); + /// + /// 符号 + /// + /// + /// + public static IIniToken Sign(int token) => new IniToken(token, default); + /// + /// 字符串 + /// + /// + /// + public static IIniToken Value(string value) => new IniToken(1, value); + + // ========================== 空白 ========================== + + /// + /// EOF + /// + public static IIniToken EOF => White(-1); + /// + /// CR + /// + public static IIniToken CR => White('\r'); + /// + /// LF + /// + public static IIniToken LF => White('\n'); + /// + /// SPACE + /// + public static IIniToken SPACE => White(' '); + /// + /// TAB + /// + public static IIniToken TAB => White('\t'); + + // ========================== 符号 ========================== + + /// + /// SEMI + /// + public static IIniToken SEMI => Sign(';'); + /// + /// EQ + /// + public static IIniToken EQ => Sign('='); + /// + /// COLON + /// + public static IIniToken COLON => Sign(':'); + /// + /// PLUS + /// + public static IIniToken PLUS => Sign('+'); + /// + /// BeginBracket + /// + public static IIniToken BeginBracket => Sign('['); + /// + /// EndBracket + /// + public static IIniToken EndBracket => Sign(']'); +} + +file sealed record class IniToken(int Token, string? Value) : IIniToken +{ + public string Type => $"{(char)Token}" + .Replace("\r", "CR") + .Replace("\n", "LF") + .Replace("\x1", "Value") + .Replace("\xFFFF", "EOF") + ; +} diff --git a/src/Shimakaze.Sdk.Ini.Ares/AresIniTokenWriter.cs b/src/Shimakaze.Sdk.Ini.Ares/AresIniTokenWriter.cs new file mode 100644 index 00000000..382d1e86 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Ares/AresIniTokenWriter.cs @@ -0,0 +1,82 @@ +namespace Shimakaze.Sdk.Ini.Ares; + +/// +/// INI Token 流写入器 +/// +/// +/// +public sealed class AresIniTokenWriter(TextWriter textWriter, bool leaveOpen = false) + : BaseIniTokenWriter(textWriter, leaveOpen) +{ + /// + public override void Write(in IIniToken token) + { + switch (token.Token) + { + case 1: + BaseWriter.Write(token.Value); + break; + case '\r': + case '\n': + case ' ': + case '\t': + case ';': + case '=': + case ':': + case '+': + case '[': + case ']': + BaseWriter.Write((char)token.Token); + break; + default: + throw new NotSupportedException(); + } + } + + /// + /// 写INI文件 + /// + /// + public override void Write(in AresIniDocument document) + { + Write(document.DefaultSection); + foreach (var item in document) + Write(item); + } + + /// + /// 写入节主体 + /// + /// + public override void Write(in AresIniSection section) + { + if (section.Name != ";Default;") + { + Write(AresIniTokenTools.BeginBracket); + Write(AresIniTokenTools.Value(section.Name)); + Write(AresIniTokenTools.EndBracket); + if (string.IsNullOrEmpty(section.BaseName)) + { + Write(AresIniTokenTools.COLON); + Write(AresIniTokenTools.BeginBracket); + Write(AresIniTokenTools.Value(section.Name)); + Write(AresIniTokenTools.EndBracket); + } + Write(AresIniTokenTools.LF); + } + foreach (var item in section.Raw) + Write(item); + } + + /// + /// 写入键值对 + /// + /// + public override void Write(in KeyValuePair keyValuePair) + { + Write(AresIniTokenTools.Value(keyValuePair.Key)); + Write(AresIniTokenTools.EQ); + Write(AresIniTokenTools.Value(keyValuePair.Value)); + Write(AresIniTokenTools.LF); + } +} \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Ini.Ares/Shimakaze.Sdk.Ini.Ares.csproj b/src/Shimakaze.Sdk.Ini.Ares/Shimakaze.Sdk.Ini.Ares.csproj new file mode 100644 index 00000000..cd511065 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini.Ares/Shimakaze.Sdk.Ini.Ares.csproj @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Shimakaze.Sdk.Ini/IniDocument.cs b/src/Shimakaze.Sdk.Ini/IniDocument.cs new file mode 100644 index 00000000..28b5e32d --- /dev/null +++ b/src/Shimakaze.Sdk.Ini/IniDocument.cs @@ -0,0 +1,28 @@ +namespace Shimakaze.Sdk.Ini; + +/// +public sealed class IniDocument : IniDocument +{ + /// + public IniDocument( + IEnumerable sections, + IEqualityComparer? sectionNameComparer = default, + IEqualityComparer? defaultSectionKeyComparer = default) + : base(sections, sectionNameComparer) + { + DefaultSection = new(";Default;", new(defaultSectionKeyComparer)); + } + + /// + public IniDocument( + IEqualityComparer? sectionNameComparer = default, + IEqualityComparer? defaultSectionKeyComparer = default) + : base(sectionNameComparer) + { + DefaultSection = new(";Default;", new(defaultSectionKeyComparer)); + } + + + /// + public override IniSection DefaultSection { get; } +} \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Ini/IniDocumentBinder.cs b/src/Shimakaze.Sdk.Ini/IniDocumentBinder.cs new file mode 100644 index 00000000..531e368f --- /dev/null +++ b/src/Shimakaze.Sdk.Ini/IniDocumentBinder.cs @@ -0,0 +1,122 @@ +using System.Text; + +namespace Shimakaze.Sdk.Ini; + +/// +/// IniDocument 绑定器 +/// +/// +/// +/// +public sealed class IniDocumentBinder( + IniTokenReader tokenReader, + IniDocumentBinderOptions? options = default, + bool leaveOpen = false + ) + : IDisposable, IIniDocumentBinder +{ + private readonly bool _leaveOpen = leaveOpen; + private readonly IniTokenReader _tokenReader = tokenReader; + + /// + public IniDocument Bind(IniDocument? ini = default) + { + options ??= IniDocumentBinderOptions.Default; + ini ??= new(options.SectionComparer, options.KeyComparer); + IniSection current = ini.DefaultSection; + // 暂存区 + StringBuilder sb = new(); + + // 状态 + bool isSection = false; + bool isComment = false; + string? key = default; + + foreach (var token in _tokenReader) + { + switch (token.Token) + { + case ' ' when !isComment: + sb.Append(' ').Append(token.Value); + break; + case '\t' when !isComment: + sb.Append('\t').Append(token.Value); + break; + case 1 when !isComment && !string.IsNullOrEmpty(token.Value): + sb.Append(token.Value); + break; + + case '=' when !isSection: + key = GetString(options.Trim); + break; + + case ';': + Flush(options.Trim); + + // 重置状态 + isComment = true; + isSection = false; + key = null; + + break; + case '\r': + case '\n': + Flush(options.Trim); + + // 重置状态 + isComment = false; + isSection = false; + key = null; + + break; + + // 读Section + case '[' when !isComment: + Flush(options.Trim); + isSection = true; + break; + case ']' when !isComment: + isSection = false; + string sectionName = GetString(false); + if (!ini.TryGetSection(sectionName, out var section)) + section = ini[sectionName] = new(sectionName, new(options.KeyComparer)); + current = section; + break; + } + } + Flush(options.Trim); + + return ini; + + string GetString(bool trim) + { + string result = sb.ToString(); + sb.Clear(); + return trim ? result.Trim() : result; + } + void Flush(bool trim) + { + string value = GetString(trim); + if (!string.IsNullOrEmpty(value)) + { + if (isSection) + value = '[' + value; + if (!string.IsNullOrEmpty(key)) + current[key] = value; + else + current[value] = string.Empty; + } + else if (!string.IsNullOrEmpty(key)) + { + current[key] = string.Empty; + } + } + } + + /// + public void Dispose() + { + if (!_leaveOpen) + _tokenReader.Dispose(); + } +} diff --git a/src/Shimakaze.Sdk.Ini/IniDocumentBinderOptions.cs b/src/Shimakaze.Sdk.Ini/IniDocumentBinderOptions.cs new file mode 100644 index 00000000..f7239f76 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini/IniDocumentBinderOptions.cs @@ -0,0 +1,30 @@ +namespace Shimakaze.Sdk.Ini; + +/// +/// IniDocument 绑定器配置 +/// +public record class IniDocumentBinderOptions +{ + /// + /// 默认配置 + /// + public static readonly IniDocumentBinderOptions Default = new() + { + Trim = true, + }; + + /// + /// Trim 键和值的首尾空格 + /// + public bool Trim { get; set; } + + /// + /// 节名称使用的比较器 + /// + public IEqualityComparer? SectionComparer { get; set; } + + /// + /// 节内使用的键的比较器 + /// + public IEqualityComparer? KeyComparer { get; set; } +} diff --git a/lib/Shimakaze.Sdk/Ini/IniSection.cs b/src/Shimakaze.Sdk.Ini/IniSection.cs similarity index 55% rename from lib/Shimakaze.Sdk/Ini/IniSection.cs rename to src/Shimakaze.Sdk.Ini/IniSection.cs index 67583a74..ffb45085 100644 --- a/lib/Shimakaze.Sdk/Ini/IniSection.cs +++ b/src/Shimakaze.Sdk.Ini/IniSection.cs @@ -11,12 +11,14 @@ namespace Shimakaze.Sdk.Ini; /// /// 节名 /// 节数据 (键值对字典) +#pragma warning disable CA1710 // 标识符应具有正确的后缀 public class IniSection(string name, Dictionary map) : IIniSection, IDictionary +#pragma warning restore CA1710 // 标识符应具有正确的后缀 { /// /// 内部的INI键值对字典 /// - protected readonly Dictionary _data = map; + protected Dictionary Data { get; } = map; /// /// Section节名 @@ -24,59 +26,53 @@ public class IniSection(string name, Dictionary map) : IIniSecti public virtual string Name { get; internal set; } = name; /// - public virtual ICollection Keys => _data.Keys; + public virtual ICollection Keys => Data.Keys; /// - public virtual ICollection Values => _data.Values; + public virtual ICollection Values => Data.Values; /// - public virtual int Count => _data.Count; + public virtual int Count => Data.Count; - bool ICollection>.IsReadOnly => ((ICollection>)_data).IsReadOnly; /// - public virtual string this[string key] { get => _data[key]; set => _data[key] = value; } - - /// - public IniSection() - : this(string.Empty, []) - { } + public virtual string this[string key] { get => Data[key]; set => Data[key] = value; } /// public IniSection(string name) : this(name, []) { } - /// - public IniSection(Dictionary map) - : this(string.Empty, map) - { } - /// - public virtual void Add(string key, string value) => _data.Add(key, value); + public virtual void Add(string key, string value) => Data.Add(key, value); /// - public virtual bool ContainsKey(string key) => _data.ContainsKey(key); + public virtual bool ContainsKey(string key) => Data.ContainsKey(key); /// - public virtual bool Remove(string key) => _data.Remove(key); + public virtual bool Remove(string key) => Data.Remove(key); /// - public virtual bool TryGetValue(string key, [MaybeNullWhen(false)] out string value) => _data.TryGetValue(key, out value); + public virtual bool TryGetValue(string key, [MaybeNullWhen(false)] out string value) + => Data.TryGetValue(key, out value); /// - public virtual void Clear() => _data.Clear(); + public virtual void Clear() => Data.Clear(); /// - public virtual IEnumerator> GetEnumerator() => _data.GetEnumerator(); + public virtual IEnumerator> GetEnumerator() => Data.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => Data.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => _data.GetEnumerator(); +#pragma warning disable CA1033 // 接口方法应可由子类型调用 + bool ICollection>.IsReadOnly => ((ICollection>)Data).IsReadOnly; - void ICollection>.Add(KeyValuePair item) => ((ICollection>)_data).Add(item); + void ICollection>.Add(KeyValuePair item) => ((ICollection>)Data).Add(item); - void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) => ((ICollection>)_data).CopyTo(array, arrayIndex); + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) => ((ICollection>)Data).CopyTo(array, arrayIndex); - bool ICollection>.Remove(KeyValuePair item) => ((ICollection>)_data).Remove(item); + bool ICollection>.Remove(KeyValuePair item) => ((ICollection>)Data).Remove(item); - bool ICollection>.Contains(KeyValuePair item) => _data.Contains(item); + bool ICollection>.Contains(KeyValuePair item) => Data.Contains(item); +#pragma warning restore CA1033 // 接口方法应可由子类型调用 } \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Ini/IniTokenReader.cs b/src/Shimakaze.Sdk.Ini/IniTokenReader.cs new file mode 100644 index 00000000..6af7b521 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini/IniTokenReader.cs @@ -0,0 +1,74 @@ +using System.Collections; +using System.Text; + +namespace Shimakaze.Sdk.Ini; + +/// +/// Ini Token 读取器 +/// +/// +/// +public sealed class IniTokenReader(TextReader textReader, bool leaveOpen = false) : IIniTokenReader +{ + private readonly StringBuilder _buffer = new(); + + private IEnumerable ReadAll() + { + while (textReader.Read() is int rawChar and not -1) + { + char ch = (char)rawChar; + + IIniToken? token = ch switch + { + // 空白 + '\r' => IniTokenTools.CR, + '\n' => IniTokenTools.LF, + ' ' => IniTokenTools.SPACE, + '\t' => IniTokenTools.TAB, + // 符号 + ';' => IniTokenTools.SEMI, + '=' => IniTokenTools.EQ, + '[' => IniTokenTools.BeginBracket, + ']' => IniTokenTools.EndBracket, + // 其他 + _ => default + }; + if (token is not null) + { + if (Flush() is IIniToken iniToken) + yield return iniToken; + yield return token; + } + else + { + _buffer.Append(ch); + } + } + if (Flush() is IIniToken iniToken1) + yield return iniToken1; + + yield return IniTokenTools.EOF; + + IIniToken? Flush() + { + if (_buffer.Length is 0) + return null; + + string value = _buffer.ToString(); + _buffer.Clear(); + return IniTokenTools.Value(value); + } + } + + /// + public IEnumerator GetEnumerator() => ReadAll().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + public void Dispose() + { + if (!leaveOpen) + textReader.Dispose(); + } +} diff --git a/src/Shimakaze.Sdk.Ini/IniTokenTools.cs b/src/Shimakaze.Sdk.Ini/IniTokenTools.cs new file mode 100644 index 00000000..767984db --- /dev/null +++ b/src/Shimakaze.Sdk.Ini/IniTokenTools.cs @@ -0,0 +1,78 @@ +namespace Shimakaze.Sdk.Ini; + +/// +/// Token生成工具 +/// +public static class IniTokenTools +{ + /// + /// 空白字符 + /// + /// + /// + public static IIniToken White(int token) => new IniToken(token, default); + /// + /// 符号 + /// + /// + /// + public static IIniToken Sign(int token) => new IniToken(token, default); + /// + /// 字符串 + /// + /// + /// + public static IIniToken Value(string value) => new IniToken(1, value); + + // ========================== 空白 ========================== + + /// + /// EOF + /// + public static IIniToken EOF => White(-1); + /// + /// CR + /// + public static IIniToken CR => White('\r'); + /// + /// LF + /// + public static IIniToken LF => White('\n'); + /// + /// SPACE + /// + public static IIniToken SPACE => White(' '); + /// + /// TAB + /// + public static IIniToken TAB => White('\t'); + + // ========================== 符号 ========================== + + /// + /// SEMI + /// + public static IIniToken SEMI => Sign(';'); + /// + /// EQ + /// + public static IIniToken EQ => Sign('='); + /// + /// BeginBracket + /// + public static IIniToken BeginBracket => Sign('['); + /// + /// EndBracket + /// + public static IIniToken EndBracket => Sign(']'); +} + +file sealed record class IniToken(int Token, string? Value) : IIniToken +{ + public string Type => $"{(char)Token}" + .Replace("\r", "CR") + .Replace("\n", "LF") + .Replace("\x1", "Value") + .Replace("\xFFFF", "EOF") + ; +} diff --git a/src/Shimakaze.Sdk.Ini/IniTokenWriter.cs b/src/Shimakaze.Sdk.Ini/IniTokenWriter.cs new file mode 100644 index 00000000..279e7ec0 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini/IniTokenWriter.cs @@ -0,0 +1,73 @@ +namespace Shimakaze.Sdk.Ini; + +/// +/// INI Token 流写入器 +/// +/// +/// +public sealed class IniTokenWriter(TextWriter textWriter, bool leaveOpen = false) + : BaseIniTokenWriter(textWriter, leaveOpen) +{ + /// + public override void Write(in IIniToken token) + { + switch (token.Token) + { + case 1: + BaseWriter.Write(token.Value); + break; + case '\r': + case '\n': + case ' ': + case '\t': + case ';': + case '=': + case '[': + case ']': + BaseWriter.Write((char)token.Token); + break; + default: + throw new NotSupportedException(); + } + } + + /// + /// 写INI文件 + /// + /// + public override void Write(in IniDocument document) + { + Write(document.DefaultSection); + foreach (var item in document) + Write(item); + } + + /// + /// 写入节主体 + /// + /// + public override void Write(in IniSection section) + { + if (section.Name != ";Default;") + { + Write(IniTokenTools.BeginBracket); + Write(IniTokenTools.Value(section.Name)); + Write(IniTokenTools.EndBracket); + Write(IniTokenTools.LF); + } + foreach (var item in section) + Write(item); + } + + /// + /// 写入键值对 + /// + /// + public override void Write(in KeyValuePair keyValuePair) + { + Write(IniTokenTools.Value(keyValuePair.Key)); + Write(IniTokenTools.EQ); + Write(IniTokenTools.Value(keyValuePair.Value)); + Write(IniTokenTools.LF); + } +} \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Ini/Shimakaze.Sdk.Ini.csproj b/src/Shimakaze.Sdk.Ini/Shimakaze.Sdk.Ini.csproj new file mode 100644 index 00000000..fa42d0d5 --- /dev/null +++ b/src/Shimakaze.Sdk.Ini/Shimakaze.Sdk.Ini.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/Shimakaze.Sdk.JsonRPC.Server/HandlerAttribute.cs b/src/Shimakaze.Sdk.JsonRPC.Server/HandlerAttribute.cs similarity index 100% rename from lib/Shimakaze.Sdk.JsonRPC.Server/HandlerAttribute.cs rename to src/Shimakaze.Sdk.JsonRPC.Server/HandlerAttribute.cs diff --git a/src/Shimakaze.Sdk.JsonRPC.Server/JsonRPCHostedService.cs b/src/Shimakaze.Sdk.JsonRPC.Server/JsonRPCHostedService.cs new file mode 100644 index 00000000..7a3ed893 --- /dev/null +++ b/src/Shimakaze.Sdk.JsonRPC.Server/JsonRPCHostedService.cs @@ -0,0 +1,50 @@ +using System.Collections.Immutable; + +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +using StreamJsonRpc; + +namespace Shimakaze.Sdk.JsonRPC.Server; + +/// +/// JsonRPCHostedService +/// +/// +/// +/// +/// +/// +/// +public sealed partial class JsonRPCHostedService(JsonRPCHostedServiceOptions options, IServiceProvider serviceProvider, ILogger? logger = null) : IHostedService +{ + private readonly JsonRpc _jsonRpc = options.JsonRpc; + private readonly ImmutableArray _methods = options.Targets; + + /// + public Task StartAsync(CancellationToken cancellationToken) + { + foreach (var method in _methods) + { + var isEvent = method.Method.ReturnType == typeof(void); + if (logger is not null) + Find(logger, isEvent ? "Event" : "Method", method.Route); + _jsonRpc.AddLocalRpcMethod(method.Route, method.Method, serviceProvider.GetService(method.Type)); + } + + return Task.Run(_jsonRpc.StartListening, cancellationToken); + } + + /// + public Task StopAsync(CancellationToken cancellationToken) + { + if (logger is not null) + Bye(logger); + return Task.CompletedTask; + } + + [LoggerMessage("Find {Type}: {Path}")] + private static partial void Find(ILogger logger, string type, string path, LogLevel level = LogLevel.Debug); + [LoggerMessage("See you next time!")] + private static partial void Bye(ILogger logger, LogLevel level = LogLevel.Information); +} diff --git a/lib/Shimakaze.Sdk.JsonRPC.Server/JsonRPCHostedServiceOptions.cs b/src/Shimakaze.Sdk.JsonRPC.Server/JsonRPCHostedServiceOptions.cs similarity index 100% rename from lib/Shimakaze.Sdk.JsonRPC.Server/JsonRPCHostedServiceOptions.cs rename to src/Shimakaze.Sdk.JsonRPC.Server/JsonRPCHostedServiceOptions.cs diff --git a/lib/Shimakaze.Sdk.JsonRPC.Server/MethodAttribute.cs b/src/Shimakaze.Sdk.JsonRPC.Server/MethodAttribute.cs similarity index 100% rename from lib/Shimakaze.Sdk.JsonRPC.Server/MethodAttribute.cs rename to src/Shimakaze.Sdk.JsonRPC.Server/MethodAttribute.cs diff --git a/lib/Shimakaze.Sdk.JsonRPC.Server/Shimakaze.Sdk.JsonRPC.Server.csproj b/src/Shimakaze.Sdk.JsonRPC.Server/Shimakaze.Sdk.JsonRPC.Server.csproj similarity index 51% rename from lib/Shimakaze.Sdk.JsonRPC.Server/Shimakaze.Sdk.JsonRPC.Server.csproj rename to src/Shimakaze.Sdk.JsonRPC.Server/Shimakaze.Sdk.JsonRPC.Server.csproj index 5e7e4f93..68d98275 100644 --- a/lib/Shimakaze.Sdk.JsonRPC.Server/Shimakaze.Sdk.JsonRPC.Server.csproj +++ b/src/Shimakaze.Sdk.JsonRPC.Server/Shimakaze.Sdk.JsonRPC.Server.csproj @@ -1,9 +1,12 @@ - - - - + + + + + + + \ No newline at end of file diff --git a/lib/Shimakaze.Sdk.JsonRPC.Server/Target.cs b/src/Shimakaze.Sdk.JsonRPC.Server/Target.cs similarity index 100% rename from lib/Shimakaze.Sdk.JsonRPC.Server/Target.cs rename to src/Shimakaze.Sdk.JsonRPC.Server/Target.cs diff --git a/lib/Shimakaze.Sdk.JsonRPC.Server/TargetExtensions.cs b/src/Shimakaze.Sdk.JsonRPC.Server/TargetExtensions.cs similarity index 88% rename from lib/Shimakaze.Sdk.JsonRPC.Server/TargetExtensions.cs rename to src/Shimakaze.Sdk.JsonRPC.Server/TargetExtensions.cs index ce1b45af..2064da93 100644 --- a/lib/Shimakaze.Sdk.JsonRPC.Server/TargetExtensions.cs +++ b/src/Shimakaze.Sdk.JsonRPC.Server/TargetExtensions.cs @@ -1,10 +1,8 @@ -using System.Linq; +using System.Data; using System.Reflection; using System.Text.RegularExpressions; -using System.Text; using Microsoft.Extensions.DependencyInjection; -using System.Data; namespace Shimakaze.Sdk.JsonRPC.Server; @@ -77,8 +75,12 @@ public static IEnumerable AddRpcHandler(this IServiceCollection services /// public static IEnumerable AddRpcHandler(this IServiceCollection services, string? route) where T : class => services.AddRpcHandler(typeof(T), route); +#if NET7_0_OR_GREATER [GeneratedRegex("Handlers?$|Controllers?$")] private static partial Regex HandlerRegex(); +#else + private static Regex HandlerRegex() => new("Handlers?$|Controllers?$"); +#endif private static string Combine(string? s1, string? s2) { @@ -88,14 +90,11 @@ private static string Combine(string? s1, string? s2) s1 ??= string.Empty; s2 ??= string.Empty; - string result; - if (s1.EndsWith('/') && s2.StartsWith('/')) - result = s1[..^1] + s2; - else if (!s1.EndsWith('/') && !s2.StartsWith('/')) - result = s1 + '/' + s2; - else - result = s1 + s2; - + string result = s1.EndsWith('/') && s2.StartsWith('/') + ? s1[..^1] + s2 + : !s1.EndsWith('/') && !s2.StartsWith('/') + ? s1 + '/' + s2 + : s1 + s2; if (!result.StartsWith('/')) result = '/' + result; @@ -103,13 +102,7 @@ private static string Combine(string? s1, string? s2) } private static string TrimController(string path) => HandlerRegex().Replace(path, string.Empty); - private static string TrimAsyncTail(string path) - { - if (path.EndsWith("Async")) - return path[..^5]; - - return path; - } + private static string TrimAsyncTail(string path) => path.EndsWith("Async", StringComparison.OrdinalIgnoreCase) ? path[..^5] : path; private static IEnumerable GetRpcMethods(Type type, string? route) => type .GetMethods() diff --git a/lib/Shimakaze.Sdk/Mix/MixConstants.cs b/src/Shimakaze.Sdk.Mix/Constants.cs similarity index 88% rename from lib/Shimakaze.Sdk/Mix/MixConstants.cs rename to src/Shimakaze.Sdk.Mix/Constants.cs index 2baedf4b..1bafbc91 100644 --- a/lib/Shimakaze.Sdk/Mix/MixConstants.cs +++ b/src/Shimakaze.Sdk.Mix/Constants.cs @@ -1,4 +1,4 @@ -namespace Shimakaze.Sdk.Mix; +namespace Shimakaze.Sdk.Mix; internal static class Constants { @@ -7,4 +7,4 @@ internal static class Constants public const int CB_MIX_KEY_SOURCE = 80; public const uint LXD_TD_ID = 0x54C2D545; public const uint LXD_TS_ID = 0x366E051F; -} \ No newline at end of file +} diff --git a/lib/Shimakaze.Sdk/IO/Mix/EndOfEntryTableException.cs b/src/Shimakaze.Sdk.Mix/EndOfEntryTableException.cs similarity index 93% rename from lib/Shimakaze.Sdk/IO/Mix/EndOfEntryTableException.cs rename to src/Shimakaze.Sdk.Mix/EndOfEntryTableException.cs index 88339d2c..7c80c248 100644 --- a/lib/Shimakaze.Sdk/IO/Mix/EndOfEntryTableException.cs +++ b/src/Shimakaze.Sdk.Mix/EndOfEntryTableException.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -namespace Shimakaze.Sdk.IO.Mix; +namespace Shimakaze.Sdk.Mix; /// /// Entry Table已结束 @@ -17,4 +17,4 @@ public EndOfEntryTableException(string message) : base(message) { } /// public EndOfEntryTableException(string message, Exception inner) : base(message, inner) { } -} \ No newline at end of file +} diff --git a/src/Shimakaze.Sdk.Mix/IdCalculaters.cs b/src/Shimakaze.Sdk.Mix/IdCalculaters.cs new file mode 100644 index 00000000..eaaa1e02 --- /dev/null +++ b/src/Shimakaze.Sdk.Mix/IdCalculaters.cs @@ -0,0 +1,62 @@ +using System.IO.Hashing; +using System.Text; + +namespace Shimakaze.Sdk.Mix; + +/// +/// File Id Calculater +/// +public delegate uint IdCalculater(string name); + +/// +/// File Id Calculaters +/// +public static class IdCalculaters +{ + /// + /// Tiberian Sun Id Calc + /// + /// File Name + /// Id + public static uint TSIdCalculater(string name) + { + name = name.ToUpperInvariant(); + int l = name.Length; + int a = l >> 2; + if ((l & 3) is not 0) + { + name += (char)(byte)(l - (a << 2)); + int i = 3 - (l & 3); + while (i-- is not 0) + name += name[a << 2]; + } + return BitConverter.ToUInt32(Crc32.Hash(Encoding.ASCII.GetBytes(name)), 0); + } + + /// + /// Id Calc for RA/TD + /// + /// + /// This method are used by RedAlert and Tiberian Down. + /// + /// File Name + /// Id + public static uint TDdCalculater(string name) + { + name = name.ToUpperInvariant(); + int i = 0; + uint id = 0; + int l = name.Length; + while (i < l) + { + uint a = 0; for (int j = 0; j < 4; j++) + { + a >>= 8; + if (i < l) + a += ((uint)name[i]) << 24; i++; + } + id = (id << 1 | id >> 31) + a; + } + return id; + } +} diff --git a/lib/Shimakaze.Sdk/IO/Mix/MixBuilder.cs b/src/Shimakaze.Sdk.Mix/MixBuilder.cs similarity index 75% rename from lib/Shimakaze.Sdk/IO/Mix/MixBuilder.cs rename to src/Shimakaze.Sdk.Mix/MixBuilder.cs index 5cdc04fd..e1dcb22e 100644 --- a/lib/Shimakaze.Sdk/IO/Mix/MixBuilder.cs +++ b/src/Shimakaze.Sdk.Mix/MixBuilder.cs @@ -1,9 +1,7 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Shimakaze.Sdk.Mix; - -namespace Shimakaze.Sdk.IO.Mix; +namespace Shimakaze.Sdk.Mix; /// /// Mix 构建器 @@ -13,12 +11,12 @@ public class MixBuilder /// /// 将要被打包的文件 /// - protected readonly List _files = new(); + protected List Files { get; } = []; /// /// 已被添加到构建器的文件的个数 /// - public int FileCount => _files.Count; + public int FileCount => Files.Count; /// /// ID计算器 @@ -32,7 +30,7 @@ public class MixBuilder /// 构建器 public MixBuilder AddFile(FileInfo file) { - _files.Add(file); + Files.Add(file); return this; } @@ -43,22 +41,22 @@ public MixBuilder AddFile(FileInfo file) public virtual async Task BuildAsync(Stream stream) { // 创建Entry写入器 - await using MixEntryWriter writer = new(stream, true); + using MixEntryWriter writer = new(stream, true); int size = 0; // 获取若干Entry MixEntry[] entries = GetEntries(ref size); // 写入Metadata - writer.WriteMetadataDirect((int)MixFlag.NONE, new((short)_files.Count, size)); + writer.WriteMetadataDirect((int)MixTag.NONE, new((short)Files.Count, size)); // 写入若干Entry foreach (var entry in entries) writer.Write(entry); // 写入文件主体 - foreach (var file in _files) + foreach (var file in Files) { - await using var fs = file.OpenRead(); + using var fs = file.OpenRead(); await fs.CopyToAsync(stream).ConfigureAwait(false); } } @@ -70,27 +68,27 @@ public virtual async Task BuildAsync(Stream stream) /// 构建器 public MixBuilder RemoveFile(FileInfo file) { - _files.Remove(file); + Files.Remove(file); return this; } private MixEntry[] GetEntries(ref int position) { ref FileInfo file = ref MemoryMarshal.GetReference( - CollectionsMarshal.AsSpan(_files) + CollectionsMarshal.AsSpan(Files) ); - MixEntry[] entries = new MixEntry[_files.Count]; + MixEntry[] entries = new MixEntry[Files.Count]; ref MixEntry entry = ref MemoryMarshal.GetReference(entries.AsSpan()); - for (int i = 0; i < _files.Count; i++) + for (int i = 0; i < Files.Count; i++) { ref var currentFile = ref Unsafe.Add(ref file, i); ref var currentEntry = ref Unsafe.Add(ref entry, i); - currentEntry.Id = IdCalculater(currentFile.Name.ToUpper()); + currentEntry.Id = IdCalculater(currentFile.Name.ToUpperInvariant()); currentEntry.Offset = position; position += currentEntry.Size = (int)currentFile.Length; } return entries; } -} \ No newline at end of file +} diff --git a/lib/Shimakaze.Sdk/Mix/MixEntry.cs b/src/Shimakaze.Sdk.Mix/MixEntry.cs similarity index 68% rename from lib/Shimakaze.Sdk/Mix/MixEntry.cs rename to src/Shimakaze.Sdk.Mix/MixEntry.cs index a50f7960..ceecaa63 100644 --- a/lib/Shimakaze.Sdk/Mix/MixEntry.cs +++ b/src/Shimakaze.Sdk.Mix/MixEntry.cs @@ -10,12 +10,5 @@ namespace Shimakaze.Sdk.Mix; /// /// File Offset
This is the offset of the file in the archive. /// File Size
This is the size of the file in the archive. -[StructLayout(LayoutKind.Explicit)] -public record struct MixEntry( - [field: FieldOffset(0)] - uint Id, - [field: FieldOffset(sizeof(uint))] - int Offset, - [field: FieldOffset(sizeof(uint) + sizeof(int))] - int Size -); \ No newline at end of file +[StructLayout(LayoutKind.Sequential)] +public record struct MixEntry(uint Id, int Offset, int Size); diff --git a/lib/Shimakaze.Sdk/IO/Mix/MixEntryReader.cs b/src/Shimakaze.Sdk.Mix/MixEntryReader.cs similarity index 74% rename from lib/Shimakaze.Sdk/IO/Mix/MixEntryReader.cs rename to src/Shimakaze.Sdk.Mix/MixEntryReader.cs index e54080de..2edd4e65 100644 --- a/lib/Shimakaze.Sdk/IO/Mix/MixEntryReader.cs +++ b/src/Shimakaze.Sdk.Mix/MixEntryReader.cs @@ -1,11 +1,14 @@ -using Shimakaze.Sdk.Mix; - -namespace Shimakaze.Sdk.IO.Mix; +namespace Shimakaze.Sdk.Mix; /// /// Mix Entry 读取器 /// -public sealed class MixEntryReader : AsyncReader, IDisposable, IAsyncDisposable +/// +/// 构造 Mix Entry 读取器 +/// +/// 基础流 +/// 退出时是否保持流打开 +public sealed class MixEntryReader(Stream stream, bool leaveOpen = false) : AsyncReader(stream, leaveOpen), IDisposable, IAsyncDisposable { private bool _inited; @@ -24,15 +27,6 @@ public sealed class MixEntryReader : AsyncReader, IDisposable, IAsyncD ///
public short Count { get; private set; } - /// - /// 构造 Mix Entry 读取器 - /// - /// 基础流 - /// 退出时是否保持流打开 - public MixEntryReader(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) - { - } - /// /// 初始化 /// @@ -40,8 +34,8 @@ public MixEntryReader(Stream stream, bool leaveOpen = false) : base(stream, leav public void Init() { // 标识符 - BaseStream.Read(out MixFlag flag); - if ((flag & MixFlag.ENCRYPTED) is not 0) + BaseStream.Read(out MixTag flag); + if ((flag & MixTag.ENCRYPTED) is not 0) throw new NotImplementedException("This Mix File is Encrypted."); BaseStream.Read(out MixMetadata info); @@ -76,4 +70,4 @@ public override async Task ReadAsync(IProgress? progress = null await Task.Yield(); return Read(); } -} \ No newline at end of file +} diff --git a/lib/Shimakaze.Sdk/IO/Mix/MixEntryWriter.cs b/src/Shimakaze.Sdk.Mix/MixEntryWriter.cs similarity index 64% rename from lib/Shimakaze.Sdk/IO/Mix/MixEntryWriter.cs rename to src/Shimakaze.Sdk.Mix/MixEntryWriter.cs index ff2b7975..c361b428 100644 --- a/lib/Shimakaze.Sdk/IO/Mix/MixEntryWriter.cs +++ b/src/Shimakaze.Sdk.Mix/MixEntryWriter.cs @@ -1,36 +1,32 @@ -using System.Runtime.InteropServices; - -using Shimakaze.Sdk.Mix; - -namespace Shimakaze.Sdk.IO.Mix; +namespace Shimakaze.Sdk.Mix; /// -/// Mix Entry ȡ +/// Mix Entry 读取器 /// public sealed class MixEntryWriter : AsyncWriter, IDisposable, IAsyncDisposable { /// - /// ǰļ + /// 当前文件个数 /// private short _count; private bool _inited; /// - /// ǰļС + /// 当前文件大小 /// private int _size; /// - /// ʼλ + /// 流开始的位置 /// private long _start; /// - /// Mix Entry ȡ + /// 构造 Mix Entry 读取器 /// - /// - /// ˳ʱǷ񱣳 + /// 基础流 + /// 退出时是否保持流打开 public MixEntryWriter(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) { if (!stream.CanSeek) @@ -38,7 +34,7 @@ public MixEntryWriter(Stream stream, bool leaveOpen = false) : base(stream, leav } /// - /// ʼ + /// 初始化 /// public void Init() { @@ -68,36 +64,24 @@ public override async Task WriteAsync(MixEntry value, IProgress? progress } /// - /// дԪ + /// 写入元数据 /// public void WriteMetadata() { long current = BaseStream.Position; BaseStream.Seek(_start, SeekOrigin.Begin); - WriteMetadataDirect((int)MixFlag.NONE, new(_count, _size)); + WriteMetadataDirect((int)MixTag.NONE, new(_count, _size)); BaseStream.Seek(current, SeekOrigin.Begin); } /// - /// ֱдԪ + /// 直接写入元数据 /// - /// - /// Ԫ + /// 标记 + /// 元数据 internal void WriteMetadataDirect(int flag, MixMetadata metadata) { BaseStream.Write(BitConverter.GetBytes(flag)); - unsafe - { - nint ptr = Marshal.AllocHGlobal(sizeof(MixMetadata)); - try - { - Marshal.StructureToPtr(metadata, ptr, true); - BaseStream.Write(new Span((byte*)ptr, sizeof(MixMetadata))); - } - finally - { - Marshal.FreeHGlobal(ptr); - } - } + BaseStream.Write(metadata); } } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Mix/MixMetadata.cs b/src/Shimakaze.Sdk.Mix/MixMetadata.cs similarity index 91% rename from lib/Shimakaze.Sdk/Mix/MixMetadata.cs rename to src/Shimakaze.Sdk.Mix/MixMetadata.cs index 7ab9f7f1..485dd800 100644 --- a/lib/Shimakaze.Sdk/Mix/MixMetadata.cs +++ b/src/Shimakaze.Sdk.Mix/MixMetadata.cs @@ -1,4 +1,4 @@ -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace Shimakaze.Sdk.Mix; diff --git a/lib/Shimakaze.Sdk/Mix/MixFlag.cs b/src/Shimakaze.Sdk.Mix/MixTag.cs similarity index 88% rename from lib/Shimakaze.Sdk/Mix/MixFlag.cs rename to src/Shimakaze.Sdk.Mix/MixTag.cs index 6781dcf9..e1403017 100644 --- a/lib/Shimakaze.Sdk/Mix/MixFlag.cs +++ b/src/Shimakaze.Sdk.Mix/MixTag.cs @@ -1,10 +1,10 @@ -namespace Shimakaze.Sdk.Mix; +namespace Shimakaze.Sdk.Mix; /// /// Flags for MixFile. /// [Flags] -public enum MixFlag : uint +public enum MixTag : uint { /// /// 0x00_00_00_00 @@ -23,4 +23,4 @@ public enum MixFlag : uint /// /// This file is be encrypted. ENCRYPTED = 0x00020000, -} \ No newline at end of file +} diff --git a/src/Shimakaze.Sdk.Mix/Shimakaze.Sdk.Mix.csproj b/src/Shimakaze.Sdk.Mix/Shimakaze.Sdk.Mix.csproj new file mode 100644 index 00000000..7fc53191 --- /dev/null +++ b/src/Shimakaze.Sdk.Mix/Shimakaze.Sdk.Mix.csproj @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/lib/Shimakaze.Sdk/Pal/Color.cs b/src/Shimakaze.Sdk.Pal/Color.cs similarity index 100% rename from lib/Shimakaze.Sdk/Pal/Color.cs rename to src/Shimakaze.Sdk.Pal/Color.cs diff --git a/lib/Shimakaze.Sdk/Pal/Palette.cs b/src/Shimakaze.Sdk.Pal/Palette.cs similarity index 65% rename from lib/Shimakaze.Sdk/Pal/Palette.cs rename to src/Shimakaze.Sdk.Pal/Palette.cs index a72bc4ff..62efd8f2 100644 --- a/lib/Shimakaze.Sdk/Pal/Palette.cs +++ b/src/Shimakaze.Sdk.Pal/Palette.cs @@ -3,16 +3,16 @@ /// /// 色板 /// -public readonly record struct Palette +public record class Palette { /// /// 颜色数量 /// - public const int COLOR_COUNT = 256; + public const int ColorCount = 256; /// /// 颜色 /// - public readonly Color[] Colors = new Color[COLOR_COUNT]; + public Color[] Colors { get; } = new Color[ColorCount]; /// /// 颜色 @@ -24,11 +24,4 @@ public Color this[int index] get => Colors[index]; set => Colors[index] = value; } - - /// - /// 色板 - /// - public Palette() - { - } } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Pal/PaletteReader.cs b/src/Shimakaze.Sdk.Pal/PaletteReader.cs similarity index 91% rename from lib/Shimakaze.Sdk/IO/Pal/PaletteReader.cs rename to src/Shimakaze.Sdk.Pal/PaletteReader.cs index 34ab6a28..5cb572f6 100644 --- a/lib/Shimakaze.Sdk/IO/Pal/PaletteReader.cs +++ b/src/Shimakaze.Sdk.Pal/PaletteReader.cs @@ -1,6 +1,4 @@ -using Shimakaze.Sdk.Pal; - -namespace Shimakaze.Sdk.IO.Pal; +namespace Shimakaze.Sdk.Pal; /// /// PaletteReader diff --git a/lib/Shimakaze.Sdk/IO/Pal/PaletteWriter.cs b/src/Shimakaze.Sdk.Pal/PaletteWriter.cs similarity index 91% rename from lib/Shimakaze.Sdk/IO/Pal/PaletteWriter.cs rename to src/Shimakaze.Sdk.Pal/PaletteWriter.cs index 4f6e8f59..0dd9c211 100644 --- a/lib/Shimakaze.Sdk/IO/Pal/PaletteWriter.cs +++ b/src/Shimakaze.Sdk.Pal/PaletteWriter.cs @@ -1,6 +1,4 @@ -using Shimakaze.Sdk.Pal; - -namespace Shimakaze.Sdk.IO.Pal; +namespace Shimakaze.Sdk.Pal; /// /// PaletteWriter diff --git a/src/Shimakaze.Sdk.Pal/Shimakaze.Sdk.Pal.csproj b/src/Shimakaze.Sdk.Pal/Shimakaze.Sdk.Pal.csproj new file mode 100644 index 00000000..71dc9cde --- /dev/null +++ b/src/Shimakaze.Sdk.Pal/Shimakaze.Sdk.Pal.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Shimakaze.Sdk.Polyfill/CollectionsMarshal.cs b/src/Shimakaze.Sdk.Polyfill/CollectionsMarshal.cs new file mode 100644 index 00000000..7d9f895d --- /dev/null +++ b/src/Shimakaze.Sdk.Polyfill/CollectionsMarshal.cs @@ -0,0 +1,7 @@ +#if NETSTANDARD +namespace System.Runtime.InteropServices; +public static class CollectionsMarshal +{ + public static Span AsSpan(List? list) => new([.. list]); +} +#endif \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Polyfill/DictionaryExtensions.cs b/src/Shimakaze.Sdk.Polyfill/DictionaryExtensions.cs new file mode 100644 index 00000000..efa8acff --- /dev/null +++ b/src/Shimakaze.Sdk.Polyfill/DictionaryExtensions.cs @@ -0,0 +1,20 @@ +#if NETSTANDARD2_0 + +namespace System.Collections.Generic; + +public static class DictionaryExtensions +{ + public static bool TryAdd(this Dictionary @this, TKey key, TValue value) + { + if (@this.ContainsKey(key)) + { + @this[key] = value; + return true; + } + else + { + return false; + } + } +} +#endif \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Polyfill/Int128.cs b/src/Shimakaze.Sdk.Polyfill/Int128.cs new file mode 100644 index 00000000..2273e9c2 --- /dev/null +++ b/src/Shimakaze.Sdk.Polyfill/Int128.cs @@ -0,0 +1,17 @@ +#if !NET7_0_OR_GREATER +using System.Runtime.InteropServices; + +namespace System; + +/// +/// This is not a real Int128! Upgrade to .Net 8 if you want to use it! +/// +[StructLayout(LayoutKind.Sequential)] +public readonly struct Int128 +{ + public readonly int Field1; + public readonly int Field2; + public readonly int Field3; + public readonly int Field4; +} +#endif \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Polyfill/LinqExtensions.cs b/src/Shimakaze.Sdk.Polyfill/LinqExtensions.cs new file mode 100644 index 00000000..b73ee71f --- /dev/null +++ b/src/Shimakaze.Sdk.Polyfill/LinqExtensions.cs @@ -0,0 +1,25 @@ +#if NETSTANDARD + +namespace System.Linq; + +public static class LinqExtensions +{ + public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) => DistinctBy(source, keySelector, null); + + public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector, IEqualityComparer? comparer) + { + if (source is null) + throw new ArgumentNullException(nameof(source)); + + if (keySelector is null) + throw new ArgumentNullException(nameof(keySelector)); + + Dictionary tmp = new(comparer); + + foreach (var item in source) + tmp.TryAdd(keySelector(item), item); + + return tmp.Values; + } +} +#endif \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Polyfill/Shimakaze.Sdk.Polyfill.csproj b/src/Shimakaze.Sdk.Polyfill/Shimakaze.Sdk.Polyfill.csproj new file mode 100644 index 00000000..da43cd4d --- /dev/null +++ b/src/Shimakaze.Sdk.Polyfill/Shimakaze.Sdk.Polyfill.csproj @@ -0,0 +1,41 @@ + + + + Shimakaze.Sdk + true + $(NoWarn);1591 + + + + + + + + + + + + + diff --git a/src/Shimakaze.Sdk.Polyfill/StackExtensions.cs b/src/Shimakaze.Sdk.Polyfill/StackExtensions.cs new file mode 100644 index 00000000..2bd7d76d --- /dev/null +++ b/src/Shimakaze.Sdk.Polyfill/StackExtensions.cs @@ -0,0 +1,37 @@ +#if NETSTANDARD2_0 +using System.Diagnostics.CodeAnalysis; + +namespace System.Collections.Generic; + +public static class StackExtensions +{ + public static bool TryPeek(this Stack stack, [NotNullWhen(true)] out T? result) + + { + if (stack.Count is not 0) + { + result = stack.Peek(); + return result is not null; + } + else + { + result = default; + return false; + } + } + + public static bool TryPop(this Stack stack, [NotNullWhen(true)] out T? result) + { + if (stack.Count is not 0) + { + result = stack.Pop(); + return result is not null; + } + else + { + result = default; + return false; + } + } +} +#endif \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Polyfill/StreamExtensions.cs b/src/Shimakaze.Sdk.Polyfill/StreamExtensions.cs new file mode 100644 index 00000000..ea87086a --- /dev/null +++ b/src/Shimakaze.Sdk.Polyfill/StreamExtensions.cs @@ -0,0 +1,23 @@ +#if NETSTANDARD2_0 +namespace System.IO; + +public static class StreamExtensions +{ + public static async ValueTask DisposeAsync(this Stream stream) => await Task.Run(stream.Dispose); + + public static int Read(this Stream stream, Span buffer) + { + var tmp = buffer.ToArray(); + stream.Read(tmp, 0, tmp.Length); + tmp.CopyTo(buffer); + return tmp.Length; + } + + public static void Write(this Stream stream, ReadOnlySpan buffer) + { + var tmp = buffer.ToArray(); + stream.Write(tmp, 0, tmp.Length); + } +} + +#endif \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Polyfill/StringExtensions.cs b/src/Shimakaze.Sdk.Polyfill/StringExtensions.cs new file mode 100644 index 00000000..eb8a200f --- /dev/null +++ b/src/Shimakaze.Sdk.Polyfill/StringExtensions.cs @@ -0,0 +1,19 @@ +namespace Shimakaze.Sdk; + +#if NETSTANDARD2_0 +public static class StringExtensions +{ + public static string[] Split(this string value, string split) + { + List strings = []; + + while (value.IndexOf(split, 0, split.Length, StringComparison.Ordinal) is int index and not -1) + { + strings.Add(value[..index]); + value = value[(index + split.Length)..]; + } + + return [.. strings]; + } +} +#endif \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Polyfill/TextWriterExtensions.cs b/src/Shimakaze.Sdk.Polyfill/TextWriterExtensions.cs new file mode 100644 index 00000000..6cb1b060 --- /dev/null +++ b/src/Shimakaze.Sdk.Polyfill/TextWriterExtensions.cs @@ -0,0 +1,12 @@ +#if NETSTANDARD2_0 +namespace System.IO; + +public static class TextWriterExtensions +{ + public static async ValueTask DisposeAsync(this TextWriter stream) + { + await Task.Run(stream.Dispose); + } +} + +#endif \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Preprocessor/Commands/ConditionCommand.cs b/src/Shimakaze.Sdk.Preprocessor/Commands/ConditionCommand.cs new file mode 100644 index 00000000..74838dc3 --- /dev/null +++ b/src/Shimakaze.Sdk.Preprocessor/Commands/ConditionCommand.cs @@ -0,0 +1,168 @@ +using Microsoft.Extensions.Logging; + +using Shimakaze.Sdk.Preprocessor.Kernel; + +namespace Shimakaze.Sdk.Preprocessor.Commands; + +/// +/// Condition Commands if elif else endif +/// +/// +/// +/// +/// +/// +public sealed class ConditionalCommand(Engine engine, Logger? logger = null) +{ + private static readonly Action Condition = LoggerMessage.Define(LogLevel.Error, 1, "Condition: \"{Condition}\" and computed value is: \"{Value}\"."); + private static readonly Action PushIf = LoggerMessage.Define(LogLevel.Error, 1, "Push Stack: If"); + private static readonly Action PushElif = LoggerMessage.Define(LogLevel.Error, 1, "Push Stack: Elif"); + private static readonly Action PushElse = LoggerMessage.Define(LogLevel.Error, 1, "Push Stack: Else"); + private static readonly Action Pop = LoggerMessage.Define(LogLevel.Error, 1, "Push Stack: {Tag}"); + private static readonly Action ElifNotActived = LoggerMessage.Define(LogLevel.Error, 1, "ElIf is NOT Actived."); + private static readonly Action ElseNotActived = LoggerMessage.Define(LogLevel.Error, 1, "Else is NOT Actived."); + private static readonly Action EndIfActived = LoggerMessage.Define(LogLevel.Error, 1, "EndIf Actived!"); + + + /// + /// #if condition + /// + /// + [Command] + public void If([Param(@".*")] string condition) + { + if (string.IsNullOrWhiteSpace(condition)) + throw new ArgumentNullException(nameof(condition)); + + var conditionStack = engine.GetOrNew("ConditionStack", () => new Stack()); + var conditionParser = engine.GetOrNew("ConditionParser", () => new ConditionParser(engine)); + + engine.CanWritable = conditionParser.Parse(condition); + if (logger is not null) + Condition(logger, condition, engine.CanWritable, default!); + + conditionStack.Push(new ConditionStatus(engine.CanWritable, condition, "if")); + if (logger is not null) + PushIf(logger, default!); + } + + /// + /// #elif condition + /// + /// + [Command] + public void Elif([Param(@".*")] string condition) + { + if (string.IsNullOrWhiteSpace(condition)) + throw new ArgumentNullException(nameof(condition)); + + var conditionStack = engine.GetOrNew("ConditionStack", () => new Stack()); + var conditionParser = engine.GetOrNew("ConditionParser", () => new ConditionParser(engine)); + + var lastStatus = conditionStack.Pop(); + if (logger is not null) + Pop(logger, lastStatus.Tag, default!); + + engine.CanWritable = false; + if (lastStatus.IsMatched) + { + if (logger is not null) + ElifNotActived(logger, default!); + } + else + { + engine.CanWritable = conditionParser.Parse(condition); + if (logger is not null) + Condition(logger, condition, engine.CanWritable, default!); + } + + conditionStack.Push(new(engine.CanWritable, condition, "elif")); + if (logger is not null) + PushElif(logger, default!); + } + + /// + /// #else + /// + [Command] + public void Else() + { + var conditionStack = engine.GetOrNew("ConditionStack", () => new Stack()); + + var lastStatus = conditionStack.Pop(); + if (logger is not null) + Pop(logger, lastStatus.Tag, default!); + + engine.CanWritable = false; + + if (lastStatus.IsMatched) + { + if (logger is not null) + ElseNotActived(logger, default!); + } + else + { + engine.CanWritable = true; + if (logger is not null) + Condition(logger, "{ELSE}", engine.CanWritable, default!); + } + + conditionStack.Push(new(engine.CanWritable, string.Empty, "else")); + if (logger is not null) + PushElse(logger, default!); + } + + /// + /// #endif + /// + [Command] + public void Endif() + { + var conditionStack = engine.GetOrNew("ConditionStack", () => new Stack()); + + var lastStatus = conditionStack.Pop(); + if (logger is not null) + Pop(logger, lastStatus.Tag, default!); + + engine.CanWritable = true; + if (logger is not null) + EndIfActived(logger, default!); + } +} + +file sealed class ConditionParser(Engine engine) +{ + public bool Parse(string condition) + { + condition = condition.Trim(); + switch (condition.ToLowerInvariant()) + { + case "true": return true; + case "false": return false; + default: break; + } + + var defines = engine.GetOrNew("Defines", () => new HashSet()); + + return defines.Any(i => i.Equals(condition, StringComparison.OrdinalIgnoreCase)) + || (condition.Contains("||") + ? OR(condition) + : condition.Contains("&&") + ? AND(condition) + : condition.TrimStart().StartsWith('!') && NOT(condition)); + } + + private bool AND(string condition) => condition.Trim().Split("&&").All(Parse); + + private bool NOT(string condition) + { + condition = condition.Trim(); + return condition.StartsWith('!') + ? !Parse(condition[1..]) + : Parse(condition); + } + + private bool OR(string condition) => condition.Trim().Split("||").Any(Parse); +} + +file sealed record ConditionStatus(bool IsMatched, string Condition, string Tag); \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Preprocessor/Commands/DefineCommand.cs b/src/Shimakaze.Sdk.Preprocessor/Commands/DefineCommand.cs new file mode 100644 index 00000000..92a5ea33 --- /dev/null +++ b/src/Shimakaze.Sdk.Preprocessor/Commands/DefineCommand.cs @@ -0,0 +1,44 @@ +using Microsoft.Extensions.Logging; + +using Shimakaze.Sdk.Preprocessor.Kernel; + +namespace Shimakaze.Sdk.Preprocessor.Commands; +/// +/// Define Commands: define undef +/// +/// +/// +/// +/// +/// +public sealed class DefineCommand(Engine engine, Logger? logger = null) +{ + private static readonly Action LogDefine = LoggerMessage.Define(LogLevel.Debug, 0, "Define {Identifier}"); + private static readonly Action LogUndefine = LoggerMessage.Define(LogLevel.Debug, 0, "Undefine {Identifier}"); + + /// + /// #define identifier + /// + /// + [Command] + public void Define(string identifier) + { + var defines = engine.GetOrNew("Defines", () => new HashSet()); + defines.Add(identifier); + if (logger is not null) + LogDefine(logger, identifier, default!); + } + + /// + /// #undef identifier + /// + /// + [Command] + public void Undef(string identifier) + { + var defines = engine.GetOrNew("Defines", () => new HashSet()); + defines.Remove(identifier); + if (logger is not null) + LogUndefine(logger, identifier, default!); + } +} \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Preprocessor/Commands/RegionCommand.cs b/src/Shimakaze.Sdk.Preprocessor/Commands/RegionCommand.cs new file mode 100644 index 00000000..bd7aff1a --- /dev/null +++ b/src/Shimakaze.Sdk.Preprocessor/Commands/RegionCommand.cs @@ -0,0 +1,41 @@ +using System.Diagnostics.CodeAnalysis; + +using Microsoft.Extensions.Logging; + +using Shimakaze.Sdk.Preprocessor.Kernel; + +namespace Shimakaze.Sdk.Preprocessor.Commands; + +/// +/// Region Commands: region endregion +/// +/// +/// +/// +/// + +public sealed class RegionCommand(Logger? logger = null) +{ + private static readonly Action LogRegion = LoggerMessage.Define(LogLevel.Debug, 0, "Region"); + private static readonly Action LogEndregion = LoggerMessage.Define(LogLevel.Debug, 0, "Endregion"); + + /// + /// #region + /// + [Command] + public void Region() + { + if (logger is not null) + LogRegion(logger, default!); + } + + /// + /// #endregion + /// + [Command] + public void Endregion() + { + if (logger is not null) + LogEndregion(logger, default!); + } +} \ No newline at end of file diff --git a/src/Shimakaze.Sdk.Preprocessor/Commands/TypeCommand.cs b/src/Shimakaze.Sdk.Preprocessor/Commands/TypeCommand.cs new file mode 100644 index 00000000..d3e28d2d --- /dev/null +++ b/src/Shimakaze.Sdk.Preprocessor/Commands/TypeCommand.cs @@ -0,0 +1,72 @@ +using System.Diagnostics.CodeAnalysis; +using System.Globalization; + +using Microsoft.Extensions.Logging; + +using Shimakaze.Sdk.Preprocessor.Kernel; + +namespace Shimakaze.Sdk.Preprocessor.Commands; + +/// +/// Region Commands: region endregion +/// +/// +/// +/// +/// +/// +public sealed class TypeCommand(Engine engine, Logger? logger = null) +{ + private static readonly Action InvalidOperation = LoggerMessage.Define(LogLevel.Error, 1, "InvalidOperation."); + private static readonly Action LogType = LoggerMessage.Define(LogLevel.Debug, 0, "Adding {Unit} as {Key} in {Type}."); + + /// + /// #type + /// + /// 类型 + /// 键 + /// 单位名 + [Command] + public void Type(string type, string key, string unit) + { + if (logger is not null) + LogType(logger, unit, key, type, default!); + string reg = $""" + [{type}] + {key}={unit} + """; + engine.Writer.WriteLine(reg); + } + + /// + [Command] + public void Type(string type, string key) + { + if (string.IsNullOrEmpty(engine.FilePath)) + { + InvalidOperationException ex = new("Cannot found current file's name"); + if (logger is not null) + InvalidOperation(logger, ex); + throw ex; + } + + string unit = Path.GetFileName(engine.FilePath).Split('.').First(); + Type(type, key, unit); + } + + /// + [Command] + public void Type(string type) + { + var typeCounter = engine.GetOrNew("TypeCounter", () => new TypeCounter()); + var key = typeCounter.Counter.ToString(CultureInfo.InvariantCulture); + Type(type, key); + } + +} + +file sealed class TypeCounter +{ + private int _counter; + public int Counter => _counter++; +} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk.Preprocessor/Kernel/Command.cs b/src/Shimakaze.Sdk.Preprocessor/Kernel/Command.cs similarity index 92% rename from lib/Shimakaze.Sdk.Preprocessor/Kernel/Command.cs rename to src/Shimakaze.Sdk.Preprocessor/Kernel/Command.cs index f7b6fffa..08e51439 100644 --- a/lib/Shimakaze.Sdk.Preprocessor/Kernel/Command.cs +++ b/src/Shimakaze.Sdk.Preprocessor/Kernel/Command.cs @@ -1,5 +1,6 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Reflection; using System.Text.RegularExpressions; @@ -30,6 +31,9 @@ public sealed class Command /// public ImmutableArray Parameters { get; init; } + internal Regex Regex { get; init; } + + internal Command(Type type, [StringSyntax("Regex")] string? name, MethodInfo method, ImmutableArray parameters) { Type = type; @@ -42,19 +46,18 @@ internal Command(Type type, [StringSyntax("Regex")] string? name, MethodInfo met Regex = new(@$"^#{Name}\s*{string.Join(@"\s*", parameters.Select(i => $"({i.Match})"))}$"); } - internal Regex Regex { get; init; } internal void Execute(object? obj, string line) { Match match = Regex.Match(line); - var args = match.Groups.Values.Skip(1).Select(i => i.Value).ToImmutableArray(); + var args = match.Groups.GetValues().Skip(1).Select(i => i.Value).ToImmutableArray(); object[] parameters = new object[Parameters.Length]; for (int i = 0; i < Parameters.Length; i++) { var parameter = Parameters[i]; string arg = args[i]; parameters[i] = parameter.Parameter.ParameterType != typeof(string) - ? Convert.ChangeType(arg, parameter.Parameter.ParameterType) + ? Convert.ChangeType(arg, parameter.Parameter.ParameterType, CultureInfo.InvariantCulture) : arg; } Method.Invoke(obj, parameters); diff --git a/lib/Shimakaze.Sdk.Preprocessor/Kernel/CommandAttribute.cs b/src/Shimakaze.Sdk.Preprocessor/Kernel/CommandAttribute.cs similarity index 100% rename from lib/Shimakaze.Sdk.Preprocessor/Kernel/CommandAttribute.cs rename to src/Shimakaze.Sdk.Preprocessor/Kernel/CommandAttribute.cs diff --git a/lib/Shimakaze.Sdk.Preprocessor/Kernel/CommandParameter.cs b/src/Shimakaze.Sdk.Preprocessor/Kernel/CommandParameter.cs similarity index 100% rename from lib/Shimakaze.Sdk.Preprocessor/Kernel/CommandParameter.cs rename to src/Shimakaze.Sdk.Preprocessor/Kernel/CommandParameter.cs diff --git a/lib/Shimakaze.Sdk.Preprocessor/Kernel/Engine.cs b/src/Shimakaze.Sdk.Preprocessor/Kernel/Engine.cs similarity index 87% rename from lib/Shimakaze.Sdk.Preprocessor/Kernel/Engine.cs rename to src/Shimakaze.Sdk.Preprocessor/Kernel/Engine.cs index 55365ad3..cd080800 100644 --- a/lib/Shimakaze.Sdk.Preprocessor/Kernel/Engine.cs +++ b/src/Shimakaze.Sdk.Preprocessor/Kernel/Engine.cs @@ -8,17 +8,17 @@ namespace Shimakaze.Sdk.Preprocessor.Kernel; /// /// 预处理器引擎设置 /// -public sealed class EngineOptions +public sealed record class EngineOptions { /// /// 命令集 /// - public ImmutableArray Commands { get; set; } = new(); + public ImmutableArray Commands { get; set; } /// /// 定义集 /// - public HashSet Defines { get; set; } = new(); + public HashSet Defines { get; set; } = []; } /// @@ -26,6 +26,7 @@ public sealed class EngineOptions /// public sealed class Engine { + private static readonly Action LogDefine = LoggerMessage.Define(LogLevel.Debug, 0, "Define {Identifier}"); private readonly ImmutableArray _commands; private readonly Dictionary _storage; private readonly IServiceProvider _provider; @@ -65,7 +66,8 @@ public Engine(IServiceProvider provider, EngineOptions options, ILogger? { ["Defines"] = options.Defines }; - _logger?.LogTrace("Define: {define}", string.Join(';', options.Defines)); + if (_logger is not null) + LogDefine(_logger, StringShim.Join(options.Defines,';'), default!); } /// @@ -84,7 +86,7 @@ public T GetOrNew(string key, Func creator) where T : notnull return result; } - private void ParseLine(string line, long lineNumber) + private void ParseLine(string line) { int index = line.IndexOf('#'); @@ -133,7 +135,7 @@ public void Execute( if (!string.IsNullOrWhiteSpace(line) && line.TrimStart().StartsWith('#')) { - ParseLine(line, Line); + ParseLine(line); continue; } diff --git a/lib/Shimakaze.Sdk.Preprocessor/Kernel/ParamAttribute.cs b/src/Shimakaze.Sdk.Preprocessor/Kernel/ParamAttribute.cs similarity index 100% rename from lib/Shimakaze.Sdk.Preprocessor/Kernel/ParamAttribute.cs rename to src/Shimakaze.Sdk.Preprocessor/Kernel/ParamAttribute.cs diff --git a/lib/Shimakaze.Sdk.Preprocessor/Kernel/PreprocessorException.cs b/src/Shimakaze.Sdk.Preprocessor/Kernel/PreprocessorException.cs similarity index 96% rename from lib/Shimakaze.Sdk.Preprocessor/Kernel/PreprocessorException.cs rename to src/Shimakaze.Sdk.Preprocessor/Kernel/PreprocessorException.cs index 69cf25a1..ef2e77a8 100644 --- a/lib/Shimakaze.Sdk.Preprocessor/Kernel/PreprocessorException.cs +++ b/src/Shimakaze.Sdk.Preprocessor/Kernel/PreprocessorException.cs @@ -6,7 +6,7 @@ namespace Shimakaze.Sdk.Preprocessor.Kernel; /// 预处理器异常 /// [Serializable] -[ExcludeFromCodeCoverage] + public class PreprocessorException : Exception { /// diff --git a/lib/Shimakaze.Sdk.Preprocessor/Kernel/PreprocessorExtensions.cs b/src/Shimakaze.Sdk.Preprocessor/Kernel/PreprocessorExtensions.cs similarity index 100% rename from lib/Shimakaze.Sdk.Preprocessor/Kernel/PreprocessorExtensions.cs rename to src/Shimakaze.Sdk.Preprocessor/Kernel/PreprocessorExtensions.cs diff --git a/lib/Shimakaze.Sdk.Preprocessor/Kernel/ThrowHelper.cs b/src/Shimakaze.Sdk.Preprocessor/Kernel/ThrowHelper.cs similarity index 100% rename from lib/Shimakaze.Sdk.Preprocessor/Kernel/ThrowHelper.cs rename to src/Shimakaze.Sdk.Preprocessor/Kernel/ThrowHelper.cs diff --git a/src/Shimakaze.Sdk.Preprocessor/Shimakaze.Sdk.Preprocessor.csproj b/src/Shimakaze.Sdk.Preprocessor/Shimakaze.Sdk.Preprocessor.csproj new file mode 100644 index 00000000..209d6e58 --- /dev/null +++ b/src/Shimakaze.Sdk.Preprocessor/Shimakaze.Sdk.Preprocessor.csproj @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/Shimakaze.Sdk.Vpl.Editor/ColorExtensions.cs b/src/Shimakaze.Sdk.Vpl.Editor/ColorExtensions.cs similarity index 100% rename from app/Shimakaze.Sdk.Vpl.Editor/ColorExtensions.cs rename to src/Shimakaze.Sdk.Vpl.Editor/ColorExtensions.cs diff --git a/app/Shimakaze.Sdk.Vpl.Editor/Program.cs b/src/Shimakaze.Sdk.Vpl.Editor/Program.cs similarity index 78% rename from app/Shimakaze.Sdk.Vpl.Editor/Program.cs rename to src/Shimakaze.Sdk.Vpl.Editor/Program.cs index ece7786d..849982af 100644 --- a/app/Shimakaze.Sdk.Vpl.Editor/Program.cs +++ b/src/Shimakaze.Sdk.Vpl.Editor/Program.cs @@ -1,7 +1,5 @@ using Sharprompt; -using Shimakaze.Sdk.IO.Pal; -using Shimakaze.Sdk.IO.Vpl; using Shimakaze.Sdk.Pal; using Shimakaze.Sdk.Vpl; using Shimakaze.Sdk.Vpl.Editor; @@ -12,18 +10,18 @@ VoxelPalette vpl; Palette pal; -await using (var vplStream = File.OpenRead(vplPath)) +using (Stream vplStream = File.OpenRead(vplPath)) await using (VoxelPaletteReader vplReader = new(vplStream)) vpl = await vplReader.ReadAsync(); -await using (var palStream = File.OpenRead(palPath)) +using (Stream palStream = File.OpenRead(palPath)) await using (PaletteReader palReader = new(palStream)) pal = await palReader.ReadAsync(); VplEditor editor = new(vpl, pal, async (editor) => { string path = Prompt.Input("Where is your new VPL file save to?", vplPath); - await using var fs = File.Create(path); + using Stream fs = File.Create(path); await using VoxelPaletteWriter writer = new(fs); await writer.WriteAsync(editor.Vpl); }); diff --git a/src/Shimakaze.Sdk.Vpl.Editor/Shimakaze.Sdk.Vpl.Editor.csproj b/src/Shimakaze.Sdk.Vpl.Editor/Shimakaze.Sdk.Vpl.Editor.csproj new file mode 100644 index 00000000..e4521e2b --- /dev/null +++ b/src/Shimakaze.Sdk.Vpl.Editor/Shimakaze.Sdk.Vpl.Editor.csproj @@ -0,0 +1,13 @@ + + + Exe + net8.0 + true + + + + + + + + diff --git a/app/Shimakaze.Sdk.Vpl.Editor/VplEditor.cs b/src/Shimakaze.Sdk.Vpl.Editor/VplEditor.cs similarity index 97% rename from app/Shimakaze.Sdk.Vpl.Editor/VplEditor.cs rename to src/Shimakaze.Sdk.Vpl.Editor/VplEditor.cs index ef7ae7f8..4889b952 100644 --- a/app/Shimakaze.Sdk.Vpl.Editor/VplEditor.cs +++ b/src/Shimakaze.Sdk.Vpl.Editor/VplEditor.cs @@ -1,4 +1,6 @@ -using Sharprompt; +using System.Globalization; + +using Sharprompt; using Shimakaze.Sdk.Pal; @@ -142,7 +144,7 @@ private void PrintColor() Console.Write("色板:"); Console.SetCursorPosition(X_SECTION_OFFSET, 0); Console.Write("Section: #"); - Console.Write((_current.Section + 1).ToString("D2")); + Console.Write((_current.Section + 1).ToString("D2", CultureInfo.InvariantCulture)); Console.WriteLine("\x1B[0m"); for (int y = 0; y < 256 / 16; y++) { diff --git a/src/Shimakaze.Sdk.Vpl/Shimakaze.Sdk.Vpl.csproj b/src/Shimakaze.Sdk.Vpl/Shimakaze.Sdk.Vpl.csproj new file mode 100644 index 00000000..323ad7a5 --- /dev/null +++ b/src/Shimakaze.Sdk.Vpl/Shimakaze.Sdk.Vpl.csproj @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/Shimakaze.Sdk/Vpl/VoxelPalette.cs b/src/Shimakaze.Sdk.Vpl/VoxelPalette.cs similarity index 55% rename from lib/Shimakaze.Sdk/Vpl/VoxelPalette.cs rename to src/Shimakaze.Sdk.Vpl/VoxelPalette.cs index e494e612..cd743d9f 100644 --- a/lib/Shimakaze.Sdk/Vpl/VoxelPalette.cs +++ b/src/Shimakaze.Sdk.Vpl/VoxelPalette.cs @@ -1,35 +1,38 @@ -using System.Diagnostics.CodeAnalysis; - -using Shimakaze.Sdk.Pal; +using Shimakaze.Sdk.Pal; namespace Shimakaze.Sdk.Vpl; /// /// VPL文件 /// -public record struct VoxelPalette +public record class VoxelPalette { + internal VoxelPaletteHeader InternalHeader; + /// /// VPL文件头 /// - public VoxelPaletteHeader Header; + public VoxelPaletteHeader Header + { + get => InternalHeader; + set => InternalHeader = value; + } /// /// VPL色板 /// - public Palette Palette; + public Palette Palette { get; set; } = new(); /// /// VPL节 /// - public VoxelPaletteSection[] Sections; + public VoxelPaletteSection[] Sections { get; set; } = []; /// /// 获取其中一个节 /// /// 节索引 /// - [ExcludeFromCodeCoverage] - public readonly VoxelPaletteSection this[int index] + public VoxelPaletteSection this[int index] { get => Sections[index]; set => Sections[index] = value; diff --git a/lib/Shimakaze.Sdk/Vpl/VoxelPaletteHeader.cs b/src/Shimakaze.Sdk.Vpl/VoxelPaletteHeader.cs similarity index 100% rename from lib/Shimakaze.Sdk/Vpl/VoxelPaletteHeader.cs rename to src/Shimakaze.Sdk.Vpl/VoxelPaletteHeader.cs diff --git a/lib/Shimakaze.Sdk/IO/Vpl/VoxelPaletteReader.cs b/src/Shimakaze.Sdk.Vpl/VoxelPaletteReader.cs similarity index 59% rename from lib/Shimakaze.Sdk/IO/Vpl/VoxelPaletteReader.cs rename to src/Shimakaze.Sdk.Vpl/VoxelPaletteReader.cs index 79deb2a8..a4424b8e 100644 --- a/lib/Shimakaze.Sdk/IO/Vpl/VoxelPaletteReader.cs +++ b/src/Shimakaze.Sdk.Vpl/VoxelPaletteReader.cs @@ -1,26 +1,23 @@ -using Shimakaze.Sdk.IO.Pal; -using Shimakaze.Sdk.Vpl; +using Shimakaze.Sdk; +using Shimakaze.Sdk.Pal; -namespace Shimakaze.Sdk.IO.Vpl; +namespace Shimakaze.Sdk.Vpl; /// /// VoxelPaletteReader /// -public sealed class VoxelPaletteReader : AsyncReader, IDisposable, IAsyncDisposable +/// +/// VoxelPaletteReader +/// +public sealed class VoxelPaletteReader(Stream stream, bool leaveOpen = false) : AsyncReader(stream, leaveOpen), IDisposable, IAsyncDisposable { - /// - /// VoxelPaletteReader - /// - public VoxelPaletteReader(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) - { - } /// public override async Task ReadAsync(IProgress? progress = null, CancellationToken cancellationToken = default) { VoxelPalette vpl = new(); - BaseStream.Read(out vpl.Header); + BaseStream.Read(out vpl.InternalHeader); await using (PaletteReader reader = new(BaseStream, true)) vpl.Palette = await reader.ReadAsync(cancellationToken); @@ -32,7 +29,11 @@ public override async Task ReadAsync(IProgress? progress = progress?.Report((float)i / vpl.Sections.Length); vpl.Sections[i] = new(); - BaseStream.Read(vpl.Sections[i].Data); + unsafe + { + fixed (byte* p = vpl.Sections[i].Data) + BaseStream.Read(new Span(p, 256)); + } } return vpl; diff --git a/lib/Shimakaze.Sdk/Vpl/VoxelPaletteSection.cs b/src/Shimakaze.Sdk.Vpl/VoxelPaletteSection.cs similarity index 51% rename from lib/Shimakaze.Sdk/Vpl/VoxelPaletteSection.cs rename to src/Shimakaze.Sdk.Vpl/VoxelPaletteSection.cs index 1ec7b7ae..bf2b8bdc 100644 --- a/lib/Shimakaze.Sdk/Vpl/VoxelPaletteSection.cs +++ b/src/Shimakaze.Sdk.Vpl/VoxelPaletteSection.cs @@ -1,31 +1,21 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Shimakaze.Sdk.Vpl; +namespace Shimakaze.Sdk.Vpl; /// /// VPL节 /// -public readonly record struct VoxelPaletteSection +public struct VoxelPaletteSection { /// /// VPL节数据(颜色索引) /// - public readonly byte[] Data = new byte[256]; - - /// - /// VoxelPaletteSection - /// - public VoxelPaletteSection() - { - } + internal unsafe fixed byte Data[256]; /// /// 获取 VPL 节数据(颜色索引) /// /// 位置 /// 颜色索引 - [ExcludeFromCodeCoverage] - public readonly byte this[int index] + public unsafe byte this[int index] { get => Data[index]; set => Data[index] = value; diff --git a/lib/Shimakaze.Sdk/IO/Vpl/VoxelPaletteWriter.cs b/src/Shimakaze.Sdk.Vpl/VoxelPaletteWriter.cs similarity index 58% rename from lib/Shimakaze.Sdk/IO/Vpl/VoxelPaletteWriter.cs rename to src/Shimakaze.Sdk.Vpl/VoxelPaletteWriter.cs index 07938506..19b91d24 100644 --- a/lib/Shimakaze.Sdk/IO/Vpl/VoxelPaletteWriter.cs +++ b/src/Shimakaze.Sdk.Vpl/VoxelPaletteWriter.cs @@ -1,20 +1,15 @@ -using Shimakaze.Sdk.IO.Pal; -using Shimakaze.Sdk.Vpl; +using Shimakaze.Sdk.Pal; -namespace Shimakaze.Sdk.IO.Vpl; +namespace Shimakaze.Sdk.Vpl; /// /// VoxelPaletteWriter /// -public sealed class VoxelPaletteWriter : AsyncWriter, IDisposable, IAsyncDisposable +/// +/// VoxelPaletteWriter +/// +public sealed class VoxelPaletteWriter(Stream stream, bool leaveOpen = false) : AsyncWriter(stream, leaveOpen), IDisposable, IAsyncDisposable { - /// - /// VoxelPaletteWriter - /// - public VoxelPaletteWriter(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) - { - } - /// public override async Task WriteAsync(VoxelPalette value, IProgress? progress = null, CancellationToken cancellationToken = default) { @@ -28,7 +23,11 @@ public override async Task WriteAsync(VoxelPalette value, IProgress? prog cancellationToken.ThrowIfCancellationRequested(); progress?.Report((float)i / value.Sections.Length); - BaseStream.Write(value.Sections[i].Data); + unsafe + { + fixed (byte* ptr = value.Sections[i].Data) + BaseStream.Write(new Span(ptr, 256)); + } } } } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Vxl/SectionData.cs b/src/Shimakaze.Sdk.Vxl/SectionData.cs similarity index 57% rename from lib/Shimakaze.Sdk/Vxl/SectionData.cs rename to src/Shimakaze.Sdk.Vxl/SectionData.cs index e1fb5c9b..cd9fb1d4 100644 --- a/lib/Shimakaze.Sdk/Vxl/SectionData.cs +++ b/src/Shimakaze.Sdk.Vxl/SectionData.cs @@ -3,18 +3,18 @@ /// /// LimbBody /// -public record struct SectionData +public record class SectionData { /// /// SpanStart /// - public int[] SpanStart; + public int[] SpanStart { get; set; } = []; /// /// SpanEnd /// - public int[] SpanEnd; + public int[] SpanEnd { get; set; } = []; /// /// Data /// - public VoxelSpan[] Voxel; + public VoxelSpan[] Voxel { get; set; } = []; } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Vxl/SectionHeader.cs b/src/Shimakaze.Sdk.Vxl/SectionHeader.cs similarity index 100% rename from lib/Shimakaze.Sdk/Vxl/SectionHeader.cs rename to src/Shimakaze.Sdk.Vxl/SectionHeader.cs diff --git a/lib/Shimakaze.Sdk/Vxl/SectionTailer.cs b/src/Shimakaze.Sdk.Vxl/SectionTailer.cs similarity index 100% rename from lib/Shimakaze.Sdk/Vxl/SectionTailer.cs rename to src/Shimakaze.Sdk.Vxl/SectionTailer.cs diff --git a/src/Shimakaze.Sdk.Vxl/Shimakaze.Sdk.Vxl.csproj b/src/Shimakaze.Sdk.Vxl/Shimakaze.Sdk.Vxl.csproj new file mode 100644 index 00000000..2f5df1e5 --- /dev/null +++ b/src/Shimakaze.Sdk.Vxl/Shimakaze.Sdk.Vxl.csproj @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/Shimakaze.Sdk.Vxl/VXLFile.cs b/src/Shimakaze.Sdk.Vxl/VXLFile.cs new file mode 100644 index 00000000..c65183f3 --- /dev/null +++ b/src/Shimakaze.Sdk.Vxl/VXLFile.cs @@ -0,0 +1,33 @@ +using Shimakaze.Sdk.Pal; + +namespace Shimakaze.Sdk.Vxl; + +/// +/// VXL 文件 +/// +public record class VXLFile +{ + internal VXLHeader InternalHeader; + + /// + /// 文件头 + /// + public VXLHeader Header + { + get => InternalHeader; + set => InternalHeader = value; + } + /// + /// 文件色板 + /// + public Palette Palette { get; set; } = new(); + /// + /// + public SectionHeader[] SectionHeaders { get; set; } = []; + /// + /// + public SectionData[] SectionData { get; set; } = []; + /// + /// + public SectionTailer[] SectionTailers { get; set; } = []; +} \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/Vxl/VXLHeader.cs b/src/Shimakaze.Sdk.Vxl/VXLHeader.cs similarity index 100% rename from lib/Shimakaze.Sdk/Vxl/VXLHeader.cs rename to src/Shimakaze.Sdk.Vxl/VXLHeader.cs diff --git a/lib/Shimakaze.Sdk/Vxl/Voxel.cs b/src/Shimakaze.Sdk.Vxl/Voxel.cs similarity index 100% rename from lib/Shimakaze.Sdk/Vxl/Voxel.cs rename to src/Shimakaze.Sdk.Vxl/Voxel.cs diff --git a/lib/Shimakaze.Sdk/IO/Vxl/VoxelReader.cs b/src/Shimakaze.Sdk.Vxl/VoxelReader.cs similarity index 92% rename from lib/Shimakaze.Sdk/IO/Vxl/VoxelReader.cs rename to src/Shimakaze.Sdk.Vxl/VoxelReader.cs index d44c3774..f71b128d 100644 --- a/lib/Shimakaze.Sdk/IO/Vxl/VoxelReader.cs +++ b/src/Shimakaze.Sdk.Vxl/VoxelReader.cs @@ -1,8 +1,7 @@ -using Shimakaze.Sdk.IO.Pal; +using Shimakaze.Sdk; using Shimakaze.Sdk.Pal; -using Shimakaze.Sdk.Vxl; -namespace Shimakaze.Sdk.IO.Vxl; +namespace Shimakaze.Sdk.Vxl; /// /// VoxelReader @@ -23,9 +22,9 @@ public VoxelReader(Stream stream, bool leaveOpen = false) : base(stream, leaveOp public override async Task ReadAsync(IProgress? progress = null, CancellationToken cancellationToken = default) { VXLFile voxel = new(); - BaseStream.Read(out voxel.Header); + BaseStream.Read(out voxel.InternalHeader); - uint limbDataOffset = 34 + Palette.COLOR_COUNT * 3 + voxel.Header.NumSections * 28; + uint limbDataOffset = 34 + Palette.ColorCount * 3 + voxel.Header.NumSections * 28; await using (PaletteReader reader = new(BaseStream, true)) voxel.Palette = await reader.ReadAsync(cancellationToken); @@ -81,7 +80,7 @@ public override async Task ReadAsync(IProgress? progress = null, if (voxel.SectionData[i].SpanStart[j] is -1 && voxel.SectionData[i].SpanEnd[j] is -1) continue; - List sections = new(); + List sections = []; BaseStream.Seek(data + voxel.SectionData[i].SpanStart[j], SeekOrigin.Begin); for (byte z = 0; z < voxel.SectionTailers[i].Size.Z;) { @@ -110,7 +109,7 @@ public override async Task ReadAsync(IProgress? progress = null, sections.Add(voxelSpanSegment); } - voxel.SectionData[i].Voxel[j].Sections = sections.ToArray(); + voxel.SectionData[i].Voxel[j].Sections = [.. sections]; } } return voxel; diff --git a/lib/Shimakaze.Sdk/Vxl/VoxelSpan.cs b/src/Shimakaze.Sdk.Vxl/VoxelSpan.cs similarity index 72% rename from lib/Shimakaze.Sdk/Vxl/VoxelSpan.cs rename to src/Shimakaze.Sdk.Vxl/VoxelSpan.cs index d792bd38..4226950f 100644 --- a/lib/Shimakaze.Sdk/Vxl/VoxelSpan.cs +++ b/src/Shimakaze.Sdk.Vxl/VoxelSpan.cs @@ -3,12 +3,12 @@ /// /// 片结构 /// -public record struct VoxelSpan +public record class VoxelSpan { /// /// Variable number of span segments to make up a whole span /// - public VoxelSpanSegment[] Sections = Array.Empty(); + public VoxelSpanSegment[] Sections { get; set; } = []; /// /// VoxelSpan diff --git a/lib/Shimakaze.Sdk/Vxl/VoxelSpanSegment.cs b/src/Shimakaze.Sdk.Vxl/VoxelSpanSegment.cs similarity index 67% rename from lib/Shimakaze.Sdk/Vxl/VoxelSpanSegment.cs rename to src/Shimakaze.Sdk.Vxl/VoxelSpanSegment.cs index 02aab8d2..e615c43d 100644 --- a/lib/Shimakaze.Sdk/Vxl/VoxelSpanSegment.cs +++ b/src/Shimakaze.Sdk.Vxl/VoxelSpanSegment.cs @@ -3,24 +3,24 @@ namespace Shimakaze.Sdk.Vxl; /// /// VoxelSpanSegment /// -public record struct VoxelSpanSegment +public record class VoxelSpanSegment { /// /// Number of empty voxels before this span segment /// - public byte SkipCount; + public byte SkipCount { get; set; } /// /// Number of voxels in this span segment /// - public byte NumVoxels; + public byte NumVoxels { get; set; } /// /// The voxels in the span segment /// - public Voxel[] Voxels; + public Voxel[] Voxels { get; set; } = []; /// /// Always equal to /// - public byte NumVoxels2; + public byte NumVoxels2 { get; set; } } \ No newline at end of file diff --git a/lib/Shimakaze.Sdk/IO/Vxl/VoxelWriter.cs b/src/Shimakaze.Sdk.Vxl/VoxelWriter.cs similarity index 77% rename from lib/Shimakaze.Sdk/IO/Vxl/VoxelWriter.cs rename to src/Shimakaze.Sdk.Vxl/VoxelWriter.cs index dbe2d920..455ee60b 100644 --- a/lib/Shimakaze.Sdk/IO/Vxl/VoxelWriter.cs +++ b/src/Shimakaze.Sdk.Vxl/VoxelWriter.cs @@ -1,25 +1,20 @@ -using Shimakaze.Sdk.IO.Pal; -using Shimakaze.Sdk.Pal; -using Shimakaze.Sdk.Vxl; +using Shimakaze.Sdk.Pal; -namespace Shimakaze.Sdk.IO.Vxl; +namespace Shimakaze.Sdk.Vxl; /// /// VoxelWriter /// -public sealed class VoxelWriter : AsyncWriter, IDisposable, IAsyncDisposable +/// +/// PaletteReader +/// +public sealed class VoxelWriter(Stream stream, bool leaveOpen = false) : AsyncWriter(stream, leaveOpen), IDisposable, IAsyncDisposable { - /// - /// PaletteReader - /// - public VoxelWriter(Stream stream, bool leaveOpen = false) : base(stream, leaveOpen) - { - } /// public override async Task WriteAsync(VXLFile value, IProgress? progress = null, CancellationToken cancellationToken = default) { - uint limbDataOffset = 34 + Palette.COLOR_COUNT * 3 + value.Header.NumSections * 28; + uint limbDataOffset = 34 + Palette.ColorCount * 3 + value.Header.NumSections * 28; BaseStream.Write(value.Header); diff --git a/test/ReadMe.md b/test/ReadMe.md deleted file mode 100644 index 7ce77a8f..00000000 --- a/test/ReadMe.md +++ /dev/null @@ -1,4 +0,0 @@ -# Unit Tests - -这里包含项目的单元测试。 -The unit tests for the project are included here. diff --git a/test/Shimakaze.Sdk.Preprocessor.Tests/Assets/conditionTest.ini b/test/Shimakaze.Sdk.Build.Tests/Assets/conditionTest.ini similarity index 100% rename from test/Shimakaze.Sdk.Preprocessor.Tests/Assets/conditionTest.ini rename to test/Shimakaze.Sdk.Build.Tests/Assets/conditionTest.ini diff --git a/test/Shimakaze.Sdk.Preprocessor.Tests/Assets/defineTest.ini b/test/Shimakaze.Sdk.Build.Tests/Assets/defineTest.ini similarity index 100% rename from test/Shimakaze.Sdk.Preprocessor.Tests/Assets/defineTest.ini rename to test/Shimakaze.Sdk.Build.Tests/Assets/defineTest.ini diff --git a/test/Shimakaze.Sdk.Preprocessor.Tests/Assets/errorTest.ini b/test/Shimakaze.Sdk.Build.Tests/Assets/errorTest.ini similarity index 100% rename from test/Shimakaze.Sdk.Preprocessor.Tests/Assets/errorTest.ini rename to test/Shimakaze.Sdk.Build.Tests/Assets/errorTest.ini diff --git a/test/Shimakaze.Sdk.Tests/Assets/normal.ini b/test/Shimakaze.Sdk.Build.Tests/Assets/normal.ini similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/normal.ini rename to test/Shimakaze.Sdk.Build.Tests/Assets/normal.ini diff --git a/test/Shimakaze.Sdk.Preprocessor.Tests/Assets/typeTest.ini b/test/Shimakaze.Sdk.Build.Tests/Assets/typeTest.ini similarity index 100% rename from test/Shimakaze.Sdk.Preprocessor.Tests/Assets/typeTest.ini rename to test/Shimakaze.Sdk.Build.Tests/Assets/typeTest.ini diff --git a/test/Shimakaze.Sdk.Build.Tests/Shimakaze.Sdk.Build.Tests.csproj b/test/Shimakaze.Sdk.Build.Tests/Shimakaze.Sdk.Build.Tests.csproj index 15b83cc4..ee815c2b 100644 --- a/test/Shimakaze.Sdk.Build.Tests/Shimakaze.Sdk.Build.Tests.csproj +++ b/test/Shimakaze.Sdk.Build.Tests/Shimakaze.Sdk.Build.Tests.csproj @@ -1,21 +1,15 @@ - - - - - - - - - - - + + + + + - - - + + + \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Build.Tests/TaskCsfGeneratorTest.cs b/test/Shimakaze.Sdk.Build.Tests/TaskCsfGeneratorTest.cs index 2df84056..6f17a6a9 100644 --- a/test/Shimakaze.Sdk.Build.Tests/TaskCsfGeneratorTest.cs +++ b/test/Shimakaze.Sdk.Build.Tests/TaskCsfGeneratorTest.cs @@ -21,8 +21,8 @@ public class TaskCsfGeneratorTest public void JsonV1Test() { TaskItem item = new(Path.Combine(Assets, InputJsonV1File)); - item.SetMetadata(TaskCsfGenerator.Metadata_Intermediate, Path.Combine(OutputPath, InputJsonV1File)); - item.SetMetadata(TaskCsfGenerator.Metadata_Type, "JsonV1"); + item.SetMetadata(TaskCsfGenerator.MetadataIntermediate, Path.Combine(OutputPath, InputJsonV1File)); + item.SetMetadata(TaskCsfGenerator.MetadataType, "JsonV1"); TaskCsfGenerator task = new() { @@ -36,8 +36,8 @@ public void JsonV1Test() public void JsonV2Test() { TaskItem item = new(Path.Combine(Assets, InputJsonV2File)); - item.SetMetadata(TaskCsfGenerator.Metadata_Intermediate, Path.Combine(OutputPath, InputJsonV2File)); - item.SetMetadata(TaskCsfGenerator.Metadata_Type, "JsonV2"); + item.SetMetadata(TaskCsfGenerator.MetadataIntermediate, Path.Combine(OutputPath, InputJsonV2File)); + item.SetMetadata(TaskCsfGenerator.MetadataType, "JsonV2"); TaskCsfGenerator task = new() { @@ -51,7 +51,7 @@ public void JsonV2Test() public void Startup() { _buildEngine = new Mock(); - _errors = new List(); + _errors = []; _buildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => _errors.Add(e)); Directory.CreateDirectory(OutputPath); @@ -61,8 +61,8 @@ public void Startup() public void UnknownTest() { TaskItem item = new(Path.Combine(Assets, InputXmlV1File)); - item.SetMetadata(TaskCsfGenerator.Metadata_Intermediate, Path.Combine(OutputPath, "Unknown")); - item.SetMetadata(TaskCsfGenerator.Metadata_Type, "Unknown"); + item.SetMetadata(TaskCsfGenerator.MetadataIntermediate, Path.Combine(OutputPath, "Unknown")); + item.SetMetadata(TaskCsfGenerator.MetadataType, "Unknown"); TaskCsfGenerator task = new() { @@ -76,8 +76,8 @@ public void UnknownTest() public void XmlV1Test() { TaskItem item = new(Path.Combine(Assets, InputXmlV1File)); - item.SetMetadata(TaskCsfGenerator.Metadata_Intermediate, Path.Combine(OutputPath, InputXmlV1File)); - item.SetMetadata(TaskCsfGenerator.Metadata_Type, "XmlV1"); + item.SetMetadata(TaskCsfGenerator.MetadataIntermediate, Path.Combine(OutputPath, InputXmlV1File)); + item.SetMetadata(TaskCsfGenerator.MetadataType, "XmlV1"); TaskCsfGenerator task = new() { @@ -91,8 +91,8 @@ public void XmlV1Test() public void YamlV1Test() { TaskItem item = new(Path.Combine(Assets, InputYamlV1File)); - item.SetMetadata(TaskCsfGenerator.Metadata_Intermediate, Path.Combine(OutputPath, InputYamlV1File)); - item.SetMetadata(TaskCsfGenerator.Metadata_Type, "YamlV1"); + item.SetMetadata(TaskCsfGenerator.MetadataIntermediate, Path.Combine(OutputPath, InputYamlV1File)); + item.SetMetadata(TaskCsfGenerator.MetadataType, "YamlV1"); TaskCsfGenerator task = new() { diff --git a/test/Shimakaze.Sdk.Build.Tests/TaskCsfMergerTest.cs b/test/Shimakaze.Sdk.Build.Tests/TaskCsfMergerTest.cs index 3a927d8b..d7365ad7 100644 --- a/test/Shimakaze.Sdk.Build.Tests/TaskCsfMergerTest.cs +++ b/test/Shimakaze.Sdk.Build.Tests/TaskCsfMergerTest.cs @@ -32,7 +32,7 @@ public void MergeTest() public void Startup() { _buildEngine = new Mock(); - _errors = new List(); + _errors = []; _buildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => _errors.Add(e)); Directory.CreateDirectory(OutputPath); diff --git a/test/Shimakaze.Sdk.Build.Tests/TaskIniMergerTest.cs b/test/Shimakaze.Sdk.Build.Tests/TaskIniMergerTest.cs index 374462db..f54442fd 100644 --- a/test/Shimakaze.Sdk.Build.Tests/TaskIniMergerTest.cs +++ b/test/Shimakaze.Sdk.Build.Tests/TaskIniMergerTest.cs @@ -32,7 +32,7 @@ public void MergeTest() public void Startup() { _buildEngine = new Mock(); - _errors = new List(); + _errors = []; _buildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => _errors.Add(e)); Directory.CreateDirectory(OutputPath); diff --git a/test/Shimakaze.Sdk.Build.Tests/TaskIniPreprocessorTest.cs b/test/Shimakaze.Sdk.Build.Tests/TaskIniPreprocessorTest.cs index af6c20f7..ab9e44f5 100644 --- a/test/Shimakaze.Sdk.Build.Tests/TaskIniPreprocessorTest.cs +++ b/test/Shimakaze.Sdk.Build.Tests/TaskIniPreprocessorTest.cs @@ -20,7 +20,7 @@ public class TaskIniPreprocessorTest public void Startup() { _buildEngine = new Mock(); - _errors = new List(); + _errors = []; _buildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => _errors.Add(e)); Directory.CreateDirectory(OutputPath); @@ -35,7 +35,7 @@ public void Test() { var path = Path.GetFullPath(Path.Combine(Assets, i)); TaskItem item = new(Path.GetFullPath(Path.Combine(Assets, i))); - item.SetMetadata(TaskIniPreprocessor.Metadata_Intermediate, Path.GetFullPath(Path.Combine(OutputPath, i))); + item.SetMetadata(TaskIniPreprocessor.MetadataIntermediate, Path.GetFullPath(Path.Combine(OutputPath, i))); return item; }).ToArray(), Defines = Defines, @@ -53,7 +53,7 @@ public void ErrorTest() { var path = Path.GetFullPath(Path.Combine(Assets, i)); TaskItem item = new(Path.GetFullPath(Path.Combine(Assets, i))); - item.SetMetadata(TaskIniPreprocessor.Metadata_Intermediate, Path.GetFullPath(Path.Combine(OutputPath, i))); + item.SetMetadata(TaskIniPreprocessor.MetadataIntermediate, Path.GetFullPath(Path.Combine(OutputPath, i))); return item; }).ToArray(), Defines = Defines, diff --git a/test/Shimakaze.Sdk.Build.Tests/TaskMixGeneratorTest.cs b/test/Shimakaze.Sdk.Build.Tests/TaskMixGeneratorTest.cs index b66fe0fb..5e668952 100644 --- a/test/Shimakaze.Sdk.Build.Tests/TaskMixGeneratorTest.cs +++ b/test/Shimakaze.Sdk.Build.Tests/TaskMixGeneratorTest.cs @@ -19,7 +19,7 @@ public class TaskMixGeneratorTest public void Startup() { _buildEngine = new Mock(); - _errors = new List(); + _errors = []; _buildEngine.Setup(x => x.LogErrorEvent(It.IsAny())).Callback(e => _errors.Add(e)); Directory.CreateDirectory(OutputPath); diff --git a/test/Shimakaze.Sdk.Tests/Assets/ra2md.v1.csf.json b/test/Shimakaze.Sdk.Csf.Json.Tests/Assets/ra2md.v1.csf.json similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/ra2md.v1.csf.json rename to test/Shimakaze.Sdk.Csf.Json.Tests/Assets/ra2md.v1.csf.json diff --git a/test/Shimakaze.Sdk.Tests/Assets/ra2md.v2.csf.json b/test/Shimakaze.Sdk.Csf.Json.Tests/Assets/ra2md.v2.csf.json similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/ra2md.v2.csf.json rename to test/Shimakaze.Sdk.Csf.Json.Tests/Assets/ra2md.v2.csf.json diff --git a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfAdvancedValueJsonConverterTests.cs b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfAdvancedValueJsonConverterTests.cs similarity index 89% rename from test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfAdvancedValueJsonConverterTests.cs rename to test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfAdvancedValueJsonConverterTests.cs index 9bb478ff..eb786907 100644 --- a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfAdvancedValueJsonConverterTests.cs +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfAdvancedValueJsonConverterTests.cs @@ -1,7 +1,9 @@ using System.Text; using System.Text.Json; -namespace Shimakaze.Sdk.Csf.Json.Converter.V1; +using Shimakaze.Sdk.Csf.Json.Converter.V1; + +namespace Shimakaze.Sdk.Csf.Json.Tests.Converter.V1; [TestClass] public class CsfAdvancedValueJsonConverterTests @@ -11,7 +13,7 @@ public class CsfAdvancedValueJsonConverterTests // This is the test method for Read method [TestMethod] - public void Read_ShouldReturnCorrectCsfValue() + public void ReadShouldReturnCorrectCsfValue() { // Arrange var json = """{"value":"Hello","extra":"World"}"""u8; @@ -30,7 +32,7 @@ public void Read_ShouldReturnCorrectCsfValue() // Generated by Sydney This is the test method for Read method [TestMethod] - public void Read_ShouldReturnCorrectCsfValue1() + public void ReadShouldReturnCorrectCsfValue1() { // Arrange var json = """{"value":"Hello","extra":"World"}"""u8; @@ -56,7 +58,7 @@ public void Startup() // This is the test method for Write method [TestMethod] - public void Write_ShouldWriteCorrectJson() + public void WriteShouldWriteCorrectJson() { // Arrange var value = new CsfValue("Hello", "World"); diff --git a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfDataJsonConverterTests.cs b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfDataJsonConverterTests.cs similarity index 88% rename from test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfDataJsonConverterTests.cs rename to test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfDataJsonConverterTests.cs index afc8e01d..30d699c1 100644 --- a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfDataJsonConverterTests.cs +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfDataJsonConverterTests.cs @@ -1,7 +1,9 @@ using System.Text; using System.Text.Json; -namespace Shimakaze.Sdk.Csf.Json.Converter.V1; +using Shimakaze.Sdk.Csf.Json.Converter.V1; + +namespace Shimakaze.Sdk.Csf.Json.Tests.Converter.V1; [TestClass] public class CsfDataJsonConverterTests @@ -11,7 +13,7 @@ public class CsfDataJsonConverterTests // This is the test method for Read method [TestMethod] - public void Read_ShouldReturnCorrectCsfValue() + public void ReadShouldReturnCorrectCsfValue() { // Arrange var json = """{"label":"Hello","values":["The","World"]}"""u8; @@ -32,7 +34,7 @@ public void Read_ShouldReturnCorrectCsfValue() // Generated by Sydney This is the test method for Read method [TestMethod] - public void Read_ShouldReturnCorrectCsfValue1() + public void ReadShouldReturnCorrectCsfValue1() { // Arrange var json = """{"label":"Hello","value":{"value":"World"},"test":null}"""u8; @@ -52,10 +54,10 @@ public void Read_ShouldReturnCorrectCsfValue1() // This is the test method for Read method [TestMethod] - public void Read_ShouldReturnCorrectCsfValue2() + public void ReadShouldReturnCorrectCsfValue2() { // Arrange - Assert.ThrowsException(() => + Assert.ThrowsException(() => { var json = """{"label":"Hello","value":0}"""u8; var reader = new Utf8JsonReader(json); @@ -75,7 +77,7 @@ public void Startup() // This is the test method for Write method [TestMethod] - public void Write_ShouldWriteCorrectJson() + public void WriteShouldWriteCorrectJson() { // Arrange var value = new CsfData("Hello", new[] diff --git a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfLanguageJsonConverterTests.cs b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfLanguageJsonConverterTests.cs similarity index 82% rename from test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfLanguageJsonConverterTests.cs rename to test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfLanguageJsonConverterTests.cs index 5a2157ec..df670bb3 100644 --- a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfLanguageJsonConverterTests.cs +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfLanguageJsonConverterTests.cs @@ -1,7 +1,9 @@ using System.Text; using System.Text.Json; -namespace Shimakaze.Sdk.Csf.Json.Converter.V1; +using Shimakaze.Sdk.Csf.Json.Converter.V1; + +namespace Shimakaze.Sdk.Csf.Json.Tests.Converter.V1; [TestClass] public class CsfLanguageJsonConverterTests @@ -11,7 +13,7 @@ public class CsfLanguageJsonConverterTests private JsonSerializerOptions? _options; [TestMethod] - public void Read_ShouldReturnIntValue_WhenReaderHasNumberToken() + public void ReadShouldReturnIntValueWhenReaderHasNumberToken() { // Arrange var reader = new Utf8JsonReader("42"u8); @@ -25,7 +27,7 @@ public void Read_ShouldReturnIntValue_WhenReaderHasNumberToken() } [TestMethod] - public void Read_ShouldReturnIntValue_WhenReaderHasStringTokenAndValidLanguageCode() + public void ReadShouldReturnIntValueWhenReaderHasStringTokenAndValidLanguageCode() { // Arrange var reader = new Utf8JsonReader("\"fr\""u8); @@ -39,7 +41,7 @@ public void Read_ShouldReturnIntValue_WhenReaderHasStringTokenAndValidLanguageCo } [TestMethod] - public void Read_ShouldReturnIntValue_WhenReaderHasStringTokenAndValidLanguageCode1() + public void ReadShouldReturnIntValueWhenReaderHasStringTokenAndValidLanguageCode1() { var arr = new[] { @@ -69,10 +71,10 @@ public void Read_ShouldReturnIntValue_WhenReaderHasStringTokenAndValidLanguageCo } [TestMethod] - public void Read_ShouldThrowException_WhenReaderHasStringTokenAndInvalidLanguageCode() + public void ReadShouldThrowExceptionWhenReaderHasStringTokenAndInvalidLanguageCode() { // Act and assert - Assert.ThrowsException(() => + Assert.ThrowsException(() => { var reader = new Utf8JsonReader("\"foo\""u8); reader.Read(); // Move to the start object token @@ -81,10 +83,10 @@ public void Read_ShouldThrowException_WhenReaderHasStringTokenAndInvalidLanguage } [TestMethod] - public void Read_ShouldThrowException_WhenReaderHasUnsupportedTokenType() + public void ReadShouldThrowExceptionWhenReaderHasUnsupportedTokenType() { // Act and assert - Assert.ThrowsException(() => + Assert.ThrowsException(() => { var reader = new Utf8JsonReader("true"u8); reader.Read(); // Move to the start object token @@ -101,7 +103,7 @@ public void Startup() } [TestMethod] - public void Write_ShouldWriteNumberValue_WhenValueIsUnknownLanguageCode() + public void WriteShouldWriteNumberValueWhenValueIsUnknownLanguageCode() { // Arrange var value = 10; @@ -119,7 +121,7 @@ public void Write_ShouldWriteNumberValue_WhenValueIsUnknownLanguageCode() } [TestMethod] - public void Write_ShouldWriteStringValue_WhenValueIsKnownLanguageCode() + public void WriteShouldWriteStringValueWhenValueIsKnownLanguageCode() { // Arrange var value = 6; @@ -137,7 +139,7 @@ public void Write_ShouldWriteStringValue_WhenValueIsKnownLanguageCode() } [TestMethod] - public void Write_ShouldWriteStringValue_WhenValueIsKnownLanguageCode1() + public void WriteShouldWriteStringValueWhenValueIsKnownLanguageCode1() { var arr = new[] { diff --git a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfMetadataJsonConverterTests.cs b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfMetadataJsonConverterTests.cs similarity index 86% rename from test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfMetadataJsonConverterTests.cs rename to test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfMetadataJsonConverterTests.cs index f8663fc0..34e843a3 100644 --- a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfMetadataJsonConverterTests.cs +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfMetadataJsonConverterTests.cs @@ -1,6 +1,8 @@ using System.Text.Json; -namespace Shimakaze.Sdk.Csf.Json.Converter.V1; +using Shimakaze.Sdk.Csf.Json.Converter.V1; + +namespace Shimakaze.Sdk.Csf.Json.Tests.Converter.V1; [TestClass] public class CsfMetadataJsonConverterTests diff --git a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfSimpleValueJsonConverterTests.cs b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfSimpleValueJsonConverterTests.cs similarity index 80% rename from test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfSimpleValueJsonConverterTests.cs rename to test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfSimpleValueJsonConverterTests.cs index d29c9257..eace5a70 100644 --- a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfSimpleValueJsonConverterTests.cs +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfSimpleValueJsonConverterTests.cs @@ -1,6 +1,8 @@ using System.Text.Json; -namespace Shimakaze.Sdk.Csf.Json.Converter.V1; +using Shimakaze.Sdk.Csf.Json.Converter.V1; + +namespace Shimakaze.Sdk.Csf.Json.Tests.Converter.V1; [TestClass] public class CsfSimpleValueJsonConverterTests @@ -12,7 +14,7 @@ public void ReadTest() { CsfSimpleValueJsonConverter converter = new(); - Assert.ThrowsException(() => + Assert.ThrowsException(() => { var reader = new Utf8JsonReader("""{"hello":null}"""u8); reader.Read(); diff --git a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfValueJsonConverterTests.cs b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfValueJsonConverterTests.cs similarity index 89% rename from test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfValueJsonConverterTests.cs rename to test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfValueJsonConverterTests.cs index a056ba6c..ffcada20 100644 --- a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V1/CsfValueJsonConverterTests.cs +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V1/CsfValueJsonConverterTests.cs @@ -1,6 +1,8 @@ using System.Text.Json; -namespace Shimakaze.Sdk.Csf.Json.Converter.V1; +using Shimakaze.Sdk.Csf.Json.Converter.V1; + +namespace Shimakaze.Sdk.Csf.Json.Tests.Converter.V1; [TestClass] public class CsfValueJsonConverterTests @@ -11,7 +13,7 @@ public class CsfValueJsonConverterTests [TestMethod] public void ReadTest() { - Assert.ThrowsException(() => + Assert.ThrowsException(() => { var reader = new Utf8JsonReader("""null"""u8); reader.Read(); diff --git a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V2/CsfDataValueJsonConverterTests.cs b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V2/CsfDataValueJsonConverterTests.cs similarity index 92% rename from test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V2/CsfDataValueJsonConverterTests.cs rename to test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V2/CsfDataValueJsonConverterTests.cs index 3350f17a..8847428e 100644 --- a/test/Shimakaze.Sdk.Tests/Csf/Json/Converter/V2/CsfDataValueJsonConverterTests.cs +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/Converter/V2/CsfDataValueJsonConverterTests.cs @@ -1,6 +1,8 @@ using System.Text.Json; -namespace Shimakaze.Sdk.Csf.Json.Converter.V2; +using Shimakaze.Sdk.Csf.Json.Converter.V2; + +namespace Shimakaze.Sdk.Csf.Json.Tests.Converter.V2; [TestClass] public class CsfDataValueJsonConverterTests @@ -11,7 +13,7 @@ public class CsfDataValueJsonConverterTests [TestMethod] public void ReadTest() { - Assert.ThrowsException(() => + Assert.ThrowsException(() => { var reader = new Utf8JsonReader("""true"""u8); reader.Read(); diff --git a/test/Shimakaze.Sdk.Tests/IO/Csf/Json/CsfJsonV1SerializerTests.cs b/test/Shimakaze.Sdk.Csf.Json.Tests/CsfJsonV1SerializerTests.cs similarity index 96% rename from test/Shimakaze.Sdk.Tests/IO/Csf/Json/CsfJsonV1SerializerTests.cs rename to test/Shimakaze.Sdk.Csf.Json.Tests/CsfJsonV1SerializerTests.cs index cc26e0d3..25d39770 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Csf/Json/CsfJsonV1SerializerTests.cs +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/CsfJsonV1SerializerTests.cs @@ -1,6 +1,6 @@ using Shimakaze.Sdk.Csf; -namespace Shimakaze.Sdk.IO.Csf.Json.Tests; +namespace Shimakaze.Sdk.Csf.Json.Tests; [TestClass] public class CsfJsonV1WriterTests diff --git a/test/Shimakaze.Sdk.Tests/IO/Csf/Json/CsfJsonV2SerializerTests.cs b/test/Shimakaze.Sdk.Csf.Json.Tests/CsfJsonV2SerializerTests.cs similarity index 96% rename from test/Shimakaze.Sdk.Tests/IO/Csf/Json/CsfJsonV2SerializerTests.cs rename to test/Shimakaze.Sdk.Csf.Json.Tests/CsfJsonV2SerializerTests.cs index 31e6824a..1b64f933 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Csf/Json/CsfJsonV2SerializerTests.cs +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/CsfJsonV2SerializerTests.cs @@ -1,6 +1,6 @@ using Shimakaze.Sdk.Csf; -namespace Shimakaze.Sdk.IO.Csf.Json.Tests; +namespace Shimakaze.Sdk.Csf.Json.Tests; [TestClass] public class CsfJsonV2WriterTests diff --git a/test/Shimakaze.Sdk.Csf.Json.Tests/Shimakaze.Sdk.Csf.Json.Tests.csproj b/test/Shimakaze.Sdk.Csf.Json.Tests/Shimakaze.Sdk.Csf.Json.Tests.csproj new file mode 100644 index 00000000..43eb6f88 --- /dev/null +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/Shimakaze.Sdk.Csf.Json.Tests.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/test/Shimakaze.Sdk.Csf.Json.Tests/ThrowHelperTests.cs b/test/Shimakaze.Sdk.Csf.Json.Tests/ThrowHelperTests.cs new file mode 100644 index 00000000..62598363 --- /dev/null +++ b/test/Shimakaze.Sdk.Csf.Json.Tests/ThrowHelperTests.cs @@ -0,0 +1,37 @@ +using System.Text.Json; + +namespace Shimakaze.Sdk.Csf.Json.Tests; + +[TestClass] +public class ThrowHelperTests +{ + [TestMethod] + public void ThrowNotSupportTokenTest() + { + Assert.ThrowsException(() => JsonTokenType.Null.ThrowNotSupportToken()); + } + + [TestMethod] + public void ThrowNotSupportValueTest() + { + Assert.ThrowsException(() => JsonTokenType.Null.ThrowNotSupportValue()); + } + + [TestMethod] + public void ThrowWhenFalseTest() + { + Assert.ThrowsException(() => false.ThrowWhenFalse()); + } + + [TestMethod] + public void ThrowWhenNotTokenTest() + { + Assert.ThrowsException(() => JsonTokenType.Null.ThrowWhenNotToken(JsonTokenType.True)); + } + + [TestMethod] + public void ThrowWhenNullTest() + { + Assert.ThrowsException(() => ((object?)null).ThrowWhenNull()); + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Tests/Assets/ra2md.csf b/test/Shimakaze.Sdk.Csf.Tests/Assets/ra2md.csf similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/ra2md.csf rename to test/Shimakaze.Sdk.Csf.Tests/Assets/ra2md.csf diff --git a/test/Shimakaze.Sdk.Tests/IO/Csf/CsfMergerTests.cs b/test/Shimakaze.Sdk.Csf.Tests/CsfMergerTests.cs similarity index 93% rename from test/Shimakaze.Sdk.Tests/IO/Csf/CsfMergerTests.cs rename to test/Shimakaze.Sdk.Csf.Tests/CsfMergerTests.cs index a2c1e364..34524c25 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Csf/CsfMergerTests.cs +++ b/test/Shimakaze.Sdk.Csf.Tests/CsfMergerTests.cs @@ -2,10 +2,10 @@ using Shimakaze.Sdk.Csf; -namespace Shimakaze.Sdk.IO.Csf; +namespace Shimakaze.Sdk.Csf.Tests; [TestClass] -public class CsfMergerTests +public class CsfSetTests { private const string Assets = "Assets"; private const string InputFile = "ra2md.csf"; @@ -20,7 +20,7 @@ public async Task BuildAndWriteToTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -40,7 +40,7 @@ public async Task BuildTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -56,7 +56,7 @@ public async Task ContainsTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -70,7 +70,7 @@ public async Task CopyToTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -94,7 +94,7 @@ public async Task ExceptWithTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -109,7 +109,7 @@ public async Task GetEnumeratorTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -126,7 +126,7 @@ public async Task IntersectWithTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -141,7 +141,7 @@ public async Task OverlapsTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -160,7 +160,7 @@ public async Task RemoveTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -176,7 +176,7 @@ public async Task SetEqualsTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -197,7 +197,7 @@ public async Task SubsetTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -219,7 +219,7 @@ public async Task SymmetricExceptWithTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.IsTrue(csf.Data.SequenceEqual(merger)); @@ -239,7 +239,7 @@ public async Task UnionWithTestAsync() using CsfReader reader = new(stream); CsfDocument csf = await reader.ReadAsync(); - CsfMerger merger = new(); + CsfSet merger = []; merger.UnionWith(csf.Data); Assert.AreNotEqual(0, merger.Count); Assert.IsTrue(csf.Data.SequenceEqual(merger)); diff --git a/test/Shimakaze.Sdk.Tests/IO/Csf/CsfReaderTests.cs b/test/Shimakaze.Sdk.Csf.Tests/CsfReaderTests.cs similarity index 90% rename from test/Shimakaze.Sdk.Tests/IO/Csf/CsfReaderTests.cs rename to test/Shimakaze.Sdk.Csf.Tests/CsfReaderTests.cs index 75cc83c4..299572f3 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Csf/CsfReaderTests.cs +++ b/test/Shimakaze.Sdk.Csf.Tests/CsfReaderTests.cs @@ -1,4 +1,4 @@ -namespace Shimakaze.Sdk.IO.Csf; +namespace Shimakaze.Sdk.Csf.Tests; [TestClass] public class CsfReaderTests diff --git a/test/Shimakaze.Sdk.Tests/Csf/CsfThrowHelperTests.cs b/test/Shimakaze.Sdk.Csf.Tests/CsfThrowHelperTests.cs similarity index 97% rename from test/Shimakaze.Sdk.Tests/Csf/CsfThrowHelperTests.cs rename to test/Shimakaze.Sdk.Csf.Tests/CsfThrowHelperTests.cs index 2e93b56c..9ec7d318 100644 --- a/test/Shimakaze.Sdk.Tests/Csf/CsfThrowHelperTests.cs +++ b/test/Shimakaze.Sdk.Csf.Tests/CsfThrowHelperTests.cs @@ -1,4 +1,4 @@ -namespace Shimakaze.Sdk.Csf; +namespace Shimakaze.Sdk.Csf.Tests; [TestClass] public class CsfThrowHelperTests diff --git a/test/Shimakaze.Sdk.Tests/IO/Csf/CsfWriterTests.cs b/test/Shimakaze.Sdk.Csf.Tests/CsfWriterTests.cs similarity index 67% rename from test/Shimakaze.Sdk.Tests/IO/Csf/CsfWriterTests.cs rename to test/Shimakaze.Sdk.Csf.Tests/CsfWriterTests.cs index f809461e..33e9e38d 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Csf/CsfWriterTests.cs +++ b/test/Shimakaze.Sdk.Csf.Tests/CsfWriterTests.cs @@ -1,8 +1,9 @@ +using System.Globalization; using System.Security.Cryptography; using Shimakaze.Sdk.Csf; -namespace Shimakaze.Sdk.IO.Csf; +namespace Shimakaze.Sdk.Csf.Tests; [TestClass] public class CsfWriterTests @@ -12,7 +13,7 @@ public class CsfWriterTests private const string OutputFile = "WriteTest.csf"; private const string OutputPath = "Out"; - private CsfDocument _csf; + private CsfDocument _csf = default!; [TestInitialize] public async Task StartupAsync() @@ -30,9 +31,9 @@ public async Task WriteTestAsync() using (CsfWriter writer = new(stream)) await writer.WriteAsync(_csf); - var a = BitConverter.ToString(MD5.HashData(File.ReadAllBytes(Path.Combine(Assets, InputFile)))); - var b = BitConverter.ToString(MD5.HashData(File.ReadAllBytes(Path.Combine(OutputPath, OutputFile)))); + var a = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(Path.Combine(Assets, InputFile)))); + var b = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(Path.Combine(OutputPath, OutputFile)))); - Assert.AreEqual(a, b, true); + Assert.AreEqual(a, b, true, CultureInfo.InvariantCulture); } } \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Csf.Tests/Shimakaze.Sdk.Csf.Tests.csproj b/test/Shimakaze.Sdk.Csf.Tests/Shimakaze.Sdk.Csf.Tests.csproj new file mode 100644 index 00000000..6b512ec9 --- /dev/null +++ b/test/Shimakaze.Sdk.Csf.Tests/Shimakaze.Sdk.Csf.Tests.csproj @@ -0,0 +1 @@ + diff --git a/test/Shimakaze.Sdk.Tests/Assets/ra2md.v1.csf.xml b/test/Shimakaze.Sdk.Csf.Xml.Tests/Assets/ra2md.v1.csf.xml similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/ra2md.v1.csf.xml rename to test/Shimakaze.Sdk.Csf.Xml.Tests/Assets/ra2md.v1.csf.xml diff --git a/test/Shimakaze.Sdk.Tests/IO/Csf/Xml/CsfXmlV1SerializerTests.cs b/test/Shimakaze.Sdk.Csf.Xml.Tests/CsfXmlV1SerializerTests.cs similarity index 82% rename from test/Shimakaze.Sdk.Tests/IO/Csf/Xml/CsfXmlV1SerializerTests.cs rename to test/Shimakaze.Sdk.Csf.Xml.Tests/CsfXmlV1SerializerTests.cs index 79015b87..e46a4c0b 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Csf/Xml/CsfXmlV1SerializerTests.cs +++ b/test/Shimakaze.Sdk.Csf.Xml.Tests/CsfXmlV1SerializerTests.cs @@ -1,6 +1,6 @@ using Shimakaze.Sdk.Csf; -namespace Shimakaze.Sdk.IO.Csf.Xml; +namespace Shimakaze.Sdk.Csf.Xml.Tests; [TestClass] public class CsfXmlV1WriterTests @@ -16,8 +16,8 @@ public class CsfXmlV1WriterTests [TestMethod] public async Task DeserializeTestAsync() { - using Stream stream = File.OpenRead(Path.Combine(Assets, InputXmlFile)); - using Stream xmlout = File.Create(Path.Combine(OutputPath, OutputDeserializeXmlFile)); + using var stream = File.OpenText(Path.Combine(Assets, InputXmlFile)); + using var xmlout = File.CreateText(Path.Combine(OutputPath, OutputDeserializeXmlFile)); using Stream csfout = File.Create(Path.Combine(OutputPath, OutputDeserializeCsfFile)); using CsfXmlV1Reader deserializer = new(stream); using CsfXmlV1Writer xmlSerializer = new(xmlout); @@ -32,7 +32,7 @@ public async Task DeserializeTestAsync() public async Task SerializeTest() { using Stream stream = File.OpenRead(Path.Combine(Assets, InputCsfFile)); - using Stream output = File.Create(Path.Combine(OutputPath, OutputSerializeFile)); + using var output = File.CreateText(Path.Combine(OutputPath, OutputSerializeFile)); using CsfReader reader = new(stream); using CsfXmlV1Writer serializer = new(output); CsfDocument document = await reader.ReadAsync(); diff --git a/test/Shimakaze.Sdk.Csf.Xml.Tests/Shimakaze.Sdk.Csf.Xml.Tests.csproj b/test/Shimakaze.Sdk.Csf.Xml.Tests/Shimakaze.Sdk.Csf.Xml.Tests.csproj new file mode 100644 index 00000000..43eb6f88 --- /dev/null +++ b/test/Shimakaze.Sdk.Csf.Xml.Tests/Shimakaze.Sdk.Csf.Xml.Tests.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/test/Shimakaze.Sdk.Tests/Assets/ra2md.v1.csf.yaml b/test/Shimakaze.Sdk.Csf.Yaml.Tests/Assets/ra2md.v1.csf.yaml similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/ra2md.v1.csf.yaml rename to test/Shimakaze.Sdk.Csf.Yaml.Tests/Assets/ra2md.v1.csf.yaml diff --git a/test/Shimakaze.Sdk.Tests/IO/Csf/Yaml/CsfYamlV1SerializerTests.cs b/test/Shimakaze.Sdk.Csf.Yaml.Tests/CsfYamlV1SerializerTests.cs similarity index 82% rename from test/Shimakaze.Sdk.Tests/IO/Csf/Yaml/CsfYamlV1SerializerTests.cs rename to test/Shimakaze.Sdk.Csf.Yaml.Tests/CsfYamlV1SerializerTests.cs index bd9fa575..f8dda65d 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Csf/Yaml/CsfYamlV1SerializerTests.cs +++ b/test/Shimakaze.Sdk.Csf.Yaml.Tests/CsfYamlV1SerializerTests.cs @@ -1,6 +1,6 @@ using Shimakaze.Sdk.Csf; -namespace Shimakaze.Sdk.IO.Csf.Yaml.Tests; +namespace Shimakaze.Sdk.Csf.Yaml.Tests; [TestClass] public class CsfYamlV1WriterTests @@ -16,9 +16,9 @@ public class CsfYamlV1WriterTests [TestMethod] public async Task DeserializeTestAsync() { - using Stream stream = File.OpenRead(Path.Combine(Assets, InputYmlFile)); + using var stream = File.OpenText(Path.Combine(Assets, InputYmlFile)); using Stream csfout = File.Create(Path.Combine(OutputPath, OutputDeserializeCsfFile)); - using Stream ymlout = File.Create(Path.Combine(OutputPath, OutputDeserializeYamlFile)); + using var ymlout = File.CreateText(Path.Combine(OutputPath, OutputDeserializeYamlFile)); using CsfYamlV1Reader deserializer = new(stream); using CsfWriter writer = new(csfout); using CsfYamlV1Writer yamlV1Writer = new(ymlout); @@ -31,7 +31,7 @@ public async Task DeserializeTestAsync() public async Task SerializeTestAsync() { using Stream stream = File.OpenRead(Path.Combine(Assets, InputCsfFile)); - using Stream output = File.Create(Path.Combine(OutputPath, OutputSerializeFile)); + using var output = File.CreateText(Path.Combine(OutputPath, OutputSerializeFile)); using CsfReader reader = new(stream); using CsfYamlV1Writer serializer = new(output); CsfDocument document = await reader.ReadAsync(); diff --git a/test/Shimakaze.Sdk.Csf.Yaml.Tests/Shimakaze.Sdk.Csf.Yaml.Tests.csproj b/test/Shimakaze.Sdk.Csf.Yaml.Tests/Shimakaze.Sdk.Csf.Yaml.Tests.csproj new file mode 100644 index 00000000..43eb6f88 --- /dev/null +++ b/test/Shimakaze.Sdk.Csf.Yaml.Tests/Shimakaze.Sdk.Csf.Yaml.Tests.csproj @@ -0,0 +1,5 @@ + + + + + diff --git a/test/Shimakaze.Sdk.Tests/Assets/jeep.hva b/test/Shimakaze.Sdk.Hva.Tests/Assets/jeep.hva similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/jeep.hva rename to test/Shimakaze.Sdk.Hva.Tests/Assets/jeep.hva diff --git a/test/Shimakaze.Sdk.Tests/IO/Hva/HvaReaderTest.cs b/test/Shimakaze.Sdk.Hva.Tests/HvaReaderTest.cs similarity index 93% rename from test/Shimakaze.Sdk.Tests/IO/Hva/HvaReaderTest.cs rename to test/Shimakaze.Sdk.Hva.Tests/HvaReaderTest.cs index fed1fa1f..3836c953 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Hva/HvaReaderTest.cs +++ b/test/Shimakaze.Sdk.Hva.Tests/HvaReaderTest.cs @@ -1,4 +1,4 @@ -namespace Shimakaze.Sdk.IO.Hva.Tests; +namespace Shimakaze.Sdk.Hva.Tests; [TestClass] public sealed class HvaReaderTest diff --git a/test/Shimakaze.Sdk.Tests/IO/Hva/HvaWriterTest.cs b/test/Shimakaze.Sdk.Hva.Tests/HvaWriterTest.cs similarity index 67% rename from test/Shimakaze.Sdk.Tests/IO/Hva/HvaWriterTest.cs rename to test/Shimakaze.Sdk.Hva.Tests/HvaWriterTest.cs index 260bdafa..2affbeea 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Hva/HvaWriterTest.cs +++ b/test/Shimakaze.Sdk.Hva.Tests/HvaWriterTest.cs @@ -1,8 +1,7 @@ +using System.Globalization; using System.Security.Cryptography; -using Shimakaze.Sdk.Hva; - -namespace Shimakaze.Sdk.IO.Hva.Tests; +namespace Shimakaze.Sdk.Hva.Tests; [TestClass] public sealed class HvaWriterTest @@ -11,7 +10,7 @@ public sealed class HvaWriterTest private const string InputFile = "jeep.hva"; private const string OutputFile = "jeep.hva"; private const string OutputPath = "Out"; - private HvaFile _hva; + private HvaFile _hva = default!; [TestInitialize] public async Task StartupAsync() @@ -31,9 +30,9 @@ public async Task WriteTestAsync() using (HvaWriter writer = new(stream)) await writer.WriteAsync(_hva); - var a = BitConverter.ToString(MD5.HashData(File.ReadAllBytes(Path.Combine(Assets, InputFile)))); - var b = BitConverter.ToString(MD5.HashData(File.ReadAllBytes(Path.Combine(OutputPath, OutputFile)))); + var a = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(Path.Combine(Assets, InputFile)))); + var b = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(Path.Combine(OutputPath, OutputFile)))); - Assert.AreEqual(a, b, true); + Assert.AreEqual(a, b, true, CultureInfo.InvariantCulture); } } \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Hva.Tests/Shimakaze.Sdk.Hva.Tests.csproj b/test/Shimakaze.Sdk.Hva.Tests/Shimakaze.Sdk.Hva.Tests.csproj new file mode 100644 index 00000000..6b512ec9 --- /dev/null +++ b/test/Shimakaze.Sdk.Hva.Tests/Shimakaze.Sdk.Hva.Tests.csproj @@ -0,0 +1 @@ + diff --git a/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniDocumentBinderTests.cs b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniDocumentBinderTests.cs new file mode 100644 index 00000000..9225f242 --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniDocumentBinderTests.cs @@ -0,0 +1,67 @@ +using System.Text.Json; + +namespace Shimakaze.Sdk.Ini.Ares.Tests; + +[TestClass()] +public class AresIniDocumentBinderTests +{ + private readonly JsonSerializerOptions _options = new() + { + WriteIndented = true, + }; + + [TestMethod()] + public void BindTest() + { + const string ini = """ + ; 节前注释 + [Section1] ; 行内注释 + ; 注释 + Key1=Value1 ; 注释 + Key2=Value2 + Key3=Value3 + Key4 + + ; 节前注释 + [Section2 ABC] : [Section1] ; 行内注释 + ; 注释 + Key1 ABC = Value1 ABC ; 注释 + Key3=ValueNot3 + Key4=Value4 + + [Section3] + += 1 + += 2 + += 3 + += 4 + += 5 + += 6 + += 7 + += 8 + += 9 + += 10 + """; + + using StringReader sr = new(ini); + using AresIniTokenReader reader = new(sr); + using AresIniDocumentBinder binder = new(reader); + AresIniDocument doc = binder.Bind(); + + Console.WriteLine(JsonSerializer.Serialize(doc, _options)); + + Assert.IsNotNull(doc); + Assert.AreEqual("Value1", doc["Section1", "Key1"]); + Assert.AreEqual("Value2", doc["Section1", "Key2"]); + Assert.AreEqual("Value3", doc["Section1", "Key3"]); + Assert.IsTrue(doc["Section1"].ContainsKey("Key4")); + + Assert.AreEqual("Section1", doc["Section2 ABC"].BaseName); + Assert.AreEqual("Value1 ABC", doc["Section2 ABC", "Key1 ABC"]); + Assert.AreEqual("Value2", doc["Section2 ABC", "Key2"]); + Assert.AreEqual("ValueNot3", doc["Section2 ABC", "Key3"]); + Assert.AreEqual("Value4", doc["Section2 ABC", "Key4"]); + + Assert.AreEqual(10, doc["Section3"].Count); + } + +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniDocumentTests.cs b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniDocumentTests.cs new file mode 100644 index 00000000..2ada8a3f --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniDocumentTests.cs @@ -0,0 +1,71 @@ +namespace Shimakaze.Sdk.Ini.Ares.Tests; + +[TestClass()] +public class AresIniDocumentTests +{ + private AresIniDocument _ini = null!; + + [TestInitialize()] + public void Initialize() + { + _ini = [ + new AresIniSection("Section") + { + ["Key1"] = "Value1" + } + ]; + } + + [TestMethod()] + public void IniDocumentTest() + { + Assert.IsNotNull(new AresIniDocument()); + Assert.IsNotNull(_ini); + } + + [TestMethod()] + public void AddTest() + { + const string section = "Section1"; + _ini.Add(new(section)); + Assert.IsNotNull(_ini[section]); + } + + [TestMethod()] + public void ClearTest() + { + _ini.Clear(); + Assert.AreEqual(0, _ini.Count); + } + + [TestMethod()] + public void ContainsSectionTest() + { + Assert.IsTrue(_ini.ContainsSection("Section")); + Assert.IsFalse(_ini.ContainsSection("Section1")); + } + + + [TestMethod()] + public void RemoveTest() + { + _ini.Remove("Section"); + Assert.IsFalse(_ini.ContainsSection("Section")); + } + + [TestMethod()] + public void TryGetSectionTest() + { + Assert.IsTrue(_ini.TryGetSection("Section", out var section)); + Assert.IsNotNull(section); + Assert.IsFalse(_ini.TryGetSection("Section1", out section)); + Assert.IsNull(section); + } + + [TestMethod()] + public void GetEnumeratorTest() + { + using var enumerator = _ini.GetEnumerator(); + Assert.IsNotNull(enumerator); + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniSectionTests.cs b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniSectionTests.cs new file mode 100644 index 00000000..0ff79304 --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniSectionTests.cs @@ -0,0 +1,72 @@ +namespace Shimakaze.Sdk.Ini.Ares.Tests; + +[TestClass()] +public class AresIniSectionTests +{ + private AresIniSection _section = null!; + + [TestInitialize()] + public void Initialize() + { + _section = new("Section", "BaseSection") + { + ["Key1"] = "Value1" + }; + } + + [TestMethod()] + public void IniSectionTest() + { + Assert.IsNotNull(_section); + Assert.AreEqual("BaseSection", _section.BaseName); + _section = new("Section", default, new() + { + ["Key1"] = "Value1" + }); + } + + [TestMethod()] + public void AddTest() + { + _section.Add("Key2", "Value2"); + Assert.AreEqual("Value2", _section["Key2"]); + } + + [TestMethod()] + public void ContainsKeyTest() + { + Assert.AreEqual(true, _section.ContainsKey("Key1")); + Assert.IsFalse(_section.ContainsKey("Key2")); + + } + + [TestMethod()] + public void RemoveTest() + { + _section.Remove("Key1"); + Assert.IsFalse(_section.ContainsKey("Key1")); + } + + [TestMethod()] + public void TryGetValueTest() + { + Assert.IsTrue(_section.TryGetValue("Key1", out var value)); + Assert.AreEqual("Value1", value); + Assert.IsFalse(_section.TryGetValue("Key2", out value)); + Assert.IsNull(value); + } + + [TestMethod()] + public void ClearTest() + { + _section.Clear(); + Assert.AreEqual(0, _section.Count); + } + + [TestMethod()] + public void GetEnumeratorTest() + { + using var enumerator = _section.GetEnumerator(); + Assert.IsNotNull(enumerator); + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniTokenReaderTests.cs b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniTokenReaderTests.cs new file mode 100644 index 00000000..ca755948 --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniTokenReaderTests.cs @@ -0,0 +1,31 @@ +namespace Shimakaze.Sdk.Ini.Ares.Tests; + +[TestClass()] +public class AresIniTokenReaderTests +{ + [TestMethod()] + public void Test() + { + const string ini = "[Section]\r\nKey=Value\r\n"; + Stack tokens = new(); + tokens.Push(AresIniTokenTools.EOF); + tokens.Push(AresIniTokenTools.LF); + tokens.Push(AresIniTokenTools.CR); + tokens.Push(AresIniTokenTools.Value("Value")); + tokens.Push(AresIniTokenTools.EQ); + tokens.Push(AresIniTokenTools.Value("Key")); + tokens.Push(AresIniTokenTools.LF); + tokens.Push(AresIniTokenTools.CR); + tokens.Push(AresIniTokenTools.EndBracket); + tokens.Push(AresIniTokenTools.Value("Section")); + tokens.Push(AresIniTokenTools.BeginBracket); + + using StringReader sr = new(ini); + using AresIniTokenReader reader = new(sr); + foreach (var item in reader) + { + Console.WriteLine(item); + Assert.AreEqual(tokens.Pop(), item); + } + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniTokenToolsTests.cs b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniTokenToolsTests.cs new file mode 100644 index 00000000..114228a9 --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniTokenToolsTests.cs @@ -0,0 +1,33 @@ +namespace Shimakaze.Sdk.Ini.Ares.Tests; + +[TestClass()] +public class AresIniTokenToolsTests +{ + [TestMethod()] + public void WhiteTest() + { + Assert.AreEqual(-1, AresIniTokenTools.EOF.Token); + Assert.AreEqual('\r', AresIniTokenTools.CR.Token); + Assert.AreEqual('\n', AresIniTokenTools.LF.Token); + Assert.AreEqual(' ', AresIniTokenTools.SPACE.Token); + Assert.AreEqual('\t', AresIniTokenTools.TAB.Token); + } + + [TestMethod()] + public void SignTest() + { + Assert.AreEqual(';', AresIniTokenTools.SEMI.Token); + Assert.AreEqual('=', AresIniTokenTools.EQ.Token); + Assert.AreEqual('[', AresIniTokenTools.BeginBracket.Token); + Assert.AreEqual(']', AresIniTokenTools.EndBracket.Token); + } + + [TestMethod()] + public void StringTest() + { + string value = Guid.NewGuid().ToString(); + var token = AresIniTokenTools.Value(value); + Assert.AreEqual(1, token.Token); + Assert.AreEqual(value, token.Value); + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniTokenWriterTests.cs b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniTokenWriterTests.cs new file mode 100644 index 00000000..88857957 --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Ares.Tests/AresIniTokenWriterTests.cs @@ -0,0 +1,23 @@ +using System.Text; + +namespace Shimakaze.Sdk.Ini.Ares.Tests; + +[TestClass()] +public class AresIniTokenWriterTests +{ + [TestMethod()] + public void WriteTest() + { + using MemoryStream ms = new(); + + using StreamWriter sw = new(ms); + using AresIniTokenWriter writer = new(sw); + writer.Write(IniTokenTools.BeginBracket); + writer.Write(IniTokenTools.Value("Section")); + writer.WriteLine(IniTokenTools.EndBracket); + writer.Flush(); + + ms.Seek(0, SeekOrigin.Begin); + Assert.AreEqual("[Section]" + sw.NewLine, Encoding.UTF8.GetString(ms.ToArray())); + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Ares.Tests/Shimakaze.Sdk.Ini.Ares.Tests.csproj b/test/Shimakaze.Sdk.Ini.Ares.Tests/Shimakaze.Sdk.Ini.Ares.Tests.csproj new file mode 100644 index 00000000..61718a1f --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Ares.Tests/Shimakaze.Sdk.Ini.Ares.Tests.csproj @@ -0,0 +1,3 @@ + + + diff --git a/test/Shimakaze.Sdk.Ini.Tests/IniDocumentBinderTests.cs b/test/Shimakaze.Sdk.Ini.Tests/IniDocumentBinderTests.cs new file mode 100644 index 00000000..984216ef --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Tests/IniDocumentBinderTests.cs @@ -0,0 +1,53 @@ +using System.Text.Json; + +namespace Shimakaze.Sdk.Ini.Tests; + +[TestClass()] +public class IniDocumentBinderTests +{ + private readonly JsonSerializerOptions _options = new() + { + WriteIndented = true, + }; + + [TestMethod()] + public void BindTest() + { + const string ini = """ + ; 节前注释 + [Section1] ; 行内注释 + ; 注释 + Key1=Value1 ; 注释 + Key2=Value2 + Key3= + Key4 + + ; 节前注释 + [Section2 ABC] ; 行内注释 + ; 注释 + Key1 ABC = Value1 ABC ; 注释 + Key2 = Value2 + Key3= + Key4 + """; + + using StringReader sr = new(ini); + using IniTokenReader reader = new(sr); + using IniDocumentBinder binder = new(reader); + IniDocument doc = binder.Bind(); + + Console.WriteLine(JsonSerializer.Serialize(doc, _options)); + + Assert.IsNotNull(doc); + Assert.AreEqual("Value1", doc["Section1", "Key1"]); + Assert.AreEqual("Value2", doc["Section1", "Key2"]); + Assert.IsTrue(doc["Section1"].ContainsKey("Key3")); + Assert.IsTrue(doc["Section1"].ContainsKey("Key4")); + + Assert.AreEqual("Value1 ABC", doc["Section2 ABC", "Key1 ABC"]); + Assert.AreEqual("Value2", doc["Section2 ABC", "Key2"]); + Assert.IsTrue(doc["Section2 ABC"].ContainsKey("Key3")); + Assert.IsTrue(doc["Section2 ABC"].ContainsKey("Key4")); + } + +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Tests/IniDocumentTests.cs b/test/Shimakaze.Sdk.Ini.Tests/IniDocumentTests.cs new file mode 100644 index 00000000..a2cd9c04 --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Tests/IniDocumentTests.cs @@ -0,0 +1,71 @@ +namespace Shimakaze.Sdk.Ini.Tests; + +[TestClass()] +public class IniDocumentTests +{ + private IniDocument _ini = null!; + + [TestInitialize()] + public void Initialize() + { + _ini = [ + new IniSection("Section") + { + ["Key1"] = "Value1" + } + ]; + } + + [TestMethod()] + public void IniDocumentTest() + { + Assert.IsNotNull(new IniDocument()); + Assert.IsNotNull(_ini); + } + + [TestMethod()] + public void AddTest() + { + const string section = "Section1"; + _ini.Add(new(section)); + Assert.IsNotNull(_ini[section]); + } + + [TestMethod()] + public void ClearTest() + { + _ini.Clear(); + Assert.AreEqual(0, _ini.Count); + } + + [TestMethod()] + public void ContainsSectionTest() + { + Assert.IsTrue(_ini.ContainsSection("Section")); + Assert.IsFalse(_ini.ContainsSection("Section1")); + } + + + [TestMethod()] + public void RemoveTest() + { + _ini.Remove("Section"); + Assert.IsFalse(_ini.ContainsSection("Section")); + } + + [TestMethod()] + public void TryGetSectionTest() + { + Assert.IsTrue(_ini.TryGetSection("Section", out var section)); + Assert.IsNotNull(section); + Assert.IsFalse(_ini.TryGetSection("Section1", out section)); + Assert.IsNull(section); + } + + [TestMethod()] + public void GetEnumeratorTest() + { + using var enumerator = _ini.GetEnumerator(); + Assert.IsNotNull(enumerator); + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Tests/IniSectionTests.cs b/test/Shimakaze.Sdk.Ini.Tests/IniSectionTests.cs new file mode 100644 index 00000000..b22336df --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Tests/IniSectionTests.cs @@ -0,0 +1,71 @@ +namespace Shimakaze.Sdk.Ini.Tests; + +[TestClass()] +public class IniSectionTests +{ + private IniSection _section = null!; + + [TestInitialize()] + public void Initialize() + { + _section = new("Section") + { + ["Key1"] = "Value1" + }; + } + + [TestMethod()] + public void IniSectionTest() + { + Assert.IsNotNull(_section); + _section = new("Section", new() + { + ["Key1"] = "Value1" + }); + } + + [TestMethod()] + public void AddTest() + { + _section.Add("Key2", "Value2"); + Assert.AreEqual("Value2", _section["Key2"]); + } + + [TestMethod()] + public void ContainsKeyTest() + { + Assert.IsTrue(_section.ContainsKey("Key1")); + Assert.IsFalse(_section.ContainsKey("Key2")); + + } + + [TestMethod()] + public void RemoveTest() + { + _section.Remove("Key1"); + Assert.IsFalse(_section.ContainsKey("Key1")); + } + + [TestMethod()] + public void TryGetValueTest() + { + Assert.IsTrue(_section.TryGetValue("Key1", out var value)); + Assert.AreEqual("Value1", value); + Assert.IsFalse(_section.TryGetValue("Key2", out value)); + Assert.IsNull(value); + } + + [TestMethod()] + public void ClearTest() + { + _section.Clear(); + Assert.AreEqual(0, _section.Count); + } + + [TestMethod()] + public void GetEnumeratorTest() + { + using var enumerator = _section.GetEnumerator(); + Assert.IsNotNull(enumerator); + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Tests/IniTokenReaderTests.cs b/test/Shimakaze.Sdk.Ini.Tests/IniTokenReaderTests.cs new file mode 100644 index 00000000..355c449a --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Tests/IniTokenReaderTests.cs @@ -0,0 +1,31 @@ +namespace Shimakaze.Sdk.Ini.Tests; + +[TestClass()] +public class IniTokenReaderTests +{ + [TestMethod()] + public void Test() + { + const string ini = "[Section]\r\nKey=Value\r\n"; + Stack tokens = new(); + tokens.Push(IniTokenTools.EOF); + tokens.Push(IniTokenTools.LF); + tokens.Push(IniTokenTools.CR); + tokens.Push(IniTokenTools.Value("Value")); + tokens.Push(IniTokenTools.EQ); + tokens.Push(IniTokenTools.Value("Key")); + tokens.Push(IniTokenTools.LF); + tokens.Push(IniTokenTools.CR); + tokens.Push(IniTokenTools.EndBracket); + tokens.Push(IniTokenTools.Value("Section")); + tokens.Push(IniTokenTools.BeginBracket); + + using StringReader sr = new(ini); + using IniTokenReader reader = new(sr); + foreach (var item in reader) + { + Console.WriteLine(item); + Assert.AreEqual(tokens.Pop(), item); + } + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Tests/IniTokenToolsTests.cs b/test/Shimakaze.Sdk.Ini.Tests/IniTokenToolsTests.cs new file mode 100644 index 00000000..3df69bdc --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Tests/IniTokenToolsTests.cs @@ -0,0 +1,33 @@ +namespace Shimakaze.Sdk.Ini.Tests; + +[TestClass()] +public class IniTokenToolsTests +{ + [TestMethod()] + public void WhiteTest() + { + Assert.AreEqual(-1, IniTokenTools.EOF.Token); + Assert.AreEqual('\r', IniTokenTools.CR.Token); + Assert.AreEqual('\n', IniTokenTools.LF.Token); + Assert.AreEqual(' ', IniTokenTools.SPACE.Token); + Assert.AreEqual('\t', IniTokenTools.TAB.Token); + } + + [TestMethod()] + public void SignTest() + { + Assert.AreEqual(';', IniTokenTools.SEMI.Token); + Assert.AreEqual('=', IniTokenTools.EQ.Token); + Assert.AreEqual('[', IniTokenTools.BeginBracket.Token); + Assert.AreEqual(']', IniTokenTools.EndBracket.Token); + } + + [TestMethod()] + public void StringTest() + { + string value = Guid.NewGuid().ToString(); + var token = IniTokenTools.Value(value); + Assert.AreEqual(1, token.Token); + Assert.AreEqual(value, token.Value); + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Tests/IniTokenWriterTests.cs b/test/Shimakaze.Sdk.Ini.Tests/IniTokenWriterTests.cs new file mode 100644 index 00000000..bb403b52 --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Tests/IniTokenWriterTests.cs @@ -0,0 +1,21 @@ +namespace Shimakaze.Sdk.Ini.Tests; + +[TestClass()] +public class IniTokenWriterTests +{ + [TestMethod()] + public void WriteTest() + { + using MemoryStream ms = new(); + using StreamWriter sw = new(ms, leaveOpen: true); + using IniTokenWriter writer = new(sw); + writer.Write(IniTokenTools.BeginBracket); + writer.Write(IniTokenTools.Value("Section")); + writer.WriteLine(IniTokenTools.EndBracket); + writer.Flush(); + + ms.Seek(0, SeekOrigin.Begin); + using StreamReader sr = new(ms); + Assert.AreEqual("[Section]" + sw.NewLine, sr.ReadToEnd()); + } +} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Ini.Tests/Shimakaze.Sdk.Ini.Tests.csproj b/test/Shimakaze.Sdk.Ini.Tests/Shimakaze.Sdk.Ini.Tests.csproj new file mode 100644 index 00000000..61718a1f --- /dev/null +++ b/test/Shimakaze.Sdk.Ini.Tests/Shimakaze.Sdk.Ini.Tests.csproj @@ -0,0 +1,3 @@ + + + diff --git a/test/Shimakaze.Sdk.Tests/Assets/local mix database.dat b/test/Shimakaze.Sdk.Mix.Tests/Assets/local mix database.dat similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/local mix database.dat rename to test/Shimakaze.Sdk.Mix.Tests/Assets/local mix database.dat diff --git a/test/Shimakaze.Sdk.Mix.Tests/Assets/ra2md.csf b/test/Shimakaze.Sdk.Mix.Tests/Assets/ra2md.csf new file mode 100644 index 00000000..93c081b3 --- /dev/null +++ b/test/Shimakaze.Sdk.Mix.Tests/Assets/ra2md.csf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3382231d7eb3bd713bf3908ecdba175a1b2e20d9d80fa2702e9ab84c3a79deb +size 573269 diff --git a/test/Shimakaze.Sdk.Tests/Assets/test.mix b/test/Shimakaze.Sdk.Mix.Tests/Assets/test.mix similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/test.mix rename to test/Shimakaze.Sdk.Mix.Tests/Assets/test.mix diff --git a/test/Shimakaze.Sdk.Tests/Mix/IdCalculatersTest.cs b/test/Shimakaze.Sdk.Mix.Tests/IdCalculatersTest.cs similarity index 55% rename from test/Shimakaze.Sdk.Tests/Mix/IdCalculatersTest.cs rename to test/Shimakaze.Sdk.Mix.Tests/IdCalculatersTest.cs index cd4aeb8f..fc06fbdb 100644 --- a/test/Shimakaze.Sdk.Tests/Mix/IdCalculatersTest.cs +++ b/test/Shimakaze.Sdk.Mix.Tests/IdCalculatersTest.cs @@ -1,12 +1,10 @@ -using Shimakaze.Sdk.IO.Mix; - -namespace Shimakaze.Sdk.Mix; + +namespace Shimakaze.Sdk.Mix.Tests; [TestClass] public class IdCalculatersTest { private const string CsfFile = "ra2md.csf"; - private const uint Lxd = 913179935; private const string LxdFile = "local mix database.dat"; private const uint Ra2mdCsf = 3179499641; @@ -14,6 +12,12 @@ public class IdCalculatersTest public void TSIdCalculaterTest() { Assert.AreEqual(Ra2mdCsf, IdCalculaters.TSIdCalculater(CsfFile)); - Assert.AreEqual(Lxd, IdCalculaters.TSIdCalculater(LxdFile)); + Assert.AreEqual(Constants.LXD_TS_ID, IdCalculaters.TSIdCalculater(LxdFile)); + } + + [TestMethod] + public void TDIdCalculaterTest() + { + Assert.AreEqual(Constants.LXD_TD_ID, IdCalculaters.TDdCalculater(LxdFile)); } -} \ No newline at end of file +} diff --git a/test/Shimakaze.Sdk.Tests/Mix/MixBuilderTest.cs b/test/Shimakaze.Sdk.Mix.Tests/MixBuilderTest.cs similarity index 94% rename from test/Shimakaze.Sdk.Tests/Mix/MixBuilderTest.cs rename to test/Shimakaze.Sdk.Mix.Tests/MixBuilderTest.cs index f93a76f0..8f14bc95 100644 --- a/test/Shimakaze.Sdk.Tests/Mix/MixBuilderTest.cs +++ b/test/Shimakaze.Sdk.Mix.Tests/MixBuilderTest.cs @@ -1,6 +1,5 @@ -using Shimakaze.Sdk.IO.Mix; - -namespace Shimakaze.Sdk.Mix; + +namespace Shimakaze.Sdk.Mix.Tests; [TestClass] public class MixBuilderTest @@ -35,4 +34,4 @@ public async Task Test() await fs.FlushAsync().ConfigureAwait(false); } -} \ No newline at end of file +} diff --git a/test/Shimakaze.Sdk.Tests/Mix/MixEntryReaderTest.cs b/test/Shimakaze.Sdk.Mix.Tests/MixEntryReaderTest.cs similarity index 66% rename from test/Shimakaze.Sdk.Tests/Mix/MixEntryReaderTest.cs rename to test/Shimakaze.Sdk.Mix.Tests/MixEntryReaderTest.cs index 8edb307a..dcc77f0f 100644 --- a/test/Shimakaze.Sdk.Tests/Mix/MixEntryReaderTest.cs +++ b/test/Shimakaze.Sdk.Mix.Tests/MixEntryReaderTest.cs @@ -1,6 +1,5 @@ -using Shimakaze.Sdk.IO.Mix; - -namespace Shimakaze.Sdk.Mix; + +namespace Shimakaze.Sdk.Mix.Tests; [TestClass] public class MixEntryReaderTest @@ -13,15 +12,31 @@ public class MixEntryReaderTest [TestMethod] public async Task InitTestAsync() { - using MemoryStream ms = new(new byte[]{ - 0, 0, 0, 0, - 1, 0, - 0, 0, 0, 0, + using MemoryStream ms = new([ + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - }); + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); ms.Seek(0, SeekOrigin.Begin); using MixEntryReader reader = new(ms); await reader.ReadAsync(); @@ -32,11 +47,18 @@ public async Task NotSupportAsyncTest() { await Assert.ThrowsExceptionAsync(async () => { - await using MemoryStream ms = new(new byte[]{ - 255, 255, 255, 255, - 0, 0, - 0, 0, 0, 0, - }); + await using MemoryStream ms = new([ + 255, + 255, + 255, + 255, + 0, + 0, + 0, + 0, + 0, + 0, + ]); await using MixEntryReader reader = new(ms); reader.Init(); }); @@ -47,11 +69,18 @@ public void NotSupportTest() { Assert.ThrowsException(() => { - using MemoryStream ms = new(new byte[]{ - 255, 255, 255, 255, - 0, 0, - 0, 0, 0, 0, - }); + using MemoryStream ms = new([ + 255, + 255, + 255, + 255, + 0, + 0, + 0, + 0, + 0, + 0, + ]); using MixEntryReader reader = new(ms); reader.Init(); }); @@ -85,12 +114,5 @@ public async Task Test() fs.Seek(csf.Offset, SeekOrigin.Current); Assert.AreEqual(reader.BodyOffset + csf.Offset, fs.Position); - await using var ra2mdfs = File.OpenRead(Path.Combine(Assets, CsfFile)); - while (fs.Position < reader.BodyOffset + csf.Offset + csf.Size) - { - var _1 = fs.ReadByte(); - var _2 = ra2mdfs.ReadByte(); - Assert.AreEqual(_2, _1, $"At Position {fs.Position}"); - } } -} \ No newline at end of file +} diff --git a/test/Shimakaze.Sdk.Tests/Mix/MixEntryWriterTest.cs b/test/Shimakaze.Sdk.Mix.Tests/MixEntryWriterTest.cs similarity index 96% rename from test/Shimakaze.Sdk.Tests/Mix/MixEntryWriterTest.cs rename to test/Shimakaze.Sdk.Mix.Tests/MixEntryWriterTest.cs index 4240ee70..6aec2435 100644 --- a/test/Shimakaze.Sdk.Tests/Mix/MixEntryWriterTest.cs +++ b/test/Shimakaze.Sdk.Mix.Tests/MixEntryWriterTest.cs @@ -1,6 +1,5 @@ -using Shimakaze.Sdk.IO.Mix; - -namespace Shimakaze.Sdk.Mix; + +namespace Shimakaze.Sdk.Mix.Tests; [TestClass] public class MixEntryWriterTest @@ -73,7 +72,7 @@ public async Task Test() } } -internal sealed class NonSeekableStream : Stream +file sealed class NonSeekableStream : Stream { public override bool CanRead => throw new NotImplementedException(); diff --git a/test/Shimakaze.Sdk.Mix.Tests/Shimakaze.Sdk.Mix.Tests.csproj b/test/Shimakaze.Sdk.Mix.Tests/Shimakaze.Sdk.Mix.Tests.csproj new file mode 100644 index 00000000..61718a1f --- /dev/null +++ b/test/Shimakaze.Sdk.Mix.Tests/Shimakaze.Sdk.Mix.Tests.csproj @@ -0,0 +1,3 @@ + + + diff --git a/test/Shimakaze.Sdk.Tests/Assets/unittem.pal b/test/Shimakaze.Sdk.Pal.Tests/Assets/unittem.pal similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/unittem.pal rename to test/Shimakaze.Sdk.Pal.Tests/Assets/unittem.pal diff --git a/test/Shimakaze.Sdk.Tests/Pal/ColorTest.cs b/test/Shimakaze.Sdk.Pal.Tests/ColorTest.cs similarity index 100% rename from test/Shimakaze.Sdk.Tests/Pal/ColorTest.cs rename to test/Shimakaze.Sdk.Pal.Tests/ColorTest.cs diff --git a/test/Shimakaze.Sdk.Tests/IO/Pal/PalReaderTest.cs b/test/Shimakaze.Sdk.Pal.Tests/PalReaderTest.cs similarity index 91% rename from test/Shimakaze.Sdk.Tests/IO/Pal/PalReaderTest.cs rename to test/Shimakaze.Sdk.Pal.Tests/PalReaderTest.cs index af5f5c5f..a95071c2 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Pal/PalReaderTest.cs +++ b/test/Shimakaze.Sdk.Pal.Tests/PalReaderTest.cs @@ -1,4 +1,4 @@ -namespace Shimakaze.Sdk.IO.Pal.Tests; +namespace Shimakaze.Sdk.Pal.Tests; [TestClass] public sealed class PalReaderTest diff --git a/test/Shimakaze.Sdk.Tests/IO/Pal/PalWriterTest.cs b/test/Shimakaze.Sdk.Pal.Tests/PalWriterTest.cs similarity index 68% rename from test/Shimakaze.Sdk.Tests/IO/Pal/PalWriterTest.cs rename to test/Shimakaze.Sdk.Pal.Tests/PalWriterTest.cs index 13e7ab6f..5d54802c 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Pal/PalWriterTest.cs +++ b/test/Shimakaze.Sdk.Pal.Tests/PalWriterTest.cs @@ -1,8 +1,9 @@ +using System.Globalization; using System.Security.Cryptography; using Shimakaze.Sdk.Pal; -namespace Shimakaze.Sdk.IO.Pal.Tests; +namespace Shimakaze.Sdk.Pal.Tests; [TestClass] public sealed class PalWriterTest @@ -11,7 +12,7 @@ public sealed class PalWriterTest private const string InputFile = "unittem.pal"; private const string OutputFile = "unittem.pal"; private const string OutputPath = "Out"; - private Palette _pal; + private Palette _pal = default!; [TestInitialize] public async Task StartupAsync() @@ -31,9 +32,9 @@ public async Task WriteTestAsync() using (PaletteWriter writer = new(stream)) await writer.WriteAsync(_pal); - var a = BitConverter.ToString(MD5.HashData(File.ReadAllBytes(Path.Combine(Assets, InputFile)))); - var b = BitConverter.ToString(MD5.HashData(File.ReadAllBytes(Path.Combine(OutputPath, OutputFile)))); + var a = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(Path.Combine(Assets, InputFile)))); + var b = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(Path.Combine(OutputPath, OutputFile)))); - Assert.AreEqual(a, b, true); + Assert.AreEqual(a, b, true, CultureInfo.InvariantCulture); } } \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Pal.Tests/Shimakaze.Sdk.Pal.Tests.csproj b/test/Shimakaze.Sdk.Pal.Tests/Shimakaze.Sdk.Pal.Tests.csproj new file mode 100644 index 00000000..6b512ec9 --- /dev/null +++ b/test/Shimakaze.Sdk.Pal.Tests/Shimakaze.Sdk.Pal.Tests.csproj @@ -0,0 +1 @@ + diff --git a/test/Shimakaze.Sdk.Tests/Csf/Json/ThrowHelperTests.cs b/test/Shimakaze.Sdk.Tests/Csf/Json/ThrowHelperTests.cs deleted file mode 100644 index b58ab440..00000000 --- a/test/Shimakaze.Sdk.Tests/Csf/Json/ThrowHelperTests.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Text.Json; - -namespace Shimakaze.Sdk.Csf.Json; - -[TestClass] -public class ThrowHelperTests -{ - [TestMethod] - public void ThrowNotSupportTokenTest() - { - try - { - JsonTokenType.Null.ThrowNotSupportToken(); - } - catch (JsonException) - { - } - } - - [TestMethod] - public void ThrowNotSupportValueTest() - { - try - { - JsonTokenType.Null.ThrowNotSupportValue(); - } - catch (JsonException) - { - } - } - - [TestMethod] - public void ThrowWhenFalseTest() - { - try - { - false.ThrowWhenFalse(); - } - catch (JsonException) - { - } - } - - [TestMethod] - public void ThrowWhenNotTokenTest() - { - try - { - JsonTokenType.Null.ThrowWhenNotToken(JsonTokenType.True); - } - catch (JsonException) - { - } - } - - [TestMethod] - public void ThrowWhenNullTest() - { - try - { - ((object?)null).ThrowWhenNull(); - } - catch (ArgumentNullException) - { - } - } -} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Tests/IO/Ini/IniMergerTests.cs b/test/Shimakaze.Sdk.Tests/IO/Ini/IniMergerTests.cs deleted file mode 100644 index 93557338..00000000 --- a/test/Shimakaze.Sdk.Tests/IO/Ini/IniMergerTests.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System.Collections; - -using Shimakaze.Sdk.Ini; - -namespace Shimakaze.Sdk.IO.Ini; - -[TestClass] -public class IniMergerTests -{ - private const string Assets = "Assets"; - private const string InputFile = "normal.ini"; - private const string OutputFile = "MergeTest.ini"; - private const string OutputFileAsync = "MergeAsyncTest.ini"; - private const string OutputPath = "Out"; - - [TestMethod] - public async Task BuildAndWriteToAsyncTest() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using Stream output = File.Create(Path.Combine(OutputPath, OutputFileAsync)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - await merger.BuildAndWriteToAsync(output); - await output.FlushAsync(); - } - - [TestMethod] - public async Task BuildAndWriteToTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using Stream output = File.Create(Path.Combine(OutputPath, OutputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - await merger.BuildAndWriteToAsync(output); - output.Flush(); - } - - [TestMethod] - public async Task BuildTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - IniDocument newIni = merger.Build(); - Assert.IsFalse(newIni.Any(i => !ini.Contains(i))); - } - - [TestMethod] - public async Task ContainsTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - Assert.IsTrue(merger.Contains(ini.First())); - } - - [TestMethod] - public async Task CopyToTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - IniSection[] arr = new IniSection[ini.Count]; - merger.CopyTo(arr, 0); - Assert.IsTrue(merger.SetEquals(arr)); - - arr = new IniSection[ini.Count + 3]; - merger.CopyTo(arr, 3); - Assert.IsTrue(merger.SetEquals(arr.Skip(3))); - - arr = new IniSection[ini.Count + 6]; - merger.CopyTo(arr, 3); - Assert.IsTrue(merger.SetEquals(arr.Skip(3).Take(ini.Count))); - } - - [TestMethod] - public async Task ExceptWithTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - merger.ExceptWith(ini.Take(3)); - Assert.IsTrue(merger.SetEquals(ini.Skip(3))); - } - - [TestMethod] - public async Task GetEnumeratorTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - using IEnumerator enumerator = merger.GetEnumerator(); - Assert.IsNotNull(enumerator); - Assert.IsInstanceOfType>(enumerator); - Assert.IsInstanceOfType(enumerator); - } - - [TestMethod] - public async Task IntersectWithTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - merger.IntersectWith(ini.Take(3)); - Assert.IsTrue(merger.SetEquals(ini.Take(3))); - } - - [TestMethod] - public async Task OverlapsTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - Assert.IsTrue(merger.Overlaps(ini)); - Assert.IsTrue(merger.Overlaps(ini.Skip(1))); - - merger.Remove(ini.Last()); - Assert.IsTrue(merger.Overlaps(ini)); - Assert.IsTrue(merger.Overlaps(ini.Skip(1))); - } - - [TestMethod] - public async Task RemoveTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - merger.Remove(ini.First()); - Assert.IsFalse(merger.SetEquals(ini)); - Assert.IsTrue(merger.SetEquals(ini.Skip(1))); - } - - [TestMethod] - public async Task SetEqualsTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - Assert.IsTrue(merger.SetEquals(ini)); - Assert.IsFalse(merger.SetEquals(ini.Skip(1))); - } - - [TestInitialize] - public void Startup() - { - Directory.CreateDirectory(OutputPath); - } - - [TestMethod] - public async Task SubsetTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - Assert.IsTrue(merger.IsSubsetOf(ini)); - Assert.IsTrue(merger.IsSupersetOf(ini)); - - Assert.IsFalse(merger.IsProperSubsetOf(ini)); - Assert.IsFalse(merger.IsProperSupersetOf(ini)); - - merger.Remove(ini.First()); - Assert.IsTrue(merger.IsProperSubsetOf(ini)); - Assert.IsTrue(merger.IsProperSupersetOf(ini.Skip(1).Take(3))); - } - - [TestMethod] - public async Task SymmetricExceptWithTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - merger.Remove(ini.Last()); - merger.SymmetricExceptWith(ini.Skip(1)); - Assert.IsTrue(merger.SetEquals(new[] - { - ini.Last(), - ini.First(), - })); - } - - [TestMethod] - public async Task UnionWithTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - IniMerger merger = new(); - merger.UnionWith(ini); - Assert.AreNotEqual(0, merger.Count); - Assert.IsFalse(ini.Any(i => !merger.Contains(i))); - - merger.Clear(); - Assert.AreEqual(0, merger.Count); - } -} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Tests/IO/Ini/Serialization/IniSerializerTests.cs b/test/Shimakaze.Sdk.Tests/IO/Ini/Serialization/IniSerializerTests.cs deleted file mode 100644 index deffe27b..00000000 --- a/test/Shimakaze.Sdk.Tests/IO/Ini/Serialization/IniSerializerTests.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Shimakaze.Sdk.Ini; - -namespace Shimakaze.Sdk.IO.Ini.Serialization; - -[TestClass] -public class IniWriterTests -{ - private const string Assets = "Assets"; - private const string InputFile = "normal.ini"; - private const string OutputFile = "SerializeTest.ini"; - private const string OutputPath = "Out"; - private IniDocument? _document; - - [TestMethod] - public async Task DeserializeAsyncTest() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - Assert.AreNotEqual(0, ini.Count); - Assert.AreNotEqual(0, ini.First().Count); - } - - [TestMethod] - public async Task DeserializeTestAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - IniDocument ini = await deserializer.ReadAsync(); - - Assert.AreNotEqual(0, ini.Count); - Assert.AreNotEqual(0, ini.First().Count); - } - - [TestMethod] - public async Task SerializeAsyncTest() - { - Assert.IsNotNull(_document); - - string path = Path.Combine(OutputPath, OutputFile); - await using var stream = File.Create(path); - using IniWriter serializer = new(stream); - await serializer.WriteAsync(_document); - } - - [TestInitialize] - public async Task StartupAsync() - { - using var stream = File.OpenRead(Path.Combine(Assets, InputFile)); - using IniReader deserializer = new(stream); - _document = await deserializer.ReadAsync(); - - Directory.CreateDirectory(OutputPath); - } -} \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Tests/Shimakaze.Sdk.Tests.csproj b/test/Shimakaze.Sdk.Tests/Shimakaze.Sdk.Tests.csproj deleted file mode 100644 index d14c9f81..00000000 --- a/test/Shimakaze.Sdk.Tests/Shimakaze.Sdk.Tests.csproj +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Tests/Assets/voxels.vpl b/test/Shimakaze.Sdk.Vpl.Tests/Assets/voxels.vpl similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/voxels.vpl rename to test/Shimakaze.Sdk.Vpl.Tests/Assets/voxels.vpl diff --git a/test/Shimakaze.Sdk.Vpl.Tests/Shimakaze.Sdk.Vpl.Tests.csproj b/test/Shimakaze.Sdk.Vpl.Tests/Shimakaze.Sdk.Vpl.Tests.csproj new file mode 100644 index 00000000..6b512ec9 --- /dev/null +++ b/test/Shimakaze.Sdk.Vpl.Tests/Shimakaze.Sdk.Vpl.Tests.csproj @@ -0,0 +1 @@ + diff --git a/test/Shimakaze.Sdk.Tests/IO/Vpl/VoxelPaletteReaderTest.cs b/test/Shimakaze.Sdk.Vpl.Tests/VoxelPaletteReaderTest.cs similarity index 94% rename from test/Shimakaze.Sdk.Tests/IO/Vpl/VoxelPaletteReaderTest.cs rename to test/Shimakaze.Sdk.Vpl.Tests/VoxelPaletteReaderTest.cs index 21ea879c..fc80513d 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Vpl/VoxelPaletteReaderTest.cs +++ b/test/Shimakaze.Sdk.Vpl.Tests/VoxelPaletteReaderTest.cs @@ -1,4 +1,4 @@ -namespace Shimakaze.Sdk.IO.Vpl.Tests; +namespace Shimakaze.Sdk.Vpl.Tests; [TestClass] public sealed class VoxelPaletteReaderTest diff --git a/test/Shimakaze.Sdk.Tests/IO/Vpl/VoxelPaletteWriterTest.cs b/test/Shimakaze.Sdk.Vpl.Tests/VoxelPaletteWriterTest.cs similarity index 67% rename from test/Shimakaze.Sdk.Tests/IO/Vpl/VoxelPaletteWriterTest.cs rename to test/Shimakaze.Sdk.Vpl.Tests/VoxelPaletteWriterTest.cs index 66978fde..4c8d9975 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Vpl/VoxelPaletteWriterTest.cs +++ b/test/Shimakaze.Sdk.Vpl.Tests/VoxelPaletteWriterTest.cs @@ -1,8 +1,7 @@ +using System.Globalization; using System.Security.Cryptography; -using Shimakaze.Sdk.Vpl; - -namespace Shimakaze.Sdk.IO.Vpl.Tests; +namespace Shimakaze.Sdk.Vpl.Tests; [TestClass] public sealed class VoxelPaletteWriterTest @@ -11,7 +10,7 @@ public sealed class VoxelPaletteWriterTest private const string InputFile = "voxels.vpl"; private const string OutputFile = "voxels.vpl"; private const string OutputPath = "Out"; - private VoxelPalette _vpl; + private VoxelPalette _vpl = default!; [TestInitialize] public async Task StartupAsync() @@ -31,9 +30,9 @@ public async Task WriteTestAsync() using (VoxelPaletteWriter writer = new(stream)) await writer.WriteAsync(_vpl); - var a = BitConverter.ToString(MD5.HashData(File.ReadAllBytes(Path.Combine(Assets, InputFile)))); - var b = BitConverter.ToString(MD5.HashData(File.ReadAllBytes(Path.Combine(OutputPath, OutputFile)))); + var a = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(Path.Combine(Assets, InputFile)))); + var b = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(Path.Combine(OutputPath, OutputFile)))); - Assert.AreEqual(a, b, true); + Assert.AreEqual(a, b, true, CultureInfo.InvariantCulture); } } \ No newline at end of file diff --git a/test/Shimakaze.Sdk.Tests/Assets/jeep.vxl b/test/Shimakaze.Sdk.Vxl.Tests/Assets/jeep.vxl similarity index 100% rename from test/Shimakaze.Sdk.Tests/Assets/jeep.vxl rename to test/Shimakaze.Sdk.Vxl.Tests/Assets/jeep.vxl diff --git a/test/Shimakaze.Sdk.Vxl.Tests/Shimakaze.Sdk.Vxl.Tests.csproj b/test/Shimakaze.Sdk.Vxl.Tests/Shimakaze.Sdk.Vxl.Tests.csproj new file mode 100644 index 00000000..6b512ec9 --- /dev/null +++ b/test/Shimakaze.Sdk.Vxl.Tests/Shimakaze.Sdk.Vxl.Tests.csproj @@ -0,0 +1 @@ + diff --git a/test/Shimakaze.Sdk.Tests/IO/Vxl/VoxelReaderTest.cs b/test/Shimakaze.Sdk.Vxl.Tests/VoxelReaderTest.cs similarity index 90% rename from test/Shimakaze.Sdk.Tests/IO/Vxl/VoxelReaderTest.cs rename to test/Shimakaze.Sdk.Vxl.Tests/VoxelReaderTest.cs index 565942d2..1172b099 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Vxl/VoxelReaderTest.cs +++ b/test/Shimakaze.Sdk.Vxl.Tests/VoxelReaderTest.cs @@ -1,4 +1,4 @@ -namespace Shimakaze.Sdk.IO.Vxl.Tests; +namespace Shimakaze.Sdk.Vxl.Tests; [TestClass] public sealed class VoxelReaderTest diff --git a/test/Shimakaze.Sdk.Tests/IO/Vxl/VoxelWriterTest.cs b/test/Shimakaze.Sdk.Vxl.Tests/VoxelWriterTest.cs similarity index 67% rename from test/Shimakaze.Sdk.Tests/IO/Vxl/VoxelWriterTest.cs rename to test/Shimakaze.Sdk.Vxl.Tests/VoxelWriterTest.cs index f7792859..b9bab6e5 100644 --- a/test/Shimakaze.Sdk.Tests/IO/Vxl/VoxelWriterTest.cs +++ b/test/Shimakaze.Sdk.Vxl.Tests/VoxelWriterTest.cs @@ -1,8 +1,7 @@ +using System.Globalization; using System.Security.Cryptography; -using Shimakaze.Sdk.Vxl; - -namespace Shimakaze.Sdk.IO.Vxl.Tests; +namespace Shimakaze.Sdk.Vxl.Tests; [TestClass] public sealed class VoxelWriterTest @@ -11,7 +10,7 @@ public sealed class VoxelWriterTest private const string InputFile = "jeep.vxl"; private const string OutputFile = "jeep.vxl"; private const string OutputPath = "Out"; - private VXLFile _vxl; + private VXLFile _vxl = default!; [TestInitialize] public async Task StartupAsync() @@ -31,9 +30,9 @@ public async Task WriteTestAsync() using (VoxelWriter writer = new(stream)) await writer.WriteAsync(_vxl); - var a = BitConverter.ToString(MD5.HashData(File.ReadAllBytes(Path.Combine(Assets, InputFile)))); - var b = BitConverter.ToString(MD5.HashData(File.ReadAllBytes(Path.Combine(OutputPath, OutputFile)))); + var a = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(Path.Combine(Assets, InputFile)))); + var b = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(Path.Combine(OutputPath, OutputFile)))); - Assert.AreEqual(a, b, true); + Assert.AreEqual(a, b, true, CultureInfo.InvariantCulture); } } \ No newline at end of file