<commit_before>package format.swf.exporters;

import flash.display.BitmapData;
import flash.text.TextFormatAlign;
import flash.utils.ByteArray;
import format.png.Data;
import format.png.Writer;
import format.swf.data.consts.BitmapFormat;
import format.swf.data.consts.BlendMode;
import format.swf.data.SWFButtonRecord;
import openfl._internal.formats.swf.FilterType;
import openfl._internal.formats.swf.ShapeCommand;
import format.swf.instance.Bitmap;
#if hxp
import hxp.Log;
#else
import lime.tools.helpers.LogHelper in Log;
#end
import lime.graphics.Image;
import openfl._internal.symbols.BitmapSymbol;
import openfl._internal.symbols.ButtonSymbol;
import openfl._internal.symbols.DynamicTextSymbol;
import openfl._internal.symbols.FontSymbol;
import openfl._internal.symbols.ShapeSymbol;
import openfl._internal.symbols.SpriteSymbol;
import openfl._internal.symbols.StaticTextSymbol;
import openfl._internal.symbols.SWFSymbol;
import openfl._internal.symbols.timeline.Frame;
import openfl._internal.symbols.timeline.FrameObject;
import openfl._internal.symbols.timeline.FrameObjectType;
import openfl._internal.formats.swf.SWFLite;
import format.swf.tags.IDefinitionTag;
import format.swf.tags.TagDefineBits;
import format.swf.tags.TagDefineBitsJPEG2;
import format.swf.tags.TagDefineBitsJPEG3;
import format.swf.tags.TagDefineBitsLossless;
import format.swf.tags.TagDefineButton;
import format.swf.tags.TagDefineButton2;
import format.swf.tags.TagDefineEditText;
import format.swf.tags.TagDefineFont;
import format.swf.tags.TagDefineFont2;
import format.swf.tags.TagDefineFont4;
import format.swf.tags.TagDefineShape;
import format.swf.tags.TagDefineSprite;
import format.swf.tags.TagDefineText;
import format.swf.tags.TagPlaceObject;
import format.swf.tags.TagSymbolClass;
import format.swf.SWFRoot;
import format.swf.SWFTimelineContainer;
import haxe.io.Bytes;
import haxe.io.BytesOutput;
import openfl.display.PNGEncoderOptions;
import format.abc.Data;
import format.abc.Data.ABCData;
import format.abc.Data.Name;
import format.swf.tags.TagDefineSound;

using StringTools;
using format.swf.exporters.SWFLiteExporter.AVM2;

class SWFLiteExporter
{
	public var bitmapAlpha:Map<Int, ByteArray>;
	public var bitmaps:Map<Int, ByteArray>;
	public var bitmapTypes:Map<Int, BitmapType>;
	public var sounds:Map<Int, ByteArray>;
	public var soundTypes:Map<Int, SoundType>;
	public var soundSymbolClassNames:Map<Int, String>;
	public var filterClasses:Map<String, Bool>;
	public var swfLite:SWFLite;

	private var alphaPalette:Bytes;
	private var data:SWFRoot;

	public function new(data:SWFRoot)
	{
		this.data = data;

		bitmapAlpha = new Map<Int, ByteArray>();
		bitmaps = new Map<Int, ByteArray>();
		bitmapTypes = new Map<Int, BitmapType>();
		sounds = new Map<Int, ByteArray>();
		soundTypes = new Map<Int, SoundType>();
		soundSymbolClassNames = new Map<Int, String>();
		filterClasses = new Map<String, Bool>();

		swfLite = new SWFLite();
		swfLite.frameRate = data.frameRate;

		addSprite(data, true);

		for (tag in data.tags)
		{
			if (Std.is(tag, TagSymbolClass))
			{
				for (symbol in cast(tag, TagSymbolClass).symbols)
				{
					processSymbol(symbol);
				}
			}
		}
	}

