Skip to content

Commit 816159c

Browse files
committed
Add script editing functionality
Still doesn't allow adding classes
1 parent 63eeba4 commit 816159c

14 files changed

Lines changed: 366 additions & 54 deletions

AS3/src/com/cff/anebe/BytecodeEditor.as

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ package com.cff.anebe
99
import flash.external.ExtensionContext;
1010
import flash.utils.ByteArray;
1111
import flash.utils.CompressionAlgorithm;
12+
import com.cff.anebe.ir.ASTrait;
13+
import com.cff.anebe.ir.ASScript;
1214

1315
/**
1416
* The main interface of this library. Allows editing bytecode of passed in SWF files.
@@ -338,11 +340,11 @@ package com.cff.anebe
338340

339341
/**
340342
* Gets a class after partial assembly.
343+
* This is essentially a shortcut for GetScript(name).GetTrait(name).clazz, but is more efficient and should likely be used instead.
341344
* @param className Name of class to be patched. Must be a QName.
342-
* @param idx If there are multiple classes that match the name, disambiguates between them.
343345
* @return The class requested.
344346
*/
345-
public function GetClass(name:ASMultiname, idx:uint):ASClass
347+
public function GetClass(name:ASMultiname):ASClass
346348
{
347349
if (name == null || name.type != ASMultiname.TYPE_QNAME)
348350
{
@@ -365,6 +367,38 @@ package com.cff.anebe
365367
}
366368
}
367369

370+
/**
371+
* Gets a script after partial assembly.
372+
* Scripts are the top-level building block of AS3 programs, but are never referenced in-engine.
373+
* They can contain functions, classes, and variables, and have a single initializer method that sets them up.
374+
* Note that direct editing of script is discouraged; using GetClass is generally a better idea.
375+
* This is mostly provided for the case of top-level-scope functions and variables, which can only be edited as script traits.
376+
* @param identifier Name of an entity in the script. Must be a QName.
377+
* @return The script requested.
378+
*/
379+
public function GetScript(identifier:ASMultiname):ASScript
380+
{
381+
if (identifier == null || !(identifier is ASMultiname))
382+
{
383+
throw new Error("script identifier must be a QName");
384+
}
385+
386+
var ret:Object = extContext.call("GetScript", identifier);
387+
388+
if (ret is String)
389+
{
390+
throw new Error(ret);
391+
}
392+
else if (ret == null || !(ret is ASScript))
393+
{
394+
throw new Error("Unknown error occurred");
395+
}
396+
else
397+
{
398+
return ret as ASScript;
399+
}
400+
}
401+
368402
private function onStatusEvent(e:StatusEvent):void
369403
{
370404
if (e.level == "ERROR")

AS3/src/com/cff/anebe/ir/ASClass.as

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,8 @@ package com.cff.anebe.ir
202202
}
203203

204204
/**
205-
* Gets the static constructor for a class
205+
* Gets the static constructor for a class.
206+
* Will be run when the "when the newclass instruction is executed on the class_info entry for the class".
206207
* @return The ASMethod that represents the static constructor
207208
*/
208209
public function getStaticConstructor():ASMethod
@@ -222,7 +223,8 @@ package com.cff.anebe.ir
222223
}
223224

