Skip to content

Commit

Permalink
feat: fully work on the serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
xhayper committed Nov 12, 2023
1 parent 070f994 commit 8412291
Show file tree
Hide file tree
Showing 13 changed files with 259 additions and 22 deletions.
15 changes: 8 additions & 7 deletions src/internal/syntacticAnalysis/deserializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,27 @@ export class Deserializer {
/**
* INTERNAL USAGE ONLY! DO NOT USE THIS PROPERTY DIRECTLY!
*/
public readonly timingChanges: TimingChange[] = [new TimingChange(0, 4)];
public readonly timingChanges: TimingChange[] = [];
private _maxFinishTime: number = 0;
/**
* INTERNAL USAGE ONLY! DO NOT USE THIS PROPERTY DIRECTLY!
*/
public currentTime: number = 0;
public currentTime: number;
/**
* INTERNAL USAGE ONLY! DO NOT USE THIS PROPERTY DIRECTLY!
*/
public currentNoteCollection: NoteCollection | undefined;
/**
* INTERNAL USAGE ONLY! DO NOT USE THIS PROPERTY DIRECTLY!
*/
public currentTiming: TimingChange = new TimingChange(0, 0);
/**
* INTERNAL USAGE ONLY! DO NOT USE THIS PROPERTY DIRECTLY!
*/
public endOfFile: boolean = false;
public endOfFile: boolean;

constructor(sequence: Iterable<Token>) {
this.enumerator = new Enumerator<Token>(sequence);
this.timingChanges.push(new TimingChange(0, 4));
this.currentNoteCollection = undefined;
this.currentTime = 0;
this.endOfFile = false;
}

public getChart(): MaiChart {
Expand Down Expand Up @@ -135,6 +135,7 @@ export class Deserializer {
}

this._chart.noteCollections = noteCollections;
this._chart.timingChanges = this.timingChanges;

return this._chart;
}
Expand Down
80 changes: 78 additions & 2 deletions src/internal/syntacticAnalysis/serializer.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,79 @@
import { EachStyle } from "../../structures/eachStyle";
import { MaiChart } from "../../structures/maiChart";
import { NoteCollection } from "../../structures/noteCollection";

/**
* INTERNAL USAGE ONLY! DO NOT USE THIS PROPERTY DIRECTLY!
*/
export class Serializer {

}
private _currentTimingChange: number = 0;
private _currentNoteCollection: number = 0;
private _currentTime: number = 0;

// there's no such thing as StringWriter in TypeScript
public serialize(chart: MaiChart): string {
let writer = "";

writer += `(${chart.timingChanges[this._currentTimingChange].tempo})`;
writer += `{{${chart.timingChanges[this._currentTimingChange].subdivisions}}}`;

while (this._currentTime <= (chart.finishTiming ? chart.finishTiming : 0)) {
if (
this._currentTimingChange < chart.timingChanges.length - 1 &&
Math.abs(chart.timingChanges[this._currentTimingChange + 1].time - this._currentTime) < 1.401298E-45
) {
this._currentTimingChange++;

if (
Math.abs(
chart.timingChanges[this._currentTimingChange].tempo -
chart.timingChanges[this._currentTimingChange - 1].tempo
) > 1.401298E-45
) {
writer += `(${chart.timingChanges[this._currentTimingChange].tempo})`;
}

if (
Math.abs(
chart.timingChanges[this._currentTimingChange].subdivisions -
chart.timingChanges[this._currentTimingChange - 1].subdivisions
) > 1.401298E-45
) {
writer += `{{${chart.timingChanges[this._currentTimingChange].subdivisions}}}`;
}
}

if (
this._currentNoteCollection < chart.noteCollections.length &&
Math.abs(chart.noteCollections[this._currentNoteCollection].time - this._currentTime) <= 1.401298E-45
) {
writer += Serializer.serializeNoteCollection(chart.noteCollections[this._currentNoteCollection]);

this._currentNoteCollection++;
}

const timingChange = chart.timingChanges[this._currentTimingChange];
this._currentTime += timingChange.secondsPerBeat;
writer += ",";
}
writer += "E";

return writer;
}

private static serializeNoteCollection(notes: NoteCollection): string {
let writer = "";

const seperator = notes.eachStyle === EachStyle.ForceBroken ? "`" : "/";

if (notes.eachStyle === EachStyle.ForceEach) writer += "0/";

for (let i = 0; i < notes.length; i++) {
writer += notes[i].write();

if (i != notes.length - 1) writer += seperator;
}

return writer;
}
}
4 changes: 2 additions & 2 deletions src/internal/syntacticAnalysis/states/slideReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class SlideReader {
}

