20 Commits
Author SHA1 Message Date
Vladimir Panteleev 32909f3893 Update Changelog 2013-08-21 21:11:03 +00:00
Vladimir Panteleev 472a9cec57 Add finddef instruction 2013-08-18 08:16:11 +00:00
Vladimir Panteleev 6cbb185114 disassembler: Use std.digest.md
Silences compiler deprecation notice.
2013-08-18 07:58:46 +00:00
Vladimir Panteleev f32f6fb128 abcfile: Add unimplemented {get|set|delete}propertylate instructions
Needed to handle code obfuscated by DoSWF.
Parameter list (empty) guessed judging by {as|is}typelate instructions.

Fixes issue #18.
2013-06-23 06:38:25 +00:00
Vladimir Panteleev d6d7ecf47e swflzmacompress: Add an option to update the SWF version number 2013-06-02 15:27:55 +00:00
Vladimir Panteleev 0cefc587e6 Fix DMD 2.063 slice warnings 2013-03-23 18:53:22 +00:00
Vladimir Panteleev a45b4e2bdc lzma: Work around liblzma dictionary size limitation 2013-03-18 11:25:04 +00:00
Vladimir Panteleev e383172765 lzma: Remove debugging code 2013-03-18 09:35:00 +00:00
Vladimir Panteleev c62b5438d7 Documentation: Update default DCFLAGS 2013-03-18 09:28:27 +00:00
Vladimir Panteleev a1a214ae72 build_rabcdasm: Allow overriding program list / build flags on command line 2013-03-18 09:28:09 +00:00
Vladimir Panteleev 222fa1109c Documentation: Update download link 2012-12-26 13:48:53 +02:00
Vladimir Panteleev 9fa2d122fa disassembler: Further improve refname generation by adding context priority 2012-12-02 12:06:06 +02:00
Vladimir Panteleev 93adc953c2 disassembler: Separate class and instance ref context 2012-12-02 12:05:34 +02:00
Vladimir Panteleev dc222f9e43 disassembler: Relax trait multiname kind restrictions 2012-12-01 14:05:33 +02:00
Vladimir Panteleev fb2f96b81a disassembler: Visit instance protected namespace 2012-12-01 14:01:19 +02:00
Vladimir Panteleev 14f5e669ee disassembler: Visit slot types 2012-11-27 15:23:24 +02:00
Vladimir Panteleev 4e3d43df5f 64-bit fixes 2012-09-29 14:06:39 +02:00
Vladimir Panteleev c1d6b29d94 Work around apparent liblzma bugs 2012-09-29 14:06:06 +02:00
Vladimir Panteleev 8fc7253bba Fix some warnings and -property compliance 2012-09-29 14:18:56 +03:00
Vladimir Panteleev ac2bde2550 Fix handling of truncated tags 2012-09-29 14:08:33 +03:00
12 changed files with 269 additions and 128 deletions
+16
View File
@@ -1,6 +1,22 @@
RABCDAsm Changelog
==================
RABCDAsm v1.14 (2013.08.21)
---------------------------
* Improved refid generation
* Worked around liblzma dictionary size limitation, which prevented
decompression of some LZMA-compressed files
* Added an option to `swflzmacompress` to update the SWF version number
* Added `finddef` instruction
* Added unimplemented `{get|set|delete}propertylate` instructions
* Documentation updates
RABCDAsm v1.13 (2012.09.29)
---------------------------
* Fixed handling of truncated SWF tags
RABCDAsm v1.12 (2012.09.08)
---------------------------
+5 -5
View File
@@ -69,7 +69,7 @@ installed, compiling should be as straight-forward as:
Substitute `dmd` with `gdmd` if you're using gdc. You can use the `DC` and
`DCFLAGS` environment variables to override the detected compiler and default
compilation flags (`-w -O -inline`).
compilation flags (`-O -inline`).
To be able to manipulate SWF files packed with LZMA compression, you'll need
to have the liblzma library and development files installed on your system.
@@ -82,11 +82,11 @@ to have the liblzma library and development files installed on your system.
Pre-compiled binaries
---------------------
You can find pre-compiled Windows binaries in the [Downloads section on
GitHub][downloads]. However, please don't expect them to be up-to-date with
the latest source versions.
You can find pre-compiled Windows binaries on [my website][downloads].
However, please don't expect them to be up-to-date with the latest source
versions.
[downloads]: http://github.com/CyberShadow/RABCDAsm/downloads
[downloads]: http://files.thecybershadow.net/RABCDAsm/
Usage
-----
+5 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -754,7 +754,7 @@ const OpcodeInfo[256] opcodeInfo = [
/* 0x5C */ {"0x5C", [OpcodeArgumentType.Unknown]},
/* 0x5D */ {"findpropstrict", [OpcodeArgumentType.Multiname]},
/* 0x5E */ {"findproperty", [OpcodeArgumentType.Multiname]},
/* 0x5F */ {"finddef", [OpcodeArgumentType.Unknown]},
/* 0x5F */ {"finddef", [OpcodeArgumentType.Multiname]},
/* 0x60 */ {"getlex", [OpcodeArgumentType.Multiname]},
/* 0x61 */ {"setproperty", [OpcodeArgumentType.Multiname]},
/* 0x62 */ {"getlocal", [OpcodeArgumentType.UIntLiteral]},
@@ -762,11 +762,11 @@ const OpcodeInfo[256] opcodeInfo = [
/* 0x64 */ {"getglobalscope", []},
/* 0x65 */ {"getscopeobject", [OpcodeArgumentType.UByteLiteral]},
/* 0x66 */ {"getproperty", [OpcodeArgumentType.Multiname]},
/* 0x67 */ {"getpropertylate", [OpcodeArgumentType.Unknown]},
/* 0x67 */ {"getpropertylate", []},
/* 0x68 */ {"initproperty", [OpcodeArgumentType.Multiname]},
/* 0x69 */ {"setpropertylate", [OpcodeArgumentType.Unknown]},
/* 0x69 */ {"setpropertylate", []},
/* 0x6A */ {"deleteproperty", [OpcodeArgumentType.Multiname]},
/* 0x6B */ {"deletepropertylate", [OpcodeArgumentType.Unknown]},
/* 0x6B */ {"deletepropertylate", []},
/* 0x6C */ {"getslot", [OpcodeArgumentType.UIntLiteral]},
/* 0x6D */ {"setslot", [OpcodeArgumentType.UIntLiteral]},
/* 0x6E */ {"getglobalslot", [OpcodeArgumentType.UIntLiteral]},
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -41,6 +41,7 @@ void main(string[] args)
while (*p++) {} // skip name
tag.data = tag.data[0..p-tag.data.ptr] ~ abc;
}
tag.length = cast(uint)tag.data.length;
write(args[1], swf.write());
return;
}
+1 -1
View File
@@ -668,7 +668,7 @@ private final class ABCtoAS
postConvertMultiname(multiname, multinames[i]);
methods.length = methodAdded.length = abc.methods.length;
foreach (i, ref method; abc.methods)
foreach (uint i, ref method; abc.methods)
methods[i] = convertMethod(method, i);
metadata.length = abc.metadata.length;
+11 -11
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -67,7 +67,7 @@ final class Assembler
return File(name, data, data.ptr, data.ptr + data.length, arguments, basePath);
}
Position position()
@property Position position()
{
Position p;
p.filename = filename;
@@ -75,7 +75,7 @@ final class Assembler
return p;
}
string positionStr()
@property string positionStr()
{
auto lines = splitLines(buf);
foreach (i, line; lines)
@@ -104,7 +104,7 @@ final class Assembler
foreach (ref c; buf)
if (c == '\\')
c = '/';
return buildPath(getBasePath, buf);
return buildPath(getBasePath(), buf);
}
void skipWhitespace()
@@ -266,7 +266,7 @@ final class Assembler
{
if (fileCount == files.length)
throw new Exception("Recursion limit exceeded");
files[1..fileCount+1] = files[0..fileCount].dup;
files[1..fileCount+1] = files[0..fileCount].dup[];
fileCount++;
files[0] = file;
}
@@ -284,7 +284,7 @@ final class Assembler
if (fileCount==1)
throw new Exception("Unexpected end of file");
fileCount--;
files[0..fileCount] = files[1..fileCount+1].dup;
files[0..fileCount] = files[1..fileCount+1].dup[];
}
void expectWord(string expected)
@@ -573,7 +573,7 @@ final class Assembler
if (pindex)
n.id = *pindex;
else
n.id = namespaceLabels[name] = namespaceLabels.length+1;
n.id = namespaceLabels[name] = cast(uint)namespaceLabels.length+1;
}
expectChar(')');
return n;
@@ -1100,13 +1100,13 @@ final class Assembler
foreach (ref f; jumpFixups)
{
scope(failure) setFile(f.where.load);
scope(failure) setFile(f.where.load());
instructions[f.ii].arguments[f.ai].jumpTarget = parseLabel(f.name, labels);
}
foreach (ref f; switchFixups)
{
scope(failure) setFile(f.where.load);
scope(failure) setFile(f.where.load());
instructions[f.ii].arguments[f.ai].switchTargets[f.si] = parseLabel(f.name, labels);
}
@@ -1211,7 +1211,7 @@ final class Assembler
auto cp = f.name in classesByID;
if (cp is null)
{
setFile(f.where.load);
setFile(f.where.load());
throw new Exception("Unknown class refid: " ~ f.name);
}
*f.ptr = *cp;
@@ -1225,7 +1225,7 @@ final class Assembler
auto mp = f.name in methodsByID;
if (mp is null)
{
setFile(f.where.load);
setFile(f.where.load());
throw new Exception("Unknown method refid: " ~ f.name);
}
*f.ptr = *mp;
+15 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -18,6 +18,7 @@
/// A simple tool to build RABCDAsm in one command.
/// You can use the DC and DCFLAGS environment variables to override the detected compiler and compilation flags.
/// You can also pass program names or compilation options on the command-line to override the default ones.
module build_rabcdasm;
@@ -59,10 +60,12 @@ void test(string code, string extraFlags=null)
stderr.writeln(" >>> OK");
}
int main()
int main(string[] args)
{
try
{
auto programs = ["rabcasm", "rabcdasm", "abcexport", "abcreplace", "swfbinexport", "swfbinreplace", "swfdecompress", "swf7zcompress"];
compiler = getenv("DC");
if (compiler is null)
compiler = DEFAULT_COMPILER;
@@ -71,6 +74,15 @@ int main()
if (flags is null)
flags = DEFAULT_FLAGS;
string[] optionArgs, programArgs;
foreach (arg; args[1..$])
(arg.startsWith("-") ? optionArgs : programArgs) ~= arg;
if (optionArgs.length)
flags = optionArgs.join(" ");
if (programArgs.length)
programs = programArgs;
stderr.writeln("* Checking for working compiler...");
test(`
void main() {}
@@ -103,7 +115,7 @@ int main()
if (haveLZMA)
flags ~= " " ~ LZMA_FLAGS;
foreach (program; ["rabcasm", "rabcdasm", "abcexport", "abcreplace", "swfbinexport", "swfbinreplace", "swfdecompress", "swf7zcompress"])
foreach (program; programs)
compile(program);
if (haveLZMA)
+137 -78
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -25,7 +25,7 @@ import std.conv;
import std.exception;
import std.algorithm;
import std.path;
import std.md5;
import std.digest.md;
import abcfile;
import asprogram;
import autodata;
@@ -53,7 +53,7 @@ final class StringBuilder
auto end = pos + s.length;
while (buf.length < end)
buf.length = buf.length*2;
buf[pos..end] = s;
buf[pos..end] = s[];
pos = end;
}
@@ -202,7 +202,7 @@ final class RefBuilder : ASTraitsVisitor
assert(0);
}
case Type.String:
return [Segment(filenameSuffix ? '.' : '/', str)];
return [Segment((filename && filenameSuffix) ? '.' : '/', str)];
case Type.Group:
{
Segment[] segments;
@@ -257,18 +257,32 @@ final class RefBuilder : ASTraitsVisitor
final switch (type)
{
case Type.Multiname:
auto ns = multiname.vQName.ns;
if (ns.kind == ASType.PrivateNamespace)
switch (multiname.kind)
{
// auto pcontext = ns.id in refs.namespaces[ns.kind].contexts;
// if (pcontext is null)
// return (&this)[0..1];
// assert(pcontext);
auto context = refs.namespaces[ns.kind].getContext(refs, ns.id);
debug(CONTEXTS) std.stdio.writefln("Context of namespace %s is:\n\t%s\n", ns, context);
// auto expanded = expand(refs, context);
// if (expanded is null) return null;
return /*expanded*/context ~ (multiname.vQName.name.length ? [ContextItem(multiname.vQName.name)] : null); // hack
case ASType.QName:
{
auto ns = multiname.vQName.ns;
if (ns.kind == ASType.PrivateNamespace)
{
// auto pcontext = ns.id in refs.namespaces[ns.kind].contexts;
// if (pcontext is null)
// return (&this)[0..1];
// assert(pcontext);
auto context = refs.namespaces[ns.kind].getContext(refs, ns.id);
debug(CONTEXTS) std.stdio.writefln("Context of namespace %s is:\n\t%s\n", ns, context);
// auto expanded = expand(refs, context);
// if (expanded is null) return null;
return /*expanded*/context ~ (multiname.vQName.name.length ? [ContextItem(multiname.vQName.name)] : null); // hack
}
break;
}
case ASType.Multiname:
return multiname.vMultiname.name.length ? [ContextItem(multiname.vMultiname.name)] : null;
default:
debug
assert(false, text(multiname.kind));
else
break;
}
break;
case Type.String:
@@ -332,6 +346,7 @@ final class RefBuilder : ASTraitsVisitor
case ContextItem.Type.String:
return i1.str == i2.str;
case ContextItem.Type.Multiname:
assert(i1.multiname.kind == ASType.QName && i2.multiname.kind == ASType.QName);
if (i1.multiname.vQName.name != i2.multiname.vQName.name) return false;
return nsSimilar(i1.multiname.vQName.ns, i2.multiname.vQName.ns);
case ContextItem.Type.Group:
@@ -389,11 +404,15 @@ final class RefBuilder : ASTraitsVisitor
if (nsSimilar(ns1, ns2))
{
assert(name1 != name2); // handled by similar() check
assert(!truncate); // handled above
if (name2.length)
return [c1, ContextItem(name2)];
static if (truncate)
assert(false); // handled above
else
return [c1];
{
if (name2.length)
return [c1, ContextItem(name2)];
else
return [c1];
}
}
if (ns1.name.length > ns2.name.length && truncate)
@@ -421,16 +440,24 @@ final class RefBuilder : ASTraitsVisitor
void pushContext(T...)(T v) { context ~= ContextItem(v); }
void popContext() { context = context[0..$-1]; }
enum ContextPriority
{
declaration,
usage,
orphan,
max
}
struct ContextSet(T, bool ALLOW_DUPLICATES)
{
ContextItem[][T] contexts;
ContextItem[][][T] contextSets;
ContextItem[][][ContextPriority.max][T] contextSets;
debug bool contextsSealed;
string[T] names, filenames;
debug bool coagulated;
bool add(U)(U obj, ContextItem[] context)
bool add(U)(U obj, ContextItem[] context, ContextPriority priority)
{
debug assert(!coagulated);
debug assert(!contextsSealed);
@@ -440,23 +467,24 @@ final class RefBuilder : ASTraitsVisitor
if (!pset)
{
contextSets[p] ~= context.dup;
contextSets[p] = contextSets[p].init;
contextSets[p][priority] ~= context.dup;
return true;
}
else
{
if ((*pset)[$-1] != context)
*pset ~= context.dup;
if ((*pset)[priority].length==0 || (*pset)[priority][$-1] != context) // Optimization: don't add contexts identical to the last added
(*pset)[priority] ~= context.dup;
return false;
}
}
bool addIfNew(U)(U obj, ContextItem[] context)
bool addIfNew(U)(U obj, ContextItem[] context, ContextPriority priority)
{
if (isAdded(obj))
return false;
else
return add(obj, context);
return add(obj, context, priority);
}
void coagulate(RefBuilder refs)
@@ -509,7 +537,10 @@ final class RefBuilder : ASTraitsVisitor
if (pcontext)
return *pcontext;
auto set = contextSets[p];
ContextItem[][] set;
foreach (prioritySet; contextSets[p])
if (prioritySet)
set = prioritySet;
static if (ALLOW_DUPLICATES)
{
@@ -593,31 +624,33 @@ final class RefBuilder : ASTraitsVisitor
ContextItem[] classContexts;
foreach (trait; v.traits)
if (trait.name.vQName.ns.kind != ASType.PrivateNamespace)
{
if (trait.name.kind == ASType.QName && trait.name.vQName.ns.kind != ASType.PrivateNamespace)
classContexts ~= ContextItem(trait.name);
}
if (!classContexts.length)
foreach (trait; v.traits)
classContexts ~= ContextItem(trait.name);
context = [ContextItem(classContexts, "script_" ~ to!string(i))];
scripts.add(v, context);
pushContext("sinit", true);
addMethod(v.sinit);
scripts.add(v, context, ContextPriority.declaration);
pushContext("init", true);
addMethod(v.sinit, ContextPriority.declaration);
context = null;
}
foreach (i, vclass; as.orphanClasses)
if (!objects.isAdded(vclass))
{
pushContext("orphan_class_" ~ to!string(i));
addClass(vclass);
addClass(vclass, ContextPriority.orphan);
popContext();
}
foreach (i, method; as.orphanMethods)
if (!objects.isAdded(method))
{
pushContext("orphan_method_" ~ to!string(i));
addMethod(method);
addMethod(method, ContextPriority.orphan);
popContext();
}
@@ -627,13 +660,13 @@ final class RefBuilder : ASTraitsVisitor
foreach (v; as.scripts)
foreach (trait; v.traits)
if (trait.name.kind == ASType.QName)
namespaces[trait.name.vQName.ns.kind].addIfNew(trait.name.vQName.ns.id, scripts.getContext(this, v));
namespaces[trait.name.vQName.ns.kind].addIfNew(trait.name.vQName.ns.id, scripts.getContext(this, v), ContextPriority.declaration);
foreach (id, b; possibleOrphanPrivateNamespaces)
if (!namespaces[ASType.PrivateNamespace].isAdded(id))
{
pushContext("orphan_namespace_" ~ to!string(id));
namespaces[ASType.PrivateNamespace].add(id, context);
namespaces[ASType.PrivateNamespace].add(id, context, ContextPriority.orphan);
popContext();
}
@@ -647,36 +680,59 @@ final class RefBuilder : ASTraitsVisitor
{
auto m = trait.name;
if (m.kind != ASType.QName)
throw new Exception("Trait name is not a QName");
// if (m.kind != ASType.QName)
// throw new Exception("Trait name is not a QName");
pushContext(m);
visitMultiname(m);
visitMultiname(m, ContextPriority.declaration);
switch (trait.kind)
{
case TraitKind.Slot:
case TraitKind.Const:
visitMultiname(trait.vSlot.typeName, ContextPriority.usage);
super.visitTrait(trait);
break;
case TraitKind.Class:
addClass(trait.vClass.vclass);
addClass(trait.vClass.vclass, ContextPriority.declaration);
pushContext("class", true);
visitTraits(trait.vClass.vclass.traits);
popContext();
pushContext("instance", true);
visitTraits(trait.vClass.vclass.instance.traits);
popContext();
break;
case TraitKind.Function:
addMethod(trait.vFunction.vfunction);
addMethod(trait.vFunction.vfunction, ContextPriority.declaration);
super.visitTrait(trait);
break;
case TraitKind.Method:
addMethod(trait.vMethod.vmethod);
addMethod(trait.vMethod.vmethod, ContextPriority.declaration);
super.visitTrait(trait);
break;
case TraitKind.Getter:
pushContext("getter");
addMethod(trait.vMethod.vmethod);
addMethod(trait.vMethod.vmethod, ContextPriority.declaration);
popContext();
super.visitTrait(trait);
break;
case TraitKind.Setter:
pushContext("setter");
addMethod(trait.vMethod.vmethod);
addMethod(trait.vMethod.vmethod, ContextPriority.declaration);
popContext();
super.visitTrait(trait);
break;
default:
super.visitTrait(trait);
break;
}
super.visitTrait(trait);
popContext();
}
@@ -712,7 +768,7 @@ final class RefBuilder : ASTraitsVisitor
bool[uint] possibleOrphanPrivateNamespaces;
void visitNamespace(ASProgram.Namespace ns)
void visitNamespace(ASProgram.Namespace ns, ContextPriority priority)
{
if (ns is null) return;
@@ -726,7 +782,7 @@ final class RefBuilder : ASTraitsVisitor
auto myPos = context.length;
foreach (i, ref item; context)
if (item.type == ContextItem.Type.Multiname && item.multiname.vQName.ns == ns)
if (item.type == ContextItem.Type.Multiname && item.multiname.kind == ASType.QName && item.multiname.vQName.ns == ns)
{
myPos = i;
break;
@@ -738,16 +794,16 @@ final class RefBuilder : ASTraitsVisitor
}
auto myContext = context[0..myPos].dup;
namespaces[ns.kind].add(ns.id, myContext);
namespaces[ns.kind].add(ns.id, myContext, priority);
}
void visitNamespaceSet(ASProgram.Namespace[] nsSet)
void visitNamespaceSet(ASProgram.Namespace[] nsSet, ContextPriority priority)
{
foreach (ns; nsSet)
visitNamespace(ns);
visitNamespace(ns, priority);
}
void visitMultiname(ASProgram.Multiname m)
void visitMultiname(ASProgram.Multiname m, ContextPriority priority)
{
if (m is null) return;
with (m)
@@ -755,20 +811,20 @@ final class RefBuilder : ASTraitsVisitor
{
case ASType.QName:
case ASType.QNameA:
visitNamespace(vQName.ns);
visitNamespace(vQName.ns, priority);
break;
case ASType.Multiname:
case ASType.MultinameA:
visitNamespaceSet(vMultiname.nsSet);
visitNamespaceSet(vMultiname.nsSet, priority);
break;
case ASType.MultinameL:
case ASType.MultinameLA:
visitNamespaceSet(vMultinameL.nsSet);
visitNamespaceSet(vMultinameL.nsSet, priority);
break;
case ASType.TypeName:
visitMultiname(vTypeName.name);
visitMultiname(vTypeName.name, priority);
foreach (param; vTypeName.params)
visitMultiname(param);
visitMultiname(param, priority);
break;
default:
break;
@@ -782,21 +838,21 @@ final class RefBuilder : ASTraitsVisitor
switch (type)
{
case OpcodeArgumentType.Namespace:
visitNamespace(instruction.arguments[i].namespacev);
visitNamespace(instruction.arguments[i].namespacev, ContextPriority.usage);
break;
case OpcodeArgumentType.Multiname:
visitMultiname(instruction.arguments[i].multinamev);
visitMultiname(instruction.arguments[i].multinamev, ContextPriority.usage);
break;
case OpcodeArgumentType.Class:
pushContext("inline_class");
if (isOrphan(instruction.arguments[i].classv))
addClass(instruction.arguments[i].classv);
addClass(instruction.arguments[i].classv, ContextPriority.usage);
popContext();
break;
case OpcodeArgumentType.Method:
pushContext("inline_method");
if (isOrphan(instruction.arguments[i].methodv))
addMethod(instruction.arguments[i].methodv);
addMethod(instruction.arguments[i].methodv, ContextPriority.usage);
popContext();
break;
default:
@@ -863,7 +919,7 @@ final class RefBuilder : ASTraitsVisitor
}
if (pathSegment.length > 240)
pathSegment = pathSegment[0..200] ~ '-' ~ getDigestString(pathSegment);
pathSegment = assumeUnique(pathSegment[0..200] ~ '-' ~ toHexString(md5Of(pathSegment)));
}
return arrayJoin(pathSegments, "/");
@@ -876,35 +932,38 @@ final class RefBuilder : ASTraitsVisitor
return arrayJoin(strings);
}
bool addObject(T)(T obj) { return objects.add(obj, context); }
bool addObject(T)(T obj, ContextPriority priority) { return objects.add(obj, context, priority); }
void addClass(ASProgram.Class vclass)
void addClass(ASProgram.Class vclass, ContextPriority priority)
{
addObject(vclass);
addObject(vclass, priority);
pushContext("cinit");
addMethod(vclass.cinit);
popContext();
pushContext("class", true);
pushContext("init", true);
addMethod(vclass.cinit, ContextPriority.declaration);
popContext(); // init
popContext(); // class
pushContext("iinit");
addMethod(vclass.instance.iinit);
popContext();
pushContext("instance", true);
pushContext("init", true);
addMethod(vclass.instance.iinit, ContextPriority.declaration);
popContext(); // init
pushContext("instance");
visitMultiname(vclass.instance.name);
visitMultiname(vclass.instance.superName);
visitMultiname(vclass.instance.name, ContextPriority.declaration);
visitMultiname(vclass.instance.superName, ContextPriority.usage);
visitNamespace(vclass.instance.protectedNs, ContextPriority.declaration);
foreach (iface; vclass.instance.interfaces)
visitMultiname(iface);
popContext();
visitMultiname(iface, ContextPriority.usage);
popContext(); // instance
}
void addMethod(ASProgram.Method method)
void addMethod(ASProgram.Method method, ContextPriority priority)
{
if (addObject(method))
if (addObject(method, priority))
{
foreach (paramType; method.paramTypes)
visitMultiname(paramType);
visitMultiname(method.returnType);
visitMultiname(paramType, ContextPriority.usage);
visitMultiname(method.returnType, ContextPriority.usage);
if (method.vbody)
visitMethodBody(method.vbody);
}
+31 -15
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -40,9 +40,9 @@ static assert(LZMAHeader.sizeof == 13);
ubyte[] lzmaDecompress(LZMAHeader header, in ubyte[] compressedData)
{
enforce(header.decompressedSize > 0, "Decompression with unknown size is unsupported");
enforce(header.decompressedSize > 0, "Decompression with unknown size is unsupported");
lzma_stream strm;
lzma_stream strm;
lzmaEnforce(lzma_alone_decoder(&strm, ulong.max), "lzma_alone_decoder");
scope(exit) lzma_end(&strm);
@@ -54,15 +54,16 @@ ubyte[] lzmaDecompress(LZMAHeader header, in ubyte[] compressedData)
{
strm.next_in = chunk.ptr;
strm.avail_in = chunk.length;
lzmaEnforce(lzma_code(&strm, lzma_action.LZMA_RUN), "lzma_code");
lzmaEnforce!true(lzma_code(&strm, lzma_action.LZMA_RUN), "lzma_code (LZMA_RUN)");
enforce(strm.avail_in == 0, "Not all data was read");
}
header.decompressedSize = -1; // Required as Flash uses End-of-Stream marker
fixDictSize(header.dictionarySize);
decompress(cast(ubyte[])(&header)[0..1]);
decompress(compressedData);
lzmaEnforce(lzma_code(&strm, lzma_action.LZMA_FINISH), "lzma_code");
lzmaEnforce!true(lzma_code(&strm, lzma_action.LZMA_FINISH), "lzma_code (LZMA_FINISH)");
enforce(strm.avail_out == 0, "Decompressed size mismatch");
@@ -71,30 +72,45 @@ ubyte[] lzmaDecompress(LZMAHeader header, in ubyte[] compressedData)
ubyte[] lzmaCompress(in ubyte[] decompressedData, LZMAHeader* header)
{
lzma_options_lzma opts;
enforce(lzma_lzma_preset(&opts, 9 | LZMA_PRESET_EXTREME) == false, "lzma_lzma_preset error");
lzma_options_lzma opts;
enforce(lzma_lzma_preset(&opts, 9 | LZMA_PRESET_EXTREME) == false, "lzma_lzma_preset error");
lzma_stream strm;
lzma_stream strm;
lzmaEnforce(lzma_alone_encoder(&strm, &opts), "lzma_alone_encoder");
scope(exit) lzma_end(&strm);
auto outBuf = new ubyte[decompressedData.length];
auto outBuf = new ubyte[decompressedData.length + 1024];
strm.next_out = outBuf.ptr;
strm.avail_out = outBuf.length;
strm.next_in = decompressedData.ptr;
strm.avail_in = decompressedData.length;
lzmaEnforce(lzma_code(&strm, lzma_action.LZMA_RUN), "lzma_code");
scope(failure) { import std.stdio; writeln("avail_in=", strm.avail_in); }
lzmaEnforce(lzma_code(&strm, lzma_action.LZMA_RUN), "lzma_code (LZMA_RUN)");
enforce(strm.avail_in == 0, "Not all data was read");
enforce(strm.avail_out != 0, "Ran out of compression space");
lzmaEnforce(lzma_code(&strm, lzma_action.LZMA_FINISH), "lzma_code");
lzmaEnforce!true(lzma_code(&strm, lzma_action.LZMA_FINISH), "lzma_code (LZMA_FINISH)");
*header = *cast(LZMAHeader*)outBuf.ptr;
return outBuf[LZMAHeader.sizeof..to!size_t(strm.total_out)];
}
private void lzmaEnforce(lzma_ret v, string f)
private void lzmaEnforce(bool STREAM_END_OK=false)(lzma_ret v, string f)
{
if (v != lzma_ret.LZMA_OK && v != lzma_ret.LZMA_STREAM_END)
throw new Exception(text(f, " error: ", v));
if (v != lzma_ret.LZMA_OK && (!STREAM_END_OK || v != lzma_ret.LZMA_STREAM_END))
throw new Exception(text(f, " error: ", v));
}
/// Work around an artificial lzma_alone_decoder limitation in liblzma
/// which prevents it from accepting any streams with a dictionary size
/// that is not 2^n or 2^n + 2^(n-1).
/// See xz\src\liblzma\common\alone_decoder.c (git commit e7b424d267), line 87
private void fixDictSize(ref uint d)
{
--d;
d |= d >> 2;
d |= d >> 3;
d |= d >> 4;
d |= d >> 8;
d |= d >> 16;
++d;
}
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -33,6 +33,7 @@ void main(string[] args)
{
auto bin = cast(ubyte[])read(args[3]);
tag.data = tag.data[0..6] ~ bin;
tag.length = cast(uint)tag.data.length;
write(args[1], swf.write());
return;
}
+9 -5
View File
@@ -65,6 +65,7 @@ final class SWFFile
{
ushort type;
ubyte[] data;
uint length; // may be >data.length if file is truncated
bool forceLongLength;
}
@@ -129,10 +130,12 @@ private final class SWFReader
pos += raw.length;
}
/// May read less than len on EOF
void[] readRaw(size_t len)
{
auto data = buf[pos..pos+len];
pos += len;
auto end = pos+len;
auto data = buf[pos..end<$?end:$];
pos = end;
return data;
}
@@ -174,6 +177,7 @@ private final class SWFReader
if (length < 0x3F)
t.forceLongLength = true;
}
t.length = length;
t.data = cast(ubyte[])readRaw(length);
return t;
}
@@ -263,16 +267,16 @@ private final class SWFWriter
foreach (ref tag; swf.tags)
{
ushort u = cast(ushort)(tag.type << 6);
if (tag.data.length < 0x3F && !tag.forceLongLength)
if (tag.length < 0x3F && !tag.forceLongLength)
{
u |= tag.data.length;
u |= tag.length;
buf ~= toArray(u);
}
else
{
u |= 0x3F;
buf ~= toArray(u);
uint l = to!uint(tag.data.length);
uint l = to!uint(tag.length);
buf ~= toArray(l);
}
buf ~= tag.data;
+35 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2010, 2011, 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* Copyright 2010, 2011, 2012, 2013 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
@@ -18,18 +18,50 @@
module swflzmacompress;
import std.exception;
import std.file;
import std.getopt;
import std.string;
import swffile;
void main(string[] args)
{
bool force, updateVersion;
getopt(args,
"--force", &force,
"--update-version", &updateVersion,
);
if (args.length == 1)
throw new Exception("No file specified");
enum MIN_LZMA_VER = 13;
foreach (arg; args[1..$])
{
auto swf = SWFFile.read(cast(ubyte[])read(arg));
if (swf.header.signature[0] == cast(ubyte)'Z')
throw new Exception("Already LZMA-compressed");
enforce(swf.header.signature[0] != cast(ubyte)'Z', "Already LZMA-compressed");
if (swf.header.ver < MIN_LZMA_VER)
{
if (updateVersion)
{
if (swf.header.ver < 8 && !force)
throw new Exception(format(
"SWF version %d has different file format than version %d, " ~
"required for LZMA. Resulting file may not work. " ~
"Use --force to override and update version anyway.",
swf.header.ver, MIN_LZMA_VER
));
swf.header.ver = MIN_LZMA_VER;
}
else
if (!force)
throw new Exception(format(
"SWF version %d is too old to support SWF LZMA compression, " ~
"which requires version %d. " ~
"Use --update-version to update the version number, " ~
"or --force to compress anyway without updating it.",
swf.header.ver, MIN_LZMA_VER
));
}
swf.header.signature[0] = cast(ubyte)'Z'; // LZMA
write(arg, swf.write());
}