224225
/**
225-
* Sets the static constructor for a class
226+
* Sets the static constructor for a class.
227+
* Will be run "when the newclass instruction is executed on the class_info entry for the class".
226228
* @param v The ASMethod that represents the new static constructor
227229
*/
228230
public function setStaticConstructor(v:ASMethod):void
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package com.cff.anebe.ir
2+
{
3+
import flash.external.ExtensionContext;
4+
5+
/**
6+
* A representation of an AS3 script, editable at a high level.
7+
* @author Chris
8+
*/
9+
public class ASScript
10+
{
11+
private var context:ExtensionContext;
12+
13+
/**
14+
* This function should not be called by any user of this library. It will be automatically called by BytecodeEditor.GetScript.
15+
*/
16+
public function ASScript()
17+
{
18+
this.context = ExtensionContext.createExtensionContext("com.cff.anebe.ANEBytecodeEditor", "Script");
19+
}
20+
21+
/**
22+
* Gets an instance trait present in the script
23+
* @param name Name of the trait to retrieve
24+
* @param favorSetter If this name refers to both a getter and a setter, true will retrieve the getter, while false will retrieve the setter. If it is neither or only one exists, has no effect.
25+
* @return The trait retrieved, or null if none were found
26+
*/
27+
public function getTrait(name:ASMultiname, favorSetter:Boolean = false):ASTrait
28+
{
29+
if (name == null || name.type != ASMultiname.TYPE_QNAME)
30+
{
31+
throw new ArgumentError("Trait name must be a QName");
32+
}
33+
34+
var ret:Object = context.call("GetTrait", name, favorSetter);
35+
36+
if (ret == null)
37+
{
38+
return null;
39+
}
40+
41+
if (ret is String)
42+
{
43+
throw new Error(ret);
44+
}
45+
46+
return ret as ASTrait;
47+
}
48+
49+
/**
50+
* Sets a trait into the script.
51+
* If a trait does not exist by the name of the new trait, the new trait will be added.
52+
* If a getter exists by the name of the new trait and the new trait is a setter, the new trait will be added.
53+
* If a setter exists by the name of the new trait and the new trait is a getter, the new trait will be added.
54+
* If both a getter and setter exist by the name of the new trait and the new trait is neither a getter nor setter, the new trait will replace both the getter and setter.
55+
* If a trait of the same kind already exists by the name of the new trait, the new trait will replace that trait.
56+
* If a trait exists by the name of the new trait and the kinds do not match any of the above rules, the new trait will replace the original.
57+
* @param value The trait to set or add to the script
58+
*/
59+
public function setTrait(value:ASTrait):void
60+
{
61+
if (value == null)
62+
{
63+
throw new ArgumentError("Cannot set a null trait");
64+
}
65+
if (value.name == null || value.name.type != ASMultiname.TYPE_QNAME)
66+
{
67+
throw new ArgumentError("Trait name must be a QName");
68+
}
69+
70+
var ret:Object = context.call("SetTrait", value);
71+
72+
if (ret is String)
73+
{
74+
throw new Error(ret);
75+
}
76+
if (!(ret is Boolean) || !ret)
77+
{
78+
throw new Error("An unspecified error occurred");
79+
}
80+
}
81+
82+
/**
83+
* Deletes a trait present in the script.
84+
* @param name Name of the trait to retrieve
85+
* @param favorSetter If this name refers to both a getter and a setter, true will delete the getter, while false will delete the setter. If it is neither or only one exists, has no effect.
86+
*/
87+
public function deleteTrait(name:ASMultiname, favorSetter:Boolean = false):void
88+
{
89+
if (name == null || name.type != ASMultiname.TYPE_QNAME)
90+
{
91+
throw new ArgumentError("Trait name must be a QName");
92+
}
93+
94+
var ret:Object = context.call("DeleteTrait", name, favorSetter);
95+
96+
if (ret is String)
97+
{
98+
throw new Error(ret);
99+
}
100+
if (!(ret is Boolean) || !ret)
101+
{
102+
throw new Error("An unspecified error occurred");
103+
}
104+
}
105+
106+
/**
107+
* Gets the initializer for a script.
108+
* Will be run either on program start (for the main script) or when "an entity exported from that script is first referenced by the program during property lookup".
109+
* @return The ASMethod that represents the script initializer
110+
*/
111+
public function getScriptInitializer():ASMethod
112+
{
113+
var ret:Object = context.call("GetInitializer");
114+
115+
if (ret is String)
116+
{
117+
throw new Error(ret);
118+
}
119+
if (!(ret is ASMethod))
120+
{
121+
throw new Error("An unspecified error occurred");
122+
}
123+
124+
return ret as ASMethod;
125+
}
126+
127+
/**
128+
* Sets the initializer for a script.
129+
* Will be run either on program start (for the main script) or when "an entity exported from that script is first referenced by the program during property lookup".
130+
* @param v The ASMethod that represents the new script initializer
131+
*/
132+
public function setScriptInitializer(v:ASMethod):void
133+
{
134+
if (v == null)
135+
{
136+
throw new ArgumentError("Static constructor cannot be set to null");
137+
}
138+
var ret:Object = context.call("SetInitializer", v);
139+
140+
if (ret is String)
141+
{
142+
throw new Error(ret);
143+
}
144+
if (!(ret is Boolean) || !ret)
145+
{
146+
throw new Error("An unspecified error occurred");
147+
}
148+
}
149+
}
150+
}

