167 lines
6.8 KiB
C#
167 lines
6.8 KiB
C#
using CommunityToolkit.HighPerformance;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace BallanceTasEditor.Backend {
|
|
public static class TasStorage {
|
|
|
|
/// <summary>
|
|
/// Initialize given TAS sequence with given count frame which has given FPS.
|
|
/// </summary>
|
|
/// <param name="seq">The TAS sequence to initialize.</param>
|
|
/// <param name="count">The count of frame.</param>
|
|
/// <param name="fps">The FPS of frame.</param>
|
|
public static void Init(ITasSequence seq, int count, uint fps) {
|
|
var frame = TasFrame.FromFps(fps);
|
|
var iter = Enumerable.Range(0, count).Select((_) => frame.Clone());
|
|
var exactSizeIter = new ExactSizeEnumerableAdapter<TasFrame>(iter, count);
|
|
seq.Insert(seq.GetCount(), exactSizeIter);
|
|
}
|
|
|
|
internal const int SIZEOF_I32 = sizeof(int);
|
|
internal const int SIZEOF_F32 = sizeof(float);
|
|
internal const int SIZEOF_U32 = sizeof(uint);
|
|
internal const int SIZEOF_RAW_TAS_FRAME = SIZEOF_F32 + SIZEOF_U32;
|
|
|
|
/// <summary>
|
|
/// Save given TAS sequence into given file path.
|
|
/// </summary>
|
|
/// <param name="filepath">The path to file for saving.</param>
|
|
/// <param name="seq">The TAS sequence to save.</param>
|
|
/// <exception cref="Exception">Any exception occurs when saving.</exception>
|
|
public static void Save(string filepath, ITasSequence seq) {
|
|
using (var fs = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)) {
|
|
Save(fs, seq);
|
|
fs.Close();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save given TAS sequence into given file stream.
|
|
/// </summary>
|
|
/// <param name="fs">The file stream for saving.</param>
|
|
/// <param name="seq">The TAS sequence to save.</param>
|
|
/// <exception cref="Exception">Any exception occurs when saving.</exception>
|
|
public static void Save(Stream fs, ITasSequence seq) {
|
|
var totalByte = seq.GetCount() * SIZEOF_RAW_TAS_FRAME;
|
|
fs.Write(BitConverter.GetBytes(totalByte), 0, SIZEOF_I32);
|
|
|
|
using (var zo = new System.IO.Compression.ZLibStream(fs, System.IO.Compression.CompressionLevel.SmallestSize, true)) {
|
|
foreach (var item in seq) {
|
|
var rawItem = item.ToRaw();
|
|
zo.Write(BitConverter.GetBytes(rawItem.TimeDelta), 0, SIZEOF_F32);
|
|
zo.Write(BitConverter.GetBytes(rawItem.KeyFlags), 0, SIZEOF_U32);
|
|
}
|
|
zo.Close();
|
|
}
|
|
|
|
//var zo = new zlib.ZOutputStream(file, 9);
|
|
//var node = mem.First;
|
|
//while (node != null) {
|
|
// zo.Write(BitConverter.GetBytes(node.Value.deltaTime), 0, 4);
|
|
// zo.Write(BitConverter.GetBytes(node.Value.keystates), 0, 4);
|
|
// node = node.Next;
|
|
//}
|
|
//zo.finish();
|
|
//zo.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load TAS sequence from given file path into given sequence.
|
|
/// </summary>
|
|
/// <param name="filepath">The path to file for loading.</param>
|
|
/// <param name="seq">The TAS sequence to load.</param>
|
|
/// <exception cref="Exception">Any exception occurs when loading.</exception>
|
|
public static void Load(string filepath, ITasSequence seq) {
|
|
using (var fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
|
|
Load(fs, seq);
|
|
fs.Close();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load TAS sequence from given file stream into given sequence.
|
|
/// </summary>
|
|
/// <param name="fs">The file stream for loading.</param>
|
|
/// <param name="seq">The TAS sequence to load.</param>
|
|
/// <exception cref="Exception">Any exception occurs when loading.</exception>
|
|
public static void Load(Stream fs, ITasSequence seq) {
|
|
// Read total bytes
|
|
var lenCache = new byte[SIZEOF_I32];
|
|
fs.Read(lenCache, 0, 4);
|
|
int expectedLength = BitConverter.ToInt32(lenCache, 0);
|
|
// Check length and compute count
|
|
int expectedCount = Math.DivRem(expectedLength, SIZEOF_RAW_TAS_FRAME, out var remainder);
|
|
ArgumentOutOfRangeException.ThrowIfNotEqual(remainder, 0);
|
|
|
|
using (var mem = new MemoryStream()) {
|
|
using (var zo = new System.IO.Compression.ZLibStream(fs, System.IO.Compression.CompressionMode.Decompress, true)) {
|
|
zo.CopyTo(mem);
|
|
zo.Close();
|
|
}
|
|
|
|
var memWrapper = new EnumerableMemoryStream(mem, expectedCount);
|
|
seq.Clear();
|
|
seq.Insert(0, memWrapper);
|
|
|
|
mem.Close();
|
|
}
|
|
|
|
//mem.Seek(0, SeekOrigin.Begin);
|
|
//for (long i = 0; i < expectedCount; i++) {
|
|
// ls.AddLast(new FrameData(mem));
|
|
//}
|
|
//mem.Close();
|
|
//zo.Close();
|
|
|
|
//var zo = new zlib.ZOutputStream(mem);
|
|
//CopyStream(file, zo);
|
|
//zo.finish();
|
|
|
|
//mem.Seek(0, SeekOrigin.Begin);
|
|
//for (long i = 0; i < expectedCount; i++) {
|
|
// ls.AddLast(new FrameData(mem));
|
|
//}
|
|
//mem.Close();
|
|
//zo.Close();
|
|
}
|
|
|
|
private sealed class EnumerableMemoryStream : IExactSizeEnumerable<TasFrame> {
|
|
public EnumerableMemoryStream(MemoryStream mem, int frameCnt) {
|
|
m_MemoryStream = mem;
|
|
m_FrameCount = frameCnt;
|
|
}
|
|
|
|
private MemoryStream m_MemoryStream;
|
|
private int m_FrameCount;
|
|
|
|
public IEnumerator<TasFrame> GetEnumerator() {
|
|
// Get the view of underlying array
|
|
var memory = m_MemoryStream.GetBuffer().AsMemory();
|
|
// Get the span which actually storing the data,
|
|
// because the length of buffer is equal or longer than the length of all stored data.
|
|
var exactMemory = memory.Slice(0, m_FrameCount * SIZEOF_RAW_TAS_FRAME);
|
|
// Convert to raw frame type.
|
|
var frameMemory = exactMemory.Cast<byte, RawTasFrame>();
|
|
// Map it and return.
|
|
return MemoryMarshal.ToEnumerable<RawTasFrame>(frameMemory).Select((rawFrame) => TasFrame.FromRaw(rawFrame)).GetEnumerator();
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() {
|
|
return GetEnumerator();
|
|
}
|
|
|
|
public int GetCount() {
|
|
return m_FrameCount;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|