From c0c1fb482f2584a274e505ca6b0bd0217d710eb3 Mon Sep 17 00:00:00 2001
From: Eric Myllyoja <ericmyllyoja@gmail.com>
Date: Wed, 23 Feb 2022 16:58:23 -0500
Subject: [PATCH] Add stage data files

---
 source/play/character/Character.hx |  18 +-
 source/play/stage/ScriptedStage.hx |  34 +-
 source/play/stage/StageData.hx     | 746 ++++++++++++++---------------
 source/util/assets/DataAssets.hx   |  60 +--
 source/util/macro/MacroUtil.hx     |  24 +-
 5 files changed, 441 insertions(+), 441 deletions(-)

diff --git a/source/play/character/Character.hx b/source/play/character/Character.hx
index ec8ec8a30..b4ee9bb03 100644
--- a/source/play/character/Character.hx
+++ b/source/play/character/Character.hx
@@ -1,9 +1,9 @@
-package play.character;
-
-enum CharacterType
-{
-	BF;
-	GF;
-	DAD;
-	OTHER;
-}
+package play.character;
+
+enum CharacterType
+{
+	BF;
+	GF;
+	DAD;
+	OTHER;
+}
diff --git a/source/play/stage/ScriptedStage.hx b/source/play/stage/ScriptedStage.hx
index d43579097..40bbdb30e 100644
--- a/source/play/stage/ScriptedStage.hx
+++ b/source/play/stage/ScriptedStage.hx
@@ -1,17 +1,17 @@
-package play.stage;
-
-import modding.IHook;
-
-/**
- * NOTE: Turns out one of the few edge case that scripted classes are broken with,
- * that being generic classes with a constrained type argument, applies to FlxSpriteGroup.
- * Will have to find a fix for the issue before stages can have scripting enabled.
- * 
- * In the meantime though, I want to get stages working just with JSON.
- * -Eric
- */
-// @:hscriptClass
-// class ScriptedStage extends Stage implements IHook
-// {
-// 	// No body needed for this class, it's magic ;)
-// }
+package play.stage;
+
+import modding.IHook;
+
+/**
+ * NOTE: Turns out one of the few edge case that scripted classes are broken with,
+ * that being generic classes with a constrained type argument, applies to FlxSpriteGroup.
+ * Will have to find a fix for the issue before stages can have scripting enabled.
+ * 
+ * In the meantime though, I want to get stages working just with JSON.
+ * -Eric
+ */
+// @:hscriptClass
+// class ScriptedStage extends Stage implements IHook
+// {
+// 	// No body needed for this class, it's magic ;)
+// }
diff --git a/source/play/stage/StageData.hx b/source/play/stage/StageData.hx
index ba97b3140..0ecf3a8cf 100644
--- a/source/play/stage/StageData.hx
+++ b/source/play/stage/StageData.hx
@@ -1,373 +1,373 @@
-package play.stage;
-
-import openfl.Assets;
-import util.assets.DataAssets;
-import haxe.Json;
-import flixel.util.typeLimit.OneOfTwo;
-
-using StringTools;
-
-/**
- * Contains utilities for loading and parsing stage data.
- */
-class StageDataParser
-{
-	/**
-	 * The current version string for the stage data format.
-	 * Handle breaking changes by incrementing this value
-	 * and adding migration to the `migrateStageData()` function.
-	 */
-	public static final STAGE_DATA_VERSION:String = "1.0";
-
-	static final stageCache:Map<String, Stage> = new Map<String, Stage>();
-
-	public static function loadStageCache():Void
-	{
-		trace("Loading stage cache...");
-		var stageIdList:Array<String> = DataAssets.listDataFilesInPath('stages/');
-		for (stageId in stageIdList)
-		{
-			var stage:Stage = new Stage(stageId);
-			if (stage != null)
-			{
-				trace('  Loaded stage data: ${stage.stageName}');
-				stageCache.set(stageId, stage);
-			}
-		}
-		trace('  Successfully loaded ${Lambda.count(stageCache)} stages.');
-	}
-
-	public static function fetchStage(stageId:String):Null<Stage>
-	{
-		if (stageCache.exists(stageId))
-		{
-			trace('Successfully fetch stage: ${stageId}');
-			return stageCache.get(stageId);
-		}
-		else
-		{
-			trace('Failed to fetch stage, not found in cache: ${stageId}');
-			return null;
-		}
-	}
-
-	/**
-	 * Load a stage's JSON file, parse its data, and return it.
-	 * 
-	 * @param stageId The stage to load.
-	 * @return The stage data, or null if validation failed.
-	 */
-	public static function parseStageData(stageId:String):Null<StageData>
-	{
-		var rawJson:String = loadStageFile(stageId);
-
-		var stageData:StageData = migrateStageData(rawJson);
-
-		return validateStageData(stageId, stageData);
-	}
-
-	static function loadStageFile(stagePath:String):String
-	{
-		var stageFilePath:String = Paths.json('stages/${stagePath}');
-		var rawJson = Assets.getText(stageFilePath).trim();
-
-		while (!rawJson.endsWith("}"))
-		{
-			rawJson = rawJson.substr(0, rawJson.length - 1);
-		}
-
-		return rawJson;
-	}
-
-	static function migrateStageData(rawJson:String)
-	{
-		// If you update the stage data format in a breaking way,
-		// handle migration here by checking the `version` value.
-
-		var stageData:StageData = cast Json.parse(rawJson);
-
-		return stageData;
-	}
-
-	static final DEFAULT_NAME:String = "Untitled Stage";
-	static final DEFAULT_CAMERAZOOM:Float = 1.0;
-	static final DEFAULT_ZINDEX:Int = 0;
-	static final DEFAULT_SCALE:Float = 1.0;
-	static final DEFAULT_POSITION:Array<Float> = [0, 0];
-	static final DEFAULT_SCROLL:Array<Float> = [0, 0];
-
-	static final DEFAULT_CHARACTER_DATA:StageDataCharacter = {
-		zIndex: DEFAULT_ZINDEX,
-		position: DEFAULT_POSITION,
-	}
-
-	/**
-	 * Set unspecified parameters to their defaults.
-	 * If the parameter is mandatory, print an error message.
-	 * @param id 
-	 * @param input 
-	 * @return The validated stage data
-	 */
-	static function validateStageData(id:String, input:StageData):Null<StageData>
-	{
-		if (input.version != STAGE_DATA_VERSION)
-		{
-			trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing version');
-			return null;
-		}
-
-		if (input.name == null)
-		{
-			trace('[STAGEDATA] WARN: Stage data for "$id" missing name');
-			input.name = DEFAULT_NAME;
-		}
-
-		if (input.cameraZoom == null)
-		{
-			input.cameraZoom = DEFAULT_CAMERAZOOM;
-		}
-
-		if (input.props == null || input.props.length == 0)
-		{
-			trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing props');
-			return null;
-		}
-
-		for (inputProp in input.props)
-		{
-			// It's fine for inputProp.name to be null
-
-			if (inputProp.assetPath == null)
-			{
-				trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing assetPath for prop "${inputProp.name}"');
-				return null;
-			}
-
-			if (inputProp.position == null)
-			{
-				inputProp.position = DEFAULT_POSITION;
-			}
-
-			if (inputProp.zIndex == null)
-			{
-				inputProp.zIndex = DEFAULT_ZINDEX;
-			}
-
-			if (inputProp.scale == null)
-			{
-				inputProp.scale = DEFAULT_SCALE;
-			}
-
-			if (Std.isOfType(inputProp.scale, Float))
-			{
-				inputProp.scale = [inputProp.scale, inputProp.scale];
-			}
-
-			if (inputProp.scroll == null)
-			{
-				inputProp.scroll = DEFAULT_SCROLL;
-			}
-
-			if (Std.isOfType(inputProp.scroll, Float))
-			{
-				inputProp.scroll = [inputProp.scroll, inputProp.scroll];
-			}
-
-			if (inputProp.animations == null)
-			{
-				inputProp.animations = [];
-			}
-
-			if (inputProp.animations.length == 0 && inputProp.startingAnimation != null)
-			{
-				trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing animations for prop "${inputProp.name}"');
-				return null;
-			}
-
-			for (inputAnimation in inputProp.animations)
-			{
-				if (inputAnimation.name == null)
-				{
-					trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing animation name for prop "${inputProp.name}"');
-					return null;
-				}
-
-				if (inputAnimation.frameRate == null)
-				{
-					inputAnimation.frameRate = 24;
-				}
-
-				if (inputAnimation.loop == null)
-				{
-					inputAnimation.loop = true;
-				}
-
-				if (inputAnimation.flipX == null)
-				{
-					inputAnimation.flipX = false;
-				}
-
-				if (inputAnimation.flipY == null)
-				{
-					inputAnimation.flipY = false;
-				}
-			}
-		}
-
-		if (input.characters == null)
-		{
-			trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing characters');
-			return null;
-		}
-
-		if (input.characters.bf == null)
-		{
-			input.characters.bf = DEFAULT_CHARACTER_DATA;
-		}
-		if (input.characters.dad == null)
-		{
-			input.characters.dad = DEFAULT_CHARACTER_DATA;
-		}
-		if (input.characters.gf == null)
-		{
-			input.characters.gf = DEFAULT_CHARACTER_DATA;
-		}
-
-		for (inputCharacter in [input.characters.bf, input.characters.dad, input.characters.gf])
-		{
-			if (inputCharacter.zIndex == null)
-			{
-				inputCharacter.zIndex = 0;
-			}
-			if (inputCharacter.position == null || inputCharacter.position.length != 2)
-			{
-				inputCharacter.position = [0, 0];
-			}
-		}
-
-		// All good!
-		return input;
-	}
-}
-
-typedef StageData =
-{
-	// Uses semantic versioning.
-	var version:String;
-	var name:String;
-	var cameraZoom:Null<Float>;
-	var props:Array<StageDataProp>;
-	var characters:
-		{
-			bf:StageDataCharacter,
-			dad:StageDataCharacter,
-			gf:StageDataCharacter,
-		};
-};
-
-typedef StageDataProp =
-{
-	/**
-	 * The name of the prop for later lookup by scripts.
-	 * Optional; if unspecified, the prop can't be referenced by scripts.
-	 */
-	var name:String;
-
-	/**
-	 * The asset used to display the prop.
-	 */
-	var assetPath:String;
-
-	/**
-	 * The position of the prop as an [x, y] array of two floats.
-	 */
-	var position:Array<Float>;
-
-	/**
-	 * A number determining the stack order of the prop, relative to other props and the characters in the stage.
-	 * Props with lower numbers render below those with higher numbers.
-	 * This is just like CSS, it isn't hard.
-	 * @default 0
-	 */
-	var zIndex:Null<Int>;
-
-	/**
-	 * Either the scale of the prop as a float, or the [w, h] scale as an array of two floats.
-	 * @default 1
-	 */
-	var scale:OneOfTwo<Float, Array<Float>>;
-
-	/**
-	 * How much the prop scrolls relative to the camera. Used to create a parallax effect.
-	 * Represented as a float or as an [x, y] array of two floats.
-	 * [1, 1] means the prop moves 1:1 with the camera.
-	 * [0.5, 0.5] means the prop half as much as the camera.
-	 * [0, 0] means the prop is not moved.
-	 * @default [0, 0]
-	 */
-	var scroll:OneOfTwo<Float, Array<Float>>;
-
-	/**
-	 * An optional array of animations which the prop can play.
-	 * @default Prop has no animations.
-	 */
-	var animations:Array<StageDataPropAnimation>;
-
-	/**
-	 * If animations are used, this is the name of the animation to play first.
-	 * @default Don't play an animation.
-	 */
-	var startingAnimation:String;
-};
-
-typedef StageDataPropAnimation =
-{
-	/**
-	 * The name of the animation.
-	 */
-	var name:String;
-
-	/**
-	 * The common beginning of image names in atlas for this animation's frames.
-	 * For example, if the frames are named "test0001.png", "test0002.png", etc., use "test".
-	 */
-	var prefix:String;
-
-	/**
-	 * The speed of the animation in frames per second.
-	 * @default 24
-	 */
-	var frameRate:Null<Int>;
-
-	/**
-	 * Whether the animation should loop.
-	 * @default false
-	 */
-	var loop:Null<Bool>;
-
-	/**
-	 * Whether to flip the sprite horizontally while animating.
-	 * @default false
-	 */
-	var flipX:Null<Bool>;
-
-	/**
-	 * Whether to flip the sprite vertically while animating.
-	 * @default false
-	 */
-	var flipY:Null<Bool>;
-};
-
-typedef StageDataCharacter =
-{
-	/**
-	 * A number determining the stack order of the character, relative to props and other characters in the stage.
-	 * Again, just like CSS.
-	 * @default 0
-	 */
-	zIndex:Null<Int>,
-
-	/**
-	 * The position to render the character at.
-	 */ position:Array<Float>
-};
+package play.stage;
+
+import openfl.Assets;
+import util.assets.DataAssets;
+import haxe.Json;
+import flixel.util.typeLimit.OneOfTwo;
+
+using StringTools;
+
+/**
+ * Contains utilities for loading and parsing stage data.
+ */
+class StageDataParser
+{
+	/**
+	 * The current version string for the stage data format.
+	 * Handle breaking changes by incrementing this value
+	 * and adding migration to the `migrateStageData()` function.
+	 */
+	public static final STAGE_DATA_VERSION:String = "1.0";
+
+	static final stageCache:Map<String, Stage> = new Map<String, Stage>();
+
+	public static function loadStageCache():Void
+	{
+		trace("Loading stage cache...");
+		var stageIdList:Array<String> = DataAssets.listDataFilesInPath('stages/');
+		for (stageId in stageIdList)
+		{
+			var stage:Stage = new Stage(stageId);
+			if (stage != null)
+			{
+				trace('  Loaded stage data: ${stage.stageName}');
+				stageCache.set(stageId, stage);
+			}
+		}
+		trace('  Successfully loaded ${Lambda.count(stageCache)} stages.');
+	}
+
+	public static function fetchStage(stageId:String):Null<Stage>
+	{
+		if (stageCache.exists(stageId))
+		{
+			trace('Successfully fetch stage: ${stageId}');
+			return stageCache.get(stageId);
+		}
+		else
+		{
+			trace('Failed to fetch stage, not found in cache: ${stageId}');
+			return null;
+		}
+	}
+
+	/**
+	 * Load a stage's JSON file, parse its data, and return it.
+	 * 
+	 * @param stageId The stage to load.
+	 * @return The stage data, or null if validation failed.
+	 */
+	public static function parseStageData(stageId:String):Null<StageData>
+	{
+		var rawJson:String = loadStageFile(stageId);
+
+		var stageData:StageData = migrateStageData(rawJson);
+
+		return validateStageData(stageId, stageData);
+	}
+
+	static function loadStageFile(stagePath:String):String
+	{
+		var stageFilePath:String = Paths.json('stages/${stagePath}');
+		var rawJson = Assets.getText(stageFilePath).trim();
+
+		while (!rawJson.endsWith("}"))
+		{
+			rawJson = rawJson.substr(0, rawJson.length - 1);
+		}
+
+		return rawJson;
+	}
+
+	static function migrateStageData(rawJson:String)
+	{
+		// If you update the stage data format in a breaking way,
+		// handle migration here by checking the `version` value.
+
+		var stageData:StageData = cast Json.parse(rawJson);
+
+		return stageData;
+	}
+
+	static final DEFAULT_NAME:String = "Untitled Stage";
+	static final DEFAULT_CAMERAZOOM:Float = 1.0;
+	static final DEFAULT_ZINDEX:Int = 0;
+	static final DEFAULT_SCALE:Float = 1.0;
+	static final DEFAULT_POSITION:Array<Float> = [0, 0];
+	static final DEFAULT_SCROLL:Array<Float> = [0, 0];
+
+	static final DEFAULT_CHARACTER_DATA:StageDataCharacter = {
+		zIndex: DEFAULT_ZINDEX,
+		position: DEFAULT_POSITION,
+	}
+
+	/**
+	 * Set unspecified parameters to their defaults.
+	 * If the parameter is mandatory, print an error message.
+	 * @param id 
+	 * @param input 
+	 * @return The validated stage data
+	 */
+	static function validateStageData(id:String, input:StageData):Null<StageData>
+	{
+		if (input.version != STAGE_DATA_VERSION)
+		{
+			trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing version');
+			return null;
+		}
+
+		if (input.name == null)
+		{
+			trace('[STAGEDATA] WARN: Stage data for "$id" missing name');
+			input.name = DEFAULT_NAME;
+		}
+
+		if (input.cameraZoom == null)
+		{
+			input.cameraZoom = DEFAULT_CAMERAZOOM;
+		}
+
+		if (input.props == null || input.props.length == 0)
+		{
+			trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing props');
+			return null;
+		}
+
+		for (inputProp in input.props)
+		{
+			// It's fine for inputProp.name to be null
+
+			if (inputProp.assetPath == null)
+			{
+				trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing assetPath for prop "${inputProp.name}"');
+				return null;
+			}
+
+			if (inputProp.position == null)
+			{
+				inputProp.position = DEFAULT_POSITION;
+			}
+
+			if (inputProp.zIndex == null)
+			{
+				inputProp.zIndex = DEFAULT_ZINDEX;
+			}
+
+			if (inputProp.scale == null)
+			{
+				inputProp.scale = DEFAULT_SCALE;
+			}
+
+			if (Std.isOfType(inputProp.scale, Float))
+			{
+				inputProp.scale = [inputProp.scale, inputProp.scale];
+			}
+
+			if (inputProp.scroll == null)
+			{
+				inputProp.scroll = DEFAULT_SCROLL;
+			}
+
+			if (Std.isOfType(inputProp.scroll, Float))
+			{
+				inputProp.scroll = [inputProp.scroll, inputProp.scroll];
+			}
+
+			if (inputProp.animations == null)
+			{
+				inputProp.animations = [];
+			}
+
+			if (inputProp.animations.length == 0 && inputProp.startingAnimation != null)
+			{
+				trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing animations for prop "${inputProp.name}"');
+				return null;
+			}
+
+			for (inputAnimation in inputProp.animations)
+			{
+				if (inputAnimation.name == null)
+				{
+					trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing animation name for prop "${inputProp.name}"');
+					return null;
+				}
+
+				if (inputAnimation.frameRate == null)
+				{
+					inputAnimation.frameRate = 24;
+				}
+
+				if (inputAnimation.loop == null)
+				{
+					inputAnimation.loop = true;
+				}
+
+				if (inputAnimation.flipX == null)
+				{
+					inputAnimation.flipX = false;
+				}
+
+				if (inputAnimation.flipY == null)
+				{
+					inputAnimation.flipY = false;
+				}
+			}
+		}
+
+		if (input.characters == null)
+		{
+			trace('[STAGEDATA] ERROR: Could not load stage data for "$id": missing characters');
+			return null;
+		}
+
+		if (input.characters.bf == null)
+		{
+			input.characters.bf = DEFAULT_CHARACTER_DATA;
+		}
+		if (input.characters.dad == null)
+		{
+			input.characters.dad = DEFAULT_CHARACTER_DATA;
+		}
+		if (input.characters.gf == null)
+		{
+			input.characters.gf = DEFAULT_CHARACTER_DATA;
+		}
+
+		for (inputCharacter in [input.characters.bf, input.characters.dad, input.characters.gf])
+		{
+			if (inputCharacter.zIndex == null)
+			{
+				inputCharacter.zIndex = 0;
+			}
+			if (inputCharacter.position == null || inputCharacter.position.length != 2)
+			{
+				inputCharacter.position = [0, 0];
+			}
+		}
+
+		// All good!
+		return input;
+	}
+}
+
+typedef StageData =
+{
+	// Uses semantic versioning.
+	var version:String;
+	var name:String;
+	var cameraZoom:Null<Float>;
+	var props:Array<StageDataProp>;
+	var characters:
+		{
+			bf:StageDataCharacter,
+			dad:StageDataCharacter,
+			gf:StageDataCharacter,
+		};
+};
+
+typedef StageDataProp =
+{
+	/**
+	 * The name of the prop for later lookup by scripts.
+	 * Optional; if unspecified, the prop can't be referenced by scripts.
+	 */
+	var name:String;
+
+	/**
+	 * The asset used to display the prop.
+	 */
+	var assetPath:String;
+
+	/**
+	 * The position of the prop as an [x, y] array of two floats.
+	 */
+	var position:Array<Float>;
+
+	/**
+	 * A number determining the stack order of the prop, relative to other props and the characters in the stage.
+	 * Props with lower numbers render below those with higher numbers.
+	 * This is just like CSS, it isn't hard.
+	 * @default 0
+	 */
+	var zIndex:Null<Int>;
+
+	/**
+	 * Either the scale of the prop as a float, or the [w, h] scale as an array of two floats.
+	 * @default 1
+	 */
+	var scale:OneOfTwo<Float, Array<Float>>;
+
+	/**
+	 * How much the prop scrolls relative to the camera. Used to create a parallax effect.
+	 * Represented as a float or as an [x, y] array of two floats.
+	 * [1, 1] means the prop moves 1:1 with the camera.
+	 * [0.5, 0.5] means the prop half as much as the camera.
+	 * [0, 0] means the prop is not moved.
+	 * @default [0, 0]
+	 */
+	var scroll:OneOfTwo<Float, Array<Float>>;
+
+	/**
+	 * An optional array of animations which the prop can play.
+	 * @default Prop has no animations.
+	 */
+	var animations:Array<StageDataPropAnimation>;
+
+	/**
+	 * If animations are used, this is the name of the animation to play first.
+	 * @default Don't play an animation.
+	 */
+	var startingAnimation:String;
+};
+
+typedef StageDataPropAnimation =
+{
+	/**
+	 * The name of the animation.
+	 */
+	var name:String;
+
+	/**
+	 * The common beginning of image names in atlas for this animation's frames.
+	 * For example, if the frames are named "test0001.png", "test0002.png", etc., use "test".
+	 */
+	var prefix:String;
+
+	/**
+	 * The speed of the animation in frames per second.
+	 * @default 24
+	 */
+	var frameRate:Null<Int>;
+
+	/**
+	 * Whether the animation should loop.
+	 * @default false
+	 */
+	var loop:Null<Bool>;
+
+	/**
+	 * Whether to flip the sprite horizontally while animating.
+	 * @default false
+	 */
+	var flipX:Null<Bool>;
+
+	/**
+	 * Whether to flip the sprite vertically while animating.
+	 * @default false
+	 */
+	var flipY:Null<Bool>;
+};
+
+typedef StageDataCharacter =
+{
+	/**
+	 * A number determining the stack order of the character, relative to props and other characters in the stage.
+	 * Again, just like CSS.
+	 * @default 0
+	 */
+	zIndex:Null<Int>,
+
+	/**
+	 * The position to render the character at.
+	 */ position:Array<Float>
+};
diff --git a/source/util/assets/DataAssets.hx b/source/util/assets/DataAssets.hx
index 219bcbfc0..d35cd7ab6 100644
--- a/source/util/assets/DataAssets.hx
+++ b/source/util/assets/DataAssets.hx
@@ -1,30 +1,30 @@
-package util.assets;
-
-using StringTools;
-
-class DataAssets
-{
-	static function buildDataPath(path:String):String
-	{
-		return 'assets/data/${path}';
-	}
-
-	public static function listDataFilesInPath(path:String, ?ext:String = '.json'):Array<String>
-	{
-		var textAssets = openfl.utils.Assets.list();
-		var queryPath = buildDataPath(path);
-
-		var results:Array<String> = [];
-		for (textPath in textAssets)
-		{
-			if (textPath.startsWith(queryPath) && textPath.endsWith(ext))
-			{
-				var pathNoSuffix = textPath.substring(0, textPath.length - ext.length);
-				var pathNoPrefix = pathNoSuffix.substring(queryPath.length);
-				results.push(pathNoPrefix);
-			}
-		}
-
-		return results;
-	}
-}
+package util.assets;
+
+using StringTools;
+
+class DataAssets
+{
+	static function buildDataPath(path:String):String
+	{
+		return 'assets/data/${path}';
+	}
+
+	public static function listDataFilesInPath(path:String, ?ext:String = '.json'):Array<String>
+	{
+		var textAssets = openfl.utils.Assets.list();
+		var queryPath = buildDataPath(path);
+
+		var results:Array<String> = [];
+		for (textPath in textAssets)
+		{
+			if (textPath.startsWith(queryPath) && textPath.endsWith(ext))
+			{
+				var pathNoSuffix = textPath.substring(0, textPath.length - ext.length);
+				var pathNoPrefix = pathNoSuffix.substring(queryPath.length);
+				results.push(pathNoPrefix);
+			}
+		}
+
+		return results;
+	}
+}
diff --git a/source/util/macro/MacroUtil.hx b/source/util/macro/MacroUtil.hx
index 5c9a3edb7..96f86223b 100644
--- a/source/util/macro/MacroUtil.hx
+++ b/source/util/macro/MacroUtil.hx
@@ -1,12 +1,12 @@
-package util.macro;
-
-class MacroUtil
-{
-	public static macro function getDefine(key:String, defaultValue:String = null):haxe.macro.Expr
-	{
-		var value = haxe.macro.Context.definedValue(key);
-		if (value == null)
-			value = defaultValue;
-		return macro $v{value};
-	}
-}
+package util.macro;
+
+class MacroUtil
+{
+	public static macro function getDefine(key:String, defaultValue:String = null):haxe.macro.Expr
+	{
+		var value = haxe.macro.Context.definedValue(key);
+		if (value == null)
+			value = defaultValue;
+		return macro $v{value};
+	}
+}