Last active
July 22, 2018 06:28
-
-
Save aliyome/2cf62e2ecf944aea7d1e to your computer and use it in GitHub Desktop.
MUGEN air parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.stdio; | |
import std.string : strip, toLower, format; | |
import std.conv : to; | |
import std.regex : ctRegex, matchFirst; | |
import std.file : readText; | |
import pegged.grammar; | |
mixin(grammar(` | |
Air: | |
# All Lines | |
Lines <- (ActionLine | |
/ CountLine | |
/ AnimLine | |
/ RectLine | |
/ LoopLine | |
/ :BlankLine)* | |
# Lines | |
ActionLine <- :ActionLeft ActionNum :ActionRight :Comment* :eol | |
CountLine <- :CountLeft CountType ~('' CountDefault*) :CountSeparate CountValue :Comment* :eol | |
RectLine <- :RectLeft RectIndex :RectRight RectElem RectElem RectElem RectElem :Comment* :eol | |
AnimLine <- AnimElem AnimElem AnimElem AnimElem AnimElem AnimFlip? AnimAlpha? :Comment* :eol | |
LoopLine <- Loopstart :Comment* :eol | |
BlankLine <- Comment* (eol / eoi) | |
# Actions | |
ActionLeft < ~("[" [bB] "egin" [aA] "ction") | |
ActionRight <~ :S* "]" :S* | |
ActionNum <~ [0-9]+ | |
# Anims | |
AnimElem <- ~(:S* :","* :S* "-"* :S* [0-9]+ :S*) | |
AnimFlip <- ~(:S* :"," :S* ("HV" / "VH" / [hHvV] / '') :S*) | |
AnimAlpha <- ~(:S* :"," :S* | |
("A1" | |
/ ("AS" [0-9]+ "D" [0-9]+) | |
/ [aAsS] | |
/ '' ) :S*) | |
# ClsnCounts | |
CountLeft < "Clsn" | |
CountType < [12] | |
CountDefault < ~([dD] "efault") | |
CountSeparate < ":" | |
CountValue <~ [0-9]+ | |
# Rects | |
RectLeft < "Clsn" [12]* "[" | |
RectIndex < ~([0-9]+) | |
RectRight < "]" Equal | |
RectElem <- ~(:S* :","* :S* "-"* :S* [0-9]+ :S*) | |
# Loops | |
Loopstart <- ~(:S* [lL] "oop" [sS] "tart" :S*) | |
# Comments | |
Comment <~ :S* ';' (!eol .)* | |
# Other Terminals | |
Equal <~ :S* '=' :S* | |
S <~ space+ | |
`)); | |
class Animation { | |
AnimationElement[] elems; | |
Clsn offensiveDefault, damageableDefault; | |
int loopStart = -1; | |
static Animation[int] loadAir(string path) { | |
auto air = readText(path); | |
auto parseTree = Air(air); | |
return parseTree.toAnimations; | |
} | |
/// for debug | |
override string toString() { | |
return format("%s loop:%s %s %s", elems, loopStart, offensiveDefault, damageableDefault); | |
} | |
} | |
enum AnimationElementFlip : ubyte { | |
Default, | |
H, | |
V, | |
HV | |
} | |
enum AnimationElementAlpha : ubyte { | |
Default, | |
Add, | |
Sub, | |
Dark, | |
Custom | |
} | |
class AnimationElement { | |
int groupNo, imageNo; | |
int x, y; | |
int frame; | |
AnimationElementFlip flip; | |
AnimationElementAlpha alpha; | |
int alphaSource, alphaDest; | |
Clsn offensive, damageable; | |
/// for debug | |
override string toString() { | |
return format("img:[%s,%s] pos:(%s,%s) %sf flip:%s alpha:%s AS%sD%s clsn1{%s} clsn2{%s}", | |
groupNo, imageNo, x, y, frame, flip, alpha, alphaSource, alphaDest, offensive, damageable); | |
} | |
} | |
enum ClsnType : ubyte { | |
Unknown = 0, | |
Offensive = 1, | |
Damageable = 2 | |
} | |
class Clsn { | |
ClsnType type; | |
ubyte count; | |
ClsnRect[] rects; | |
/// for debug | |
override string toString() { | |
return format("type:%s count:%s %s", type, count, rects); | |
} | |
} | |
struct ClsnRect { | |
int back, bottom, front, top; | |
} | |
Animation[int] toAnimations(ParseTree parseTree) { | |
Animation[int] anims; | |
Animation currentAnimation; | |
AnimationElement currentElement; | |
Clsn currentClsnOffensive, currentClsnDamageable; | |
ClsnType currentClsnType; | |
bool currentClsnIsDefault; | |
void parseToCode(ParseTree p) { | |
switch (p.name) { | |
case "Air" : | |
parseToCode(p.children[0]); | |
break; | |
case "Air.Lines" : | |
foreach (c; p.children) { | |
parseToCode(c); | |
} | |
break; | |
case "Air.ActionLine" : | |
auto a = new Animation(); | |
auto index = p.matches[0].to!int; | |
anims[index] = a; | |
currentAnimation = a; | |
break; | |
case "Air.CountLine" : | |
auto c = new Clsn; | |
c.type = cast(ClsnType)p.matches[0].to!ubyte(); | |
c.count = p.matches[2].to!ubyte(); | |
currentClsnType = c.type; | |
currentClsnIsDefault = (p.matches[1].toLower() == "default" ); | |
if (c.type == ClsnType.Offensive) { | |
currentClsnOffensive = c; | |
} | |
else if (c.type == ClsnType.Offensive) { | |
currentClsnDamageable = c; | |
} | |
break; | |
case "Air.RectLine" : | |
auto r = ClsnRect(); | |
r.back = p.matches[0].to!int(); | |
r.bottom = p.matches[1].to!int(); | |
r.front = p.matches[2].to!int(); | |
r.top = p.matches[3].to!int(); | |
if (currentClsnType == ClsnType.Offensive) { | |
currentClsnOffensive.rects ~= r; | |
} | |
else if (currentClsnType == ClsnType.Offensive) { | |
currentClsnDamageable.rects ~= r; | |
} | |
break; | |
case "Air.AnimLine" : | |
currentElement = new AnimationElement; | |
currentElement.groupNo = p.matches[0].to!int(); | |
currentElement.imageNo = p.matches[1].to!int(); | |
currentElement.x = p.matches[2].to!int(); | |
currentElement.y = p.matches[3].to!int(); | |
currentElement.frame = p.matches[4].to!int(); | |
if (p.matches.length > 5) { | |
auto str = p.matches[5].toLower(); | |
if (str == "h") | |
currentElement.flip = AnimationElementFlip.H; | |
else if (str == "v") | |
currentElement.flip = AnimationElementFlip.V; | |
else if (str == "hv" || str == "vh") | |
currentElement.flip = AnimationElementFlip.H; | |
else if (str == "") | |
currentElement.flip = AnimationElementFlip.Default; | |
} | |
if (p.matches.length == 7) { | |
auto str = p.matches[6].to!string().strip(); | |
if (str == "A") { | |
currentElement.alpha = AnimationElementAlpha.Add; | |
} | |
else if (str == "S") { | |
currentElement.alpha = AnimationElementAlpha.Sub; | |
} | |
else if (str == "A1") { | |
currentElement.alpha = AnimationElementAlpha.Dark; | |
} | |
else if (str == "") { | |
currentElement.alpha = AnimationElementAlpha.Default; | |
} | |
else { | |
enum reg = ctRegex!(`AS(\d+)D(\d+)`); | |
auto c = matchFirst(str, reg); | |
currentElement.alphaSource = c[1].to!int(); | |
currentElement.alphaDest = c[2].to!int(); | |
} | |
} | |
if (currentClsnIsDefault) { | |
currentAnimation.offensiveDefault = currentClsnOffensive; | |
currentAnimation.damageableDefault = currentClsnDamageable; | |
} | |
else { | |
currentElement.offensive = currentClsnOffensive; | |
currentElement.damageable = currentClsnDamageable; | |
} | |
currentElement.offensive = currentClsnOffensive; | |
currentElement.damageable = currentClsnDamageable; | |
currentAnimation.elems ~= currentElement; | |
currentClsnOffensive = null; | |
currentClsnDamageable = null; | |
currentElement = null; | |
currentClsnIsDefault = false; | |
break; | |
case "Air.LoopLine": | |
currentAnimation.loopStart = currentAnimation.elems.length; | |
break; | |
default: | |
break; | |
} | |
} | |
parseToCode(parseTree); | |
return anims; | |
} | |
void main() { | |
enum testdata = | |
` | |
[Begin Action 10] ; comment | |
Clsn2Default: 1 ; comment | |
Clsn2[0] = 1, 2, 3, 4 ; comm | |
Clsn1: 1 ; comment | |
Clsn[0] = -10, 0, 10,-79 ; Clsn [12]* | |
1,2,3,4,5,H,AS0D256 ; comm | |
Loopstart | |
1,2,3,4,5,,AS0D256 ; comm | |
;;;; comment | |
`; | |
Air.ActionLine("[Begin Action 10]\n").successful.writeln; | |
Air.CountLine("Clsn1: 1\n").successful.writeln; | |
Air.CountLine("Clsn1Default: 1\n").successful.writeln; | |
Air.RectLine("Clsn[0] = -10, 0, 10,-79\n").successful.writeln; | |
Air.AnimLine("1,2,3,4,5\n").successful.writeln; | |
Air.AnimLine("1,2,3,4,5,HV\n").successful.writeln; | |
Air.AnimLine("1,2,3,4,5,H,A1\n").successful.writeln; | |
Air.AnimLine("1,2,3,4,5,,AS128D128\n").successful.writeln; | |
Air.LoopLine("Loopstart\n").successful.writeln; | |
auto t = Air(testdata); | |
writeln(t); | |
auto a = t.toAnimations(); | |
writeln(a); | |
auto anims = Animation.loadAir("alice.air"); | |
writeln(anims); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment