diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53e7ee8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +Native/BytecodeEditor/Release +Native/BytecodeEditor/Debug +Native/BytecodeEditor/AdobeAIRSDK +Native/BytecodeEditor/.vs +Native/BytecodeEditor/.vscode +Native/BytecodeEditor/BytecodeEditor.vcxproj.filters +Native/BytecodeEditor/BytecodeEditor.vcxproj.user +*.swc +*.swf +*.dll +AS3/bin +AS3/obj diff --git a/ANEBytecodeEditor.ane b/ANEBytecodeEditor.ane new file mode 100644 index 0000000..e719347 Binary files /dev/null and b/ANEBytecodeEditor.ane differ diff --git a/AS3/ANEBytecodeEditorSWC.as3proj b/AS3/ANEBytecodeEditorSWC.as3proj new file mode 100644 index 0000000..2da0727 --- /dev/null +++ b/AS3/ANEBytecodeEditorSWC.as3proj @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "$(BaseDir)\Tools\swcbuild\swcbuild.exe" "$(ProjectPath)" "-compiler=$(CompilerPath)" "-debug=$(BuildConfig)" "-library=C:\Program Files (x86)\FlashDevelop\Library" -asdoc=true -keep-asdoc=false + + + + + + + + \ No newline at end of file diff --git a/AS3/src/com/cff/anebe/AssemblyDoneEvent.as b/AS3/src/com/cff/anebe/AssemblyDoneEvent.as new file mode 100644 index 0000000..b1722f4 --- /dev/null +++ b/AS3/src/com/cff/anebe/AssemblyDoneEvent.as @@ -0,0 +1,32 @@ +package com.cff.anebe +{ + import flash.events.Event; + import flash.utils.ByteArray; + + /** + * ... + * @author Chris + */ + public class AssemblyDoneEvent extends Event + { + public var assembled:ByteArray; + + public function AssemblyDoneEvent(assembled:ByteArray, bubbles:Boolean=false, cancelable:Boolean=false) + { + super(Events.ASSEMBLY_DONE, bubbles, cancelable); + this.assembled = assembled; + } + + public override function clone():Event + { + return new AssemblyDoneEvent(assembled, bubbles, cancelable); + } + + public override function toString():String + { + return formatToString("AssemblyDoneEvent", "type", "bubbles", "cancelable", "eventPhase"); + } + + } + +} \ No newline at end of file diff --git a/AS3/src/com/cff/anebe/BytecodeEditor.as b/AS3/src/com/cff/anebe/BytecodeEditor.as new file mode 100644 index 0000000..bea49b3 --- /dev/null +++ b/AS3/src/com/cff/anebe/BytecodeEditor.as @@ -0,0 +1,367 @@ +package com.cff.anebe +{ + import flash.events.EventDispatcher; + import flash.events.StatusEvent; + import flash.external.ExtensionContext; + import flash.utils.ByteArray; + import flash.utils.CompressionAlgorithm; + /** + * ... + * @author Chris + */ + public class BytecodeEditor extends EventDispatcher + { + private var extContext:ExtensionContext; + + public function BytecodeEditor() + { + extContext = ExtensionContext.createExtensionContext("com.cff.anebe.ANEBytecodeEditor", ""); + extContext.addEventListener(StatusEvent.STATUS, this.onStatusEvent); + } + + // Disassembles SWF into a map of file name to file contents + public function Disassemble(swf:ByteArray):Object + { + swf.position = 0; + var decompressor:ByteArray = new ByteArray(); + switch (swf.readUTFBytes(1)) + { + case "F": + swf.position = 0; + break; + case "C": + swf.position = 8; + swf.readBytes(decompressor); + swf.position = 8; + decompressor.uncompress(CompressionAlgorithm.ZLIB); + swf.writeBytes(decompressor); + swf.position = 0; + swf.writeUTFBytes("F"); + swf.position = 0; + decompressor = null; + break; + case "Z": + swf.position = 8; + swf.readBytes(decompressor, 8); + swf.position = 8; + decompressor.uncompress(CompressionAlgorithm.LZMA); + swf.writeBytes(decompressor); + swf.position = 0; + swf.writeUTFBytes("F"); + swf.position = 0; + decompressor = null; + break; + default: + throw new Error("Unrecognized compression scheme for SWF"); + } + + var ret:Object = extContext.call("SetCurrentSWF", swf); + + if (ret == null) + { + throw new Error("Unknown error occurred while setting SWF"); + } + else if (ret is String) + { + throw new Error(ret); + } + else // returned Boolean + { + if (!(ret as Boolean)) + { + throw new Error("SetCurrentSWF returned false somehow"); + } + else + { + ret = extContext.call("Disassemble"); + + if (ret == null) + { + throw new Error("Unknown error occurred"); + } + else if (ret is String) + { + throw new Error(ret); + } + else + { + return ret; + } + } + } + } + + // Assembles an SWF from a map of file name to file contents. If replaceSWF is unspecified, and Disassemble was called, uses + // the last SWF's data for the rest of the SWF. + public function Assemble(strings:Object, replaceSWF:ByteArray = null):ByteArray + { + if (replaceSWF != null) + { + replaceSWF.position = 0; + var decompressor:ByteArray = new ByteArray(); + switch (replaceSWF.readUTFBytes(1)) + { + case "F": + replaceSWF.position = 0; + break; + case "C": + replaceSWF.position = 8; + replaceSWF.readBytes(decompressor); + replaceSWF.position = 8; + decompressor.uncompress(CompressionAlgorithm.ZLIB); + replaceSWF.writeBytes(decompressor); + replaceSWF.position = 0; + replaceSWF.writeUTFBytes("F"); + replaceSWF.position = 0; + decompressor = null; + break; + case "Z": + replaceSWF.position = 8; + replaceSWF.readBytes(decompressor, 8); + replaceSWF.position = 8; + decompressor.uncompress(CompressionAlgorithm.LZMA); + replaceSWF.writeBytes(decompressor); + replaceSWF.position = 0; + replaceSWF.writeUTFBytes("F"); + replaceSWF.position = 0; + decompressor = null; + break; + default: + throw new Error("Unrecognized compression scheme for SWF"); + } + + var ret:Object = extContext.call("SetCurrentSWF", replaceSWF); + + if (ret == null) + { + throw new Error("Unknown error occurred while setting SWF"); + } + else if (ret is String) + { + throw new Error(ret); + } + else // returned Boolean + { + if (!(ret as Boolean)) + { + throw new Error("SetCurrentSWF returned false somehow"); + } + } + } + + var vec:Vector. = new []; + for (var str:String in strings) + { + vec[vec.length] = str; + } + + ret = extContext.call("Assemble", strings, vec); + + if (ret == null) + { + throw new Error("Unknown error occurred"); + } + else if (ret is String) + { + throw new Error(ret); + } + else // byte array was returned + { + return ret as ByteArray; + } + } + + // Disassembles SWF into a map of file name to file contents, asynchronously + public function DisassembleAsync(swf:ByteArray):void + { + swf.position = 0; + var decompressor:ByteArray = new ByteArray(); + switch (swf.readUTFBytes(1)) + { + case "F": + swf.position = 0; + break; + case "C": + swf.position = 8; + swf.readBytes(decompressor); + swf.position = 8; + decompressor.uncompress(CompressionAlgorithm.ZLIB); + swf.writeBytes(decompressor); + swf.position = 0; + swf.writeUTFBytes("F"); + swf.position = 0; + decompressor = null; + break; + case "Z": + swf.position = 8; + swf.readBytes(decompressor, 8); + swf.position = 8; + decompressor.uncompress(CompressionAlgorithm.LZMA); + swf.writeBytes(decompressor); + swf.position = 0; + swf.writeUTFBytes("F"); + swf.position = 0; + decompressor = null; + break; + default: + throw new Error("Unrecognized compression scheme for SWF"); + } + + var ret:Object = extContext.call("SetCurrentSWF", swf); + + if (ret == null) + { + throw new Error("Unknown error occurred while setting SWF"); + } + else if (ret is String) + { + throw new Error(ret); + } + else // returned Boolean + { + if (!(ret as Boolean)) + { + throw new Error("SetCurrentSWF returned false somehow"); + } + else + { + ret = extContext.call("DisassembleAsync"); + + if (ret == null) + { + throw new Error("Unknown error occurred during DisassembleAsync"); + } + else if (ret is String) + { + throw new Error(ret); + } + else // Returned Boolean + { + if (!(ret as Boolean)) + { + throw new Error("DisassembleAsync returned false somehow"); + } + } + } + } + } + + // Assembles an SWF from a map of file name to file contents, asynchronously. If replaceSWF is unspecified, and Disassemble was called, uses + // the last SWF's data for the rest of the SWF. + public function AssembleAsync(strings:Object, replaceSWF:ByteArray = null):void + { + if (replaceSWF != null) + { + replaceSWF.position = 0; + var decompressor:ByteArray = new ByteArray(); + switch (replaceSWF.readUTFBytes(1)) + { + case "F": + replaceSWF.position = 0; + break; + case "C": + replaceSWF.position = 8; + replaceSWF.readBytes(decompressor); + replaceSWF.position = 8; + decompressor.uncompress(CompressionAlgorithm.ZLIB); + replaceSWF.writeBytes(decompressor); + replaceSWF.position = 0; + replaceSWF.writeUTFBytes("F"); + replaceSWF.position = 0; + decompressor = null; + break; + case "Z": + replaceSWF.position = 8; + replaceSWF.readBytes(decompressor, 8); + replaceSWF.position = 8; + decompressor.uncompress(CompressionAlgorithm.LZMA); + replaceSWF.writeBytes(decompressor); + replaceSWF.position = 0; + replaceSWF.writeUTFBytes("F"); + replaceSWF.position = 0; + decompressor = null; + break; + default: + throw new Error("Unrecognized compression scheme for SWF"); + } + + var ret:Object = extContext.call("SetCurrentSWF", replaceSWF); + + if (ret == null) + { + throw new Error("Unknown error occurred while setting SWF"); + } + else if (ret is String) + { + throw new Error(ret); + } + else // returned Boolean + { + if (!(ret as Boolean)) + { + throw new Error("SetCurrentSWF returned false somehow"); + } + } + } + + var vec:Vector. = new []; + for (var str:String in strings) + { + vec[vec.length] = str; + } + + ret = extContext.call("AssembleAsync", strings, vec); + + if (ret == null) + { + throw new Error("Unknown error occurred"); + } + else if (ret is String) + { + throw new Error(ret); + } + else // returned Boolean + { + if (!(ret as Boolean)) + { + throw new Error("AssembleAsync returned false somehow"); + } + } + } + + public function Cleanup():void + { + extContext.call("Cleanup"); + } + + private function onStatusEvent(e:StatusEvent):void + { + if (e.level == "ERROR") + { + var error:Object = extContext.call("AsyncTaskResult"); + if (error is String) + { + throw new Error(error); + } + else + { + throw new Error("Unknown error occurred while performing an ANEByteCodeEditor async task"); + } + } + else + { + var data:Object = extContext.call("AsyncTaskResult"); + + if (data is ByteArray) + { + this.dispatchEvent(new AssemblyDoneEvent(data as ByteArray)); + } + else + { + this.dispatchEvent(new DisassemblyDoneEvent(data)); + } + } + } + } + +} \ No newline at end of file diff --git a/AS3/src/com/cff/anebe/DisassemblyDoneEvent.as b/AS3/src/com/cff/anebe/DisassemblyDoneEvent.as new file mode 100644 index 0000000..3c8d627 --- /dev/null +++ b/AS3/src/com/cff/anebe/DisassemblyDoneEvent.as @@ -0,0 +1,31 @@ +package com.cff.anebe +{ + import flash.events.Event; + + /** + * ... + * @author Chris + */ + public class DisassemblyDoneEvent extends Event + { + public var strings:Object; + public function DisassemblyDoneEvent(strings:Object, bubbles:Boolean=false, cancelable:Boolean=false) + { + super(Events.DISASSEMBLY_DONE, bubbles, cancelable); + + this.strings = strings; + } + + public override function clone():Event + { + return new DisassemblyDoneEvent(strings, bubbles, cancelable); + } + + public override function toString():String + { + return formatToString("DisassemblyDoneEvent", "type", "bubbles", "cancelable", "eventPhase"); + } + + } + +} \ No newline at end of file diff --git a/AS3/src/com/cff/anebe/Events.as b/AS3/src/com/cff/anebe/Events.as new file mode 100644 index 0000000..d3fa439 --- /dev/null +++ b/AS3/src/com/cff/anebe/Events.as @@ -0,0 +1,13 @@ +package com.cff.anebe +{ + /** + * ... + * @author Chris + */ + public final class Events + { + public static const ASSEMBLY_DONE:String = "ASSEMBLY_DONE"; + public static const DISASSEMBLY_DONE:String = "DISASSEMBLY_DONE"; + } + +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4619efa --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Chris Feger + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Native/BytecodeEditor/BytecodeEditor.sln b/Native/BytecodeEditor/BytecodeEditor.sln new file mode 100644 index 0000000..a443173 --- /dev/null +++ b/Native/BytecodeEditor/BytecodeEditor.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BytecodeEditor", "BytecodeEditor.vcxproj", "{76827AD2-1616-45C5-8FE5-58E26AF91B3D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {76827AD2-1616-45C5-8FE5-58E26AF91B3D}.Debug|Win32.ActiveCfg = Debug|Win32 + {76827AD2-1616-45C5-8FE5-58E26AF91B3D}.Debug|Win32.Build.0 = Debug|Win32 + {76827AD2-1616-45C5-8FE5-58E26AF91B3D}.Release|Win32.ActiveCfg = Release|Win32 + {76827AD2-1616-45C5-8FE5-58E26AF91B3D}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Native/BytecodeEditor/BytecodeEditor.vcxproj b/Native/BytecodeEditor/BytecodeEditor.vcxproj new file mode 100644 index 0000000..5f9fa71 --- /dev/null +++ b/Native/BytecodeEditor/BytecodeEditor.vcxproj @@ -0,0 +1,171 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {76827AD2-1616-45C5-8FE5-58E26AF91B3D} + Win32Proj + FRESteamWorks + BytecodeEditor + 10.0 + + + + DynamicLibrary + true + Unicode + v143 + + + DynamicLibrary + false + true + Unicode + v143 + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)\ + AllRules.ruleset + + + false + $(SolutionDir)$(Configuration)\ + + + + + + Level3 + Disabled + USE_BREAKPAD_HANDLER;VERSION_SAFE_STEAM_API_INTERFACES;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + $(SolutionDir)\AdobeAIRSDK\include;$(SolutionDir)SteamSDK\public;$(ProjectDir)\include;%(AdditionalIncludeDirectories) + MultiThreadedDebug + 4068 + + + Windows + true + $(SolutionDir)AdobeAIRSDK\lib\win;$(SolutionDir)SteamSDK\redistributable_bin;%(AdditionalLibraryDirectories) + steam_api.lib;FlashRuntimeExtensions.lib;%(AdditionalDependencies) + + + + + Level3 + + + MaxSpeed + true + true + USE_BREAKPAD_HANDLER;VERSION_SAFE_STEAM_API_INTERFACES;WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + $(SolutionDir)\AdobeAIRSDK\include;$(ProjectDir)\include;%(AdditionalIncludeDirectories) + MultiThreaded + 4068 + + + Windows + true + true + true + $(SolutionDir)AdobeAIRSDK\lib\win;%(AdditionalLibraryDirectories) + FlashRuntimeExtensions.lib;%(AdditionalDependencies) + + + true + + + + + + + + + stdcpp20 + + + stdcpp20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Native/BytecodeEditor/include/ABC/ABCFile.hpp b/Native/BytecodeEditor/include/ABC/ABCFile.hpp new file mode 100644 index 0000000..8d2d688 --- /dev/null +++ b/Native/BytecodeEditor/include/ABC/ABCFile.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "ABC/Class.hpp" +#include "ABC/Instance.hpp" +#include "ABC/Metadata.hpp" +#include "ABC/MethodBody.hpp" +#include "ABC/MethodInfo.hpp" +#include "ABC/Multiname.hpp" +#include "ABC/Namespace.hpp" +#include "ABC/Script.hpp" +#include +#include + +namespace ABC +{ + struct ABCFile + { + uint16_t minorVersion, majorVersion; + std::vector ints; + std::vector uints; + std::vector doubles; + std::vector strings; + std::vector namespaces; + std::vector> namespaceSets; + std::vector multinames; + + std::vector methods; + std::vector metadata; + std::vector instances; + std::vector classes; + std::vector