forked from NtsFranz/Spark
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathReplayFileReader.cs
199 lines (169 loc) · 4.92 KB
/
ReplayFileReader.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Threading.Tasks;
using ButterReplays;
using EchoVRAPI;
namespace Spark
{
public class ReplayFile
{
public int nframes;
public string filename;
public List<string> rawFrames;
public List<Frame> frames { private get; set; }
/// <summary>
/// Gets or converts the requested frame.
/// May return null if the frame can't be converted.
/// </summary>
public Frame GetFrame(int index)
{
if (frames[index] != null) return frames[index];
// repeat because maybe the requested frame needs to be discarded.
while (rawFrames.Count > 0)
{
Frame newFrame = ReplayFileReader.FromEchoReplayString(rawFrames[index]);
if (newFrame != null)
{
frames[index] = newFrame;
return frames[index];
}
Logger.LogRow(Logger.LogType.Error, $"Discarded frame {index}");
frames.RemoveAt(index);
rawFrames.RemoveAt(index);
nframes--;
}
Logger.LogRow(Logger.LogType.Error, "File contains no valid arena frames.");
return null;
}
}
internal class ReplayFileReader
{
public float fileReadProgress = 0;
public ReplayFile replayFile;
public object replayFileLock = new object();
/// <summary>
/// Part of the process for reading the file
/// </summary>
/// <param name="replayFilePath">The full filepath of the replay file</param>
/// <param name="processFrames"></param>
public async Task<ReplayFile> LoadFileAsync(string replayFilePath = "", bool processFrames = false)
{
if (string.IsNullOrEmpty(replayFilePath)) return null;
Logger.LogRow(Logger.LogType.Info, "Reading file: " + replayFilePath);
if (replayFilePath.EndsWith(".butter"))
{
List<Frame> butterFile = ButterFile.FromBytes(await File.ReadAllBytesAsync(replayFilePath));
return new ReplayFile
{
filename = replayFilePath,
nframes = butterFile.Count,
frames = butterFile
};
}
else
{
StreamReader reader = new StreamReader(replayFilePath);
Thread loadThread = new Thread(() => ReadReplayFile(reader, replayFilePath));
loadThread.Start();
while (loadThread.IsAlive)
{
// maybe put a progress bar here
await Task.Delay(10);
}
//if (processFrames)
//{
// Thread processTemporalDataThread = new Thread(() => ProcessAllTemporalData(loadedDemo));
// processTemporalDataThread.Start();
//}
return replayFile;
}
}
/// <summary>
/// Actually reads the replay file into memory
/// </summary>
private void ReadReplayFile(StreamReader fileReader, string filename)
{
using (fileReader = OpenOrExtract(fileReader))
{
fileReadProgress = 0;
List<string> allLines = new List<string>();
do
{
allLines.Add(fileReader.ReadLine());
fileReadProgress += .0001f;
fileReadProgress %= 1;
} while (!fileReader.EndOfStream);
//string fileData = fileReader.ReadToEnd();
//List<string> allLines = fileData.LowMemSplit("\n");
ReplayFile game = new ReplayFile
{
rawFrames = allLines,
nframes = allLines.Count,
filename = filename,
frames = new List<Frame>(new Frame[allLines.Count])
};
lock (replayFileLock)
{
replayFile = game;
}
}
}
private static StreamReader OpenOrExtract(StreamReader reader)
{
char[] buffer = new char[2];
reader.Read(buffer, 0, buffer.Length);
reader.DiscardBufferedData();
reader.BaseStream.Seek(0, SeekOrigin.Begin);
if (buffer[0] != 'P' || buffer[1] != 'K') return reader;
ZipArchive archive = new ZipArchive(reader.BaseStream);
StreamReader ret = new StreamReader(archive.Entries[0].Open());
//reader.Close();
return ret;
}
public static Frame FromEchoReplayString(string line)
{
if (!string.IsNullOrEmpty(line))
{
string[] splitJSON = line.Split('\t');
string onlyJSON, onlyTime;
if (splitJSON.Length == 2)
{
onlyJSON = splitJSON[1];
onlyTime = splitJSON[0];
}
else
{
Logger.LogRow(Logger.LogType.Error, "Row doesn't include both a time and API JSON");
return null;
}
DateTime frameTime = DateTime.Parse(onlyTime);
// if this is actually valid arena data
if (onlyJSON.Length > 800)
{
return FromJSON(frameTime, onlyJSON);
}
Logger.LogRow(Logger.LogType.Error, "Row is not arena data.");
return null;
}
Logger.LogRow(Logger.LogType.Error, "String is empty");
return null;
}
/// <summary>
/// Creates a frame from json and a timestamp
/// </summary>
/// <param name="time">The time the frame was recorded</param>
/// <param name="json">The json for the frame</param>
/// <returns>A Frame object</returns>
private static Frame FromJSON(DateTime time, string json)
{
Frame frame = JsonConvert.DeserializeObject<Frame>(json);
if (frame == null) return null;
frame.recorded_time = time;
return frame;
}
}
}