AS3/src/com/cff/anebe/ir/ASTrait.as

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ package com.cff.anebe.ir
3030
/** For getter, setter, method, or function kinds, the ASMethod that represents the function. Null for others. */
3131
public var funcOrMethod:ASMethod;
3232

33-
/** For class kind, the ASClass that represents the stored class. Null for others. */
33+
/**
34+
* For class kind, the ASClass that represents the stored class. Null for others.
35+
* NOTE: currently, editing an ASClass through its methods changes the C++-side data for it *without* using a wrapper setTrait on this object.
36+
* This is done for efficiency reasons. It is still recommended to perform a setTrait with this object in case this changes in the future.
37+
*/
3438
public var clazz:ASClass;
3539

3640
/** Kind string for slots */

Native/BytecodeEditor/BytecodeEditor.vcxproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
</PrecompiledHeader>
5757
<WarningLevel>Level4</WarningLevel>
5858
<Optimization>Disabled</Optimization>
59-
<PreprocessorDefinitions>USE_BREAKPAD_HANDLER;VERSION_SAFE_STEAM_API_INTERFACES;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
59+
<PreprocessorDefinitions>USE_BREAKPAD_HANDLER;VERSION_SAFE_STEAM_API_INTERFACES;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOGDICAPMASKS;NOVIRTUALKEYCODES;NOWINMESSAGES;NOWINSTYLES;NOSYSMETRICS;NOMENUS;NOICONS;NOKEYSTATES;NOSYSCOMMANDS;NORASTEROPS;NOSHOWWINDOW;OEMRESOURCE;NOATOM;NOCLIPBOARD;NOCOLOR;NOCTLMGR;NODRAWTEXT;NOGDI;NOKERNEL;NOUSER;NONLS;NOMB;NOMEMMGR;NOMETAFILE;NOMINMAX;NOMSG;NOOPENFILE;NOSCROLL;NOSERVICE;NOSOUND;NOTEXTMETRIC;NOWH;NOWINOFFSETS;NOCOMM;NOKANJI;NOHELP;NOPROFILER;NODEFERWINDOWPOS;NOMCX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
6060
<AdditionalIncludeDirectories>$(SolutionDir)\AdobeAIRSDK\include;$(SolutionDir)SteamSDK\public;$(ProjectDir)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
6161
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
6262
<DisableSpecificWarnings>4068</DisableSpecificWarnings>
@@ -76,7 +76,7 @@
7676
<Optimization>MaxSpeed</Optimization>
7777
<FunctionLevelLinking>true</FunctionLevelLinking>
7878
<IntrinsicFunctions>true</IntrinsicFunctions>
79-
<PreprocessorDefinitions>USE_BREAKPAD_HANDLER;VERSION_SAFE_STEAM_API_INTERFACES;WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
79+
<PreprocessorDefinitions>USE_BREAKPAD_HANDLER;VERSION_SAFE_STEAM_API_INTERFACES;WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;NOGDICAPMASKS;NOVIRTUALKEYCODES;NOWINMESSAGES;NOWINSTYLES;NOSYSMETRICS;NOMENUS;NOICONS;NOKEYSTATES;NOSYSCOMMANDS;NORASTEROPS;NOSHOWWINDOW;OEMRESOURCE;NOATOM;NOCLIPBOARD;NOCOLOR;NOCTLMGR;NODRAWTEXT;NOGDI;NOKERNEL;NOUSER;NONLS;NOMB;NOMEMMGR;NOMETAFILE;NOMINMAX;NOMSG;NOOPENFILE;NOSCROLL;NOSERVICE;NOSOUND;NOTEXTMETRIC;NOWH;NOWINOFFSETS;NOCOMM;NOKANJI;NOHELP;NOPROFILER;NODEFERWINDOWPOS;NOMCX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
8080
<AdditionalIncludeDirectories>$(SolutionDir)\AdobeAIRSDK\include;$(ProjectDir)\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
8181
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
8282
<DisableSpecificWarnings>4068</DisableSpecificWarnings>
@@ -109,7 +109,7 @@
109109
<ClCompile Include="source\ANEBytecodeEditorFunctions.cpp" />
110110
<ClCompile Include="source\ASASM\ASProgram.cpp" />
111111
<ClCompile Include="source\ANEBytecodeEditor.cpp" />
112-
<ClCompile Include="source\ASClassFunctions.cpp" />
112+
<ClCompile Include="source\ASTypeFunctions.cpp" />
113113
<ClCompile Include="source\BytecodeEditor.cpp" />
114114
<ClCompile Include="source\utils\ANEUtils.cpp" />
115115
</ItemGroup>

