sinmai-mods/MoreChartFormats/patch_NotesReader.cs
2024-08-08 13:32:25 +07:00

211 lines
7.5 KiB
C#

// ReSharper disable CheckNamespace
// ReSharper disable InconsistentNaming
using System.Xml.Serialization;
using Manager.MaiStudio.Serialize;
using MonoMod;
using MoreChartFormats;
using MoreChartFormats.MaiSxt;
using MoreChartFormats.Simai;
using MoreChartFormats.Simai.Errors;
namespace Manager;
class patch_NotesReader : NotesReader
{
private new FormatType checkFormat(string fileName) => Path.GetExtension(fileName) switch
{
".simai" => FormatType.FORMAT_M2S,
".srt" => FormatType.FORMAT_SRT,
".szt" => FormatType.FORMAT_SZT,
".sct" => FormatType.FORMAT_SCT,
".sdt" => FormatType.FORMAT_SDT,
_ => FormatType.FORMAT_MA2,
};
[MonoModIgnore]
private extern bool loadMa2(string fileName, LoadType loadType = LoadType.LOAD_FULL);
[MonoModReplace]
public new bool load(string fileName, LoadType loadType = LoadType.LOAD_FULL)
{
if (!File.Exists(fileName))
{
return false;
}
var format = checkFormat(fileName);
return format switch
{
FormatType.FORMAT_MA2 => loadMa2(fileName, loadType),
FormatType.FORMAT_SRT or FormatType.FORMAT_SZT or FormatType.FORMAT_SCT or FormatType.FORMAT_SDT =>
loadSxt(format, fileName),
FormatType.FORMAT_M2S => loadSimai(fileName),
_ => false,
};
}
private bool loadSxt(FormatType format, string fileName)
{
init(_playerID);
fillDummyHeader(fileName);
_loadType = LoadType.LOAD_FULL;
try
{
// HACK: we are assuming that the chart file is stored in the same folder
// as the Music.xml, which it must be due to how this game loads assets.
// refer to `Manager.MaiStudio.Serialize.FilePath.AddPath(string parentPath)`.
//
// There must be a better way to get the song's BPM...
var musicFolder = Path.GetDirectoryName(fileName);
if (musicFolder == null)
{
throw new Exception("Music.xml is contained in the root directory?!");
}
var serializer = new XmlSerializer(typeof(MusicData));
using (var musicXml = File.OpenRead(Path.Combine(musicFolder, "Music.xml")))
{
var music = (MusicData)serializer.Deserialize(musicXml);
var bpmChangeData = new BPMChangeData
{
bpm = music.bpm,
time = new NotesTime(0, 0, this),
};
_composition._bpmList.Add(bpmChangeData);
calcBPMList();
}
var content = File.ReadAllText(fileName);
var refs = new NotesReferences
{
Reader = this,
Header = _header,
Composition = _composition,
Notes = _note,
};
SxtReaderBase reader = format == FormatType.FORMAT_SRT ? new SrtReader(refs) : new SxtReader(refs);
reader.Deserialize(content);
calcAll();
}
catch (Exception e)
{
System.Console.WriteLine("[MoreChartFormats] [SXT] Could not load SXT chart: {0}", e);
return false;
}
return true;
}
private bool loadSimai(string fileName)
{
init(_playerID);
fillDummyHeader(fileName);
_loadType = LoadType.LOAD_FULL;
try
{
System.Console.WriteLine("[MoreChartFormats] [Simai] Tokenizing chart");
var tokens = SimaiReader.Tokenize(File.ReadAllText(fileName));
var refs = new NotesReferences
{
Reader = this,
Header = _header,
Composition = _composition,
Notes = _note,
};
System.Console.WriteLine("[MoreChartFormats] [Simai] Processing BPM changes");
SimaiReader.ReadBpmChanges(tokens, refs);
calcBPMList();
System.Console.WriteLine("[MoreChartFormats] [Simai] Reading chart");
SimaiReader.Deserialize(tokens, refs);
System.Console.WriteLine("[MoreChartFormats] [Simai] Mirroring chart and calculating timings");
foreach (var note in _note._noteData)
{
note.time.calcMsec(this);
note.end.calcMsec(this);
if (note.type.isTouch() && note.touchArea is TouchSensorType.D or TouchSensorType.E)
{
note.startButtonPos = ConvertMirrorTouchEPosition(note.startButtonPos);
}
else
{
note.startButtonPos = ConvertMirrorPosition(note.startButtonPos);
}
if (note.type.isSlide() || note.type == NotesTypeID.Def.ConnectSlide)
{
note.slideData.shoot.time.calcMsec(this);
note.slideData.arrive.time.calcMsec(this);
note.slideData.targetNote = ConvertMirrorPosition(note.slideData.targetNote);
note.slideData.type = ConvertMirrorSlide(note.slideData.type);
}
}
#if DEBUG
System.Console.WriteLine("[MoreChartFormats] [Simai] Calculating chart data");
#endif
calcAll();
#if DEBUG
System.Console.WriteLine("[MoreChartFormats] [Simai] Loaded {0} notes", _total.GetAllNoteNum());
System.Console.WriteLine("[MoreChartFormats] [Simai] > {0} taps", _total.GetTapNum());
System.Console.WriteLine("[MoreChartFormats] [Simai] > {0} holds", _total.GetHoldNum());
System.Console.WriteLine("[MoreChartFormats] [Simai] > {0} slides", _total.GetSlideNum());
System.Console.WriteLine("[MoreChartFormats] [Simai] > {0} touches", _total.GetTouchNum());
System.Console.WriteLine("[MoreChartFormats] [Simai] > {0} break", _total.GetBreakNum());
#endif
}
catch (SimaiException e)
{
System.Console.WriteLine($"[MoreChartFormats] [Simai] There was an error loading the chart at line {e.line}, col {e.character}: {e} ");
return false;
}
catch (Exception e)
{
System.Console.WriteLine($"[MoreChartFormats] [Simai] There was an error loading the chart: {e}");
return false;
}
return true;
}
private void fillDummyHeader(string fileName)
{
_header._notesName = fileName;
_header._resolutionTime = ReaderConst.DEFAULT_RESOLUTION_TIME;
_header._version[0].major = 0;
_header._version[0].minor = 0;
_header._version[0].release = 0;
_header._version[1].major = 1;
_header._version[1].minor = 4;
_header._version[0].release = 0;
_header._metInfo.denomi = 4;
_header._metInfo.num = 4;
_header._clickFirst = ReaderConst.DEFAULT_RESOLUTION_TIME;
// The game doesn't care if a non-fes-mode chart uses utage mechanics.
// It's just a flag.
_header._isFes = false;
}
private void calcAll()
{
calcNoteTiming();
calcEach();
calcSlide();
calcEndTiming();
calcBPMInfo();
calcBarList();
calcSoflanList();
calcClickList();
calcTotal();
}
}