56 Commits
Author SHA1 Message Date
Vladimir Panteleev 3c5101f6c0 Update Changelog 2012-03-15 02:55:03 +02:00
Vladimir Panteleev c2042660f2 Fix metadata handling (caused by error in ABC format specification) 2012-03-15 02:47:48 +02:00
Vladimir Panteleev 57d05c6c35 disassembler: Use Windows filename logic on all platforms
This is to allow files disassembled on one platform to
work on other platforms.
2012-03-15 02:23:44 +02:00
Vladimir Panteleev 532700df03 disassembler: Use more Windows-friendly filenames 2012-03-15 02:21:26 +02:00
Vladimir Panteleev fe9b49bfd3 Use UNC paths on Windows to avoid the MAX_PATH limitation 2012-03-15 01:35:26 +02:00
Vladimir Panteleev e7b65d6d50 Handle empty contexts 2012-03-15 01:12:27 +02:00
Vladimir Panteleev a304843b5b Improve handling of out-of-range signed integers 2012-03-15 01:00:32 +02:00
Vladimir Panteleev ebe6639e8a Update asasm.hrc 2012-03-15 00:39:15 +02:00
Vladimir Panteleev 09be72ea9c Improve refid generation for protected namespaces 2012-03-15 00:39:08 +02:00
Vladimir Panteleev 151bf87038 Fix unintentional hierarchy flattening 2012-03-07 05:09:27 +02:00
Vladimir Panteleev 950f16fd23 Update Changelog 2012-02-29 14:01:22 +02:00
Vladimir Panteleev 0dd9dcdb2f Escape empty path segments 2012-02-29 13:56:37 +02:00
Vladimir Panteleev 5e69e44c17 Fix compilation with latest DMD 2012-02-27 21:54:51 +02:00
Vladimir Panteleev 15d20019d5 Update Changelog 2012-02-07 04:40:36 +02:00
Vladimir Panteleev b0795bfcde Fix handling of overflown integers 2012-02-06 08:26:04 +02:00
Vladimir Panteleev bd93ae35d7 disassembler: Discover private namespaces through class ancestry 2012-02-06 08:01:28 +02:00
Vladimir Panteleev e9bf251777 Documentation updates 2012-02-06 07:33:45 +02:00
Vladimir Panteleev de68fea1f1 swffile: 64-bit fix 2012-02-06 06:36:13 +02:00
Vladimir Panteleev 03c02cbc4a build_rabcdasm: Minor visual tweak 2012-02-06 06:29:30 +02:00
Vladimir Panteleev fa08188324 Documentation: mention liblzma 2012-02-06 06:24:01 +02:00
Vladimir Panteleev 9278ae9910 Update build_rabcdasm, allow building without LZMA 2012-02-06 06:21:46 +02:00
Vladimir Panteleev e479fbc25a Add swflzmacompress 2012-02-06 05:15:07 +02:00
Vladimir Panteleev 10464de2a2 Add LZMA compression 2012-02-06 04:50:22 +02:00
Vladimir Panteleev b371fb5bec Add LZMA decompression 2012-02-06 04:14:08 +02:00
Vladimir Panteleev 216beb2edd Fix build instructions for gdmd 2012-01-02 17:53:27 +02:00
Vladimir Panteleev b67c0f8751 AStoABC refactoring 2011-10-03 15:23:46 +03:00
Vladimir Panteleev 8fe2227446 Fix DMD 2.055 deprecation warnings 2011-10-02 18:48:48 +03:00
Vladimir Panteleev 1f0ef64b83 64-bit fixes 2011-07-25 02:12:22 +03:00
Vladimir Panteleev 701e5b6f80 build_rabcdasm: build with warnings 2011-07-25 02:11:15 +03:00
Vladimir Panteleev fc46ad02e9 Documentation: fix abcreplace example 2011-07-13 19:22:45 +03:00
Vladimir Panteleev e7a5dd92c6 Fix DMD 2.054 deprecation warnings 2011-07-11 19:47:32 +03:00
Vladimir Panteleev 352456d257 Update Changelog 2011-07-06 14:25:14 +03:00
Vladimir Panteleev d6544ae7cb Documentation: add link to FiddlerAutoCapture 2011-06-23 01:31:17 +03:00
Vladimir Panteleev 37cd4b415b disassembler: fixed handling of orphan methods with multiple references 2011-06-16 13:02:59 +03:00
Vladimir Panteleev 62d0756dbc asprogram: treat out-of-bounds namespace/multiname indices as null 2011-06-16 01:00:36 +03:00
Vladimir Panteleev 48b221535f disassembler: apply "blank line" change for si32-like instructions as well 2011-06-14 15:23:32 +03:00
Vladimir Panteleev beb7229bf8 Update Changelog 2011-06-14 15:15:21 +03:00
Vladimir Panteleev 296196ff71 Add support for out-of-bounds class/method indices 2011-06-12 18:57:01 +03:00
Vladimir Panteleev 170e7d6a8b Revert "abcfile: validate constant indices when reading bytecode"
This reverts commit 51536e4add.
2011-06-12 18:14:37 +03:00
Vladimir Panteleev 2c63eb592a disassembler: track private namespaces referenced only at script level 2011-06-12 18:09:19 +03:00
Vladimir Panteleev 16a5f649de disassembler: add support for orphan private namespaces 2011-06-12 18:08:45 +03:00
Vladimir Panteleev 111cd75eb4 disassembler: fix handling of null multinames 2011-06-12 16:35:45 +03:00
Vladimir Panteleev e103048af6 abcfile: Fix handling of exception info in corrupted method bodies 2011-06-12 16:21:06 +03:00
Vladimir Panteleev 61df70dd0e autodata: use to!string instead of format in AutoToString, for enum names 2011-06-12 14:43:52 +03:00
Vladimir Panteleev 51536e4add abcfile: validate constant indices when reading bytecode 2011-06-12 10:57:09 +03:00
Vladimir Panteleev fb5c03ada5 Update documentation 2011-06-12 10:40:15 +03:00
Vladimir Panteleev 2226b0059f disassembler: improve double shortest representation code 2011-06-12 10:32:05 +03:00
Vladimir Panteleev 69cf64b85c abcexport: delimit base file name and index with a dash 2011-06-10 18:51:33 +03:00
Vladimir Panteleev 2b49e77267 Fix handling of null namespaces 2011-06-10 18:44:10 +03:00
Vladimir Panteleev fab198a648 disassembler: add newlines after si32 as well 2011-06-10 14:21:32 +03:00
Vladimir Panteleev 4e4fd2b56d disassembler: externalize script-level methods, too 2011-06-10 13:24:57 +03:00
Vladimir Panteleev 7e4f7f5bac disassembler: dump classes to own file, use second-level extensions 2011-06-10 13:15:20 +03:00
Vladimir Panteleev 7a92e9fe81 asprogram: fix compiler warning 2011-06-10 12:36:25 +03:00
Vladimir Panteleev 41002f7085 Disassembler: fix handling of malformed UTF-8 2011-06-09 17:32:24 +03:00
Vladimir Panteleev 3b131fa932 Documentation: add a note about swfbinexport and swfbinreplace usage 2011-06-09 14:55:44 +03:00
Vladimir Panteleev 34316bd4c9 Documentation: fix typos 2011-05-26 15:39:01 +03:00
36 changed files with 5287 additions and 594 deletions
+127 -81
View File
@@ -1,81 +1,127 @@
RABCDAsm Changelog
==================
RABCDAsm v1.6 (2011.05.13)
--------------------------
* Overhaul of the structure of disassembler-generated files
* Private namespace names and `refid`s for nested classes are now generated
from their referenced contexts
* This also fixes problems caused by private namespaces being referenced
in multiple distinct contexts
* One `#include`d file per script instead of per class
* Nested classes, script initializers and other script traits are now in
the same file as their main class
* Inline methods are now in separate files, placed according to their usage
context
* Private namespaces are sorted by their autogenerated names in the
`.privatens.asasm` file
* No syntax changes, output is backwards-compatible
* Improved escaping of file names
* Output correct opcode error locations
* Disassembler will now refuse to overwrite files
* `rabcdasm` will now always generate output in the same directory as the
input file
* Failure to decode a method body is now not a fatal error
* Aggressively try to shorten double literals to their shortest representation
* Added the `swfbinexport` and `swfbinreplace` utilities, to aid in manipulating
contents of `DefineBinaryData` tags
* The package can now be built using a simple, dedicated build tool
* Documentation updates
RABCDAsm v1.5 (2011.04.14)
--------------------------
* Fixed v1.4 constant pool regression
* Added support for memory-access and sign-extend opcodes
* Speed optimizations
* Documentation updates
RABCDAsm v1.4 (2011.03.07)
--------------------------
* Source code ported to D2
* Add support for forward-references for TypeName-kind Multinames
* Correctly order classes by dependencies (extends/implements) and reference
count
* Finish Metadata support
* Documentation updates
RABCDAsm v1.3 (2010.11.11)
--------------------------
* Fixed double precision problem
* This also fixes problems with illegal default values for function
parameters (default values for integer parameters are stored as doubles,
which might become out-of-range due to inadequate double precision)
* Added Changelog
* Documentation markdown fixes
RABCDAsm v1.2 (2010.11.06)
--------------------------
* Fixed ref generation for orphan objects which were only referenced
by other orphans
* Better error handling in `abcexport`; warn when no DoABC tags found
* Documentation updates
RABCDAsm v1.1 (2010.06.30)
--------------------------
* Private namespaces are now referenced by auto-generated names
* Use `:` to delimit namespace and name in QNames for consistency
* Fixed relative include paths
* Add optional byte offsets to labels, which allows lossless representation
of jumps inside instructions and outside the function bounds
* Documentation updates
RABCDAsm v1.0 (2010.05.05)
--------------------------
* Initial release.
RABCDAsm Changelog
==================
RABCDAsm v1.11 (2012.03.15)
---------------------------
* Fixed v1.10 hierarchy flattening regression
* Improved refid generation for protected namespaces
* Improved handling of overflown signed integers
* Fixed metadata handling (caused by error in ABC format specification)
* Removed path length limitation on Windows
* Improved filename sanitization
* Updated asasm.hrc
RABCDAsm v1.10 (2012.02.29)
---------------------------
* Fixed escaping of paths with empty segments
RABCDAsm v1.9 (2012.02.07)
--------------------------
* Added support for LZMA-compressed SWF files
* Added swflzmacompress
* Fixed undiscovered private namespaces in class interfaces
* Fixed handling of overflown integers
* Documentation updates
RABCDAsm v1.8 (2011.07.06)
--------------------------
* Fixed handling of orphan methods with multiple references
* Treat out-of-bounds namespace/multiname indices as null
* Documentation updates
RABCDAsm v1.7 (2011.06.14)
--------------------------
* Fixed handling of invalid UTF-8 in strings
* Fixed handling of null namespaces and multinames
* Fixed handling of exception info in corrupted method bodies
* Fixed handling of orphan private namespaces
* Fixed handling of out-of-bounds class/method indices
* Disassembler now creates one file per class and script-level method
* Disassembler now outputs blank lines after `si32` etc. instructions as well
* `abcexport` now delimits the index with a dash in output filenames
* Improved shortening of `double` literals
* Documentation updates
RABCDAsm v1.6 (2011.05.13)
--------------------------
* Overhaul of the structure of disassembler-generated files
* Private namespace names and `refid`s for nested classes are now generated
from their referenced contexts
* This also fixes problems caused by private namespaces being referenced
in multiple distinct contexts
* One `#include`d file per script instead of per class
* Nested classes, script initializers and other script traits are now in
the same file as their main class
* Inline methods are now in separate files, placed according to their usage
context
* Private namespaces are sorted by their autogenerated names in the
`.privatens.asasm` file
* No syntax changes, output is backwards-compatible
* Improved escaping of file names
* Output correct opcode error locations
* Disassembler will now refuse to overwrite files
* `rabcdasm` will now always generate output in the same directory as the
input file
* Failure to decode a method body is now not a fatal error
* Aggressively try to shorten double literals to their shortest representation
* Added the `swfbinexport` and `swfbinreplace` utilities, to aid in manipulating
contents of `DefineBinaryData` tags
* The package can now be built using a simple, dedicated build tool
* Documentation updates
RABCDAsm v1.5 (2011.04.14)
--------------------------
* Fixed v1.4 constant pool regression
* Added support for memory-access and sign-extend opcodes
* Speed optimizations
* Documentation updates
RABCDAsm v1.4 (2011.03.07)
--------------------------
* Source code ported to D2
* Add support for forward-references for TypeName-kind Multinames
* Correctly order classes by dependencies (extends/implements) and reference
count
* Finish Metadata support
* Documentation updates
RABCDAsm v1.3 (2010.11.11)
--------------------------
* Fixed double precision problem
* This also fixes problems with illegal default values for function
parameters (default values for integer parameters are stored as doubles,
which might become out-of-range due to inadequate double precision)
* Added Changelog
* Documentation markdown fixes
RABCDAsm v1.2 (2010.11.06)
--------------------------
* Fixed ref generation for orphan objects which were only referenced
by other orphans
* Better error handling in `abcexport`; warn when no DoABC tags found
* Documentation updates
RABCDAsm v1.1 (2010.06.30)
--------------------------
* Private namespaces are now referenced by auto-generated names
* Use `:` to delimit namespace and name in QNames for consistency
* Fixed relative include paths
* Add optional byte offsets to labels, which allows lossless representation
of jumps inside instructions and outside the function bounds
* Documentation updates
RABCDAsm v1.0 (2010.05.05)
--------------------------
* Initial release.
+37 -18
View File
@@ -11,18 +11,23 @@ These are:
* `abcreplace` - replaces ABC in SWF files
* `swfdecompress` - decompresses zlib-compressed SWF files
* `swf7zcompress` - (re-)compress the contents of a SWF using 7-Zip
* `swflzmacompress` - compress the contents of a SWF using LZMA
* `swfbinexport` / `swfbinreplace` - extract/replace contents of binary
data tags from SWF files
`abcexport` and `abcreplace` are reimplementations of similar utilities from
my [swfutilsex][] Java package, however these work faster as they do not parse
the SWF files as deeply.
`swfdecompress` is ancilliary and is only useful for debugging and studying of
`swfdecompress` is ancillary and is only useful for debugging and studying of
the SWF file format, and not required for ABC manipulation. It is functionally
equivalent to [flasm][]'s `-x` option.
equivalent to [flasm][]'s `-x` option. If you frequently work on compressed
SWF files, you may want to decompress them to speed processing up.
`swf7zcompress` is an utility to further reduce the size of SWF files. It uses
[7-Zip][] to compress the data better than the standard zlib library would. It
requires that the `7z` command-line program be installed and in `PATH`.
requires that the `7z` command-line program be installed and in `PATH`.
`swflzmacompress` compresses SWF files using the [LZMA][] algorithm, support
for which was introduced in Flash 11. It will only work with SWF files with
version 13 or higher.
`swfbinexport` and `swfbinreplace` aid in the manipulation of
`DefineBinaryData` tags in SWF files (some files may contain nested SWF files
stored in these tags).
@@ -31,6 +36,7 @@ stored in these tags).
[swfutilsex]: http://github.com/CyberShadow/swfutilsex
[flasm]: http://flasm.sourceforge.net/
[7-Zip]: http://www.7-zip.org/
[LZMA]: http://en.wikipedia.org/wiki/Lempel-Ziv-Markov_chain_algorithm
Motivation and goals
--------------------
@@ -60,13 +66,16 @@ installed, compiling should be as straight-forward as:
git clone git://github.com/CyberShadow/RABCDAsm.git
cd RABCDAsm
dmd -run build_rabcdasm
dmd -run build_rabcdasm.d
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 (`-O -inline`).
compilation flags (`-w -O -inline`).
[d2]: http://www.digitalmars.com/d/2.0/
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.
[d2]: http://dlang.org/
[dmd]: http://www.digitalmars.com/d/download.html
[gdc]: http://bitbucket.org/goshawk/gdc/
[git]: http://git-scm.com/
@@ -87,25 +96,29 @@ To begin hacking on a SWF file:
abcexport file.swf
This will create `file0.abc` ... `fileN.abc` (often just `file0.abc`). Each
This will create `file-0.abc` ... `file-N.abc` (often just `file-0.abc`). Each
file corresponds to an ABC block inside the SWF file.
To disassemble one of the `.abc` files:
rabcdasm file0.abc
rabcdasm file-0.abc
This will create a `file0` directory, which will contain `file0.main.asasm`
(the main program file), `file0.privatens.asasm` (private namespace alias
definitions) and a file per ActionScript class.
This will create a `file-0` directory, which will contain `file-0.main.asasm`
(the main program file), `file-0.privatens.asasm` (private namespace alias
definitions), and files for ActionScript scripts, classes, and orphan and
script-level methods.
To assemble the `.asasm` files back, and update the SWF file:
rabcasm file0/file0.main.asasm
abcreplace file0.swf 0 file0/file0.main.abc
rabcasm file-0/file-0.main.asasm
abcreplace file.swf 0 file-0/file-0.main.abc
The second `abcreplace` argument represents the index of the ABC block in the
SWF file, and corresponds to the number in the filename created by `abcexport`.
`swfbinexport` and `swfbinreplace` are used in the same manner as `abcexport`
and `abcreplace`.
Syntax
======
@@ -247,7 +260,7 @@ namespace. Internally (the ABC file format), private namespaces are
distinguished by a numerical index - `rabcdasm` will attempt to give them
descriptive names based on their context. Aliases can be defined using the
`#privatens` directive. `rabcdasm` will create a separate file containing the
aliases (`file0.privatens.asasm`).
aliases (`file-0.privatens.asasm`).
Strings have a syntax similar to C string literals. Strings start and end with
a `"`. Supported escape sequences (a backslash followed by a letter) are `\n`
@@ -291,6 +304,7 @@ Directives start with a `#`, followed by a word identifying the directive:
variable *word*.
* `#unset` *word* - deletes the variable *word*.
* `#privatens` defines a private namespace alias, as described above.
* `#version` specifies the version of the disassembly.
### Variables
@@ -393,7 +407,7 @@ RABCDAsm users.
[debug Flash Player]: http://www.adobe.com/support/flashplayer/downloads.html
3. The [Fiddler][] Web Debugging Proxy can be very useful for analysing
3. The [Fiddler][] Web Debugging Proxy can be very useful for analyzing
websites with SWF content. The following script fragment (which is to be
placed in the `OnBeforeResponse` function) will automatically save all SWF
files while preserving the directory structure.
@@ -412,11 +426,15 @@ RABCDAsm users.
oSession.SaveResponseBody(path);
}
A more robust version of the above snippet is available as a Fiddler plugin
[here][FiddlerAutoCapture].
Once you have edited a SWF file, you can use Fiddler's [AutoResponder][] to
replace the original file with your modified version.
[Fiddler]: http://www.fiddler2.com/fiddler2/
[AutoResponder]: http://www.fiddler2.com/fiddler2/help/AutoResponder.asp
[FiddlerAutoCapture]: https://github.com/CyberShadow/FiddlerAutoCapture
Limitations
===========
@@ -427,6 +445,7 @@ License
=======
RABCDAsm is distributed under the terms of the GPL v3 or later, with the
exception of `murmurhash2a.d` and `zlibx.d`, which are in the public domain,
and `asasm.hrc`, which is tri-licensed under the MPL 1.1/GPL 2.0/LGPL 2.1. The
full text of the GNU General Public License can be found in the file `COPYING`.
exception of `murmurhash2a.d`, `zlibx.d` and LZMA components, which are in the
public domain, and `asasm.hrc`, which is tri-licensed under the MPL 1.1/GPL
2.0/LGPL 2.1. The full text of the GNU General Public License can be found in
the file `COPYING`.
+1 -1
View File
@@ -45,7 +45,7 @@ void main(string[] args)
while (*p++) {} // skip name
abc = tag.data[p-tag.data.ptr..$];
}
std.file.write(getName(arg) ~ to!string(count++) ~ ".abc", abc);
std.file.write(stripExtension(arg) ~ "-" ~ to!string(count++) ~ ".abc", abc);
}
if (count == 0)
throw new Exception("No DoABC tags found");
+60 -48
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
@@ -19,6 +19,7 @@
module abcfile;
import std.string : format; // exception formatting
import std.conv;
import std.exception;
/**
@@ -46,9 +47,13 @@ class ABCFile
Script[] scripts;
MethodBody[] bodies;
static const long NULL_INT = long.max;
static const ulong NULL_UINT = ulong.max;
static const double NULL_DOUBLE = double.init; // NaN
enum long NULL_INT = long.max;
enum ulong NULL_UINT = ulong.max;
enum double NULL_DOUBLE = double.init; // NaN
enum ulong MAX_UINT = (1L << 36) - 1;
enum long MAX_INT = MAX_UINT / 2;
enum long MIN_INT = -MAX_INT - 1;
this()
{
@@ -126,13 +131,8 @@ class ABCFile
struct Metadata
{
struct Item
{
uint key, value;
}
uint name;
Item[] items;
uint[] keys, values;
}
struct Instance
@@ -222,7 +222,7 @@ class ABCFile
uint index; /// instruction index
int offset; /// signed offset relative to said instruction
}
private int absoluteOffset; /// internal temporary value used during reading and writing
private ptrdiff_t absoluteOffset; /// internal temporary value used during reading and writing
}
}
@@ -1018,27 +1018,39 @@ private final class ABCReader
/// Note: may return values larger than 0xFFFFFFFF.
ulong readU32()
out(result)
{
ulong result = readU8();
assert(result <= ABCFile.MAX_UINT);
}
body
{
ulong next() { return readU8(); } // force ulong
ulong result = next();
if (0==(result & 0x00000080))
return result;
result = result & 0x0000007f | readU8()<<7;
result = result & 0x0000007f | next()<<7;
if (0==(result & 0x00004000))
return result;
result = result & 0x00003fff | readU8()<<14;
result = result & 0x00003fff | next()<<14;
if (0==(result & 0x00200000))
return result;
result = result & 0x001fffff | readU8()<<21;
result = result & 0x001fffff | next()<<21;
if (0==(result & 0x10000000))
return result;
return result & 0x0fffffff | readU8()<<28;
return result & 0x0fffffff | next()<<28;
}
long readS32()
out(result)
{
ulong l = readU32();
if (l & 0xFFFFFFFF00000000) // preserve unused bits
return cast(long)l;
assert(result >= ABCFile.MIN_INT && result <= ABCFile.MAX_INT);
}
body
{
auto l = readU32();
if (l & 0xFFFFFFFF_00000000) // preserve unused bits
return l | 0xFFFFFFF0_00000000;
else
return cast(int)l;
}
@@ -1178,12 +1190,11 @@ private final class ABCReader
{
ABCFile.Metadata r;
r.name = readU30();
r.items.length = readU30();
foreach (ref value; r.items)
{
value.key = readU30();
value.value = readU30();
}
r.keys.length = r.values.length = readU30();
foreach (ref key; r.keys)
key = readU30();
foreach (ref value; r.values)
value = readU30();
return r;
}
@@ -1280,18 +1291,18 @@ private final class ABCReader
r.instructions = null;
size_t len = readU30();
uint[] instructionAtOffset = new uint[len];
auto instructionAtOffset = new uint[len];
r.rawBytes = buf[pos..pos+len];
void translateLabel(ref ABCFile.Label label)
{
int absoluteOffset = label.absoluteOffset;
int instructionOffset = absoluteOffset;
auto absoluteOffset = label.absoluteOffset;
auto instructionOffset = absoluteOffset;
while (true)
{
if (instructionOffset >= cast(int)len)
if (instructionOffset >= len)
{
label.index = r.instructions.length;
label.index = to!uint(r.instructions.length);
instructionOffset = len;
break;
}
@@ -1308,23 +1319,23 @@ private final class ABCReader
}
instructionOffset--;
}
label.offset = absoluteOffset-instructionOffset;
label.offset = to!int(absoluteOffset-instructionOffset);
}
size_t start = pos;
size_t end = pos + len;
uint offset() { return pos - start; }
size_t offset() { return pos - start; }
try
{
instructionAtOffset[] = uint.max;
uint[] instructionOffsets;
size_t[] instructionOffsets;
while (pos < end)
{
uint instructionOffset = offset;
auto instructionOffset = offset;
scope(failure) pos = start + instructionOffset;
instructionAtOffset[instructionOffset] = r.instructions.length;
instructionAtOffset[instructionOffset] = to!uint(r.instructions.length);
ABCFile.Instruction instruction;
instruction.opcode = cast(Opcode)readU8();
instruction.arguments.length = opcodeInfo[instruction.opcode].argumentTypes.length;
@@ -1398,6 +1409,7 @@ private final class ABCReader
{
r.instructions = null;
r.error = e.msg;
instructionAtOffset[] = 0;
}
pos = end;
@@ -1516,7 +1528,6 @@ private final class ABCWriter
writeU8(cast(ubyte)(v>>16));
}
/// Note: may return values larger than 0xFFFFFFFF.
void writeU32(ulong v)
{
if ( v < 128)
@@ -1556,8 +1567,9 @@ private final class ABCWriter
writeU32(cast(ulong)v);
}
void writeU30(uint v)
void writeU30(ulong v)
{
enforce(v < (1<<30));
writeU32(v);
}
@@ -1668,12 +1680,12 @@ private final class ABCWriter
void writeMetadata(ref ABCFile.Metadata v)
{
writeU30(v.name);
writeU30(v.items.length);
foreach (ref value; v.items)
{
writeU30(value.key);
writeU30(value.value);
}
assert(v.keys.length == v.values.length);
writeU30(v.keys.length);
foreach (key; v.keys)
writeU30(key);
foreach (value; v.values)
writeU30(value);
}
void writeInstance(ref ABCFile.Instance v)
@@ -1755,9 +1767,9 @@ private final class ABCWriter
writeU30(v.initScopeDepth);
writeU30(v.maxScopeDepth);
uint[] instructionOffsets = new uint[v.instructions.length+1];
auto instructionOffsets = new size_t[v.instructions.length+1];
uint resolveLabel(ref ABCFile.Label label) { return instructionOffsets[label.index]+label.offset; }
ptrdiff_t resolveLabel(ref ABCFile.Label label) { return instructionOffsets[label.index]+label.offset; }
{
// we don't know the length before writing all the instructions - swap buffer with a temporary one
@@ -1767,12 +1779,12 @@ private final class ABCWriter
buf = methodBuf[];
pos = 0;
struct Fixup { ABCFile.Label target; uint pos, base; }
struct Fixup { ABCFile.Label target; size_t pos, base; }
Fixup[] fixups;
foreach (ii, ref instruction; v.instructions)
{
uint instructionOffset = pos;
auto instructionOffset = pos;
instructionOffsets[ii] = instructionOffset;
writeU8(instruction.opcode);
@@ -1836,7 +1848,7 @@ private final class ABCWriter
foreach (ref fixup; fixups)
{
pos = fixup.pos;
writeS24(resolveLabel(fixup.target)-fixup.base);
writeS24(to!int(cast(ptrdiff_t)(resolveLabel(fixup.target)-fixup.base)));
}
auto code = buf;
+9 -4
View File
@@ -4,7 +4,7 @@
<hrc version="take5" xmlns="http://colorer.sf.net/2003/hrc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://colorer.sf.net/2003/hrc http://colorer.sf.net/2003/hrc.xsd">
<type name="asasm">
<annotation><documentation>
ActionScript Assembler Syntax (RABCDAsm variant)
@@ -31,20 +31,24 @@
<!-- Decimal numbers -->
<regexp match="/\b[0-9]+\b/" region0="def:NumberDec"/>
<!-- Paired brackets -->
<block start="/(\()/" end="/(\))/" scheme="asasm" region00="def:Symbol" region01="def:PairStart" region10="def:Symbol" region11="def:PairEnd"/>
<block start="/(\[)/" end="/(\])/" scheme="asasm" region00="def:Symbol" region01="def:PairStart" region10="def:Symbol" region11="def:PairEnd"/>
<block start="/(\<)/" end="/(\>)/" scheme="asasm" region00="def:Symbol" region01="def:PairStart" region10="def:Symbol" region11="def:PairEnd"/>
<!-- Outliner for refids -->
<regexp match="/(\brefid\b)\s*((?{def:StringEdge}&#34;))((?{def:String}[^&#34;]*))((?{def:StringEdge}&#34;))/" region1="def:Keyword" region2="def:PairStart" region3="def:Outlined" region4="def:PairEnd"/> <!-- BUG: does not support escape sequences -->
<!--regexp match="/(\brefid\b)\s*((?{def:StringEdge}&#34;))((?{def:String}[^&#34;]*))((?{def:StringEdge}&#34;))/" region1="def:Keyword" region2="def:PairStart" region3="def:Outlined" region4="def:PairEnd"/--> <!-- BUG: does not support escape sequences -->
<regexp
match="/(\brefid\b)\M\s*&#34;(.*)&#34;$/"
region1="def:Keyword"
region2="def:Outlined"/>
<!--block start="/(\brefid\b)\s*(?{def:StringEdge}&#34;)/" end="/(?{def:StringEdge}&#34;)/" scheme="StringContent" region="def:Outlined" inner-region="yes" region00="def:PairStart" region10="def:PairEnd"/-->
<!-- Symbol/keyword highlighting below -->
<keywords region="def:Symbol">
<symb name=","/>
<symb name=","/>
</keywords>
<keywords ignorecase="no" region="def:Directive">
@@ -55,6 +59,7 @@
<word name="#privatens"/>
<word name="#set"/>
<word name="#unset"/>
<word name="#version"/>
</keywords>
<keywords ignorecase="no" region="def:Keyword">
+313 -253
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
@@ -159,16 +159,8 @@ final class ASProgram
static class Metadata
{
struct Item
{
string key, value;
mixin AutoCompare;
mixin ProcessAllData;
}
string name;
Item[] items;
string[] keys, values;
mixin AutoCompare;
mixin ProcessAllData;
@@ -449,6 +441,7 @@ private final class ABCtoAS
n.vTypeName.params.length = multiname.TypeName.params.length;
foreach (j, param; multiname.TypeName.params)
n.vTypeName.params[j] = multinames[param];
break;
default:
break;
}
@@ -476,12 +469,12 @@ private final class ABCtoAS
{
auto n = new ASProgram.Metadata();
n.name = abc.strings[md.name];
n.items.length = md.items.length;
foreach (j, ref item; md.items)
{
n.items[j].key = abc.strings[item.key];
n.items[j].value = abc.strings[item.value];
}
n.keys.length = md.keys.length;
foreach (j, key; md.keys)
n.keys[j] = abc.strings[key];
n.values.length = md.values.length;
foreach (j, value; md.values)
n.values[j] = abc.strings[value];
return n;
}
@@ -621,16 +614,16 @@ private final class ABCtoAS
r.arguments[i].stringv = abc.strings[instruction.arguments[i].index];
break;
case OpcodeArgumentType.Namespace:
r.arguments[i].namespacev = namespaces[instruction.arguments[i].index];
r.arguments[i].namespacev = namespaces.checkedGet(instruction.arguments[i].index);
break;
case OpcodeArgumentType.Multiname:
r.arguments[i].multinamev = multinames[instruction.arguments[i].index];
r.arguments[i].multinamev = multinames.checkedGet(instruction.arguments[i].index);
break;
case OpcodeArgumentType.Class:
r.arguments[i].classv = classes[instruction.arguments[i].index];
r.arguments[i].classv = classes.checkedGet(instruction.arguments[i].index);
break;
case OpcodeArgumentType.Method:
r.arguments[i].methodv = methods[instruction.arguments[i].index];
r.arguments[i].methodv = methods.checkedGet(instruction.arguments[i].index);
break;
case OpcodeArgumentType.JumpTarget:
@@ -654,7 +647,7 @@ private final class ABCtoAS
as.majorVersion = abc.majorVersion;
namespaces.length = abc.namespaces.length;
foreach (i, ref namespace; abc.namespaces)
foreach (uint i, ref namespace; abc.namespaces)
if (i)
namespaces[i] = convertNamespace(namespace, i);
@@ -684,7 +677,7 @@ private final class ABCtoAS
instances[i] = convertInstance(instance);
classes.length = abc.classes.length;
foreach (i, ref vclass; abc.classes)
foreach (uint i, ref vclass; abc.classes)
classes[i] = convertClass(vclass, i);
as.scripts.length = abc.scripts.length;
@@ -704,10 +697,9 @@ private final class ABCtoAS
}
}
private final class AStoABC
private final class AStoABC : ASVisitor
{
ABCFile abc;
ASProgram as;
static bool isNull(T)(T value)
{
@@ -797,7 +789,7 @@ private final class AStoABC
all.sort;
enum { NullOffset = haveNull ? 1 : 0 }
values.length = all.length + NullOffset;
foreach (i, ref c; all)
foreach (uint i, ref c; all)
{
pool[cast(I)c.value].index = i + NullOffset;
values[i + NullOffset] = c.value;
@@ -837,7 +829,7 @@ private final class AStoABC
auto rp = p in pool;
if (rp is null)
{
pool[p] = Entry(1, p, pool.length);
pool[p] = Entry(1, p, to!uint(pool.length));
return true;
}
else
@@ -886,7 +878,7 @@ private final class AStoABC
topSort:
// update indices
foreach (j, e; all)
foreach (uint j, e; all)
e.index = j;
foreach (ref a; pool)
@@ -924,242 +916,49 @@ private final class AStoABC
ReferencePool!(ASProgram.Class) classes;
ReferencePool!(ASProgram.Method) methods;
void visitNamespace(ASProgram.Namespace ns)
override void visitInt(long v) { ints.add(v); }
override void visitUint(ulong v) { uints.add(v); }
override void visitDouble(double v) { doubles.add(v); }
override void visitString(string v) { strings.add(v); }
override void visitNamespace(ASProgram.Namespace ns)
{
if (namespaces.add(ns))
strings.add(ns.name);
super.visitNamespace(ns);
}
void visitNamespaceSet(ASProgram.Namespace[] nsSet)
override void visitNamespaceSet(ASProgram.Namespace[] nsSet)
{
if (namespaceSets.add(nsSet))
foreach (ns; nsSet)
visitNamespace(ns);
super.visitNamespaceSet(nsSet);
}
void visitMultiname(ASProgram.Multiname multiname)
override void visitMultiname(ASProgram.Multiname multiname)
{
if (multinames.notAdded(multiname))
{
with (multiname)
switch (kind)
{
case ASType.QName:
case ASType.QNameA:
visitNamespace(vQName.ns);
strings.add(vQName.name);
break;
case ASType.RTQName:
case ASType.RTQNameA:
strings.add(vRTQName.name);
break;
case ASType.RTQNameL:
case ASType.RTQNameLA:
break;
case ASType.Multiname:
case ASType.MultinameA:
strings.add(vMultiname.name);
visitNamespaceSet(vMultiname.nsSet);
break;
case ASType.MultinameL:
case ASType.MultinameLA:
visitNamespaceSet(vMultinameL.nsSet);
break;
case ASType.TypeName:
visitMultiname(vTypeName.name);
foreach (param; vTypeName.params)
visitMultiname(param);
break;
default:
throw new .Exception("Unknown Multiname kind");
}
super.visitMultiname(multiname);
bool r = multinames.add(multiname);
assert(r, "Recursive multiname reference");
}
}
void visitScript(ASProgram.Script script)
{
visitTraits(script.traits);
visitMethod(script.sinit);
}
void visitTraits(ASProgram.Trait[] traits)
{
foreach (ref trait; traits)
{
visitMultiname(trait.name);
switch (trait.kind)
{
case TraitKind.Slot:
case TraitKind.Const:
visitMultiname(trait.vSlot.typeName);
visitValue(trait.vSlot.value);
break;
case TraitKind.Class:
visitClass(trait.vClass.vclass);
break;
case TraitKind.Function:
visitMethod(trait.vFunction.vfunction);
break;
case TraitKind.Method:
case TraitKind.Getter:
case TraitKind.Setter:
visitMethod(trait.vMethod.vmethod);
break;
default:
throw new Exception("Unknown trait kind");
}
foreach (metadata; trait.metadata)
visitMetadata(metadata);
}
}
void visitMetadata(ASProgram.Metadata metadata)
override void visitMetadata(ASProgram.Metadata metadata)
{
if (metadatas.add(metadata))
{
strings.add(metadata.name);
foreach (ref item; metadata.items)
{
strings.add(item.key);
strings.add(item.value);
}
}
super.visitMetadata(metadata);
}
void visitValue(ref ASProgram.Value value)
override void visitClass(ASProgram.Class vclass)
{
switch (value.vkind)
{
case ASType.Integer:
ints.add(value.vint);
break;
case ASType.UInteger:
uints.add(value.vuint);
break;
case ASType.Double:
doubles.add(value.vdouble);
break;
case ASType.Utf8:
strings.add(value.vstring);
break;
case ASType.Namespace:
case ASType.PackageNamespace:
case ASType.PackageInternalNs:
case ASType.ProtectedNamespace:
case ASType.ExplicitNamespace:
case ASType.StaticProtectedNs:
case ASType.PrivateNamespace:
visitNamespace(value.vnamespace);
break;
case ASType.True:
case ASType.False:
case ASType.Null:
case ASType.Undefined:
break;
default:
throw new Exception("Unknown type");
}
if (classes.add(vclass))
super.visitClass(vclass);
}
void visitClass(ASProgram.Class vclass)
{
if (classes.notAdded(vclass))
{
visitMethod(vclass.cinit);
visitTraits(vclass.traits);
visitInstance(vclass.instance);
bool r = classes.add(vclass);
assert(r, "Recursive class reference");
}
}
void visitMethod(ASProgram.Method method)
override void visitMethod(ASProgram.Method method)
{
if (methods.add(method))
with (method)
{
foreach (type; paramTypes)
visitMultiname(type);
visitMultiname(returnType);
strings.add(name);
foreach (ref value; options)
visitValue(value);
foreach (name; paramNames)
strings.add(name);
if (vbody)
visitMethodBody(vbody);
}
}
void visitMethodBody(ASProgram.MethodBody vbody)
{
foreach (ref instruction; vbody.instructions)
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
final switch (type)
{
case OpcodeArgumentType.Unknown:
throw new Exception("Don't know how to visit OP_" ~ opcodeInfo[instruction.opcode].name);
case OpcodeArgumentType.UByteLiteral:
case OpcodeArgumentType.IntLiteral:
case OpcodeArgumentType.UIntLiteral:
break;
case OpcodeArgumentType.Int:
ints.add(instruction.arguments[i].intv);
break;
case OpcodeArgumentType.UInt:
uints.add(instruction.arguments[i].uintv);
break;
case OpcodeArgumentType.Double:
doubles.add(instruction.arguments[i].doublev);
break;
case OpcodeArgumentType.String:
strings.add(instruction.arguments[i].stringv);
break;
case OpcodeArgumentType.Namespace:
visitNamespace(instruction.arguments[i].namespacev);
break;
case OpcodeArgumentType.Multiname:
visitMultiname(instruction.arguments[i].multinamev);
break;
case OpcodeArgumentType.Class:
visitClass(instruction.arguments[i].classv);
break;
case OpcodeArgumentType.Method:
visitMethod(instruction.arguments[i].methodv);
break;
case OpcodeArgumentType.JumpTarget:
case OpcodeArgumentType.SwitchDefaultTarget:
case OpcodeArgumentType.SwitchTargets:
break;
}
foreach (ref exception; vbody.exceptions)
{
visitMultiname(exception.excType);
visitMultiname(exception.varName);
}
visitMethod(vbody.method);
visitTraits(vbody.traits);
}
void visitInstance(ASProgram.Instance instance)
{
visitMultiname(instance.name);
visitMultiname(instance.superName);
visitNamespace(instance.protectedNs);
foreach (intf; instance.interfaces)
visitMultiname(intf);
visitMethod(instance.iinit);
visitTraits(instance.traits);
super.visitMethod(method);
}
uint getValueIndex(ref ASProgram.Value value)
@@ -1216,18 +1015,13 @@ private final class AStoABC
this(ASProgram as)
{
super(as);
this.abc = new ABCFile();
this.as = as;
abc.minorVersion = as.minorVersion;
abc.majorVersion = as.majorVersion;
foreach (script; as.scripts)
visitScript(script);
foreach (vclass; as.orphanClasses)
visitClass(vclass);
foreach (method; as.orphanMethods)
visitMethod(method);
super.run();
registerClassDependencies();
@@ -1308,12 +1102,12 @@ private final class AStoABC
{
auto n = &abc.metadata[i];
n.name = strings.get(m.name);
n.items.length = m.items.length;
foreach (j, ref item; m.items)
{
n.items[j].key = strings.get(m.items[j].key);
n.items[j].value = strings.get(m.items[j].value);
}
n.keys.length = m.keys.length;
foreach (j, key; m.keys)
n.keys[j] = strings.get(key);
n.values.length = m.values.length;
foreach (j, value; m.values)
n.values[j] = strings.get(value);
}
ASProgram.MethodBody[] bodies;
@@ -1483,10 +1277,16 @@ private final class AStoABC
r.arguments[i].index = multinames.get(instruction.arguments[i].multinamev);
break;
case OpcodeArgumentType.Class:
r.arguments[i].index = classes.get(instruction.arguments[i].classv);
if (instruction.arguments[i].classv is null)
r.arguments[i].index = to!uint(abc.classes.length);
else
r.arguments[i].index = classes.get(instruction.arguments[i].classv);
break;
case OpcodeArgumentType.Method:
r.arguments[i].index = methods.get(instruction.arguments[i].methodv);
if (instruction.arguments[i].methodv is null)
r.arguments[i].index = to!uint(abc.methods.length);
else
r.arguments[i].index = methods.get(instruction.arguments[i].methodv);
break;
case OpcodeArgumentType.JumpTarget:
@@ -1550,6 +1350,261 @@ class ASTraitsVisitor
}
}
class ASVisitor : ASTraitsVisitor
{
this(ASProgram as) { super(as); }
void visitInt(long) {}
void visitUint(ulong) {}
void visitDouble(double) {}
void visitString(string) {}
void visitNamespace(ASProgram.Namespace ns)
{
if (ns)
visitString(ns.name);
}
void visitNamespaceSet(ASProgram.Namespace[] nsSet)
{
foreach (ns; nsSet)
visitNamespace(ns);
}
void visitMultiname(ASProgram.Multiname multiname)
{
if (multiname)
with (multiname)
switch (kind)
{
case ASType.QName:
case ASType.QNameA:
visitNamespace(vQName.ns);
visitString(vQName.name);
break;
case ASType.RTQName:
case ASType.RTQNameA:
visitString(vRTQName.name);
break;
case ASType.RTQNameL:
case ASType.RTQNameLA:
break;
case ASType.Multiname:
case ASType.MultinameA:
visitString(vMultiname.name);
visitNamespaceSet(vMultiname.nsSet);
break;
case ASType.MultinameL:
case ASType.MultinameLA:
visitNamespaceSet(vMultinameL.nsSet);
break;
case ASType.TypeName:
visitMultiname(vTypeName.name);
foreach (param; vTypeName.params)
visitMultiname(param);
break;
default:
throw new .Exception("Unknown Multiname kind");
}
}
void visitScript(ASProgram.Script script)
{
if (script)
{
visitTraits(script.traits);
visitMethod(script.sinit);
}
}
override void visitTrait(ref ASProgram.Trait trait)
{
visitMultiname(trait.name);
switch (trait.kind)
{
case TraitKind.Slot:
case TraitKind.Const:
visitMultiname(trait.vSlot.typeName);
visitValue(trait.vSlot.value);
break;
case TraitKind.Class:
visitClass(trait.vClass.vclass);
break;
case TraitKind.Function:
visitMethod(trait.vFunction.vfunction);
break;
case TraitKind.Method:
case TraitKind.Getter:
case TraitKind.Setter:
visitMethod(trait.vMethod.vmethod);
break;
default:
throw new Exception("Unknown trait kind");
}
foreach (metadata; trait.metadata)
visitMetadata(metadata);
}
void visitMetadata(ASProgram.Metadata metadata)
{
if (metadata)
{
visitString(metadata.name);
foreach (key; metadata.keys)
visitString(key);
foreach (value; metadata.values)
visitString(value);
}
}
void visitValue(ref ASProgram.Value value)
{
switch (value.vkind)
{
case ASType.Integer:
visitInt(value.vint);
break;
case ASType.UInteger:
visitUint(value.vuint);
break;
case ASType.Double:
visitDouble(value.vdouble);
break;
case ASType.Utf8:
visitString(value.vstring);
break;
case ASType.Namespace:
case ASType.PackageNamespace:
case ASType.PackageInternalNs:
case ASType.ProtectedNamespace:
case ASType.ExplicitNamespace:
case ASType.StaticProtectedNs:
case ASType.PrivateNamespace:
visitNamespace(value.vnamespace);
break;
case ASType.True:
case ASType.False:
case ASType.Null:
case ASType.Undefined:
break;
default:
throw new Exception("Unknown type");
}
}
void visitClass(ASProgram.Class vclass)
{
if (vclass)
{
visitMethod(vclass.cinit);
visitTraits(vclass.traits);
visitInstance(vclass.instance);
}
}
void visitMethod(ASProgram.Method method)
{
if (method)
with (method)
{
foreach (type; paramTypes)
visitMultiname(type);
visitMultiname(returnType);
visitString(name);
foreach (ref value; options)
visitValue(value);
foreach (name; paramNames)
visitString(name);
if (vbody)
visitMethodBody(vbody);
}
}
void visitMethodBody(ASProgram.MethodBody vbody)
{
if (vbody)
{
foreach (ref instruction; vbody.instructions)
foreach (i, type; opcodeInfo[instruction.opcode].argumentTypes)
final switch (type)
{
case OpcodeArgumentType.Unknown:
throw new Exception("Don't know how to visit OP_" ~ opcodeInfo[instruction.opcode].name);
case OpcodeArgumentType.UByteLiteral:
case OpcodeArgumentType.IntLiteral:
case OpcodeArgumentType.UIntLiteral:
break;
case OpcodeArgumentType.Int:
visitInt(instruction.arguments[i].intv);
break;
case OpcodeArgumentType.UInt:
visitUint(instruction.arguments[i].uintv);
break;
case OpcodeArgumentType.Double:
visitDouble(instruction.arguments[i].doublev);
break;
case OpcodeArgumentType.String:
visitString(instruction.arguments[i].stringv);
break;
case OpcodeArgumentType.Namespace:
visitNamespace(instruction.arguments[i].namespacev);
break;
case OpcodeArgumentType.Multiname:
visitMultiname(instruction.arguments[i].multinamev);
break;
case OpcodeArgumentType.Class:
visitClass(instruction.arguments[i].classv);
break;
case OpcodeArgumentType.Method:
visitMethod(instruction.arguments[i].methodv);
break;
case OpcodeArgumentType.JumpTarget:
case OpcodeArgumentType.SwitchDefaultTarget:
case OpcodeArgumentType.SwitchTargets:
break;
}
foreach (ref exception; vbody.exceptions)
{
visitMultiname(exception.excType);
visitMultiname(exception.varName);
}
visitMethod(vbody.method);
visitTraits(vbody.traits);
}
}
void visitInstance(ASProgram.Instance instance)
{
if (instance)
{
visitMultiname(instance.name);
visitMultiname(instance.superName);
visitNamespace(instance.protectedNs);
foreach (intf; instance.interfaces)
visitMultiname(intf);
visitMethod(instance.iinit);
visitTraits(instance.traits);
}
}
override void run()
{
foreach (script; as.scripts)
visitScript(script);
foreach (vclass; as.orphanClasses)
visitClass(vclass);
foreach (method; as.orphanMethods)
visitMethod(method);
}
}
private bool contains(T)(T[] arr, T val)
{
foreach (v; arr)
@@ -1557,3 +1612,8 @@ private bool contains(T)(T[] arr, T val)
return true;
return false;
}
private T checkedGet(T)(T[] array, uint index, T def = T.init)
{
return index < array.length ? array[index] : def;
}
+62 -30
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
@@ -25,6 +25,7 @@ import std.path;
import std.exception;
import abcfile;
import asprogram;
import common;
final class Assembler
{
@@ -52,12 +53,12 @@ final class Assembler
static File load(string filename, string[] arguments = null)
{
return fromFile(filename, cast(string)read(filename), arguments);
return fromFile(filename, cast(string)read(longPath(filename)), arguments);
}
static File fromFile(string filename, string data, string[] arguments = null)
{
return fromData(filename, data, arguments, getDirName(filename));
return fromData(filename, data, arguments, dirName(filename));
}
static File fromData(string name, string data, string[] arguments = null, string basePath = null)
@@ -76,7 +77,7 @@ final class Assembler
string positionStr()
{
auto lines = splitlines(buf);
auto lines = splitLines(buf);
foreach (i, line; lines)
if (pos <= line.ptr + line.length)
return format("%s(%d,%d)", filename, i+1, pos-line.ptr+1);
@@ -103,7 +104,7 @@ final class Assembler
foreach (ref c; buf)
if (c == '\\')
c = '/';
return std.path.join(getBasePath, assumeUnique(buf));
return buildPath(getBasePath, buf);
}
void skipWhitespace()
@@ -133,6 +134,7 @@ final class Assembler
string[string] vars;
uint[string] privateNamespaces;
uint sourceVersion = 1;
void handlePreprocessor()
{
@@ -151,7 +153,7 @@ final class Assembler
break;
case "get":
auto filename = convertFilename(readString());
pushFile(File.fromFile(filename, toStringLiteral(cast(string)read(filename))));
pushFile(File.fromFile(filename, toStringLiteral(cast(string)read(longPath(filename)))));
break;
case "set":
vars[readWord()] = readString();
@@ -163,6 +165,9 @@ final class Assembler
uint index = cast(uint)readUInt();
privateNamespaces[readString()] = index;
break;
case "version":
sourceVersion = cast(uint)readUInt();
break;
default:
files[0].pos -= word.length;
throw new Exception("Unknown preprocessor declaration: " ~ word);
@@ -491,7 +496,9 @@ final class Assembler
string w = readWord();
if (w == "null")
return ABCFile.NULL_INT;
return to!int(w);
auto v = to!long(w);
enforce(v >= ABCFile.MIN_INT && v <= ABCFile.MAX_INT, "Int out of bounds");
return v;
}
ulong readUInt()
@@ -499,7 +506,9 @@ final class Assembler
string w = readWord();
if (w == "null")
return ABCFile.NULL_UINT;
return to!uint(w);
auto v = to!ulong(w);
enforce(v <= ABCFile.MAX_UINT, "UInt out of bounds");
return v;
}
double readDouble()
@@ -752,13 +761,30 @@ final class Assembler
{
auto metadata = new ASProgram.Metadata;
metadata.name = readString();
string[] items;
while (true)
switch (readWord())
{
case "item":
metadata.items ~= ASProgram.Metadata.Item(readString(), readString());
items ~= readString();
items ~= readString();
break;
case "end":
if (sourceVersion < 2)
{
metadata.keys = items[0..$/2];
metadata.values = items[$/2..$];
}
else
{
metadata.keys .length = items.length/2;
metadata.values.length = items.length/2;
foreach (i; 0..items.length/2)
{
metadata.keys [i] = items[i*2 ];
metadata.values[i] = items[i*2+1];
}
}
return metadata;
default:
throw new Exception("Expected item or end");
@@ -992,7 +1018,7 @@ final class Assembler
break;
if (peekChar() == ':')
{
addUnique!("label")(labels, word, instructions.length);
addUnique!("label")(labels, word, to!uint(instructions.length));
skipChar(); // :
continue;
}
@@ -1008,7 +1034,7 @@ final class Assembler
instruction.opcode = *popcode;
auto argTypes = opcodeInfo[instruction.opcode].argumentTypes;
instruction.arguments.length = argTypes.length;
foreach (i, type; argTypes)
foreach (uint i, type; argTypes)
{
final switch (type)
{
@@ -1044,22 +1070,22 @@ final class Assembler
instruction.arguments[i].multinamev = readMultiname();
break;
case OpcodeArgumentType.Class:
localClassFixups ~= LocalFixup(files[0].position, instructions.length, i, readString());
localClassFixups ~= LocalFixup(files[0].position, to!uint(instructions.length), i, readString());
break;
case OpcodeArgumentType.Method:
localMethodFixups ~= LocalFixup(files[0].position, instructions.length, i, readString());
localMethodFixups ~= LocalFixup(files[0].position, to!uint(instructions.length), i, readString());
break;
case OpcodeArgumentType.JumpTarget:
case OpcodeArgumentType.SwitchDefaultTarget:
jumpFixups ~= LocalFixup(files[0].position, instructions.length, i, readWord());
jumpFixups ~= LocalFixup(files[0].position, to!uint(instructions.length), i, readWord());
break;
case OpcodeArgumentType.SwitchTargets:
string[] switchTargetLabels = readList!('[', ']', readWord, false)();
instruction.arguments[i].switchTargets.length = switchTargetLabels.length;
foreach (li, s; switchTargetLabels)
switchFixups ~= LocalFixup(files[0].position, instructions.length, i, s, li);
foreach (uint li, s; switchTargetLabels)
switchFixups ~= LocalFixup(files[0].position, to!uint(instructions.length), i, s, li);
break;
}
if (i < argTypes.length-1)
@@ -1175,26 +1201,32 @@ final class Assembler
readProgram();
foreach (ref f; classFixups)
{
auto cp = f.name in classesByID;
if (cp is null)
if (f.name is null)
*f.ptr = null;
else
{
setFile(f.where.load);
throw new Exception("Unknown class refid: " ~ f.name);
auto cp = f.name in classesByID;
if (cp is null)
{
setFile(f.where.load);
throw new Exception("Unknown class refid: " ~ f.name);
}
*f.ptr = *cp;
}
*f.ptr = *cp;
}
foreach (ref f; methodFixups)
{
auto mp = f.name in methodsByID;
if (mp is null)
if (f.name is null)
*f.ptr = null;
else
{
setFile(f.where.load);
throw new Exception("Unknown method refid: " ~ f.name);
auto mp = f.name in methodsByID;
if (mp is null)
{
setFile(f.where.load);
throw new Exception("Unknown method refid: " ~ f.name);
}
*f.ptr = *mp;
}
*f.ptr = *mp;
}
}
catch (Exception e)
{
+30 -8
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
@@ -20,6 +20,7 @@ module autodata;
import murmurhash2a;
import std.traits;
public import std.conv;
string addAutoField(string name, bool reverseSort = false)
{
@@ -47,7 +48,7 @@ template AutoCompare()
int opCmp(ref const typeof(this) s) const { return _AutoDataCmp(&s); }
}
private hash_t _AutoDataHash() const
@trusted private hash_t _AutoDataHash() const
{
HashDataHandler handler;
handler.hasher.Begin();
@@ -75,7 +76,7 @@ template AutoCompare()
template AutoToString()
{
static if (is(typeof(this)==class))
override string toString() { return _AutoDataToString(); }
override string toString() const { return _AutoDataToString(); }
else // struct
string toString() { return _AutoDataToString(); }
@@ -119,8 +120,11 @@ template RawDataHandlerWrapper()
static if (!hasAliasing!(T))
enum getMixinRecursive = getRawMixin!("&" ~ name, name ~ ".sizeof");
else
static if (is(typeof(this)==struct) || is(typeof(this)==class))
static if (is(T==struct))
enum getMixinRecursive = name ~ ".processData!(void, ``, ``)(handler);";
else
static if (is(T==class))
enum getMixinRecursive = "if ("~name~" !is null) " ~ name ~ ".processData!(void, ``, ``)(handler);";
else
static assert(0, "Don't know how to process type: " ~ T.stringof);
}
@@ -134,7 +138,7 @@ struct HashDataHandler
template getRawMixin(string ptr, string len)
{
enum getRawMixin = "handler.hasher.Add(" ~ ptr ~ ", " ~ len ~ ");";
enum getRawMixin = "handler.hasher.Add(" ~ ptr ~ ", to!int(" ~ len ~ "));";
}
}
@@ -144,7 +148,7 @@ struct EqualsDataHandler(O)
template nullCheck(T, string name)
{
static if (is(T U : U[]))
static if (is(typeof(T.init is null)))
enum nullCheck = "if ((this." ~ name ~ " is null) != (_AutoDataOther." ~ name ~ " is null)) return false;";
else
enum nullCheck = "";
@@ -165,6 +169,23 @@ struct CmpDataHandler(O)
enum getMixin = getMixinComposite!(T, name, reverseSort).code;
}
template nullCheck(T, string name, string reverseStr)
{
static if (is(typeof(T.init is null)))
enum nullCheck = "
if (this."~name~" is null && _AutoDataOther."~name~" is null)
{ /* skip */ }
else
if (this."~name~" is null && _AutoDataOther."~name~" !is null)
return " ~ reverseStr ~ "(-1);
else
if (this."~name~" !is null && _AutoDataOther."~name~" is null)
return " ~ reverseStr ~ "( 1);
else";
else
enum nullCheck = "";
}
template getMixinComposite(T, string name, bool reverseSort)
{
enum reverseStr = reverseSort ? "-" : "";
@@ -180,7 +201,8 @@ struct CmpDataHandler(O)
enum dataCode = "{ int _AutoDataCmp = this." ~ name ~ " - _AutoDataOther." ~ name ~ "; if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }"; // TODO: use long?
else
static if (is(typeof(T.opCmp)))
enum dataCode = "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
enum dataCode = nullCheck!(T, name, reverseStr)
~ "{ int _AutoDataCmp = this." ~ name ~ ".opCmp(_AutoDataOther." ~ name ~ "); if (_AutoDataCmp != 0) return " ~ reverseStr ~ "_AutoDataCmp; }";
else
enum dataCode = "if (this." ~ name ~ " < _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "(-1);" ~
"if (this." ~ name ~ " > _AutoDataOther." ~ name ~ ") return " ~ reverseStr ~ "( 1);";
@@ -192,6 +214,6 @@ struct ToStringDataHandler
{
template getMixin(T, string name, bool reverseSort)
{
enum getMixin = "_AutoDataResult ~= format(`%s = %s `, `" ~ name ~ "`, this." ~ name ~ ");";
enum getMixin = "_AutoDataResult ~= `" ~ name ~ " = ` ~ to!string(this." ~ name ~ ") ~ ` `;";
}
}
+75 -27
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
@@ -24,48 +24,96 @@ module build_rabcdasm;
version(D_Version2)
{ /* All OK */ }
else
static assert(false, "Unsupported D version.\nThis software requires a D2 ( http://www.digitalmars.com/d/2.0/ ) compiler to build.");
static assert(false, "Unsupported D version.\nThis software requires a D2 ( http://dlang.org/ ) compiler to build.");
version(D_Version2):
version(DigitalMars)
const DEFAULT_COMPILER = "dmd";
else
const DEFAULT_COMPILER = "gdmd";
const DEFAULT_FLAGS = "-O -inline";
const DEFAULT_FLAGS = "-w -O -inline";
const LZMA_FLAGS = "-version=HAVE_LZMA";
import std.exception;
import std.file;
import std.process;
import std.stdio;
import std.string;
string[][string] programs;
string compiler, flags;
static this()
void compile(string program)
{
programs["rabcasm" ] = ["abcfile", "asprogram", "assembler", "autodata", "murmurhash2a"];
programs["rabcdasm" ] = ["abcfile", "asprogram", "disassembler", "autodata", "murmurhash2a"];
programs["abcexport" ] = ["swffile", "zlibx"];
programs["abcreplace" ] = ["swffile", "zlibx"];
programs["swfbinexport" ] = ["swffile", "zlibx"];
programs["swfbinreplace"] = ["swffile", "zlibx"];
programs["swfdecompress"] = ["swffile", "zlibx"];
programs["swf7zcompress"] = ["swffile", "zlibx"];
stderr.writeln("* Building ", program);
enforce(system(format("rdmd --build-only --compiler=%s %s %s", compiler, flags, program)) == 0, "Compilation of " ~ program ~ " failed");
}
void test(string code, string extraFlags=null)
{
const FN = "test.d";
std.file.write(FN, code);
scope(exit) remove(FN);
enforce(system(format("rdmd --compiler=%s %s %s %s", compiler, flags, extraFlags, FN)) == 0, "Test failed");
stderr.writeln(" >>> OK");
}
int main()
{
string compiler = getenv("DC");
if (compiler is null)
compiler = DEFAULT_COMPILER;
string flags = getenv("DCFLAGS");
if (flags is null)
flags = DEFAULT_FLAGS;
foreach (program, modules; programs)
try
{
int ret = system(format("%s %s %s %s", compiler, flags, program, join(modules, " ")));
if (ret)
return ret;
}
compiler = getenv("DC");
if (compiler is null)
compiler = DEFAULT_COMPILER;
return 0;
flags = getenv("DCFLAGS");
if (flags is null)
flags = DEFAULT_FLAGS;
stderr.writeln("* Checking for working compiler...");
test(`
void main() {}
`);
bool haveLZMA;
stderr.writeln("* Checking for LZMA...");
try
{
test(`
import lzma, std.exception;
void main()
{
LZMAHeader header;
auto data = cast(immutable(ubyte)[])"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
auto cdata = lzmaCompress(data, &header);
header.decompressedSize = data.length;
auto ddata = lzmaDecompress(header, cdata);
enforce(data == ddata);
}
`, LZMA_FLAGS);
// Test succeeded
haveLZMA = true;
}
catch (Exception e)
stderr.writeln(" >>> LZMA not found, building without LZMA support.");
if (haveLZMA)
flags ~= " " ~ LZMA_FLAGS;
foreach (program; ["rabcasm", "rabcdasm", "abcexport", "abcreplace", "swfbinexport", "swfbinreplace", "swfdecompress", "swf7zcompress"])
compile(program);
if (haveLZMA)
compile("swflzmacompress");
return 0;
}
catch (Exception e)
{
stderr.writeln("Error: ", e.msg);
return 1;
}
}
+36
View File
@@ -0,0 +1,36 @@
/*
* Copyright 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RABCDAsm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RABCDAsm. If not, see <http://www.gnu.org/licenses/>.
*/
module common;
import std.path;
import std.string;
import std.array;
string longPath(string s)
{
version(Windows)
{
if (s.startsWith(`\\`))
return s;
else
return `\\?\` ~ s.absolutePath().buildNormalizedPath().replace(`/`, `\`);
}
else
return s;
}
+83
View File
@@ -0,0 +1,83 @@
/**
* \file api/lzma.h
* \brief The public API of liblzma data compression library
*
* liblzma is a public domain general-purpose data compression library with
* a zlib-like API. The native file format is .xz, but also the old .lzma
* format and raw (no headers) streams are supported. Multiple compression
* algorithms (filters) are supported. Currently LZMA2 is the primary filter.
*
* liblzma is part of XZ Utils <http://tukaani.org/xz/>. XZ Utils includes
* a gzip-like command line tool named xz and some other tools. XZ Utils
* is developed and maintained by Lasse Collin.
*
* Major parts of liblzma are based on Igor Pavlov's public domain LZMA SDK
* <http://7-zip.org/sdk.html>.
*
* The SHA-256 implementation is based on the public domain code found from
* 7-Zip <http://7-zip.org/>, which has a modified version of the public
* domain SHA-256 code found from Crypto++ <http://www.cryptopp.com/>.
* The SHA-256 code in Crypto++ was written by Kevin Springle and Wei Dai.
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
module deimos.lzma;
/***********
* nothrow *
***********/
/*
* None of the functions in liblzma may throw an exception. Even
* the functions that use callback functions won't throw exceptions,
* because liblzma would break if a callback function threw an exception.
*/
//lzma_nothrow --> nothrow
/********************
* GNU C extensions *
********************/
/*
* GNU C extensions are used conditionally in the public API. It doesn't
* break anything if these are sometimes enabled and sometimes not, only
* affects warnings and optimizations.
*/
//lzma_attr_pure --> pure
//lzma_attr_const --> const
//lzma_attr_warn_unused_result --> n/a
/**************
* Subheaders *
**************/
public import deimos.lzma_.version_;
public import deimos.lzma_.base;
public import deimos.lzma_.vli;
public import deimos.lzma_.check;
/* Filters */
public import deimos.lzma_.filter;
public import deimos.lzma_.bcj;
public import deimos.lzma_.delta;
public import deimos.lzma_.lzma;
/* Container formats */
public import deimos.lzma_.container;
/* Advanced features */
public import deimos.lzma_.stream_flags;
public import deimos.lzma_.block;
public import deimos.lzma_.index;
public import deimos.lzma_.index_hash;
/* Hardware information */
public import deimos.lzma_.hardware;
+582
View File
@@ -0,0 +1,582 @@
/**
* \file lzma/base.h
* \brief Data types and functions used in many places in liblzma API
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.base;
import deimos.lzma;
extern(C):
/**
* \brief Boolean
*
* This is here because C89 doesn't have stdbool.h. To set a value for
* variables having type lzma_bool, you can use
* - C99's `true' and `false' from stdbool.h;
* - C++'s internal `true' and `false'; or
* - integers one (true) and zero (false).
*/
alias bool lzma_bool;
/**
* \brief Type of reserved enumeration variable in structures
*
* To avoid breaking library ABI when new features are added, several
* structures contain extra variables that may be used in future. Since
* sizeof(enum) can be different than sizeof(int), and sizeof(enum) may
* even vary depending on the range of enumeration constants, we specify
* a separate type to be used for reserved enumeration variables. All
* enumeration constants in liblzma API will be non-negative and less
* than 128, which should guarantee that the ABI won't break even when
* new constants are added to existing enumerations.
*/
enum lzma_reserved_enum
{
LZMA_RESERVED_ENUM = 0
}
/**
* \brief Return values used by several functions in liblzma
*
* Check the descriptions of specific functions to find out which return
* values they can return. With some functions the return values may have
* more specific meanings than described here; those differences are
* described per-function basis.
*/
enum lzma_ret
{
LZMA_OK = 0,
/**<
* \brief Operation completed successfully
*/
LZMA_STREAM_END = 1,
/**<
* \brief End of stream was reached
*
* In encoder, LZMA_SYNC_FLUSH, LZMA_FULL_FLUSH, or
* LZMA_FINISH was finished. In decoder, this indicates
* that all the data was successfully decoded.
*
* In all cases, when LZMA_STREAM_END is returned, the last
* output bytes should be picked from strm->next_out.
*/
LZMA_NO_CHECK = 2,
/**<
* \brief Input stream has no integrity check
*
* This return value can be returned only if the
* LZMA_TELL_NO_CHECK flag was used when initializing
* the decoder. LZMA_NO_CHECK is just a warning, and
* the decoding can be continued normally.
*
* It is possible to call lzma_get_check() immediately after
* lzma_code has returned LZMA_NO_CHECK. The result will
* naturally be LZMA_CHECK_NONE, but the possibility to call
* lzma_get_check() may be convenient in some applications.
*/
LZMA_UNSUPPORTED_CHECK = 3,
/**<
* \brief Cannot calculate the integrity check
*
* The usage of this return value is different in encoders
* and decoders.
*
* Encoders can return this value only from the initialization
* function. If initialization fails with this value, the
* encoding cannot be done, because there's no way to produce
* output with the correct integrity check.
*
* Decoders can return this value only from lzma_code() and
* only if the LZMA_TELL_UNSUPPORTED_CHECK flag was used when
* initializing the decoder. The decoding can still be
* continued normally even if the check type is unsupported,
* but naturally the check will not be validated, and possible
* errors may go undetected.
*
* With decoder, it is possible to call lzma_get_check()
* immediately after lzma_code() has returned
* LZMA_UNSUPPORTED_CHECK. This way it is possible to find
* out what the unsupported Check ID was.
*/
LZMA_GET_CHECK = 4,
/**<
* \brief Integrity check type is now available
*
* This value can be returned only by the lzma_code() function
* and only if the decoder was initialized with the
* LZMA_TELL_ANY_CHECK flag. LZMA_GET_CHECK tells the
* application that it may now call lzma_get_check() to find
* out the Check ID. This can be used, for example, to
* implement a decoder that accepts only files that have
* strong enough integrity check.
*/
LZMA_MEM_ERROR = 5,
/**<
* \brief Cannot allocate memory
*
* Memory allocation failed, or the size of the allocation
* would be greater than SIZE_MAX.
*
* Due to internal implementation reasons, the coding cannot
* be continued even if more memory were made available after
* LZMA_MEM_ERROR.
*/
LZMA_MEMLIMIT_ERROR = 6,
/**
* \brief Memory usage limit was reached
*
* Decoder would need more memory than allowed by the
* specified memory usage limit. To continue decoding,
* the memory usage limit has to be increased with
* lzma_memlimit_set().
*/
LZMA_FORMAT_ERROR = 7,
/**<
* \brief File format not recognized
*
* The decoder did not recognize the input as supported file
* format. This error can occur, for example, when trying to
* decode .lzma format file with lzma_stream_decoder,
* because lzma_stream_decoder accepts only the .xz format.
*/
LZMA_OPTIONS_ERROR = 8,
/**<
* \brief Invalid or unsupported options
*
* Invalid or unsupported options, for example
* - unsupported filter(s) or filter options; or
* - reserved bits set in headers (decoder only).
*
* Rebuilding liblzma with more features enabled, or
* upgrading to a newer version of liblzma may help.
*/
LZMA_DATA_ERROR = 9,
/**<
* \brief Data is corrupt
*
* The usage of this return value is different in encoders
* and decoders. In both encoder and decoder, the coding
* cannot continue after this error.
*
* Encoders return this if size limits of the target file
* format would be exceeded. These limits are huge, thus
* getting this error from an encoder is mostly theoretical.
* For example, the maximum compressed and uncompressed
* size of a .xz Stream is roughly 8 EiB (2^63 bytes).
*
* Decoders return this error if the input data is corrupt.
* This can mean, for example, invalid CRC32 in headers
* or invalid check of uncompressed data.
*/
LZMA_BUF_ERROR = 10,
/**<
* \brief No progress is possible
*
* This error code is returned when the coder cannot consume
* any new input and produce any new output. The most common
* reason for this error is that the input stream being
* decoded is truncated or corrupt.
*
* This error is not fatal. Coding can be continued normally
* by providing more input and/or more output space, if
* possible.
*
* Typically the first call to lzma_code() that can do no
* progress returns LZMA_OK instead of LZMA_BUF_ERROR. Only
* the second consecutive call doing no progress will return
* LZMA_BUF_ERROR. This is intentional.
*
* With zlib, Z_BUF_ERROR may be returned even if the
* application is doing nothing wrong, so apps will need
* to handle Z_BUF_ERROR specially. The above hack
* guarantees that liblzma never returns LZMA_BUF_ERROR
* to properly written applications unless the input file
* is truncated or corrupt. This should simplify the
* applications a little.
*/
LZMA_PROG_ERROR = 11,
/**<
* \brief Programming error
*
* This indicates that the arguments given to the function are
* invalid or the internal state of the decoder is corrupt.
* - Function arguments are invalid or the structures
* pointed by the argument pointers are invalid
* e.g. if strm->next_out has been set to NULL and
* strm->avail_out > 0 when calling lzma_code().
* - lzma_* functions have been called in wrong order
* e.g. lzma_code() was called right after lzma_end().
* - If errors occur randomly, the reason might be flaky
* hardware.
*
* If you think that your code is correct, this error code
* can be a sign of a bug in liblzma. See the documentation
* how to report bugs.
*/
}
/**
* \brief The `action' argument for lzma_code()
*
* After the first use of LZMA_SYNC_FLUSH, LZMA_FULL_FLUSH, or LZMA_FINISH,
* the same `action' must is used until lzma_code() returns LZMA_STREAM_END.
* Also, the amount of input (that is, strm->avail_in) must not be modified
* by the application until lzma_code() returns LZMA_STREAM_END. Changing the
* `action' or modifying the amount of input will make lzma_code() return
* LZMA_PROG_ERROR.
*/
enum lzma_action
{
LZMA_RUN = 0,
/**<
* \brief Continue coding
*
* Encoder: Encode as much input as possible. Some internal
* buffering will probably be done (depends on the filter
* chain in use), which causes latency: the input used won't
* usually be decodeable from the output of the same
* lzma_code() call.
*
* Decoder: Decode as much input as possible and produce as
* much output as possible.
*/
LZMA_SYNC_FLUSH = 1,
/**<
* \brief Make all the input available at output
*
* Normally the encoder introduces some latency.
* LZMA_SYNC_FLUSH forces all the buffered data to be
* available at output without resetting the internal
* state of the encoder. This way it is possible to use
* compressed stream for example for communication over
* network.
*
* Only some filters support LZMA_SYNC_FLUSH. Trying to use
* LZMA_SYNC_FLUSH with filters that don't support it will
* make lzma_code() return LZMA_OPTIONS_ERROR. For example,
* LZMA1 doesn't support LZMA_SYNC_FLUSH but LZMA2 does.
*
* Using LZMA_SYNC_FLUSH very often can dramatically reduce
* the compression ratio. With some filters (for example,
* LZMA2), fine-tuning the compression options may help
* mitigate this problem significantly (for example,
* match finder with LZMA2).
*
* Decoders don't support LZMA_SYNC_FLUSH.
*/
LZMA_FULL_FLUSH = 2,
/**<
* \brief Finish encoding of the current Block
*
* All the input data going to the current Block must have
* been given to the encoder (the last bytes can still be
* pending in* next_in). Call lzma_code() with LZMA_FULL_FLUSH
* until it returns LZMA_STREAM_END. Then continue normally
* with LZMA_RUN or finish the Stream with LZMA_FINISH.
*
* This action is currently supported only by Stream encoder
* and easy encoder (which uses Stream encoder). If there is
* no unfinished Block, no empty Block is created.
*/
LZMA_FINISH = 3
/**<
* \brief Finish the coding operation
*
* All the input data must have been given to the encoder
* (the last bytes can still be pending in next_in).
* Call lzma_code() with LZMA_FINISH until it returns
* LZMA_STREAM_END. Once LZMA_FINISH has been used,
* the amount of input must no longer be changed by
* the application.
*
* When decoding, using LZMA_FINISH is optional unless the
* LZMA_CONCATENATED flag was used when the decoder was
* initialized. When LZMA_CONCATENATED was not used, the only
* effect of LZMA_FINISH is that the amount of input must not
* be changed just like in the encoder.
*/
}
/**
* \brief Custom functions for memory handling
*
* A pointer to lzma_allocator may be passed via lzma_stream structure
* to liblzma, and some advanced functions take a pointer to lzma_allocator
* as a separate function argument. The library will use the functions
* specified in lzma_allocator for memory handling instead of the default
* malloc() and free(). C++ users should note that the custom memory
* handling functions must not throw exceptions.
*
* liblzma doesn't make an internal copy of lzma_allocator. Thus, it is
* OK to change these function pointers in the middle of the coding
* process, but obviously it must be done carefully to make sure that the
* replacement `free' can deallocate memory allocated by the earlier
* `alloc' function(s).
*/
struct lzma_allocator
{
/**
* \brief Pointer to a custom memory allocation function
*
* If you don't want a custom allocator, but still want
* custom free(), set this to NULL and liblzma will use
* the standard malloc().
*
* \param opaque lzma_allocator.opaque (see below)
* \param nmemb Number of elements like in calloc(). liblzma
* will always set nmemb to 1, so it is safe to
* ignore nmemb in a custom allocator if you like.
* The nmemb argument exists only for
* compatibility with zlib and libbzip2.
* \param size Size of an element in bytes.
* liblzma never sets this to zero.
*
* \return Pointer to the beginning of a memory block of
* `size' bytes, or NULL if allocation fails
* for some reason. When allocation fails, functions
* of liblzma return LZMA_MEM_ERROR.
*
* The allocator should not waste time zeroing the allocated buffers.
* This is not only about speed, but also memory usage, since the
* operating system kernel doesn't necessarily allocate the requested
* memory in physical memory until it is actually used. With small
* input files, liblzma may actually need only a fraction of the
* memory that it requested for allocation.
*
* \note LZMA_MEM_ERROR is also used when the size of the
* allocation would be greater than SIZE_MAX. Thus,
* don't assume that the custom allocator must have
* returned NULL if some function from liblzma
* returns LZMA_MEM_ERROR.
*/
void* function(void *opaque, size_t nmemb, size_t size) alloc;
/**
* \brief Pointer to a custom memory freeing function
*
* If you don't want a custom freeing function, but still
* want a custom allocator, set this to NULL and liblzma
* will use the standard free().
*
* \param opaque lzma_allocator.opaque (see below)
* \param ptr Pointer returned by lzma_allocator.alloc(),
* or when it is set to NULL, a pointer returned
* by the standard malloc().
*/
void function(void *opaque, void *ptr) free;
/**
* \brief Pointer passed to .alloc() and .free()
*
* opaque is passed as the first argument to lzma_allocator.alloc()
* and lzma_allocator.free(). This intended to ease implementing
* custom memory allocation functions for use with liblzma.
*
* If you don't need this, you should set this to NULL.
*/
void *opaque;
}
/**
* \brief Internal data structure
*
* The contents of this structure is not visible outside the library.
*/
struct lzma_internal {}
/**
* \brief Passing data to and from liblzma
*
* The lzma_stream structure is used for
* - passing pointers to input and output buffers to liblzma;
* - defining custom memory hander functions; and
* - holding a pointer to coder-specific internal data structures.
*
* Typical usage:
*
* - After allocating lzma_stream (on stack or with malloc()), it must be
* initialized to LZMA_STREAM_INIT (see LZMA_STREAM_INIT for details).
*
* - Initialize a coder to the lzma_stream, for example by using
* lzma_easy_encoder() or lzma_auto_decoder(). Some notes:
* - In contrast to zlib, strm->next_in and strm->next_out are
* ignored by all initialization functions, thus it is safe
* to not initialize them yet.
* - The initialization functions always set strm->total_in and
* strm->total_out to zero.
* - If the initialization function fails, no memory is left allocated
* that would require freeing with lzma_end() even if some memory was
* associated with the lzma_stream structure when the initialization
* function was called.
*
* - Use lzma_code() to do the actual work.
*
* - Once the coding has been finished, the existing lzma_stream can be
* reused. It is OK to reuse lzma_stream with different initialization
* function without calling lzma_end() first. Old allocations are
* automatically freed.
*
* - Finally, use lzma_end() to free the allocated memory. lzma_end() never
* frees the lzma_stream structure itself.
*
* Application may modify the values of total_in and total_out as it wants.
* They are updated by liblzma to match the amount of data read and
* written, but aren't used for anything else.
*/
struct lzma_stream
{
const(ubyte)* next_in; /**< Pointer to the next input byte. */
size_t avail_in; /**< Number of available input bytes in next_in. */
ulong total_in; /**< Total number of bytes read by liblzma. */
ubyte* next_out; /**< Pointer to the next output position. */
size_t avail_out; /**< Amount of free space in next_out. */
ulong total_out; /**< Total number of bytes written by liblzma. */
/**
* \brief Custom memory allocation functions
*
* In most cases this is NULL which makes liblzma use
* the standard malloc() and free().
*/
lzma_allocator *allocator;
/** Internal state is not visible to applications. */
lzma_internal *internal;
/*
* Reserved space to allow possible future extensions without
* breaking the ABI. Excluding the initialization of this structure,
* you should not touch these, because the names of these variables
* may change.
*/
void *reserved_ptr1;
void *reserved_ptr2;
void *reserved_ptr3;
void *reserved_ptr4;
ulong reserved_int1;
ulong reserved_int2;
size_t reserved_int3;
size_t reserved_int4;
lzma_reserved_enum reserved_enum1;
lzma_reserved_enum reserved_enum2;
}
/**
* Initialization for lzma_stream
*
* Not needed in d, just use lzma_stream.init
*/
//void LZMA_STREAM_INIT(){};
/**
* \brief Encode or decode data
*
* Once the lzma_stream has been successfully initialized (e.g. with
* lzma_stream_encoder()), the actual encoding or decoding is done
* using this function. The application has to update strm->next_in,
* strm->avail_in, strm->next_out, and strm->avail_out to pass input
* to and get output from liblzma.
*
* See the description of the coder-specific initialization function to find
* out what `action' values are supported by the coder.
*/
nothrow lzma_ret lzma_code(lzma_stream *strm, lzma_action action);
/**
* \brief Free memory allocated for the coder data structures
*
* \param strm Pointer to lzma_stream that is at least initialized
* with LZMA_STREAM_INIT.
*
* After lzma_end(strm), strm->internal is guaranteed to be NULL. No other
* members of the lzma_stream structure are touched.
*
* \note zlib indicates an error if application end()s unfinished
* stream structure. liblzma doesn't do this, and assumes that
* application knows what it is doing.
*/
nothrow void lzma_end(lzma_stream *strm);
/**
* \brief Get the memory usage of decoder filter chain
*
* This function is currently supported only when *strm has been initialized
* with a function that takes a memlimit argument. With other functions, you
* should use e.g. lzma_raw_encoder_memusage() or lzma_raw_decoder_memusage()
* to estimate the memory requirements.
*
* This function is useful e.g. after LZMA_MEMLIMIT_ERROR to find out how big
* the memory usage limit should have been to decode the input. Note that
* this may give misleading information if decoding .xz Streams that have
* multiple Blocks, because each Block can have different memory requirements.
*
* \return How much memory is currently allocated for the filter
* decoders. If no filter chain is currently allocated,
* some non-zero value is still returned, which is less than
* or equal to what any filter chain would indicate as its
* memory requirement.
*
* If this function isn't supported by *strm or some other error
* occurs, zero is returned.
*/
nothrow pure ulong lzma_memusage(const lzma_stream *strm);
/**
* \brief Get the current memory usage limit
*
* This function is supported only when *strm has been initialized with
* a function that takes a memlimit argument.
*
* \return On success, the current memory usage limit is returned
* (always non-zero). On error, zero is returned.
*/
nothrow pure ulong lzma_memlimit_get(const lzma_stream *strm);
/**
* \brief Set the memory usage limit
*
* This function is supported only when *strm has been initialized with
* a function that takes a memlimit argument.
*
* \return - LZMA_OK: New memory usage limit successfully set.
* - LZMA_MEMLIMIT_ERROR: The new limit is too small.
* The limit was not changed.
* - LZMA_PROG_ERROR: Invalid arguments, e.g. *strm doesn't
* support memory usage limit or memlimit was zero.
*/
nothrow lzma_ret lzma_memlimit_set(lzma_stream *strm, ulong memlimit);
+91
View File
@@ -0,0 +1,91 @@
/**
* \file lzma/bcj.h
* \brief Branch/Call/Jump conversion filters
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.bcj;
import deimos.lzma;
extern(C):
/* Filter IDs for lzma_filter.id */
enum LZMA_FILTER_X86 = 0x04UL;
/**<
* Filter for x86 binaries
*/
enum LZMA_FILTER_POWERPC = 0x05UL;
/**<
* Filter for Big endian PowerPC binaries
*/
enum LZMA_FILTER_IA64 = 0x06UL;
/**<
* Filter for IA-64 (Itanium) binaries.
*/
enum LZMA_FILTER_ARM = 0x07UL;
/**<
* Filter for ARM binaries.
*/
enum LZMA_FILTER_ARMTHUMB = 0x08UL;
/**<
* Filter for ARM-Thumb binaries.
*/
enum LZMA_FILTER_SPARC = 0x09UL;
/**<
* Filter for SPARC binaries.
*/
/**
* \brief Options for BCJ filters
*
* The BCJ filters never change the size of the data. Specifying options
* for them is optional: if pointer to options is NULL, default value is
* used. You probably never need to specify options to BCJ filters, so just
* set the options pointer to NULL and be happy.
*
* If options with non-default values have been specified when encoding,
* the same options must also be specified when decoding.
*
* \note At the moment, none of the BCJ filters support
* LZMA_SYNC_FLUSH. If LZMA_SYNC_FLUSH is specified,
* LZMA_OPTIONS_ERROR will be returned. If there is need,
* partial support for LZMA_SYNC_FLUSH can be added in future.
* Partial means that flushing would be possible only at
* offsets that are multiple of 2, 4, or 16 depending on
* the filter, except x86 which cannot be made to support
* LZMA_SYNC_FLUSH predictably.
*/
struct lzma_options_bcj
{
/**
* \brief Start offset for conversions
*
* This setting is useful only when the same filter is used
* _separately_ for multiple sections of the same executable file,
* and the sections contain cross-section branch/call/jump
* instructions. In that case it is beneficial to set the start
* offset of the non-first sections so that the relative addresses
* of the cross-section branch/call/jump instructions will use the
* same absolute addresses as in the first section.
*
* When the pointer to options is NULL, the default value (zero)
* is used.
*/
uint start_offset;
}
+523
View File
@@ -0,0 +1,523 @@
/**
* \file lzma/block.h
* \brief .xz Block handling
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.block;
import deimos.lzma;
extern(C):
//TODO: initialize fields to void?
/**
* \brief Options for the Block and Block Header encoders and decoders
*
* Different Block handling functions use different parts of this structure.
* Some read some members, other functions write, and some do both. Only the
* members listed for reading need to be initialized when the specified
* functions are called. The members marked for writing will be assigned
* new values at some point either by calling the given function or by
* later calls to lzma_code().
*/
struct lzma_block
{
/**
* \brief Block format version
*
* To prevent API and ABI breakages if new features are needed in
* the Block field, a version number is used to indicate which
* fields in this structure are in use. For now, version must always
* be zero. With non-zero version, most Block related functions will
* return LZMA_OPTIONS_ERROR.
*
* Read by:
* - All functions that take pointer to lzma_block as argument,
* including lzma_block_header_decode().
*
* Written by:
* - lzma_block_header_decode()
*/
uint version_;
/**
* \brief Size of the Block Header field
*
* This is always a multiple of four.
*
* Read by:
* - lzma_block_header_encode()
* - lzma_block_header_decode()
* - lzma_block_compressed_size()
* - lzma_block_unpadded_size()
* - lzma_block_total_size()
* - lzma_block_decoder()
* - lzma_block_buffer_decode()
*
* Written by:
* - lzma_block_header_size()
* - lzma_block_buffer_encode()
*/
uint header_size;
enum LZMA_BLOCK_HEADER_SIZE_MIN = 8;
enum LZMA_BLOCK_HEADER_SIZE_MAX = 1024;
/**
* \brief Type of integrity Check
*
* The Check ID is not stored into the Block Header, thus its value
* must be provided also when decoding.
*
* Read by:
* - lzma_block_header_encode()
* - lzma_block_header_decode()
* - lzma_block_compressed_size()
* - lzma_block_unpadded_size()
* - lzma_block_total_size()
* - lzma_block_encoder()
* - lzma_block_decoder()
* - lzma_block_buffer_encode()
* - lzma_block_buffer_decode()
*/
lzma_check check;
/**
* \brief Size of the Compressed Data in bytes
*
* Encoding: If this is not LZMA_VLI_UNKNOWN, Block Header encoder
* will store this value to the Block Header. Block encoder doesn't
* care about this value, but will set it once the encoding has been
* finished.
*
* Decoding: If this is not LZMA_VLI_UNKNOWN, Block decoder will
* verify that the size of the Compressed Data field matches
* compressed_size.
*
* Usually you don't know this value when encoding in streamed mode,
* and thus cannot write this field into the Block Header.
*
* In non-streamed mode you can reserve space for this field before
* encoding the actual Block. After encoding the data, finish the
* Block by encoding the Block Header. Steps in detail:
*
* - Set compressed_size to some big enough value. If you don't know
* better, use LZMA_VLI_MAX, but remember that bigger values take
* more space in Block Header.
*
* - Call lzma_block_header_size() to see how much space you need to
* reserve for the Block Header.
*
* - Encode the Block using lzma_block_encoder() and lzma_code().
* It sets compressed_size to the correct value.
*
* - Use lzma_block_header_encode() to encode the Block Header.
* Because space was reserved in the first step, you don't need
* to call lzma_block_header_size() anymore, because due to
* reserving, header_size has to be big enough. If it is "too big",
* lzma_block_header_encode() will add enough Header Padding to
* make Block Header to match the size specified by header_size.
*
* Read by:
* - lzma_block_header_size()
* - lzma_block_header_encode()
* - lzma_block_compressed_size()
* - lzma_block_unpadded_size()
* - lzma_block_total_size()
* - lzma_block_decoder()
* - lzma_block_buffer_decode()
*
* Written by:
* - lzma_block_header_decode()
* - lzma_block_compressed_size()
* - lzma_block_encoder()
* - lzma_block_decoder()
* - lzma_block_buffer_encode()
* - lzma_block_buffer_decode()
*/
lzma_vli compressed_size;
/**
* \brief Uncompressed Size in bytes
*
* This is handled very similarly to compressed_size above.
*
* uncompressed_size is needed by fewer functions than
* compressed_size. This is because uncompressed_size isn't
* needed to validate that Block stays within proper limits.
*
* Read by:
* - lzma_block_header_size()
* - lzma_block_header_encode()
* - lzma_block_decoder()
* - lzma_block_buffer_decode()
*
* Written by:
* - lzma_block_header_decode()
* - lzma_block_encoder()
* - lzma_block_decoder()
* - lzma_block_buffer_encode()
* - lzma_block_buffer_decode()
*/
lzma_vli uncompressed_size;
/**
* \brief Array of filters
*
* There can be 1-4 filters. The end of the array is marked with
* .id = LZMA_VLI_UNKNOWN.
*
* Read by:
* - lzma_block_header_size()
* - lzma_block_header_encode()
* - lzma_block_encoder()
* - lzma_block_decoder()
* - lzma_block_buffer_encode()
* - lzma_block_buffer_decode()
*
* Written by:
* - lzma_block_header_decode(): Note that this does NOT free()
* the old filter options structures. All unused filters[] will
* have .id == LZMA_VLI_UNKNOWN and .options == NULL. If
* decoding fails, all filters[] are guaranteed to be
* LZMA_VLI_UNKNOWN and NULL.
*
* \note Because of the array is terminated with
* .id = LZMA_VLI_UNKNOWN, the actual array must
* have LZMA_FILTERS_MAX + 1 members or the Block
* Header decoder will overflow the buffer.
*/
lzma_filter *filters;
/**
* \brief Raw value stored in the Check field
*
* After successful coding, the first lzma_check_size(check) bytes
* of this array contain the raw value stored in the Check field.
*
* Note that CRC32 and CRC64 are stored in little endian byte order.
* Take it into account if you display the Check values to the user.
*
* Written by:
* - lzma_block_encoder()
* - lzma_block_decoder()
* - lzma_block_buffer_encode()
* - lzma_block_buffer_decode()
*/
ubyte[LZMA_CHECK_SIZE_MAX] raw_check;
/*
* Reserved space to allow possible future extensions without
* breaking the ABI. You should not touch these, because the names
* of these variables may change. These are and will never be used
* with the currently supported options, so it is safe to leave these
* uninitialized.
*/
void *reserved_ptr1;
void *reserved_ptr2;
void *reserved_ptr3;
uint reserved_int1;
uint reserved_int2;
lzma_vli reserved_int3;
lzma_vli reserved_int4;
lzma_vli reserved_int5;
lzma_vli reserved_int6;
lzma_vli reserved_int7;
lzma_vli reserved_int8;
lzma_reserved_enum reserved_enum1;
lzma_reserved_enum reserved_enum2;
lzma_reserved_enum reserved_enum3;
lzma_reserved_enum reserved_enum4;
lzma_bool reserved_bool1;
lzma_bool reserved_bool2;
lzma_bool reserved_bool3;
lzma_bool reserved_bool4;
lzma_bool reserved_bool5;
lzma_bool reserved_bool6;
lzma_bool reserved_bool7;
lzma_bool reserved_bool8;
}
/**
* \brief Decode the Block Header Size field
*
* To decode Block Header using lzma_block_header_decode(), the size of the
* Block Header has to be known and stored into lzma_block.header_size.
* The size can be calculated from the first byte of a Block using this macro.
* Note that if the first byte is 0x00, it indicates beginning of Index; use
* this macro only when the byte is not 0x00.
*
* There is no encoding macro, because Block Header encoder is enough for that.
*/
template lzma_block_header_size_decode(uint b)
{
enum lzma_block_header_size_decode = (b+1)*4;
}
/**
* \brief Calculate Block Header Size
*
* Calculate the minimum size needed for the Block Header field using the
* settings specified in the lzma_block structure. Note that it is OK to
* increase the calculated header_size value as long as it is a multiple of
* four and doesn't exceed LZMA_BLOCK_HEADER_SIZE_MAX. Increasing header_size
* just means that lzma_block_header_encode() will add Header Padding.
*
* \return - LZMA_OK: Size calculated successfully and stored to
* block->header_size.
* - LZMA_OPTIONS_ERROR: Unsupported version, filters or
* filter options.
* - LZMA_PROG_ERROR: Invalid values like compressed_size == 0.
*
* \note This doesn't check that all the options are valid i.e. this
* may return LZMA_OK even if lzma_block_header_encode() or
* lzma_block_encoder() would fail. If you want to validate the
* filter chain, consider using lzma_memlimit_encoder() which as
* a side-effect validates the filter chain.
*/
nothrow lzma_ret lzma_block_header_size(lzma_block *block);
/**
* \brief Encode Block Header
*
* The caller must have calculated the size of the Block Header already with
* lzma_block_header_size(). If a value larger than the one calculated by
* lzma_block_header_size() is used, the Block Header will be padded to the
* specified size.
*
* \param out Beginning of the output buffer. This must be
* at least block->header_size bytes.
* \param block Block options to be encoded.
*
* \return - LZMA_OK: Encoding was successful. block->header_size
* bytes were written to output buffer.
* - LZMA_OPTIONS_ERROR: Invalid or unsupported options.
* - LZMA_PROG_ERROR: Invalid arguments, for example
* block->header_size is invalid or block->filters is NULL.
*/
nothrow lzma_ret lzma_block_header_encode(const (lzma_block)* block, ubyte* out_);
/**
* \brief Decode Block Header
*
* block->version should be set to the highest value supported by the
* application; currently the only possible version is zero. This function
* will set version to the lowest value that still supports all the features
* required by the Block Header.
*
* The size of the Block Header must have already been decoded with
* lzma_block_header_size_decode() macro and stored to block->header_size.
*
* block->filters must have been allocated, but they don't need to be
* initialized (possible existing filter options are not freed).
*
* \param block Destination for Block options.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() (and also free()
* if an error occurs).
* \param in Beginning of the input buffer. This must be
* at least block->header_size bytes.
*
* \return - LZMA_OK: Decoding was successful. block->header_size
* bytes were read from the input buffer.
* - LZMA_OPTIONS_ERROR: The Block Header specifies some
* unsupported options such as unsupported filters. This can
* happen also if block->version was set to a too low value
* compared to what would be required to properly represent
* the information stored in the Block Header.
* - LZMA_DATA_ERROR: Block Header is corrupt, for example,
* the CRC32 doesn't match.
* - LZMA_PROG_ERROR: Invalid arguments, for example
* block->header_size is invalid or block->filters is NULL.
*/
nothrow lzma_ret lzma_block_header_decode(lzma_block* block,
lzma_allocator* allocator, const(ubyte)* in_);
/**
* \brief Validate and set Compressed Size according to Unpadded Size
*
* Block Header stores Compressed Size, but Index has Unpadded Size. If the
* application has already parsed the Index and is now decoding Blocks,
* it can calculate Compressed Size from Unpadded Size. This function does
* exactly that with error checking:
*
* - Compressed Size calculated from Unpadded Size must be positive integer,
* that is, Unpadded Size must be big enough that after Block Header and
* Check fields there's still at least one byte for Compressed Size.
*
* - If Compressed Size was present in Block Header, the new value
* calculated from Unpadded Size is compared against the value
* from Block Header.
*
* \note This function must be called _after_ decoding the Block Header
* field so that it can properly validate Compressed Size if it
* was present in Block Header.
*
* \return - LZMA_OK: block->compressed_size was set successfully.
* - LZMA_DATA_ERROR: unpadded_size is too small compared to
* block->header_size and lzma_check_size(block->check).
* - LZMA_PROG_ERROR: Some values are invalid. For example,
* block->header_size must be a multiple of four and
* between 8 and 1024 inclusive.
*/
nothrow lzma_ret lzma_block_compressed_size(
lzma_block* block, lzma_vli unpadded_size);
/**
* \brief Calculate Unpadded Size
*
* The Index field stores Unpadded Size and Uncompressed Size. The latter
* can be taken directly from the lzma_block structure after coding a Block,
* but Unpadded Size needs to be calculated from Block Header Size,
* Compressed Size, and size of the Check field. This is where this function
* is needed.
*
* \return Unpadded Size on success, or zero on error.
*/
nothrow lzma_vli lzma_block_unpadded_size(const lzma_block* block);
/**
* \brief Calculate the total encoded size of a Block
*
* This is equivalent to lzma_block_unpadded_size() except that the returned
* value includes the size of the Block Padding field.
*
* \return On success, total encoded size of the Block. On error,
* zero is returned.
*/
nothrow pure lzma_vli lzma_block_total_size(const(lzma_block*) block);
/**
* \brief Initialize .xz Block encoder
*
* Valid actions for lzma_code() are LZMA_RUN, LZMA_SYNC_FLUSH (only if the
* filter chain supports it), and LZMA_FINISH.
*
* \return - LZMA_OK: All good, continue with lzma_code().
* - LZMA_MEM_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_UNSUPPORTED_CHECK: block->check specifies a Check ID
* that is not supported by this buid of liblzma. Initializing
* the encoder failed.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_block_encoder(
lzma_stream* strm, lzma_block* block);
/**
* \brief Initialize .xz Block decoder
*
* Valid actions for lzma_code() are LZMA_RUN and LZMA_FINISH. Using
* LZMA_FINISH is not required. It is supported only for convenience.
*
* \return - LZMA_OK: All good, continue with lzma_code().
* - LZMA_UNSUPPORTED_CHECK: Initialization was successful, but
* the given Check ID is not supported, thus Check will be
* ignored.
* - LZMA_PROG_ERROR
* - LZMA_MEM_ERROR
*/
nothrow lzma_ret lzma_block_decoder(
lzma_stream *strm, lzma_block *block);
/**
* \brief Calculate maximum output size for single-call Block encoding
*
* This is equivalent to lzma_stream_buffer_bound() but for .xz Blocks.
* See the documentation of lzma_stream_buffer_bound().
*/
nothrow size_t lzma_block_buffer_bound(size_t uncompressed_size);
/**
* \brief Single-call .xz Block encoder
*
* In contrast to the multi-call encoder initialized with
* lzma_block_encoder(), this function encodes also the Block Header. This
* is required to make it possible to write appropriate Block Header also
* in case the data isn't compressible, and different filter chain has to be
* used to encode the data in uncompressed form using uncompressed chunks
* of the LZMA2 filter.
*
* When the data isn't compressible, header_size, compressed_size, and
* uncompressed_size are set just like when the data was compressible, but
* it is possible that header_size is too small to hold the filter chain
* specified in block->filters, because that isn't necessarily the filter
* chain that was actually used to encode the data. lzma_block_unpadded_size()
* still works normally, because it doesn't read the filters array.
*
* \param block Block options: block->version, block->check,
* and block->filters must have been initialized.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_size Size of the input buffer
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_BUF_ERROR: Not enough output buffer space.
* - LZMA_UNSUPPORTED_CHECK
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
* - LZMA_DATA_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_block_buffer_encode(
lzma_block *block, lzma_allocator *allocator,
const(ubyte)* in_, size_t in_size,
ubyte* out_, size_t *out_pos, size_t out_size);
/**
* \brief Single-call .xz Block decoder
*
* This is single-call equivalent of lzma_block_decoder(), and requires that
* the caller has already decoded Block Header and checked its memory usage.
*
* \param block Block options just like with lzma_block_decoder().
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_pos The next byte will be read from in[*in_pos].
* *in_pos is updated only if decoding succeeds.
* \param in_size Size of the input buffer; the first byte that
* won't be read is in[in_size].
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Decoding was successful.
* - LZMA_OPTIONS_ERROR
* - LZMA_DATA_ERROR
* - LZMA_MEM_ERROR
* - LZMA_BUF_ERROR: Output buffer was too small.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_block_buffer_decode(
lzma_block *block, lzma_allocator *allocator,
const(ubyte)* in_, size_t *in_pos, size_t in_size,
ubyte* out_, size_t *out_pos, size_t out_size);
+141
View File
@@ -0,0 +1,141 @@
/**
* \file lzma/check.h
* \brief Integrity checks
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.check;
import deimos.lzma;
extern(C):
/**
* \brief Type of the integrity check (Check ID)
*
* The .xz format supports multiple types of checks that are calculated
* from the uncompressed data. They vary in both speed and ability to
* detect errors.
*/
enum lzma_check
{
LZMA_CHECK_NONE = 0,
/**<
* No Check is calculated.
*
* Size of the Check field: 0 bytes
*/
LZMA_CHECK_CRC32 = 1,
/**<
* CRC32 using the polynomial from the IEEE 802.3 standard
*
* Size of the Check field: 4 bytes
*/
LZMA_CHECK_CRC64 = 4,
/**<
* CRC64 using the polynomial from the ECMA-182 standard
*
* Size of the Check field: 8 bytes
*/
LZMA_CHECK_SHA256 = 10
}
/**
* \brief Maximum valid Check ID
*
* The .xz file format specification specifies 16 Check IDs (0-15). Some
* of them are only reserved, that is, no actual Check algorithm has been
* assigned. When decoding, liblzma still accepts unknown Check IDs for
* future compatibility. If a valid but unsupported Check ID is detected,
* liblzma can indicate a warning; see the flags LZMA_TELL_NO_CHECK,
* LZMA_TELL_UNSUPPORTED_CHECK, and LZMA_TELL_ANY_CHECK in container.h.
*/
enum LZMA_CHECK_ID_MAX = 15;
/**
* \brief Test if the given Check ID is supported
*
* Return true if the given Check ID is supported by this liblzma build.
* Otherwise false is returned. It is safe to call this with a value that
* is not in the range [0, 15]; in that case the return value is always false.
*
* You can assume that LZMA_CHECK_NONE and LZMA_CHECK_CRC32 are always
* supported (even if liblzma is built with limited features).
*/
nothrow lzma_bool lzma_check_is_supported(lzma_check check);
/**
* \brief Get the size of the Check field with the given Check ID
*
* Although not all Check IDs have a check algorithm associated, the size of
* every Check is already frozen. This function returns the size (in bytes) of
* the Check field with the specified Check ID. The values are:
* { 0, 4, 4, 4, 8, 8, 8, 16, 16, 16, 32, 32, 32, 64, 64, 64 }
*
* If the argument is not in the range [0, 15], UINT32_MAX is returned.
*/
nothrow uint lzma_check_size(lzma_check check);
/**
* \brief Maximum size of a Check field
*/
enum LZMA_CHECK_SIZE_MAX = 64;
/**
* \brief Calculate CRC32
*
* Calculate CRC32 using the polynomial from the IEEE 802.3 standard.
*
* \param buf Pointer to the input buffer
* \param size Size of the input buffer
* \param crc Previously returned CRC value. This is used to
* calculate the CRC of a big buffer in smaller chunks.
* Set to zero when starting a new calculation.
*
* \return Updated CRC value, which can be passed to this function
* again to continue CRC calculation.
*/
nothrow pure uint lzma_crc32(
const(ubyte)* buf, size_t size, uint crc);
/**
* \brief Calculate CRC64
*
* Calculate CRC64 using the polynomial from the ECMA-182 standard.
*
* This function is used similarly to lzma_crc32(). See its documentation.
*/
nothrow pure ulong lzma_crc64(
const(ubyte)* buf, size_t size, ulong crc);
/*
* SHA-256 functions are currently not exported to public API.
* Contact Lasse Collin if you think it should be.
*/
/**
* \brief Get the type of the integrity check
*
* This function can be called only immediately after lzma_code() has
* returned LZMA_NO_CHECK, LZMA_UNSUPPORTED_CHECK, or LZMA_GET_CHECK.
* Calling this function in any other situation has undefined behavior.
*/
nothrow lzma_check lzma_get_check(const lzma_stream *strm);
+413
View File
@@ -0,0 +1,413 @@
/**
* \file lzma/container.h
* \brief File formats
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.container;
import deimos.lzma;
extern(C):
/************
* Encoding *
************/
/**
* \brief Default compression preset
*
* It's not straightforward to recommend a default preset, because in some
* cases keeping the resource usage relatively low is more important that
* getting the maximum compression ratio.
*/
enum uint LZMA_PRESET_DEFAULT = 6U;
/**
* \brief Mask for preset level
*
* This is useful only if you need to extract the level from the preset
* variable. That should be rare.
*/
enum uint LZMA_PRESET_LEVEL_MASK = 0x1FU;
/*
* Preset flags
*
* Currently only one flag is defined.
*/
/**
* \brief Extreme compression preset
*
* This flag modifies the preset to make the encoding significantly slower
* while improving the compression ratio only marginally. This is useful
* when you don't mind wasting time to get as small result as possible.
*
* This flag doesn't affect the memory usage requirements of the decoder (at
* least not significantly). The memory usage of the encoder may be increased
* a little but only at the lowest preset levels (0-3).
*/
enum uint LZMA_PRESET_EXTREME = (1U << 31);
/**
* \brief Calculate approximate memory usage of easy encoder
*
* This function is a wrapper for lzma_raw_encoder_memusage().
*
* \param preset Compression preset (level and possible flags)
*
* \return Number of bytes of memory required for the given
* preset when encoding. If an error occurs, for example
* due to unsupported preset, UINT64_MAX is returned.
*/
nothrow pure ulong lzma_easy_encoder_memusage(uint preset);
/**
* \brief Calculate approximate decoder memory usage of a preset
*
* This function is a wrapper for lzma_raw_decoder_memusage().
*
* \param preset Compression preset (level and possible flags)
*
* \return Number of bytes of memory required to decompress a file
* that was compressed using the given preset. If an error
* occurs, for example due to unsupported preset, UINT64_MAX
* is returned.
*/
nothrow pure ulong lzma_easy_decoder_memusage(uint preset);
/**
* \brief Initialize .xz Stream encoder using a preset number
*
* This function is intended for those who just want to use the basic features
* if liblzma (that is, most developers out there).
*
* \param strm Pointer to lzma_stream that is at least initialized
* with LZMA_STREAM_INIT.
* \param preset Compression preset to use. A preset consist of level
* number and zero or more flags. Usually flags aren't
* used, so preset is simply a number [0, 9] which match
* the options -0 ... -9 of the xz command line tool.
* Additional flags can be be set using bitwise-or with
* the preset level number, e.g. 6 | LZMA_PRESET_EXTREME.
* \param check Integrity check type to use. See check.h for available
* checks. The xz command line tool defaults to
* LZMA_CHECK_CRC64, which is a good choice if you are
* unsure. LZMA_CHECK_CRC32 is good too as long as the
* uncompressed file is not many gigabytes.
*
* \return - LZMA_OK: Initialization succeeded. Use lzma_code() to
* encode your data.
* - LZMA_MEM_ERROR: Memory allocation failed.
* - LZMA_OPTIONS_ERROR: The given compression preset is not
* supported by this build of liblzma.
* - LZMA_UNSUPPORTED_CHECK: The given check type is not
* supported by this liblzma build.
* - LZMA_PROG_ERROR: One or more of the parameters have values
* that will never be valid. For example, strm == NULL.
*
* If initialization fails (return value is not LZMA_OK), all the memory
* allocated for *strm by liblzma is always freed. Thus, there is no need
* to call lzma_end() after failed initialization.
*
* If initialization succeeds, use lzma_code() to do the actual encoding.
* Valid values for `action' (the second argument of lzma_code()) are
* LZMA_RUN, LZMA_SYNC_FLUSH, LZMA_FULL_FLUSH, and LZMA_FINISH. In future,
* there may be compression levels or flags that don't support LZMA_SYNC_FLUSH.
*/
nothrow lzma_ret lzma_easy_encoder(
lzma_stream *strm, uint preset, lzma_check check);
/**
* \brief Single-call .xz Stream encoding using a preset number
*
* The maximum required output buffer size can be calculated with
* lzma_stream_buffer_bound().
*
* \param preset Compression preset to use. See the description
* in lzma_easy_encoder().
* \param check Type of the integrity check to calculate from
* uncompressed data.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_size Size of the input buffer
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_BUF_ERROR: Not enough output buffer space.
* - LZMA_UNSUPPORTED_CHECK
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
* - LZMA_DATA_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_easy_buffer_encode(
uint preset, lzma_check check,
lzma_allocator *allocator, const(ubyte)* in_, size_t in_size,
ubyte* out_, size_t *out_pos, size_t out_size);
/**
* \brief Initialize .xz Stream encoder using a custom filter chain
*
* \param strm Pointer to properly prepared lzma_stream
* \param filters Array of filters. This must be terminated with
* filters[n].id = LZMA_VLI_UNKNOWN. See filter.h for
* more information.
* \param check Type of the integrity check to calculate from
* uncompressed data.
*
* \return - LZMA_OK: Initialization was successful.
* - LZMA_MEM_ERROR
* - LZMA_UNSUPPORTED_CHECK
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_stream_encoder(lzma_stream *strm,
const lzma_filter *filters, lzma_check check);
/**
* \brief Initialize .lzma encoder (legacy file format)
*
* The .lzma format is sometimes called the LZMA_Alone format, which is the
* reason for the name of this function. The .lzma format supports only the
* LZMA1 filter. There is no support for integrity checks like CRC32.
*
* Use this function if and only if you need to create files readable by
* legacy LZMA tools such as LZMA Utils 4.32.x. Moving to the .xz format
* is strongly recommended.
*
* The valid action values for lzma_code() are LZMA_RUN and LZMA_FINISH.
* No kind of flushing is supported, because the file format doesn't make
* it possible.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_alone_encoder(
lzma_stream *strm, const lzma_options_lzma *options);
/**
* \brief Calculate output buffer size for single-call Stream encoder
*
* When trying to compress uncompressible data, the encoded size will be
* slightly bigger than the input data. This function calculates how much
* output buffer space is required to be sure that lzma_stream_buffer_encode()
* doesn't return LZMA_BUF_ERROR.
*
* The calculated value is not exact, but it is guaranteed to be big enough.
* The actual maximum output space required may be slightly smaller (up to
* about 100 bytes). This should not be a problem in practice.
*
* If the calculated maximum size doesn't fit into size_t or would make the
* Stream grow past LZMA_VLI_MAX (which should never happen in practice),
* zero is returned to indicate the error.
*
* \note The limit calculated by this function applies only to
* single-call encoding. Multi-call encoding may (and probably
* will) have larger maximum expansion when encoding
* uncompressible data. Currently there is no function to
* calculate the maximum expansion of multi-call encoding.
*/
nothrow size_t lzma_stream_buffer_bound(size_t uncompressed_size);
/**
* \brief Single-call .xz Stream encoder
*
* \param filters Array of filters. This must be terminated with
* filters[n].id = LZMA_VLI_UNKNOWN. See filter.h
* for more information.
* \param check Type of the integrity check to calculate from
* uncompressed data.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_size Size of the input buffer
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_BUF_ERROR: Not enough output buffer space.
* - LZMA_UNSUPPORTED_CHECK
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
* - LZMA_DATA_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_stream_buffer_encode(
lzma_filter *filters, lzma_check check,
lzma_allocator *allocator, const(ubyte)* in_, size_t in_size,
ubyte* out_, size_t *out_pos, size_t out_size);
/************
* Decoding *
************/
/**
* This flag makes lzma_code() return LZMA_NO_CHECK if the input stream
* being decoded has no integrity check. Note that when used with
* lzma_auto_decoder(), all .lzma files will trigger LZMA_NO_CHECK
* if LZMA_TELL_NO_CHECK is used.
*/
enum uint LZMA_TELL_NO_CHECK = 0x01U;
/**
* This flag makes lzma_code() return LZMA_UNSUPPORTED_CHECK if the input
* stream has an integrity check, but the type of the integrity check is not
* supported by this liblzma version or build. Such files can still be
* decoded, but the integrity check cannot be verified.
*/
enum uint LZMA_TELL_UNSUPPORTED_CHECK = 0x02U;
/**
* This flag makes lzma_code() return LZMA_GET_CHECK as soon as the type
* of the integrity check is known. The type can then be got with
* lzma_get_check().
*/
enum uint LZMA_TELL_ANY_CHECK = 0x04U;
/**
* This flag enables decoding of concatenated files with file formats that
* allow concatenating compressed files as is. From the formats currently
* supported by liblzma, only the .xz format allows concatenated files.
* Concatenated files are not allowed with the legacy .lzma format.
*
* This flag also affects the usage of the `action' argument for lzma_code().
* When LZMA_CONCATENATED is used, lzma_code() won't return LZMA_STREAM_END
* unless LZMA_FINISH is used as `action'. Thus, the application has to set
* LZMA_FINISH in the same way as it does when encoding.
*
* If LZMA_CONCATENATED is not used, the decoders still accept LZMA_FINISH
* as `action' for lzma_code(), but the usage of LZMA_FINISH isn't required.
*/
enum uint LZMA_CONCATENATED = 0x08U;
/**
* \brief Initialize .xz Stream decoder
*
* \param strm Pointer to properly prepared lzma_stream
* \param memlimit Memory usage limit as bytes. Use UINT64_MAX
* to effectively disable the limiter.
* \param flags Bitwise-or of zero or more of the decoder flags:
* LZMA_TELL_NO_CHECK, LZMA_TELL_UNSUPPORTED_CHECK,
* LZMA_TELL_ANY_CHECK, LZMA_CONCATENATED
*
* \return - LZMA_OK: Initialization was successful.
* - LZMA_MEM_ERROR: Cannot allocate memory.
* - LZMA_OPTIONS_ERROR: Unsupported flags
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_stream_decoder(
lzma_stream *strm, ulong memlimit, uint flags);
/**
* \brief Decode .xz Streams and .lzma files with autodetection
*
* This decoder autodetects between the .xz and .lzma file formats, and
* calls lzma_stream_decoder() or lzma_alone_decoder() once the type
* of the input file has been detected.
*
* \param strm Pointer to properly prepared lzma_stream
* \param memlimit Memory usage limit as bytes. Use UINT64_MAX
* to effectively disable the limiter.
* \param flags Bitwise-or of flags, or zero for no flags.
*
* \return - LZMA_OK: Initialization was successful.
* - LZMA_MEM_ERROR: Cannot allocate memory.
* - LZMA_OPTIONS_ERROR: Unsupported flags
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_auto_decoder(
lzma_stream *strm, ulong memlimit, uint flags);
/**
* \brief Initialize .lzma decoder (legacy file format)
*
* Valid `action' arguments to lzma_code() are LZMA_RUN and LZMA_FINISH.
* There is no need to use LZMA_FINISH, but allowing it may simplify
* certain types of applications.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_alone_decoder(
lzma_stream *strm, ulong memlimit);
/**
* \brief Single-call .xz Stream decoder
*
* \param memlimit Pointer to how much memory the decoder is allowed
* to allocate. The value pointed by this pointer is
* modified if and only if LZMA_MEMLIMIT_ERROR is
* returned.
* \param flags Bitwise-or of zero or more of the decoder flags:
* LZMA_TELL_NO_CHECK, LZMA_TELL_UNSUPPORTED_CHECK,
* LZMA_CONCATENATED. Note that LZMA_TELL_ANY_CHECK
* is not allowed and will return LZMA_PROG_ERROR.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_pos The next byte will be read from in[*in_pos].
* *in_pos is updated only if decoding succeeds.
* \param in_size Size of the input buffer; the first byte that
* won't be read is in[in_size].
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if decoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Decoding was successful.
* - LZMA_FORMAT_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_DATA_ERROR
* - LZMA_NO_CHECK: This can be returned only if using
* the LZMA_TELL_NO_CHECK flag.
* - LZMA_UNSUPPORTED_CHECK: This can be returned only if using
* the LZMA_TELL_UNSUPPORTED_CHECK flag.
* - LZMA_MEM_ERROR
* - LZMA_MEMLIMIT_ERROR: Memory usage limit was reached.
* The minimum required memlimit value was stored to *memlimit.
* - LZMA_BUF_ERROR: Output buffer was too small.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_stream_buffer_decode(
ulong *memlimit, uint flags, lzma_allocator *allocator,
const (ubyte)* in_, size_t *in_pos, size_t in_size,
ubyte* out_, size_t *out_pos, size_t out_size);
+79
View File
@@ -0,0 +1,79 @@
/**
* \file lzma/delta.h
* \brief Delta filter
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.delta;
import deimos.lzma;
extern(C):
/**
* \brief Filter ID
*
* Filter ID of the Delta filter. This is used as lzma_filter.id.
*/
enum LZMA_FILTER_DELTA = 0x03UL;
/**
* \brief Type of the delta calculation
*
* Currently only byte-wise delta is supported. Other possible types could
* be, for example, delta of 16/32/64-bit little/big endian integers, but
* these are not currently planned since byte-wise delta is almost as good.
*/
enum lzma_delta_type
{
LZMA_DELTA_TYPE_BYTE
}
/**
* \brief Options for the Delta filter
*
* These options are needed by both encoder and decoder.
*/
struct lzma_options_delta
{
/** For now, this must always be LZMA_DELTA_TYPE_BYTE. */
lzma_delta_type type;
/**
* \brief Delta distance
*
* With the only currently supported type, LZMA_DELTA_TYPE_BYTE,
* the distance is as bytes.
*
* Examples:
* - 16-bit stereo audio: distance = 4 bytes
* - 24-bit RGB image data: distance = 3 bytes
*/
uint dist;
enum LZMA_DELTA_DIST_MIN = 1;
enum LZMA_DELTA_DIST_MAX = 256;
/*
* Reserved space to allow possible future extensions without
* breaking the ABI. You should not touch these, because the names
* of these variables may change. These are and will never be used
* when type is LZMA_DELTA_TYPE_BYTE, so it is safe to leave these
* uninitialized.
*/
uint reserved_int1;
uint reserved_int2;
uint reserved_int3;
uint reserved_int4;
void *reserved_ptr1;
void *reserved_ptr2;
}
+416
View File
@@ -0,0 +1,416 @@
/**
* \file lzma/filter.h
* \brief Common filter related types and functions
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.filter;
import deimos.lzma;
extern(C):
/**
* \brief Maximum number of filters in a chain
*
* A filter chain can have 1-4 filters, of which three are allowed to change
* the size of the data. Usually only one or two filters are needed.
*/
enum LZMA_FILTERS_MAX = 4;
/**
* \brief Filter options
*
* This structure is used to pass Filter ID and a pointer filter's
* options to liblzma. A few functions work with a single lzma_filter
* structure, while most functions expect a filter chain.
*
* A filter chain is indicated with an array of lzma_filter structures.
* The array is terminated with .id = LZMA_VLI_UNKNOWN. Thus, the filter
* array must have LZMA_FILTERS_MAX + 1 elements (that is, five) to
* be able to hold any arbitrary filter chain. This is important when
* using lzma_block_header_decode() from block.h, because too small
* array would make liblzma write past the end of the filters array.
*/
struct lzma_filter
{
/**
* \brief Filter ID
*
* Use constants whose name begin with `LZMA_FILTER_' to specify
* different filters. In an array of lzma_filter structures, use
* LZMA_VLI_UNKNOWN to indicate end of filters.
*
* \note This is not an enum, because on some systems enums
* cannot be 64-bit.
*/
lzma_vli id;
/**
* \brief Pointer to filter-specific options structure
*
* If the filter doesn't need options, set this to NULL. If id is
* set to LZMA_VLI_UNKNOWN, options is ignored, and thus
* doesn't need be initialized.
*/
void *options;
}
/**
* \brief Test if the given Filter ID is supported for encoding
*
* Return true if the give Filter ID is supported for encoding by this
* liblzma build. Otherwise false is returned.
*
* There is no way to list which filters are available in this particular
* liblzma version and build. It would be useless, because the application
* couldn't know what kind of options the filter would need.
*/
nothrow lzma_bool lzma_filter_encoder_is_supported(lzma_vli id);
/**
* \brief Test if the given Filter ID is supported for decoding
*
* Return true if the give Filter ID is supported for decoding by this
* liblzma build. Otherwise false is returned.
*/
nothrow lzma_bool lzma_filter_decoder_is_supported(lzma_vli id);
/**
* \brief Copy the filters array
*
* Copy the Filter IDs and filter-specific options from src to dest.
* Up to LZMA_FILTERS_MAX filters are copied, plus the terminating
* .id == LZMA_VLI_UNKNOWN. Thus, dest should have at least
* LZMA_FILTERS_MAX + 1 elements space unless the caller knows that
* src is smaller than that.
*
* Unless the filter-specific options is NULL, the Filter ID has to be
* supported by liblzma, because liblzma needs to know the size of every
* filter-specific options structure. The filter-specific options are not
* validated. If options is NULL, any unsupported Filter IDs are copied
* without returning an error.
*
* Old filter-specific options in dest are not freed, so dest doesn't
* need to be initialized by the caller in any way.
*
* If an error occurs, memory possibly already allocated by this function
* is always freed.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_OPTIONS_ERROR: Unsupported Filter ID and its options
* is not NULL.
* - LZMA_PROG_ERROR: src or dest is NULL.
*/
nothrow lzma_ret lzma_filters_copy(const lzma_filter*src,
lzma_filter *dest, lzma_allocator *allocator);
/**
* \brief Calculate approximate memory requirements for raw encoder
*
* This function can be used to calculate the memory requirements for
* Block and Stream encoders too because Block and Stream encoders don't
* need significantly more memory than raw encoder.
*
* \param filters Array of filters terminated with
* .id == LZMA_VLI_UNKNOWN.
*
* \return Number of bytes of memory required for the given
* filter chain when encoding. If an error occurs,
* for example due to unsupported filter chain,
* UINT64_MAX is returned.
*/
pure nothrow ulong lzma_raw_encoder_memusage(const lzma_filter *filters);
/**
* \brief Calculate approximate memory requirements for raw decoder
*
* This function can be used to calculate the memory requirements for
* Block and Stream decoders too because Block and Stream decoders don't
* need significantly more memory than raw decoder.
*
* \param filters Array of filters terminated with
* .id == LZMA_VLI_UNKNOWN.
*
* \return Number of bytes of memory required for the given
* filter chain when decoding. If an error occurs,
* for example due to unsupported filter chain,
* UINT64_MAX is returned.
*/
nothrow pure ulong lzma_raw_decoder_memusage(const lzma_filter *filters);
/**
* \brief Initialize raw encoder
*
* This function may be useful when implementing custom file formats.
*
* \param strm Pointer to properly prepared lzma_stream
* \param filters Array of lzma_filter structures. The end of the
* array must be marked with .id = LZMA_VLI_UNKNOWN.
*
* The `action' with lzma_code() can be LZMA_RUN, LZMA_SYNC_FLUSH (if the
* filter chain supports it), or LZMA_FINISH.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_raw_encoder(
lzma_stream *strm, const lzma_filter *filters);
/**
* \brief Initialize raw decoder
*
* The initialization of raw decoder goes similarly to raw encoder.
*
* The `action' with lzma_code() can be LZMA_RUN or LZMA_FINISH. Using
* LZMA_FINISH is not required, it is supported just for convenience.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_raw_decoder(
lzma_stream *strm, const lzma_filter *filters);
/**
* \brief Update the filter chain in the encoder
*
* This function is for advanced users only. This function has two slightly
* different purposes:
*
* - After LZMA_FULL_FLUSH when using Stream encoder: Set a new filter
* chain, which will be used starting from the next Block.
*
* - After LZMA_SYNC_FLUSH using Raw, Block, or Stream encoder: Change
* the filter-specific options in the middle of encoding. The actual
* filters in the chain (Filter IDs) cannot be changed. In the future,
* it might become possible to change the filter options without
* using LZMA_SYNC_FLUSH.
*
* While rarely useful, this function may be called also when no data has
* been compressed yet. In that case, this function will behave as if
* LZMA_FULL_FLUSH (Stream encoder) or LZMA_SYNC_FLUSH (Raw or Block
* encoder) had been used right before calling this function.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_MEMLIMIT_ERROR
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_filters_update(
lzma_stream *strm, const lzma_filter *filters);
/**
* \brief Single-call raw encoder
*
* \param filters Array of lzma_filter structures. The end of the
* array must be marked with .id = LZMA_VLI_UNKNOWN.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_size Size of the input buffer
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_BUF_ERROR: Not enough output buffer space.
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
* - LZMA_DATA_ERROR
* - LZMA_PROG_ERROR
*
* \note There is no function to calculate how big output buffer
* would surely be big enough. (lzma_stream_buffer_bound()
* works only for lzma_stream_buffer_encode(); raw encoder
* won't necessarily meet that bound.)
*/
nothrow lzma_ret lzma_raw_buffer_encode(
const lzma_filter *filters, lzma_allocator *allocator,
const(ubyte) *in_, size_t in_size, ubyte *out_,
size_t *out_pos, size_t out_size);
/**
* \brief Single-call raw decoder
*
* \param filters Array of lzma_filter structures. The end of the
* array must be marked with .id = LZMA_VLI_UNKNOWN.
* \param allocator lzma_allocator for custom allocator functions.
* Set to NULL to use malloc() and free().
* \param in Beginning of the input buffer
* \param in_pos The next byte will be read from in[*in_pos].
* *in_pos is updated only if decoding succeeds.
* \param in_size Size of the input buffer; the first byte that
* won't be read is in[in_size].
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*/
nothrow lzma_ret lzma_raw_buffer_decode(const lzma_filter *filters,
lzma_allocator *allocator,
const(ubyte) *in_, size_t *in_pos, size_t in_size,
ubyte *out_, size_t *out_pos, size_t out_size);
/**
* \brief Get the size of the Filter Properties field
*
* This function may be useful when implementing custom file formats
* using the raw encoder and decoder.
*
* \param size Pointer to uint32_t to hold the size of the properties
* \param filter Filter ID and options (the size of the properties may
* vary depending on the options)
*
* \return - LZMA_OK
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*
* \note This function validates the Filter ID, but does not
* necessarily validate the options. Thus, it is possible
* that this returns LZMA_OK while the following call to
* lzma_properties_encode() returns LZMA_OPTIONS_ERROR.
*/
nothrow lzma_ret lzma_properties_size(
uint *size, const lzma_filter *filter);
/**
* \brief Encode the Filter Properties field
*
* \param filter Filter ID and options
* \param props Buffer to hold the encoded options. The size of
* buffer must have been already determined with
* lzma_properties_size().
*
* \return - LZMA_OK
* - LZMA_OPTIONS_ERROR
* - LZMA_PROG_ERROR
*
* \note Even this function won't validate more options than actually
* necessary. Thus, it is possible that encoding the properties
* succeeds but using the same options to initialize the encoder
* will fail.
*
* \note If lzma_properties_size() indicated that the size
* of the Filter Properties field is zero, calling
* lzma_properties_encode() is not required, but it
* won't do any harm either.
*/
nothrow lzma_ret lzma_properties_encode(
const lzma_filter *filter, ubyte *props);
/**
* \brief Decode the Filter Properties field
*
* \param filter filter->id must have been set to the correct
* Filter ID. filter->options doesn't need to be
* initialized (it's not freed by this function). The
* decoded options will be stored to filter->options.
* filter->options is set to NULL if there are no
* properties or if an error occurs.
* \param allocator Custom memory allocator used to allocate the
* options. Set to NULL to use the default malloc(),
* and in case of an error, also free().
* \param props Input buffer containing the properties.
* \param props_size Size of the properties. This must be the exact
* size; giving too much or too little input will
* return LZMA_OPTIONS_ERROR.
*
* \return - LZMA_OK
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
*/
nothrow lzma_ret lzma_properties_decode(
lzma_filter *filter, lzma_allocator *allocator,
const ubyte *props, size_t props_size);
/**
* \brief Calculate encoded size of a Filter Flags field
*
* Knowing the size of Filter Flags is useful to know when allocating
* memory to hold the encoded Filter Flags.
*
* \param size Pointer to integer to hold the calculated size
* \param filter Filter ID and associated options whose encoded
* size is to be calculated
*
* \return - LZMA_OK: *size set successfully. Note that this doesn't
* guarantee that filter->options is valid, thus
* lzma_filter_flags_encode() may still fail.
* - LZMA_OPTIONS_ERROR: Unknown Filter ID or unsupported options.
* - LZMA_PROG_ERROR: Invalid options
*
* \note If you need to calculate size of List of Filter Flags,
* you need to loop over every lzma_filter entry.
*/
nothrow lzma_ret lzma_filter_flags_size(
uint *size, const lzma_filter *filter);
/**
* \brief Encode Filter Flags into given buffer
*
* In contrast to some functions, this doesn't allocate the needed buffer.
* This is due to how this function is used internally by liblzma.
*
* \param filter Filter ID and options to be encoded
* \param out Beginning of the output buffer
* \param out_pos out[*out_pos] is the next write position. This
* is updated by the encoder.
* \param out_size out[out_size] is the first byte to not write.
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_OPTIONS_ERROR: Invalid or unsupported options.
* - LZMA_PROG_ERROR: Invalid options or not enough output
* buffer space (you should have checked it with
* lzma_filter_flags_size()).
*/
nothrow lzma_ret lzma_filter_flags_encode(const lzma_filter *filter,
ubyte *out_, size_t *out_pos, size_t out_size);
/**
* \brief Decode Filter Flags from given buffer
*
* The decoded result is stored into *filter. The old value of
* filter->options is not free()d.
*
* \return - LZMA_OK
* - LZMA_OPTIONS_ERROR
* - LZMA_MEM_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_filter_flags_decode(
lzma_filter *filter, lzma_allocator *allocator,
const ubyte *in_, size_t *in_pos, size_t in_size);
+50
View File
@@ -0,0 +1,50 @@
/**
* \file lzma/hardware.h
* \brief Hardware information
*
* Since liblzma can consume a lot of system resources, it also provides
* ways to limit the resource usage. Applications linking against liblzma
* need to do the actual decisions how much resources to let liblzma to use.
* To ease making these decisions, liblzma provides functions to find out
* the relevant capabilities of the underlaying hardware. Currently there
* is only a function to find out the amount of RAM, but in the future there
* will be also a function to detect how many concurrent threads the system
* can run.
*
* \note On some operating systems, these function may temporarily
* load a shared library or open file descriptor(s) to find out
* the requested hardware information. Unless the application
* assumes that specific file descriptors are not touched by
* other threads, this should have no effect on thread safety.
* Possible operations involving file descriptors will restart
* the syscalls if they return EINTR.
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.hardware;
import deimos.lzma;
extern(C):
/**
* \brief Get the total amount of physical memory (RAM) in bytes
*
* This function may be useful when determining a reasonable memory
* usage limit for decompressing or how much memory it is OK to use
* for compressing.
*
* \return On success, the total amount of physical memory in bytes
* is returned. If the amount of RAM cannot be determined,
* zero is returned. This can happen if an error occurs
* or if there is no code in liblzma to detect the amount
* of RAM on the specific operating system.
*/
nothrow ulong lzma_physmem();
+663
View File
@@ -0,0 +1,663 @@
/**
* \file lzma/index.h
* \brief Handling of .xz Index and related information
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.index;
import deimos.lzma;
extern(C):
/**
* \brief Opaque data type to hold the Index(es) and other information
*
* lzma_index often holds just one .xz Index and possibly the Stream Flags
* of the same Stream and size of the Stream Padding field. However,
* multiple lzma_indexes can be concatenated with lzma_index_cat() and then
* there may be information about multiple Streams in the same lzma_index.
*
* Notes about thread safety: Only one thread may modify lzma_index at
* a time. All functions that take non-const pointer to lzma_index
* modify it. As long as no thread is modifying the lzma_index, getting
* information from the same lzma_index can be done from multiple threads
* at the same time with functions that take a const pointer to
* lzma_index or use lzma_index_iter. The same iterator must be used
* only by one thread at a time, of course, but there can be as many
* iterators for the same lzma_index as needed.
*/
struct lzma_index {};
/**
* \brief Iterator to get information about Blocks and Streams
*/
struct lzma_index_iter
{
struct StreamStruct
{
/**
* \brief Pointer to Stream Flags
*
* This is NULL if Stream Flags have not been set for
* this Stream with lzma_index_stream_flags().
*/
const lzma_stream_flags *flags;
const void *reserved_ptr1;
const void *reserved_ptr2;
const void *reserved_ptr3;
/**
* \brief Stream number in the lzma_index
*
* The first Stream is 1.
*/
lzma_vli number;
/**
* \brief Number of Blocks in the Stream
*
* If this is zero, the block structure below has
* undefined values.
*/
lzma_vli block_count;
/**
* \brief Compressed start offset of this Stream
*
* The offset is relative to the beginning of the lzma_index
* (i.e. usually the beginning of the .xz file).
*/
lzma_vli compressed_offset;
/**
* \brief Uncompressed start offset of this Stream
*
* The offset is relative to the beginning of the lzma_index
* (i.e. usually the beginning of the .xz file).
*/
lzma_vli uncompressed_offset;
/**
* \brief Compressed size of this Stream
*
* This includes all headers except the possible
* Stream Padding after this Stream.
*/
lzma_vli compressed_size;
/**
* \brief Uncompressed size of this Stream
*/
lzma_vli uncompressed_size;
/**
* \brief Size of Stream Padding after this Stream
*
* If it hasn't been set with lzma_index_stream_padding(),
* this defaults to zero. Stream Padding is always
* a multiple of four bytes.
*/
lzma_vli padding;
lzma_vli reserved_vli1;
lzma_vli reserved_vli2;
lzma_vli reserved_vli3;
lzma_vli reserved_vli4;
}
StreamStruct stream;
struct BlockStruct
{
/**
* \brief Block number in the file
*
* The first Block is 1.
*/
lzma_vli number_in_file;
/**
* \brief Compressed start offset of this Block
*
* This offset is relative to the beginning of the
* lzma_index (i.e. usually the beginning of the .xz file).
* Normally this is where you should seek in the .xz file
* to start decompressing this Block.
*/
lzma_vli compressed_file_offset;
/**
* \brief Uncompressed start offset of this Block
*
* This offset is relative to the beginning of the lzma_index
* (i.e. usually the beginning of the .xz file).
*
* When doing random-access reading, it is possible that
* the target offset is not exactly at Block boundary. One
* will need to compare the target offset against
* uncompressed_file_offset or uncompressed_stream_offset,
* and possibly decode and throw away some amount of data
* before reaching the target offset.
*/
lzma_vli uncompressed_file_offset;
/**
* \brief Block number in this Stream
*
* The first Block is 1.
*/
lzma_vli number_in_stream;
/**
* \brief Compressed start offset of this Block
*
* This offset is relative to the beginning of the Stream
* containing this Block.
*/
lzma_vli compressed_stream_offset;
/**
* \brief Uncompressed start offset of this Block
*
* This offset is relative to the beginning of the Stream
* containing this Block.
*/
lzma_vli uncompressed_stream_offset;
/**
* \brief Uncompressed size of this Block
*
* You should pass this to the Block decoder if you will
* decode this Block. It will allow the Block decoder to
* validate the uncompressed size.
*/
lzma_vli uncompressed_size;
/**
* \brief Unpadded size of this Block
*
* You should pass this to the Block decoder if you will
* decode this Block. It will allow the Block decoder to
* validate the unpadded size.
*/
lzma_vli unpadded_size;
/**
* \brief Total compressed size
*
* This includes all headers and padding in this Block.
* This is useful if you need to know how many bytes
* the Block decoder will actually read.
*/
lzma_vli total_size;
lzma_vli reserved_vli1;
lzma_vli reserved_vli2;
lzma_vli reserved_vli3;
lzma_vli reserved_vli4;
const void *reserved_ptr1;
const void *reserved_ptr2;
const void *reserved_ptr3;
const void *reserved_ptr4;
}
BlockStruct block;
/*
* Internal data which is used to store the state of the iterator.
* The exact format may vary between liblzma versions, so don't
* touch these in any way.
*/
union InternalData
{
const void *p;
size_t s;
lzma_vli v;
}
InternalData internal[6];
}
/**
* Operation mode for lzma_index_iter_next()
*/
enum lzma_index_iter_mode
{
LZMA_INDEX_ITER_ANY = 0,
/**<
* \brief Get the next Block or Stream
*
* Go to the next Block if the current Stream has at least
* one Block left. Otherwise go to the next Stream even if
* it has no Blocks. If the Stream has no Blocks
* (lzma_index_iter.stream.block_count == 0),
* lzma_index_iter.block will have undefined values.
*/
LZMA_INDEX_ITER_STREAM = 1,
/**<
* \brief Get the next Stream
*
* Go to the next Stream even if the current Stream has
* unread Blocks left. If the next Stream has at least one
* Block, the iterator will point to the first Block.
* If there are no Blocks, lzma_index_iter.block will have
* undefined values.
*/
LZMA_INDEX_ITER_BLOCK = 2,
/**<
* \brief Get the next Block
*
* Go to the next Block if the current Stream has at least
* one Block left. If the current Stream has no Blocks left,
* the next Stream with at least one Block is located and
* the iterator will be made to point to the first Block of
* that Stream.
*/
LZMA_INDEX_ITER_NONEMPTY_BLOCK = 3
}
/**
* \brief Calculate memory usage of lzma_index
*
* On disk, the size of the Index field depends on both the number of Records
* stored and how big values the Records store (due to variable-length integer
* encoding). When the Index is kept in lzma_index structure, the memory usage
* depends only on the number of Records/Blocks stored in the Index(es), and
* in case of concatenated lzma_indexes, the number of Streams. The size in
* RAM is almost always significantly bigger than in the encoded form on disk.
*
* This function calculates an approximate amount of memory needed hold
* the given number of Streams and Blocks in lzma_index structure. This
* value may vary between CPU architectures and also between liblzma versions
* if the internal implementation is modified.
*/
nothrow ulong lzma_index_memusage(
lzma_vli streams, lzma_vli blocks);
/**
* \brief Calculate the memory usage of an existing lzma_index
*
* This is a shorthand for lzma_index_memusage(lzma_index_stream_count(i),
* lzma_index_block_count(i)).
*/
nothrow ulong lzma_index_memused(const lzma_index *i);
/**
* \brief Allocate and initialize a new lzma_index structure
*
* \return On success, a pointer to an empty initialized lzma_index is
* returned. If allocation fails, NULL is returned.
*/
nothrow lzma_index* lzma_index_init(lzma_allocator *allocator);
/**
* \brief Deallocate lzma_index
*
* If i is NULL, this does nothing.
*/
nothrow void lzma_index_end(lzma_index *i, lzma_allocator *allocator);
/**
* \brief Add a new Block to lzma_index
*
* \param i Pointer to a lzma_index structure
* \param allocator Pointer to lzma_allocator, or NULL to
* use malloc()
* \param unpadded_size Unpadded Size of a Block. This can be
* calculated with lzma_block_unpadded_size()
* after encoding or decoding the Block.
* \param uncompressed_size Uncompressed Size of a Block. This can be
* taken directly from lzma_block structure
* after encoding or decoding the Block.
*
* Appending a new Block does not invalidate iterators. For example,
* if an iterator was pointing to the end of the lzma_index, after
* lzma_index_append() it is possible to read the next Block with
* an existing iterator.
*
* \return - LZMA_OK
* - LZMA_MEM_ERROR
* - LZMA_DATA_ERROR: Compressed or uncompressed size of the
* Stream or size of the Index field would grow too big.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_append(
lzma_index *i, lzma_allocator *allocator,
lzma_vli unpadded_size, lzma_vli uncompressed_size);
/**
* \brief Set the Stream Flags
*
* Set the Stream Flags of the last (and typically the only) Stream
* in lzma_index. This can be useful when reading information from the
* lzma_index, because to decode Blocks, knowing the integrity check type
* is needed.
*
* The given Stream Flags are copied into internal preallocated structure
* in the lzma_index, thus the caller doesn't need to keep the *stream_flags
* available after calling this function.
*
* \return - LZMA_OK
* - LZMA_OPTIONS_ERROR: Unsupported stream_flags->version.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_stream_flags(
lzma_index *i, const lzma_stream_flags *stream_flags);
/**
* \brief Get the types of integrity Checks
*
* If lzma_index_stream_flags() is used to set the Stream Flags for
* every Stream, lzma_index_checks() can be used to get a bitmask to
* indicate which Check types have been used. It can be useful e.g. if
* showing the Check types to the user.
*
* The bitmask is 1 << check_id, e.g. CRC32 is 1 << 1 and SHA-256 is 1 << 10.
*/
nothrow pure uint lzma_index_checks(const lzma_index *i);
/**
* \brief Set the amount of Stream Padding
*
* Set the amount of Stream Padding of the last (and typically the only)
* Stream in the lzma_index. This is needed when planning to do random-access
* reading within multiple concatenated Streams.
*
* By default, the amount of Stream Padding is assumed to be zero bytes.
*
* \return - LZMA_OK
* - LZMA_DATA_ERROR: The file size would grow too big.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_stream_padding(
lzma_index *i, lzma_vli stream_padding);
/**
* \brief Get the number of Streams
*/
nothrow pure lzma_vli lzma_index_stream_count(const lzma_index *i);
/**
* \brief Get the number of Blocks
*
* This returns the total number of Blocks in lzma_index. To get number
* of Blocks in individual Streams, use lzma_index_iter.
*/
nothrow pure lzma_vli lzma_index_block_count(const lzma_index *i);
/**
* \brief Get the size of the Index field as bytes
*
* This is needed to verify the Backward Size field in the Stream Footer.
*/
nothrow pure lzma_vli lzma_index_size(const lzma_index *i);
/**
* \brief Get the total size of the Stream
*
* If multiple lzma_indexes have been combined, this works as if the Blocks
* were in a single Stream. This is useful if you are going to combine
* Blocks from multiple Streams into a single new Stream.
*/
nothrow pure lzma_vli lzma_index_stream_size(const lzma_index *i);
/**
* \brief Get the total size of the Blocks
*
* This doesn't include the Stream Header, Stream Footer, Stream Padding,
* or Index fields.
*/
nothrow pure lzma_vli lzma_index_total_size(const lzma_index *i);
/**
* \brief Get the total size of the file
*
* When no lzma_indexes have been combined with lzma_index_cat() and there is
* no Stream Padding, this function is identical to lzma_index_stream_size().
* If multiple lzma_indexes have been combined, this includes also the headers
* of each separate Stream and the possible Stream Padding fields.
*/
nothrow pure lzma_vli lzma_index_file_size(const lzma_index *i);
/**
* \brief Get the uncompressed size of the file
*/
nothrow pure lzma_vli lzma_index_uncompressed_size(const lzma_index *i);
/**
* \brief Initialize an iterator
*
* \param iter Pointer to a lzma_index_iter structure
* \param i lzma_index to which the iterator will be associated
*
* This function associates the iterator with the given lzma_index, and calls
* lzma_index_iter_rewind() on the iterator.
*
* This function doesn't allocate any memory, thus there is no
* lzma_index_iter_end(). The iterator is valid as long as the
* associated lzma_index is valid, that is, until lzma_index_end() or
* using it as source in lzma_index_cat(). Specifically, lzma_index doesn't
* become invalid if new Blocks are added to it with lzma_index_append() or
* if it is used as the destination in lzma_index_cat().
*
* It is safe to make copies of an initialized lzma_index_iter, for example,
* to easily restart reading at some particular position.
*/
nothrow void lzma_index_iter_init(
lzma_index_iter *iter, const lzma_index *i);
/**
* \brief Rewind the iterator
*
* Rewind the iterator so that next call to lzma_index_iter_next() will
* return the first Block or Stream.
*/
nothrow void lzma_index_iter_rewind(lzma_index_iter *iter);
/**
* \brief Get the next Block or Stream
*
* \param iter Iterator initialized with lzma_index_iter_init()
* \param mode Specify what kind of information the caller wants
* to get. See lzma_index_iter_mode for details.
*
* \return If next Block or Stream matching the mode was found, *iter
* is updated and this function returns false. If no Block or
* Stream matching the mode is found, *iter is not modified
* and this function returns true. If mode is set to an unknown
* value, *iter is not modified and this function returns true.
*/
nothrow lzma_bool lzma_index_iter_next(
lzma_index_iter *iter, lzma_index_iter_mode mode);
/**
* \brief Locate a Block
*
* If it is possible to seek in the .xz file, it is possible to parse
* the Index field(s) and use lzma_index_iter_locate() to do random-access
* reading with granularity of Block size.
*
* \param iter Iterator that was earlier initialized with
* lzma_index_iter_init().
* \param target Uncompressed target offset which the caller would
* like to locate from the Stream
*
* If the target is smaller than the uncompressed size of the Stream (can be
* checked with lzma_index_uncompressed_size()):
* - Information about the Stream and Block containing the requested
* uncompressed offset is stored into *iter.
* - Internal state of the iterator is adjusted so that
* lzma_index_iter_next() can be used to read subsequent Blocks or Streams.
* - This function returns false.
*
* If target is greater than the uncompressed size of the Stream, *iter
* is not modified, and this function returns true.
*/
nothrow lzma_bool lzma_index_iter_locate(
lzma_index_iter *iter, lzma_vli target);
/**
* \brief Concatenate lzma_indexes
*
* Concatenating lzma_indexes is useful when doing random-access reading in
* multi-Stream .xz file, or when combining multiple Streams into single
* Stream.
*
* \param dest lzma_index after which src is appended
* \param src lzma_index to be appended after dest. If this
* function succeeds, the memory allocated for src
* is freed or moved to be part of dest, and all
* iterators pointing to src will become invalid.
* \param allocator Custom memory allocator; can be NULL to use
* malloc() and free().
*
* \return - LZMA_OK: lzma_indexes were concatenated successfully.
* src is now a dangling pointer.
* - LZMA_DATA_ERROR: *dest would grow too big.
* - LZMA_MEM_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_cat(
lzma_index *dest, lzma_index *src, lzma_allocator *allocator);
/**
* \brief Duplicate lzma_index
*
* \return A copy of the lzma_index, or NULL if memory allocation failed.
*/
nothrow lzma_index * lzma_index_dup(
const lzma_index *i, lzma_allocator *allocator);
/**
* \brief Initialize .xz Index encoder
*
* \param strm Pointer to properly prepared lzma_stream
* \param i Pointer to lzma_index which should be encoded.
*
* The valid `action' values for lzma_code() are LZMA_RUN and LZMA_FINISH.
* It is enough to use only one of them (you can choose freely; use LZMA_RUN
* to support liblzma versions older than 5.0.0).
*
* \return - LZMA_OK: Initialization succeeded, continue with lzma_code().
* - LZMA_MEM_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_encoder(
lzma_stream *strm, const lzma_index *i);
/**
* \brief Initialize .xz Index decoder
*
* \param strm Pointer to properly prepared lzma_stream
* \param i The decoded Index will be made available via
* this pointer. Initially this function will
* set *i to NULL (the old value is ignored). If
* decoding succeeds (lzma_code() returns
* LZMA_STREAM_END), *i will be set to point
* to a new lzma_index, which the application
* has to later free with lzma_index_end().
* \param memlimit How much memory the resulting lzma_index is
* allowed to require.
*
* The valid `action' values for lzma_code() are LZMA_RUN and LZMA_FINISH.
* It is enough to use only one of them (you can choose freely; use LZMA_RUN
* to support liblzma versions older than 5.0.0).
*
* \return - LZMA_OK: Initialization succeeded, continue with lzma_code().
* - LZMA_MEM_ERROR
* - LZMA_MEMLIMIT_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_decoder(
lzma_stream *strm, lzma_index **i, ulong memlimit);
/**
* \brief Single-call .xz Index encoder
*
* \param i lzma_index to be encoded
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* *out_pos is updated only if encoding succeeds.
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_BUF_ERROR: Output buffer is too small. Use
* lzma_index_size() to find out how much output
* space is needed.
* - LZMA_PROG_ERROR
*
* \note This function doesn't take allocator argument since all
* the internal data is allocated on stack.
*/
nothrow lzma_ret lzma_index_buffer_encode(const lzma_index *i,
ubyte *out_, size_t *out_pos, size_t out_size);
/**
* \brief Single-call .xz Index decoder
*
* \param i If decoding succeeds, *i will point to a new
* lzma_index, which the application has to
* later free with lzma_index_end(). If an error
* occurs, *i will be NULL. The old value of *i
* is always ignored and thus doesn't need to be
* initialized by the caller.
* \param memlimit Pointer to how much memory the resulting
* lzma_index is allowed to require. The value
* pointed by this pointer is modified if and only
* if LZMA_MEMLIMIT_ERROR is returned.
* \param allocator Pointer to lzma_allocator, or NULL to use malloc()
* \param in Beginning of the input buffer
* \param in_pos The next byte will be read from in[*in_pos].
* *in_pos is updated only if decoding succeeds.
* \param in_size Size of the input buffer; the first byte that
* won't be read is in[in_size].
*
* \return - LZMA_OK: Decoding was successful.
* - LZMA_MEM_ERROR
* - LZMA_MEMLIMIT_ERROR: Memory usage limit was reached.
* The minimum required memlimit value was stored to *memlimit.
* - LZMA_DATA_ERROR
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_buffer_decode(lzma_index **i,
ulong *memlimit, lzma_allocator *allocator,
const ubyte *in_, size_t *in_pos, size_t in_size);
+103
View File
@@ -0,0 +1,103 @@
/**
* \file lzma/index_hash.h
* \brief Validate Index by using a hash function
*
* Hashing makes it possible to use constant amount of memory to validate
* Index of arbitrary size.
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.index_hash;
import deimos.lzma;
extern(C):
/**
* \brief Opaque data type to hold the Index hash
*/
struct lzma_index_hash {};
/**
* \brief Allocate and initialize a new lzma_index_hash structure
*
* If index_hash is NULL, a new lzma_index_hash structure is allocated,
* initialized, and a pointer to it returned. If allocation fails, NULL
* is returned.
*
* If index_hash is non-NULL, it is reinitialized and the same pointer
* returned. In this case, return value cannot be NULL or a different
* pointer than the index_hash that was given as an argument.
*/
nothrow lzma_index_hash * lzma_index_hash_init(
lzma_index_hash *index_hash, lzma_allocator *allocator);
/**
* \brief Deallocate lzma_index_hash structure
*/
nothrow void lzma_index_hash_end(
lzma_index_hash *index_hash, lzma_allocator *allocator);
/**
* \brief Add a new Record to an Index hash
*
* \param index Pointer to a lzma_index_hash structure
* \param unpadded_size Unpadded Size of a Block
* \param uncompressed_size Uncompressed Size of a Block
*
* \return - LZMA_OK
* - LZMA_DATA_ERROR: Compressed or uncompressed size of the
* Stream or size of the Index field would grow too big.
* - LZMA_PROG_ERROR: Invalid arguments or this function is being
* used when lzma_index_hash_decode() has already been used.
*/
nothrow lzma_ret lzma_index_hash_append(lzma_index_hash *index_hash,
lzma_vli unpadded_size, lzma_vli uncompressed_size);
/**
* \brief Decode and validate the Index field
*
* After telling the sizes of all Blocks with lzma_index_hash_append(),
* the actual Index field is decoded with this function. Specifically,
* once decoding of the Index field has been started, no more Records
* can be added using lzma_index_hash_append().
*
* This function doesn't use lzma_stream structure to pass the input data.
* Instead, the input buffer is specified using three arguments. This is
* because it matches better the internal APIs of liblzma.
*
* \param index_hash Pointer to a lzma_index_hash structure
* \param in Pointer to the beginning of the input buffer
* \param in_pos in[*in_pos] is the next byte to process
* \param in_size in[in_size] is the first byte not to process
*
* \return - LZMA_OK: So far good, but more input is needed.
* - LZMA_STREAM_END: Index decoded successfully and it matches
* the Records given with lzma_index_hash_append().
* - LZMA_DATA_ERROR: Index is corrupt or doesn't match the
* information given with lzma_index_hash_append().
* - LZMA_BUF_ERROR: Cannot progress because *in_pos >= in_size.
* - LZMA_PROG_ERROR
*/
nothrow lzma_ret lzma_index_hash_decode(lzma_index_hash *index_hash,
const ubyte *in_, size_t *in_pos, size_t in_size);
/**
* \brief Get the size of the Index field as bytes
*
* This is needed to verify the Backward Size field in the Stream Footer.
*/
nothrow pure lzma_vli lzma_index_hash_size(
const lzma_index_hash *index_hash);
+422
View File
@@ -0,0 +1,422 @@
/**
* \file lzma/lzma.h
* \brief LZMA1 and LZMA2 filters
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.lzma;
import deimos.lzma;
extern(C):
/**
* \brief LZMA1 Filter ID
*
* LZMA1 is the very same thing as what was called just LZMA in LZMA Utils,
* 7-Zip, and LZMA SDK. It's called LZMA1 here to prevent developers from
* accidentally using LZMA when they actually want LZMA2.
*
* LZMA1 shouldn't be used for new applications unless you _really_ know
* what you are doing. LZMA2 is almost always a better choice.
*/
enum LZMA_FILTER_LZMA1 = 0x4000000000000001UL;
/**
* \brief LZMA2 Filter ID
*
* Usually you want this instead of LZMA1. Compared to LZMA1, LZMA2 adds
* support for LZMA_SYNC_FLUSH, uncompressed chunks (smaller expansion
* when trying to compress uncompressible data), possibility to change
* lc/lp/pb in the middle of encoding, and some other internal improvements.
*/
enum LZMA_FILTER_LZMA2 = 0x21UL;
/**
* \brief Match finders
*
* Match finder has major effect on both speed and compression ratio.
* Usually hash chains are faster than binary trees.
*
* If you will use LZMA_SYNC_FLUSH often, the hash chains may be a better
* choice, because binary trees get much higher compression ratio penalty
* with LZMA_SYNC_FLUSH.
*
* The memory usage formulas are only rough estimates, which are closest to
* reality when dict_size is a power of two. The formulas are more complex
* in reality, and can also change a little between liblzma versions. Use
* lzma_raw_encoder_memusage() to get more accurate estimate of memory usage.
*/
enum lzma_match_finder
{
LZMA_MF_HC3 = 0x03,
/**<
* \brief Hash Chain with 2- and 3-byte hashing
*
* Minimum nice_len: 3
*
* Memory usage:
* - dict_size <= 16 MiB: dict_size * 7.5
* - dict_size > 16 MiB: dict_size * 5.5 + 64 MiB
*/
LZMA_MF_HC4 = 0x04,
/**<
* \brief Hash Chain with 2-, 3-, and 4-byte hashing
*
* Minimum nice_len: 4
*
* Memory usage:
* - dict_size <= 32 MiB: dict_size * 7.5
* - dict_size > 32 MiB: dict_size * 6.5
*/
LZMA_MF_BT2 = 0x12,
/**<
* \brief Binary Tree with 2-byte hashing
*
* Minimum nice_len: 2
*
* Memory usage: dict_size * 9.5
*/
LZMA_MF_BT3 = 0x13,
/**<
* \brief Binary Tree with 2- and 3-byte hashing
*
* Minimum nice_len: 3
*
* Memory usage:
* - dict_size <= 16 MiB: dict_size * 11.5
* - dict_size > 16 MiB: dict_size * 9.5 + 64 MiB
*/
LZMA_MF_BT4 = 0x14
/**<
* \brief Binary Tree with 2-, 3-, and 4-byte hashing
*
* Minimum nice_len: 4
*
* Memory usage:
* - dict_size <= 32 MiB: dict_size * 11.5
* - dict_size > 32 MiB: dict_size * 10.5
*/
}
/**
* \brief Test if given match finder is supported
*
* Return true if the given match finder is supported by this liblzma build.
* Otherwise false is returned. It is safe to call this with a value that
* isn't listed in lzma_match_finder enumeration; the return value will be
* false.
*
* There is no way to list which match finders are available in this
* particular liblzma version and build. It would be useless, because
* a new match finder, which the application developer wasn't aware,
* could require giving additional options to the encoder that the older
* match finders don't need.
*/
nothrow lzma_bool lzma_mf_is_supported(lzma_match_finder match_finder);
/**
* \brief Compression modes
*
* This selects the function used to analyze the data produced by the match
* finder.
*/
enum lzma_mode
{
LZMA_MODE_FAST = 1,
/**<
* \brief Fast compression
*
* Fast mode is usually at its best when combined with
* a hash chain match finder.
*/
LZMA_MODE_NORMAL = 2
/**<
* \brief Normal compression
*
* This is usually notably slower than fast mode. Use this
* together with binary tree match finders to expose the
* full potential of the LZMA1 or LZMA2 encoder.
*/
}
/**
* \brief Test if given compression mode is supported
*
* Return true if the given compression mode is supported by this liblzma
* build. Otherwise false is returned. It is safe to call this with a value
* that isn't listed in lzma_mode enumeration; the return value will be false.
*
* There is no way to list which modes are available in this particular
* liblzma version and build. It would be useless, because a new compression
* mode, which the application developer wasn't aware, could require giving
* additional options to the encoder that the older modes don't need.
*/
nothrow lzma_bool lzma_mode_is_supported(lzma_mode mode);
/**
* \brief Options specific to the LZMA1 and LZMA2 filters
*
* Since LZMA1 and LZMA2 share most of the code, it's simplest to share
* the options structure too. For encoding, all but the reserved variables
* need to be initialized unless specifically mentioned otherwise.
* lzma_lzma_preset() can be used to get a good starting point.
*
* For raw decoding, both LZMA1 and LZMA2 need dict_size, preset_dict, and
* preset_dict_size (if preset_dict != NULL). LZMA1 needs also lc, lp, and pb.
*/
struct lzma_options_lzma
{
/**
* \brief Dictionary size in bytes
*
* Dictionary size indicates how many bytes of the recently processed
* uncompressed data is kept in memory. One method to reduce size of
* the uncompressed data is to store distance-length pairs, which
* indicate what data to repeat from the dictionary buffer. Thus,
* the bigger the dictionary, the better the compression ratio
* usually is.
*
* Maximum size of the dictionary depends on multiple things:
* - Memory usage limit
* - Available address space (not a problem on 64-bit systems)
* - Selected match finder (encoder only)
*
* Currently the maximum dictionary size for encoding is 1.5 GiB
* (i.e. (UINT32_C(1) << 30) + (UINT32_C(1) << 29)) even on 64-bit
* systems for certain match finder implementation reasons. In the
* future, there may be match finders that support bigger
* dictionaries.
*
* Decoder already supports dictionaries up to 4 GiB - 1 B (i.e.
* UINT32_MAX), so increasing the maximum dictionary size of the
* encoder won't cause problems for old decoders.
*
* Because extremely small dictionaries sizes would have unneeded
* overhead in the decoder, the minimum dictionary size is 4096 bytes.
*
* \note When decoding, too big dictionary does no other harm
* than wasting memory.
*/
uint dict_size;
enum LZMA_DICT_SIZE_MIN = 4096U;
enum LZMA_DICT_SIZE_DEFAULT = (1U << 23);
/**
* \brief Pointer to an initial dictionary
*
* It is possible to initialize the LZ77 history window using
* a preset dictionary. It is useful when compressing many
* similar, relatively small chunks of data independently from
* each other. The preset dictionary should contain typical
* strings that occur in the files being compressed. The most
* probable strings should be near the end of the preset dictionary.
*
* This feature should be used only in special situations. For
* now, it works correctly only with raw encoding and decoding.
* Currently none of the container formats supported by
* liblzma allow preset dictionary when decoding, thus if
* you create a .xz or .lzma file with preset dictionary, it
* cannot be decoded with the regular decoder functions. In the
* future, the .xz format will likely get support for preset
* dictionary though.
*/
const ubyte *preset_dict;
/**
* \brief Size of the preset dictionary
*
* Specifies the size of the preset dictionary. If the size is
* bigger than dict_size, only the last dict_size bytes are
* processed.
*
* This variable is read only when preset_dict is not NULL.
* If preset_dict is not NULL but preset_dict_size is zero,
* no preset dictionary is used (identical to only setting
* preset_dict to NULL).
*/
uint preset_dict_size;
/**
* \brief Number of literal context bits
*
* How many of the highest bits of the previous uncompressed
* eight-bit byte (also known as `literal') are taken into
* account when predicting the bits of the next literal.
*
* E.g. in typical English text, an upper-case letter is
* often followed by a lower-case letter, and a lower-case
* letter is usually followed by another lower-case letter.
* In the US-ASCII character set, the highest three bits are 010
* for upper-case letters and 011 for lower-case letters.
* When lc is at least 3, the literal coding can take advantage of
* this property in the uncompressed data.
*
* There is a limit that applies to literal context bits and literal
* position bits together: lc + lp <= 4. Without this limit the
* decoding could become very slow, which could have security related
* results in some cases like email servers doing virus scanning.
* This limit also simplifies the internal implementation in liblzma.
*
* There may be LZMA1 streams that have lc + lp > 4 (maximum possible
* lc would be 8). It is not possible to decode such streams with
* liblzma.
*/
uint lc;
enum LZMA_LCLP_MIN = 0;
enum LZMA_LCLP_MAX = 4;
enum LZMA_LC_DEFAULT = 3;
/**
* \brief Number of literal position bits
*
* lp affects what kind of alignment in the uncompressed data is
* assumed when encoding literals. A literal is a single 8-bit byte.
* See pb below for more information about alignment.
*/
uint lp;
enum LZMA_LP_DEFAULT = 0;
/**
* \brief Number of position bits
*
* pb affects what kind of alignment in the uncompressed data is
* assumed in general. The default means four-byte alignment
* (2^ pb =2^2=4), which is often a good choice when there's
* no better guess.
*
* When the aligment is known, setting pb accordingly may reduce
* the file size a little. E.g. with text files having one-byte
* alignment (US-ASCII, ISO-8859-*, UTF-8), setting pb=0 can
* improve compression slightly. For UTF-16 text, pb=1 is a good
* choice. If the alignment is an odd number like 3 bytes, pb=0
* might be the best choice.
*
* Even though the assumed alignment can be adjusted with pb and
* lp, LZMA1 and LZMA2 still slightly favor 16-byte alignment.
* It might be worth taking into account when designing file formats
* that are likely to be often compressed with LZMA1 or LZMA2.
*/
uint pb;
enum LZMA_PB_MIN = 0;
enum LZMA_PB_MAX = 4;
enum LZMA_PB_DEFAULT = 2;
/** Compression mode */
lzma_mode mode;
/**
* \brief Nice length of a match
*
* This determines how many bytes the encoder compares from the match
* candidates when looking for the best match. Once a match of at
* least nice_len bytes long is found, the encoder stops looking for
* better candidates and encodes the match. (Naturally, if the found
* match is actually longer than nice_len, the actual length is
* encoded; it's not truncated to nice_len.)
*
* Bigger values usually increase the compression ratio and
* compression time. For most files, 32 to 128 is a good value,
* which gives very good compression ratio at good speed.
*
* The exact minimum value depends on the match finder. The maximum
* is 273, which is the maximum length of a match that LZMA1 and
* LZMA2 can encode.
*/
uint nice_len;
/** Match finder ID */
lzma_match_finder mf;
/**
* \brief Maximum search depth in the match finder
*
* For every input byte, match finder searches through the hash chain
* or binary tree in a loop, each iteration going one step deeper in
* the chain or tree. The searching stops if
* - a match of at least nice_len bytes long is found;
* - all match candidates from the hash chain or binary tree have
* been checked; or
* - maximum search depth is reached.
*
* Maximum search depth is needed to prevent the match finder from
* wasting too much time in case there are lots of short match
* candidates. On the other hand, stopping the search before all
* candidates have been checked can reduce compression ratio.
*
* Setting depth to zero tells liblzma to use an automatic default
* value, that depends on the selected match finder and nice_len.
* The default is in the range [4, 200] or so (it may vary between
* liblzma versions).
*
* Using a bigger depth value than the default can increase
* compression ratio in some cases. There is no strict maximum value,
* but high values (thousands or millions) should be used with care:
* the encoder could remain fast enough with typical input, but
* malicious input could cause the match finder to slow down
* dramatically, possibly creating a denial of service attack.
*/
uint depth;
/*
* Reserved space to allow possible future extensions without
* breaking the ABI. You should not touch these, because the names
* of these variables may change. These are and will never be used
* with the currently supported options, so it is safe to leave these
* uninitialized.
*/
uint reserved_int1;
uint reserved_int2;
uint reserved_int3;
uint reserved_int4;
uint reserved_int5;
uint reserved_int6;
uint reserved_int7;
uint reserved_int8;
lzma_reserved_enum reserved_enum1;
lzma_reserved_enum reserved_enum2;
lzma_reserved_enum reserved_enum3;
lzma_reserved_enum reserved_enum4;
void *reserved_ptr1;
void *reserved_ptr2;
}
/**
* \brief Set a compression preset to lzma_options_lzma structure
*
* 0 is the fastest and 9 is the slowest. These match the switches -0 .. -9
* of the xz command line tool. In addition, it is possible to bitwise-or
* flags to the preset. Currently only LZMA_PRESET_EXTREME is supported.
* The flags are defined in container.h, because the flags are used also
* with lzma_easy_encoder().
*
* The preset values are subject to changes between liblzma versions.
*
* This function is available only if LZMA1 or LZMA2 encoder has been enabled
* when building liblzma.
*
* \return On success, false is returned. If the preset is not
* supported, true is returned.
*/
nothrow lzma_bool lzma_lzma_preset(
lzma_options_lzma *options, uint preset);
+219
View File
@@ -0,0 +1,219 @@
/**
* \file lzma/stream_flags.h
* \brief .xz Stream Header and Stream Footer encoder and decoder
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.stream_flags;
import deimos.lzma;
extern(C):
/**
* \brief Size of Stream Header and Stream Footer
*
* Stream Header and Stream Footer have the same size and they are not
* going to change even if a newer version of the .xz file format is
* developed in future.
*/
enum LZMA_STREAM_HEADER_SIZE = 12;
/**
* \brief Options for encoding/decoding Stream Header and Stream Footer
*/
struct lzma_stream_flags
{
/**
* \brief Stream Flags format version
*
* To prevent API and ABI breakages if new features are needed in
* Stream Header or Stream Footer, a version number is used to
* indicate which fields in this structure are in use. For now,
* version must always be zero. With non-zero version, the
* lzma_stream_header_encode() and lzma_stream_footer_encode()
* will return LZMA_OPTIONS_ERROR.
*
* lzma_stream_header_decode() and lzma_stream_footer_decode()
* will always set this to the lowest value that supports all the
* features indicated by the Stream Flags field. The application
* must check that the version number set by the decoding functions
* is supported by the application. Otherwise it is possible that
* the application will decode the Stream incorrectly.
*/
uint version_;
/**
* \brief Backward Size
*
* Backward Size must be a multiple of four bytes. In this Stream
* format version, Backward Size is the size of the Index field.
*
* Backward Size isn't actually part of the Stream Flags field, but
* it is convenient to include in this structure anyway. Backward
* Size is present only in the Stream Footer. There is no need to
* initialize backward_size when encoding Stream Header.
*
* lzma_stream_header_decode() always sets backward_size to
* LZMA_VLI_UNKNOWN so that it is convenient to use
* lzma_stream_flags_compare() when both Stream Header and Stream
* Footer have been decoded.
*/
lzma_vli backward_size;
enum LZMA_BACKWARD_SIZE_MIN = 4;
enum LZMA_BACKWARD_SIZE_MAX = (1UL << 34);
/**
* \brief Check ID
*
* This indicates the type of the integrity check calculated from
* uncompressed data.
*/
lzma_check check;
/*
* Reserved space to allow possible future extensions without
* breaking the ABI. You should not touch these, because the
* names of these variables may change.
*
* (We will never be able to use all of these since Stream Flags
* is just two bytes plus Backward Size of four bytes. But it's
* nice to have the proper types when they are needed.)
*/
lzma_reserved_enum reserved_enum1;
lzma_reserved_enum reserved_enum2;
lzma_reserved_enum reserved_enum3;
lzma_reserved_enum reserved_enum4;
lzma_bool reserved_bool1;
lzma_bool reserved_bool2;
lzma_bool reserved_bool3;
lzma_bool reserved_bool4;
lzma_bool reserved_bool5;
lzma_bool reserved_bool6;
lzma_bool reserved_bool7;
lzma_bool reserved_bool8;
uint reserved_int1;
uint reserved_int2;
}
/**
* \brief Encode Stream Header
*
* \param options Stream Header options to be encoded.
* options->backward_size is ignored and doesn't
* need to be initialized.
* \param out Beginning of the output buffer of
* LZMA_STREAM_HEADER_SIZE bytes.
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_OPTIONS_ERROR: options->version is not supported by
* this liblzma version.
* - LZMA_PROG_ERROR: Invalid options.
*/
nothrow lzma_ret lzma_stream_header_encode(
const lzma_stream_flags *options, ubyte *out_);
/**
* \brief Encode Stream Footer
*
* \param options Stream Footer options to be encoded.
* \param out Beginning of the output buffer of
* LZMA_STREAM_HEADER_SIZE bytes.
*
* \return - LZMA_OK: Encoding was successful.
* - LZMA_OPTIONS_ERROR: options->version is not supported by
* this liblzma version.
* - LZMA_PROG_ERROR: Invalid options.
*/
nothrow lzma_ret lzma_stream_footer_encode(
const lzma_stream_flags *options, ubyte *out_);
/**
* \brief Decode Stream Header
*
* \param options Target for the decoded Stream Header options.
* \param in Beginning of the input buffer of
* LZMA_STREAM_HEADER_SIZE bytes.
*
* options->backward_size is always set to LZMA_VLI_UNKNOWN. This is to
* help comparing Stream Flags from Stream Header and Stream Footer with
* lzma_stream_flags_compare().
*
* \return - LZMA_OK: Decoding was successful.
* - LZMA_FORMAT_ERROR: Magic bytes don't match, thus the given
* buffer cannot be Stream Header.
* - LZMA_DATA_ERROR: CRC32 doesn't match, thus the header
* is corrupt.
* - LZMA_OPTIONS_ERROR: Unsupported options are present
* in the header.
*
* \note When decoding .xz files that contain multiple Streams, it may
* make sense to print "file format not recognized" only if
* decoding of the Stream Header of the _first_ Stream gives
* LZMA_FORMAT_ERROR. If non-first Stream Header gives
* LZMA_FORMAT_ERROR, the message used for LZMA_DATA_ERROR is
* probably more appropriate.
*
* For example, Stream decoder in liblzma uses LZMA_DATA_ERROR if
* LZMA_FORMAT_ERROR is returned by lzma_stream_header_decode()
* when decoding non-first Stream.
*/
nothrow lzma_ret lzma_stream_header_decode(
lzma_stream_flags *options, const ubyte *in_);
/**
* \brief Decode Stream Footer
*
* \param options Target for the decoded Stream Header options.
* \param in Beginning of the input buffer of
* LZMA_STREAM_HEADER_SIZE bytes.
*
* \return - LZMA_OK: Decoding was successful.
* - LZMA_FORMAT_ERROR: Magic bytes don't match, thus the given
* buffer cannot be Stream Footer.
* - LZMA_DATA_ERROR: CRC32 doesn't match, thus the Stream Footer
* is corrupt.
* - LZMA_OPTIONS_ERROR: Unsupported options are present
* in Stream Footer.
*
* \note If Stream Header was already decoded successfully, but
* decoding Stream Footer returns LZMA_FORMAT_ERROR, the
* application should probably report some other error message
* than "file format not recognized", since the file more likely
* is corrupt (possibly truncated). Stream decoder in liblzma
* uses LZMA_DATA_ERROR in this situation.
*/
nothrow lzma_ret lzma_stream_footer_decode(
lzma_stream_flags *options, const ubyte *in_);
/**
* \brief Compare two lzma_stream_flags structures
*
* backward_size values are compared only if both are not
* LZMA_VLI_UNKNOWN.
*
* \return - LZMA_OK: Both are equal. If either had backward_size set
* to LZMA_VLI_UNKNOWN, backward_size values were not
* compared or validated.
* - LZMA_DATA_ERROR: The structures differ.
* - LZMA_OPTIONS_ERROR: version in either structure is greater
* than the maximum supported version (currently zero).
* - LZMA_PROG_ERROR: Invalid value, e.g. invalid check or
* backward_size.
*/
nothrow pure lzma_ret lzma_stream_flags_compare(
const lzma_stream_flags *a, const lzma_stream_flags *b);
+108
View File
@@ -0,0 +1,108 @@
/**
* \file lzma/version.h
* \brief Version number
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.version_;
import deimos.lzma;
import std.conv;
extern(C):
/*
* Version number split into components
*/
enum LZMA_VERSION_MAJOR = 5;
enum LZMA_VERSION_MINOR = 0;
enum LZMA_VERSION_PATCH = 3;
enum LZMA_VERSION_STABILITY = LZMA_VERSION_STABILITY_STABLE;
/*
#ifndef LZMA_VERSION_COMMIT
# define LZMA_VERSION_COMMIT ""
#endif*/
enum LZMA_VERSION_COMMIT = "";
/*
* Map symbolic stability levels to integers.
*/
enum LZMA_VERSION_STABILITY_ALPHA = 0;
enum LZMA_VERSION_STABILITY_BETA = 1;
enum LZMA_VERSION_STABILITY_STABLE = 2;
/**
* \brief Compile-time version number
*
* The version number is of format xyyyzzzs where
* - x = major
* - yyy = minor
* - zzz = revision
* - s indicates stability: 0 = alpha, 1 = beta, 2 = stable
*
* The same xyyyzzz triplet is never reused with different stability levels.
* For example, if 5.1.0alpha has been released, there will never be 5.1.0beta
* or 5.1.0 stable.
*
* \note The version number of liblzma has nothing to with
* the version number of Igor Pavlov's LZMA SDK.
*/
enum LZMA_VERSION = (LZMA_VERSION_MAJOR * 10000000U
+ LZMA_VERSION_MINOR * 10000U
+ LZMA_VERSION_PATCH * 10U
+ LZMA_VERSION_STABILITY);
/*
* Macros to construct the compile-time version string
*/
static if(LZMA_VERSION_STABILITY == LZMA_VERSION_STABILITY_ALPHA)
enum LZMA_VERSION_STABILITY_STRING = "alpha";
else static if(LZMA_VERSION_STABILITY == LZMA_VERSION_STABILITY_BETA)
enum LZMA_VERSION_STABILITY_STRING = "beta";
else static if(LZMA_VERSION_STABILITY == LZMA_VERSION_STABILITY_STABLE)
enum LZMA_VERSION_STABILITY_STRING = "";
else
static assert(false, "Incorrect LZMA_VERSION_STABILITY");
/**
* \brief Compile-time version as a string
*
* This can be for example "4.999.5alpha", "4.999.8beta", or "5.0.0" (stable
* versions don't have any "stable" suffix). In future, a snapshot built
* from source code repository may include an additional suffix, for example
* "4.999.8beta-21-g1d92". The commit ID won't be available in numeric form
* in LZMA_VERSION macro.
*/
enum LZMA_VERSION_STRING =
to!string(LZMA_VERSION_MAJOR) ~ "." ~ to!string(LZMA_VERSION_MINOR) ~
"." ~ to!string(LZMA_VERSION_PATCH) ~ LZMA_VERSION_STABILITY_STRING ~
LZMA_VERSION_COMMIT;
/**
* \brief Run-time version number as an integer
*
* Return the value of LZMA_VERSION macro at the compile time of liblzma.
* This allows the application to compare if it was built against the same,
* older, or newer version of liblzma that is currently running.
*/
nothrow uint lzma_version_number();
/**
* \brief Run-time version as a string
*
* This function may be useful if you want to display which version of
* liblzma your application is currently using.
*/
nothrow immutable(char)* lzma_version_string();
+167
View File
@@ -0,0 +1,167 @@
/**
* \file lzma/vli.h
* \brief Variable-length integer handling
*
* In the .xz format, most integers are encoded in a variable-length
* representation, which is sometimes called little endian base-128 encoding.
* This saves space when smaller values are more likely than bigger values.
*
* The encoding scheme encodes seven bits to every byte, using minimum
* number of bytes required to represent the given value. Encodings that use
* non-minimum number of bytes are invalid, thus every integer has exactly
* one encoded representation. The maximum number of bits in a VLI is 63,
* thus the vli argument must be less than or equal to UINT64_MAX / 2. You
* should use LZMA_VLI_MAX for clarity.
*/
/*
* Author: Lasse Collin
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*
* See ../lzma.h for information about liblzma as a whole.
*/
module deimos.lzma_.vli;
import deimos.lzma;
extern(C):
/**
* \brief Maximum supported value of a variable-length integer
*/
enum LZMA_VLI_MAX = (ulong.max / 2);
/**
* \brief VLI value to denote that the value is unknown
*/
enum LZMA_VLI_UNKNOWN = ulong.max;
/**
* \brief Maximum supported encoded length of variable length integers
*/
enum LZMA_VLI_BYTES_MAX = 9;
/**
* \brief VLI constant suffix
*/
//#define LZMA_VLI_C(n) UINT64_C(n)
/**
* \brief Variable-length integer type
*
* Valid VLI values are in the range [0, LZMA_VLI_MAX]. Unknown value is
* indicated with LZMA_VLI_UNKNOWN, which is the maximum value of the
* underlaying integer type.
*
* lzma_vli will be uint64_t for the foreseeable future. If a bigger size
* is needed in the future, it is guaranteed that 2 * LZMA_VLI_MAX will
* not overflow lzma_vli. This simplifies integer overflow detection.
*/
alias ulong lzma_vli;
/**
* \brief Validate a variable-length integer
*
* This is useful to test that application has given acceptable values
* for example in the uncompressed_size and compressed_size variables.
*
* \return True if the integer is representable as VLI or if it
* indicates unknown value.
*/
bool lzma_vli_is_valid(lzma_vli vli)
{
return vli <= LZMA_VLI_MAX || (vli) == LZMA_VLI_UNKNOWN;
}
/**
* \brief Encode a variable-length integer
*
* This function has two modes: single-call and multi-call. Single-call mode
* encodes the whole integer at once; it is an error if the output buffer is
* too small. Multi-call mode saves the position in *vli_pos, and thus it is
* possible to continue encoding if the buffer becomes full before the whole
* integer has been encoded.
*
* \param vli Integer to be encoded
* \param vli_pos How many VLI-encoded bytes have already been written
* out. When starting to encode a new integer in
* multi-call mode, *vli_pos must be set to zero.
* To use single-call encoding, set vli_pos to NULL.
* \param out Beginning of the output buffer
* \param out_pos The next byte will be written to out[*out_pos].
* \param out_size Size of the out buffer; the first byte into
* which no data is written to is out[out_size].
*
* \return Slightly different return values are used in multi-call and
* single-call modes.
*
* Single-call (vli_pos == NULL):
* - LZMA_OK: Integer successfully encoded.
* - LZMA_PROG_ERROR: Arguments are not sane. This can be due
* to too little output space; single-call mode doesn't use
* LZMA_BUF_ERROR, since the application should have checked
* the encoded size with lzma_vli_size().
*
* Multi-call (vli_pos != NULL):
* - LZMA_OK: So far all OK, but the integer is not
* completely written out yet.
* - LZMA_STREAM_END: Integer successfully encoded.
* - LZMA_BUF_ERROR: No output space was provided.
* - LZMA_PROG_ERROR: Arguments are not sane.
*/
nothrow lzma_ret lzma_vli_encode(lzma_vli vli, size_t *vli_pos,
ubyte* out_, size_t *out_pos, size_t out_size);
/**
* \brief Decode a variable-length integer
*
* Like lzma_vli_encode(), this function has single-call and multi-call modes.
*
* \param vli Pointer to decoded integer. The decoder will
* initialize it to zero when *vli_pos == 0, so
* application isn't required to initialize *vli.
* \param vli_pos How many bytes have already been decoded. When
* starting to decode a new integer in multi-call
* mode, *vli_pos must be initialized to zero. To
* use single-call decoding, set vli_pos to NULL.
* \param in Beginning of the input buffer
* \param in_pos The next byte will be read from in[*in_pos].
* \param in_size Size of the input buffer; the first byte that
* won't be read is in[in_size].
*
* \return Slightly different return values are used in multi-call and
* single-call modes.
*
* Single-call (vli_pos == NULL):
* - LZMA_OK: Integer successfully decoded.
* - LZMA_DATA_ERROR: Integer is corrupt. This includes hitting
* the end of the input buffer before the whole integer was
* decoded; providing no input at all will use LZMA_DATA_ERROR.
* - LZMA_PROG_ERROR: Arguments are not sane.
*
* Multi-call (vli_pos != NULL):
* - LZMA_OK: So far all OK, but the integer is not
* completely decoded yet.
* - LZMA_STREAM_END: Integer successfully decoded.
* - LZMA_DATA_ERROR: Integer is corrupt.
* - LZMA_BUF_ERROR: No input was provided.
* - LZMA_PROG_ERROR: Arguments are not sane.
*/
nothrow lzma_ret lzma_vli_decode(lzma_vli *vli, size_t *vli_pos,
const(ubyte)* in_, size_t *in_pos, size_t in_size);
/**
* \brief Get the number of bytes required to encode a VLI
*
* \return Number of bytes on success (1-9). If vli isn't valid,
* zero is returned.
*/
nothrow pure uint lzma_vli_size(lzma_vli vli);
+273 -98
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
@@ -20,12 +20,18 @@ module disassembler;
import std.file;
import std.string;
import std.array;
import std.conv;
import std.exception;
import std.algorithm;
import std.path;
import std.md5;
import abcfile;
import asprogram;
import autodata;
import common;
alias std.array.join join;
final class StringBuilder
{
@@ -35,7 +41,7 @@ final class StringBuilder
this(string filename)
{
if (exists(filename))
if (exists(longPath(filename)))
throw new Exception(filename ~ " exists");
this.filename = filename;
buf.length = 1024;
@@ -64,10 +70,10 @@ final class StringBuilder
for (int l=0; l<dirSegments.length-1; l++)
{
auto subdir = join(dirSegments[0..l+1], "/");
if (!exists(subdir))
mkdir(subdir);
if (!exists(longPath(subdir)))
mkdir(longPath(subdir));
}
write(filename, buf[0..pos]);
write(longPath(filename), buf[0..pos]);
}
int indent;
@@ -127,10 +133,9 @@ final class RefBuilder : ASTraitsVisitor
{
case Type.Multiname:
assert(multiname.kind == ASType.QName);
if (multiname.vQName.ns.kind == ASType.PrivateNamespace)
throw new Exception("Stringifying unexpanded context");
auto nsName = multiname.vQName.ns.name;
if (multiname.vQName.ns.kind == ASType.PrivateNamespace) // unexpanded private namespace
nsName ~= '#' ~ to!string(multiname.vQName.ns.privateIndex);
if (nsName.length)
if (multiname.vQName.name.length)
return [Segment('/', nsName), Segment(filename ? '/' : ':', multiname.vQName.name)];
@@ -146,29 +151,31 @@ final class RefBuilder : ASTraitsVisitor
}
}
static ContextItem[] expand(RefBuilder refs, ContextItem[] context, bool noThrow) /// recursively expand all private namespaces
static ContextItem[] expand(RefBuilder refs, ContextItem[] context, bool abortOnUnknown) /// recursively expand all private namespaces
{
ContextItem[] newContext;
foreach (ref c; context)
{
auto cEx = c.expand(refs, noThrow);
auto cEx = c.expand(refs, abortOnUnknown);
if (cEx is null) return null;
newContext ~= cEx;
}
return newContext;
}
ContextItem[] expand(RefBuilder refs, bool noThrow)
ContextItem[] expand(RefBuilder refs, bool abortOnUnknown)
{
if (type==Type.Multiname && multiname.vQName.ns.kind == ASType.PrivateNamespace)
{
auto pcontext = multiname.vQName.ns.privateIndex in refs.privateNamespaces.contexts;
if (pcontext is null)
if (noThrow)
{
if (abortOnUnknown)
return null;
else
throw new Exception("Expanding unknown private namespace " ~ to!string(multiname.vQName.ns.privateIndex));
auto expanded = expand(refs, *pcontext, noThrow);
return (&this)[0..1];
}
auto expanded = expand(refs, *pcontext, abortOnUnknown);
if (expanded is null) return null;
return expanded ~ (multiname.vQName.name.length ? [ContextItem(multiname.vQName.name)] : null); // hack
}
@@ -208,6 +215,77 @@ final class RefBuilder : ASTraitsVisitor
}
mixin(epilog);
}
static bool similar(ref ContextItem i1, ref ContextItem i2)
{
if (i1.type != i2.type) return false;
if (i1.type == ContextItem.Type.String)
return i1.str == i2.str;
if (i1.multiname.vQName.name != i2.multiname.vQName.name) return false;
return nsSimilar(i1.multiname.vQName.ns, i2.multiname.vQName.ns);
}
// truncate=true -> return partial ContextItem
// truncate=false -> return null on partial match
static ContextItem[] combine(bool truncate)(ref ContextItem c1, ref ContextItem c2)
{
if (similar(c1, c2))
return [c1];
if (c1.type != ContextItem.Type.Multiname || c2.type != ContextItem.Type.Multiname)
return null;
if (c1.multiname.kind != ASType.QName || c2.multiname.kind != ASType.QName)
return null;
auto ns1 = c1.multiname.vQName.ns;
auto ns2 = c2.multiname.vQName.ns;
if (nsSimilar(ns1, ns2) && ns1.name.length && truncate)
{
auto m = new ASProgram.Multiname;
m.kind = ASType.QName;
m.vQName.ns = ns1;
return [ContextItem(m)];
}
if (c1.multiname.vQName.name && !c2.multiname.vQName.name && truncate)
{
swap(c1, c2);
swap(ns1, ns2);
}
if (!c1.multiname.vQName.name && c2.multiname.vQName.name && nsSimilar(ns1, ns2))
{
if (truncate)
{
auto m = new ASProgram.Multiname;
m.kind = ASType.QName;
m.vQName.ns = ns1;
return [ContextItem(m)];
}
else
return [c2];
}
if (ns1.name.length && ns2.name.length)
{
if (ns1.name.length > ns2.name.length && truncate)
{
swap(c1, c2);
swap(ns1, ns2);
}
auto fullName1 = ns1.name ~ (c1.multiname.vQName.name ? ':' ~ c1.multiname.vQName.name : "");
auto fullName2 = ns2.name ~ (c2.multiname.vQName.name ? ':' ~ c2.multiname.vQName.name : "");
if (fullName2.startsWith(fullName1 ~ ":"))
return [truncate ? c1 : c2];
}
return null;
}
alias combine!true commonRoot;
alias combine!false deduplicate;
}
ContextItem[] context; // potential optimization: use array-based stack
@@ -229,7 +307,6 @@ final class RefBuilder : ASTraitsVisitor
if (pexisting)
{
auto rootContext = contextRoot(*pexisting, context);
enforce(rootContext.length, format("Can't find common private namespace root between ", *pexisting, " and ", context));
contexts[p] = rootContext;
}
else
@@ -237,8 +314,10 @@ final class RefBuilder : ASTraitsVisitor
}
else
{
enforce(p !in contexts, format("Duplicate object reference: ", contexts[p], " and ", context));
contexts[p] = context.dup;
if (p in contexts)
contexts[p] = [ContextItem("multireferenced")];
else
contexts[p] = context.dup;
}
}
@@ -282,35 +361,31 @@ final class RefBuilder : ASTraitsVisitor
return *pname;
}
version (Windows)
static string[string] filenameMappings;
static string[string] filenameMappings;
string getFilename(U)(U obj)
string getFilename(U)(U obj, string suffix)
{
auto pname = cast(T)obj in filenames;
assert(pname, format("Unscanned object: ", obj));
auto filename = *pname;
version (Windows)
string[] dirSegments = split(filename, "/");
for (int l=0; l<dirSegments.length; l++)
{
string[] dirSegments = split(filename, "/");
for (int l=0; l<dirSegments.length; l++)
again:
string subpath = join(dirSegments[0..l+1], "/");
string subpathl = toLower(subpath);
string* canonicalp = subpathl in filenameMappings;
if (canonicalp && *canonicalp != subpath)
{
again:
string subpath = join(dirSegments[0..l+1], "/");
string subpathl = tolower(subpath);
string* canonicalp = subpathl in filenameMappings;
if (canonicalp && *canonicalp != subpath)
{
dirSegments[l] = dirSegments[l] ~ "_"; // not ~=
goto again;
}
filenameMappings[subpathl] = subpath;
dirSegments[l] = dirSegments[l] ~ "_"; // not ~=
goto again;
}
filename = join(dirSegments, "/");
filenameMappings[subpathl] = subpath;
}
filename = join(dirSegments, "/");
return filename ~ ".asasm";
return filename ~ "." ~ suffix ~ ".asasm";
}
}
@@ -343,7 +418,10 @@ final class RefBuilder : ASTraitsVisitor
if (c)
classContexts ~= c;
}
context = reduce!contextRoot(new ContextItem[0], classContexts);
if (classContexts.length)
context = reduce!contextRoot(classContexts);
else
context = null;
if (context.length==0)
pushContext("script_" ~ to!string(i));
scripts.add(v, context);
@@ -366,6 +444,20 @@ final class RefBuilder : ASTraitsVisitor
popContext();
}
// add private namespaces referenced only at script level
foreach (v; as.scripts)
foreach (trait; v.traits)
if (trait.name.kind == ASType.QName && trait.name.vQName.ns.kind == ASType.PrivateNamespace && !privateNamespaces.isAdded(trait.name.vQName.ns.privateIndex))
privateNamespaces.add(trait.name.vQName.ns.privateIndex, scripts.getContext(v));
foreach (privateIndex, b; possibleOrphanPrivateNamespaces)
if (!privateNamespaces.isAdded(privateIndex))
{
pushContext("private_namespace_" ~ to!string(privateIndex));
privateNamespaces.add(privateIndex, context);
popContext();
}
privateNamespaces.coagulate(this);
objects.coagulate(this);
scripts.coagulate(this);
@@ -413,7 +505,6 @@ final class RefBuilder : ASTraitsVisitor
static bool uninteresting(ContextItem[] c)
{
return
(c.length==0) ||
(c.length==1 && c[0].type==ContextItem.Type.String && c[0].str.startsWith("script_") && c[0].str.endsWith("_sinit")) ||
(c.length==1 && c[0].type==ContextItem.Type.String && c[0].str.startsWith("orphan_method_")) ||
false;
@@ -422,44 +513,30 @@ final class RefBuilder : ASTraitsVisitor
if (uninteresting(c1)) return c2;
if (uninteresting(c2)) return c1;
static bool nsSimilar(ASProgram.Namespace ns1, ASProgram.Namespace ns2)
ContextItem[] c;
while (c.length<c1.length && c.length<c2.length)
{
if (ns1.kind==ASType.PrivateNamespace || ns2.kind==ASType.PrivateNamespace)
return ns1.kind==ns2.kind && ns1.privateIndex==ns2.privateIndex;
// ignore ns kind in other cases
return ns1.name == ns2.name;
}
static bool similar(ref ContextItem i1, ref ContextItem i2)
{
if (i1.type != i2.type) return false;
if (i1.type == ContextItem.Type.String)
return i1.str == i2.str;
if (i1.multiname.vQName.name != i2.multiname.vQName.name) return false;
return nsSimilar(i1.multiname.vQName.ns, i2.multiname.vQName.ns);
}
int i=0;
while (i<c1.length && i<c2.length && similar(c1[i], c2[i])) i++;
auto c = c1[0..i];
if (i<c1.length && i<c2.length && c1[i].type==ContextItem.Type.Multiname && c2[i].type==ContextItem.Type.Multiname && nsSimilar(c1[i].multiname.vQName.ns, c2[i].multiname.vQName.ns) && c1[i].multiname.vQName.ns.name.length)
{
auto m = new ASProgram.Multiname;
m.kind = ASType.QName;
m.vQName.ns = c1[i].multiname.vQName.ns;
c ~= ContextItem(m);
auto root = ContextItem.commonRoot(c1[c.length], c2[c.length]);
assert(root.length <= 1);
if (root.length)
c ~= root;
else
break;
}
return c;
}
bool[uint] possibleOrphanPrivateNamespaces;
void visitNamespace(ASProgram.Namespace ns)
{
if (ns is null) return;
if (ns.kind == ASType.PrivateNamespace)
{
assert(context.length > 0, "No context");
//assert(ns.name is null, "Named private namespace");
int myPos = context.length;
auto myPos = context.length;
foreach (i, ref item; context)
if (item.type == ContextItem.Type.Multiname && item.multiname.vQName.ns == ns)
{
@@ -467,7 +544,10 @@ final class RefBuilder : ASTraitsVisitor
break;
}
if (myPos == 0)
{
possibleOrphanPrivateNamespaces[ns.privateIndex] = true;
return;
}
auto myContext = context[0..myPos].dup;
privateNamespaces.add(ns.privateIndex, myContext);
@@ -482,6 +562,7 @@ final class RefBuilder : ASTraitsVisitor
void visitMultiname(ASProgram.Multiname m)
{
if (m is null) return;
with (m)
switch (kind)
{
@@ -539,10 +620,15 @@ final class RefBuilder : ASTraitsVisitor
string contextToString(ContextItem[] context, bool filename)
{
context = ContextItem.expand(this, context, false);
if (!context.length)
return null;
foreach_reverse (i, c; context)
if (i>0 && c==context[i-1])
context = context[0..i] ~ context[i+1..$];
foreach_reverse (i; 0..context.length-1)
{
auto root = ContextItem.deduplicate(context[i], context[i+1]);
if (root.length)
context = context[0..i] ~ root ~ context[i+2..$];
}
ContextItem.Segment[] segments;
foreach (ci; context)
@@ -555,21 +641,43 @@ final class RefBuilder : ASTraitsVisitor
string result;
foreach (c; s)
if (c == '.')
if (c == '.' || c == ':')
result ~= '/';
else
if (c == ':' || c == '\\' || c == '*' || c == '?' || c == '"' || c == '<' || c == '>' || c == '|' || c < 0x20 || c >= 0x7F || c == ' ' || c == '%')
if (c == '\\' || c == '*' || c == '?' || c == '"' || c == '<' || c == '>' || c == '|' || c < 0x20 || c >= 0x7F || c == ' ' || c == '%')
result ~= format("%%%02X", c);
else
result ~= c;
return result;
auto pathSegments = result.split("/");
if (!pathSegments.length)
pathSegments = [""];
foreach (ref pathSegment; pathSegments)
{
if (pathSegment == "")
pathSegment = "%";
static const reservedNames = ["CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"];
auto pathSegmentU = pathSegment.toUpper();
foreach (reservedName; reservedNames)
if (pathSegmentU.startsWith(reservedName))
{
pathSegment = "%" ~ pathSegment;
break;
}
if (pathSegment.length > 240)
pathSegment = pathSegment[0..200] ~ '-' ~ getDigestString(pathSegment);
}
return arrayJoin(pathSegments, "/");
}
string[] strings = new string[segments.length];
foreach (i, ref s; segments)
strings[i] = (i>0 ? cast(string)[s.delim] : null) ~ escape(s.str);
return join(strings);
return arrayJoin(strings);
}
void addObject(T)(T obj) { objects.add(obj, context); }
@@ -577,12 +685,21 @@ final class RefBuilder : ASTraitsVisitor
void addClass(ASProgram.Class vclass)
{
addObject(vclass);
pushContext("cinit");
addMethod(vclass.cinit);
popContext();
pushContext("iinit");
addMethod(vclass.instance.iinit);
popContext();
pushContext("instance");
visitMultiname(vclass.instance.name);
visitMultiname(vclass.instance.superName);
foreach (iface; vclass.instance.interfaces)
visitMultiname(iface);
popContext();
}
void addMethod(ASProgram.Method method)
@@ -599,17 +716,27 @@ final class Disassembler
string name, dir;
RefBuilder refs;
void newInclude(StringBuilder mainsb, string filename, void delegate(StringBuilder) callback)
void newInclude(StringBuilder mainsb, string filename, void delegate(StringBuilder) callback, bool doInline = true)
{
if (mainsb.filename.split("/").length != 2)
throw new Exception("TODO");
StringBuilder sb = new StringBuilder(dir ~ "/" ~ filename);
callback(sb);
sb.save();
if (doInline)
{
string base = dirName(mainsb.filename);
string full = dir ~ "/" ~ filename;
uint up = 0;
while (!full.startsWith(base))
base = dirName(base), up++;
string rel = replicate("../", up) ~ full[base.length+1..$];
mainsb ~= "#include ";
dumpString(mainsb, filename);
mainsb.newLine();
StringBuilder sb = new StringBuilder(full);
callback(sb);
sb.save();
mainsb ~= "#include ";
dumpString(mainsb, rel);
mainsb.newLine();
}
else
callback(mainsb);
}
this(ASProgram as, string dir, string name)
@@ -626,6 +753,8 @@ final class Disassembler
StringBuilder sb = new StringBuilder(dir ~ "/" ~ name ~ ".main.asasm");
sb ~= "#version 2";
sb.newLine();
sb ~= "#include ";
dumpString(sb, name ~ ".privatens.asasm");
sb.newLine();
@@ -641,9 +770,9 @@ final class Disassembler
sb.newLine();
sb.newLine();
foreach (i, script; as.scripts)
foreach (uint i, script; as.scripts)
{
newInclude(sb, refs.scripts.getFilename(script), (StringBuilder sb) {
newInclude(sb, refs.scripts.getFilename(script, "script"), (StringBuilder sb) {
dumpScript(sb, script, i);
});
}
@@ -656,7 +785,7 @@ final class Disassembler
sb.newLine();
foreach (i, vclass; as.orphanClasses)
newInclude(sb, refs.objects.getFilename(vclass), (StringBuilder sb) {
newInclude(sb, refs.objects.getFilename(vclass, "class"), (StringBuilder sb) {
dumpClass(sb, vclass);
});
@@ -670,7 +799,7 @@ final class Disassembler
sb.newLine();
foreach (i, method; as.orphanMethods)
newInclude(sb, refs.objects.getFilename(method), (StringBuilder sb) {
newInclude(sb, refs.objects.getFilename(method, "method"), (StringBuilder sb) {
dumpMethod(sb, method, "method");
});
@@ -742,8 +871,13 @@ final class Disassembler
static double forceDouble(double d) { static double n; n = d; return n; }
if (s != "nan" && s != "inf" && s != "-inf")
{
foreach_reverse (i; 1..s.length)
if (s[i]>='0' && s[i]<='8' && forceDouble(to!double(s[0..i] ~ cast(char)(s[i]+1)))==v)
s = s[0..i] ~ cast(char)(s[i]+1);
while (s.length>2 && s[$-1]!='.' && forceDouble(to!double(s[0..$-1]))==v)
s = s[0..$-1];
}
sb ~= s;
}
}
@@ -870,9 +1004,9 @@ final class Disassembler
}
}
void dumpTraits(StringBuilder sb, ASProgram.Trait[] traits)
void dumpTraits(StringBuilder sb, ASProgram.Trait[] traits, bool inScript = false)
{
foreach (ref trait; traits)
foreach (/*ref*/ trait; traits)
{
sb ~= "trait ";
sb ~= TraitKindNames[trait.kind];
@@ -909,7 +1043,10 @@ final class Disassembler
dumpUInt(sb, trait.vClass.slotId);
}
sb.indent++; sb.newLine();
dumpClass(sb, trait.vClass.vclass);
newInclude(sb, refs.objects.getFilename(trait.vClass.vclass, "class"), (StringBuilder sb) {
dumpClass(sb, trait.vClass.vclass);
});
break;
case TraitKind.Function:
if (trait.vFunction.slotId)
@@ -918,7 +1055,9 @@ final class Disassembler
dumpUInt(sb, trait.vFunction.slotId);
}
sb.indent++; sb.newLine();
dumpMethod(sb, trait.vFunction.vfunction, "method");
newInclude(sb, refs.objects.getFilename(trait.vFunction.vfunction, "method"), (StringBuilder sb) {
dumpMethod(sb, trait.vFunction.vfunction, "method");
}, inScript);
break;
case TraitKind.Method:
case TraitKind.Getter:
@@ -929,7 +1068,9 @@ final class Disassembler
dumpUInt(sb, trait.vMethod.dispId);
}
sb.indent++; sb.newLine();
dumpMethod(sb, trait.vMethod.vmethod, "method");
newInclude(sb, refs.objects.getFilename(trait.vMethod.vmethod, "method"), (StringBuilder sb) {
dumpMethod(sb, trait.vMethod.vmethod, "method");
}, inScript);
break;
default:
throw new Exception("Unknown trait kind");
@@ -957,12 +1098,13 @@ final class Disassembler
sb ~= "metadata ";
dumpString(sb, metadata.name);
sb.indent++; sb.newLine();
foreach (ref item; metadata.items)
assert(metadata.keys.length == metadata.values.length);
foreach (i; 0..metadata.keys.length)
{
sb ~= "item ";
dumpString(sb, item.key);
dumpString(sb, metadata.keys[i]);
sb ~= " ";
dumpString(sb, item.value);
dumpString(sb, metadata.values[i]);
sb.newLine();
}
sb.indent--; sb ~= "end ; metadata"; sb.newLine();
@@ -1125,7 +1267,7 @@ final class Disassembler
sb ~= to!string(index);
sb.indent++; sb.newLine();
dumpMethod(sb, script.sinit, "sinit");
dumpTraits(sb, script.traits);
dumpTraits(sb, script.traits, true);
sb.indent--; sb ~= "end ; script"; sb.newLine();
}
@@ -1239,7 +1381,7 @@ final class Disassembler
}
bool extraNewLine = false;
foreach (ii, ref instruction; instructions)
foreach (uint ii, ref instruction; instructions)
{
if (extraNewLine)
sb.newLine();
@@ -1250,7 +1392,7 @@ final class Disassembler
auto argTypes = opcodeInfo[instruction.opcode].argumentTypes;
if (argTypes.length)
{
for (int i=opcodeInfo[instruction.opcode].name.length; i<20; i++)
foreach (i; opcodeInfo[instruction.opcode].name.length..20)
sb ~= ' ';
foreach (i, type; argTypes)
{
@@ -1288,10 +1430,16 @@ final class Disassembler
dumpMultiname(sb, instruction.arguments[i].multinamev);
break;
case OpcodeArgumentType.Class:
dumpString(sb, refs.objects.getName(instruction.arguments[i].classv));
if (instruction.arguments[i].classv is null)
sb ~= "null";
else
dumpString(sb, refs.objects.getName(instruction.arguments[i].classv));
break;
case OpcodeArgumentType.Method:
dumpString(sb, refs.objects.getName(instruction.arguments[i].methodv));
if (instruction.arguments[i].methodv is null)
sb ~= "null";
else
dumpString(sb, refs.objects.getName(instruction.arguments[i].methodv));
break;
case OpcodeArgumentType.JumpTarget:
@@ -1317,10 +1465,20 @@ final class Disassembler
}
sb.newLine();
}
checkLabel(instructions.length);
checkLabel(to!uint(instructions.length));
}
}
private:
bool nsSimilar(ASProgram.Namespace ns1, ASProgram.Namespace ns2)
{
if (ns1.kind==ASType.PrivateNamespace || ns2.kind==ASType.PrivateNamespace)
return ns1.kind==ns2.kind && ns1.privateIndex==ns2.privateIndex;
// ignore ns kind in other cases
return ns1.name == ns2.name;
}
bool[256] newLineAfter;
static this()
@@ -1357,7 +1515,24 @@ static this()
Opcode.OP_setproperty,
Opcode.OP_setpropertylate,
Opcode.OP_setslot,
Opcode.OP_setsuper
Opcode.OP_setsuper,
Opcode.OP_si8,
Opcode.OP_si16,
Opcode.OP_si32,
Opcode.OP_sf32,
Opcode.OP_sf64,
])
newLineAfter[o] = true;
}
/// Force a raw data join (workaround for issue 6064)
T[] arrayJoin(T)(T[][] arrays, T[] sep)
{
return cast(T[])join(cast(ubyte[][])arrays, cast(ubyte[])sep);
}
/// ditto
T[] arrayJoin(T)(T[][] arrays)
{
return cast(T[])join(cast(ubyte[][])arrays);
}
BIN
View File
Binary file not shown.
+100
View File
@@ -0,0 +1,100 @@
/*
* Copyright 2012 Vladimir Panteleev <vladimir@thecybershadow.net>
* This file is part of RABCDAsm.
*
* RABCDAsm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RABCDAsm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RABCDAsm. If not, see <http://www.gnu.org/licenses/>.
*/
module lzma;
version(HAVE_LZMA) {} else static assert(0, "LZMA is not available (HAVE_LZMA version is not defined)");
import deimos.lzma;
import std.conv;
import std.exception;
version (Windows)
{ pragma(lib, "liblzma"); }
else
{ pragma(lib, "lzma"); }
align(1)
struct LZMAHeader
{
ubyte compressionParameters;
uint dictionarySize;
long decompressedSize = -1;
}
static assert(LZMAHeader.sizeof == 13);
ubyte[] lzmaDecompress(LZMAHeader header, in ubyte[] compressedData)
{
enforce(header.decompressedSize > 0, "Decompression with unknown size is unsupported");
lzma_stream strm;
lzmaEnforce(lzma_alone_decoder(&strm, ulong.max), "lzma_alone_decoder");
scope(exit) lzma_end(&strm);
auto outBuf = new ubyte[to!size_t(header.decompressedSize)];
strm.next_out = outBuf.ptr;
strm.avail_out = outBuf.length;
void decompress(in ubyte[] chunk)
{
strm.next_in = chunk.ptr;
strm.avail_in = chunk.length;
lzmaEnforce(lzma_code(&strm, lzma_action.LZMA_RUN), "lzma_code");
enforce(strm.avail_in == 0, "Not all data was read");
}
header.decompressedSize = -1; // Required as Flash uses End-of-Stream marker
decompress(cast(ubyte[])(&header)[0..1]);
decompress(compressedData);
lzmaEnforce(lzma_code(&strm, lzma_action.LZMA_FINISH), "lzma_code");
enforce(strm.avail_out == 0, "Decompressed size mismatch");
return outBuf;
}
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_stream strm;
lzmaEnforce(lzma_alone_encoder(&strm, &opts), "lzma_alone_encoder");
scope(exit) lzma_end(&strm);
auto outBuf = new ubyte[decompressedData.length];
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); }
enforce(strm.avail_in == 0, "Not all data was read");
lzmaEnforce(lzma_code(&strm, lzma_action.LZMA_FINISH), "lzma_code");
*header = *cast(LZMAHeader*)outBuf.ptr;
return outBuf[LZMAHeader.sizeof..to!size_t(strm.total_out)];
}
private void lzmaEnforce(lzma_ret v, string f)
{
if (v != lzma_ret.LZMA_OK && v != lzma_ret.LZMA_STREAM_END)
throw new Exception(text(f, " error: ", v));
}
+4 -2
View File
@@ -16,6 +16,8 @@
module murmurhash2a;
import std.conv;
struct MurmurHash2A
{
private static string mmix(string h, string k) { return "{ "~k~" *= m; "~k~" ^= "~k~" >> r; "~k~" *= m; "~h~" *= m; "~h~" ^= "~k~"; }"; }
@@ -67,8 +69,8 @@ public:
void Add(ref ubyte v) { Add(&v, v.sizeof); }
void Add(ref int v) { Add(&v, v.sizeof); }
void Add(ref uint v) { Add(&v, v.sizeof); }
void Add(string s) { Add(s.ptr, s.length); }
void Add(ubyte[] s) { Add(s.ptr, s.length); }
void Add(string s) { Add(s.ptr, to!uint(s.length)); }
void Add(ubyte[] s) { Add(s.ptr, to!uint(s.length)); }
private:
+1 -1
View File
@@ -35,5 +35,5 @@ void main(string[] args)
assembler.assemble(arg);
}
auto abc = as.toABC();
write(getName(args[1]) ~ ".abc", abc.write());
write(setExtension(args[1], "abc"), abc.write());
}
+1 -1
View File
@@ -32,7 +32,7 @@ void main(string[] args)
{
scope abc = ABCFile.read(cast(ubyte[])read(arg));
scope as = ASProgram.fromABC(abc);
scope disassembler = new Disassembler(as, getName(arg), getName(getBaseName(arg)));
scope disassembler = new Disassembler(as, stripExtension(arg), stripExtension(baseName(arg)));
disassembler.disassemble();
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ void main(string[] args)
enforce(tag.data.length >= 6);
ushort id = *cast(short*)tag.data.ptr;
ubyte[] bin = tag.data[6..$];
std.file.write(format("%s-%d.bin", getName(arg), id), bin);
std.file.write(format("%s-%d.bin", stripExtension(arg), id), bin);
}
enforce(found, "No DefineBinaryData tags found");
}
+5 -10
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
@@ -19,8 +19,6 @@
module swfdecompress;
import std.file;
import std.zlib;
import zlibx;
import swffile;
void main(string[] args)
@@ -29,13 +27,10 @@ void main(string[] args)
throw new Exception("No file specified");
foreach (arg; args[1..$])
{
auto swf = cast(ubyte[])read(arg);
auto header = cast(SWFFile.Header*)swf.ptr;
if (header.signature[0] == cast(ubyte)'F')
auto swf = SWFFile.read(cast(ubyte[])read(arg));
if (swf.header.signature[0] == cast(ubyte)'F')
throw new Exception("Already uncompressed");
if (header.signature[0] != cast(ubyte)'C')
throw new Exception("Unknown format");
header.signature[0] = cast(ubyte)'F'; // uncompressed
write(arg, swf[0..8] ~ exactUncompress(swf[8..$], header.fileLength-8));
swf.header.signature[0] = cast(ubyte)'F'; // uncompressed
write(arg, swf.write());
}
}
+53 -8
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
@@ -18,8 +18,11 @@
module swffile;
import std.conv;
import std.exception;
import std.zlib;
import zlibx;
version (HAVE_LZMA) import lzma;
/**
* Implements a shallow representation of a .swf file.
@@ -42,6 +45,14 @@ final class SWFFile
static assert(Header.sizeof == 8);
}
align(1) struct LZMAHeader
{
uint compressedLength;
ubyte compressionParameters;
uint dictionarySize;
static assert(LZMAHeader.sizeof == 9);
}
struct Rect
{
//int xMin, xMax, yMin, yMax;
@@ -78,14 +89,30 @@ private final class SWFReader
swf = new SWFFile();
readRaw((&swf.header)[0..1]);
if ((swf.header.signature[0] != 'F' && swf.header.signature[0] != 'C') || swf.header.signature[1] != 'W' || swf.header.signature[2] != 'S')
throw new Exception("Invalid file signature");
enforce(swf.header.signature == "FWS" || swf.header.signature == "CWS" || swf.header.signature == "ZWS", "Invalid file signature");
if (swf.header.signature[0] == 'C')
buf = buf[0..swf.header.sizeof] ~ exactUncompress(buf[swf.header.sizeof..$], swf.header.fileLength-swf.header.sizeof);
else
if (swf.header.signature[0] == 'Z')
{
buf = buf[0..8] ~ exactUncompress(buf[8..$], swf.header.fileLength-8);
if (swf.header.fileLength != buf.length)
throw new Exception("Incorrect file length in file header");
version (HAVE_LZMA)
{
SWFFile.LZMAHeader lzHeader;
readRaw((&lzHeader)[0..1]);
lzma.LZMAHeader lzInfo;
lzInfo.compressionParameters = lzHeader.compressionParameters;
lzInfo.dictionarySize = lzHeader.dictionarySize;
lzInfo.decompressedSize = swf.header.fileLength - swf.header.sizeof;
enforce(swf.header.sizeof + lzHeader.sizeof + lzHeader.compressedLength == buf.length, "Trailing data in LZMA-compressed SWF file");
buf = buf[0..swf.header.sizeof] ~ lzmaDecompress(lzInfo, buf[swf.header.sizeof + lzHeader.sizeof .. $]);
pos = swf.header.sizeof;
}
else
enforce(false, "This version was built without LZMA support");
}
enforce(swf.header.fileLength == buf.length, "Incorrect file length in file header");
swf.frameSize = readRect();
swf.frameRate = readU16();
swf.frameCount = readU16();
@@ -243,15 +270,33 @@ private final class SWFWriter
{
u |= 0x3F;
buf ~= toArray(u);
uint l = tag.data.length;
uint l = to!uint(tag.data.length);
buf ~= toArray(l);
}
buf ~= tag.data;
}
swf.header.fileLength = 8 + buf.length;
swf.header.fileLength = to!uint(swf.header.sizeof + buf.length);
if (swf.header.signature[0] == 'C')
buf = cast(ubyte[])compress(buf, 9);
else
if (swf.header.signature[0] == 'Z')
{
version (HAVE_LZMA)
{
lzma.LZMAHeader lzInfo;
buf = lzmaCompress(buf, &lzInfo);
SWFFile.LZMAHeader lzHeader;
lzHeader.compressionParameters = lzInfo.compressionParameters;
lzHeader.dictionarySize = lzInfo.dictionarySize;
lzHeader.compressedLength = to!uint(buf.length);
buf = cast(ubyte[])(&lzHeader)[0..1] ~ buf;
}
else
enforce(false, "This version was built without LZMA support");
}
buf = toArray(swf.header) ~ buf;
return buf;
+36
View File
@@ -0,0 +1,36 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RABCDAsm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RABCDAsm. If not, see <http://www.gnu.org/licenses/>.
*/
module swflzmacompress;
import std.file;
import swffile;
void main(string[] args)
{
if (args.length == 1)
throw new Exception("No file specified");
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");
swf.header.signature[0] = cast(ubyte)'Z'; // LZMA
write(arg, swf.write());
}
}
+3 -3
View File
@@ -2,7 +2,7 @@
module zlibx;
import std.zlib, etc.c.zlib;
import std.zlib, etc.c.zlib, std.conv;
static import etc.c.zlib;
alias std.zlib.Z_SYNC_FLUSH Z_SYNC_FLUSH;
@@ -15,10 +15,10 @@ ubyte[] exactUncompress(ubyte[] srcbuf, size_t destlen)
uint err;
zs.next_in = srcbuf.ptr;
zs.avail_in = srcbuf.length;
zs.avail_in = to!uint(srcbuf.length);
zs.next_out = destbuf.ptr;
zs.avail_out = destbuf.length;
zs.avail_out = to!uint(destbuf.length);
err = etc.c.zlib.inflateInit2(&zs, 15);
if (err)