Native/BytecodeEditor/include/ANEBytecodeEditor.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include <windows.h>
4+
35
#include <FlashRuntimeExtensions.h>
46

57
#if defined(WIN32)

Native/BytecodeEditor/include/ANEFunctions.hpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22

33
#include "BytecodeEditor.hpp"
44
#include "utils/ANEUtils.hpp"
5-
#include <FlashRuntimeExtensions.h>
65
#include <stdint.h>
76
#include <unordered_map>
87

9-
inline ASASM::Class* classPointerHelper = nullptr;
8+
#include <windows.h>
9+
10+
#include <FlashRuntimeExtensions.h>
11+
12+
inline ASASM::Class* classPointerHelper = nullptr;
13+
inline ASASM::Script* scriptPointerHelper = nullptr;
1014

1115
template <FREObject (BytecodeEditor::*Assembler)(
1216
std::unordered_map<std::string, std::string>&&, bool)>
@@ -19,6 +23,7 @@ FREObject TransparentZeroArg(FREContext ctx, void* funcData, uint32_t argc, FREO
1923
FREObject SetCurrentSWF(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
2024
FREObject Cleanup(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
2125
FREObject GetClass(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
26+
FREObject GetScript(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
2227

2328
namespace ASClass
2429
{
@@ -54,3 +59,14 @@ namespace ASClass
5459
// Allows converting an FREObject back to a shared_ptr
5560
FREObject ConvertClassHelper(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
5661
}
62+
63+
namespace ASScript
64+
{
65+
// sinit
66+
FREObject GetInitializer(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
67+
FREObject SetInitializer(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
68+
// traits
69+
FREObject GetTrait(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
70+
FREObject SetTrait(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
71+
FREObject DeleteTrait(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
72+
}

Native/BytecodeEditor/include/ANEFunctions.tcc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
#pragma once
22
#include "BytecodeEditor.hpp"
33
#include "utils/ANEUtils.hpp"
4-
#include <FlashRuntimeExtensions.h>
54
#include <stdint.h>
65
#include <unordered_map>
76

7+
#include <windows.h>
8+
9+
#include <FlashRuntimeExtensions.h>
10+
811
#define GET_EDITOR() BytecodeEditor* editor = reinterpret_cast<BytecodeEditor*>(funcData)
912

1013
template <FREObject (BytecodeEditor::*Assembler)(

Native/BytecodeEditor/include/BytecodeEditor.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include "ASASM/ASProgram.hpp"
44
#include "SWF/SWFFile.hpp"
55
#include "utils/ANEUtils.hpp"
6-
#include <FlashRuntimeExtensions.h>
76
#include <memory>
87
#include <optional>
98
#include <string>
@@ -12,6 +11,10 @@
1211
#include <variant>
1312
#include <vector>
1413

14+
#include <windows.h>
15+
16+
#include <FlashRuntimeExtensions.h>
17+
1518
class BytecodeEditor
1619
{
1720
private:
@@ -46,6 +49,7 @@ class BytecodeEditor
4649
FREObject finishAssembleAsync();
4750

4851
ASASM::Class* getClass(const ASASM::Multiname& className) const;
52+
ASASM::Script* getScript(const ASASM::Multiname& trait) const;
4953

5054
void setSWF(SWF::SWFFile&& file) { currentSWF = std::move(file); }
5155

Native/BytecodeEditor/include/utils/ANEUtils.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#pragma once
22

3-
#include <FlashRuntimeExtensions.h>
4-
53
#include "ABC/Error.hpp"
64
#include "ABC/Label.hpp"
75
#include "ASASM/Class.hpp"
@@ -16,6 +14,10 @@
1614
#include <string>
1715
#include <string_view>
1816

17+
#include <windows.h>
18+
19+
#include <FlashRuntimeExtensions.h>
20+
1921
#define DO_OR_FAIL_NULL(x) \
2022
if (x != FRE_OK) \
2123
FAIL_RETURN(nullptr)

0 commit comments

Comments
 (0)