case TokenType.Duration: {
SlideReader.readDuration(token, parent.currentTiming, path);
SlideReader.readDuration(parent.timingChanges[parent.timingChanges.length - 1], token, path);
break;
}

Expand Down Expand Up @@ -151,7 +151,7 @@ export class SlideReader {
} while (parent.enumerator.current.type === TokenType.Location);
}

private static readDuration(token: Token, timing: TimingChange, path: SlidePath) {
private static readDuration(timing: TimingChange, token: Token, path: SlidePath) {
const startOfDurationDeclaration = 0;
const overrideTiming = timing;

Expand Down
27 changes: 20 additions & 7 deletions src/internal/syntacticAnalysis/states/subdivisionReader.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { UnexpectedCharacterException } from "../../errors/unexpectedCharacterException";
import { Token } from "../../lexicalAnalysis/token";
import { Deserializer } from "../deserializer";
import { TimingChange } from "../timingChange";

/**
* INTERNAL USAGE ONLY! DO NOT USE THIS PROPERTY DIRECTLY!
Expand All @@ -13,11 +14,19 @@ export class SubdivisionReader {
if (isNaN(explicitTempo))
throw new UnexpectedCharacterException(token.line, token.character + 1, '0~9, or "."');

// TODO: This might not copy the object and just be a reference.
const newTimingChange = parent.timingChanges[parent.timingChanges.length - 1];
newTimingChange.explicitOverride(explicitTempo);
let newTimingChange;
{
const oldTimingChange = parent.timingChanges[parent.timingChanges.length - 1];
newTimingChange = new TimingChange(oldTimingChange.tempo, oldTimingChange.subdivisions);
}

if (Math.abs(parent.timingChanges[parent.timingChanges.length - 1].time - parent.currentTime) <= 0.0001)
newTimingChange.setSeconds(explicitTempo);
newTimingChange.time = parent.currentTime;

if (
Math.abs(parent.timingChanges[parent.timingChanges.length - 1].time - parent.currentTime) <=
1.401298e-45
)
parent.timingChanges.pop();

parent.timingChanges.push(newTimingChange);
Expand All @@ -28,11 +37,15 @@ export class SubdivisionReader {

if (isNaN(subdivision)) throw new UnexpectedCharacterException(token.line, token.character, '0~9, or "."');

const newTimingChange = parent.timingChanges[parent.timingChanges.length - 1];
let newTimingChange;
{
const oldTimingChange = parent.timingChanges[parent.timingChanges.length - 1];
newTimingChange = new TimingChange(oldTimingChange.tempo, oldTimingChange.subdivisions);
}

newTimingChange.subdivisions = subdivision;

// TODO: This might not copy the object and just be a reference.
if (Math.abs(parent.timingChanges[parent.timingChanges.length - 1].time - parent.currentTime) <= 0.0001)
if (Math.abs(parent.timingChanges[parent.timingChanges.length - 1].time - parent.currentTime) <= 1.401298e-45)
parent.timingChanges.pop();

parent.timingChanges.push(newTimingChange);
Expand Down
10 changes: 8 additions & 2 deletions src/internal/syntacticAnalysis/states/tempoReader.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { UnexpectedCharacterException } from "../../errors/unexpectedCharacterException";
import { Token } from "../../lexicalAnalysis/token";
import { Deserializer } from "../deserializer";
import { TimingChange } from "../timingChange";

/**
* INTERNAL USAGE ONLY! DO NOT USE THIS PROPERTY DIRECTLY!
Expand All @@ -11,11 +12,16 @@ export class TempoReader {

if (isNaN(tempo)) throw new UnexpectedCharacterException(token.line, token.character, '0~9, or "."');

const newTimingChange = parent.timingChanges[parent.timingChanges.length - 1];
let newTimingChange;
{
const oldTimingChange = parent.timingChanges[parent.timingChanges.length - 1];
newTimingChange = new TimingChange(oldTimingChange.tempo, oldTimingChange.subdivisions);
}

newTimingChange.tempo = tempo;
newTimingChange.time = parent.currentTime;

if (Math.abs(parent.timingChanges[parent.timingChanges.length - 1].time - parent.currentTime) <= 0.0001)
if (Math.abs(parent.timingChanges[parent.timingChanges.length - 1].time - parent.currentTime) <= 1.401298e-45)
parent.timingChanges.pop();

parent.timingChanges.push(newTimingChange);
Expand Down
2 changes: 1 addition & 1 deletion src/internal/syntacticAnalysis/timingChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class TimingChange {
this.subdivisions = subdivisions;
}

explicitOverride(value: number) {
public setSeconds(value: number) {
this.tempo = 60 / value;
this.subdivisions = 4;
}
Expand Down
7 changes: 7 additions & 0 deletions src/internal/utility.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
export type FileEncoding = "utf7" | "utf8" | "utf16le" | "utf16be" | "utf32le" | "utf32be" | "unicode" | "unicodebe";

export class Utility {
/**
* Format a number to `0.0000000` format.
*/
static formatNumber(number: number): string {
return number.toFixed(7);
}

static removeLineEndings(str: string): string {
return str.replace(/(\r\n|\n|\r)/gm, "");
}
Expand Down
6 changes: 6 additions & 0 deletions src/simaiConvert.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Deserializer } from "./internal/syntacticAnalysis/deserializer";
import { Tokenizer } from "./internal/lexicalAnalysis/tokenizer";
import { MaiChart } from "./structures/maiChart";
import { Serializer } from "./internal/syntacticAnalysis/serializer";

export class SimaiConvert {
public static deserialize(data: string): MaiChart {
Expand All @@ -9,4 +10,9 @@ export class SimaiConvert {

return chart;
}

public static serialize(chart: MaiChart): string {
const serializer = new Serializer();
return serializer.serialize(chart);
}
}
28 changes: 27 additions & 1 deletion src/structures/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,32 @@ export class Location {
}

public toString(): string {
return `index: ${this.index}, group: ${this.group.toString()}`;
switch (this.group) {
case NoteGroup.Tap:
return (this.index + 1).toString();
case NoteGroup.CSensor:
return "C";
default:
let groupChar = "";

switch (this.group) {
case NoteGroup.ASensor:
groupChar = "A";
break;
case NoteGroup.BSensor:
groupChar = "B";
break;
case NoteGroup.DSensor:
groupChar = "D";
break;
case NoteGroup.ESensor:
groupChar = "E";
break;
// default:
// throw new ArgumentOutOfRangeException();
}

return groupChar + (this.index + 1).toString();
}
}
}
34 changes: 34 additions & 0 deletions src/structures/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,38 @@ export class Note {

return baseValue;
}

public write(): string {
let writer = "";

writer += this.location.toString();

if ((this.styles & NoteStyles.Ex) != 0) writer += "x";

if ((this.styles & NoteStyles.Mine) != 0) writer += "m";

if (this.type === NoteType.ForceInvalidate) writer += this.slideMorph === SlideMorph.FadeIn ? "?" : "!";

switch (this.appearance) {
case NoteAppearance.ForceNormal:
writer += "@";
break;
case NoteAppearance.ForceStarSpinning:
writer += "$$";
break;
case NoteAppearance.ForceStar:
writer += "$";
break;
}

if (this.length) `h[#${this.length.toFixed(7)}]`;

for (let i = 0; i < this.slidePaths.length; i++) {
if (i > 0) writer += "*";

writer += this.slidePaths[i].write();
}

return writer;
}
}
14 changes: 14 additions & 0 deletions src/structures/slidePath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,18 @@ export class SlidePath {
constructor(segments: SlideSegment[]) {
this.segments = segments;
}

public write(): string {
let writer = "";

for (const segment of this.segments) {
writer += segment.write(this.startLocation);
}

if (this.type === NoteType.Break) writer += "b";

writer += `[${this.delay.toFixed(7)}##${this.duration.toFixed(7)}]`;

return writer;
}
}
Loading

0 comments on commit 8412291

Please sign in to comment.