Skip to content

Instantly share code, notes, and snippets.

@aliyome
Last active July 22, 2018 06:28
Show Gist options
  • Save aliyome/2cf62e2ecf944aea7d1e to your computer and use it in GitHub Desktop.
Save aliyome/2cf62e2ecf944aea7d1e to your computer and use it in GitHub Desktop.
MUGEN air parser
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