BizHawk/BizHawk.Client.Common/movie/bkm/BkmMovie.IO.cs

282 lines
6.0 KiB
C#

using System;
using System.IO;
using System.Text;
using BizHawk.Common;
namespace BizHawk.Client.Common
{
public partial class BkmMovie
{
private int _preloadFramecount; // Not a a reliable number, used for preloading (when no log has yet been loaded), this is only for quick stat compilation for dialogs such as play movie
public void SaveAs(string path)
{
Filename = path;
if (!Loaded)
{
return;
}
var directoryInfo = new FileInfo(Filename).Directory;
if (directoryInfo != null)
{
Directory.CreateDirectory(directoryInfo.FullName);
}
Write(Filename);
}
public void Save()
{
if (!Loaded || string.IsNullOrWhiteSpace(Filename))
{
return;
}
SaveAs(Filename);
_changes = false;
}
public void SaveBackup()
{
if (!Loaded || string.IsNullOrWhiteSpace(Filename))
{
return;
}
var backupName = Filename;
backupName = backupName.Insert(Filename.LastIndexOf("."), $".{DateTime.Now:yyyy-MM-dd HH.mm.ss}");
backupName = Path.Combine(Global.Config.PathEntries["Global", "Movie backups"].Path, Path.GetFileName(backupName));
var directoryInfo = new FileInfo(backupName).Directory;
if (directoryInfo != null)
{
Directory.CreateDirectory(directoryInfo.FullName);
}
Write(backupName);
}
public bool Load(bool preload)
{
var file = new FileInfo(Filename);
if (file.Exists == false)
{
Loaded = false;
return false;
}
Header.Clear();
_log.Clear();
using (var sr = file.OpenText())
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (line == "")
{
continue;
}
if (line.Contains("LoopOffset"))
{
try
{
_loopOffset = int.Parse(line.Split(new[] { ' ' }, 2)[1]);
}
catch (Exception)
{
continue;
}
}
else if (Header.ParseLineFromFile(line))
{
continue;
}
else if (line.StartsWith("|"))
{
_log.Add(line);
}
else
{
Header.Comments.Add(line);
}
}
}
if (Header.SavestateBinaryBase64Blob != null)
{
BinarySavestate = Convert.FromBase64String(Header.SavestateBinaryBase64Blob);
}
Loaded = true;
_changes = false;
return true;
}
/// <summary>
/// Load Header information only for displaying file information in dialogs such as play movie
/// TODO - consider not loading the SavestateBinaryBase64Blob key?
/// </summary>
public bool PreLoadHeaderAndLength(HawkFile hawkFile)
{
Loaded = false;
var file = new FileInfo(hawkFile.CanonicalFullPath);
if (file.Exists == false)
{
return false;
}
Header.Clear();
_log.Clear();
var origStreamPosn = hawkFile.GetStream().Position;
hawkFile.GetStream().Position = 0; // Reset to start
// No using block because we're sharing the stream and need to give it back undisposed.
var sr = new StreamReader(hawkFile.GetStream());
for (;;)
{
// read to first space (key/value delimeter), or pipe, or EOF
int first = sr.Read();
if (first == -1)
{
break;
} // EOF
if (first == '|') // pipe: begin input log
{
// NOTE - this code is a bit convoluted due to its predating the basic outline of the parser which was upgraded in may 2014
var line = '|' + sr.ReadLine();
// how many bytes are left, total?
long remain = sr.BaseStream.Length - sr.BaseStream.Position;
// try to find out whether we use \r\n or \n
// but only look for 1K characters.
bool usesR = false;
for (int i = 0; i < 1024; i++)
{
int c = sr.Read();
if (c == -1)
{
break;
}
if (c == '\r')
{
usesR = true;
break;
}
if (c == '\n')
{
break;
}
}
int lineLen = line.Length + 1; // account for \n
if (usesR)
{
lineLen++; // account for \r
}
_preloadFramecount = (int)(remain / lineLen); // length is remaining bytes / length per line
_preloadFramecount++; // account for the current line
break;
}
else
{
// a header line. finish reading key token, to make sure it isn't one of the FORBIDDEN keys
var sbLine = new StringBuilder();
sbLine.Append((char)first);
for (;;)
{
int c = sr.Read();
if (c == -1 || c == '\n' || c == ' ')
{
break;
}
sbLine.Append((char)c);
}
var line = sbLine.ToString();
// ignore these suckers, theyre way too big for preloading. seriously, we will get out of memory errors.
var skip = line == HeaderKeys.SAVESTATEBINARYBASE64BLOB;
if (skip)
{
// skip remainder of the line
sr.DiscardBufferedData();
var stream = sr.BaseStream;
for (;;)
{
int c = stream.ReadByte();
if (c == -1 || c == '\n')
{
break;
}
}
// proceed to next line
continue;
}
var remainder = sr.ReadLine();
sbLine.Append(' ');
sbLine.Append(remainder);
line = sbLine.ToString();
if (string.IsNullOrWhiteSpace(line) || Header.ParseLineFromFile(line))
{
continue;
}
Header.Comments.Add(line);
}
}
hawkFile.GetStream().Position = origStreamPosn;
return true;
}
private void Write(string fn)
{
Header.SavestateBinaryBase64Blob = BinarySavestate != null
? Convert.ToBase64String(BinarySavestate)
: null;
using (var fs = new FileStream(fn, FileMode.Create, FileAccess.Write, FileShare.Read))
{
using (var sw = new StreamWriter(fs))
{
sw.Write(Header.ToString());
// TODO: clean this up
if (_loopOffset.HasValue)
{
sw.WriteLine("LoopOffset " + _loopOffset);
}
foreach (var input in _log)
{
sw.WriteLine(input);
}
}
}
_changes = false;
}
}
}