	private function addButton(tag:IDefinitionTag):SWFSymbol
	{
		var symbol = new ButtonSymbol();

		if (Std.is(tag, IDefinitionTag))
		{
			symbol.id = untyped tag.characterId;
		}

		var processRecords = function(records:Array<SWFButtonRecord>)
		{
			if (records.length > 0)
			{
				var sprite = new SpriteSymbol();
				var frame = new Frame();
				frame.objects = [];

				for (i in 0...records.length)
				{
					var object = records[i];

					var frameObject = new FrameObject();
					frameObject.type = FrameObjectType.CREATE;
					frameObject.symbol = object.characterId;
					frameObject.id = i;

					processTag(cast data.getCharacter(object.characterId));

					if (object.placeMatrix != null)
					{
						var matrix = object.placeMatrix.matrix;
						matrix.tx *= (1 / 20);
						matrix.ty *= (1 / 20);

						frameObject.matrix = matrix;
					}

					if (object.colorTransform != null)
					{
						frameObject.colorTransform = object.colorTransform.colorTransform;
					}

					if (object.hasBlendMode)
					{
						var blendMode = BlendMode.toString(object.blendMode);
						frameObject.blendMode = blendMode;
					}

					if (object.hasFilterList)
					{
						var filters:Array<FilterType> = [];

						for (filter in object.filterList)
						{
							var type = filter.type;

							if (type != null)
							{
								filters.push(filter.type);
								// filterClasses.set (Type.getClassName (Type.getClass (surfaceFilter.filter)), true);
							}
						}

						frameObject.filters = filters;
					}

					frameObject.depth = i;
					frameObject.clipDepth = 0;
					frameObject.visible = true;

					frame.objects.push(frameObject);
				}

				sprite.frames.push(frame);

				return sprite;
			}

			return null;
		}

		if (Std.is(tag, TagDefineButton))
		{
			var defineButton:TagDefineButton = cast tag;

			symbol.downState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_DOWN));
			symbol.hitState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_HIT));
			symbol.overState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_OVER));
			symbol.upState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_UP));
		}
		else
		{
			var defineButton:TagDefineButton2 = cast tag;

			symbol.downState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_DOWN));
			symbol.hitState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_HIT));
			symbol.overState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_OVER));
			symbol.upState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_UP));
		}

		swfLite.symbols.set(symbol.id, symbol);

		return symbol;
	}

	private function addBitmap(tag:IDefinitionTag):BitmapSymbol
	{
		var alphaByteArray = null;
		var byteArray = null;
		var type = null;

		if (Std.is(tag, TagDefineBitsLossless))
		{
			var data:TagDefineBitsLossless = cast tag;

			var transparent = (data.level > 1);
			var buffer = data.zlibBitmapData;
			buffer.uncompress();
			buffer.position = 0;

			if (data.bitmapFormat == BitmapFormat.BIT_8)
			{
				var palette = Bytes.alloc(data.bitmapColorTableSize * 3);
				var alpha = null;

				if (transparent) alpha = Bytes.alloc(data.bitmapColorTableSize);
				var index = 0;

				for (i in 0...data.bitmapColorTableSize)
				{
					palette.set(index++, buffer.readUnsignedByte());
					palette.set(index++, buffer.readUnsignedByte());
					palette.set(index++, buffer.readUnsignedByte());
					if (transparent) alpha.set(i, buffer.readUnsignedByte());
				}

				var paddedWidth:Int = Math.ceil(data.bitmapWidth / 4) * 4;
				var values = Bytes.alloc((data.bitmapWidth + 1) * data.bitmapHeight);
				index = 0;

				for (y in 0...data.bitmapHeight)
				{
					values.set(index++, 0);
					values.blit(index, buffer, buffer.position, data.bitmapWidth);
					index += data.bitmapWidth;
					buffer.position += paddedWidth;
				}

				var png = new List();
				png.add(CHeader(
					{
						width: data.bitmapWidth,
						height: data.bitmapHeight,
						colbits: 8,
						color: ColIndexed,
						interlaced: false
					}));
				png.add(CPalette(palette));
				if (transparent) png.add(CUnknown("tRNS", alpha));
				var valuesBA:ByteArray = values;
				valuesBA.deflate();
				png.add(CData(valuesBA));
				png.add(CEnd);

				var output = new BytesOutput();
				var writer = new Writer(output);
				writer.write(png);

				byteArray = ByteArray.fromBytes(output.getBytes());
				type = BitmapType.PNG;
			}
			else
			{
				var bitmapData = new BitmapData(data.bitmapWidth, data.bitmapHeight, transparent);

				bitmapData.image.buffer.premultiplied = false;
				bitmapData.setPixels(bitmapData.rect, buffer);
				bitmapData.image.buffer.premultiplied = true;
				bitmapData.image.premultiplied = false;

				byteArray = bitmapData.encode(bitmapData.rect, new PNGEncoderOptions());
				type = BitmapType.PNG;
			}
		}
		else if (Std.is(tag, TagDefineBitsJPEG2))
		{
			var data:TagDefineBitsJPEG2 = cast tag;

			if (Std.is(tag, TagDefineBitsJPEG3))<commit_msg>Fixing an issue with SWFLiteExporter where it wouldn't compress PNG IDAT chunk, outputting corrupted PNG files<commit_after>package format.swf.exporters;

import flash.display.BitmapData;
import flash.text.TextFormatAlign;
import flash.utils.ByteArray;
import format.png.Data;
import format.png.Writer;
import format.swf.data.consts.BitmapFormat;
import format.swf.data.consts.BlendMode;
import format.swf.data.SWFButtonRecord;
import openfl._internal.formats.swf.FilterType;
import openfl._internal.formats.swf.ShapeCommand;
import format.swf.instance.Bitmap;
#if hxp
import hxp.Log;
#else
import lime.tools.helpers.LogHelper in Log;
#end
import lime.graphics.Image;
import openfl._internal.symbols.BitmapSymbol;
import openfl._internal.symbols.ButtonSymbol;
import openfl._internal.symbols.DynamicTextSymbol;
import openfl._internal.symbols.FontSymbol;
import openfl._internal.symbols.ShapeSymbol;
import openfl._internal.symbols.SpriteSymbol;
import openfl._internal.symbols.StaticTextSymbol;
import openfl._internal.symbols.SWFSymbol;
import openfl._internal.symbols.timeline.Frame;
import openfl._internal.symbols.timeline.FrameObject;
import openfl._internal.symbols.timeline.FrameObjectType;
import openfl._internal.formats.swf.SWFLite;
import format.swf.tags.IDefinitionTag;
import format.swf.tags.TagDefineBits;
import format.swf.tags.TagDefineBitsJPEG2;
import format.swf.tags.TagDefineBitsJPEG3;
import format.swf.tags.TagDefineBitsLossless;
import format.swf.tags.TagDefineButton;
import format.swf.tags.TagDefineButton2;
import format.swf.tags.TagDefineEditText;
import format.swf.tags.TagDefineFont;
import format.swf.tags.TagDefineFont2;
import format.swf.tags.TagDefineFont4;
import format.swf.tags.TagDefineShape;
import format.swf.tags.TagDefineSprite;
import format.swf.tags.TagDefineText;
import format.swf.tags.TagPlaceObject;
import format.swf.tags.TagSymbolClass;
import format.swf.SWFRoot;
import format.swf.SWFTimelineContainer;
import haxe.io.Bytes;
import haxe.io.BytesOutput;
import openfl.display.PNGEncoderOptions;
import format.abc.Data;
import format.abc.Data.ABCData;
import format.abc.Data.Name;
import format.swf.tags.TagDefineSound;

using StringTools;
using format.swf.exporters.SWFLiteExporter.AVM2;

class SWFLiteExporter
{
	public var bitmapAlpha:Map<Int, ByteArray>;
	public var bitmaps:Map<Int, ByteArray>;
	public var bitmapTypes:Map<Int, BitmapType>;
	public var sounds:Map<Int, ByteArray>;
	public var soundTypes:Map<Int, SoundType>;
	public var soundSymbolClassNames:Map<Int, String>;
	public var filterClasses:Map<String, Bool>;
	public var swfLite:SWFLite;

	private var alphaPalette:Bytes;
	private var data:SWFRoot;

	public function new(data:SWFRoot)
	{
		this.data = data;

		bitmapAlpha = new Map<Int, ByteArray>();
		bitmaps = new Map<Int, ByteArray>();
		bitmapTypes = new Map<Int, BitmapType>();
		sounds = new Map<Int, ByteArray>();
		soundTypes = new Map<Int, SoundType>();
		soundSymbolClassNames = new Map<Int, String>();
		filterClasses = new Map<String, Bool>();

		swfLite = new SWFLite();
		swfLite.frameRate = data.frameRate;

		addSprite(data, true);

		for (tag in data.tags)
		{
			if (Std.is(tag, TagSymbolClass))
			{
				for (symbol in cast(tag, TagSymbolClass).symbols)
				{
					processSymbol(symbol);
				}
			}
		}
	}

	private function addButton(tag:IDefinitionTag):SWFSymbol
	{
		var symbol = new ButtonSymbol();

		if (Std.is(tag, IDefinitionTag))
		{
			symbol.id = untyped tag.characterId;
		}

		var processRecords = function(records:Array<SWFButtonRecord>)
		{
			if (records.length > 0)
			{
				var sprite = new SpriteSymbol();
				var frame = new Frame();
				frame.objects = [];

				for (i in 0...records.length)
				{
					var object = records[i];

					var frameObject = new FrameObject();
					frameObject.type = FrameObjectType.CREATE;
					frameObject.symbol = object.characterId;
					frameObject.id = i;

					processTag(cast data.getCharacter(object.characterId));

					if (object.placeMatrix != null)
					{
						var matrix = object.placeMatrix.matrix;
						matrix.tx *= (1 / 20);
						matrix.ty *= (1 / 20);

						frameObject.matrix = matrix;
					}

					if (object.colorTransform != null)
					{
						frameObject.colorTransform = object.colorTransform.colorTransform;
					}

					if (object.hasBlendMode)
					{
						var blendMode = BlendMode.toString(object.blendMode);
						frameObject.blendMode = blendMode;
					}

					if (object.hasFilterList)
					{
						var filters:Array<FilterType> = [];

						for (filter in object.filterList)
						{
							var type = filter.type;

							if (type != null)
							{
								filters.push(filter.type);
								// filterClasses.set (Type.getClassName (Type.getClass (surfaceFilter.filter)), true);
							}
						}

						frameObject.filters = filters;
					}

					frameObject.depth = i;
					frameObject.clipDepth = 0;
					frameObject.visible = true;

					frame.objects.push(frameObject);
				}

				sprite.frames.push(frame);

				return sprite;
			}

			return null;
		}

		if (Std.is(tag, TagDefineButton))
		{
			var defineButton:TagDefineButton = cast tag;

			symbol.downState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_DOWN));
			symbol.hitState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_HIT));
			symbol.overState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_OVER));
			symbol.upState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_UP));
		}
		else
		{
			var defineButton:TagDefineButton2 = cast tag;

			symbol.downState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_DOWN));
			symbol.hitState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_HIT));
			symbol.overState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_OVER));
			symbol.upState = processRecords(defineButton.getRecordsByState(TagDefineButton.STATE_UP));
		}

		swfLite.symbols.set(symbol.id, symbol);

		return symbol;
	}

	private function addBitmap(tag:IDefinitionTag):BitmapSymbol
	{
		var alphaByteArray = null;
		var byteArray = null;
		var type = null;

		if (Std.is(tag, TagDefineBitsLossless))
		{
			var data:TagDefineBitsLossless = cast tag;

			var transparent = (data.level > 1);
			var buffer = data.zlibBitmapData;
			buffer.uncompress();
			buffer.position = 0;

			if (data.bitmapFormat == BitmapFormat.BIT_8)
			{
				var palette = Bytes.alloc(data.bitmapColorTableSize * 3);
				var alpha = null;

				if (transparent) alpha = Bytes.alloc(data.bitmapColorTableSize);
				var index = 0;

				for (i in 0...data.bitmapColorTableSize)
				{
					palette.set(index++, buffer.readUnsignedByte());
					palette.set(index++, buffer.readUnsignedByte());
					palette.set(index++, buffer.readUnsignedByte());
					if (transparent) alpha.set(i, buffer.readUnsignedByte());
				}

				var paddedWidth:Int = Math.ceil(data.bitmapWidth / 4) * 4;
				var values = Bytes.alloc((data.bitmapWidth + 1) * data.bitmapHeight);
				index = 0;

				for (y in 0...data.bitmapHeight)
				{
					values.set(index++, 0);
					values.blit(index, buffer, buffer.position, data.bitmapWidth);
					index += data.bitmapWidth;
					buffer.position += paddedWidth;
				}

				var png = new List();
				png.add(CHeader(
					{
						width: data.bitmapWidth,
						height: data.bitmapHeight,
						colbits: 8,
						color: ColIndexed,
						interlaced: false
					}));
				png.add(CPalette(palette));
				if (transparent) png.add(CUnknown("tRNS", alpha));
				var valuesBA:ByteArray = values;
				valuesBA.compress();
				png.add(CData(valuesBA));
				png.add(CEnd);

				var output = new BytesOutput();
				var writer = new Writer(output);
				writer.write(png);

				byteArray = ByteArray.fromBytes(output.getBytes());
				type = BitmapType.PNG;
			}
			else
			{
				var bitmapData = new BitmapData(data.bitmapWidth, data.bitmapHeight, transparent);

				bitmapData.image.buffer.premultiplied = false;
				bitmapData.setPixels(bitmapData.rect, buffer);
				bitmapData.image.buffer.premultiplied = true;
				bitmapData.image.premultiplied = false;

				byteArray = bitmapData.encode(bitmapData.rect, new PNGEncoderOptions());
				type = BitmapType.PNG;
			}
		}
		else if (Std.is(tag, TagDefineBitsJPEG2))
		{
			var data:TagDefineBitsJPEG2 = cast tag;

			if (Std.is(tag, TagDefineBitsJPEG3))
{'fraction_non_alphanumeric': 0.11471082313414926, 'fraction_numerical': 0.003839692824574034, 'mean_word_length': 3.984748803827751, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4433401', 'n_tokens_mistral': 6549, 'n_tokens_neox': 5388, 'n_words': 1018}